Returning Std::String/Std::List from Dll

returning std::string/std::list from dll

The main thing to keep in mind is that dlls contain code and not memory. Memory allocated belongs to the process(1). When you instantiate an object in your process, you invoke the constructor code. During that object's lifetime you will invoke other pieces of code(methods) to work on that object's memory. Then when the object is going away the destructor code is invoked.

STL Templates are not explicitly exported from the dll. The code is statically linked into each dll. So when std::string s is created in a.dll and passed to b.dll, each dll will have two different instances of the string::copy method. copy called in a.dll invokes a.dll's copy method... If we are working with s in b.dll and call copy, the copy method in b.dll will be invoked.

This is why in Simon's answer he says:

Bad things will happen unless you can
always guarantee that your entire set
of binaries is all built with the same
toolchain.

because if for some reason, string s's copy differs between a.dll and b.dll, weird things will happen. Even worse if string itself is different between a.dll and b.dll, and the destructor in one knows to clean extra memory that the other ignores... you can have difficult to track down memory leaks. Maybe even worse... a.dll might have been built against a completely different version of the STL (ie STLPort) while b.dll is built using Microsoft's STL implementation.

So what should you do? Where we work, we have strict control over the toolchain and build settings for each dll. So when we develop internal dll's, we freely transfer STL templates around. We still have problems that on rare occasion crop up because someone didn't correctly setup their project. However we find the convenience of the STL worth the occasional problem that crops up.

For exposing dlls to 3rd parties, that's another story entirely. Unless you want to strictly require specific build settings from clients, you'll want to avoid exporting STL templates. I don't recommend strictly enforcing your clients to have specific build settings... they may have another 3rd party tool that expects you to use completely opposite build settings.

(1) Yes I know static and locals are instantiated/deleted on dll load/unload.

Returning Strings from DLL Functions

Since your error message indicates you're using Microsoft C++ I'll offer an MS specific answer.

As long as you compile both the EXE and the DLL with the SAME compiler, and both link the the SAME version of the runtime DYNAMICALLY then you'll be just fine. For example, using "Multi-threaded DLL" for both.

If you link against the runtime statically, or link against different versions of the runtime then you're SOL for the reasons @Billy ONeal points out (memory will be allocated in one heap and freed in another).

Return multiple strings from dll

The best way is probably using a safe array storing BSTR strings.

Both VB and C# understand safe arrays very well: in C#, a safe array of BSTR strings is automatically converted to a string[] array.

On the C++ side, you can use the ATL::CComSafeArray helper class to simplify safe array programming.

You will find interesting material in this MSDN Magazine article (in particular, take a look at the paragraph Producing a Safe Array of Strings).


From the aforementioned article: On the C++ side, you can implement a C-interface DLL, exporting a function like this:

extern "C" HRESULT MyDllGetStrings(/* [out] */ SAFEARRAY** ppsa)
{
try {
// Create a SAFEARRAY containing 'count' BSTR strings
CComSafeArray<BSTR> sa(count);

for (LONG i = 0; i < count; i++) {
// Use ATL::CComBSTR to safely wrap BSTR strings in C++
CComBSTR bstr = /* your string, may build from std::wstring or CString */ ;

// Move the the BSTR string into the safe array
HRESULT hr = sa.SetAt(i, bstr.Detach(), FALSE);

if (FAILED(hr)) {
// Error...
return hr;
}
}

// Return ("move") the safe array to the caller
// as an output parameter (SAFEARRAY **ppsa)
*ppsa = sa.Detach();

} catch (const CAtlException& e) {
// Convert ATL exceptions to HRESULTs
return e;
}

// All right
return S_OK;
}

On the C# side, you can use this PInvoke declaration:

[DllImport("MyDll.dll", PreserveSig = false)]
public static extern void MyDllGetStrings(
[Out, MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_BSTR)]
out string[] result);

Is it safe to return std::wstring from a DLL?

Nothing has changed with regards passing C++ objects across DLL boundaries. That is still not allowed for the same reason as before. The module on the other side of the boundary may have a different definition of the class.

The fact that &s[0] is a valid modifiable pointer to character array is not really relevant. Because a std::basic_string is a lot more than just an array of characters.

Remember that each implementation of std::basic_string can have different internal storage. Can have a different implementation for operator[]. Can be allocated off a different heap. And so on.

I think it is safe to assume that it will never be valid to pass C++ objects across general DLL boundaries. It is only viable if you guarantee that both sides of the boundary are linked against the same runtime instance.



Related Topics



Leave a reply



Submit