VB調(diào)用動(dòng)態(tài)鏈接庫(DLL)_第1頁
VB調(diào)用動(dòng)態(tài)鏈接庫(DLL)_第2頁
VB調(diào)用動(dòng)態(tài)鏈接庫(DLL)_第3頁
VB調(diào)用動(dòng)態(tài)鏈接庫(DLL)_第4頁
VB調(diào)用動(dòng)態(tài)鏈接庫(DLL)_第5頁
已閱讀5頁,還剩22頁未讀, 繼續(xù)免費(fèi)閱讀

下載本文檔

版權(quán)說明:本文檔由用戶提供并上傳,收益歸屬內(nèi)容提供方,若內(nèi)容存在侵權(quán),請(qǐng)進(jìn)行舉報(bào)或認(rèn)領(lǐng)

文檔簡介

1、VB調(diào)用動(dòng)態(tài)鏈接庫(DLL)作為一種簡單易用的Windows開發(fā)環(huán)境,VisualBasic從一推出就受到了廣大編程人員的歡迎。它使程序員不必再直接面對(duì)紛繁復(fù)雜的Windows消息,而可以將精力主要集中在程序功能的實(shí)現(xiàn)上,大大提高了編程效率。但凡事有利必有弊。VB中高度的封裝和模塊化減輕了編程者的負(fù)擔(dān),同時(shí)也使開發(fā)人員失去了許多訪問低層API函數(shù)和直接與Windows交互的機(jī)會(huì)。因此,相比而言,VB應(yīng)用程序的執(zhí)行效率和功能比C/C+或Delphi生成的程序要差。為了解決這個(gè)問題,在一個(gè)大型的VB開發(fā)應(yīng)用中,直接調(diào)用WindowsAPI函數(shù)幾乎是不可避免的;同時(shí),還有可能需要程序員自己用C/C+

2、等開發(fā)一些動(dòng)態(tài)連接庫,用于在VB中調(diào)用。本文主要討論在32位開發(fā)環(huán)境VisualBasic5.0中直接調(diào)用Windows95API函數(shù)或用戶生成的32位動(dòng)態(tài)連接庫的方法與規(guī)則。Windows動(dòng)態(tài)連接庫是包含數(shù)據(jù)和函數(shù)的模塊,可以被其它可執(zhí)行文件(EXE、DLL、OCX等)調(diào)用。動(dòng)態(tài)連接庫包含兩種函數(shù):輸出(exported)函數(shù)和內(nèi)部(internal)函數(shù)。輸出函數(shù)可以被其它模塊調(diào)用,而內(nèi)部函數(shù)則只能在動(dòng)態(tài)連接庫內(nèi)部使用。盡管動(dòng)態(tài)連接庫也能輸出數(shù)據(jù),但實(shí)際上它的數(shù)據(jù)通常是只在內(nèi)部使用的。使用動(dòng)態(tài)連接庫的優(yōu)點(diǎn)是顯而易見的。將應(yīng)用程序的一部分功能提取出來做成動(dòng)態(tài)連接庫,不但減小了主應(yīng)用程序的大小

3、,提高了程序運(yùn)行效率,還使它更加易于升級(jí)。多個(gè)應(yīng)用程序共享一個(gè)動(dòng)態(tài)連接庫還能有效地節(jié)省系統(tǒng)資源。正因?yàn)槿绱?,在Windows系統(tǒng)中,動(dòng)態(tài)連接庫得到了大量的使用。一般來說,動(dòng)態(tài)連接庫都是以DLL為擴(kuò)展名的文件,如Kernel32.dll、commdlg.dll等。但也有例外,如16位Windows的核心部件之一GDI.exe其實(shí)也是一個(gè)動(dòng)態(tài)庫。編寫動(dòng)態(tài)連接庫的工具很多,如VisualC+、BorlandC+、Delphi等,具體方法可以參見相關(guān)文檔。下面只以VisualC+5.0為例,介紹一下開發(fā)應(yīng)用于VisualBasic5.0的動(dòng)態(tài)連接庫時(shí)應(yīng)注意的問題(本文中所有涉及C/C+語言或編譯環(huán)境的

4、地方,都以VC5為例;所有涉及VisualBasic的地方都以VB5為例)。作為一種32位Windows應(yīng)用程序的開發(fā)工具,VB5生成的exe文件自然也都是32位的,通常情況下也只能調(diào)用32位的動(dòng)態(tài)連接庫。但是,并不是所有的32位動(dòng)態(tài)庫都能被VB生成的exe文件正確地識(shí)別。一般來說,自己編寫用于VB應(yīng)用程序調(diào)用的動(dòng)態(tài)連接庫時(shí),應(yīng)注意以下幾個(gè)方面的問題:1、生成動(dòng)態(tài)庫時(shí)要使用_stdcall調(diào)用約定,而不能使用缺省的_cdecl調(diào)用約定;_stdcall約定通常用于32位API函數(shù)的調(diào)用2、在VC5中的定義文件(.def)中,必須列出輸出函數(shù)的函數(shù)名,以強(qiáng)制VC5系統(tǒng)將輸出函數(shù)的裝飾名(deco

5、ratedname)改成普通函數(shù)名;所謂裝飾名是VC的編譯器在編譯過程中生成的輸出函數(shù)名,它包含了用戶定義的函數(shù)名、函數(shù)參數(shù)及函數(shù)所在的類等多方面的信息。由于在VC5中定義文件不是必需的,因此工程不包含定義文件時(shí)VC5就按自己的約定將用戶定義的輸出函數(shù)名修改成裝飾名后放到輸出函數(shù)列表中,這樣的輸出函數(shù)在VB生成的應(yīng)用程序中是不能正確調(diào)用的(除非聲明時(shí)使用Alias子句)。因此需要增加一個(gè).def文件,其中列出用戶需要的函數(shù)名,以強(qiáng)制VC5不按裝飾名進(jìn)行輸出。3、VC5中的編譯選項(xiàng)結(jié)構(gòu)成員對(duì)齊方式(structurememberalignment)應(yīng)設(shè)成4字節(jié),其原因?qū)⒃诤笪脑敿?xì)介紹。4、由于在

