2

I created dll in c++ and wanted to call function from it using c#. I has an error if function, which program calls, returns string. Dll code:

#include <string>
using namespace std;
#define EXPORT_API extern "C" __declspec(dllexport) 
EXPORT_API  void DllFunc()
{
    MessageBoxA(0,"DDL box", "Yeah!", 0);
}

EXPORT_API  string DllFuncStr()
{
    return "testStr";
}

C# application code:

[DllImport("dllka.dll")]
 static extern void DllFunc();
[DllImport("dllka.dll")]
static extern string DllFuncStr();

 private void btnStart_Click(object sender, RoutedEventArgs e)
 {
     DllFunc();
     string str = DllFuncStr();           
 }

"DllFunc();" - works nice, but on line "string str = DllFuncStr();" i had an error:

The runtime has encountered a fatal error. The address of the error was at 0x5e6dceca, on thread 0x16b0. The error code is 0xc0000005. This error may be a bug in the CLR or in the unsafe or non-verifiable portions of user code. Common sources of this bug include user marshaling errors for COM-interop or PInvoke, which may corrupt the stack.

What is wrong with string type? How to fix this problem?

Alex
  • 177
  • 2
  • 15

2 Answers2

5

You can't marshal std::string from C++ to C#. Instead either use a zero-terminated string marshalled with StringBuilder, or return a BSTR.

The BSTR approach is quite simple. If you prefer zero-terminated string then look on the web for sample P/Invokes from Win32 APIs, e.g. GetWindowText().

You are exporting from C++ with cdecl calling convention, but using stdcall in the C# code. You'll need to match those up once you have sorted the data type marshalling out. It doesn't matter which you use, so long as it's the same at both ends.

You will also need to broach the fact that your C++ code uses char (8 bit encoding) and C# uses Windows native UTF-16.

If it was me, I'd do it with a BSTR, as I outline in this answer to another question.

Community
  • 1
  • 1
David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
3

As David said, c++ std::string and c# string are different things. I did it following way in my application:

c++ DLL code:

EXPORT_API void DllFuncStr(char* sRetText, int nCapacity)
{
  if(!sRetText)
    return;

  std::string sRetTextStr("testStr");

  strcpy_s(sRetText, nCapacity, sRetTextStr.c_str());
  return;
}

c# application code:

[DllImport("dllka.dll")]
static extern bool DllFuncStr(StringBuilder sStrBuilder, int nCapacity);

private void btnStart_Click(object sender, RoutedEventArgs e)
{
  StringBuilder strBuilder = new StringBuilder(50);
  DllFuncStr(strBuilder, strBuilder.Capacity);
  string str = strBuilder.ToString();
}

(Thank you Hans for suggestions. I will repair this thing in my application, too)

jing
  • 1,919
  • 2
  • 20
  • 39
  • 2
    This is a good approach. However, it is crucial that you add an argument that says how large the buffer is. Not doing so invites heap corruption due to buffer overflow, horribly hard to debug. Pass StringBuilder.Capacity. Also, strcpy_s() is good, you are however using it incorrectly. It can *never* provide protection if you pass the string length instead of the buffer length. – Hans Passant Mar 19 '11 at 21:32
  • You did. But either use strcpy() or fix the strcpy_s() argument. Letting it bomb the program is usually the better choice btw, a bool return value is so often ignored. Unfortunately it is a very unfriendly bomb. – Hans Passant Mar 19 '11 at 22:57
  • You're right, I didn't read MSDN carefully. I repaired the argument. – jing Mar 19 '11 at 23:50