PInvoke使用wchar16_t参数调用函数

时间:2016-02-05 10:51:49

标签: c# c++ pinvoke

我有一个带有C ++函数的.dll,它带有const wchar16_t*个参数。 我正在尝试导入并在c#中使用它,包含字符串,字符数组和带有额外'\ 0'字符的char数组,但我没有结果。 当我在调试模式下在原始c ++程序中检查它时,它在末尾有额外的'\ 0'字符。我应该使用什么样的确切类型?

P.S。由于这些参数,我不能100%确定问题出现了。 如果有人能够仔细研究我attach说明问题的小项目,我会非常感谢并给予很多回复。 C ++程序运行正常(我们得到登录响应),但在c#项目中,OnLoginResponseCallback永远不会激活。

我在C#中做了什么

[DllImport("ActiveTickServerAPI.dll", CharSet = CharSet.Unicode, EntryPoint = "?ATCreateLoginRequest@@YA_K_KPB_W1P6AX00PAU_ATLOGIN_RESPONSE@@@Z@Z", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
        private static extern ulong ATCreateLoginRequest(ulong sessionId, string user, string pwd, ATLoginResponseCallback onLoginResponse);

 public delegate void ATLoginResponseCallback(ulong hSession, ulong hRequest, ATLOGINRESPONSE response);
        public delegate void ATRequestTimeoutCallback(ulong origRequest);
 static void Main(string[] args)
    {
 lastRequest = ATCreateLoginRequest(hSession, userId, pw, OnLoginResponseCallback); 
                bool rc = ATSendRequest(hSession, lastRequest, 3000, OnRequestTimeoutCallback);
    }
 static void OnLoginResponseCallback(ulong hSession, ulong hRequest, ATLOGINRESPONSE response)
        {
            Console.WriteLine("!THIS SHOULD FIRE, but fire only timeout callback");
        }

 [StructLayout(LayoutKind.Sequential)]
 public struct ATLOGINRESPONSE
    {
        public byte loginResponse;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
        public byte[] permissions;
        public ATTIME serverTime;
    }

[StructLayout(LayoutKind.Sequential)]
public struct ATTIME
{
    public ushort year;
    public ushort month;
    public ushort dayOfWeek;
    public ushort day;
    public ushort hour;
    public ushort minute;
    public ushort second;
    public ushort milliseconds;
}

在c ++中它的工作(获得登录响应)。这是来自api docs的功能描述:

ACTIVETICKSERVERAPI_API uint64_t ATCreateLoginRequest   (   uint64_t    session,
    const wchar16_t *   userid,
    const wchar16_t *   password,
    ATLoginResponseCallback     pCallback 
)   

来自c ++的工作结构

typedef struct _ATLOGIN_RESPONSE
{
    ATLoginResponseType loginResponse;
    uint8_t permissions[255];
    ATTIME serverTime;
} ATLOGIN_RESPONSE, *LPATLOGIN_RESPONSE;

    typedef struct _ATTIME
{
    uint16_t year;
    uint16_t month;
    uint16_t dayOfWeek;
    uint16_t day;
    uint16_t hour;
    uint16_t minute;
    uint16_t second;
    uint16_t milliseconds;
} ATTIME, *LPATTIME;

2 个答案:

答案 0 :(得分:1)

ATLOGINRESPONSE的声明几乎肯定是错误的。

public class ATLOGINRESPONSE
{
    public byte loginResponse;
    public byte[] permissions;
    public ATTIME serverTime;
}

通过将其声明为类,可以确保通过引用对其进行编组。但是你排除了它被价值所束缚。哪个可能没事。无论如何,我认为我更愿意将其声明为结构。

此外,前两个参数对我来说是错误的。第一个可能是一个枚举,应该这样声明。第二个是字节数组,但您需要指定如何编组它。像这样:

[StructLayout(LayoutKind.Sequential)]
public struct ATLOGINRESPONSE
{
    public ATLoginResponseType loginResponse; // you need to define the ATLoginResponseType enum
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 255)]
    public byte[] permissions;
    public ATTIME serverTime;
}

我们不知道ATTIME是否被正确宣布。

我在这里取消了您的功能名称:https://demangler.com/

C ++函数具有此签名

unsigned __int64 __cdecl ATCreateLoginRequest(
    unsigned __int64,
    wchar_t const *,
    wchar_t const *,
    void (__cdecl*)(unsigned __int64,unsigned __int64,struct _ATLOGIN_RESPONSE *)
)

因此,您的委托被声明为错误的调用约定。它应该是:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ATLoginResponseCallback(
    ulong hSession, 
    ulong hRequest, 
    ref ATLOGINRESPONSE response
);

请注意,将ATLOGINRESPONSE更改为结构后,我们必须将参数设为ref参数。

在C#代码中正确声明了函数ATLoginResponseCallback

您的其他代表也需要与[UnmanagedFunctionPointer(CallingConvention.Cdecl)]一起宣布。

除非仅从ATCreateLoginRequest调用回调,否则传递的委托将被垃圾收集。因此,如果ATCreateLoginRequest获取该委托的副本,并在稍后调用,那么您将需要使委托保持活动状态。将其分配给类型为ATLoginResponseCallback的变量,其寿命超出了对回调的最终调用。

问题完全在其他地方。

答案 1 :(得分:1)

我下载了您的测试项目并立即修复了委托声明,因为它们错了:

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void ATSessionStatusChangeCallback(ulong hSession, byte statusTyp);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void ATLoginResponseCallback(ulong hSession, ulong hRequest, ref ATLOGINRESPONSE response);

    [UnmanagedFunctionPointer(CallingConvention.Cdecl)]
    public delegate void ATRequestTimeoutCallback(ulong origRequest);

我第一次运行我得到的程序:

SessionStatus - Connected
request timeout
!THIS SHOULD FIRE
SessionStatus - Connected
!THIS SHOULD FIRE

第二次及以后我得到了:

SessionStatus - Connected
!THIS SHOULD FIRE

没有超时。为什么它第一次表现的方式很难猜测。你可能需要等待ATSendRequest(),直到你得到正确的登录确认,等等。

但是明确修复代表处理问题,你现在正在回调中获得正确的hSession值。它还不完善,你需要确保它们不能被垃圾收集。将它们存储在静态变量中:

static ATSessionStatusChangeCallback statusCallback;
....
    if (res == true) {
        statusCallback = new ATSessionStatusChangeCallback(OnSessionStatusChangeCallback);
        res = ATInitSession(sessionNumber, "activetick1.activetick.com",
                 "activetick2.activetick.com", 443, statusCallback, true);
    }

对另外两个人做同样的事情。