6、C中整型變量是4個(gè)字節(jié),而VB中的整型變量依然只有2個(gè)字節(jié),因此在C中聲明的整型(int)變量在VB中調(diào)用時(shí)要聲明為長整型(long),而C中的短整型(short)在VB中則要聲明成整型(integer);下表針對(duì)最常用的C語言數(shù)據(jù)類型列出了與之等價(jià)的VisualBasic類型(用于32位版本的Windows)。C語言數(shù)據(jù)類型在VisualBasic中聲明為調(diào)用時(shí)使用的表達(dá)式ATOMByValvariableAsInteger結(jié)果為Integer類型的表達(dá)式BOOLByValvariableAsLong結(jié)果為Long類型的表達(dá)式BYTEByValvariableAsByte結(jié)果為Byte類型的

7、表達(dá)式CHARByValvariableAsByte結(jié)果為Byte類型的表達(dá)式COLORREFByValvariableAsLong結(jié)果為Long類型的表達(dá)式DWORDByValvariableAsLong結(jié)果為Long類型的表達(dá)式HWND,HDC,HMENUByValvariableAsLong結(jié)果為Long類型的表達(dá)式等Windows句柄INT,UINTByValvariableAsLong結(jié)果為Long類型的表達(dá)式LONGByValvariableAsLong結(jié)果為Long類型的表達(dá)式LPARAMByValvariableAsLong結(jié)果為Long類型的表達(dá)式LPDWORDvariabl

8、eAsLong結(jié)果為Long類型的表達(dá)式LPINT,LPUINTvariableAsLong結(jié)果為Long類型的表達(dá)式LPRECTvariableAstype自定義類型的任意變量LPSTR,LPCSTRByValvariableAsString結(jié)果為String類型的表達(dá)式LPVOIDvariableAsAny任何變量(在傳遞字符串的時(shí)候使用ByVal)LPWORDvariableAsInteger結(jié)果為Integer類型的表達(dá)式LRESULTByValvariableAsLong結(jié)果為Long類型的表達(dá)式NULLAsAny或ByValNothing或ByValvariableAsLongBy

9、Val0&或VBNullStringSHORTByValvariableAsInteger結(jié)果為Integer類型的表達(dá)式VOIDSubprocedure不可用WORDByValvariableAsInteger結(jié)果為Integer類型的表達(dá)式WPARAMByValvariableAsLong結(jié)果為Long類型的表達(dá)式5、VB中進(jìn)行32位動(dòng)態(tài)庫的聲明時(shí),函數(shù)名是大小寫敏感的。在獲得了需要的動(dòng)態(tài)連接庫之后,就可以在VB中進(jìn)行調(diào)用了。但是,由于VB不能驗(yàn)證應(yīng)用程序傳遞到動(dòng)態(tài)連接庫中的參數(shù)值是否正確,因此VB程序中大量的API調(diào)用可能會(huì)降低整個(gè)應(yīng)用程序的穩(wěn)定性,也會(huì)增加以后維護(hù)的難度。所以,

10、決定在VB程序中直接調(diào)用API函數(shù)時(shí)要慎重,但適當(dāng)?shù)氖褂肁PI調(diào)用確實(shí)能夠有效地提高VB程序的性能。這之間的平衡需要編程人員根據(jù)實(shí)際情況來掌握。下面就具體介紹一下在VB中調(diào)用API函數(shù)時(shí)需要做的工作。要聲明一個(gè)DLL過程,首先需要在代碼窗口的通用(General)部分增加一個(gè)Declare語句。如果該過程返回一個(gè)值,應(yīng)將其聲明為Function:DeclareFunctionpublicnameLiblibnameAliasalias(ByValvariableAstype,ByValvariableAstype.)AsType如果過程沒有返回值,可將其聲明為Sub:DeclareSubpub

11、licnameLiblibnameAliasalias(ByValvariableAstype,ByValvariableAstype.)缺省情況下,在標(biāo)準(zhǔn)模塊中聲明的DLL過程,可以在應(yīng)用程序的任何地方調(diào)用它。在其它類型的模塊中定義的DLL過程則是模塊私有的,必須在它們前面聲明Private關(guān)鍵字,以示區(qū)分。下面分別介紹聲明語句的各個(gè)組成部分。(一) 、指定動(dòng)態(tài)庫:Declare語句中的Lib子句用來告訴VisualBasic如何找到包含過程的.dll文件。如果引用的過程屬于Windows核心庫(User32、Kernel32或GDI32),則可以不包含文件擴(kuò)展名,如:DeclareFunc

