C# Marshalling unsigned char* array from C++ DLL - c#

I am trying to marshal data from a C++ dll to C# that has an unsigned char* parameter.
I know this question has been asked before but I have tried most of the answers and still not getting far....the best I go was an array of bytes but that contained all 0000's
So here's the C++ function
int Hid_Read(int iIndex, unsigned char* pucaBuffer, int iSize);
Here's the C# DLLImport
[DllImport(#"The.dll", CallingConvention = CallingConvention.Cdecl)]
internal static extern int hid_Read(int iIndex, [Out] byte[] pucaBuffer, int iSize);
And the c# call:-
byte[] HID_ReceiveBuffer = new byte[128];
int intResult = hid_Read(HIDdeviceIndex, HID_ReceiveBuffer, 128);
for (int inteach = 0; inteach < 128; inteach++)
{
Debug.WriteLine("Data = " + HID_ReceiveBuffer[inteach].ToString());
}
This exits successfully but returns an array of 000's in the hid HID_ReceiveBuffer
Any help would be gratefully appreciated

Related

Calling C++ function with LPStr return value from C#

I have a C++ dll in 64-Bit, which contains a function that returns an LPStr. I would like to call this function in C#. The function declaration looks like this:
__declspec(dllexport) LPSTR __stdcall function(int16_t error_code);
In my C# code I have tried the following:
[DllImport(#"<PathToInterface.dll>", EntryPoint = "function")]
[return: MarshalAs(UnmanagedType.LPStr)]
public static extern string function(Int16 error_code);
And then in the program:
string ErrorMessage = "";
ErrorMessage = function(-10210);
I know that the function itself is good, as I can call it from another program (written in LabVIEW FWIW). But when I execute the C# Program, it just exits with error code 0x80000003, I can't even try, catch the exeption.
How do I call this function properly?
As a side node: I do have other functions in this dll, that use LPStr as parameters, which I can call without a problem. It is only two functions that return LPStr that make problems
How do I call this function properly?
As interop? you can't ... it is also error prone in plain C++
you should rather do it like
extern "C" __declspec(dllexport) int __stdcall function(int16_t error_code,
LPSTR buffer, size_t size)
{
LPCSTR format = "error: %i";
size_t req = _scprintf(format, error_code); // check for require size
if (req > size) //buffer size is too small
{
return req; //return required size
}
sprintf_s(buffer, size, format, error_code); //fill buffer
return 0;
}
And usage
class Program
{
static void Main(string[] args)
{
short error_code = -10210;
var ret = function(error_code, null, 0); // check for required size of the buffer
var sb = new StringBuilder(ret); // create large enough buffer
ret = function(error_code, sb, (uint)sb.Capacity + 1); //call again
var error_desc = sb.ToString(); //get results
Console.WriteLine(error_desc);
Console.ReadKey();
}
[DllImport("TestDll.dll", EntryPoint = "function", CharSet = CharSet.Ansi)]
public static extern int function(short error_code, StringBuilder sb, int size);
}
usage in C++
typedef int (__stdcall *function)(int16_t error_code, LPSTR buffer, size_t size);
int main()
{
auto handle = LoadLibrary(L"TestDll.dll");
auto proc = (function)GetProcAddress(handle, "_function#12");
// of course it can be done via linking
int16_t error_code = 333;
const int ret = proc(error_code, NULL, 0); // check for size
CHAR* buffer = new CHAR[ret + 1];
//CHAR buffer[200]; //eventually allocate on stack
//but size have to be constant value and may be too small
proc(error_code, buffer, ret+1); // call again
MessageBoxA(0, buffer, "Return", 0); //show result
delete[] buffer; //free buffer!
FreeLibrary(handle);
return 0;
}

Calling a dll with a void* from C#

I've read most of the hints but I can't get it to work.
I have a native C dll with this prototype:
int utl_Conv_HexString(U8 u8_Mode, void* DataIn, void* DataOut, int *piInOutLen, int maxOutLen);
This dll converts several string formats in byte arrays:
The dll is used in a system with unmanaged code (written in C)
Now I would like to use this dll in a C# / WPF Enviroment.
I still use other dll's in C#, but all have prototypes with no void*.
Examples from C:
//ByteArr to Telegramm
u8_Dst[0] = 0xAA;
u8_Dst[1] = 0xBB;
u8_Dst[2] = 0xCC;
u32_InOutLen = 3;
s32_res = utl_Conv_HexString(UTL_CONV_BYTEARR_TO_TELEGRAM, u8_Dst, ac8_Src, &u32_InOutLen, sizeof(ac8_Src));
or
strcpy(ac8_Src, "0xAA,0xBB,0xCC");
memset(u8_Dst, 0, sizeof(u8_Dst));
s32_res = utl_Conv_HexString(UTL_CONV_TELEGRAM_TO_BYTEARR, ac8_Src, u8_Dst, &u32_InOutLen, sizeof(u8_Dst));
My problem is that I can not figure out how this could be used in C#
You should use it like:
public enum U8
{
UTL_CONV_BYTEARR_TO_TELEGRAM = 1, // TODO
UTL_CONV_TELEGRAM_TO_BYTEARR = 2,
}
(you will have to put here your constants...)
[DllImport("SomeDll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int utl_Conv_HexString(U8 u8Mode, byte[] dataIn, byte[] dataOut, ref int piInOutLen, int maxOutLen);
(note that the CallingConvention could be StdCall... you'll have to check your code)
and then:
byte[] src = Encoding.UTF8.GetBytes("0xAA, 0xBB, 0xCC");
byte[] dest = new byte[64];
int lenSrc = src.Length;
int res = utl_Conv_HexString(U8.UTL_CONV_TELEGRAM_TO_BYTEARR, src, dest, ref lenSrc, dest.Length);
The void* is normally translated to a byte[].

Pass C# Byte[] to C++ API

I have to pass an Byte array containing an MAC-Address to a C++ Method. Since I don't have much experience with working with c
C++ APIsI don't know how to do this. I've tried to pass the array itself, but got an invalid parameter code as response from the API. I've also tried to create an IntPtr but to no avail.
I know that the problem is that C++ can't handle managed datatypes such as arrays, so I've to create a unmanaged array somehow, I think.
Here is the definition of the C++ Method:
ll_status_t LL_Connect(
ll_intf_t intf,
uint8_t address[6]);
The array in C# is defined the following way:
Byte[] addr = new Byte[6];
Of course, the array is not empty.
For example:
C++
extern "C"
{
__declspec(dllexport) void GetData(uint8_t* data, uint32_t length)
{
for (size_t i = 0; i < length; ++i)
data[i] = i;
}
}
C#
[DllImport("LibName.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void GetData([In, Out] [MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);
And use in C#
byte[] data = new byte[4];
GetData(data, (unit)data.Lenght);
If you have an array fixed length, for example:
C++
extern "C"
{
__declspec(dllexport) void GetData(uint8_t data[6])
{
for (size_t i = 0; i < 6; ++i)
data[i] = i;
}
}
C#
[DllImport("LibName.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void GetData([In, Out] [MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] data);
And use in C#
byte[] data = new byte[6];
GetData(data);
For your case:
[DllImport("LibName.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int LL_Connect(byte intf, [In, Out] [MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] address);

C# call C++ DLL passing pointer-to-pointer argument

Could you guys please help me solve the following issue?
I have a C++ function dll, and it will be called by another C# application.
One of the functions I needed is as follow:
struct DataStruct
{
unsigned char* data;
int len;
};
DLLAPI int API_ReadFile(const wchar_t* filename, DataStruct** outData);
I wrote the following code in C#:
class CS_DataStruct
{
public byte[] data;
public int len;
}
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, ref CS_DataStruct data);
Unfortunately, the above code is not working... I guess that is due to the C++ func takes a pointer-to-pointer of DataStruct, while I just passed a reference of CS_DataStruct in.
May I know how can I pass a pointer-to-pointer to the C++ func? If it is not possible, is there any workaround? (the C++ API is fixed, so changing API to pointer is not possible)
Edit:
Memory of DataStruct will be allocated by c++ function. Before that, I have no idea how large the data array should be.
(Thanks for the comments below)
I used the following test implementation:
int API_ReadFile(const wchar_t* filename, DataStruct** outData)
{
*outData = new DataStruct();
(*outData)->data = (unsigned char*)_strdup("hello");
(*outData)->len = 5;
return 0;
}
void API_Free(DataStruct** pp)
{
free((*pp)->data);
delete *pp;
*pp = NULL;
}
The C# code to access those functions are as follows:
[StructLayout(LayoutKind.Sequential)]
struct DataStruct
{
public IntPtr data;
public int len;
};
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
unsafe private static extern int API_ReadFile([MarshalAs(UnmanagedType.LPWStr)]string filename, DataStruct** outData);
[DllImport("ReadFile.dll", CallingConvention = CallingConvention.Cdecl)]
unsafe private static extern void API_Free(DataStruct** handle);
unsafe static int ReadFile(string filename, out byte[] buffer)
{
DataStruct* outData;
int result = API_ReadFile(filename, &outData);
buffer = new byte[outData->len];
Marshal.Copy((IntPtr)outData->data, buffer, 0, outData->len);
API_Free(&outData);
return result;
}
static void Main(string[] args)
{
byte[] buffer;
ReadFile("test.txt", out buffer);
foreach (byte ch in buffer)
{
Console.Write("{0} ", ch);
}
Console.Write("\n");
}
The data is now transferred to buffer safely, and there should be no memory leaks. I wish it would help.
It isn't necessary to use unsafe to pass a pointer to an array from a DLL. Here is an example (see the 'results' parameter). The key is to use the ref attribute. It also shows how to pass several other types of data.
As defined in C++/C:
#ifdef __cplusplus
extern "C" {
#endif
#ifdef BUILDING_DLL
#define DLLCALL __declspec(dllexport)
#else
#define DLLCALL __declspec(dllimport)
#endif
static const int DataLength = 10;
static const int StrLen = 16;
static const int MaxResults = 30;
enum Status { on = 0, off = 1 };
struct Result {
char name[StrLen]; //!< Up to StrLen-1 char null-terminated name
float location;
Status status;
};
/**
* Analyze Data
* #param data [in] array of doubles
* #param dataLength [in] number of floats in data
* #param weight [in]
* #param status [in] enum with data status
* #param results [out] array of MaxResults (pre-allocated) DLLResult structs.
* Up to MaxResults results will be returned.
* #param nResults [out] the actual number of results being returned.
*/
void DLLCALL __stdcall analyzeData(
const double *data, int dataLength, float weight, Status status, Result **results, int *nResults);
#ifdef __cplusplus
}
#endif
As used in C#:
private const int DataLength = 10;
private const int StrLen = 16;
private const int MaxThreatPeaks = 30;
public enum Status { on = 0, off = 1 };
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct Result
{
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = StrLen)] public string name; //!< Up to StrLen-1 char null-terminated name
public float location;
public Status status;
}
[DllImport("dllname.dll", CallingConvention = CallingConvention.StdCall, EntryPoint = "analyzeData#32")] // "#32" is only used in the 32-bit version.
public static extern void analyzeData(
double[] data,
int dataLength,
float weight,
Status status,
[MarshalAs(UnmanagedType.LPArray, SizeConst = MaxResults)] ref Result[] results,
out int nResults
);
Without the extern "C" part, the C++ compiler would mangle the export name in a compiler dependent way. I noticed that the EntryPoint / Exported function name matches the function name exactly in a 64-bit DLL, but has an appended '#32' (the number may vary) when compiled into a 32-bit DLL. Run dumpbin /exports dllname.dll to find the exported name for sure. In some cases you may also need to use the DLLImport parameter ExactSpelling = true. Note that this function is declared __stdcall. If it were not specified, it would be __cdecl and you'd need CallingConvention.Cdecl.
Here is how it might be used in C#:
Status status = Status.on;
double[] data = { -0.034, -0.05, -0.039, -0.034, -0.057, -0.084, -0.105, -0.146, -0.174, -0.167};
Result[] results = new Result[MaxResults];
int nResults = -1; // just to see that it changes (input value is ignored)
analyzeData(data, DataLength, 1.0f, status, ref results, out nResults);
If you do call native code, make sure your structs are alligned in the memory. CLR does not guarantee alignment unless you push it.
Try
[StructLayout(LayoutKind.Explicit)]
struct DataStruct
{
string data;
int len;
};
More info:
http://www.developerfusion.com/article/84519/mastering-structs-in-c/

c++ to c# dll import ReadImage

I have this function in C++ and i want import it in c#
BS_RET_CODE BS_ReadImage( int handle, int imageType, unsigned char* bitmapImage, int* imageLen)
I've tried
[DllImport("BS_SDK.dll",CharSet = CharSet.Ansi, EntryPoint = "BS_ReadImage")]
public static extern int BS_ReadImage(int handle, int imageType, IntPtr bitmapImage, ref int imageLen);
IntPtr image = new IntPtr();
int len = 0;
BSSDK.BS_ReadImage(m_Handle, 0xff, image, ref len);
byte[] _imageTemp = new byte[len];
Marshal.Copy(image, _imageTemp, 0, len);
but i get an Access Violation Exception
Maybe you should check that len is infact equal to or greater than the size of image??
Where is the exception occurring? What line exactly?

Categories