I've been reading on this problem a lot before i decided to post it here. I have C dll that i need to access inside of a C# program. The DLL's code is pretty simple. As it's nothing more then a hook into biometrics driver (it's C because it's has to include lib and header files from driver for the code to work). Here is the code for the dll:
#include <stdio.h>
#include <windows.h>
#include <DPCN_TM.h>
__declspec(dllexport) unsigned char * ConvertPlatinumTemplateToDpTemplate(const unsigned char* inputData);
HRESULT Convert(
const unsigned char* inputData,
const size_t size,
DPCN_DATA_TYPE inputDataType,
DPCN_DATA_TYPE outputDataType,
DPCN_PURPOSE purpose,
unsigned char** ppbOutputData,
const void * outputParameters,
size_t * pcbData)
{
HRESULT hr = 0;
size_t cbData = 0;
if (FAILED(hr = DPCNConvertFingerprintData(inputData, size, inputDataType, purpose, NULL, outputDataType, outputParameters, NULL, &cbData))) {
return hr;
}
if (!(*ppbOutputData = (unsigned char *)malloc(cbData))) {
return DPCN_ERR_NO_MEMORY;
}
hr = DPCNConvertFingerprintData(inputData, size, inputDataType, purpose, NULL, outputDataType, outputParameters, *ppbOutputData, &cbData);
*pcbData = cbData;
return hr;
}
unsigned char * ConvertPlatinumTemplateToDpTemplate(const unsigned char* inputData) {
HRESULT hr = 0;
const size_t inputSize = sizeof(inputData);
DPCN_DATA_TYPE inputDataType = DPCN_DT_DP_TEMPLATE;
DPCN_DATA_TYPE outputDataType = DPCN_DT_DP_PLATINUM_TEMPLATE;
unsigned char *pbOutputData = NULL;
size_t cbData = 0;
hr = Convert(inputData, inputSize, inputDataType, outputDataType, DPCN_PURPOSE_IDENTIFICATION, &pbOutputData, NULL, &cbData);
return pbOutputData;
}
As you can see the contents of the DLL is pretty straight forward. From the code you can see I'm needing to access this function inside of the C# program.
unsigned char * ConvertPlatinumTemplateToDpTemplate(const unsigned char* inputData);
Now in my C# code I have done this:
[DllImport(#"path_to_dll\DPFPTemplateConvert.dll")]
public extern byte[] ConvertPlatinumTemplateToDpTemplate(byte[] inputData);
When I call the function I end up getting this error:
A first chance exception of type 'System.Runtime.InteropServices.MarshalDirectiveException' occurred in DLLImportTest.exe
Additional information: Cannot marshal 'return value': Invalid managed/unmanaged type combination.
What am I doing wrong?
An unsigned char * cannot be converted to a .NET byte array, for one simple reason: what should be the length of that array?
And even then, it's a bad idea to pass a pointer out of your function, if that pointer is pointing to memory allocated by that function. Who will release this memory?
You should have the .NET side allocate the byte[] for the result, and pass it into the function.
If the .NET side does not know beforehand how big the allocated array needs to be, use a callback as explained here: http://blog.getpaint.net/2012/04/30/marshaling-native-arrays-back-as-managed-arrays-without-copying/
Related
I can't seem to get a byte array in a C# program filled from a COM C++ program. The C# program includes a reference to the C++ DLL and is instantiated by:
_wiCore = new WebInspectorCoreLib.WICore();
Actual call
uint imageSize = *image byte count*; // set to size of image being retrieved
var arr = new byte[imageSize];
_wiCore.GetFlawImage(1, 0, ref imageSize, out arr);
C++ IDL:
[id(5)] HRESULT GetFlawImage([in] ULONG flawID, [in] USHORT station, [in, out] ULONG *pImageSize, [out] BYTE *pImageBytes);
This returns the image size correctly, but nothing in the array. I've also tried the signature (extra level of indirection for pImageBytes):
[id(5)] HRESULT GetFlawImage([in] ULONG flawID, [in] USHORT station, [in, out] ULONG *pImageSize, [out] BYTE **pImageBytes);
and in C# passing in an IntPtr but this returns the address of memory that contains the address of the image bytes, not the image bytes.
Any thoughts on what I'm doing wrong?
There are multiple ways to pass an array back from C++.
For example, you can use a raw byte array like you were trying to do. It works but it's not very practical from .NET because it's not a COM automation type which .NET loves.
So, let's say we have this .idl:
interface IBlah : IUnknown
{
HRESULT GetBytes([out] int *count, [out] unsigned char **bytes);
}
Here is a sample native implementation:
STDMETHODIMP CBlah::GetBytes(int* count, unsigned char** bytes)
{
if (!count || !bytes)
return E_INVALIDARG;
*count = numBytes;
*bytes = (unsigned char*)CoTaskMemAlloc(*count);
if (!*bytes)
return E_OUTOFMEMORY;
for (unsigned char i = 0; i < *count; i++)
{
(*bytes)[i] = i;
}
return S_OK;
}
And a sample C# calling code (note the .NET type lib importer doesn't know anything beyond pointers when it's not a COM automation type, so it just blindly defines the argument as an IntPtr):
var obj = (IBlah)Activator.CreateInstance(myType);
// we must allocate a pointer (to a byte array pointer)
var p = Marshal.AllocCoTaskMem(IntPtr.Size);
try
{
obj.GetBytes(out var count, p);
var bytesPtr = Marshal.ReadIntPtr(p);
try
{
var bytes = new byte[count];
Marshal.Copy(bytesPtr, bytes, 0, bytes.Length);
// here bytes is filled
}
finally
{
// here, we *must* use the same allocator than used in native code
Marshal.FreeCoTaskMem(bytesPtr);
}
}
finally
{
Marshal.FreeCoTaskMem(p);
}
Note: this won't work in out-of-process scenario as the .idl is not complete to support this, etc.
Or you can use a COM Automation type such as SAFEARRAY (or a wrapping VARIANT). which also would allow you to use it with other languages (such as VB/VBA, Scripting engines, etc.)
So, we could have this .idl:
HRESULT GetBytesAsArray([out] SAFEARRAY(BYTE)* array);
This sample native implementation (a bit more complex, as COM automation was not meant for C/C++, but for VB/VBA/Scripting object...):
STDMETHODIMP CBlah::GetBytesAsArray(SAFEARRAY** array)
{
if (!array)
return E_INVALIDARG;
// create a 1-dim array of UI1 (byte)
*array = SafeArrayCreateVector(VT_UI1, 0, numBytes);
if (!*array)
return E_OUTOFMEMORY;
unsigned char* bytes;
HRESULT hr = SafeArrayAccessData(*array, (void**)&bytes); // check errors
if (FAILED(hr))
{
SafeArrayDestroy(*array);
return hr;
}
for (unsigned char i = 0; i < numBytes; i++)
{
bytes[i] = i;
}
SafeArrayUnaccessData(*array);
return S_OK;
}
And the sample C# code is much simpler, as expected:
var obj = (IBlah)Activator.CreateInstance(myType);
obj.GetBytesAsArray(out var bytesArray);
Hi all I am trying to compile a C++ project into a DLL and then use the Structs and methods from C#. I made an openCV program and I need to pass data back and forth. For now I am trying simple things to get familiar with the language since it is the first time I touch C++ and C# ...
So in C++ I have :
#pragma pack(push, 2)
typedef struct
{
//char *plate;
unsigned int width;
unsigned int height;
//bool hasPlateOn;
//unsigned char *imgData;
} Sframe;
extern "C" Sframe MYCVAPI findPossiblePlates(Sframe& in)
And I try to use it from C# like this :
[StructLayout(LayoutKind.Sequential)]
struct Sframe
{
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
// char[] plate;
[MarshalAs(UnmanagedType.U4)]
int width;
[MarshalAs(UnmanagedType.U4)]
int height;
// bool hasPlateOn;
// [MarshalAs(UnmanagedType.ByValArray, SizeConst = 10)]
// char[] imgData;
}
[DllImport(#"ALPR_pwv.dll",CallingConvention=CallingConvention.Cdecl,EntryPoint = "findPossiblePlates")]
static extern Sframe findPossiblePlates(Sframe infoOut);
But no matter what I try I get errors that the method's type signature is not PInvoke compatible.
is there anyone that could help me understand this issue?
Is it even possible to pass and get custom structs among C++ and C# ?
because the end stuct will be much more complex in the end since I have to pass images with data like coordinates etc...
As #SaniSinghHuttunen mentioned I was missing the ref keyword and had to use MarshalAs.U4 ... Thanks again :)
I could use help with how to properly import several functions from a C++ DLL into my C# application. Here are a couple examples from the C++ side that show what I am trying to do in C#. I do not think I am properly marshaling either/or some of the return types and some of the parameters (especially pointers/ref/out).
C++ Header File Declarations:
unsigned long __stdcall mfcsez_initialisation(unsigned short serial);
unsigned char __stdcall mfcs_get_serial(unsigned long int handle,
unsigned short * serial);
unsigned char __stdcall mfcs_read_chan(unsigned long int handle,
unsigned char canal,
float * pressure,
unsigned short * chrono);
C++ Code:
/* Define functions prototype */
typedef unsigned long(__stdcall *init)(int);
typedef unsigned char(__stdcall *serial)(unsigned long handle, unsigned
short *serial);
typedef unsigned char(__stdcall *readChannel)(unsigned long handle,
unsigned char chan,
float * pressure,
unsigned short * chrono);
int main(int argc, char *argv[])
{
unsigned char pressureChannel = 1;
HINSTANCE hGetProcIDDLL=NULL;
/* Load DLL into memory */
hGetProcIDDLL = LoadLibrary(TEXT("mfcs64_c.dll"));
/* Declare pointers on dll functions */
init dll_init;
serial dll_serial;
readChannel dll_readChannel;
/* Link dll pointers with functions prototype */
dll_init = (init)GetProcAddress(hGetProcIDDLL,
"mfcsez_initialisation");
dll_serial = (serial)GetProcAddress(hGetProcIDDLL,
"mfcs_get_serial");
dll_readChannel = (readChannel)GetProcAddress(hGetProcIDDLL,
"mfcs_read_chan");
/* Define variables used for MFCS device */
unsigned long mfcsHandle;
unsigned short mySerial;
float read_pressure;
unsigned short chrono;
int loop_index;
if (hGetProcIDDLL != NULL)
{
std::cout << "mfcs_c.dll is loaded" << std::endl;
/* Initialize device */
if (dll_init != NULL)
{
/* Initialize the first MFCS in Windows enumeration list */
mfcsHandle = dll_init(0);
}
/* Read device serial number */
dll_serial(mfcsHandle, &mySerial);
for (loop_index = int(start_pressure);
loop_index<target_pressure; loop_index++)
{
Sleep(1000);
dll_readChannel(mfcsHandle, pressureChannel,
&read_pressure, &chrono);
}
}
return EXIT_SUCCESS;
}
I have tried importing them with various footprints. I am able to call mfcsez_initialisation and it works just fine as imported below. The other two I have tried many different ways and always get an exception - either from the DLL (unrecoverable) or from improper marshalling which I can try/catch.
Example of C# Import Statements:
[DllImport("mfcs_c_64.dll", CallingConvention =
CallingConvention.StdCall)]
protected static unsafe extern uint mfcsez_initialisation(ushort
serial_number);
[DllImport("mfcs_c_64.dll", CallingConvention =
CallingConvention.StdCall)]
public static unsafe extern byte mfcs_get_serial(uint handle, ref
ushort serial);
[DllImport("mfcs_c_64.dll", CallingConvention =
CallingConvention.StdCall)]
protected static unsafe extern byte mfcs_read_chan(ulong handle, byte
canal, ref float pressure, ref ushort chrono);
Example of C# Code:
unit mfcsHandle = mfcsez_initialisation(0); // Returns with valid handle
mfcs_get_serial(mfcsHandle, mySerial); // Memory write exception
float pressure = -1.0f;
ushort chrono = 0;
mfcs_read_chan(mfcsHandle, 1, ref pressure, ref chrono); // Same ex
Any and all help is appreciated!
As you have stated in comments (subsequently deleted), you can't be sure whether the problem lies in the interop or the parameters passed to the function. How are you going to resolve that doubt?
The way to do that is to create a test bed DLL that has functions with the same signatures, and then prove that you can move data correctly between that DLL and your C# p/invoke code. Once you can do that you can remove interop as a potential source of your problem, and concentrate on the parameters passed to the function. So, here is what is needed to make that test bed DLL.
dllmain.cpp
#include <Windows.h>
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
Dll1.cpp
#include <iostream>
extern "C"
{
unsigned long __stdcall mfcsez_initialisation(unsigned short serial)
{
std::cout << "mfcsez_initialisation, " << serial << std::endl;
return 1;
}
unsigned char __stdcall mfcs_get_serial(unsigned long int handle,
unsigned short * serial)
{
std::cout << "mfcs_get_serial, " << handle << std::endl;
*serial = 2;
return 3;
}
unsigned char __stdcall mfcs_read_chan(unsigned long int handle,
unsigned char canal,
float * pressure,
unsigned short * chrono)
{
std::cout << "mfcs_read_chan, " << handle << ", " << static_cast<int>(canal) << std::endl;
*pressure = 4.5f;
*chrono = 5;
return 6;
}
}
Dll1.def
LIBRARY Dll1
EXPORTS
mfcsez_initialisation
mfcs_get_serial
mfcs_read_chan
Note that I am using a .def file to ensure that functions are exported using their undecorated names.
The C# program that calls this looks like so:
Program1.cs
using System;
using System.Runtime.InteropServices;
using System.Text;
namespace ConsoleApp1
{
class Program
{
const string dllname = "Dll1.dll";
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern uint mfcsez_initialisation(ushort serial);
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern byte mfcs_get_serial(uint handle, out ushort serial);
[DllImport(dllname, CallingConvention = CallingConvention.StdCall)]
static extern byte mfcs_read_chan(uint handle, byte canal, out float pressure, out ushort chrono);
static void Main(string[] args)
{
uint retval1 = mfcsez_initialisation(11);
Console.WriteLine("return value = " + retval1.ToString());
Console.WriteLine();
ushort serial;
byte retval2 = mfcs_get_serial(12, out serial);
Console.WriteLine("serial = " + serial.ToString());
Console.WriteLine("return value = " + retval2.ToString());
Console.WriteLine();
float pressure;
ushort chrono;
byte retval3 = mfcs_read_chan(13, 14, out pressure, out chrono);
Console.WriteLine("pressure = " + pressure.ToString());
Console.WriteLine("chrono = " + chrono.ToString());
Console.WriteLine("return value = " + retval3.ToString());
Console.ReadLine();
}
}
}
The output is:
mfcsez_initialisation, 11
return value = 1
mfcs_get_serial, 12
serial = 2
return value = 3
mfcs_read_chan, 13, 14
pressure = 4.5
chrono = 5
return value = 6
As you can see, all the desired values travel between the two modules correctly. This demonstrates that the p/invoke interop code here is correct.
Notes:
In C++ on windows, both int and long int are 32 bit types. They therefore map to int on C#, oruint` for unsigned variants.
On C#, long and ulong are 64 bit types, and so do not match C++ int or long int`.
Map unsigned char on C++ to byte on C#.
There is no need for unsafe here. You would use unsafe if you needed to use pointers, but you don't, and seldom do.
I used out rather than ref because I infer that these parameters are only used for data flowing out of the DLL.
If you use this interop code against your DLL and still encounter failure then there are two plausible explanations:
The parameters you are passing to the DLL are incorrect.
The DLL is not what you think it is. Perhaps you have a version of the DLL which was built against different header files.
It depends what this DLL is besides written in C++.
If it is a C++ .NET DLL, you can use it like any other .NET DLL. And like the ones you already use provided by the Framework.
If it was written with .NET's predecessor COM in mind, you can use COM interop. Backwards compatibiltiy was thought about when making .NET
If it is neither of those, there is P/Invoke.
Note that COM interop and P/Invoke usually involve handling naked pointers. That means binarity issues and having to go into Unmanaged Code. I do not envy you for having to go that low level language.
I'm getting an error with a Java exception while executing my program. Though java catches the exception it continues the execution of native code without giving the intended result.
here is the native code that I guess something wrong with it :-
System::Collections::Generic::List<cli::array<unsigned char>^>^ processImages(unsigned char* frontImage, unsigned char* backImage);
jbyteArray convertUnsignedCharIntoJByteArray(JNIEnv *env,array<unsigned char>^ inputArray);
unsigned char* convertJByteArrayIntoUnsignedChar(JNIEnv *env, jbyteArray inImage);
JNIEXPORT jobject JNICALL Java_com_me_NativeCaller_processImages
(JNIEnv *env, jclass c, jbyteArray front, jbyteArray back){
unsigned char* frontBuffer = convertJByteArrayIntoUnsignedChar(env, front);
unsigned char* backBuffer = convertJByteArrayIntoUnsignedChar(env, back);
jbyteArray intermediateArray1 = convertUnsignedCharIntoJByteArray(env,(array<unsigned char>^)returnedValue[0]);
jbyteArray intermediateArray2 = convertUnsignedCharIntoJByteArray(env,(array<unsigned char>^)returnedValue[1]);
int aLen1 = strlen(reinterpret_cast<const char*>(&intermediateArray1));
int aLen2 = strlen(reinterpret_cast<const char*>(&intermediateArray2));
jbyteArray finalArray = env->NewByteArray(2);
env->SetByteArrayRegion(finalArray,0,1,(jbyte*)&intermediateArray1);
env->SetByteArrayRegion(finalArray,1,2,(jbyte*)&intermediateArray2);
return static_cast<jobject>(finalArray);
}
/*takes an array<unsigned char>^ as input and convert it into a jbyteArray*/
jbyteArray convertUnsignedCharIntoJByteArray(JNIEnv *env,array<unsigned char>^ inputArray){
int aLen = strlen(reinterpret_cast<const char*>(&inputArray));
jbyteArray intermediateArray = env->NewByteArray(aLen);
env->SetByteArrayRegion(intermediateArray,0,aLen,(jbyte*)&inputArray);
return intermediateArray;
}
to be precise I think the way I convert manged unsigned char into jbyteArray is not correct.
can someone show me where did I go wrong, and a possible way to overcome this problem.
This looks incorrect:
jbyteArray intermediateArray1 = convertUnsignedCharIntoJByteArray(env,(array<unsigned char>^)returnedValue[0]);
jbyteArray intermediateArray2 = convertUnsignedCharIntoJByteArray(env,(array<unsigned char>^)returnedValue[1]);
//...
env->SetByteArrayRegion(finalArray,0,1,(jbyte*)&intermediateArray1);
env->SetByteArrayRegion(finalArray,1,2,(jbyte*)&intermediateArray2);
Not familiar with C++/CLI syntax using that "^" stuff, I'm focusing on jbyteArray and the SetByteArrayRegion() call. A jbyteArray is an alias for a pointer. Given that, the calls to SetByteArrayRegion() are not correct. It should be:
env->SetByteArrayRegion(finalArray,0,1,(jbyte*)intermediateArray1);
env->SetByteArrayRegion(finalArray,1,2,(jbyte*)intermediateArray2);
You may also check your other usages of &array in other parts of your code. Again, I'm not familiar with the nuances of the "^" syntax, but have used traditional C++ to implement JNI code.
I'm trying to marshall some data that my native dll allocated via CoTaskMemAlloc into my c# application and wondering if the way I'm doing it is just plain wrong or I'm missing some sublte decorating of the method c# side.
Currently I have c++ side.
extern "C" __declspec(dllexport) bool __stdcall CompressData( unsigned char* pInputData, unsigned int inSize, unsigned char*& pOutputBuffer, unsigned int& uOutputSize)
{ ...
pOutputBuffer = static_cast<unsigned char*>(CoTaskMemAlloc(60000));
uOutputSize = 60000;
And on the C# side.
private const string dllName = "TestDll.dll";
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport(dllName)]
public static extern bool CompressData(byte[] inputData, uint inputSize, out byte[] outputData, out uint outputSize );
...
byte[] outputData;
uint outputSize;
bool ret = CompressData(packEntry.uncompressedData, (uint)packEntry.uncompressedData.Length, out outputData, out outputSize);
here outputSize is 60000 as expected, but outputData has a size of 1, and when I memset the buffer c++ side, it seems to only copy across 1 byte, so is this just wrong and I need to marshall the data outside the call using an IntPtr + outputSize, or is there something sublte I'm missing to get working what I have already?
Thanks.
There are two things.
First, the P/Invoke layer does not handle reference parameters in C++, it can only work with pointers. The last two parameters (pOutputBuffer and uOutputSize) in particular are not guaranteed to marshal correctly.
I suggest you change your C++ method declaration to (or create a wrapper of the form):
extern "C" __declspec(dllexport) bool __stdcall CompressData(
unsigned char* pInputData, unsigned int inSize,
unsigned char** pOutputBuffer, unsigned int* uOutputSize)
That said, the second problem comes from the fact that the P/Invoke layer also doesn't know how to marshal back "raw" arrays (as opposed to say, a SAFEARRAY in COM that knows about it's size) that are allocated in unmanaged code.
This means that on the .NET side, you have to marshal the pointer that is created back, and then marshal the elements in the array manually (as well as dispose of it, if that's your responsibility, which it looks like it is).
Your .NET declaration would look like this:
[System.Security.SuppressUnmanagedCodeSecurity]
[DllImport(dllName)]
public static extern bool CompressData(byte[] inputData, uint inputSize,
ref IntPtr outputData, ref uint outputSize);
Once you have the outputData as an IntPtr (this will point to the unmanaged memory), you can convert into a byte array by calling the Copy method on the Marshal class like so:
var bytes = new byte[(int) outputSize];
// Copy.
Marshal.Copy(outputData, bytes, 0, (int) outputSize);
Note that if the responsibility is yours to free the memory, you can call the FreeCoTaskMem method, like so:
Marshal.FreeCoTaskMem(outputData);
Of course, you can wrap this up into something nicer, like so:
static byte[] CompressData(byte[] input, int size)
{
// The output buffer.
IntPtr output = IntPtr.Zero;
// Wrap in a try/finally, to make sure unmanaged array
// is cleaned up.
try
{
// Length.
uint length = 0;
// Make the call.
CompressData(input, size, ref output, ref length);
// Allocate the bytes.
var bytes = new byte[(int) length)];
// Copy.
Marshal.Copy(output, bytes, 0, bytes.Length);
// Return the byte array.
return bytes;
}
finally
{
// If the pointer is not zero, free.
if (output != IntPtr.Zero) Marshal.FreeCoTaskMem(output);
}
}
The pinvoke marshaller cannot guess how large the returned byte[] might be. Raw pointers to memory in C++ do not have a discoverable size of the pointed-to memory block. Which is why you added the uOutputSize argument. Good for the client program but not quite good enough for the pinvoke marshaller. You have to help and apply the [MarshalAs] attribute to pOutputBuffer, specifying the SizeParamIndex property.
Do note that the array is getting copied by the marshaller. That's not so desirable, you can avoid it by allowing the client code to pass an array. The marshaller will pin it and pass the pointer to the managed array. The only trouble is that the client code will have no decent way to guess how large to make the array. The typical solution is to allow the client to call it twice, first with uOutputSize = 0, the function returns the required array size. Which would make the C++ function look like this:
extern "C" __declspec(dllexport)
int __stdcall CompressData(
const unsigned char* pInputData, unsigned int inSize,
[Out]unsigned char* pOutputBuffer, unsigned int uOutputSize)