12、tionGetTickCountLibkernel32AliasGetTickCount()AsLong對(duì)于其它動(dòng)態(tài)連接庫,可以在Lib子句指定文件的路徑:DeclareFunctionlzCopyLibc:windowslzexpand.dll_(ByValSAsInteger,ByValDAsInteger)AsLong如果未指定libname的路徑,VisualBasic將按照下列順序查找該文件: .exe文件所在的目錄 當(dāng)前目錄 Windows系統(tǒng)目錄 Windows目錄 Path環(huán)境變量中的目錄下表中列出了常用的操作系統(tǒng)環(huán)境庫文件。動(dòng)態(tài)鏈接庫描述Advapi32.dll高級(jí)API服務(wù)

13、,支持大量的API(其中包括許多安全與注冊(cè)方面的調(diào)用)Comdlg32.dll通用對(duì)話框API庫Gdi32.dll圖形設(shè)備接口API庫Kernel32.dllWindows32位核心的API支持Lz32.dll32位壓縮例程Mpr.dll多接口路由器庫Netapi32.dll32位網(wǎng)絡(luò)API庫Shell32.dll32位ShellAPI庫User32.dll用戶接口例程庫Version.dll版本庫Winmm.dllWindows多媒體庫Winspool.drv后臺(tái)打印接口,包含后臺(tái)打印API調(diào)用。對(duì)于Windows的系統(tǒng)API函數(shù),可以利用VB提供的工具APIViewer查找某一函數(shù)及其相關(guān)

14、數(shù)據(jù)結(jié)構(gòu)和常數(shù)的聲明,并復(fù)制到自己的程序中。(二)、使用別名:Declare語句中的Alias子句是一個(gè)可選的部分,用戶可以通過它所標(biāo)識(shí)的別名對(duì)動(dòng)態(tài)庫中的函數(shù)進(jìn)行引用。例如,在下面的語句中,聲明了一個(gè)在VB中名為MyFunction的函數(shù),而它在動(dòng)態(tài)庫Mydll.dll中最初的名字是MyFunctionX。PrivateDeclareFunctionMyFunctionLibMydll.dll_AliasMyFunctionX()AsLong需要注意的是,Alias子句中的函數(shù)名是大小寫敏感的,也就是說,必須與函數(shù)在生成時(shí)的聲明(如在C源文件中的聲明)一致。這是因?yàn)?2位動(dòng)態(tài)庫與16位動(dòng)態(tài)庫不

15、同,其中的函數(shù)名是區(qū)分大小寫的。同樣道理,如果沒有使用Alias子句,那么在Function(或Sub)后的函數(shù)名也是區(qū)分大小寫的。通常在以下幾種情況時(shí)需要使用Alias子句:A. 處理使用字符串的系統(tǒng)WindowsAPI過程如果調(diào)用的系統(tǒng)WindowsAPI過程要使用字符串,那么聲明語句中必須增加一個(gè)Alias子句,以指定正確的字符集。包含字符串的系統(tǒng)WindowsAPI函數(shù)實(shí)際有兩種格式:ANSI和Unicode(關(guān)于ANSI和Unicode兩種字符集的區(qū)別將在后面詳細(xì)闡述)。因此,在Windows頭文件中,每個(gè)包含字符串的函數(shù)都同時(shí)有ANSI版本和Unicode版本。例如,下面是SetW

16、indowText函數(shù)的兩種C語言描述??梢钥吹?,第一個(gè)描述將函數(shù)定義為SetWindowTextA,尾部的A表明它是一個(gè)ANSI函數(shù):WINUSERAPIBOOLWINAPISetWindowTextA(HWNDhWnd,LPCSTRlpString);第二個(gè)描述將它定義為SetWindowTextW,尾部的W表明它是一個(gè)Unicode函數(shù):WINUSERAPIBOOLWINAPISetWindowTextW(HWNDhWnd,LPCWSTRlpString);因?yàn)閮蓚€(gè)函數(shù)實(shí)際的名稱都不是SetWindowText,要引用正確的函數(shù)就必須增加一個(gè)Alias子句:PrivateDeclareF

17、unctionSetWindowTextLibuser32_AliasSetWindowTextA(ByValhwndAsLong,ByVal_lpStringAsString)AsLong應(yīng)當(dāng)注意,對(duì)于VB中使用的系統(tǒng)WindowsAPI函數(shù),應(yīng)該指定函數(shù)的ANSI版本,因?yàn)橹挥蠾indowsNT才支持Unicode版本,而Windows95不支持這個(gè)版本。僅當(dāng)應(yīng)用程序只運(yùn)行在WindowsNT平臺(tái)上的時(shí)候才可以使用Unicode版本。B. 函數(shù)名是不標(biāo)準(zhǔn)的名稱有時(shí),個(gè)別的DLL過程的名稱不是有效的標(biāo)識(shí)符。例如,它可能包含了非法的字符(如連字符),或者名稱是VB的關(guān)鍵字(如GetObject

18、)。在這種情況下,可以使用Alias關(guān)鍵字。例如,操作環(huán)境DLLs中的某些過程名以下劃線開始。盡管在VB標(biāo)識(shí)符中允許使用標(biāo)識(shí)符,但是下劃線不能作為標(biāo)識(shí)符的第一個(gè)字符。為了使用這種過程,必須先聲明一個(gè)名稱合法的過程,然后用Alias子句引用過程的真實(shí)名稱:DeclareFunctionlopenLibkernel32Alias_lopen_(ByVallpPathNameAsString,ByValiReadWrite_AsLong)AsLong在上例中,lopen是VB中使用的過程名稱。而_lopen則是動(dòng)態(tài)連接庫中可以識(shí)別的名稱。C. 使用序號(hào)標(biāo)識(shí)DLL過程除了使用名稱之外,還可以使用序號(hào)來

