从 DLL res 文件中将希伯来语字符串读入 VB6

Reading a Hebrew string into VB6 from a DLL res file

我在 Visual Studio 2019 年创建了一个字符串资源 .RES 文件,其中包含多种语言的各种字符串表,然后我将 .RES 编译成一个 VB6 DLL(没有代码,VB6 项目只是一个编译后的 VB6动态链接库)。这是创建 DLL 的无代码 VB6 项目:

然后我将此 DLL 中的字符串读入 VB6 程序,并输出到支持 Unicode 的标签控件。

英语和阿拉伯语的字符串 read/output 很好,但对于希伯来语,它只显示相同的字符。

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

如您所见,阿拉伯语输出很好(英语也很好,只是没有截屏)。但是...希伯来语打印出相同的字符?!

您不能 Declare 参数 As String*W 函数族。
VB6 在调用 Declared 函数时将 一个 String 到非 Unicode 程序的当前系统代码页,并在调用 returns 时转换回 Unicode。此机制旨在与处理 ANSI 的 *A 函数族进行交互。

当以这种方式调用 *W 函数时,不仅 Unicode 数据会在您有机会执行 StrConv(vbFromUnicode) 之前被破坏(您几乎永远不应该这样做,这里它只会破坏数据甚至进一步),但你也有一个buffer overflow,你向函数承诺你已经提供了space的1000个字符,而您只提供 1000 字节,这是原来的一半。

为了调用*W函数,您必须声明字符串缓冲区As Long并传递StrPtr()字符串变量。

您也不需要回退到 LoadStringA,因为它只不过是 LoadStringW.
的包装器 您对 SetThreadUILanguage 的声明也是错误的(LANGID is an Integer, as opposed to LCIDLong)。

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

修复:在控制 Panel/Region/Administrative 选项卡中,我必须将“非 Unicode 程序的当前语言”更改为 'Hebrew' 或 'Arabic' 才能使其正确显示。 @GSerg 还添加了正确调用 W 函数的有用提示。