0

I have a native C++ function that I have to call from my C# code, the C++ function looks like this:

__declspec(dllexport) int getCameraFrame(void *buffer);

Calling the function in C++ looks like this:

unsigned char* data = new unsigned char[640 * 480 * 4];
getCameraFrame(data);

From C# I import the function with PInvoke as:

[DllImport("CameraSDK.dll")]
public static extern int getCameraFrame(???);

What do I use as a parameter here, and how would I call the function from my C# code?

byte[] buffer;
getCameraFrame(out buffer); //???

UPDATE

I have tried using string, byte[], char[], IntPtr as parameters but I keep getting a PInvokeStackImbalance exception.

A call to PInvoke function 'Test!Test.Form1::getCameraFrame' has unbalanced the stack. This is likely because the managed PInvoke signature does not match the unmanaged target signature. Check that the calling convention and parameters of the PInvoke signature match the target unmanaged signature.

UPDATE - Solved

With the answers given by David and Michael, I've managed to solve my issue with the below code

[DllImport("CameraSDK.dll", CallingConvention.Cdecl)]
public static extern int getCameraFrame([Out] StringBuilder buffer);

StringBuilder data = new StringBuilder(1024 * 768 * 4)
int result = getCameraFrame(data);

David, your answer was indeed correct, note that my StringBuilder init is 1024 * 768 * 4 (and not 640 * 480 * 4 - this is what was throwing the AccessViolationException) - in the C++ code the buffer is scaled from (1024 * 768 * 4) to (640 * 480 * 4) - I must have missed that part, so, that's my bad.

I'm able to get the raw data but cannot convert it to a bitmap (raw data is in some weird format) but that is another question.

Thanks

langjacques
  • 407
  • 5
  • 16

2 Answers2

2

I have a following working code:

C++ definition:

__declspec(dllexport) void SendRenderedImage(byte buffer[]);

C# declaration

[DllImport("External.dll", EntryPoint = "SendRenderedImage", CallingConvention = CallingConvention.Cdecl)]
public static extern void SendRenderedImage(
    [MarshalAs(UnmanagedType.LPArray)] byte[] buffer
);

C# usage:

SendRenderedImage(new byte[10]);

Hope it helps.

  • Right, one step closer, I'm able to call the native function, however, when I call it a second time I get a AccessViolationException - Attempted to read or write protected memory - any ideas? – langjacques Jul 28 '15 at 14:42
  • Can you show the code which calls the function? Do you get the exception before you reach the C++ code? – Michael Gopshtein Jul 28 '15 at 15:04
  • I have included the C++ & C# bits that calls the function, the exception is thrown on the (with the modified code from above) getCameraFrame(data) line. – langjacques Jul 29 '15 at 05:17
  • When you call the function, you don't need the `out` keyword. And in your example you call the function with uninitialized `byte[] buffer`, you have to do `new byte[n]` first and, probably, fill the data in. – Michael Gopshtein Jul 29 '15 at 06:45
  • @Michael No. The data flows out. No need to initialize it. – David Heffernan Jul 29 '15 at 07:15
  • Can you clarify on that? Do you want the C++ code to "return" the data? Is the data size known? If yes - you can allocate the buffer in C# and have C++ fill the data in. If the size is unknown - you need to allocate the buffer in C++ code as part of the function call. So what is the use case? – Michael Gopshtein Jul 29 '15 at 07:18
  • In your example of _Calling the function in C++_ you first allocate the buffer with `new` and only then you call the function. So the size is known in advance? Do you have `new` also inside the function? – Michael Gopshtein Jul 29 '15 at 07:26
0

I'd declare it like this:

[DllImport(dllname, CallingConvention = ???)]
public static extern int getCameraFrame(
    [Out] byte[] buffer
);

Call the function like this:

byte[] data = new byte[640 * 480 * 4];
int retval = getCameraFrame(data);

Note that you need to work out what the calling convention is. On the face of it, it looks like cdecl.

I use the [Out] attribute to indicate that the data flows from callee to caller. However, since the marshaller will simply pin the array, this has no discernible impact. It does at least document the intent for future readers.

Update

You say in comments that you encounter an access violation when using this code The code here is accurate given the information you have provided. The problem therefore must be in something we cannot see. Perhaps the library has not been initialized. Perhaps the funcions are instance methods. Or perhaps there is some other problem that cannot be discerned.

You need to look beyond what you have shown in the question. Were I in your place I'd start by making a complete C++ program that uses the library, to prove that the library works and that you know what sequence of calls to make. Then I'd translate that to C#.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • How can I determine what the calling convention is? The function's return type is int, I'll edit my question... Putting [Out] before the byte[] parameter takes me back to square one i.e. getting a PInvokeStackImbalance exception. – langjacques Jul 28 '15 at 14:44
  • Have you pasted the exact declaration yet? The question history makes me doubt that we've seen the actual C++ code yet. – David Heffernan Jul 28 '15 at 15:01
  • I have included all the code that relates to my issue, I have no idea what the function body looks like because it is in an SDK dll file (which is obfuscated) – langjacques Jul 29 '15 at 05:12
  • The calling convention is indeed Cdecl – langjacques Jul 29 '15 at 05:45
  • Given the information in the question, my answer is accurate. – David Heffernan Jul 29 '15 at 06:29
  • I tried it with your code exactly, still getting an AccessViolationException – langjacques Jul 29 '15 at 06:36
  • The problem is elsewhere. Given the information in the question, my answer is accurate. Instead of hoping we can work it out based on information we don't have, you need to look deeper. First of all convince yourself that I am right by making your DLL that populates a caller supplied array. Use the exact same signature as the real one. Then you'll know to look elsewhere. – David Heffernan Jul 29 '15 at 06:41