Rawat
Rawat

Reputation: 469

Return string from c++ dll export function called from c#

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 do.

My c++ code to export function:

extern "C" __declspec(dllexport)  char*  __cdecl getDataFromTable(char* tableName)
{
    std::string st = getDataTableWise(statementObject, columnIndex);
    printf(st.c_str()); 

    char *cstr = new char[st.length() + 1];
    strcpy(cstr, st.c_str());
    return cstr;
} 

When I try to call this function from c#:

[DllImport("\\SD Card\\ISAPI1.dll")]
private static extern string getDataFromTable(byte[] tablename);
static void Main(string[] args)
{
    string str = getDataFromTable(byteArray);
    Console.writeLine(str);
}

I got an error while calling it. I am creating this for WinCE 6.0

EDITED------------------------

is there something like, i can pass a empty buffer to c++ from c# and c++ function will fill the data and i can reuse it in C#

Upvotes: 10

Views: 32297

Answers (5)

Cyclone
Cyclone

Reputation: 41

This simply works :)

_strdup creates a copy of the string in the memory which only can be destroyed using delete.

https://learn.microsoft.com/en-us/cpp/c-runtime-library/reference/strdup-wcsdup-mbsdup?view=msvc-170

const char* returnStringFunc()
{
    return _strdup("String to Return");
}

Upvotes: 0

Bohdan
Bohdan

Reputation: 608

On windows you can return string directly using BSTR (from wtypes.hincluded by windows.h). BSTRcan 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);

Upvotes: 6

user3690202
user3690202

Reputation: 4075

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.

Upvotes: 8

Valter Minute
Valter Minute

Reputation: 2210

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.

Upvotes: 1

Bas in het Veld
Bas in het Veld

Reputation: 1322

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();
}

Upvotes: 10

Related Questions