2011-06-22 107 views
0

我正在用導出的函數在MSVC++ 2010中編寫一個Win32 DLL。其中一些函數返回文件名爲LPCSTR。由於我有時需要在字符串之前進行操作,因此我正在使用一個長度爲32184的全局緩衝區變量,它應該覆蓋Windows中可能出現的任何文件名,然後我總是初始化並返回需要字符串的位置。C++ DLL中線程安全的字符串緩衝區變量

我的老闆使用VB6傳統應用程序中的這個庫。他現在告訴我他需要它是線程安全的:不幸的是,由於VB6的事件驅動行爲,可能會發生這樣的情況:即使另一個函數尚未返回,函數也會在我的庫中調用。這當然意味着我不能依靠單個內部緩衝區,但每次需要時都必須創建一個,然後將其返回。

2個問題:

  1. 我嚴重依賴於Windows API函數,如FindFirstFilefilesystemregex庫升壓功能。我可以假設他們都是線程安全的嗎?

  2. 如果每次我想要返回一個字符串時,我必須在堆上創建一個新的緩衝區,那麼我又在哪裏釋放內存?

+4

你需要的是不是線程安全的,但重新進入安全 –

回答

3
  1. Windows API函數一般都是線程安全的,具有一定的限制(例如,你不能FindNextFile從兩個線程在同一時間同一個句柄,但是你可以用兩種不同的手柄)。對於boost函數,請查閱文檔,但一般來說,只要不在同一時間在兩個線程之間使用同一對象,文件系統/正則表達式函數應該是安全的。
  2. 您將必須回調VB6應用程序以釋放字符串。您可能還想考慮將您的DLL編寫爲COM庫;當COM不再需要時,由COM調用返回的BSTR將被VB6自動釋放。
+1

有一點補充一下,我不認爲你需要,以受益於使用COM 'BSTR'。你可以從一個普通的DLL中返回'BSTR',並且VB6代碼肯定會釋放它。 –

+0

@David,可能是,但我沒有在VB6上工作足以確保在這方面。不過,我知道它尊重正常的COM分配規則,所以它似乎是安全的建議。另外你不需要在VB6端明確寫出你的導入。 – bdonlan

+0

@大衛:我不認爲這會奏效。 VB6通常將不是來自COM接口的字符串視爲多字節C字符串,然後在內部將它們轉換爲BSTR。我只能聲明'function()As String',其餘部分是自動的。我該如何告訴它期望在這裏有一個BSTR? –

1

VB6代碼很可能是單線程的。重新進入大概侷限於VB6代碼。 VB6代碼不能將重入事件注入到C++代碼中。只要C++代碼不會回調到VB6代碼中,那麼C++代碼本身就不會以可重入的方式調用。

如果這些假設是正確的,那麼您的當前代碼與單個全局緩衝區將正確運行。也就是說,在我看來,切換到BSTR會更好,因爲它可以允許將來與多線程的調用者進行鏈接。

0

您可以使用TLS進行分配的字符串:



const int string_size = 1024; // string size 
DWORD idTlsString = 0; 


// use this function to get the string which you will use to return to VB 
char* GetTheString() 
{ 
    return (char*)TlsGetValue(idTlsString); 
} 

// Dll init function 
BOOL WINAPI DllMain(
    HINSTANCE hinstDLL, 
    DWORD fdwReason, 
    LPVOID lpvReserved 
) 
{ 
    switch(fdwReason) 
    { 
     // allocate TSL 
     case DLL_PROCESS_ATTACH: 
      idTlsString = TlsAlloc(); 
     break; 

     // allocate the srting 
     case DLL_THREAD_ATTACH: 
      TlsSetValue(idTlsString, (LPVOID)new char[string_size]); 
     break; 

     // free the string 
     case DLL_THREAD_DETACH: 
      delete[] (char*)TlsGetValue(idTlsString); 
     break; 

     // release TLS 
     case DLL_PROCESS_DETACH: 
      TlsFree(idTlsString); 
     break; 

    } 
    return true; 
} 
+1

聽起來像問題是可重入的調用,而不是來自不同線程的調用,所以這不起作用。加線程本地存儲通常是一個壞主意。 –

+0

我非常確定(因爲在問題中使用了全局變量),在C++調用後,將結果字符串複製到VB字符串中,這使得它在單線程環境中可重入調用的安全。有了TLS,我只能保證多線程調用的安全性,除此之外別無其他。對我來說,使用TLS是個不錯的主意。 TLS允許你寫無鎖碼。既然這是件壞事? – Sasha

+0

TLS是共享全局狀態的一種形式,與真正的全局變量有許多相同的缺點。基於堆棧的變量總是首選。其實關於我打賭老闆的代碼是單線程的實際問題。重新入侵只在VB的領域內。在這種情況下,使用全局變量的原始代碼可能是正確的。我之前的評論認爲C++ DLL可以以重入式方式調用,但只有在調用VB時纔有可能。 –