如何撰寫 C DLL 及呼叫從 Visual Basic

文章翻譯 文章翻譯
文章編號: 106553 - 檢視此文章適用的產品。
全部展開 | 全部摺疊

在此頁中

結論

這篇文章概述如何與 Visual Basic 使用 DLL。它涵蓋了下列問題:

區段 A

  • 1.0 什麼是 DLL?
  • 1.1 為什麼使用 DLL?
  • 1.2 分解架構的 DLL。
  • 1.3 DLL 記憶體管理問題。
  • 1.4 建置 DLL,使用 Visual C++。
  • 1.5 範例 C DLL。

區段 B

  • 2.0 從 Visual Basic 電話 DLL。
  • 2.1 DLL 參數。
  • 2.2 疑難排解。
  • 2.3 範例 Visual Basic 撥號程式。

其他相關資訊

區段 a

1.0 什麼是 DLL?

DLL (動態連結程式庫) 都是 Windows 中重要的層面。DLL 包含可執行的程式可以在執行期間呼叫的函式。 亦即 DLL 會為您的程式可以動態地連結使用的函式的程式庫。

連結可以是靜態或動態。靜態連結並不會改變。您的程式存取程式庫函式所需的所有地址資訊被固定的可執行檔建立和執行期間保持不變。

視需要建立動態連結。當程式所需的函式不是在可執行檔,Windows 載入動態連結程式庫 (DLL) 可以其函式的所有應用程式中使用。到那個時候 Windows 解析每個函式的位址,並動態連結至您的應用程式。

在 Visual Basic 中使用的所有自訂控制項都是 DLL。唯一的差別在於它們需要特殊處理方面的 Visual Basic 從收到郵件。

1.1 為什麼使用 DLL?

以下是您為何想要使用 DLL 的四個原因:

  • C 執行階段函式的存取:

    C 執行階段程式庫具有許多有用的函式,就無法再使用 Visual Basic 程式設計人員會議不供 DLL。比方說 _dos_getdiskfree 函式可讓您計算磁碟空間總數與磁碟機上可用磁碟空間。
  • 需要回呼常式的 Windows API (應用程式發展介面) 函式的存取:

    有些 Windows API 函數則需要回呼函式。回呼函式是將呼叫 Windows 函式執行 API 時呼叫。這類的函式的範例是將提供的特定工作所擁有的所有視窗控制代碼的 EnumTaskWindows。
  • 速度:

    C 是完全編譯過的語言,是相當接近機器的原生程式碼的層級的運作方式。這表示也以 C 撰寫的程式執行將會是快速。
  • 載入上使用:

    僅在需要時,程式碼和資料從 DLL 會載入。可以組織的 DLL,使得只有必要的組件會載入相對於整個 DLL。這可減少所需的記憶體與帶到載入的時間量。

1.2 的分解架構的 DLL

