Thread-safe string buffer variables in C++ DLL
I am writing a Win32 DLL in MSVC++2010 with exported functions. Some of those functions return filenames as LPCSTR
. Because I sometimes need to fiddle with strings before, I am curren开发者_StackOverflowtly using a global buffer variable of length 32184 which should cover any filename that can occur in Windows which I then always initialize and return where a string is needed.
My boss uses this library from a VB6 legacy app. He now informed me that he needs it to be thread-safe: unfortunately for me, due to VB6's event-driven behaviour, it can happen that a function is called in my library even if another function has not yet returned. This, of course, means that I cannot rely on a single internal buffer but have to create one every time I need one and then return it.
2 Questions:
I rely heavily on Windows API functions such as
FindFirstFile
and Boost functions from thefilesystem
andregex
libraries. Can I assume that they are all thread-safe?If I have to create a new buffer on the heap every time I want to return a string, where do I free the memory again?
- Windows API functions are generally thread-safe, with certain limitations (eg, you cannot
FindNextFile
on the same handle from two threads at the same time, but you can with two different handles). For boost functions, consult the documentation, but generally speaking filesystem/regex functions should be safe as long as you don't use the same object between two threads at the same time. - You will have to have the VB6 app call back to free the string when it's done with it. You may also want to consider writing your DLL as a COM library; BSTRs returned from COM calls will be automatically freed by VB6 when they're no longer needed.
The VB6 code is most probably single threaded. The re-entrancy is presumably limited to the VB6 code. The VB6 code can't inject re-entrant events into the C++ code. So long as the C++ code does not call back into the VB6 code then the C++ code itself will not be called in re-entrant fashion.
If these presumptions are correct then your current code with a single global buffer will operate correctly. That said you would be better off switching to BSTR
in my view because it would allow for future linking against caller's that were multi-threaded.
You can use TLS for allocation the string:
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;
}
精彩评论