3

I created a String Resource .RES file in Visual Studio 2019 with various string tables for multiple languages, then I compiled the .RES into a VB6 DLL (no code, the VB6 project is just a compiled VB6 DLL). Here is the no-code VB6 project that creates the DLL:

enter image description here

I then read the strings from this DLL into a VB6 program, and output to a Unicode-aware label control.

The strings read/output fine from English and Arabic, but for Hebrew, it just shows the same character.

Option Explicit

Private Declare Function LoadString Lib "user32" Alias "LoadStringA" (ByVal hInstance As Long, ByVal wID As Long, ByVal lpBuffer As String, ByVal nBufferMax As Long) As Long
Private Declare Function LoadStringW Lib "user32" (ByVal hInstance As Long, ByVal wID As Long, ByVal lpBuffer As String, ByVal nBufferMax As Long) As Long '   Works Arabic
Private Declare Function SetThreadUILanguage Lib "kernel32" (ByVal dwLCID As Long) As Long
Private Declare Function SetThreadLocale Lib "kernel32" (ByVal dwLCID As Long) As Long

Private Sub Form_Load()
    Dim hInst As Long, lResult As Long
    Dim resstring As String
    Dim icc As Long
    
    Const STRLENGTH As Long = 1000
    Const HEBREW As Long = 1037
    Const ARABIC As Long = 3073
    Const ENGLISH As Long = 1033

    icc = ENGLISH   ' convenience, set it once here
    
    SetThreadUILanguage icc
    SetThreadLocale icc
    
    hInst = LoadLibrary("c:\temp\resstr.dll")
    If hInst Then
        resstring = String(STRLENGTH, Chr(0))
        If icc = ENGLISH Then
            lResult = LoadString(hInst, 101, resstring, STRLENGTH)
            Label1.Caption = Left$(resstring, lResult)
        Else
            lResult = LoadStringW(hInst, 101, resstring, STRLENGTH)
            Label1.Caption = StrConv(Left(resstring, lResult * 2), vbFromUnicode, icc)
        End If
        lResult = FreeLibrary(hInst)
    End If
End Sub

Here is the output and what is in the .RES file that is compiled into a DLL

As you can see, the Arabic output is fine (and so is the English, just not screen captured). BUT...the Hebrew prints out the same character?!

JeffR
  • 765
  • 2
  • 8
  • 23

2 Answers2

3

You cannot Declare an argument As String to the *W family of functions.
VB6 will automatically convert a String to the current system codepage for non-Unicode programs when calling into a Declared function, and convert back to Unicode when the call returns. This mechanism is designed to interact with the *A family of functions that deal with ANSI.

When calling a *W function in that way, not only the Unicode data will be destroyed even before you get a chance to execute your StrConv(vbFromUnicode) (which you should almost never do, and here it will only destroy the data even further), but you also have a buffer overflow where you promise to the function that you have provided 1000 characters of space, whereas you only provide 1000 bytes, which is half as much.

In order to call a *W function, you must declare the string buffer As Long and pass StrPtr() of the string variable.

You also don't need to fall back to LoadStringA, as it is nothing more than a wrapper around LoadStringW.
Your declaration for SetThreadUILanguage is also wrong (LANGID is an Integer, as opposed to LCID which is a Long).

Option Explicit

Private Declare Function LoadStringW Lib "user32" (ByVal hInstance As Long, ByVal wID As Long, ByVal lpBuffer As Long, ByVal nBufferMax As Long) As Long
Private Declare Function SetThreadUILanguage Lib "kernel32" (ByVal LangId As Integer) As Integer
Private Declare Function SetThreadLocale Lib "kernel32" (ByVal dwLCID As Long) As Long

Private Sub Form_Load()
    Dim hInst As Long, lResult As Long
    Dim resstring As String
    Dim icc As Long
    
    Const STRLENGTH As Long = 1000
    Const HEBREW As Long = 1037
    Const ARABIC As Long = 3073
    Const ENGLISH As Long = 1033

    icc = ENGLISH   ' convenience, set it once here
    
    SetThreadUILanguage icc
    SetThreadLocale icc
    
    hInst = LoadLibrary("c:\temp\resstr.dll")
    If hInst Then
        resstring = String(STRLENGTH, vbNullChar)
        lResult = LoadStringW(hInst, 101, StrPtr(resstring), STRLENGTH)
        Label1.Caption = Left$(resstring, lResult)

        lResult = FreeLibrary(hInst)
    End If
End Sub
GSerg
  • 76,472
  • 17
  • 159
  • 346
  • so `resstring = String(STRLENGTH*2, vbNullChar)`? – JeffR Feb 19 '21 at 11:04
  • It should be a Long in VB, because an INT in C++ is -2,147,483,648 to 2,147,483,647, and an Integer in VB is -32,768 to 32,767. So when an API has an INT defined, it is really a Long in VB. – JeffR Feb 19 '21 at 11:05
  • 1
    @JeffR No, it should be `String(STRLENGTH, vbNullChar)`, because `STRLENGTH` is, correctly, length in chars. No, it should be `Integer`, because [`LANGID`](https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#LANGID) is a `WORD`, and [`WORD`](https://learn.microsoft.com/en-us/windows/win32/winprog/windows-data-types#WORD) is `unsigned short`. – GSerg Feb 19 '21 at 11:12
  • I changed the Declare of LoadStringW to `As Long` instead of `As String`, and put in StrPtr(resstring), but it does not read the string in correctly...just gives random chinese characters. – JeffR Feb 19 '21 at 11:23
  • @JeffR Now verify that your res file actually contains Unicode data. – GSerg Feb 19 '21 at 11:36
  • if you look at the image I embedded above, you can see the .RES file contains Unicode chars in both Hebrew and Arabic. – JeffR Feb 19 '21 at 11:41
  • Ahhh, I didn't see at first you removed my StrConv line. Works great! – JeffR Feb 19 '21 at 11:46
0

FIX: In Control Panel/Region/Administrative tab, I had to change the "Current language for non-Unicode programs" to 'Hebrew' or 'Arabic' to get it to display correctly. @GSerg also added helpful tips of properly calling a W function.

JeffR
  • 765
  • 2
  • 8
  • 23
  • 1
    You don't need to change the language for non-Unicode programs in order to display Unicode correctly. If you do, then your statement that you are using "a Unicode-aware label control" is not correct. – GSerg Feb 19 '21 at 12:05
  • @GSerg I had the non-Unicode OCX in the distribution. Now that the Unicode-OCX is distributed, I don't have to change the language for non-Unicode programs. Ty! – JeffR Feb 19 '21 at 13:44