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

问题描述

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

enter image description here

然后我从这个 DLL 中读取字符串一个 VB6 程序中,并输出一个 Unicode 感知标签控件。

从英语和阿拉伯语中读取/输出的字符串很好,但对于希伯来语,它只显示相同的字符。

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 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,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

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

解决方法

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

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

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

您也不需要退回到 LoadStringA,因为它只不过是 LoadStringW 的包装。
您对 SetThreadUILanguage 的声明也是错误的(LANGIDInteger,而 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
,

修复:在控制面板/区域/管理选项卡中,我必须将“非 Unicode 程序的当前语言”更改为“希伯来语”或“阿拉伯语”才能正确显示。 @GSerg 还添加了正确调用 W 函数的有用提示。