10

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#

Rawat
  • 461
  • 3
  • 6
  • 23
  • OutOfMemory Exception at console.writeLine(String value) – Rawat Oct 07 '15 at 12:06
  • 1
    Sure, nobody is going to call delete[] to get the string buffer space released again. That eventually ends with OOM. You cannot do it this way. Try CoTaskMemAlloc() first. Get ahead next by allowing the caller to supply the buffer, like int getDataFromTable(char* tableName, char* buffer, int buflen). Must be a StringBuilder on the C# side with enough Capacity. – Hans Passant Oct 07 '15 at 13:10

5 Answers5

9

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();
}
Bas in het Veld
  • 1,271
  • 10
  • 17
7

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.

user3690202
  • 3,844
  • 3
  • 22
  • 36
4

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);
Bohdan
  • 468
  • 5
  • 6
2

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.

Valter Minute
  • 2,177
  • 1
  • 11
  • 13
0

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");
}
Cyclone
  • 23
  • 8
  • Your answer could be improved with additional supporting information. Please [edit] to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Jan 09 '22 at 02:16