24小时热门版块排行榜    

查看: 1024  |  回复: 1

recen

木虫 (文坛精英)


[资源] 【原创】一点很有用处的教大家使用Unicode版和Ansi版API的学习心得,希望有所帮助

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函数


相应的,凡涉及到字符集的通知事件都有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传递。比如,下面两种声明:


用下面的代码调用  


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

因此,我们常可以看到,同样位置的参数,ANSI的API声明为string,而UNICODE的声明为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函数就行了。例如:


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


在VB3之前,用这种方法来获取字符串缓冲的指针是非常普遍的。但是由于VB6的UNICODE和ANSI字符串的自动变化机制,和当一个字符串传递给VarPtr函数时,函数执行后所返回的地址是保存临时ANSI字符串的临时ANSI字符串或变量的地址。换句话说,这个地址并不是你声明的变量的真正地址。因此,对于字符串变量以及包括字符串的结构来讲,这个函数一点用也没有。

不过,它对字符串也不是完全没用。该函数能与要求包含有UNICODE字符串的结构的API调用一起使用。如果将一个MyUDTVariable变量(一个自定义类型的变量)传递给一个由ByRef UDTParam As MyUDT定义的参数,就会发生ANSI/UNICODE之间的转换。但是,如果将VarPtr(MyUDTVariable)传递给由ByVal UDTParam As Long定义的参数,则不会发生这样的转换。

6.2 StrPtr出现之前的办法:字节数组

如果有些API只有Unicode版本,你必须自己完成字符串的转换工作才能使用。在VB4中,这必须借助于Byte数组。  

1 将 VB6 的字符串转化成 unicode 内码的字节数组  

2 把字节数组传给 unnicode 界面的API  

3 把API处理结果又自行转化为 String。  

例如:


6.3. StrPtr:返回真正的UNICODE字符串缓冲区的地址
如果使用StrPtr,上面的代码精简为:


StrPtr还能用于优化ANSI API函数的调用。在调用时使用StrConv和StrPtr就能避免将一个字符串变量多资传递给函数以及为每个调用而执行转换操作所造成的系统负担。例如


StrPtr还是唯一能直观地告诉你空字符串和null字符串的不同的方法。对于null字符串(vbNullString),StrPtr的返回值为0,而对于空字符串,函数的返回值为非零。

6.4. ObjPtr

该函数返回由对象变量引用的接口指针。由于大多数对象都支持多重接口,因此搞清楚地址对应的是对象的哪一个接口就非常重要了。通常这个函数用于处理放在集合中的对象。通过创建基于对象地址的关键字,你就可以在不需要遍历整个集合中所有元素的情况下,轻松地将对象从集合中删除。在许多情况下,对象地址是唯一可靠的能作为关键字的东西,示例如下:


6.5. VB6中字符串转换常用函数
对于VB的字符串,几个专门“武器”大概有:  


以Asc、AscB、AscW为例,其区别如下。

Asc(string) 返回与字符串的第一个字母对应的 ANSI 字符代码。返回值:英文 >0,中文 <0。

AscB(binstr) 适用于二进制数据。AscB 不是返回第一个字符的字符代码,而是返回首字节。

AscW(string) 是为使用 Unicode 字符的 32 位平台提供的。它返回 Unicode (宽型)字符代码,因此可以避免从 ANSI 到 Unicode 的代码转换。它的返回值与 JavaScript 中的 charCodeAt 相同。返回值:英文 >0,中文 >255

6.6. 指针啊指针

在6.1节里我们提到,如果结构里有字符串,那么最好用varptr传结构的指针过去,以避免无谓的WA转换。不过的话,由于VB里和C里对内存的使用不一样,结构定义得不小心的话,容易导致内存溢出。这又是另一个话题了,在另一篇文章里做笔记吧。



7、小结

(0)在VB6中,用A版API写代码简单(因为编码转换VB自动给做了),用U版API效率要高些(因为有时可以省略不必要地编码转换)。

(1)在VB6中,API调用中,只要是声明为string类型的参数都会自动受到unicode到ansi的转换。

(2)用U版API,对于string型参数,要转化为long,并用StrPrt传递字符串缓冲区指针进去。

(3)用U版API,传结构型参数时,用varptr传进去可避免不必要的编码转换,见6.1节;或者也可以用字节数组的办法,见6.2节

(4)用A版API,用strconv和strptr结合,可提高效率,见6.4节



参考链接:

(1)http://hi.baidu.com/ensteinniese ... f85ccb7cd92a5b.html

(2)http://topic.csdn.net/u/20081223 ... 0-2BC85704BEB8.html

(3)http://topic.csdn.net/u/20090417 ... 1-de5f0360b9fa.html

(4)http://topic.csdn.net/u/20090417 ... 4-c328c24fca03.html

(5)http://www.vbgood.com/vb.good/article-do-view-articleid-2386.html

(6)http://www.cftea.com/c/2007/04/HF4VRNN40DBTU1UR.asp

[ Last edited by recen on 2010-10-27 at 09:48 ]
回复此楼

» 收录本帖的淘帖专辑推荐

五湖四海者

» 猜你喜欢

已阅   回复此楼   关注TA 给TA发消息 送TA红花 TA的回帖

magic7004

金虫 (职业作家)


不同意楼主观点,哥认为能用UNICODE就一定要用UNICODE,因为UNCODE的好处非常多,不止是效率问题,而且用UNICODE一点都不比ANSI麻烦。

ANSI的
Public Declare XXXA (*** As String)
Call XXXA(str) 'str是一个string变量

UNICODE的
Public Declare XXXW(ByVal *** As Long)
Call XXXW(StrPtr(str))
2楼2010-10-27 12:48:32
已阅   回复此楼   关注TA 给TA发消息 送TA红花 TA的回帖
相关版块跳转 我要订阅楼主 recen 的主题更新
☆ 无星级 ★ 一星级 ★★★ 三星级 ★★★★★ 五星级
普通表情 高级回复 (可上传附件)
信息提示
请填处理意见