Search code examples
vb6localehebrewunicode-stringresource-files

Reading a Hebrew string into VB6 from a DLL res file


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?!


Solution

  • 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