19、標(biāo)識(shí)DLL過程。某些動(dòng)態(tài)連接庫中不包含過程的名稱,在聲明它們包含的過程時(shí)必須使用序號(hào)。同使用名稱標(biāo)識(shí)的DLL過程相比,如果使用序號(hào),在最終的應(yīng)用程序中消耗的內(nèi)存將比較少,而且速度會(huì)快些。但是,一個(gè)具體的API的序號(hào)在不同的操作系統(tǒng)中可能是不同的。例如GetWindowsDirectory在Win95下的序號(hào)為432,而在WindowsNT4.0下為338??偠灾?,如果希望應(yīng)用程序能夠在不同的操作系統(tǒng)下運(yùn)行,那么最好不要使用序號(hào)來標(biāo)識(shí)API過程。如果過程不屬于API,或者應(yīng)用程序使用的范圍很有限,那么使用序號(hào)還是有好處的。要使用序號(hào)來聲明DLL過程,Alias子句中的字符串需要包含過程的序號(hào),

20、并在序號(hào)的前面加一個(gè)數(shù)字標(biāo)記字符(#)。例如,Windowskernel中的GetWindowsDirectory函數(shù)的序號(hào)為432;可以用下面的語句來聲明該DLL過程:DeclareFunctionGetWindowsDirectoryLibkernel32_Alias#432(ByVallpBufferAsString,_ByValnSizeAsLong)AsLong在這里,可以使用任意的合法名稱作為過程的名稱,VB將用序號(hào)在DLL中尋找過程。為了得到要聲明的過程的序號(hào),可以使用Dumpbin.exe等實(shí)用工具(Dumpbin.exe是MicrosoftVisualC+提供的一個(gè)實(shí)用工具,

21、它的使用說明可以參見VC的文檔)。利用Dumpbin,可以提取出.dll文件中的各種信息,例如DLL中的函數(shù)列表,它們的序號(hào)以及與代碼有關(guān)的其它信息。(三)、使用值或引用傳遞在缺省的情況下,VB以引用方式傳遞所有參數(shù)(ByRef)。這意味著并沒有傳遞實(shí)際的參數(shù)值,VB只傳遞了數(shù)據(jù)的32位地址。另外有許多DLL過程要求參數(shù)以值方式傳遞(ByVai)。這意味著它們需要實(shí)際的數(shù)據(jù),而不是數(shù)據(jù)的內(nèi)存地址。如果過程需要一個(gè)傳值參數(shù),而傳遞給它的參數(shù)是一個(gè)指針,那么由于得到了錯(cuò)誤的數(shù)據(jù),該過程將不能正確地工作。要使參數(shù)以使用值方式傳遞,在Deciare語句中需要在參數(shù)聲明的前面加上ByVai關(guān)鍵字。例如I

22、nvertRect過程要求第一個(gè)參數(shù)用傳值方式傳遞,而第二個(gè)用引用方式傳遞:DeclareFunctionInvertRectLibuser32Alias_InvertRectA(ByValhdcAsLong,lpRectAsRECT)AsLong動(dòng)態(tài)連接庫的參數(shù)傳遞是一個(gè)復(fù)雜的問題,也是VB中調(diào)用動(dòng)態(tài)連接庫時(shí)最容易出現(xiàn)錯(cuò)誤的地方。參數(shù)類型或傳遞方式的聲明錯(cuò)誤都可能導(dǎo)致應(yīng)用程序出現(xiàn)GPF(通用保護(hù)錯(cuò)誤),甚至使操作系統(tǒng)崩潰,因此我們將在后面專門詳細(xì)地討論這個(gè)問題。(四) 、靈活的參數(shù)類型某些DLL過程的同一個(gè)參數(shù)能夠接受多種數(shù)據(jù)類型。如果需要傳遞多種類型的數(shù)據(jù),可以將參數(shù)聲明為AsAny,從而

23、取消類型限制。例如,下面的聲明中的第三個(gè)參數(shù)(lpptAsAny)既可以傳遞一個(gè)POINT結(jié)構(gòu)的數(shù)組,也可以傳遞一個(gè)RECT結(jié)構(gòu):DeclareFunctionMapWindowPointsLibuser32Alias_MapWindowPoints(ByValhwndFromAsLong,_ByValhwndToAsLong,lpptAsAny,_ByValcPointsAsLong)AsLongAsAny子句提供了一定的靈活性,但是,由于它不進(jìn)行任何的類型檢查,風(fēng)險(xiǎn)也隨之增加。因此在使用AsAny子句時(shí),必須仔細(xì)檢查所有參數(shù)的類型。正確的函數(shù)聲明是在VB中調(diào)用動(dòng)態(tài)連接庫的前提,但要想在VB