每個 DLL 必須包含 LibMain 函式,且應包含除了匯出可由可執行程式呼叫的函式一個 Windows 的 [結束程序 (WEP)。

  • LibMain:

    DLL 必須包含 LibMain 函式。初始化 DLL,系統就會呼叫 LibMain 函式。LibMain 時會呼叫一次--第一個程式需要 DLL 載入。參數傳遞至 LibMain 如下:
    -DLL 的執行個體控點: 控制代碼。
    -WORD: 程式庫的資料區段。
    -WORD: 堆積大小。
    -LPSTR: 命令列參數。
  • WEP:

    WEP (Windows 結束程序) 執行 DLL 的清理,文件庫在卸載之前。雖然 WEP 函式是需要在舊版的 Windows 作業系統中每個 DLL,基於 3.1 版是選擇性的。一個 WEP 應該包含在模組定義檔案 (.DEF) 在 Visual C,例如:
    匯出
    WEP
  • 匯出的函式:

    這些是您要從您的 DLL 呼叫函的式。它們是由 _export 表示。_export 用於回溯相容性。您要呼叫的所有函數必須也都列在您的 DLL 的 (.DEF) 檔。

1.3 DLL 記憶體管理問題

使用大量記憶體模型。

C 會儲存所有變數定義為靜態或全域 (定義函式之外) 在程式的堆積空間和 C 都儲存所有其他變數在堆疊上。

小型和中型] 模型中所有指標附近都是根據預設值。這表示資料由 16 位元位移,資料區段 (DS) 暫存器,或堆疊區段 (SS) 登錄存取。不幸的是,編譯器有無法知道位移會從 DS 或 [SS。在大多數程式中這不會有問題因為 DS 和 SS 指向相同的區段。一個 DLL 不過,是一種特殊情況。

DLL 有它自己的資料區段,但是與呼叫程式共用其堆疊。這表示 DS 及 [SS 未指向相同的位置。此問題最簡單的解決方案是建置 DLL 在大型記憶體模型中所有變數在 32 位元值所都參考的位置。

為什麼動態配置的記憶體嗎?

動態配置的記憶體是一項 Windows 友善的技巧。宣告大型陣列資料佔用的空間也就是限於 64K 的程式的堆疊或您程式的資料區段會浪費磁碟空間和 Windows 記憶體中。最好時需要或當您完成時再釋放要求記憶體 Windows。

配置記憶體

在 Windows 中,您可以動態地配置兩種類型的記憶體區域和全域。本機記憶體是限制為 64 K,與呼叫 DLL 的程式在一個 DLL 的情況下共用本機記憶體。它已經載入之後,全域記憶體就是記憶體的所有 Windows 可用。

配置本機記憶體及 Managed 使用 LocalAlloc、 LocalLock LocalUnlock 和 LocalFree 運作--如下例所示:
   char* pszBuffer;
   ....
   pszBuffer = (char *) LocalAlloc (LPTR, 20);
   ...
   LocalFree (pszBuffer);
				
是速度比配置全域記憶體配置本機記憶體。 但從本機的堆積配置限於必須呼叫 DLL 的所有程式之間共用的 64 K。您最好使用時小短 lived 的記憶體區塊所需的本機記憶體。

全域記憶體配置及管理使用 GlobalAlloc、 GlobalLock GlobalUnlock 和 GlobalFree 運作--如下例所示:
   HGLOBAL hglb;
   char* pszBuffer;

   hglb = GlobalAlloc (GHND, 2048);
      // GHND allocates the memory as moveable and
      // initialized to 0
      // 2048 is the amount of memory to be allocated...
   pszBuffer = GlobalLock (hglb);
   ...
   GlobalUnlock (hglb);
   GlobalFree (hglb);
				
[GlobalAlloc 函式會配置記憶體 4 K 的倍數。

如果想共用 DLL 與其他程式中配置記憶體應該配置它使用 GMEM_SHARED 旗標。如果想共用透過 DDE 記憶體必須將它配置使用 GMEM_DDESHARE 旗標。

在靜態變數中儲存資料時請小心

如果嘗試將資料儲存於 DLL,使用全域或靜態變數中不要是感到驚訝,如果這些值已經變更當您接下來呼叫您的 DLL。儲存在這種方式,資料會通用於所有存取此 DLL 的應用程式。不論多少應用程式使用 DLL,沒有 DLL 的只有一個執行個體。若要解決這個問題,最好是從 DLL 傳回結構,並一次傳遞它們在中,在需要時。

檔案控點

您不可能共用應用程式或 DLL 之間的檔案控制代碼。每一個應用程式有它自己的檔案控制碼表格。兩個應用程式可以使用相同的檔案使用 DLL,他們必須兩者都開啟的檔案個別。

1.4 建置 DLL,使用 Visual C++

以下是建置 DLL,使用 Visual C++ 的必要步驟:

  1. 啟動 Visual C++。
  2. 建立新的專案時,請從 [專案] 功能表中選擇 [新增]。選取下列選項:

    • 將 [專案類型] 設為"Windows 動態連結程式庫 (.DLL)"
    • 清除 「 使用 MFC 」] 核取方塊。
    您也可以設定,或從 [選項] 功能表中選擇 [專案,稍後檢視這些選項。
  3. 將您現有的.C 和.DEF 檔加入至專案中,使用 [當您從 [專案] 功能表中選擇 [編輯] 時出現的對話方塊。或直接在 Visual C++ 編輯視窗中輸入您的程式碼。(請參閱.C 和.DEF 範例程式碼下面列出)。
  4. 從 [專案] 功能表中,選擇 [建置 <yourname>.DLL 選項]。

1.5 範例 C DLL

下列的 DLL 包含 GetDiskInfo 函式可從 Visual Basic 呼叫。它會傳回可用磁碟空間、 目前的磁碟機名稱和磁碟區名稱。
      C Code Example, DISKINFO.C:

   #include <windows.h>
   #include <dos.h>

   int CALLBACK LibMain (HANDLE hInstance, WORD wDataSeg, WORD wHeapSize,
   LPSTR lpszCmdLine)

   // The following is required only under Windows version 3.1
   // Win32 does not require or support UnlockData()
   {
      if (wHeapSize > 0)
         UnlockData (0);  //Unlocks the data segment of the library.
      return 1;
   }

   void __export CALLBACK GetDiskInfo (char *cDrive, char *szVolumeName,
   unsigned long *ulFreeSpace)
   {
      unsigned drive;
      struct _diskfree_t driveinfo;
      struct _find_t c_file;

      _dos_getdrive (&drive);
      _dos_getdiskfree( drive, &driveinfo );

      if (!_dos_findfirst( "*.*", _A_VOLID, &c_file ))
         wsprintf( szVolumeName, "%s", c_file.name);
      else
         wsprintf ( szVolumeName, "NO LABEL");

      *cDrive = drive + 'A' -1;

      *ulFreeSpace = (unsigned long) driveinfo.avail_clusters * (unsigned
         long) driveinfo.sectors_per_cluster * (unsigned long)
         driveinfo.bytes_per_sector;
   }
				
使用下列 DISKINFO.DEF 檔案 Visual C++ 中的:
LIBRARY diskinfo
描述 ' GetDiskInfo 罐從呼叫 Visual Basic '
EXETYPE 視窗 3.1
程式碼 PRELOAD MOVEABLE 可捨棄
資料 PRELOAD 可移動單
HEAPSIZE 4096
匯出
GetDiskInfo @ 1
注意: LIBRARY 名稱在.DEF 檔必須做為 DLL 檔案名稱相同或其他 Visual Basic 會提供您 「 錯誤中載入的 DLL 」。比方說建立檔案 DISKINFO.DLL 使用上述的.DEF 檔中 LIBRARY DISKINFO 陳述式。

區段 b

從 Visual Basic 呼叫 DLL 的 2.0

在 Visual Basic 所有函式中包括您想要呼叫 DLL 函式必須先宣告使用 Declare 陳述式。您可以宣告您在表單或模組的宣告區段中的函式。如果宣告 DLL 程序或在表單中的函式是私用到該表單。 若要使其公用,您必須在模組中宣告。下列是範例 Declare 陳述式:
   Declare Sub getdiskinfo Lib "c:\somepath\diskinfo.dll"
      (ByVal mydrive As String, ByVal myvolume As String, free As Long)
				
您必須為一個,單線輸入整個 Declare 陳述式。此特定 Declare 陳述式宣告 GETDISKINFO 位於使用者建立 DISKINFO.DLL 檔使用者定義程序。

一旦您宣告函式時,您可以呼叫,並使用函式,方式就會呼叫和使用 Visual Basic 函數一樣。

2.1 DLL 參數

由於 DLL 通常以 C 撰寫,DLL 可以使用各種不同的參數不直接支援 Visual Basic 的。如此一來當傳遞參數,程式設計人員尋找傳遞適當資料型別。

傳遞引數傳值方式或傳址

預設情況下,Visual Basic 會由參考傳遞所有引數。(當以傳址方式傳遞,Visual Basic 提供 32 位元的遠程位址)。不過,許多 DLL 函式預期要傳值方式傳遞的引數。這將可達到藉由放置 ByVal 關鍵字引數宣告的前面。

下列各節將告訴您,如何將參數轉換成 Visual Basic。

8 至 16 位元數值參數

(int、 簡短、 不帶正負號的 int、 不帶正負號的短整數、 BOOL 及 WORD) 8 至 16 位元數值參數傳遞為整數。

32 位元數值參數

32 位元數值參數傳遞 (長,未簽署長時間和 DWORD) 作為 LONG。

物件控點

所有的控點是與視窗相關聯的唯一的 16 位元的整數值,並傳值方式傳遞,所以這些參數傳遞為整數。

字串

字串包含 LPSTR 和 LPBYTE 資料類型 (字元的指標) 或不帶正負號字元的指標。這些參數傳遞為 (ByVal paramname 為 String)。要直接傳遞 Visual Basic 字串,將當做 (param 為 String) 傳遞它們。

如需有關 Visual Basic 和 C DLL 之間傳遞字串的詳細資訊,請參閱 「 Microsoft 知識庫 」 中下列文:
118643如何 VB 和 C DLL 之間傳遞字串或字串陣列
注意: Visual Basic 字串需要特殊處理因此不要傳遞字串直接除非 DLL 明確要求。

數字值的指標

只需不使用 ByVal 關鍵字,將指標傳遞到數字值。

結構

如果 Visual Basic 使用者定義型別符合該 DLL 所預期的結構,結構可以以傳址方式傳遞。

注意: 結構不能以傳址方式傳遞值。

陣列的指標

以傳址方式傳遞陣列的第一個項目。

函式的指標

Visual Basic 不支援回呼函式,因此有指標的函式的 DLL 函式不能與 Visual Basic 一起使用。

null 指標

如果 DLL 所預期 Null 指標,將它當做 (ByVal paramname As Any) 傳遞。您可以使用 0 做為 paramname 呼叫 DLL 時的值 (& I)。

2.2 疑難排解

以下是一些您可能會遇到的問題的解決方案。

呼叫 DLL 之後,系統資源保留取得較低

如果您的 DLL 使用 GDI 物件,您必須記得要釋放之後使用它們。這可能不是在 Visual Basic 中明顯,但如果您建立 GDI 物件 (比方說 CreateBrushIndirect),請使用 Windows SDK (軟體開發套件) 時, 必須刪除它稍後使用 DeleteObject。

無效的 DLL 呼叫慣例錯誤

這項錯誤是通常因不正確地省略或包括 ByVal 關鍵字從 Declare 陳述式。如果傳遞錯誤參數,也可能引起這個錯誤。

載入 DLL 時發生錯誤

當您呼叫動態連結程式庫程序,而且不能載入程序的 Declare 陳述式中所指定之檔案時,就會發生這個錯誤。您可以使用 Microsoft Windows API 函數 LoadLibrary 以找出有關為何無法載入一個 DLL 更特定資訊。

一般性的保護 (GP) 錯誤

當您的程式將寫入至不屬於它的記憶體區塊時,就會發生 GP 錯誤。這兩個最可能原因包括:

  • overstepped 陣列界限。C 並不會檢查您正在撰寫之陣列註標無效。因此,您可以輕易地寫入至不屬於您的記憶體。
  • 您正在使用的指標已釋放的記憶體位置。最佳的選項是指派給所有指標的 NULL 之後釋放它們的記憶體。
不正確的變數型別傳遞至 DLL 函式時,也可能會發生 GP 錯誤。

2.3 範例 Visual Basic 呼叫程式

有兩個部分 Visual Basic 程式中呼叫的 DLL。首先宣告在函式,然後在事件程式碼中使用它。

以下是 Declare 陳述式的範例。Declare 陳述式應該將放在模組或表單的一般宣告區段。
   ' Enter the following Declare as one, single line:
   Declare Sub getdiskinfo Lib "c:\dllartic\diskinfo.dll"

      (ByVal mydrive As String, ByVal myvolume As String, free As Long)
				
指定 ByVal 陳述式完全如所示,或其他 GP 錯誤可能會發生。

一旦宣告函式可在事件程式碼中使用它。下列範例會使用從 DLL 函式 Command1 Click 事件程式碼中:
   Sub Command1_Click ()
      Dim drive As String * 1
      Dim volume As String * 20
      Dim free As Long
      Call getdiskinfo(drive, volume, free)
      Text1.Text = drive
      Text2.Text = volume
      Text3.Text = Str$(free)
   End Sub
				

屬性

文章編號: 106553 - 上次校閱: 2004年7月15日 - 版次: 2.1
這篇文章中的資訊適用於:
  • Microsoft Visual Basic 3.0 Professional Edition
  • Microsoft Visual Basic 3.0 Professional Edition
關鍵字:?
kbmt kbhowto kbprogramming KB106553 KbMtzh
機器翻譯
重要:本文是以 Microsoft 機器翻譯軟體翻譯而成,而非使用人工翻譯而成。Microsoft 同時提供使用者人工翻譯及機器翻譯兩個版本的文章,讓使用者可以依其使用語言使用知識庫中的所有文章。但是,機器翻譯的文章可能不盡完美。這些文章中也可能出現拼字、語意或文法上的錯誤,就像外國人在使用本國語言時可能發生的錯誤。Microsoft 不為內容的翻譯錯誤或客戶對該內容的使用所產生的任何錯誤或損害負責。Microsoft也同時將不斷地就機器翻譯軟體進行更新。
按一下這裡查看此文章的英文版本:106553
Microsoft及(或)其供應商不就任何在本伺服器上發表的文字資料及其相關圖表資訊的恰當性作任何承諾。所有文字資料及其相關圖表均以「現狀」供應,不負任何擔保責任。Microsoft及(或)其供應商謹此聲明,不負任何對與此資訊有關之擔保責任,包括關於適售性、適用於某一特定用途、權利或不侵權的明示或默示擔保責任。Microsoft及(或)其供應商無論如何不對因或與使用本伺服器上資訊或與資訊的實行有關而引起的契約、過失或其他侵權行為之訴訟中的特別的、間接的、衍生性的損害或任何因使用而喪失所導致的之損害、資料或利潤負任何責任。
依現狀不再更新的知識庫內容免責聲明
本文旨在說明 Microsoft 不再提供支援的產品。因此,本文係依「現狀」提供,不會再更新。

提供意見

 

Contact us for more help

Contact us for more help
Connect with Answer Desk for expert help.
Get more support from smallbusiness.support.microsoft.com