C# PInvoke pass out unsigned char* - c#

I am trying to create a simple dll in C++ and call it from C# using PInvoke. I want to pass in a byte array, do some manipulation on it, and send back another byte array. I figured I would pass in the frame and size then create an unmanaged unsigned char*. I would pass that back in an out IntPtr and return the size. Then later I would free that memory with another function. Everything is working ok except I cannot get the out IntPtr to work. I always just get 0 back. I created my C++ dll in qt. Here is the code I have so far.
#pragma once
#ifdef TOOLS_EXPORTS
#define TOOLS_API __declspec(dllexport)
#else
#define TOOLS_API __declspec(dllimport)
#endif
class Tools
{
public:
int TOOLS_API TestFunction(const unsigned char *inData, int inSize, unsigned char *outData);
};
#include "Tools.h"
int Tools::TestFunction(const unsigned char *inData, int inSize, unsigned char* outData)
{
outData = (unsigned char*)malloc(sizeof(unsigned char) * inSize);
memcpy(outData, inData, sizeof(unsigned char) * inSize);
return inSize;
}
[DllImport("Tools.dll", EntryPoint = "?TestFunction#Tools##QAEHPBEHPAE#Z", CallingConvention = CallingConvention.StdCall)]
public static extern int TestFunction(byte[] inData, int inSize, out IntPtr outData);
IntPtr outData;
int test = TestFunction(data, data.Length, out outData);

You have the final parameter as:
unsigned char* outData
This is a pointer, passed by value. Any modifications to the address are not seen by the caller, because the address is passed by value.
You need to return the address of the memory you allocate to the caller. So you need:
unsigned char** outData
Then in the implementation of the function in your C++ code you replace references to outData with *outData.
By definition sizeof(unsigned char) == 1 and it would be idiomatic to replace sizeof(unsigned char) * inSize with inSize.

Related

C# Interop with C DLL, how do I marshal a char array where length is returned from the function?

I have to access a C DLL from C#. I don't have source for the DLL so I am stuck with the following function signature:
int func(unsigned char* instr, unsigned char* outstr, int inlength);
How do I declare the DllImport for this in C# so that .NET understands that the returned INT is the length of the outstr array. The output char* is not a C-style string. They are byte arrays and thus there can be zeroes embedded in the output.
[DllImport("the.dll")]
int func([In] string instr, [Out] ?????? outstr, int inlength);
EDIT: Please read carefully. The RETURN value is length of the outstr parameter.
Your C API is less than ideal. The function can’t allocate a new buffer and return it, and doesn’t even take the length of the buffer.
API user (you) have to know in advance upper bound of how many bytes the function will write to that array, and supply output buffer of that size.
The DLL import itself is simple:
[DllImport( "the.dll" )]
int func( [In, MarshalAs( UnmanagedType.LPArray, SizeParamIndex = 2 )] byte[] instr, [Out, MarshalAs( UnmanagedType.LPArray)] byte[] outstr, int inlength );
If the function returns count of bytes written, call the function then resize the output array.

Calling C++ dll from C#. "Cannot marshal 'return value': Invalid managed/unmanaged type combination."

My Header file.
extern "C" class MyFuncs
{
public:
__declspec(dllexport) unsigned char PassImage(unsigned char buffer, int size);
};
CPP file.
unsigned char MyFuncs::PassImage(unsigned char buffer, int size)
{
return buffer;
}
All works well except when i am returning buffer back to my main application.
[DllImport("ExampleDLL.dll", EntryPoint = "?PassImage#MyFuncs#Funcs##QAEXEH#Z")]
public static extern byte[] PassImage(byte[] a, int count);
The error occurs when i am return unsigned char to byte[].
If i change byte[] to byte i get a value back an no error.
This is the exact error:
Cannot marshal 'return value': Invalid managed/unmanaged type
combination.
How can I accept back unsigned char to byte[]?
The unsigned char should be an unsigned char * :)
EDIT:
You also need to pass the length of the array and handle it as a byte pointer in C# because .NET doesn't know the length of it. Described in this article: https://stackoverflow.com/questions/8268625/get-pointer-on-byte-array-from-unman‌​aged-c-dll-in-c-sharp
In C++ you return an unsigned char, which is one byte long. In C# you expect a byte array. You probably want to return an unsigned char * from C++.

C# Marshaling an ushort/ulong array

I have a C-DLL + header file and try to p/invoke a function from C#. I also have some example C++ code of how to use the function. Here is the function definition:
int GetData(unsigned char* buffer, long bufferSize);
The more interesting part is the example code and how the function can be called:
if(dataSize == 16)
{
unsigned short* usData = new unsigned short[m_numX * m_numY * 3 / 2];
GetData( (unsigned char*)usData, m_numX * m_numY * sizeof(unsigned short) );
}
else if (dataSize == 32)
{
unsigned long* ulData = new unsigned long[m_numX * m_numY];
GetData( (unsigned char*)ulData, m_numX * m_numY * sizeof(unsigned long) );
}
So, depending on the dataSize variable, the actual data array can be an ushort or an ulong. However, it is always passed as an unsigned char pointer.
For the sake of simpleness I just tried to get at least one of the variants to work. Here's the code I tried for dataSize = 16
[DllImport("External.dll", EntryPoint = "GetData", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(ref ushort[] pBuffer, long lBufferSize);
long bufferSize1 = numX * numY * 3 / 2;
long bufferSize2 = numX * numY * sizeof(ushort);
ushort[] data = new ushort[bufferSize1];
GetData(ref data, bufferSize2)
If I run above code, the application quits with an 'Access Violation' Exception. That usually means, that the unsafe code tried to write over the buffer limits or that the p/invoke declaration has an error. I tried huge buffers (which would be able to hold any kind of data I'm expecting) but my guess would be, that my mistake is in the declaration.
I also tried to declare the buffer as byte[] (since the example casts it as unsigned char*) and ulong in the p/invoke declaration. same for the actual buffer I pass as reference. the error remains the same.
How can I make this work?
A couple of mistakes:
The array must not be passed as ref. That is because ref ushort[] matches unsigned short**.
C++ long does not match C# long on Windows. The former is 32 bits, the latter 64 bits.
You need to import like this:
[DllImport("External.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(
[Out] ushort[] pBuffer,
int lBufferSize
);
It would be perfectly reasonable, for convenience, to use an overload for the 32 bit data variant:
[DllImport("External.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int GetData(
[Out] uint[] pBuffer,
int lBufferSize
);
Likewise, and overload for an array of byte would also be valid should you need it.

SWIG unsigned char to byte array c# [duplicate]

I am calling a C++ dll from my C# program. The DLL consists of several functions and I am able to call most of them except this one.
The C++ function is like as below:
__declspec(dllexport) uint8_t* myHash(const char *filename)
{
uint8_t *hash = (unsigned char*)malloc(72*sizeof(uint8_t));
//some processing on hash
return hash;
}
As can be seen in the above code, the hash function stores a character array. I want to receive the values in my C# program but I am not able to do it.
My C# code is like as below:
[DllImport("myHash.dll", CharSet = CharSet.Ansi)]
public static extern IntPtr myHash(string filename);
IntPtr ptr = myHash(fileA);
char[] result = new char[72];
Marshal.Copy(ptr, result, 0, 72);
The problem is that char in C# is a 16 bit character element. Your C++ code returns an array of 8 bit uint8_t values. You should switch to using a byte array instead.
[DllImport("myHash.dll", CallingConvention=CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
public static extern IntPtr myHash(string filename);
....
IntPtr ptr = myHash(fileA);
byte[] result = new byte[72];
Marshal.Copy(ptr, result, 0, 72);
I specified a calling convention because, as written, your function is __cdecl. Perhaps you omitted something in the transcribing of the question, but the declaration above matches the unmanaged code in the question.
This function would be much better designed to allow the caller to allocate the buffer. That avoids you having to export a deallocator from the C++ code. I'd write the C++ like this:
__declspec(dllexport) int myHash(const char *filename, uint8_t* hash)
{
// calculate hash and copy to the provided buffer
return 0; // return value is an error code
}
And the corresponding C# code:
[DllImport("myHash.dll", CallingConvention=CallingConvention.Cdecl,
CharSet = CharSet.Ansi)]
public static extern int myHash(string filename, byte[] hash);
....
byte[] hash = new byte[72];
int retval = myHash(fileA, hash);
This function hard-codes in its interface that the buffer is of length 72. That might be reasonable, but it might make sense to pass the length of the buffer too so that the unmanaged code can defend against buffer overruns.
Note that although you refer to the output of this function as a character array, the use of uint8_t* makes it seem more likely to be a byte array. If it really is a character array, then you can use Encoding.GetString() to convert into a string.

Call C++ function in C# from DLL - strange parameters

I have a function writen in C++ with the next header:
void EncodeFromBufferIN(void* bufferIN,int bufferINSize, unsigned char* &bufferOUT, int &bufferOUTSize);
I've edited the .h and .cpp files like this, to be able calling the function by importing the DLL in C#:
**EncodeFromBufferIN.h**
extern "C" {
__declspec(dllexport) void EncodeFromBufferIN(void* bufferIN, int bufferINSize, unsigned char* &bufferOUT, int &bufferOUTSize);
}
**EncodeFromBufferIN.cpp**
extern void EncodeFromBufferIN(void* bufferIN, int bufferINSize, unsigned char* &bufferOUT, int &bufferOUTSize){
// stuff to be done
}
But now my problem is that I don't know how to call the function in C#. I've added the next code in C# but not sure how to pass the parameters to the function.
[DllImport("QASEncoder.dll")]
unsafe public static extern void EncodeFromBufferIN(void* bufferIN, int bufferINSize, out char[] bufferOUT, out int bufferOUTSize);
The bufferIN and bufferOUT should be strings but if I'm calling the function like this:
public string prepareJointsForQAS()
{
string bufferIN = "0 0 0 0 0";
char[] bufferOUT;
int bufferOUTSize;
EncodeFromBufferIN(bufferIN, bufferIN.Length, bufferOUT, bufferOUTSize);
}
I get this error: "The best overloaded method matrch for ... has some invalid arguments". So how should the parameters be passed?
Marshalling works best with C Style calls. So it is best to use
pure C on your public interface.
If it is at all feasible to change the native code to
void EncodeFromBufferIN(
unsigned char* bufferIN,
int bufferINSize,
unsigned char* bufferOUT,
int* bufferOUTSize);
Then the call in C# can be thus
[DllImport("QASEncoder.dll")]
public static extern void EncodeFromBufferIN(
String bufferIN,
int bufferINSize,
StringBuilder bufferOUT,
ref int bufferOUTSize);
String inStr = new String(255);
int inSize = 255;
// make an educated estimate for the output size
// and preallocate in C# (I am guessing 255)
StringBuilder outStr = new StringBuilder(255);
int outSize = 255;
EncodeFromBufferIN(inStr, inSize, outStr, outSize);
This way you can avoid memory allocations in unmanaged code
which (although feasible) can get messy.
Hope this gets you going.
A few corrections that worked for me:
string inStr = "Value to pass";
int inSize = inStr.Length;
StringBuilder outStr = new StringBuilder(255);
int outSize = 255;
EncodeFromBufferIN(inStr, inSize, outStr, ref outSize);
regards.

Categories