首页 文章

从C#调用C DLL函数 - 将char参数转换为字符串

提问于
浏览
0

我试图从我的C#应用程序中用C编写的DLL调用一个方法 . 以下是我实现调用的方式:

  • C方法签名:(来自第三方公司)

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类型定义:

#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方法声明:

[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);

方法调用:

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;
        }
    }

然后我得到 Win32Exception: 1008 - An attempt was made to reference a token that does not exist

我知道问题必须在非托管(C)和托管(C#)代码之间的CHAR到STRING转换中 . 如果我修改 BindingLogPath 参数以输入 SByte ,它不知道如何将文本传递给方法,因为它需要一个SByte变量...

我知道我可能不得不使用像 MarshalAs 这样的东西,但是我已经取得了成功 .

谁能告诉我我做错了什么?

非常感谢你!!


这是回调定义:

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);

问题是C DLL方法需要"REF"参数 . 对 SS_Initialize 的调用将执行附加到 ProcessMessage 回调函数 . 在这个函数中,我需要能够从SS_Initialize中获取修改后的参数(ref)...

您能否建议您如何构建代码?

谢谢!!

3 回答

  • 0

    谢谢 RavadreR Ubben ,你的帮助结合起来了!

    最后,一切都是为了正确控制参数中的REF子句并使用MarshalAs标签 . C部分中作为“const char *”的参数实际上不需要REF标记 . 我还使用[MarshalAs(UnmanagedType.LPStr)]作为字符串参数 . 而已!

    ps: Stackoverflow.com is amazing! I've already found a lot of answers to other problems here just by searching. This is my first post and I'm surprised with the quick replies I've got for this one. Congratulations to all staff team and members! ;)

  • 0

    您不需要将ref用于Binding和LogPath;它们是const char *并且不会改变 .

    回调中的ResponseMessage将返回一个字符串数组,(char **)而不仅仅是一个字符串 . 响应大小可能表示阵列的深度 . 尝试[MarshalAs(UnamangedType.LPArray,ArraySubType = UnmanagedType.LPStr)] string [] .

  • 0

    对于编组字符串,您不会传递 ref string ,只需 string . 此外,您应该使用 [MarshalAs( UnmanagedType.LPStr)] 装饰每个字符串以指示您正在传递包含ascii字符的字符串并且它已终止 .
    编辑:你能否给出你对这个回调程序的定义,因为你可能犯了类似的错误 .

相关问题