Sign in to follow this  
King_DuckZ

Unicode CreateWindowEx() with ANSI strings

Recommended Posts

Hello everyone, I'm still working on my Win32 DLL in C++, hopefully I'm almost finished, but I have a little problem, I'll try to explain at my best: I wrote a class, CSimpleWindow, which has, amongst the others, those methods:
int BuildWindow ( const wchar_t* strTitle, const wchar_t* strName, WNDPROC callback, int nCmdShow );
int BuildWindow ( const char* strTitle, const char* strName, WNDPROC callback, int nCmdShow );
The class is made to work only with unicode strings internally. Assuming that overload is the correct way to deal with Unicode/ANSI support, I wrote those two methods: the first one does all the real work, while the second one only converts the char* arguments into wchar_t* and passes them to the first one, like this:
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strTitle, -1, strTitleW, (int)uTitle + 1);
MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, strName, -1, strNameW, (int)uName + 1);
const int nRet = BuildWindow(strTitleW, strNameW, callback, nCmdShow);
Ok, now to the problem: from the exe that uses my dll, if I call the first method (thus, UNICODE is defined), all is well. If I don't define UNICODE, the overloaded method gets invoked, correctly converts the string and the wchar_t* version of BuildWindow() is called, BUT! the text of the newly created window is truncated, apparently to the first NULL char (char, although CreateWindowExW() is being called). This is how the exe calls the CSimpleWindow's method:
wnd.BuildWindow(TEXT("Window"), TEXT("CWNDTEST"), &EventHandler, SW_SHOW);
The window's title is just a "W", as if the padding 0 was seen as a terminating null char. MessageBox(), on the other hand, displays the full string. I added the following test code to the wchar_t version of BuildWindow():
unsigned char ch[] = {
0x44, 0x30, 0x57, 0x30, 0x83, 0x30, 0x26, 0x20, 0x01, 0xFF,
0x00, 0x30, 0x44, 0x30, 0x57, 0x30, 0x83, 0x30, 0x01, 0xFF,
0x00, 0x30, 0x42, 0x30, 0x42, 0x30, 0x01, 0xFF, 0x00, 0x30,
0x4A, 0x30, 0x8C, 0x30, 0x60, 0x30, 0x01, 0xFF, 0x00, 0x00
};
m_hWnd = CreateWindowEx (0, strClassSrc, (wchar_t*)&ch[0], dwWindowstyle, 0, 0, 640, 480, NULL, NULL, hAppInstance, NULL);
MessageBox(0, (wchar_t*)&ch[0], strClassSrc, MB_OK | MB_ICONSTOP);
Here, the title is only 5 characters long, as if the 0x3000 was a terminator. Again, MessageBox() works fine. The fun thing is that ch[] is hardcoded within the dll, but defining/undefining UNICODE within the exe (without recompiling the dll) messes up everything. I imagine I could've explained all this much better, but I'm a bit tired, so sorry for the long post :/ I hope you guys get the point.

Share this post


Link to post
Share on other sites
Are you 100% sure the code isn't being recompiled as well? You didn't implement BuildWindow inside a header file, did you? That would obviously be recompiled whereever it is included.

If you want to always have the unicode version called regardless of whether UNICODE is defined or not, suffix a "W" to CreateWindowEx so the full name becomes CreateWindowExW. For the non-unicode version, suffix an A instead.

Also, are you sure you're providing enough room for the converted string? You should call MultiByteToWideChar with a last parameter of 0 to get the number of characters required, then allocate an appropriate amount (using vector<wchar_t> to avoid pointers).

Share this post


Link to post
Share on other sites
Yes, I'm sure. Methods' bodies are written into a .cpp file, and of course I'm not #including it anywhere.

I'm sure the W version is being used, since I already tried what you suggested. I changed the required space calculations as you said, but the result is always the same.

I tried compiling both with CodeWarrior and Visual Studio .net, the result is the same (except for a square character that Visual Studio appends to the title bar when the string is truncated). I'm 100% sure the values I'm passing are good - I also tried with hardcoded ones. If you want to see more code, please ask.

Share this post


Link to post
Share on other sites
Ok, I did a couple modifications that simplify the problem. Here's what's happening now:

the overloaded char* version of BuildWindow() doesn't exist anymore -
the exe calls BuildWindow() with NULL string arguments (the first two) -
BuildWindow() inside the dll ignores the first two arguments and uses hardcoded strings -

Once the dll is compiled:

if I define UNICODE within the exe, the window's title is the hardcoded string -
if I don't, the title is truncated -
MessageBox() (called from within BuildWindow) always works -

Edit:
Here's some code:


//From the DLL
int CSimpleWindow::BuildWindow(const wchar_t* strTitle, const wchar_t* strName, WNDPROC callback, int nCmdShow) {
WNDCLASSEX wnd;
const wchar_t* strTitleSrc = g_strDefTitle;
const wchar_t* strClassSrc = g_strDefClass;

HBRUSH hBr = CreateSolidBrush(BackColor & 0xFFFFFF);

wnd.cbClsExtra = 0;
wnd.cbSize = sizeof(WNDCLASSEX);
wnd.cbWndExtra = 0;
wnd.hbrBackground = hBr; //(HBRUSH)GetStockObject(BLACK_BRUSH);
wnd.hCursor = LoadCursor (NULL, IDC_ARROW);
wnd.hIcon = hIcon;
wnd.hIconSm = hIcon;
wnd.hInstance = hAppInstance;
wnd.lpfnWndProc = callback;
wnd.lpszClassName = strClassSrc;
wnd.lpszMenuName = NULL;
wnd.style = dwStyle;

RegisterClassEx (&wnd);

m_hWnd = CreateWindowEx (0, strClassSrc, strTitleSrc, dwWindowStyle,
0, 0, 640, 480, NULL, NULL, hAppInstance, NULL);
ShowWindow (m_hWnd, nCmdShow);
UpdateWindow (m_hWnd);

return 0;
}

//From the exe
//#define UNICODE

//blah blah blah

CSimpleWindow wnd;
wnd.nWidth = AREA_WIDTH;
wnd.nHeight = AREA_HEIGHT;
wnd.BuildWindow(0, 0, &EventHandler, SW_SHOW);

MSG msg;
while (GetMessage(&msg, 0, 0, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}



As said, removing the // from #define UNICODE causes the dll to work.

Share this post


Link to post
Share on other sites
On a whim, I looked up "GetMessage" and apparently it also varies by unicode - there is both "GetMessageA" and "GetMessageW"

The same is true for "DispatchMessage" - both "DispatchMessageA" and "DispatchMessageW" exist.

It looks like you should put message handling into your window library.

Also, read the "Return Value" section on MSDN on GetMessage because that while loop isn't properly done - GetMessage can return -1 to indicate an error condition, which won't be caught in your setup.

Share this post


Link to post
Share on other sites
You're right, forcing the exe to use the W functions does the trick, especially the DefWindowProcW() inside the event handler. This makes sense, since SetWindowText() (and CreateWindowEx(), I assume) simply posts a message to the Window.

Good thing you told me about that -1 return value, since GetMessage() returns a BOOL, I expected it to be only TRUE or FALSE.

Well, with little effort I can put both the main loop and the event handler within the dll, so everything is fine now ;) Thanks a lot!

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this