24、中用對(duì)、用好動(dòng)態(tài)庫中的函數(shù),僅僅有聲明還是遠(yuǎn)遠(yuǎn)不夠的。前面已經(jīng)說過,由于VB不能驗(yàn)證應(yīng)用程序傳遞到動(dòng)態(tài)連接庫中的參數(shù)值是否正確,因此就要求程序員應(yīng)對(duì)參數(shù)類型有非常詳細(xì)的了解,否則很容易引起應(yīng)用程序發(fā)生通用保護(hù)錯(cuò)或?qū)е聺撛诘腂ug,降低軟件的可靠性。下面將參數(shù)類型分為簡單數(shù)據(jù)類型、字符串、和用戶自定義類型三種分別進(jìn)行討論。(1)、簡單數(shù)據(jù)類型:簡單數(shù)據(jù)類型是指Numeric數(shù)據(jù)類型(包括Integer、Long、Single、Double、Currency類型)、Byte數(shù)據(jù)類型和Boolean數(shù)據(jù)類型。它們的共同的特點(diǎn)是結(jié)構(gòu)簡單,操作系統(tǒng)在處理時(shí)不必進(jìn)行特殊的轉(zhuǎn)換。簡單數(shù)據(jù)類型參數(shù)的傳遞比較簡

25、單。我們知道,在VB中傳遞參數(shù)的方式有兩種:傳值(Byval)和傳址(ByRef),缺省的方式是傳址。所謂傳值,就是對(duì)一個(gè)變量的具體值進(jìn)行傳遞;而傳址則是傳遞變量的地址。例如,在VB程序中需要將一個(gè)整型變量m=10的值傳進(jìn)動(dòng)態(tài)庫,如果用傳值方式,那么傳進(jìn)動(dòng)態(tài)庫的值就是10,而在傳址方式下,傳入的則是變量m的地址,相當(dāng)于C/C+中&m的值。需要注意的是,以傳值方式傳進(jìn)動(dòng)態(tài)連接庫的變量,其值在動(dòng)態(tài)庫中是不能被改變的;如果需要在動(dòng)態(tài)連接庫中修改傳入?yún)?shù)的值,則必須使用傳址方式。一般來說,在VB和動(dòng)態(tài)連接庫之間傳遞單個(gè)的簡單數(shù)據(jù)類型,只要注意了以上幾個(gè)方面就可以了。當(dāng)需要將一個(gè)簡單數(shù)據(jù)類型的整

26、個(gè)數(shù)組傳進(jìn)動(dòng)態(tài)庫時(shí),必須將相應(yīng)參數(shù)聲明為傳址方式,然后把數(shù)組的第一個(gè)元素作為參數(shù)傳入,這樣在動(dòng)態(tài)連接庫中就得到了數(shù)組的首地址,從而可以對(duì)整個(gè)數(shù)組進(jìn)行訪問。例如,聲明了一個(gè)名為ReadArray的DLL過程,要求傳入一個(gè)整型數(shù)組aArray:DeclareFunctionReadArrayLibmydll.dll_(aArrayAsInteger)AsInteger在調(diào)用時(shí)可以采用如下方式:Dimret,I(5)asIntegerret=ReadArray(I(0)注釋:將整個(gè)數(shù)組傳入動(dòng)態(tài)連接庫(2)、字符串參數(shù)的傳遞:與簡單數(shù)據(jù)類型相比,字符串類型(String、String*n)的參數(shù)傳遞要

27、復(fù)雜得多,這主要是Windows98API和VB使用的字符串類型不同的緣故。VB使用被稱為BSTR的String數(shù)據(jù)類型,它是由自動(dòng)化(以前被稱為OLEAutomation)定義的數(shù)據(jù)類型。一個(gè)BSTR由頭部和字符串組成,頭部包含了字符串的長度信息,字符串中可以包含嵌入的null值。大部分的BSTR是Unicode的,即每個(gè)字符需要兩個(gè)字節(jié)。BSTR通常以兩字節(jié)的兩個(gè)null字符結(jié)束。下圖表示了一個(gè)BSTR類型的字符串FunctionGetCharByte(ByValOneCharAsInteger,ByValIsHighByteAsBoolean)AsByte注釋:該函數(shù)獲得一個(gè)字符的高字節(jié)

28、或低字節(jié)IfIsHighByteThenIfOneChar>=0ThenGetCharByte=CByte(OneChar256)注釋:右移8位,得到高字節(jié)ElseGetCharByte=CByte(OneCharAnd&H7FFF)256)Or&H80EndIfExitFunctionElseGetCharByte=CByte(OneCharAnd&HFF)注釋:屏蔽掉高字節(jié),得到低字節(jié)ExitFunctionEndIfEndFunctionSubStrToByte(StrToChangeAsString,ByteArray()AsByte)注釋:該函數(shù)將一個(gè)字

