Can I combine A(nsi) and W(ide char) Win32 API calls whan I deal with controls?

121 views Asked by At

Is there a guide or a document that specifies what is possible if I need to call sometimes ansi version of SetWindowTextA() and later SetWindowTextW(). Do I need to create an edit box with CreateWindowW() if I want to set text content with SetWindowTextW().

I am asking this because certain things seem to work wile others don't.

For example setting text with SetWindowTextW() loses certain accented characters. I just found out that for example letter 'è' converts to 'e' even if I set it with SetWindowTextW().

I have seen this post: SetWindowTextW in an ANSI project

I have tried all the answers there but all of them leave my controls empty, they fail to set the text. This code does nothing:

savedWndProc = (WNDPROC)GetWindowLong (ctlHwnd, GWL_WNDPROC);
  
SetWindowLongPtrW (ctlHwnd, GWLP_WNDPROC, (LONG_PTR)DefWindowProcW);  // or SetWindowLong (ctlHwnd, GWL_WNDPROC, (long)DefWindowProcW);
result = SetWindowTextW (ctlHwnd, buffPtr);
SetWindowLong (ctlHwnd, GWL_WNDPROC, (long)savedWndProc);

Field is left empty. And this might be a reason:

if (GetWindowLong(ctlHwnd, GWL_WNDPROC) == DefWindowProc)...

... never evaluates to true.

So, is it possible that edit controls have their own default window procedure, different from DefWindowProc, some DefEditWindowProc?

EDIT - in the end I created an invisible edit with CreateWindowW() just to grab its windows procedure and when I call it:

CallWindowProcW (origEditProcW, ctlHwnd, WM_SETTEXT, 0L, (long)buffPtr);

... I have my è character. Great!

EDIT 2:

Thanks Ben for the patience.

I have found out the root of the problem, why SetWindowTextW() lost my è. First I tried to solve this the way they suggested on that StackOverfow link above, but it failed because using DefWindowProcW was not appropriate for the edit field.

But there I learned SetWindowTextW() ends up as a WM_SETTEXT call to the windows procedure and since all of my edit fields have my own custom procedure that handles a few different things but then for the rest of the messages ends up with a call to CallWindowProc() on the original procedure and since it was an ansi project that is CallWindowProcA() and that is the main reason why SetWindowTextW() failed in the first place.

SetWindowTextW() would have worked without me sublassing the edit box. So now, I explicitly call CallWindowProcW() for SETTEXT & GETTEXT outside of that custom procedure and everything works as it should.

EDIT 3:

This is all very interesting as it occured to me that when I used SetWindowTextW() and it displayed e instead of è it should have crashed really or displayed some random characters. But no, edit control's proc detected what is in the buffer (or lParam for WM_SETTEXT message) and converted it to my local encoding because I was handling that message with CallWindowProcA().

But how did edit control know what is in the lParam? SetWindowTextW() should have sent Unicode string. Or maybe not?

What is actually more interesting is that I intercepted that lParam after I set the breakpoint on the SetWindowTextW() call and then in my custom edit proc, on the last line where I use CallWindowProcA() and lParam does not seem to be Unicode string but it's just a plain C string. How SetWindowTextW() knows I will call CallWindowProcA()? Because I did before?

If only Raymond Chen wrote something about it. I found these somewhat related posts but nothing about WM_SETTEXT and how it's handling lParam.

https://devblogs.microsoft.com/oldnewthing/20211210-00/?p=106021

https://devblogs.microsoft.com/oldnewthing/20211210-00/?p=106021

In the end, in my case with sublassed edit proc, the only way to make it work with unicode is not to use SetWindowTextW() and to explicitly call edit's proc like this:

CallWindowProcW (origEditProc, ctlHwnd, WM_SETTEXT, 0L, (long)wcharBuffPtr);

.. instead of:

SetWindowTextW (ctlHwnd, wcharBuffPtr);
1

There are 1 answers

2
Ben Voigt On BEST ANSWER

You can combine the API calls, but that just causes the OS to do string conversions. Making a Unicode API call won't enable an ANSI window to store Unicode contents.

The character set of a window is determined by the use of the RegisterClass function. If the window class was registered with the ANSI version of RegisterClass (RegisterClassA), the character set of the window is ANSI. If the window class was registered with the Unicode version of RegisterClass (RegisterClassW), the character set of the window is Unicode.

The system does automatic two-way translation (Unicode to ANSI) for window messages. For example, if an ANSI window message is sent to a window that uses the Unicode character set, the system translates that message into a Unicode message before calling the window procedure. The system calls IsWindowUnicode to determine whether to translate the message.

from the documentation for IsWindowUnicode

Note that the class "EDIT" should be registered by the system as a Unicode class to begin with... all edit controls should support Unicode whether created by CreateWindowA or CreateWindowW.