Found on: April 2009
Applicable systems: Visual Basic 6, Windows OS versions with native UNICODE support (not tested with Win95 and 98)
Visual Basic 6 is quite old now and has limited UNICODE support. Strings are internally stored as UNICODE (UTF-16 BSTR; which is really a far-sighted decision from VB6 development team) so you can use many of the string functions to work correctly on both ANSI and UNICODE text. But one of the areas where you may have difficulty in setting UNICODE text is the form’s window text. You cannot paste UNICODE text in the IDE and SetWindowText() API will not work because the form is created as an ANSI window. Trying to create your own window using CreateWindowExW() would just kill the advantage of using VB’s easy to use form editor.
In newer NT OSes, everything is internally stored in UNICODE. Even ANSI versions of API function calls are redirected to their UNICODE counterparts after going through a stub function which translates ANSI text to UNICODE text. This means that application window text are also in UNICODE. But the fact that VB6 forms when being created into windows during application runtime are created using CreateWindowExA() is the cause of the inability of SetWindowText() API to change window text to an UNICODE one. Creating windows using CreateWindowExA() lets the OS know that the window procedure only processes ANSI text and the OS must convert its internally stored UNICODE text to ANSI before passing to the set window procedure.
When you call SetWindowText() from within your code, what happens is that the OS will send WM_SETWINDOWTEXT message along with ANSI translated window text to the window procedure set by VB6. The VB6 procedure will then call DefWindowProcA() to set the new window text.
The solution to this problem that I found was to subclass the window procedure of the form in a module. Then when you receive WM_SETTEXT or WM_GETTEXT, call DefWindowProcW() yourself then return its return value as yours without calling the next window procedure in the chain, i.e. the window procedure set by VB6. For all else, just call VB6’s window procedure. What this will do is prevent the VB6’s ANSI window procedure from messing around with the window text only. The following illustrates the code:
'In a module after subclassing the window using SetWindowLongW(..., GWL_WNDPROC, ...) Private Function WindowProc(ByVal hWnd As Long, ByVal uMsg As Long, ByVal wParam As Long, ByVal lParam As Long) As Long If ((uMsg = WM_SETTEXT) Or (uMsg = WM_GETTEXT)) Then 'Allows frmMain to have and return unicode window text 'Send message to default window procedure and not to next win proc WindowProc = DefWindowProcW(hWnd, uMsg, wParam, lParam) Exit Function End If ' Your code here ' ... 'Else, Call ANSI version of VB's window proc WindowProc = CallWindowProcA(PrevProc, hWnd, uMsg, wParam, lParam) End Function
I have found no problem with my application using this approach but this isn’t a guarantee that there isn’t. Be sure to research well yourself. Enjoy!