Win32 API Programming with Visual Basic
作者:Steven Roman
摘自《Win32 API Programming with Visual Basic》。
請聯系:nuts@oreilly.com或http://www.oreilly.com.
前言:
字符串很容易把人搞糊涂,但是只要關心一下細節其實還是可以做到心中有數的。主要的問題是術語“string”在Visual Basic中至少有兩種不同的使用方式。
在Visual Basic什么叫做string?Visual Basic文檔中是這樣說的:
由一組有次序的相鄰的字符組成的相對于數字來說是表達字符性質的一種數據類型。
哈!
好象微軟打算說string類型的根本特征是有限個的字符長度。對Visual Basic來說,所有的字符是以2字節的Unicode形式表述的。(譯者:在Visual Basic里處理字符是以Unicode格式的,不管所處的操作系統是可以以ANSI格式處理字符的Win98還是以Unicode格式的WinNT,但Visual Basic也支持ANSI格式的字符串傳送,例如調用一個需要接收ANSI字符串的API函數時,Visual Basic會自動將其內部以Unicode格式表述的字符串轉換為ANSI格式字符串傳送。ANSI格式的字符是僅以一個字節來表述的字符。)從另一方面來說,Visual Basic使用Unicode格式在字符串中表述字符。例如,ASCII這樣表述字符h:&H68,而Unicode是這樣表述的:&H0068,同時在內存中則是這樣保存:68 00。
本來,字符串“help”在內存中應該保存為這樣:
00 68 00 65 00 6c 00 70
注意,由于字符串在內存中是顛倒保存的,所以,實際上“help“在內存是這樣保存的:
68 00 65 00 6c 00 70 00
好了,我們現在知道在Visual Basic中字符串類string并非我們原先想象的那樣。為了避免任何可能的誤解,我們把string類型理解為Unicode格式的字符數組,而實際上它就是這樣子的。這也有助于我們區別ANSI格式的字符數組。
請仔細閱讀以下幾句話,因為這是理解string類型的關鍵:
當我們寫下這幾句代碼時:
Dim str as String
Str=”help”
本質上來說,我們其實并沒有定義了一個Unicode格式的字符數組。我們其實定義了一個BSTR類型的變量。一個BSTR類型的變量就是一個指向以NULL結尾的,以四個字節為保留字開頭的Unicode格式字符數組的指針。
BSTR 類型:
事實上,在Visual Basic3升級到Visual Basic4的時候, 用Dim語句定義一個string已經經歷了一個根本性的變化。這種變化部份的起因是為了使string類型更好地兼容Win32操作系統。
為了對比一下(也為了顯示我們現在是多么地幸運),圖6-1展示了在Visual Basic3時叫做HLSTR(High-Level String)的Visual Basic string類型數據格式。
Figure 6-1. The high-level string format (HLSTR) used by VB3
這種復雜的HLSTR格式的數據其實是一個指向string descriptor的指針。String descriptor共占用4個字節。2字節描述字符串長度,另外2字節保存指向ANSI格式字符數組的指針。
在調用Win32 API的時候,這種數據類型對程序員來說簡直就是個惡夢。從Visual Basic 4開始,string類型就不同了。新的數據類型叫做BSTR字符串。請參考圖6-2:
Figure 6-2. A BSTR
這種數據類型事實上是以OLE 2.0規范定義的,也就是說,它是Microsoft’s ActiveX規范的一部分。
BSTR字符串有以下幾個要點要強調一下:
1.一個BSTR字符串變量實際上是一個指針變量。它占用32bit即4個字節,就像其它的指針一樣。而且,它指向一個Unicode格式的字符數組。但是,我們不能把字符串與BSTR字符串等同起來。我們必須用它自己確切的名字――“BSTR“。
2.一個BSTR字符串變量指向的字符串數組必須由4個字節的保留字開始(保存字符串數組的字節數而不是字符數),由2個空字符結束。
3.由于空字符在Unicode格式的字符串中的任何位置都有可能出現,所以,以空字符聲明一個Unicode格式的字符串的結束并不合適。因此,在4個保留字中保存字符串的長度是至關重要的。
4.我們再強調一下,BSTR字符串指針實際指向的是Unicode格式的字符數組的首地址,而不是開頭的4個字節。接下來我們就會看到,在這兒這樣不厭其煩地強調BSTR字符串變量的特征是為了與馬上就要解釋的VC++中的string類型作個比較。
5.圖示中的4個字節的length記錄的是字符數組的字節數(注意,不是字符數),包括結尾的空字節。因為數組是Unicode格式的,所以實際字符是length的一半。
在這兒強調一下,一個Unicode格式的空字符其實是占用2個字節的空間,而不是1個字節。在Unicode格式的數組中測試空字符時要當心這一點。
我們一般在慣例上說BSTR字符串“help”是“一個BSTR類型的字符串”。一般公認為,一個BSTR字符串變量指向的一個字符數組中包括至少兩個空字符。
就Visual Basic來說,BSTR字符串未尾的兩個空字節可能沒什么用處,但是對Win32來說,它們卻是至關重要的。原因在于,Win32版本的Unicode String(它稱為LPWSTR)定義為指向一個空字符結尾的Unicode格式的字符串的指針。
從這個原因上解釋BSTR字符串為什么要以空字符結尾就合情合理了。下面讓我們討論一下C++類型的string變量。
還是剛才的那兩句代碼:
Dim str as String
str=”help”
str表示的是一個BSTR字符串變量的名字,而不是一個Unicode格式的字符數組。換句話說,str是一個保存地址xxxx(參見圖6-2)的變量的名字。
以下是個小小的實驗,它表明Visual Basic中string變量是指向字符數組的指針而不是字符數組。下面定義了一個結構,它的成員變量的類型是string。
Private Tyep utTest
astring As String
bstring As String
End Type
Dim uTest As utTest
Dim s as String
s=”testing”
uTest.astring=”testing”
uTest.astring=”testing”
Debug.Print Len(s)
Debug.Print Len(uTest)
這幾句代碼的執行結果是:
7 8
對string變量來說,Len函數返回的是字符串數組的字符個數。所以7個字符的字符串“testing”返回7。對結構變量uTest來說,Len函數返回的是該結構占用的內存空間。所以返回值8清楚地表明了每一個BSTR變量在內存中占用4個字節。因為BSTR是一個Win32的指針!
C類型的LPSTR和LPWSTR 字符串
Visual C++使用LPSTR和LPWSTR字符串。
LPSTR類型字符串的定義是:指向一個空字節結尾的ANSI格式的字符串數組的指針。但是,因為我們是以空字節的位置來判斷LPSTR字符串的終止的,所以,在LPSTR中是不允許字符串中還有第二個空字節存在。同樣,LPWSTR是一個指向空字節終止的Unicode格式的字符串的指針,它的中間也不允許有空字節存在。LPWSTR中的W指Wide,它是微軟對Unicode的另一種說法。LPWSTR如圖6-3所示。
Fig. 6-3.LPSTR and LPWSTR data types
可能我們也會碰到LPCSTR和LPCWSTR類型的字符串。其中的C表示Constant(常量)。這種字符串是不能被API函數修改的。除此以外,LPCSTR都與LPSTR相同。同理,LPCWSTR除不能修改外,其它的都與LPWSTR相同。
再說LPTSTR,LPTSTR一般都用在條件編譯中,就象TCHAR一樣。以下是一個例子代碼:
#ifdef UNICODE
typedef LPWSTR LPTSTR; // 在Unicode下LPTSTR與LPWSTR是相同的
typedef LPCWSTR LPCTSTR; // 在Unicode下LPCTSTR與LPCWSTR是相同的
#else typedef LPSTR LPTSTR; //在ANSI下LPTSTR與LPSTR是相同的
typedef LPCSTR LPCTSTR; //在ANSI下LPTCSTR與LPCSTR是相同的
#endif
這幾種類型的圖解如下:
Figure 6-4.The LP... STR mess.
MILY: 'Times New Roman'; FONT-SIZE: 10.5pt; mso-bidi-font-size: 12.0pt; mso-fareast-font-family: 宋體; mso-font-kerning: 1.0pt; mso-ansi-language: EN-US; mso-fareast-language: ZH-CN; mso-bidi-language: AR-SA">
有關String這個術語
為了避免任何可能的誤解,以下我們僅使用術語BSTR、Unicode字符數組和ANSI字符數組,我們會盡量避開使用string這個術語。如果必須使用String,我們會把它改為Vusual Basic String(就是BSTR)或Visual C++ String(就是以上說過的LP??STR)。
然而,在Vusual Basic文檔里,你會經?吹絊tring這個字,至于它是以上所說的三種字符串中的哪一種,就要靠你自己的去判斷了。
研究String的工具
如果我們想繼續研究String,那么我們還需要一些工具。讓我們來看看以下幾個工具。
Visual Basic的StrConv函數:
StrConv函數是用來轉變字符數組的格式的函數。它的語法為:
StrConv(string,conversion,LCID)
其中的String指的是一個BSTR類型的字符串,Conversion是一個常量(接下來馬上就要介紹),而LCID是一個可選的標識符(在此我們忽略它)。
我們感興趣的Conversion參數是以下兩個:
VbUnicode(其實叫VbToUnicode更合適)
VbFromUnicode
這兩個參數將BSTR字符數組轉換為Unicode格式或ANSI格式。
但是現在我們有個麻煩。我們沒有一個ANSI BSTR類型的字符串。定義中已經說過,BSTR字符數組指向的是Unicode格式的字符數組。
但我們還是可以想象一下,一個ANSI BSTR的字符串應該是怎樣的。如同在圖6-2中描述的一樣,只須將Unicode格式的字符數組用ANSI格式的字符數組代替就是了。
現在我們說StrConv至少有兩種合法的形式:
StrConv(an_ANSI_BSTR,vbFromUnicode)
StrConv(an_BSTR,vbUnicode)具有諷刺意思的是,在第一種情況的時候,Visual Basic居然不認識自己的函數的返回值!請看下面的代碼:
s = "help"
Debug.Print s
Debug.Print StrConv(s, vbFromUnicode)
其輸出居然是:
help
??
原因是Visual Basic試圖把ANSI BSTR字符串解釋為BSTR字符串。請再看下面的代碼:
s = "h" & vbNullChar & "e" & vbNullChar & "l" & vbNullChar & "p" & vbNullChar
Debug.Print s
Debug.Print StrConv(s, vbFromUnicode)
輸出結果是:
h e l p
help
在這兒我們為了讓StrConv正常工作,而仿照Unicode格式填充一個字符串數組并傳遞給Visual Basic讓它處理。而這種結果是:一個ANSI BSTR字符串于是有了一個合法的BSTR的解釋。
這表明,StrConv函數并不真正理解或關心傳過來的字符串究竟是BSTR還是ANSI BSTR。它只是設想無論如何你只要傳遞給它一個字符串指針就成了,它的任務只是盲目地去轉換這個字符數組而已。我們以后會發現,很多其它的字符串函數都是這樣。這就是說,它們可以接收一個BSTR或是一個ANSI BSTR字符指針,只要這個指針指向的是空字節結尾的字符數組就行了。
Len和LenB函數
Visual Basic有兩個返回字符串長度的函數:Len和LenB。它們都可以接收一個BSTR或是一個ANSI BSTR,并且返回一個長整型數值。以下的代碼說明了一切:
s = "help"
Debug.Print Len(s), LenB(s)
Debug.Print Len(StrConv(s, vbFromUnicode)), LenB(StrConv(s, vbFromUnicode))
輸出結果是:
4 8
2 4
這表明,Len返回的是字符數,而LenB返回的是在BSTR中的字節數。
Chr,ChrB和ChrW函數
這幾個函數的輸入參數和輸出結果都不相同。剛接觸它們時,你可能會感到無所適從。對此,我的建議是:把它們的定義多讀幾遍。
Chr函數接收一個在0到255之間的長整型的整數,返回一個長度為1的BSTR類型字符串。此處,BSTR指向的字符是Unicode格式的。所以,雖然長度為1,但實際占用2字節。從最新的Visual Basic的文檔來看,Chr和Chr$沒有區別。
ChrB函數接收一個在0到255之間的長整型的整數,但它的返回值是一個長度為1字節的ANSI BSTR類型的字符串。該字符串是ANSI格式,所以占用1個字節。
ChrW函數接收一個在0到255之間的長整型的整數,返回值是一個長度為1的BSTR的字符串。該字符串是Unicode格式,所以占用2個字節。
Asc,AscB和AscW函數
這些函數就是Chr的反函數。其中,AscB接收一個ANSI BSTR類型的字符串,并返回一個Byte型的該字符串第一個字符的ASCII代碼。為了證明返回值確是Byte類型的數據,請看下面的代碼:
Debug.Print VarType(AscB("h")) = vbByte
輸出結果是True。從字面上來看,可能你會認為AscB會接收一個BSTR類型的字符,但是,實際上它只認這個BSTR字符的第一個字節,其它的字節就被忽略掉了。
Asc函數接收一個BSTR類型的字符串(不是ANSI BSTR),并返回一個該字符串第一個字符的Unicode代碼。
Null字符串和Null字符
Visual Basic允許Null值的BSTR類型字符串。請看下面代碼:
Dim s As String
s = vbNullString
Debug.Print VarPtr(s) ‘有關這兩個函數,接下來馬上介紹。
Debug.Print StrPtr(s)
輸出結果是:
1243948
0
這說明,一個Null值的BSTR字符串僅僅是一個指向內容為0的字符指針。在Win32和Visual C++中,這種字符串叫做空指針。讓我們再來看看vbNullString和vbNullChar的區別。vbNullChar并不是指針,它是一個值為0的Unicode字符。
請大家不要把一個值為Null的BSTR和一個空BSTR搞混了。請看下面代碼:
Dim s As String
Dim t As String
s = vbNullString
t = ""
空BSTR字符串t是一個指向非空內存地址的指針。在那個地址保存的是空BSTR字符串的終止符。而且,它前面的那4個保留字節中保存的該字符串的長度信息為0。
VarPtr和StrPtr函數
在微軟的文檔里并沒有VarPtr和StrPtr函數的描述,但它們是非常有用的。特別是VarPtr函數。
請看,如果參數var為有效變量的話,那么,
VarPtr(var)
返回的是變量的長整型地址。如果str是一個BSTR字符串變量,那么:
StrPtr(str)
返回的是BSTR字符串指針指向的Unicode字符串的首地址。
讓我們看下圖:
Figure 6-5. A BSTR
如果代碼如下:
Dim str As String
str = "help"
注意,變量str在內存中的地址是aaaa,str保存的內容是字符串的首地址xxxx.。
請看:
VarPtr = aaaa
StrPtr = xxxx
請再運行以下程序:
Dim lng As Long
Dim i As Integer
Dim s As String
Dim b(1 To 10) As Byte
Dim sp As Long, vp As Long
s = "help"
sp = StrPtr(s ) ’sp是字符串首地址即字符h在內存中的地址xxxx
Debug.Print "StrPtr:" & sp
vp = VarPtr(s) ‘vp是sp的保存的內容xxxx在內存中的地址aaaa
Debug.Print "VarPtr:" & vp
CopyMemory lng, ByVal vp, 4 ‘將長整型指針vp指向的內容xxxx拷貝到長整型變量lng,并作比較。
Debug.Print lng = sp
CopyMemory b(1), ByVal sp, 10 ‘將sp保存的地址的實際內容拷貝給byte型數組b(),并輸出。
For i = 1 To 10
Debug.Print b(i)
Next
輸出結果為:
StrPtr:1836612
VarPtr:1243988
True
104 0 101 0 108 0 112 0 0 0
在此我們清楚地看到,在內存中BSTR類型的字符串是以Unicode格式保存的。
請在這幾行代碼:
sp = StrPtr(s)
Debug.Print "StrPtr:" & sp
后加入以下幾行代碼:
Dim ct As Long
CopyMemory ct, ByVal sp - 4, 4
Debug.Print "Length field: " & ct
運行后可以得到這條輸出結果:
Length field: 8
在此我們清楚地看到,這四個字節的保留字保存的是字符串的字節數,而不是字符數。
文章來源于領測軟件測試網 http://www.kjueaiud.com/
版權所有(C) 2003-2010 TestAge(領測軟件測試網)|領測國際科技(北京)有限公司|軟件測試工程師培訓網 All Rights Reserved
北京市海淀區中關村南大街9號北京理工科技大廈1402室 京ICP備10010545號-5
技術支持和業務聯系:info@testage.com.cn 電話:010-51297073
老湿亚洲永久精品ww47香蕉图片_日韩欧美中文字幕北美法律_国产AV永久无码天堂影院_久久婷婷综合色丁香五月