Discussion:
Unicode question
(too old to reply)
Jack
2009-12-15 12:25:40 UTC
Permalink
Hi vc gurus,

bool LoadMesh(std::wstring szPath)
{
//...

::GetModuleFileNameW(NULL, (LPWCH) szPath.c_str(), szPath.size());

szPath.append(szfilename);



//// Open .dat mesh
HANDLE h = CreateFileW((LPCWSTR)szPath.c_str(), GENERIC_READ, NULL, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
MessageBoxA(NULL, "Couldn't open file with CreateFile()", "Error",
MB_OK);
return E_FAIL;
}
//.....


The handle "h" is always 0xffffffff, does anyone know which part is wrong?

Thanks
Jack
Jack
2009-12-15 12:27:35 UTC
Permalink
Post by Jack
bool LoadMesh(std::wstring szPath)
bool LoadMesh(std::wstring szFilename)
{

Thanks
Jack
Jack
2009-12-15 12:32:48 UTC
Permalink
A side note:
When I use ASCII, hence CreateFileA,
it loads the mesh correctly (h is valid)...
Thanks
Jack
Giovanni Dicanio
2009-12-15 12:38:44 UTC
Permalink
Post by Jack
bool LoadMesh(std::wstring szPath)
I would pass an input string using a const reference, to avoid deep-copies,
e.g.

bool LoadMesh( const std::wstring & path )

Moreover, I tend to prefer CString class instead of STL [w]string for Win32
coding.
Post by Jack
::GetModuleFileNameW(NULL, (LPWCH) szPath.c_str(), szPath.size());
I don't think that you can modify the content of an STL string using c_str
(this method returns a const pointer to enforce that).

Moreover, I don't like explicitly specifying the 'W' suffix for Win32 APIs
(compiling in Unicode is just fine).

I would write some code like this:

CString strFilename;
static const int cchFilename = 4096;
TCHAR * pszFilename = strFilename.GetBuffer( cchFilename );
::GetModuleFileName( NULL, pszFilename, cchFilename );
... verify return code...
strFilename.ReleaseBuffer();

Now 'strFilename' should contain the required string.
Post by Jack
//// Open .dat mesh
HANDLE h = CreateFileW((LPCWSTR)szPath.c_str(),
I would just use CreateFile (not CreateFileW) in Unicode builds.
If szPath is an instance of std::wstring, you don't need an LPCWSTR cast for
the return value of c_str(), it is already a 'const wchar_t *', i.e.
LPCWSTR.

Giovanni
Giovanni Dicanio
2009-12-15 16:24:09 UTC
Permalink
Post by Jack
bool LoadMesh(std::wstring szPath)
[...]
Post by Jack
//// Open .dat mesh
HANDLE h = CreateFileW((LPCWSTR)szPath.c_str(), GENERIC_READ, NULL, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (h == INVALID_HANDLE_VALUE)
{
MessageBoxA(NULL, "Couldn't open file with CreateFile()", "Error",
MB_OK);
return E_FAIL;
}
I was missing that (not directly related to your original problem, but
probably worth mentioning):

1. Your function's return value is 'bool'. So you should return something
like 'true' or 'false', but not E_FAIL.
You should use E_FAIL if your function returns an HRESULT.

2. Instead of using MessageBoxA, you may want to use MessageBox, and
decorate your string literals using _T() (which will work in both ANSI/MBCS
and Unicode builds) or L"..." (Unicode builds only), e.g.:

MessageBox(
NULL,
L"Couldn't open file with CreateFile()",
L"Error",
MB_OK );

A better approach would be to store strings in resources (instead of using
string literals in source code), and load these messages from resources.
Post by Jack
The handle "h" is always 0xffffffff, does anyone know which part is wrong?
Please verify the content of 'szPath' passed to CreateFileW.

HTH,
Giovanni
Tim Roberts
2009-12-16 05:11:19 UTC
Permalink
Post by Jack
Hi vc gurus,
bool LoadMesh(std::wstring szPath)
{
//...
::GetModuleFileNameW(NULL, (LPWCH) szPath.c_str(), szPath.size());
Unless you have stored something in it previously, szPath.size() will be
zero.

Can you see that you are fighting the construct here? Seriously, when
you're working with the Win32 API, you're much better off using CString
than using std::string.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Leigh Johnston
2009-12-16 14:01:19 UTC
Permalink
Why the std::string hate? Using std::string/std::wstring with Win32 API is
fine, I have no problems with it.

/Leigh
Post by Tim Roberts
Post by Jack
Hi vc gurus,
bool LoadMesh(std::wstring szPath)
{
//...
::GetModuleFileNameW(NULL, (LPWCH) szPath.c_str(), szPath.size());
Unless you have stored something in it previously, szPath.size() will be
zero.
Can you see that you are fighting the construct here? Seriously, when
you're working with the Win32 API, you're much better off using CString
than using std::string.
--
Providenza & Boekelheide, Inc.
Alex Blekhman
2009-12-16 14:55:01 UTC
Permalink
Post by Leigh Johnston
Why the std::string hate? Using std::string/std::wstring with
Win32 API is fine, I have no problems with it.
This is not about hate or love. Some standard classes are fine
with Win32 API, but some aren't. std::string is significantly less
convenient than CString. Think about the following operations:

1. Load a string from resources.
2. Pass a pointer to internal character buffer to Windows API
function.
3. Trim a string from right and/or left.
4. Tokenize a string.
5. Convert a string between ANSI and Unicode encodings.

All the above mentioned tasks are quite easy with CString and hard
with std::string.

Alex
Leigh Johnston
2009-12-16 15:22:37 UTC
Permalink
Post by Alex Blekhman
1. Load a string from resources.
Fair enough, the only time I use CString
Post by Alex Blekhman
2. Pass a pointer to internal character buffer to Windows API function.
Trivial to do with std::string.. resize(...) and &s[0]
Post by Alex Blekhman
3. Trim a string from right and/or left.
Trivial to do with find/substr
Post by Alex Blekhman
4. Tokenize a string.
Writing a tokenizer is trivial, there is also boost
Post by Alex Blekhman
5. Convert a string between ANSI and Unicode encodings.
Fair enough although it is fairly trivial to implement your own (I did for
UTF-8) or use something like ICU.
Post by Alex Blekhman
All the above mentioned tasks are quite easy with CString and hard with
std::string.
Wrong.
Alex Blekhman
2009-12-16 15:50:19 UTC
Permalink
Post by Leigh Johnston
Post by Alex Blekhman
All the above mentioned tasks are quite easy with CString and
hard with std::string.
Wrong.
Everything is trivial, but requires you to write new code and
maintain it afterwards. As a developer you want your code to be
cheap and simple.

Alex
David Wilkinson
2009-12-16 16:00:50 UTC
Permalink
Post by Leigh Johnston
Post by Alex Blekhman
2. Pass a pointer to internal character buffer to Windows API function.
Trivial to do with std::string.. resize(...) and &s[0]
Are you sure that you are doing this correctly? What is the size of the
std::string after you do this?

It also depends on the internal string in std::string being contiguous, which is
not guaranteed by the current standard.

But in principle, I agree with you. In fact, I have a motto: Never use an MFC
class unless you have to.
--
David Wilkinson
Visual C++ MVP
Leigh Johnston
2009-12-16 16:08:58 UTC
Permalink
VC++ strings are contiguous and C++0x will guarantee contiguousness. The
current standard is ambiguous (due to an error I believe) as to whether
&s[0] is contiguous.

/Leigh
Post by David Wilkinson
Post by Leigh Johnston
Post by Alex Blekhman
2. Pass a pointer to internal character buffer to Windows API function.
Trivial to do with std::string.. resize(...) and &s[0]
Are you sure that you are doing this correctly? What is the size of the
std::string after you do this?
It also depends on the internal string in std::string being contiguous,
which is not guaranteed by the current standard.
But in principle, I agree with you. In fact, I have a motto: Never use an
MFC class unless you have to.
--
David Wilkinson
Visual C++ MVP
David Wilkinson
2009-12-16 17:34:20 UTC
Permalink
Post by Leigh Johnston
VC++ strings are contiguous and C++0x will guarantee contiguousness.
The current standard is ambiguous (due to an error I believe) as to
whether &s[0] is contiguous.
Well, OK.

But what about the length/size of the std::string s after you pass &s[0] to some
API? If s.length() and strlen(s.c_str()) are not the same, things could get very
confusing.
--
David Wilkinson
Visual C++ MVP
Leigh Johnston
2009-12-16 18:06:41 UTC
Permalink
Post by David Wilkinson
But what about the length/size of the std::string s after you pass &s[0]
to some API? If s.length() and strlen(s.c_str()) are not the same, things
could get very confusing.
strlen(s.c_str()) and s.length() will always be the same. use
std::vector<char> if you want to fill a buffer with a null terminated string
or perhaps into a std::string then delete the null terminator with erase.

/Leigh
Leigh Johnston
2009-12-16 18:09:48 UTC
Permalink
Well strlen(s.c_str()) and s.length() will not be the same if the string
includes a null terminator but you are using string incorrectly if this is
the case IMO.
Post by Leigh Johnston
Post by David Wilkinson
But what about the length/size of the std::string s after you pass &s[0]
to some API? If s.length() and strlen(s.c_str()) are not the same, things
could get very confusing.
strlen(s.c_str()) and s.length() will always be the same. use
std::vector<char> if you want to fill a buffer with a null terminated
string or perhaps into a std::string then delete the null terminator with
erase.
/Leigh
Igor Tandetnik
2009-12-16 18:13:07 UTC
Permalink
Post by Leigh Johnston
Post by David Wilkinson
But what about the length/size of the std::string s after you pass
&s[0] to some API? If s.length() and strlen(s.c_str()) are not the
same, things could get very confusing.
strlen(s.c_str()) and s.length() will always be the same.
string s;
s.resize(100);
s[0] = 0; // Replace with, say, GetWindowText for added realism
assert(strlen(s.c_str()) == s.length()); // oops
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Leigh Johnston
2009-12-16 18:22:36 UTC
Permalink
You can do a resize() or erase to remove the null terminator after copying
to the buffer. You obviously should not use c_str() if it is valid for your
std::string string to contain nulls. This is one reason for using
std::string over C null terminated strings, the inclusion of length.
Post by Igor Tandetnik
Post by Leigh Johnston
Post by David Wilkinson
But what about the length/size of the std::string s after you pass
&s[0] to some API? If s.length() and strlen(s.c_str()) are not the
same, things could get very confusing.
strlen(s.c_str()) and s.length() will always be the same.
string s;
s.resize(100);
s[0] = 0; // Replace with, say, GetWindowText for added realism
assert(strlen(s.c_str()) == s.length()); // oops
--
With best wishes,
Igor Tandetnik
With sufficient thrust, pigs fly just fine. However, this is not
necessarily a good idea. It is hard to be sure where they are going to
land, and it could be dangerous sitting under them as they fly
overhead. -- RFC 1925
Alex Blekhman
2009-12-16 16:54:04 UTC
Permalink
In fact, I have a motto: Never use an MFC class unless you have
to.
Actually, CString is not MFC class anymore. They factored it out
along with other several popular classes, so now you can include
CString support in your project with minimal overhead.

Alex
Giovanni Dicanio
2009-12-16 15:58:13 UTC
Permalink
Post by Leigh Johnston
Post by Alex Blekhman
2. Pass a pointer to internal character buffer to Windows API function.
Trivial to do with std::string.. resize(...) and &s[0]
Are std::string's always NUL-terminated?
My understanding is that calling c_str() always returns a NUL-terminated
string, but I'm not sure about &s[0] (assuming 's' being an instance of
std::[w]string).

Moreover, when calling Win32 APIs that have LPCTSTR parameters, we can pass
a CString instance and implicit LPCTSTR conversion operator is called,
instead with std::[w]string it is required an additional call to .c_str()
method.
Moreover, does the .c_str() method return a *copy* of the string data? This
would cause inefficiencies...

And CString is reference counted, so using this class could save useless
deep-copies (optimizing both in space and time) that instead can happen with
std::[w]string.
Post by Leigh Johnston
Post by Alex Blekhman
3. Trim a string from right and/or left.
Trivial to do with find/substr
Is it as trivial as CString::Trim/TrimeLeft/TrimRight?
I don't think so.
Post by Leigh Johnston
Post by Alex Blekhman
4. Tokenize a string.
Writing a tokenizer is trivial, there is also boost
CString does it out-of-the-box.
No need for custom code.
Post by Leigh Johnston
Post by Alex Blekhman
All the above mentioned tasks are quite easy with CString and hard with
std::string.
Wrong.
I agree with Alex.
CString interface is more convenient than std::string, especially in contest
of Win32 programming.

The benefit of STL's string class is portability of C++ code, but when you
#include <windows.h> you've already left the kingdom of portable C++ code.

Giovanni
Leigh Johnston
2009-12-16 16:06:39 UTC
Permalink
You are right about the null termination being a problem, I usually use the
following when interfacing with Win32:

std::vector<char> text;
text.resize(length+1);
::SendMessageA(hwnd, WM_GETTEXT, static_cast<WPARAM>(length+1),
reinterpret_cast<LPARAM>(&text[0]));
std::string string;
string.assign(&text[0], length);

I prefer to have as much of my code to be as portable as possible, hence my
preference for std::string; I prefer the MVC way of doing things where you
separate "model" and "gui" specific code, "gui" specefic e.g. MFC dialogs
are where I employ CString, otherwise I use std::string as much as possible.

I prefer an explicit call to c_str() rather than relying on ugly conversion
operators and it is up to the implementation as to whether c_str() returns a
copy or not, in VC++ it does not return a copy I believe.

/Leigh
Post by Giovanni Dicanio
Post by Leigh Johnston
Post by Alex Blekhman
2. Pass a pointer to internal character buffer to Windows API function.
Trivial to do with std::string.. resize(...) and &s[0]
Are std::string's always NUL-terminated?
My understanding is that calling c_str() always returns a NUL-terminated
string, but I'm not sure about &s[0] (assuming 's' being an instance of
std::[w]string).
Moreover, when calling Win32 APIs that have LPCTSTR parameters, we can
pass a CString instance and implicit LPCTSTR conversion operator is
called, instead with std::[w]string it is required an additional call to
.c_str() method.
Moreover, does the .c_str() method return a *copy* of the string data?
This would cause inefficiencies...
And CString is reference counted, so using this class could save useless
deep-copies (optimizing both in space and time) that instead can happen
with std::[w]string.
Post by Leigh Johnston
Post by Alex Blekhman
3. Trim a string from right and/or left.
Trivial to do with find/substr
Is it as trivial as CString::Trim/TrimeLeft/TrimRight?
I don't think so.
Post by Leigh Johnston
Post by Alex Blekhman
4. Tokenize a string.
Writing a tokenizer is trivial, there is also boost
CString does it out-of-the-box.
No need for custom code.
Post by Leigh Johnston
Post by Alex Blekhman
All the above mentioned tasks are quite easy with CString and hard with
std::string.
Wrong.
I agree with Alex.
CString interface is more convenient than std::string, especially in
contest of Win32 programming.
The benefit of STL's string class is portability of C++ code, but when you
#include <windows.h> you've already left the kingdom of portable C++ code.
Giovanni
Giovanni Dicanio
2009-12-16 16:57:54 UTC
Permalink
Post by Leigh Johnston
You are right about the null termination being a problem,
Actually, Tim Roberts enlightened me about that.

Giovanni
Tim Roberts
2009-12-21 00:06:21 UTC
Permalink
Post by Leigh Johnston
I prefer to have as much of my code to be as portable as possible, hence my
preference for std::string;
I can appreciate this goal, but once you start calling SendMessage and
GetWindowsDirectory, portability is no longer an issue. That's my basic
point. In code that is dealing with the Win32 API, you might as well use
CString, since the impedance match is better.

In code that CAN be made portable, std::string is a great choice.
Post by Leigh Johnston
I prefer an explicit call to c_str() rather than relying on ugly conversion
operators and it is up to the implementation as to whether c_str() returns a
copy or not, in VC++ it does not return a copy I believe.
Quite correct. In fact, in VC++, &s[0] happens to return a zero-terminated
buffer, but that's an implementation detail.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Tim Roberts
2009-12-20 23:59:50 UTC
Permalink
Post by Leigh Johnston
Post by Alex Blekhman
2. Pass a pointer to internal character buffer to Windows API function.
Trivial to do with std::string.. resize(...) and &s[0]
Trivial, and unsafe. &s[0] on a std::string gives you a counted vector,
not necessarily a zero-terminated string. The implementation CAN do so,
but the standard does not require it. To be safe, you have to use
s.c_str().
Post by Leigh Johnston
Post by Alex Blekhman
All the above mentioned tasks are quite easy with CString and hard with
std::string.
Wrong.
No, he's not. "Hard" may be a bit of an exaggeration, but "unnatural" is
not. Win32 API code using CString is simply more natural than std::string.
--
Tim Roberts, ***@probo.com
Providenza & Boekelheide, Inc.
Leigh Johnston
2009-12-21 01:56:00 UTC
Permalink
Post by Tim Roberts
Trivial, and unsafe. &s[0] on a std::string gives you a counted vector,
not necessarily a zero-terminated string. The implementation CAN do so,
but the standard does not require it. To be safe, you have to use
s.c_str().
You cannot write to the buffer returned by c_str(). You can resize the
string to include a possible null terminator, WinAPI write to it via &s[0]
(which is contiguous on most sane implementations and guaranteed to be in
c++0x), then remove the null terminator with erase.

/Leigh
Leigh Johnston
2009-12-21 02:06:21 UTC
Permalink
Herb Sutter on &s[0]:

. In current ISO C++ (C++98 and C++03), std::string is not fully required to
store its data contiguously. You're supposed to use str.c_str() to get a
pointer to a contiguous and null-terminated string (albeit a read-only
string). However, current ISO C++ does require &str[0] to cough up a pointer
to contiguous string data (but not necessarily null-terminated!), so there
wasn't much leeway for implementers to have non-contiguous strings, anyway.
For C++0x we have already adopted the guarantee that std::string contents
must indeed be stored contiguously. For details, see
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#530 .
Jack
2009-12-16 07:18:48 UTC
Permalink
Thank you for all the advices

Jack
Continue reading on narkive:
Loading...