我正在尝试从我的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)您能否建议您如何构建代码?
谢谢!
答案 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太神奇了!我已经通过搜索找到了很多其他问题的答案。这是我的第一篇文章,我对这篇文章的快速回复感到惊讶。祝贺所有员工团队和成员! ;) 强>