29、符串轉(zhuǎn)換成字節(jié)數(shù)組DimLowBound,UpBoundAsIntegerDimi,count,lengthAsIntegerDimOneCharAsIntegercount=0length=Len(StrToChange)LowBound=LBound(ByteArray)UpBound=UBound(ByteArray)Fori=LowBoundToUpBoundByteArray(i)=0注釋:初始化字節(jié)數(shù)組NextFori=LowBoundToUpBoundcount=count+1Ifcount<=lengthThenOneChar=Asc(Mid(StrToChange,co

30、unt,1)If(OneChar>255)Or(OneChar<0)Then注釋:該字符是非ASCII字符ByteArray(i)=GetCharByte(OneChar,True)注釋:得到高字節(jié)i=i+1Ifi<=UpBoundThenByteArray(i)=GetCharByte(OneChar,False)注釋:得到低字節(jié)Else注釋:該字符是ASCII字符ByteArray(i)=OneCharEndIfElseExitForEndIfNextEndSubSubChangeStrAryToByte(StrAry()AsString,ByteAry()AsByte)

31、注釋:將字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組DimLowBound,UpBoundAsIntegerDimi,count,StartPos,MaxLenAsIntegerDimTmpByte()AsByteLowBound=LBound(StrAry)UpBound=UBound(StrAry)count=0ReDimByteAry(0)Fori=LowBoundToUpBoundMaxLen=LenB(StrAry(i)ReDimTmpByte(MaxLen+1)ReDimPreserveByteAry(count+MaxLen+1)CallStrToByte(StrAry(i),TmpByte)注釋:

32、轉(zhuǎn)換一個(gè)字符串StartPos=countDoByteAry(count)=TmpByte(count-StartPos)count=count+1IfByteAry(count-1)=0ThenExitDoLoop注釋:將每一個(gè)字符串對(duì)應(yīng)的字節(jié)數(shù)組按順序填入結(jié)果數(shù)組中ReDimPreserveByteAry(count-1)NextiEndSub下面看一個(gè)轉(zhuǎn)換的例子:DimResultAry()asByteDimSomeStr(2)asStringSomeStr(0)=測試1SomeStr(1)=測試222SomeStr(2)=測試33CallChangeStrAryToByte(SomeS

33、tr,ResultAry)注釋:轉(zhuǎn)換字符串?dāng)?shù)組當(dāng)轉(zhuǎn)換完成以后,查看字節(jié)數(shù)組ResultAry,其中包含了21個(gè)元素,依次是:178,226,202,212,49,0,178,226,202,212,50,50,50,0,178,226,202,212,51,51,0。其中,178,226是測的字節(jié)碼,202,112是試的字節(jié)碼,49,50,51分別為字符1、2、3的ASCII碼??梢?,經(jīng)過轉(zhuǎn)換后,字符串?dāng)?shù)組中的各個(gè)元素按順序放在了字節(jié)數(shù)組中,相互間以終止符0分隔。這樣,字符串?dāng)?shù)組就全部轉(zhuǎn)換成了字節(jié)數(shù)組,然后只要將字節(jié)數(shù)組的第一個(gè)元素以傳址的方式傳入動(dòng)態(tài)連接庫,DLL過程就可以正確地訪問數(shù)組中的

34、所有字符串了。但是,使用這種方法,當(dāng)DLL過程處理結(jié)束返回VB時(shí),VB得到的仍然是字節(jié)數(shù)組。如果需要在VB中再次得到該字節(jié)數(shù)組表示的字符串,還要把整個(gè)字節(jié)數(shù)組重新以0為分割符分成多個(gè)子數(shù)組(每個(gè)子數(shù)組都對(duì)應(yīng)原來字符串?dāng)?shù)組中的一個(gè)元素),然后使用VB函數(shù)StrConv將每個(gè)子數(shù)組轉(zhuǎn)換成字符串(轉(zhuǎn)換時(shí)第二個(gè)參數(shù)選vbUnicode),就可以顯示或進(jìn)行其它操作了。例如,其中一個(gè)子數(shù)組的名字是SubAry,則函數(shù)StrConv(SubAry,vbUnicode)就返回了它所對(duì)應(yīng)的字符串??傊?,VB應(yīng)用程序和動(dòng)態(tài)庫間字符串參數(shù)的傳遞是一個(gè)比較復(fù)雜的過程,使用時(shí)要非常謹(jǐn)慎。同時(shí)應(yīng)盡可能避免傳遞字符串?dāng)?shù)組類

35、型的參數(shù),因?yàn)檫@很容易引起下標(biāo)越界、堆棧溢出等嚴(yán)重錯(cuò)誤。、用戶自定義類型(User-definedType)參數(shù)的傳遞用戶自定義類型在VB中是一種重要的數(shù)據(jù)類型,它為編程者提供了很大的靈活性,使開發(fā)人員可以根據(jù)需要構(gòu)造自己的數(shù)據(jù)結(jié)構(gòu)。它相當(dāng)于C/C+中的結(jié)構(gòu)類型(structure)在VB中,允許程序員以傳址的方式將自定義數(shù)據(jù)類型參數(shù)傳入動(dòng)態(tài)庫,DLL過程也可以將修改后的參數(shù)返回VB程序。但是,在VB中仍然不支持以傳值的方式傳遞用戶自定義類型參數(shù)。傳遞用戶自定義類型參數(shù)時(shí),必須確保VB中的數(shù)據(jù)類型的成員與動(dòng)態(tài)庫中的結(jié)構(gòu)成員是一一對(duì)應(yīng)的,所占空間也必須嚴(yán)格一致。這里所說的一一對(duì)應(yīng),不僅是指VB中

36、的所有結(jié)構(gòu)成員在動(dòng)態(tài)庫的結(jié)構(gòu)中都必須有對(duì)應(yīng)的元素,而且它們?cè)跀?shù)據(jù)結(jié)構(gòu)中定義的順序也必須嚴(yán)格一致,這是VB中使用的數(shù)據(jù)結(jié)構(gòu)成員對(duì)齊方式?jīng)Q定的。在VB中,數(shù)據(jù)結(jié)構(gòu)使用雙字對(duì)齊方式(4-bytealignment),因此,在用戶自己生成用于VB調(diào)用的動(dòng)態(tài)連接庫時(shí),也必須把編譯選項(xiàng)structurememberalignment設(shè)為4字節(jié)(如前文所述)。所謂結(jié)構(gòu)成員對(duì)齊方式是指一個(gè)數(shù)據(jù)結(jié)構(gòu)內(nèi)部,其成員的排列方式。譬如,在VB中,其對(duì)齊方式是4字節(jié),這就好象在一個(gè)數(shù)據(jù)結(jié)構(gòu)內(nèi)部分成了很多個(gè)4字節(jié)大小的小單元,如果相鄰兩個(gè)或多個(gè)數(shù)據(jù)成員的大小可以放在一個(gè)單元中,那么就放在一起;否則這些小單元中可能會(huì)出現(xiàn)未用

