1

I am a seasoned software engineer but somewhat new to C#. I have a DLL which is made up of C++ objects with a C interface as shown. I used this C interface because I was not sure if there was a way of accessing C++ entry points in the DLL from my C# application.

   __declspec(dllexport) void *Constructor();
   __declspec(dllexport) void Destructor(void *pObj);
   __declspec(dllexport) int Method(void *pObj, char *pSrcDst, int len);

This is the C code for the above interface.

extern "C" void *Constructor()
{
   return (void *)new Object;
}
extern "C" void Destructor(void *pObj)
{
   delete (Object *)pObj;
}
extern "C" int Method(void *pObj, char *pSrcDst, int len)
{
   if (!pObj) return 0;
   return ((Object *)pObj)->Method(pSrcDst, len);
}

So far so good. Now, I need to be able to access this interface from a C# application. From what I have learned I have implemented the following C# interface.

   unsafe public class Wrapper
   {
      [SuppressUnmanagedCodeSecurityAttribute()]
      [DllImport("MyDLL.dll")]
      public static extern
         IntPtr Constructor();
      [SuppressUnmanagedCodeSecurityAttribute()]
      [DllImport("MyDLL.dll")]
      public static extern
         void Destructor(IntPtr pObj);
      [SuppressUnmanagedCodeSecurityAttribute()]
      [DllImport("MyDLL.dll")]
      public static extern
         int Method(IntPtr pObj, [In,Out] byte[] pSrcDst, int len);

And here's the C# code which uses the above interface.

  private IntPtr pObject;
  public object()
  {
     pObject = Wrapper.Constructor();
  }
  ~object()
  {
     Wrapper.Destructor(pObject);
  }
  public override byte[] method()
  {
     byte[] bytes = new byte[100];
     int converted = Wrapper.Method(pObject, bytes, bytes.length);
     return bytes;
  }

The code asserts at both the call to Wrapper.Method as well as the Wrapper.Destructor. I assume I am not handling the pointer to the object properly. Any suggestions?

svick
  • 236,525
  • 50
  • 385
  • 514
Robert Kirnum
  • 11
  • 1
  • 2
  • What do you mean “the code asserts”? Are you getting some error message? – svick Feb 15 '12 at 21:22
  • Yes, 'PInvokeStackImbalance was detected'. – Robert Kirnum Feb 15 '12 at 21:28
  • Keep in mind that most compilers assume that `extern "C"` functions don't throw exceptions (especially if C code is calling them), so you'll want to make sure to wrap the `new Object()` bit in an exception block to prevent any exceptions (specifically `std::bad_alloc`) from escaping. – André Caron Feb 15 '12 at 21:31
  • 1
    Side note: you don't need to repeat extern "C" for every function. You can just wrap them all in the scope of an extern "C" { /*functions here*/ } – Lzh Feb 15 '12 at 22:01

2 Answers2

3

The most likely problem is that your code doesn't use the correct calling convention. In C++ the default is __cdecl, but for DllImport, the default is __stdcall.

To fix this, specify the calling convention explicitly:

[DllImport("MyDLL.dll", CallingConvention = CallingConvention.Cdecl)]
svick
  • 236,525
  • 50
  • 385
  • 514
  • In case OP doesn't know, the default calling convention in Visual Studio is a [compiler setting](http://msdn.microsoft.com/en-us/library/46t77ak2.aspx). It can be changed to `__stdcall` so as to match the default `[DllImport()]` convention. – André Caron Feb 15 '12 at 21:33
1

I was under the impression that the best way to do this sort of thing was using a managed c++/CLI wrapper.

Creating simple c++.net wrapper. Step-by-step

and

http://devmaster.net/forums/topic/7357-how-to-build-a-net-wrapper-for-a-c-library/

look like promising leads.

Community
  • 1
  • 1
Andrei
  • 4,880
  • 23
  • 30
  • 1
    If you just need to call few unmanaged functions, PInvoke is probably easier. But yeah, C++/CLI is certainly an alternative. – svick Feb 15 '12 at 21:31
  • True, but the only reason those few unmanaged functions even exist was lack of knowledge on C++/CLI. If there's a way to "do it right" why not use it. Would you really rather use a function called "void * Constructor" ? – Andrei Feb 15 '12 at 21:50
  • As long as it's not publicly exposed to the rest of the C# code, I don't have problem with that. If you know C#, learning to use PInvoke is much easier than learning to use C++/CLI. – svick Feb 15 '12 at 21:53