问题描述
我在 Visual Studio 2019 中创建了一个字符串资源 .RES 文件,其中包含多种语言的各种字符串表,然后我将 .RES 编译成 VB6 DLL(没有代码,VB6 项目只是一个编译后的 VB6 DLL)。这是创建 DLL 的无代码 VB6 项目:
然后我从这个 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
如您所见,阿拉伯语输出很好(英语也很好,只是没有截屏)。但是......希伯来文打印出相同的字符?!
解决方法
您不能将 Declare
参数 As String
用于 *W
系列函数。
当调用 String
d 函数时,VB6 将 automatically convert 一个 Declare
到非 Unicode 程序的当前系统代码页,并在调用返回时转换回 Unicode。此机制旨在与处理 ANSI 的 *A
系列函数进行交互。
当以这种方式调用 *W
函数时,不仅 Unicode 数据会在您有机会执行 StrConv(vbFromUnicode)
之前被破坏(您几乎不应该这样做,在这里它会只会破坏数据甚至进一步),但是您也有一个 buffer overflow,您可以在其中向函数承诺您提供了 1000 个字符的空间,而您只提供了 1000 个字节,这是一半一样多。
为了调用 *W
函数,您必须声明字符串缓冲区 As Long
并传递字符串变量的 StrPtr()
。
您也不需要退回到 LoadStringA
,因为它只不过是 LoadStringW
的包装。
您对 SetThreadUILanguage
的声明也是错误的(LANGID
是 Integer
,而 LCID
是 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
,
修复:在控制面板/区域/管理选项卡中,我必须将“非 Unicode 程序的当前语言”更改为“希伯来语”或“阿拉伯语”才能正确显示。 @GSerg 还添加了正确调用 W 函数的有用提示。