C# – Call C DLL function from C# – convert char parameter to string

cchardllstring

I'm trying to call a method from a DLL written in C, from my C# application. Here is how I've implemented the call:

–> C method signature: (from a third-part company)

Int16 SSEXPORT SS_Initialize(Uint16 ServerIds[], Uint16 ServerQty, const char PTR *Binding, const char PTR *LogPath, Uint16 LogDays, Int16 LogLevel,
Uint16 MaxThreads, Uint16 ThreadTTL, Int16 (CALLBACK *ProcessMessage)(Uint16, const char PTR *, Uint32, char PTR **, Uint32 PTR *, Int16 PTR *));

–> C types definition:

#ifndef _CSISERVERTYPES_
#define _CSISERVERTYPES_

#if defined(OS_NT)

#define CALLBACK __stdcall 
#define SSEXPORT __stdcall
#define PTR      

typedef char                Int8;
typedef unsigned char       Uint8;
typedef short               Int16;
typedef unsigned short      Uint16;
typedef long                Int32;
typedef unsigned long       Uint32;
typedef unsigned char       Byte;
typedef unsigned short      Word;
typedef unsigned long       Dword;
typedef short               Bool;

#endif

typedef Int16 (CALLBACK *PM_Function)
        (Uint16,const char PTR *,Uint32,char PTR **,Uint32 PTR *,Int16 PTR *);

#define SS_OK               0
#define SS_NOT_INIT         -1
#define SS_PROC_ERR         -2
#define SS_ERR_TCP_SRV      -3
#define SS_ERR_OUT_MEM      -4

#ifndef NULL
#define NULL                0
#endif

#endif

–> C# DLL Method declaration:

[DllImport("CSITCPServer.dll", SetLastError = true)]
    static extern Int16 SS_Initialize(
        UInt16[] ServerIds,
        UInt16 ServerQty,
        ref string Binding,
        ref string LogPath,
        UInt16 LogDays,
        Int16 LogLevel,
        UInt16 MaxThreads,
        UInt16 ThreadTTL,
        ProcessMessageCallback callback);

Method call:

public static void Call_SS_Initialize()
    {   
        Int16 ret;
        UInt16[] serverIds = new UInt16[] { 2020, 2021 };
        string binding = "";
        string logPath = "";

        try
        {
            ret = SS_Initialize(
                serverIds,
                Convert.ToUInt16(serverIds.ToList().Count),
                ref binding,
                ref logPath,
                10,
                0,
                256,
                300,
                ProcessMessage);

            Console.WriteLine("Call_SS_Initialize() -> Result of SS_Initialize:  {0}", ret.ToString());
        }
        catch (Exception ex)
        {
            Int32 err = Marshal.GetLastWin32Error();
            throw new Win32Exception(err);
            //throw;
        }
    }

And then I get the Win32Exception: 1008 – An attempt was made to reference a token that does not exist

I know that the problem must be in the CHAR to STRING conversion between unmanaged (C) and managed (C#) codes. If I modify the Binding or LogPath parameters to type SByte, it doesn't give any errors. But since the method expects for a text (string), I don't know how can I pass the text to the method since it expects a SByte variable…

I'm aware that I might have to use something like MarshalAs, but I've tried a few options and didn't had any success.

Can anybody tell me what am I doing wrong??

Thank you very much!!


Here is the callback definition:

 public delegate Int16 ProcessMessageCallback(
        UInt16 ServerId,
        [MarshalAs(UnmanagedType.LPStr)] ref string RequestMsg,
        UInt32 RequestSize,
        [MarshalAs(UnmanagedType.LPStr)] ref string ResponseMsg,
        ref UInt32 ResponseSize,
        ref Int16 AppErrCode);

The thing is that the C DLL method is expecting a "REF" parameter. The call to SS_Initialize attachs the execution to the ProcessMessage callback function. In this function I need to be able to get the modified parameters (ref) from SS_Initialize …

Could you please suggest how do you think the code should be structured?

Thank you!!

Best Answer

You do not need to use ref for Binding and LogPath; they are const char* and will not change.

That ResponseMessage in the callback will return an array of strings, (char **) not just a single string. The Response size probably will indicate the depth of the array. Try [MarshalAs(UnamangedType.LPArray, ArraySubType=UnmanagedType.LPStr)] string[] instead.