2012-03-03 15 views
3

在這種情況下,我的動態加載的DLL由Windows資源管理器加載,以便爲文件/文件夾屬性頁面添加一個新的屬性表(新選項卡)。如何在動態加載的DLL中正確實現(C++)線程本地存儲?

一個簡單的例子是StrmExt.dll(download source)。在這個例子中(微軟提供的源代碼)dll不使用線程本地存儲(TLS),因此在同時加載多個屬性頁時會導致嚴重問題。

在審查源的DLL所需要的一個線程基變量(該文件的文件路徑)...

static TCHAR g_szFile[MAX_PATH]; 

的代碼這一行改爲:

_declspec (thread) TCHAR g_szFile[MAX_PATH]; 

。 ..使DLL支持多線程,因此支持propertysheet的多個實例。不過,我知道這個改變只會被Windows Vista和更新版本支持(Windows 7的測試非常積極)。例如,XP不支持動態加載的庫......這是known to crash the application。 (見最後一段)。

爲了在XP上運行我不能使用這個聲明。我懷疑我需要提高自己的DLL入口點來自:

extern "C" 
BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/) 
{ 
    if (dwReason == DLL_PROCESS_ATTACH) 
    { 
     _Module.Init(ObjectMap, hInstance, &LIBID_STRMEXTLib); 
     DisableThreadLibraryCalls(hInstance); 
    } 
    else if (dwReason == DLL_PROCESS_DETACH) 
     _Module.Term(); 
    return TRUE; // ok 
} 

...到這樣的事情......如先前看到here

struct ThreadData { 
    static TCHAR g_szFile[MAX_PATH]; 
}; 
... 
DWORD g_dwThreadIndex; 

extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, 
         DWORD dwReason, LPVOID /*pReserved*/) 
{ 
    ThreadData* pData; 
    switch (dwReason) { 
     case DLL_PROCESS_ATTACH: 

      g_dwThreadIndex = ::TlsAlloc(); 
      if (g_dwThreadIndex == TLS_OUT_OF_INDEXES) 
       return FALSE; 

      // execute the DLL_THREAD_ATTACH code 

     case DLL_THREAD_ATTACH: 

      // allocate memory for this thread 
      pData = (ThreadData*) ::LocalAlloc(LPTR, sizeof(ThreadData)); 
      if (pData == 0) 
       return FALSE; 

      ::TlsSetValue(g_dwThreadIndex, (LPVOID) pData); 
      break; 

     case DLL_THREAD_DETACH: 

      // release memory for this thread 
      pData = (ThreadData*) ::TlsGetValue(g_dwThreadIndex); 
      if (pData != 0) 
       ::LocalFree((HLOCAL) pData); 
      break; 

     case DLL_PROCESS_DETACH: 

      // release memory for this thread 
      pData = (ThreadData*) ::TlsGetValue(g_dwThreadIndex); 
      if (pData != 0) 
       ::LocalFree((HLOCAL) pData); 
      // release the TLS index 
      ::TlsFree(g_dwThreadIndex); 
      break; 
    } 
    return TRUE; 
} 

該DLL的第一次加載時工作正常是否創建1或2個線程。在釋放dll之後,資源管理器在下一次加載庫時崩潰。

我誤解了什麼?我注意到原始開發者故意在dll進程附加通知時禁用線程通知。爲什麼?

DisableThreadLibraryCalls(hInstance); 

在先進的感謝。

+1

DisableThreadLibraryCalls只是一個優化。 「Explorer崩潰」不是一個合適的問題描述,你必須更好地記錄崩潰細節。 – 2012-03-03 19:48:23

+0

公平的評論。我知道這是一個優化選項,但是如果還有其他原因,我們很好奇。我使用的XP系統目前位於另一個系統的虛擬機上,我將在下一天看到提供異常報告。但是,我在示例中詢問通知代碼。它看起來是否正確,特別是在Windows資源管理器的上下文中?即進程附加邏輯如何正確執行線程附加邏輯?而且,TLS是否在線程分離和進程分離上正確釋放? – clsturgeon 2012-03-03 20:10:49

+0

很明顯,您必須刪除對「DisableThreadLibraryCalls」的調用才能接收「DLL_THREAD_ATTACH」和「DLL_THREAD_DETACH」。除此之外,我想你可能會因爲DllMain的變幻莫測而出現一些泄漏。 – 2012-03-03 21:03:19

回答

1

在這種情況下,最好避免這個問題。是的,你可能會擁有比進程更多的線程,是的,每個屬性表將只與一個線程關聯,但是不能保證相反。兩個屬性表可能共享一個線程,這取決於操作系統。 (並且這些未公開的決定在版本之間改變)。

取而代之,請使用PROPSHEETPAGElParam成員。它足夠容納一個指針,也是在64位系統上。指向你自己的班級。終身管理比您嘗試的DLL連接/分離簡單得多; Windows在正確的時刻調用您的PropSheetPageProc

+0

謝謝爲了正確引導我。我現在有一個32位和64位工作DLL,似乎可以在所有Windows操作系統版本,XP及更高版本上運行。 – clsturgeon 2012-03-12 11:33:32