我有一个带有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;
答案 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);
}
对另外两个人做同样的事情。