日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

在VB 中调用动态连接库

發布時間:2025/3/20 编程问答 22 豆豆
生活随笔 收集整理的這篇文章主要介紹了 在VB 中调用动态连接库 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
在VB 中調用動態連接庫
?
2001-11-08· · ··yesky

1 2 3 4 5 6 7 8 ?下一頁

  作為一種簡單易用的Windows開發環境,Visual Basic從一推出就受到了廣大編程人員的歡迎。它使 程序員不必再直接面對紛繁復雜的Windows消息,而可以將精力主要集中在程序功能的實現上,大大提高了編程效率。但凡事有利必有弊。VB中高度的封裝和模塊化減輕了編程者的負擔,同時也使開發人員失去了許多訪問低層API函數和直接與Windows交互的機會。因此,相比而言,VB應用程序的執行效率和功能比C/C++或Delphi生成的程序要差。為了解決這個問題,在一個大型的VB開發應用中,直接調用Windows API函數幾乎是不可避免的;同時,還有可能需 要程序員自己用C/C++等開發一些動態連接庫,用于在VB中調用。本文主要討論在32位開發環 境Visual Basic 5.0中直接調用Windows 95 API函數或用戶生成的32位動態連接庫的方法 與規則。

  Windows動態連接庫是包含數據和函數的模塊,可以被其它可執行文件(EXE、DLL、OCX 等)調用。動態連接庫包含兩種函數:輸出(exported)函數和內部(internal)函數。輸出函數可以被其它模塊調用,而內部函數則只能在動態連接庫內部使用。盡管動態連接庫也能輸出 數據,但實際上它的數據通常是只在內部使用的。使用動態連接庫的優點是顯而易見的。將應 用程序的一部分功能提取出來做成動態連接庫,不但減小了主應用程序的大小,提高了程序 運行效率,還使它更加易于升級。多個應用程序共享一個動態連接庫還能有效地節省系統資 源。正因為如此,在Windows系統中,動態連接庫得到了大量的使用。

  一般來說,動態連接庫都是以DLL為擴展名的文件,如Kernel32.dll、commdlg.dll等。但也有例外,如16位Windows的核心部件之一GDI.exe其實也是一個動態庫。編寫動態連接庫的工具很多,如VisualC++、BorlandC++、Delphi等,具體方法可以參見相關文檔。下面只以Visual C++5.0為例,介紹一下開發應用于VisualBasic5.0的動態連接庫時應注意的問題(本文中所有涉及C/C++語言或編譯環境的地方,都以VC5為例;所有涉及VisualBasic的地方都以VB5 為例)。

  作為一種32位Windows應用程序的開發工具,VB5生成的exe文件自然也都是32位的,通常情況下也只能調用32位的動態連接庫。但是,并不是所有的32位動態庫都能被VB生成的exe 文件正確地識別。一般來說,自己編寫用于VB應用程序調用的動態連接庫時,應注意以下幾個方面的問題:

  1、生成動態庫時要使用__stdcall調用約定,而不能使用缺省的__cdecl調用約定;__stdcall 約定通常用于32位API函數的調用。

  2、在VC5中的定義文件(.def)中,必須列出輸出函數的函數名,以強制VC5系統將輸出函數的裝飾名(decoratedname)改成普通函數名;所謂裝飾名是VC的編譯器在編譯過程中生成的輸出函數名,它包含了用戶定義的函數名、函數參數及函數所在的類等多方面的信息。由于在VC5中定義文件不是必需的,因此工程不包含定義文件時VC5就按自己的約定將用戶定義的輸出函數名修改成裝飾名后放到輸出函數列表中,這樣的輸出函數在VB生成的應用程序中是不能正確調用的(除非聲明時使用Alias子句)。因此需要增加一個.def文件,其中列出用戶需要的函數名,以強制VC5不按裝飾名進行輸出。

  3、VC5中的編譯選項"結構成員對齊方式(structure member alignment)" 應設成4字節,其原因將在后文詳細介紹。

  4、由于在C中整型變量是4個字節,而VB中的整型變量依然只有2個字節,因此在C中聲 明的整型(int)變量在VB中調用時要聲明為長整型(long),而C中的短整型(short)在VB中則 要聲明成整型(integer);下表針對最常用的C語言數據類型列出了與之等價的Visual Basic 類型(用于32位版本的Windows)。

?

A.處理使用字符串的系統Windows API過程

  如果調用的系統Windows API過程要使用字符串,那么聲明語句中必須增加一個Alias 子句,以指定正確的字符集。包含字符串的系統Windows API函數實際有兩種格式:ANSI和Unicode( 關于ANSI和Unicode兩種字符集的區別將在后面詳細闡述)。因此,在Windows頭文件中,每 個包含字符串的函數都同時有ANSI版本和Unicode版本。例如,下面是SetWindowText函數 的兩種C語言描述。可以看到,第一個描述將函數定義為SetWindowTextA,尾部的"A" 表明它是一個ANSI函數:

   WINUSERAPI BOOL WINAPI SetWindowTextA(HWND hWnd, LPCSTR lpString);


  第二個描述將它定義為 SetWindowTextW, 尾部的"W" 表明它是一個 Unicode 函數:

   WINUSERAPI BOOL WINAPI SetWindowTextW(HWND hWnd, LPCWSTR lpString);


  因為兩個函數實際的名稱都不是"SetWindowText",要引用正確的函數就必 須增加一個Alias子句:

Private Declare Function SetWindowText Lib "user32" _
Alias "SetWindowTextA" (ByVal hwnd As Long, ByVal _
lpString As String) As Long

  應當注意,對于VB中使用的系統WindowsAPI函數,應該指定函數的ANSI版本,因為只 有WindowsNT才支持Unicode版本,而Windows95不支持這個版本。僅當應用程序只運行 在WindowsNT平臺上的時候才可以使用Unicode版本。

  B.函數名是不標準的名稱

  有時,個別的DLL過程的名稱不是有效的標識符。例如,它可能包含了非法的字符(如連 字符),或者名稱是VB的關鍵字(如GetObject)。在這種情況下,可以使用Alias關鍵字。例 如,操作環境DLLs中的某些過程名以下劃線開始。盡管在VB標識符中允許使用標識符,但是 下劃線不能作為標識符的第一個字符。為了使用這種過程,必須先聲明一個名稱合法的過程, 然后用Alias子句引用過程的真實名稱:

Declare Function lopen Lib "kernel32" Alias "_lopen" _
(ByVal lpPathName As String, ByVal iReadWrite _
As Long) As Long

  在上例中,lopen是VB中使用的過程名稱。而_lopen則是動態連接庫中可以識別的名 稱。

  C.使用序號標識DLL過程

  除了使用名稱之外,還可以使用序號來標識DLL過程。某些動態連接庫中不包含過程的名稱,在聲明它們包含的過程時必須使用序號。同使用名稱標識的DLL過程相比,如果使用序號,在最終的應用程序中消耗的內存將比較少,而且速度會快些。但是,一個具體的API的序號 在不同的操作系統中可能是不同的。例如GetWindowsDirectory在Win95下的序號為432,而在WindowsNT4.0下為338。總而言之,如果希望應用程序能夠在不同的操作系統下運行,那么最好不要使用序號來標識API過程。如果過程不屬于API,或者應用程序使用的范圍很有 限,那么使用序號還是有好處的。

  要使用序號來聲明DLL過程,Alias子句中的字符串需要包含過程的序號,并在序號的 前面加一個數字標記字符(#)。例如,Windowskernel中的GetWindowsDirectory函數的序 號為432;可以用下面的語句來聲明該DLL過程:

Declare Function GetWindowsDirectory Lib "kernel32" _
Alias "#432" (ByVal lpBuffer As String, _
ByVal nSize As Long) As Long

  在這里,可以使用任意的合法名稱作為過程的名稱,VB將用序號在DLL中尋找過程。

  為了得到要聲明的過程的序號,可以使用Dumpbin.exe等實用工具(Dumpbin.exe是Microsoft VisualC++提供的一個實用工具,它的使用說明可以參見VC的文檔)。利用Dumpbin,可以提取出.dll文件中的各種信息,例如DLL中的函數列表,它們的序號以及與代碼有關的其它信息。

?

?

(2)、字符串參數的傳遞:

  與簡單數據類型相比,字符串類型(String、String*n)的參數傳遞要復雜得多,這主要是Windows 95 API和VB使用的字符串類型不同的緣故。VB使用被稱為BSTR的String數據類型,它是由自動化(以前被稱為OLE Automation)定義的數據類型。一個BSTR由頭部和字符串組成,頭部包含了字符串的長度信息,字符串中可以包含嵌入的null值。大部分的BSTR是 Unicode的,即每個字符需要兩個字節。BSTR通常以兩字節的兩個null字符結束。下圖表示 了一個BSTR類型的字符串。

  (前綴)aTest/0
  頭部BSTR指向數據的第一個字節

  另一方面,大部分的DLL過程(包括Windows 95 API中的所有過程)使用LPSTR類型字符串,這是指向標準的以null結束的C語言字符串的指針,它也被稱為ASCIIZ字符串。LPSTR 沒有前綴。下圖顯示了一個指向ASCIIZ字符串的LPSTR。

  aTest/0

  LPSTR指向一個以null結尾的字符串數據的第一個字節

  如果DLL過程需要一個LPSTR(指向以null結束的字符串的指針)作為參數,可以在VB 中將一個字符串以傳值的方式傳遞給它。因為指向BSTR的指針實際指向以null值結束的字符串的第一個數據字節,所以對于DLL過程來說,它就是一個LPSTR。這樣傳入動態連接庫的字符串,DLL過程也可以對它進行修改,盡管它是以傳值方式傳入的。只有當DLL過程需要一個指向LPSTR的指針時,才以傳址的方式傳入字符串,這時DLL過程得到的是一個指向字符串指針的指針(相當于C/C++中的char**),而不是通常所用的字符串的首地址(相當于C/C++中的char*)。

  當需要把一個字符串數組整個傳入動態連接庫時,情況就變得復雜多了,用傳遞簡單數據類型數組的方式來傳遞字符串數組是行不通的。當我們以傳值的方式將一個字符串數組的第一個元素傳進動態連接庫時,DLL過程得到的實際上是該元素壓入堆棧段后的地址,而不是數據段中整個數組的首地址。也就是說,這時DLL過程只能得到數組的第一個元素,而無法訪問整個數組。而以傳址方式傳入第一個元素時,DLL過程只能得到指向該元素在堆棧段中地址的指針,同樣無法訪問整個數組。這不能不說是VB的一個不足。因此,在程序設計中,如果確實需要將整個字符串數組傳入動態庫,就必須采取其它方法。

  我們知道,在VB中,有一種Byte數據類型。每個Byte型變量占一個字節,不含符號位,因 此所能表示的范圍為0到255。這種數據類型是專門用于存放二進制數據的。為了將整個字符 串數組傳進動態庫,可以用字節數組來保存字符串。由于Byte是一種簡單數據類型,因此字節 數組的傳遞是非常簡單的。首先,需要把一個字符串正確地轉變成一個字節數組。這要涉及一 些字符集的知識。Windows 95和VB使用不同的字符集,Windows 95 API使用的是ANSI或DBCS 字符集,而VB使用的則是Unicode字符集。所謂ANSI字符集,是指每個字符都用一個字節表示, 因此最多只能有28=256個不同的字符,這對于英語來說已經足夠了,但不能完全支持其它語 言。DBCS字符集支持很多不同的東亞語言,如漢語、日語和朝鮮語,它使用數字0-255表示ASCII 字符,其它大于255或小于0的數字表明該字符屬于非拉丁字符集;在DBCS中,ASCII字符的長 度是一個字節,而漢語、日語和其它東亞字符的長度是2個字節。而Unicode字符集則完全用 兩個字節表示一個字符,因此最多可以表示216=65536個不同字符。也就是說,ANSI字符集中 所有的字符都只占一個字節,DBCS字符集中ASCII字符占一個字節,漢字占兩個字節,Unicode 字符集中每個字符都占兩個字節。由于VB與WindowsAPI使用的字符集不同,因此在進行字符 串到字節數組的轉換時,當用Asc函數取得一個字符的字節碼后,需要判斷它是否是一個ASCII 字符;如果是ASCII字符,則在轉換后的字節數組中就只占一個字節,否則要占兩個字節。

  下面給出了轉換函數:GetChar Byte得到一個字符的高字節或低字節,它的第一個參數 是一個字符的ASCII碼,第二個參數是標志取高字節還是低字節;StrToByte按DBCS或ANSI格 式將一個字符串轉換成一個字節數組,第一個參數是待轉換的字符串,第二個參數是轉換后的一個定長字節數組,若該數組長度不足以存放整個字符串,則截去超長的部分;ChangeStrAryToByte 利用前兩個函數將字符串數組轉換成字節數組,第一個參數是定長的字符串數組,其中每個元素都是一個字符串(各個元素包含的字符數可以不同),第二個參數是一個變長的字節數組, 保存轉換后的結果。

?

Function GetCharByte(ByVal OneChar As Integer, ByVal IsHighByte As Boolean) As Byte ' 該函數獲得一個字符的高字節或低字節

If IsHighByte Then
If OneChar >= 0 Then
GetCharByte = CByte(OneChar / 256)
'右移8位,得到高字節
Else
GetCharByte = CByte((OneChar
And &H7FFF) / 256) Or &H80
End If
Exit Function
Else
GetCharByte = CByte(OneChar And &HFF)
'屏蔽掉高字節,得到低字節
Exit Function
End If
End Function

Sub StrToByte(StrToChange As String, ByteArray() As Byte)
'該函數將一個字符串轉換成字節數組
Dim LowBound, UpBound As Integer
Dim i, count, length As Integer
Dim OneChar As Integer

count = 0
length = Len(StrToChange)
LowBound = LBound(ByteArray)
UpBound = UBound(ByteArray)

For i = LowBound To UpBound
ByteArray(i) = 0 '初始化字節數組
Next

For i = LowBound To UpBound
count = count + 1
If count <= length Then
OneChar = Asc(Mid(StrToChange, count, 1))

If (OneChar > 255) Or (OneChar < 0) Then
'該字符是非ASCII字符
ByteArray(i) = GetCharByte(OneChar, True) '得到高字節
i = i + 1
If i <= UpBound Then ByteArray(i)
= GetCharByte(OneChar, False)
'得到低字節
Else
'該字符是ASCII字符
ByteArray(i) = OneChar
End If
Else
Exit For
End If
Next
End Sub

Sub ChangeStrAryToByte(StrAry()
As String, ByteAry() As Byte)
'將字符串數組轉換成字節數組
Dim LowBound, UpBound As Integer
Dim i, count, StartPos, MaxLen As Integer
Dim TmpByte() As Byte

LowBound = LBound(StrAry)
UpBound = UBound(StrAry)
count = 0
ReDim ByteAry(0)

For i = LowBound To UpBound
MaxLen = LenB(StrAry(i))
ReDim TmpByte(MaxLen + 1)
ReDim Preserve ByteAry(count + MaxLen + 1)
Call StrToByte(StrAry(i), TmpByte) '轉換一個字符串
StartPos = count
Do
ByteAry(count) = TmpByte(count - StartPos)
count = count + 1
If ByteAry(count - 1) = 0 Then Exit Do
Loop '將每一個字符串對應
的字節數組按順序填入結果數組中
ReDim Preserve ByteAry(count - 1)
Next i
End Sub



  下面看一個轉換的例子:

DimResultAry()asByte
DimSomeStr(2)asString
SomeStr(0)="測試1"
SomeStr(1)="測試222"
SomeStr(2)="測試33"
CallChangeStrAryToByte
(SomeStr,ResultAry)'轉換字符串數組


  當轉換完成以后,查看字節數組ResultAry,其中包含了21個元素,依次是:178,226,202,212,49,0,178,226,202,212,50,50,50,0,178,226,202,212,51,51,0。其中,[178,226]是"測"的字節碼,[202,112]是"試"的字節碼,49,50,51 分別為字符1、2、3的ASCII碼。可見,經過轉換后,字符串數組中的各個元素按順序放在了字節數組中,相互間以終止符0分隔。

  這樣,字符串數組就全部轉換成了字節數組,然后只要將字節數組的第一個元素以傳址的方式傳入動態連接庫,DLL過程就可以正確地訪問數組中的所有字符串了。但是,使用這種方法,當DLL過程處理結束返回VB時,VB得到的仍然是字節數組。如果需要在VB中再次得到該字節數組表示的字符串,還要把整個字節數組重新以0為分割符分成多個子數組(每個子數組都對應原來字符串數組中的一個元素),然后使用VB函數StrConv將每個子數組轉換成字符串(轉換時第二個參數選vbUnicode),就可以顯示或進行其它操作了。例如,其中一個子數組的名字是SubAry,則函數StrConv(SubAry,vbUnicode)就返回了它所對應的字符串。

  總之,VB應用程序和動態庫間字符串參數的傳遞是一個比較復雜的過程,使用時要非常謹慎。同時應盡可能避免傳遞字符串數組類型的參數,因為這很容易引起下標越界、堆棧溢出等嚴重錯誤。

?

?

http://www.yesky.com/20011030/202753.shtml

?

總結

以上是生活随笔為你收集整理的在VB 中调用动态连接库的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。