简体   繁体   English

在 C# 互操作中传递结构指针导致 NULL

[英]Passing a struct pointer in C# interop results in NULL

I am building a managed DLL for use in unmanaged environment (C/C++ app - FreeRDP).我正在构建一个用于非托管环境的托管 DLL(C/C++ 应用程序 - FreeRDP)。 Interop works fine in most cases, but in one particular I am not able to pass a pointer to struct.互操作在大多数情况下都可以正常工作,但在一个特定情况下,我无法将指针传递给 struct。 In the API I have a struct:在 API 中,我有一个结构:

typedef struct _IWTSListenerCallback IWTSListenerCallback;
struct _IWTSListenerCallback
{
    UINT(*OnNewChannelConnection)(IWTSListenerCallback* pListenerCallback,
                              IWTSVirtualChannel* pChannel,
                              BYTE* Data,
                              BOOL* pbAccept,
                              IWTSVirtualChannelCallback** ppCallback);
};

As well as a function I am calling:以及我正在调用的函数:

UINT(*CreateListener)(IWTSVirtualChannelManager* pChannelMgr,
                      const char* pszChannelName,
                      ULONG ulFlags,
                      IWTSListenerCallback* pListenerCallback);

Both translated to C#:两者都转换为 C#:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ListenerCallbackNewConnectionDelegate(IntPtr listenerCallback, IntPtr channel, [MarshalAs(UnmanagedType.LPArray)] byte[] data, IntPtr accept, ref IntPtr channelCallback);

[StructLayout(LayoutKind.Sequential)]
public struct IWTSListenerCallback
{
    [MarshalAs(UnmanagedType.FunctionPtr)]
    public ListenerCallbackNewConnectionDelegate OnNewChannelConnection;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, ulong flags, IntPtr listenerCallback);

[MarshalAs(UnmanagedType.FunctionPtr)]
public ChannelManagerCreateListenerDelegate CreateListener;

And execution code:和执行代码:

var callback = new IWTSListenerCallback();
callback.OnNewChannelConnection = NewChannelConnection;
var pCallback = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IWTSListenerCallback)));
Marshal.StructureToPtr(callback, pCallback, false);
var ret = channelManager.CreateListener(pChannelManager, "TestChannel", 0, pCallback);

And while pChannelManager (which is a pointer I obtain from unmanaged code calling my DLL) and the string are sent through without any problems, the pointer I create here (pCallback) is assigned successfuly in C#, but it results in a NULL in unmanaged code.虽然 pChannelManager(这是我从调用我的 DLL 的非托管代码中获得的指针)并且字符串被发送没有任何问题,但我在此处创建的指针 (pCallback) 在 C# 中成功分配,但它导致非托管代码中的 NULL .

I assume the problem is either with how I defined the struct, or how I defined the function (although the function is being called successfuly in unmanaged code).我认为问题在于我如何定义结构,或者我如何定义函数(尽管在非托管代码中成功调用了该函数)。 I use the method to create the pointer in exact same way as in another part of the DLL, and it works perfectly fine there when passed to unmanaged function.我使用该方法以与 DLL 的另一部分完全相同的方式创建指针,当传递给非托管函数时,它在那里工作得非常好。

EDIT: By @jdweng suggestion:编辑:@jdweng 建议:

[StructLayout(LayoutKind.Sequential)]
public struct TestCall
{
    public IntPtr channelManager;
    [MarshalAs(UnmanagedType.LPStr)]
    public string channelName;
    public ulong flags;
    public IntPtr listenerCallback;
}

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr testStructure);

var test = new TestCall();
test.channelManager = pChannelManager;
test.channelName = "TestChannel";
test.flags = 0;
test.listenerCallback = pCallback;
var pTest = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FreeRDPTypes.TestCall)));
Marshal.StructureToPtr(test, pTest, false);
var ret = channelManager.CreateListener(pTest);

Didn't work.没用。

EDIT2: Workaround! EDIT2:解决方法! Only if you have access to original unmanaged code.仅当您有权访问原始非托管代码时。 I rearranged the function arguments so the structure pointers are first, like this:我重新排列了函数参数,因此首先是结构指针,如下所示:

UINT(*CreateListener)(IWTSVirtualChannelManager* pChannelMgr,
                      IWTSListenerCallback* pListenerCallback,
                      const char* pszChannelName,
                      ULONG ulFlags);

And it works!它有效! Probably a problem with offset.可能是偏移量的问题。

It was a matter of offset.这是一个抵消问题。 C/C++ ULONG was typedef unsigned long which I wrongly assumed corresponded to C# ulong , but in fact the first one is 4 bytes in Visual, while the other is 8 bytes, which resulted in 4 bytes offset. C/C++ ULONGtypedef unsigned long ,我错误地认为它对应于 C# ulong ,但实际上第一个在 Visual 中是 4 个字节,而另一个是 8 个字节,这导致了 4 个字节的偏移。 Fixed by changing ulong to uint and adding [MarshalAs(UnmanagedType.U4)] for good measure.通过将ulong更改为uint并添加[MarshalAs(UnmanagedType.U4)]以进行良好测量来修复。 Final look of the function I was calling inside C#:我在 C# 中调用的函数的最终外观:

[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, [MarshalAs(UnmanagedType.U4)] uint flags, IntPtr listenerCallback);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM