在C#interop中传递结构指针会导致NULL

时间:2017-10-18 10:05:44

标签: c# pointers interop unmanaged

我正在构建一个托管DLL,用于非托管环境(C / C ++应用程序 - FreeRDP)。在大多数情况下,Interop工作正常,但在一个特定的情况下,我无法传递指向struct的指针。 在API中我有一个结构:

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

以及我正在调用的函数:

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

两者都翻译成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;

执行代码:

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);

虽然pChannelManager(这是我从非托管代码调用我的DLL获得的指针)和字符串发送没有任何问题,我在这里创建的指针(pCallback)在C#中成功分配,但它导致NULL在非托管代码中。

我认为问题是我如何定义结构,或者我是如何定义函数的(虽然函数在非托管代码中被成功调用)。我使用该方法以与DLL的另一部分完全相同的方式创建指针,并且在传递给非托管函数时它完全正常。

编辑: 由@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);

没有工作。

EDIT2:解决方法!仅当您有权访问原始的非托管代码时。我重新安排了函数参数,所以结构指针是第一个,如下所示:

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

它有效!可能是偏移问题。

1 个答案:

答案 0 :(得分:0)

这是一个抵消问题。 C / C ++ typedef unsigned longulong,我错误地假设它对应于C#ulong,但实际上第一个是Visual中的4个字节,而另一个是8个字节,这导致了4个字节字节偏移量。修复了将uint更改为[MarshalAs(UnmanagedType.U4)]并添加[UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, [MarshalAs(UnmanagedType.U4)] uint flags, IntPtr listenerCallback); 以获得良好衡量标准的方法。我在C#中调用的函数的最终外观:

{{1}}