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

时间:2009-08-04 13:48:37

标签: c# c string dll char

我正在尝试从我的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 - 尝试引用不存在的令牌

我知道问题必须在非托管(C)和托管(C#)代码之间的CHAR到STRING转换中。如果我修改绑定 LogPath 参数以键入 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 :(得分:0)

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

答案 1 :(得分:0)

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

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

答案 2 :(得分:0)

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

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

ps:Stackoverflow.com太神奇了!我已经通过搜索找到了很多其他问题的答案。这是我的第一篇文章,我对这篇文章的快速回复感到惊讶。祝贺所有员工团队和成员! ;)