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.
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);
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With