C# PInvoke for 2 ** pointers - c#

I am working with a 3. party SDK, which is made up from .dll, .lib and .h files. I am using the .dll's to PInvoke against. And the .h files to see the function names and parameters. (So I am not using the .lib files).
The SDK is rather complex, so making the PInvoke wrappers have proven to be a challenge. All the functions/structs/enums is defined in the .h files.
My question is how to implement a pinvoke for a function with 2 **.
I expect it is my C# function definition that is wrong.
When I call the function it simplely crashes, no exception throw or anything. The program just stops.
Function: GetInformatiuon(...)
//C Function: GetInformatiuon(...)
ERROR GetInformatiuon(
Component comp,
struct Information** Info);
//C# Function: GetInformatiuon(...)
[DllImport("externalSDK.dll", EntryPoint = "GetInformatiuon", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR GetInformatiuon(Component comp, ref Information Info);
);
Enum: ERROR
//C Enum: ERROR
typedef enum ERROR_E {
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
} ERROR;
//C# Enum: ERROR
public enum ERROR
{
OK = 0, //Everything is ok
E_ARG = 1, //Error in the Arguments
E_DATA = 2 //Data error
//And more...
}
Struct: Component
//C struct: Component
typedef struct Component_S
{
void* ObjPointer;
unsigned long number;
} Component;
//C# class: Component
[StructLayout(LayoutKind.Sequential)]
public class Component
{
public IntPtr ObjPointer;
public uint number; //uint because usigned long C is 4 bytes (32 bits) and C# ulong is 8 bytes (64 bits), where C# uint is 4 bytes(32 bits)
}
Struct: Information
//C struct: Information
typedef struct Information_S {
char* language;
unsigned long sampleFrequency;
unsigned long frameShift;
}Information;
//C# struct: Information
[StructLayout(LayoutKind.Sequential)]
public struct Information
{
public string language;
public uint sampleFrequency; //uint because usigned long C is 4 bytes (32 bits) and C# ulong is 8 bytes (64 bits), where C# uint is 4 bytes(32 bits)
public uint frameShift; //uint because usigned long C is 4 bytes (32 bits) and C# ulong is 8 bytes (64 bits), where C# uint is 4 bytes(32 bits)
}

I found the solution! (aka Hans Passant comment)
I changed the function to:
Function: GetInformatiuon(...)
//C Function: GetInformatiuon(...)
ERROR GetInformatiuon(
Component comp,
struct Information** Info);
//C# Function: GetInformatiuon(...)
[DllImport("externalSDK.dll", EntryPoint = "GetInformatiuon", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR GetInformatiuon(Component comp, out IntPtr InfoPtr);
);
Calling the function
IntPtr infoPtr;
lhErr = SDK.GetInformatiuon(component, out infoPtr);
Information info = (Information)Marshal.PtrToStructure(infoPtr, typeof(Information)) ;

Related

PInvoke C++ dll from C# throws Exception

The project I am working on has a case where I have to read and display hardware info, for which they have a function written in a C++ DLL, and I am writing a C# stub to PInvoke it. I am fairly new to PInvoke, and hold beginner status in C# and C++ in general, as I mostly work in the Java space.
The issue here is that I am getting an exception, I feel like there is some marshaling issue going on here, which is causing this exception, but please correct me if I am wrong. Can you please help me spot the issue?
Also, I did try combining multiple MarshalAs options and different data types. In addition to the complication as it is, the function takes in a reference to a struct as an argument, and the struct itself has a nested struct within. Also, it returns a long as a flag I believe, when called with no arguments returns 0 with no exception, which is interesting.
===========================================================================================================
Part of sample hardware operation C++ dll header.
===========================================================================================================
#define APOINTER *
typedef unsigned char BYTETYPE;
typedef BYTETYPE CHARTYPE;
typedef unsigned long int ULONGTYPE;
typedef HDINFO APOINTER PTR_HDINFO;
typedef struct VERSION {
BYTETYPE major;
BYTETYPE minor;
}VERSION;
typedef VERSION APOINTER PTR_VERSION;
typedef struct HDINFO {
VERSION hardwareVersion;
CHARTYPE hardwareManufactureID[32]; /* blank padded */
ULONGTYPE hardwareflags; /* must be zero */
CHARTYPE hardwareDesc[32]; /* blank padded */
VERSION apiVersion;
}HDINFO;
typedef HDINFO APOINTER PTR_HDINFO;
extern "C" __declspec(dllexport) ULONGTYPE GetHardwareInfo(PTR_HDINFO pSoInfo);
===========================================================================================================
Sample C# code for PInvoke. Throws Exception
===========================================================================================================
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
namespace ConsoleHardwareApp
{
[StructLayout(LayoutKind.Sequential, Size =1)]
struct VERSION
{
byte majorVersion;
byte minorVerison;
}
[StructLayout(LayoutKind.Sequential, Size =1)]
struct HDINFO
{
VERSION hardwareVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
string hardwareManufactureID;
int hardwareflags;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
string hardwareDesc;
VERSION apiVersion;
}
class Program
{
[DllImport("sampleHardwareOp.dll")]
public static extern int GetHardwareInfo(ref IntPtr hdInfoPtr);
static void Main(string[] args)
{
HDINFO hdInfo = new HDINFO();
IntPtr hdInfoPtr = new IntPtr();
hdInfoPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(HDINFO)));
Marshal.StructureToPtr<HDINFO>(hdInfo, hdInfoPtr, false);
int rv = 1;
rv = GetHardwareInfo(ref hdInfoPtr); //Exception thrown here
Console.WriteLine(rv);
Console.ReadKey();
}
}
}
The exception:
ConsoleHardwareApp.Program::GetHardwareInfo' 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.
Without concise details about which C++ compiler was used to make the DLL, what size its long data type is (may be 32bit or 64bit), what alignment settings are used, if any structure padding is present, etc, then a translation to C# is difficult. However, your C# code should probably look something more like the following instead:
namespace ConsoleHardwareApp
{
[StructLayout(LayoutKind.Sequential/*, Pack=4 or 8*/)]
struct VERSION
{
byte major;
byte minor;
}
[StructLayout(LayoutKind.Sequential, CharSet=CharSet.Ansi/*, Pack=4 or 8*/)]
struct HDINFO
{
VERSION hardwareVersion;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
string hardwareManufactureID;
uint hardwareflags; // or ulong, depending on the C++ compiler used
[MarshalAs(UnmanagedType.ByValTStr, SizeConst=32)]
string hardwareDesc;
VERSION apiVersion;
}
class Program
{
[DllImport("sampleHardwareOp.dll", CallingConvention=CallingConvention.Cdecl)]
public static extern uint/*or: ulong*/ GetHardwareInfo(ref HDINFO pSoInfo);
static void Main(string[] args)
{
HDINFO hdInfo = new HDINFO();
uint rv = GetHardwareInfo(ref hdInfo);
Console.WriteLine(rv);
Console.ReadKey();
}
}
}
The CallingConvention in particular is very important. The default calling convention in C++ is usually __cdecl (but can be changed in compiler settings), however in C# the default CallingConvention in DllImport is CallingConvention.Winapi, which is __stdcall on Windows. A calling convention mismatch can easily corrupt the call stack, causing the error you are seeing.

