【学习笔记】怎么使用Unicode版和Ansi版API——送给和小弟我一样的初学者,并请各位高人电瓶

【学习笔记】如何使用Unicode版和Ansi版API——送给和我一样的菜鸟,并请各位高人电瓶
出处:http://blog.csdn.net/slowgrace/archive/2009/04/24/4105426.aspx

本文来自对icansaymyabc、chenjl1031、cnzdgs、VBAdvisor、unsigned、Soyokaze、eglic、ahao等网友发言和参考链接里的信息的总结。

1、ANSI字符集和Unicode字符集
ANSI的ASCII字符集及其派生字符集(也称多字节字符集)比较旧,Unicode字符集比较新,固定以双字节表示一个字。具体见参考链接1。随着32位世界和VB4的到来,我们迈进了一半是UNICODE,一半是ANSI的Windows世界。而在此之前,是ANSI一统天下。

 

2、WINDOWS API所用的字符集
操作字符串的API在声明时,会指定字符集。每个含有字符串的API同时有两个版本:即ANSI,Unicode。尾部带A的API是ANSI版本,带W的API是Unicode版本。例如:SetWindowTextA,是ANSI函数;而SetWindowTextW,是Unicode函数
C/C++ code
WINUSERAPI BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString)   
WINUSERAPI BOOL WINAPI SetWindowTextW(HWND hWnd, LPCWSTR lpString)  


相应的,凡涉及到字符集的通知事件都有A和W两种消息定义。比如,TVN_SELCHANGEDA和TVN_SELCHANGEDW实际上是两个不同的通知,其含义是相同的,不同之处在于通知中附带的结构,A结构中使用多字节字符串,W结构使用Unicode字符串。

 

3、VB所用的字符集
在VB中,所有字符串按UNICODE保存,但所有的API调用却仍使用ANSI字符串。这就要求在调用API函数之前,将字符串从UNICODE转换成ANSI,函数执行结束后,将返回的字符串从ANSI转换成UNICODE。虽然大多数时候这种转换对用户来说是透明的,但这就使利我们不能将一个字符串类型的参数以UNICODE方式从VB传递给DLL。类似地,任何包含有字符串的结构在执行API调用时,也必须经过这种双重转换。
VB.NET 是和 VB6 完全不同的东东,是 Unicode 内核的,但是有 ANSI 和 Unicode 两种界面。

 

4、VB6中使用API函数


上节提到,VB6 的String传给API时会自动被转化为ANSI string,从API返回后又被自动转换为unicode String,所以用 VB6/VBA/VBS 最好用ANSI版的API。

如果有些API只有Unicode版本,那就要把string类型的参数转化为指针(以long声明)传进去。因为用“Private/Public Declare ...”语法声明的函数,VB编译器认为就是API函数,那么只要是用“As String”声明的参数,VB编译器一概无条件地把字串由BSTR转换为ANSI传递。比如,下面两种声明:
VB code
Private Declare Function CharUpperWide Lib "user32" Alias "CharUpperW" (ByVal lpsz As Long) As Long
Private Declare Function CharUpperWide Lib "user32" Alias "CharUpperW" (ByVal s As String) As Long 


用下面的代码调用 
VB code
Dim s as String    
s = "hello"    
Call CharUpperWide(StrPtr(s))    
Debug.Print s   



用第一种声明,结果是HELLO;而第二种却得不到预期结果。

因此,我们常可以看到,同样位置的参数,ANSI的API声明为string,而UNICODE的声明为long,像下面这样:
VB code
Private Declare Function SetWindowText Lib "user32.dll" Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal lpString As String) As Long    
Private Declare Function SetWindowText Lib "user32.dll" Alias "SetWindowTextW" (ByVal hwnd As Long, ByVal lpString As Long) As Long  



5、VB6中模拟控件发送通知消息
两种字符集的通知都可以在任意字符集的程序中响应,但前提是控件必须要发出该类型的通知。通常情况下,控件发出通知的类型是与程序使用的字符集相同的。比如,VB6里的TreeView控件发的通知消息应该是A型的,与VB一致;但是如果Treeview是CreateWindowExW创建的,就得用W-type 消息。反之,用A-type。

另外,ahao提到,标准控件大部分都有一个消息,比如,TreeView 是 TVM_SETUNICODEFORMAT 、HeaderCtrl 是 HDM_SETUNICODEFORMAT、ListView 是LVM_SETUNICODEFORMAT。作用就是在运行期改变控件的字符集,不用重新创建控件,如果设置为TRUE,就是发送W版本的Notify消息;如果设置为FALSE;就是发送A版本的Notify消息。

 

6、VarPtr、StrPtr和ObjPtr函数的用法

上面提到,所有指针都一律定义为Long,但是自己要记得,调用该API函数的时候,要通过VarPtr等函数传入指针(注意,对应的实体一定不能被释放掉)。VarPtr/StrPtr/ObjPtr的执行速度非常非常快,因此调用UNICODE函数所造成的系统负担实际上小于调用相对应的ANSI函数,因为前者不需进行转换。

VarPtr:返回变量地址 
StrPtr:返回真正的UNICODE字符串缓冲区的地址 
ObjPtr:返回任何对象变量引用的地址 

6.1 对字符串没用的VarPtr
为了获取变量的地址,只须将变量名传递给VarPtr函数就行了。例如:
VB code
Dim l As Long
Debug.Print VarPtr(l) 


类似地,为了获取字符串的指针,而非保存字符串的变量的指针,只须在变量名前加上ByVal即可。如: