C++到C#:PInvoke封送具有复杂类型的回调

本文关键字:复杂 类型 回调 PInvoke C++ | 更新日期: 2024-10-19 05:56:58

我正在使用C++SDK将我的C#代码连接到一些CCTV摄像机。我想使用SDK的一个方法来设置一个回调,我可以从中检索帧信息。

来自.h文件的代码:

typedef struct _frame_info
{
    unsigned long       deviceID;
    unsigned long       channel;
    unsigned long       frameType;
    unsigned long       length;
    unsigned long       keyFrame;
    unsigned long       width;
    unsigned long       height;
    unsigned long       frameIndex;
    unsigned long       frameAttrib;
    unsigned long       streamID;
    long long           time;
    long long           relativeTime;
} FRAME_INFO;
typedef void (__stdcall *PLAY_DATA_CALLBACK)(long lPlayHandle,
    FRAME_INFO frameInfo, unsigned char *pBuffer, void *pUser);
extern "C" __declspec(dllimport) BOOL __stdcall SetPlayDataCallBack(
    long lPlayHandle, PLAY_DATA_CALLBACK fPlayDataCallBack, void *pUser);

PInvoke代码:

[StructLayout(LayoutKind.Sequential, Pack = 0)]
public class FrameInfo
{
    public uint deviceID;
    public uint channel;
    public uint frameType;
    public uint length;
    public uint keyFrame;
    public uint width;
    public uint height;
    public uint frameIndex;
    public uint frameAttrib;
    public uint streamID;
    public long time;
    public long relativeTime;
}
public delegate void PlayDataCallback(int playbackHandle,
    FrameInfo frameInfo, IntPtr buffer, IntPtr userData);
[DllImport("SDK.dll")]
public static extern bool SetPlayDataCallBack(int playbackHandle,
    PlayDataCallback playDataCallback, IntPtr userData);

使用PInvoke代码的代码:

// Class member to keep the callback alive
PlayDataCallback _callbackDelegate;
// Assigning the callback
_callbackDelegate = new PlayDataCallback(MyCallback);
// Setting the callback
SetPlayDataCallBack(_playbackId, _callbackDelegate, IntPtr.Zero);
// The callback
private void MyCallback(int playbackHandle, FrameInfo frameInfo,
    IntPtr buffer, IntPtr userdata)
{
    // wishful thinking
}

当我运行代码时,我得到错误:"System.NullReferenceException未处理消息:对象引用未设置为对象的实例。"

我尝试将回调签名更改为以下内容(即使用IntPtr代替FrameInfo)-

public delegate void PlayDataCallback(int playbackHandle, IntPtr frameInfo,
    IntPtr buffer, IntPtr userData);

结果-实际上调用了回调(!),但我对IntPtr frameInfo无能为力,例如尝试从回调调用:

Marshal.PtrToStructure(frameInfo, typeof(FrameInfo));

var a = new IntPtr[14];
Marshal.Copy(frameInfo, a, 0, 1); // '1' is just an example, any number except '0' results in the same error

导致错误:"System.AccessViolationException未处理消息:试图读取或写入受保护的内存。这通常表示其他内存已损坏。"

我还尝试在回调委托上方使用UnmanagedFunctionPointer属性,但得到了相同的错误。

正如您所看到的,我正在迈出在托管代码和非托管代码之间切换的第一步。我读了一堆文章,听了几个似乎相关的多元话题,但没有一篇谈到上面的问题。我认为我封送FrameInfo结构的方式有问题,但我已经成功封送了类似的结构,所以老实说,我不知道下一步该怎么办。

我将感谢任何帮助。谢谢

C++到C#:PInvoke封送具有复杂类型的回调

您将结构转换为一个类。这是一个引用类型。这意味着它是通过引用传递的。然而,C++代码通过了strict by值。

通过将FrameInfo声明为结构来解决此问题。