PInvoke error marshaling C++ dll into C#, passing struct into method

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 :)

How do I properly import functions from a C++ DLL into my C# Project

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.

How to import function from c++ .dll into c sharp

In C++ i have this function and used it like below. How to i need to code in c sharp?
birdRS232WakeUp(int nGroupID, BOOL bStandAlone, int nNumDevices,
WORD *pwComport, DWORD dwBaudRate, DWORD dwReadTimeout,DWORD dwWriteTimeout,int nGroupMode
= GMS_GROUP_MODE_ALWAYS);
in the manual it state that "pwComport" points to an array of words, each of which is the number of the comport attached to one of the birds(e.g., COM1 = 1, COM2 = 2, etc.)
WORD COM_port[5] = {0,15,0,0,0}
if ((!birdRS232WakeUp(GROUP_ID,
FALSE, // Not stand-alone
DEVCOUNT, // Number of Devices
COM_port, // COM Port
BAUD_RATE, // BAUD
READ_TIMEOUT,WRITE_TIMEOUT, // Reponses timeouts
GMS_GROUP_MODE_ALWAYS)))
{
printf("Can't Wake Up Flock!\n");
Sleep(3000);
exit(-1);
}
This is how i do it in c sharp .
[DllImport(#"Bird.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern bool birdRS232WakeUp(int nGroupID, Boolean bStandAlone, int
nNumDevices,ref ushort pwComport, uint dwBaudRate, uint dwReadTimeout, uint
dwWriteTimeout);
ushort[] COM_port = new ushort[5]{0,13,0,0,0};
if ((!birdRS232WakeUp(GROUP_ID, false, DEVCOUNT,ref COM_port, BAUD_RATE, READ_TIMEOUT, WRITE_TIMEOUT)))
{
LWakeUpStatus.Text = "Failde to wake up FOB";
}
And finalyy i got this error message "Error 2 Argument '4': cannot convert from 'ref ushort[]' to 'ref ushort'"
Someone have any clue about it?
The immediate issue is that you're passing an array of type ushort when only a single ushort value is expected by your current managed extern definition:
That is:
ushort pwComport
Should be:
ushort[] pwComport

passing structure reference from a c# code to call a c++ DLL function that accept structure reference in its prototype

I hav a function in c++ DLL that has following prototype
int function(RefPar &params);
how can i call this function from a c# program using "DLLImport".
when i tried like below, AccessViolationException happened while running in visual studio 2008..
[DllImport("VistaGMMDLL.dll", EntryPoint = "function"]
unsafe static extern int function(ref RefPar params);
and called as..
int ret=function(ref params);
Note:RefPar structure has many
unsigned integer values and 1 enum
value as its members.
pls anyone help me to call the function correctly..
A couple of things jump out at me. First of all I don't see why you need to use unsafe. Secondly, you probably have a calling convention mismatch, cdecl in the C++ and stdcall in the C#.
I'd do it like this:
C++
struct RESOURCE_PARAMETERS{
unsigned int uSurfaceHeight;
unsigned int uSurfaceDepth;
unsigned int uSurfaceWidth;
unsigned int uMSAAHeight;
unsigned int uMSAAWidth;
unsigned int uArraySize;
unsigned int uNumSamples;
unsigned int uMaxLod;
unsigned int uBpp;
unsigned int uprefFlag;
unsigned int uusageFlag;
RESOURCE_TYPE_REC ResourceType;
int ResourceFormat;
int iBuildNumber;
};
int function(RefPar &parameters)
{
}
C#
[StructLayout(LayoutKind.Sequential)]
public struct RESOURCE_PARAMETERS
{
uint uSurfaceHeight;
uint uSurfaceDepth;
uint uSurfaceWidth;
uint uMSAAHeight;
uint uMSAAWidth;
uint uArraySize;
uint uNumSamples;
uint uMaxLod;
uint uBpp;
uint uprefFlag;
uint uusageFlag;
[MarshalAs(UnmanagedType.U4)]
ResourceType ResourceType;
int ResourceFormat;
int iBuildNumber;
}
[DllImport("VistaGMMDLL.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function(ref RESOURCE_PARAMETERS parameters);
RESOURCE_PARAMETERS parameters = new RESOURCE_PARAMETERS();
int result = function(ref parameters);
I'm not sure how big the enum is on the C++ size. That's why I've put an explicit MarshalAs in the C# code. If it's just a single byte, then use UnmanagedType.U1 instead. I trust you get the idea.
If your C++ function treats its parameter as an in/out parameter then using ref on the C# side is correct. If its actually an out parameter then change the code to be like this:
[DllImport("VistaGMMDLL.dll", CallingConvention = CallingConvention.Cdecl)]
static extern int function(out RESOURCE_PARAMETERS parameters);
RESOURCE_PARAMETERS parameters;
int result = function(out parameters);
Try in this way:
[StructLayout(LayoutKind.Sequential)]
public struct RefPar
{
UInt32 uint1;
UInt32 unti2;
....
}
[DllImport("VistaGMMDLL.dll", EntryPoint = "function"]
unsafe static extern int function(IntPtr params);
//calling
//fill the refParStructure
//create the IntPtr
refParStruct rs = new RefPar();
IntPtr refparPtr = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(refPar)));
Marshal.StructureToPtr(refParStruct, refparPtr,false);
ret=function(refParPtr);
Let Me know if you need more details
Another very simple way to call this function is:
Create a c++ dll warapper that link your original dll and contains this function
//c++ code
function2(uint param1, uint param2.....)
{
RefPar refpar;
refpar.param1=param1
refpar.param2=param2
function(&refpar)
}
in this way you have just to import (in C#) the dll wrapper function in this way
[DllImport("wrapperdll.dll", EntryPoint = "function2"]
static extern int function2(Uint32 param1,Uint32 param2....);
that is very simple to call.
Regards

Categories

Resources