Reputation: 999
we are having a discussion as what is a good way to return multiple strings from one dll function. Currently we have 8 strings, but there will be more. For simplicity I now consider all strings will have equal lengths.
extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults);
where
struct TestResults
{
int stringLengths;
char* string1;
char* string2;
char* string3;
char* string4;
...
};
or second option: where
struct TestResults
{
int stringLengths;
char string1[64];
char string2[64];
char string3[64];
char string4[64];
...
};
third option: extern "C" int DLLNAME_ _stdcall GetResult(int stringLengths, char* string1, char* string2, char* string3, ...);
The dll will communicate over a serial line and retrieve information that will be filled into the strings. Where the memory needs to be allocated is open for discussion and can be part of the answer.
The background is that we have a VB6 application team that prefers the second method and a C++/C# team that prefers the first method. Last method looks to suit both teams but looks a bit strange to me with so many parameters.
Maybe there are more options. What is common practice under Windows? Any examples from the Windows API or arguments to choose one over the other?
Edit: The strings have a meaning as in first name, last name, email. We currently have eight, but in the future we might add a couple for example for address. An array would not be the correct choice for this, but that was not clear from the original context.
Upvotes: 3
Views: 1321
Reputation: 42924
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);
Upvotes: 5
Reputation: 5631
As you declare your function as extern "C"
I suppose that you cannot use std::vector<std::string>
as return type.
Another possibility would be:
struct String
{
int size; /* size of string */
const char* str; /* actual string */
}
struct TestResults
{
int size; /* number of strings */
String* arr; /* pointer to an array of String */
};
and then the same as before:
extern "C" int DLLNAME_ _stdcall GetResult(TestResults* testResults);
With that you are flexible to return as much strings as you like. Also loop through your TestResults
is easy.
Edit #1: As said in the comments: use BSTR. So your struct would look like:
struct TestResults
{
int size; /* number of strings */
BSTR* arr; /* pointer to an array of BSTR */
};
A BSTR
will be allocated by: BSTR MyBstr = SysAllocString(L"I am a happy BSTR");
. This allocation also sets the member which contain the length of the string. You have to free the allocated memory with: SysFreeString(MyBstr);
. Also you need to allocate the whole array BSTR*
.
Upvotes: 1