gneri
gneri

Reputation: 593

C++ to C#: PInvoke \ Marshaling a callback with a complex type

I'm working with a C++ SDK to connect my C# code to some CCTV cameras. I want to use a method of the SDK to set a callback from which I can retrieve frame information.

The code from the .h file:

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

The PInvoke code:

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

The code using the PInvoke code:

// 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
}

When I run the code I get the error: "System.NullReferenceException was unhandled Message: Object reference not set to an instance of an object."

I tried Changing the callback signature to the following (i.e. instead of FrameInfo using IntPtr) -

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

Result - the callback was actually called (!) but there was nothing I could do with IntPtr frameInfo, e.g. trying to call from the callback:

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

or

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

Resulted in the error: "System.AccessViolationException was unhandled Message: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."

I also tried using the UnmanagedFunctionPointer attribute above the callback delegate and got the same error.

As you may see I am taking my first steps of juggling between managed and unmanaged code. I read a bunch of articles and listened to a couple of pluralsight topics that seemed relevant but none talked about the issue above. I think that something is wrong with the way I marshaled the FrameInfo struct but I have already marshaled similar structs successfully so honestly I have no idea what to do next.

I will appreciate any help. Thanks.

Upvotes: 2

Views: 457

Answers (1)

David Heffernan
David Heffernan

Reputation: 612954

You translated the struct as a class. That is a reference type. Which means that it is passed by reference. However the C++ code passes the strict by value.

Fix the problem by declaring FrameInfo as a struct.

Upvotes: 1

Related Questions