I am trying to return a string from a c++ dll export function. I am calling this function from c#. I have seen a lot of examples on the internet and I am really confused what to
How about this (Note, it assumes correct lengths - you should pass in the buffer length and prevent overflows, etc):
extern "C" __declspec(dllexport) void __cdecl getDataFromTable(char* tableName, char* buf)
{
std::string st = getDataTableWise(statementObject, columnIndex);
printf(st.c_str());
strcpy(buf, st.c_str());
}
Then in C#:
[DllImport("\\SD Card\\ISAPI1.dll")]
private static extern string getDataFromTable(byte[] tablename, byte[] buf);
static void Main(string[] args)
{
byte[] buf = new byte[300];
getDataFromTable(byteArray, buf);
Console.writeLine(System.Text.Encoding.ASCII.GetString(buf));
}
Note, this does make some assumptions about character encodings in your C++ app being NOT unicode. If they are unicode, use UTF16 instead of ASCII.
On windows you can return string directly using BSTR
(from wtypes.h
included by windows.h
). BSTR
can be created from standard wide string bySysAllocString
function:
_declspec(dllexport) BSTR getDataFromTable(const char* tableName)
{
return SysAllocString(L"String to return");
}
Then in C# you marshal the return type as BStr
:
[DllImport("\\SD Card\\ISAPI1.dll", CallingConvention = CallingConvention.Cdecl )]
[return: MarshalAs(UnmanagedType.BStr)]
static extern string getDataFromTable(string tableName);
I have had this problem too, recently, and though I have a solution for you, sadly I can't really explain it. I haven't found a sound explanation yet.
my c++ code for retrieving a string is:
extern "C" { __declspec(dllexport) void __GetValue__(char* str, int strlen); }
and my C# code:
[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void __GetValue__(StringBuilder str, int strlen);
So as you can see, instead of returning a value, you can supply a string (by using StringBuilder) and let C++ fill in the data like:
void __GetValue__(char* str, int strlen) {
std::string result = "Result";
result = result.substr(0, strlen);
std::copy(result.begin(), result.end(), str);
str[std::min(strlen-1, (int)result.size())] = 0;
}
And for completeness the C# code to request the string:
public String GetValue() {
StringBuilder str = new StringBuilder(STRING_MAX_LENGTH);
__GetValue__(str, STRING_MAX_LENGTH);
return str.ToString();
}
The .NET runtime uses unicode (wchar_t) strings, not ascii (char) so this require some changes. You should also consider that .NET has no way to free a string that has been allocated by a C/C++ application, so having the buffer pre-allocated and passed in from C# is the only safe way to manage this without memory leaks or worse.