2011-06-22 102 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时才有可能。 –