C++ 到 C#:PInvoke \ 封送具有复杂类型的回调
C++ to C#: PInvoke \ Marshaling a callback with a complex type
我正在使用 C++ SDK 将我的 C# 代码连接到一些闭路电视摄像机。我想用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 属性,但得到了同样的错误。
如您所见,我正在迈出处理托管代码和非托管代码的第一步。我阅读了一堆文章并听取了一些看似相关但 none 讨论了上述问题的 pluralsight 主题。
我认为我编组 FrameInfo 结构的方式有问题,但我已经成功编组了类似的结构,所以说实话,我不知道下一步该做什么。
如有任何帮助,我将不胜感激。谢谢。
您将结构翻译为 class。那是一个引用类型。这意味着它是通过引用传递的。但是,C++ 代码按值传递严格。
通过将 FrameInfo
声明为结构来解决问题。
我正在使用 C++ SDK 将我的 C# 代码连接到一些闭路电视摄像机。我想用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 属性,但得到了同样的错误。
如您所见,我正在迈出处理托管代码和非托管代码的第一步。我阅读了一堆文章并听取了一些看似相关但 none 讨论了上述问题的 pluralsight 主题。 我认为我编组 FrameInfo 结构的方式有问题,但我已经成功编组了类似的结构,所以说实话,我不知道下一步该做什么。
如有任何帮助,我将不胜感激。谢谢。
您将结构翻译为 class。那是一个引用类型。这意味着它是通过引用传递的。但是,C++ 代码按值传递严格。
通过将 FrameInfo
声明为结构来解决问题。