37、的空字節(jié)。我們來看下面一個(gè)數(shù)據(jù)類型:TypeTestTypem1asIntegerm2asBytem3asLongEndType它的三個(gè)成員的大小加起來是2+1+4=7。但是,由于m1和m2的字節(jié)總長度是3,小于4,它們就存放于一個(gè)單元中;但該單元剩下的一個(gè)字節(jié)不足以放下一個(gè)Long型的成員m3,于是m3就被放在下一個(gè)單元中,它們之間就有了一個(gè)未用的空字節(jié);因此,整個(gè)結(jié)構(gòu)所占實(shí)際長度是8字節(jié)。同理,如果將m3和m2的位置交換一下,它所占的尺寸就變成了9字節(jié)??梢?,成員在結(jié)構(gòu)中的聲明順序也是非常重要的。通常,當(dāng)一個(gè)用戶自定義類型中不包含字符串時(shí),向動(dòng)態(tài)連接庫中傳遞該類型的參數(shù)是沒有什么問題的。如

38、果只傳遞一個(gè)自定義類型變量,則既可以傳遞該變量名,也可以傳遞該變量的第一個(gè)成員,它們的效果是一樣的,都是將該變量的地址傳進(jìn)了動(dòng)態(tài)庫;同樣,如果要傳遞一個(gè)自定義類型的數(shù)組,則既可以傳遞該數(shù)組的第一個(gè)元素,也可以傳遞第一個(gè)元素的第一個(gè)成員。但是,如果用戶自定義類型中包含字符串類型時(shí),又該如何與動(dòng)態(tài)連接庫傳遞參數(shù)呢?答案是令人遺憾的:在VB中,你無法將一個(gè)包含字符串成員的用戶自定義類型變量或數(shù)組安全、正確地傳入動(dòng)態(tài)庫中。如果你這樣做了,即使某次僥幸得到了正確的結(jié)果,在其背后也隱藏著許多致命的危險(xiǎn)。因此,如果一定要在用戶自定義類型中包含字符串變量,并且該類型的變量又要作為參數(shù)傳入動(dòng)態(tài)庫時(shí),你最好修改類

39、型定義,把其中的字符串成員用相應(yīng)的字節(jié)數(shù)組類型替換掉(轉(zhuǎn)換方法可參見前文),這樣就可以在VB和動(dòng)態(tài)庫間傳遞這種類型的參數(shù)了。另外,在VB中還可以把一個(gè)函數(shù)的指針傳遞到動(dòng)態(tài)庫中,方法也并不復(fù)雜。但筆者強(qiáng)烈建議最好不要這么做,因?yàn)檫@樣一來VB應(yīng)用程序就幾乎完全喪失了它所應(yīng)有的安全性。如果確實(shí)需要傳遞函數(shù)指針的話,那么還是編一個(gè)C/C+的程序來完成這項(xiàng)工作吧??傊?,在VB中調(diào)用DLL過程是一個(gè)比較復(fù)雜的問題,編程人員必須很好地把握,才能達(dá)到既提高了程序效率,開拓了程序功能,又不降低程序安全性的目的。另外需要特別指出的一點(diǎn)是,在本文中提到的所有動(dòng)態(tài)連接庫,都是指沒有使用自動(dòng)化(OLEAutomatio

40、n)技術(shù)的動(dòng)態(tài)庫,WindowsAPI和大多數(shù)用戶自編的動(dòng)態(tài)連接庫都是這種類型的。對(duì)于使用了OLEAutomation技術(shù)的動(dòng)態(tài)連接庫,其參數(shù)傳遞的方式有所不同,讀者可以參閱有關(guān)OLE技術(shù)的書籍,在此不再涉及。FunctionGetCharByte(ByValOneCharAsInteger,ByValIsHighByteAsBoolean)AsByte注釋:該函數(shù)獲得一個(gè)字符的高字節(jié)或低字節(jié)IfIsHighByteThenIfOneChar>=0ThenGetCharByte=CByte(OneChar256)注釋:右移8位,得到高字節(jié)ElseGetCharByte=CByte(One

41、CharAnd&H7FFF)256)Or&H80EndIfExitFunctionElseGetCharByte=CByte(OneCharAnd&HFF)注釋:屏蔽掉高字節(jié),得到低字節(jié)ExitFunctionEndIfEndFunctionSubStrToByte(StrToChangeAsString,ByteArray()AsByte)注釋:該函數(shù)將一個(gè)字符串轉(zhuǎn)換成字節(jié)數(shù)組DimLowBound,UpBoundAsIntegerDimi,count,lengthAsIntegerDimOneCharAsIntegercount=0length=Len(StrToC

42、hange)LowBound=LBound(ByteArray)UpBound=UBound(ByteArray)Fori=LowBoundToUpBoundByteArray(i)=0注釋:初始化字節(jié)數(shù)組NextFori=LowBoundToUpBoundcount=count+1Ifcount<=lengthThenOneChar=Asc(Mid(StrToChange,count,1)If(OneChar>255)Or(OneChar<0)Then注釋:該字符是非ASCII字符ByteArray(i)=GetCharByte(OneChar,True)注釋:得到高字節(jié)i

43、=i+1Ifi<=UpBoundThenByteArray(i)=GetCharByte(OneChar,False)注釋:得到低字節(jié)Else注釋:該字符是ASCII字符ByteArray(i)=OneCharEndIfElseExitForEndIfNextEndSubSubChangeStrAryToByte(StrAry()AsString,ByteAry()AsByte)注釋:將字符串?dāng)?shù)組轉(zhuǎn)換成字節(jié)數(shù)組DimLowBound,UpBoundAsIntegerDimi,count,StartPos,MaxLenAsIntegerDimTmpByte()AsByteLowBound=

