2

Ok my series of problems with a laser device controller goes on... I want to call the following C++ function within a DLL from my C# code:

extern "C" _declspec(dllimport) int SendFrame(DWORD deviceIndex, byte* pData, DWORD numOfPoints, DWORD scanrate);

The pointer pData points to an array of laser points that are defined in the C++ header file as follows:

#pragma pack (1)
struct LaserPoint {
    WORD x;
    WORD y;
    byte colors[6];
};

On the C# side I defined the function import as follows:

[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, ref byte[] pData, UInt32 numOfPoints, UInt32 scanrate);

... and the LaserPoint struct like this:

[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct LaserPoint {
    public UInt16 x;
    public UInt16 y;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
    public byte[] colors;
}

Then I call the SendFrame function like that:

LaserPoint point = new LaserPoint();
point.x = 16384;
point.y = 32768;
point.colors = new byte[] {255, 0, 0, 0, 0, 0};

byte[] arr = point2array(points);
SendFrame(0, ref arr, 1, 30000);

This is my function to convert the LaserPoint struct instance to a byte array:

private static byte[] point2array(object obj) {
    int len = Marshal.SizeOf(obj);
    byte[] arr = new byte[len];
    IntPtr ptr = Marshal.AllocHGlobal(len);
    Marshal.StructureToPtr(obj, ptr, true);
    Marshal.Copy(ptr, arr, 0, len);
    Marshal.FreeHGlobal(ptr);
    return arr;
}

But my laser device does not receive correct input, since the laser behaves very weird. Using the same code within the C++ project works fine. So the bug is somewhere with this C# interoperability code.

Any ideas?

3
  • Wow that's it :) ! Please make an answer and I'll check it as solution. Thank you! Commented May 2, 2018 at 13:52
  • No problem. :-] Sorry for the brief answer, on mobile at the moment. Commented May 2, 2018 at 13:54
  • As an aside, are you in full control of both the C++ side and the C# side of the code? Because SendFrame could be easily changed to obviate the need for point2array (pretty hideous as-is)... Commented May 2, 2018 at 13:57

2 Answers 2

3

Arrays are already reference types, so ref byte[] is a double indirection, akin to byte** — lose the ref.

Sign up to request clarification or add additional context in comments.

Comments

1

If you only intend to use LaserPoint structs here you can define the function as

[DllImport("..\\..\\dll\\StclDevices.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern int SendFrame(UInt32 deviceIndex, LaserPoint[] pData, UInt32 numOfPoints, UInt32 scanrate);

And get rid of the copying and let the runtime do that for you.

Otherwise as ildjarn says, getting rid of ref should work, as byte arrays are already reference (pointer) types.

For examples of how to do this stuff, the PInvoke wiki has got structs and signatures for most of the windows API, so while your call won't be there, there are lots of similar examples.

1 Comment

Thank you very much for your answer and suggestion. I had ref LaserPoint[] pData before which didn't work. But LaserPoint[] pData works fine too :-) Seems I did another mistake with pointers before. I will discard my silly point2array function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.