PInvoke implementation of a C SDK function causing memory violation

0

I am wanting to call a C DLL function from C#. The function is described in the C include header file as:

#ifdef _WIN32
#ifdef CMGO_BUILD_DLL
    #define CMGO_DLL_API __declspec(dllexport)
#else
    #define CMGO_DLL_API __declspec(dllimport)
#endif

#define CMGO_API_CC __stdcall
#else
#define CMGO_DLL_API
#define CMGO_API_CC
#endif
typedef void* CMGO_DATACHANNEL_HANDLE;
CMGO_DLL_API int CMGO_API_CC cmgo_create_datachannel(const char* host, unsigned short port, CMGO_DATACHANNEL_HANDLE* handle);

A sample C file showing how to use the cmgo_create_datachanell DLL call to create the channel has this example usage:

int main(int argc, char** argv)
{
    int st;
    char address[] = "127.0.0.1";
    unsigned int port = 9800;

    CMGO_DATACHANNEL_HANDLE handle = NULL;
    struct CMGO_DATACHANNEL_READ_RESULT result;

    memset(&result, 0, sizeof(result));

    printf("Creating DataChannel [%s,%u] ...\n", address, port);

    st = cmgo_create_datachannel(address, port, &handle);

In C# I have written the following:

const string CarmenSDKDLL = "cmgocapi.dll";

[DllImport(CarmenSDKDLL, CallingConvention= CallingConvention.Cdecl)]
static extern int cmgo_create_datachannel(string host, ushort port, IntPtr handle);

static void Main(string[] args)
{
    const string ANPRServer = "127.0.0.1";
    ushort port = 9800;
    IntPtr iHandle = new IntPtr(0);

    int iResult = cmgo_create_datachannel(ANPRServer, port, iHandle);
}

When I run the C# code I get an exception

System.AccessViolationException HResult=0x80004003 Message=Attempted to read or write protected memory.

Can anyone pointer me in the right direction and explain what I have written incorrectly in the C# code?

c#
asked on Stack Overflow Nov 30, 2020 by Robin_CHCH • edited Nov 30, 2020 by Uwe Keim

2 Answers

2

The main problem with your code is you're passing the handle incorrectly - it is in fact a void**, not a void*; meaning, it's an out IntPtr, not an IntPtr

Change the calling code as follows:

const string CarmenSDKDLL = "cmgocapi.dll";

[DllImport(CarmenSDKDLL, CallingConvention = CallingConvention.StdCall)]
extern
static int cmgo_create_datachannel([MarshalAs(UnmanagedType.LPStr)] string host,
                                   ushort port,
                                   out IntPtr handle);

There are a few corrections in the above declaration:

  • Calling convention - you need to match the declared calling convention, which is __stdcall, not __cdecl
  • String type - C# strings are Unicode, not ANSI. You need to specify correct marshalling type.
  • out parameter - Notice the declaration typedef void* CMGO_DATACHANNEL_HANDLE; - it's a void*; however, the function takes a pointer to that type - which is now a void**.

The access violation exception you are getting is specifically due to the last bullet point - the C function is trying to write to an invalid memory location.

Calling code:

const string ANPRServer = "127.0.0.1"; 
ushort port = 9800;

int errorCode = cmgo_create_datachannel(ANPRServer, port, out var handle);

if(errorCode == 0)
{
   // handle is a valid pointer of type IntPtr
}

In general, the errors that happen in the C functions called via P/Invoke may differ quite a bit from the exceptions you see in C# calling code. I recommend looking into Marshal.GetLastWin32Error() documentation and detailed example to improve the error handling in working with P/Invoke.

answered on Stack Overflow Nov 30, 2020 by CoolBots • edited Nov 30, 2020 by CoolBots
2

The handle argument is obviously used to return a handle - you need to declare and call it with ref. Other than that, string arguments generally need some care - for example, by default, C# marshals strings as Unicode, and it seems that the DLL expects ANSI strings. So my guess at the correct P/Invoke signature is

[DllImport(CarmenSDKDLL, CallingConvention= CallingConvention.Cdecl)]
static extern int cmgo_create_datachannel([MarshalAs(UnmanagedType.LPStr)] string host, ushort port, ref IntPtr handle);
answered on Stack Overflow Nov 30, 2020 by Luaan

User contributions licensed under CC BY-SA 3.0