44、LBound(StrAry)UpBound=UBound(StrAry)count=0ReDimByteAry(0)Fori=LowBoundToUpBoundMaxLen=LenB(StrAry(i)ReDimTmpByte(MaxLen+1)ReDimPreserveByteAry(count+MaxLen+1)CallStrToByte(StrAry(i),TmpByte)注釋:轉(zhuǎn)換一個(gè)字符串StartPos=countDoByteAry(count)=TmpByte(count-StartPos)count=count+1IfByteAry(count-1)=0ThenExitDoLo

45、op注釋:將每一個(gè)字符串對(duì)應(yīng)的字節(jié)數(shù)組按順序填入結(jié)果數(shù)組中ReDimPreserveByteAry(count-1)NextiEndSub下面看一個(gè)轉(zhuǎn)換的例子:DimResultAry()asByteDimSomeStr(2)asStringSomeStr(0)=測試1SomeStr(1)=測試222SomeStr(2)=測試33CallChangeStrAryToByte(SomeStr,ResultAry)注釋:轉(zhuǎn)換字符串?dāng)?shù)組當(dāng)轉(zhuǎn)換完成以后,查看字節(jié)數(shù)組ResultAry,其中包含了21個(gè)元素,依次是:178,226,202,212,49,0,178,226,202,212,50,50,5

46、0,0,178,226,202,212,51,51,0。其中,178,226是測的字節(jié)碼,202,112是試的字節(jié)碼,49,50,51分別為字符1、2、3的ASCII碼??梢?,經(jīng)過轉(zhuǎn)換后,字符串?dāng)?shù)組中的各個(gè)元素按順序放在了字節(jié)數(shù)組中,相互間以終止符0分隔。這樣,字符串?dāng)?shù)組就全部轉(zhuǎn)換成了字節(jié)數(shù)組,然后只要將字節(jié)數(shù)組的第一個(gè)元素以傳址的方式傳入動(dòng)態(tài)連接庫,DLL過程就可以正確地訪問數(shù)組中的所有字符串了。但是,使用這種方法,當(dāng)DLL過程處理結(jié)束返回VB時(shí),VB得到的仍然是字節(jié)數(shù)組。如果需要在VB中再次得到該字節(jié)數(shù)組表示的字符串,還要把整個(gè)字節(jié)數(shù)組重新以0為分割符分成多個(gè)子數(shù)組(每個(gè)子數(shù)組都對(duì)應(yīng)原來字符串?dāng)?shù)組中的一個(gè)元素),然后使用VB函數(shù)StrConv將每個(gè)子數(shù)組轉(zhuǎn)換成字符串(轉(zhuǎn)換時(shí)第二個(gè)參數(shù)選vbUnicode),就可以顯示或進(jìn)行其它操作了。例如,其中一個(gè)子數(shù)組的名字是SubAry,則函數(shù)StrConv(SubAry,vbUnicode)就返回了它所對(duì)應(yīng)的字符串??傊琕B應(yīng)用程序和動(dòng)態(tài)庫間字符串參數(shù)的傳遞是一個(gè)比較復(fù)雜的過程,使用時(shí)要非常謹(jǐn)慎。同時(shí)應(yīng)盡可能避免傳遞字符串?dāng)?shù)組類型的參數(shù),因?yàn)檫@很容易引起下標(biāo)越界、堆棧溢出等嚴(yán)重錯(cuò)誤。(3)、用戶自定義類型(User-def

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請(qǐng)下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請(qǐng)聯(lián)系上傳者。文件的所有權(quán)益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內(nèi)容里面會(huì)有圖紙預(yù)覽,若沒有圖紙預(yù)覽就沒有圖紙。
  • 4. 未經(jīng)權(quán)益所有人同意不得將文件中的內(nèi)容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網(wǎng)僅提供信息存儲(chǔ)空間,僅對(duì)用戶上傳內(nèi)容的表現(xiàn)方式做保護(hù)處理,對(duì)用戶上傳分享的文檔內(nèi)容本身不做任何修改或編輯,并不能對(duì)任何下載內(nèi)容負(fù)責(zé)。
  • 6. 下載文件中如有侵權(quán)或不適當(dāng)內(nèi)容,請(qǐng)與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準(zhǔn)確性、安全性和完整性, 同時(shí)也不承擔(dān)用戶因使用這些下載資源對(duì)自己和他人造成任何形式的傷害或損失。

最新文檔

評(píng)論

0/150

提交評(píng)論