我要如何與應用程式或其他 DLL 共用我的 DLL 中的資料?

更新:2007 年 11 月

Win32 DLL 是對應到呼叫處理序的位址空間。根據預設,每個使用 DLL 的處理序都有自己的 DLL 全域和靜態變數的執行個體。如果 DLL 需要與它自己的其他執行個體 (由其他應用程式所載入) 共用資料時,您可以使用下列其中一個方法:

  • 使用 data_seg pragma 來建立具名資料區段。

  • 使用記憶體映射檔。請參閱 MSDN Lib 中的<在 Win32 中管理記憶體映射檔>文件 (英文)。

下面是一個使用 data_seg Pragma 的範例:

#pragma data_seg (".myseg")
   int i = 0; 
   char a[32]n = "hello world";
#pragma data_seg()

data_seg 可以用來建立一個新的具名區段 (在此範例為 .myseg)。最常見的用法,是清楚地呼叫資料區段 .shared。接著您必須在 .def 檔中或是使用連結器選項 /SECTION:.MYSEC,RWS,為這個新的具名資料區段指定正確的共用屬性。

在您使用共用資料區段之前,請先考慮下面幾項限制:

  • 共用資料區段中的任何變數都必須靜態初始化。在上述範例中,i 會被初始化為 0,而 a 具有 32 個字元且被初始化為 hello world。

  • 所有共用變數都會放置在已編譯 DLL 中的指定資料區段內。超大型的陣列會產生超大型的 DLL。所有已完成初始化的全域變數都是如此。

  • 請勿將特定處理序的資訊儲存在共用資料區段中。大部分 Win32 資料結構或資料值 (例如 HANDLE),僅會在單一處理序內容中有效。

  • 每個處理序會取得自己的位址空間。請勿將指標儲存在共用資料區段所包含的某個變數中,這點非常重要。在某個應用程式中可以正常運作的指標,在另一個應用程式中可能無法正常運作。

  • DLL 本身有可能會被載入每個處理序的虛擬位址空間中不同的位址。因此,擁有指向 DLL 中的函式或其他共用變數的指標,就顯得相當危險。

請注意,最後所提到的三點注意事項,是針對記憶體映射檔和共用資料區段。

記憶體映射檔比共用資料區段好用,因為記憶體映射檔的起始位址是已知的。開發人員可以在共用記憶體中的所有資料中,使用「共用記憶體區段起始位址的位移」,來實作類似指標的行為。我們強烈建議您使用 __based 指標來快速又輕鬆地達成此目標。然而請特別注意,基底 (或是記憶體映射檔的起始位址) 在每個處理序中可能會不同,所以儲存 __based 指標之基底的變數,本身無法位於共用記憶體中。

下數幾項限制對 C++ 類別的使用方式有重要的影響。

  • 具有虛擬函式的類別一定會包含函式指標。因此請勿將具有虛擬函式的類別儲存於共用資料區段或是記憶體映射檔中。這點對 MFC 類別或是從 MFC 繼承而來的類別來說更是重要。

  • 靜態資料成員以全域變數的方式實作。這表示每個處理序都各自擁有一組該類別的靜態資料成員。具有靜態資料成員的類別不應該共用。

  • 共用資料區段的初始化需求會造成 C++ 類別的特定問題。如果您在共用資料區段中有像 CTest Counter(0); 這類的程式碼,則 Counter 物件將會在每個處理序載入 DLL 時進行初始化,這樣可能會每次都將物件的資料歸零。這點和當建立 DLL 時由連結器進行初始化的內建資料型別非常不同。

因為這些限制,所以 Microsoft 不建議在處理序之間共用 C++ 物件。一般說來,如果您要使用 C++ 在處理序之間共用資料,請撰寫會在內部使用記憶體映射檔來共用資料的類別,但是不要共用類別執行個體本身。開發這種類別時要特別地小心,不過這樣可以讓應用程式開發人員完全掌控共用資料所產生的副作用。

如需建立具名資料區段的詳細資訊,請參閱下列位於 https://support.microsoft.com 的知識庫 (Knowledge Base) 文件:

  • <How To:在 DLL 的不同對應間分享資料>(Q125677)。

  • <在 DLL 中指定共用和非共用的資料>(Q100634)。

  • <在 DLL 中共用所有資料>(Q109619)。

  • <共用程式碼區段中的記憶體並未在共用終端伺服器階段中共用>(Q251045)。

請參閱

概念

DLL 常見問題集