I'm trying to call a function from a C++ DLL using DLLImport (P/Invoke) and I keep getting a System.AccessViolationException error when I call it. Note that I other functions to work.
Declaration of the DLLImport function:
[DllImport("DocProc.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern uint getDPpupTrkBaseConfigPath(DPHandle hdl, StringBuilder str, uint strsize);
Usage:
StringBuilder sb = new StringBuilder(256);
getDPpupTrkBaseConfigPath(handle, sb, (uint)sb.Capacity);
DPHandle: (note that it works in other functions)
public struct DPHandle
{
public uint Size;
public IntPtr UserHandle;
[MarshalAs(UnmanagedType.LPStr)] public string DeviceName;
public uint DeviceTypeId;
public uint DeviceState;
public uint OpenFlags;
public IntPtr Reserved1;
};
C++:
BPS_PROPL getDPpupTrkBaseConfigPath(DPHandle hdl, char *str, unsigned long strsize);
(Note BPS_PROPL = unsigned long)
DPHandle:
struct DocProcHandle {
unsigned long Size;//sizeof(DocProcHandle)
void* UserHandle;
const char* DeviceName;
unsigned long DeviceTypeId;
unsigned long DeviceState;
unsigned long OpenFlags;
void* Reserved1;
};
typedef struct DocProcHandle *DPHandle;
Again, when I try to call the function that exception shows up. I tried looking at other answers but can't figure out what's wrong?
In the C++ code, you have:
typedef struct DocProcHandle *DPHandle;
This means that DPHandle is a pointer to the struct. But in your C# code you pass the the struct by value.
The simplest way to achieve that is to change the declaration of the first argument of your pinvoke code from
DPHandle hdl
to
[In] ref DPHandle hdl
Related
I am trying to port a windows-only USB driver to Linux by switching from hid.dll and other windows-only-stuff to the cross-platform hidapi.
hidapi.h contains the following definitions:
struct hid_device_info {
char *path;
unsigned short vendor_id;
unsigned short product_id;
wchar_t *serial_number;
unsigned short release_number;
wchar_t *manufacturer_string;
wchar_t *product_string;
unsigned short usage_page;
unsigned short usage;
int interface_number;
struct hid_device_info *next;
};
and
struct hid_device_info HID_API_EXPORT * HID_API_CALL hid_enumerate(unsigned short vendor_id, unsigned short product_id);
Please see the link above for the code with comments.
The driver is written in C#, so I would like to connect to the hidapi library and use these directly. I have managed to get access to hid_enumerate by doing this:
[DllImport ("/usr/local/lib/libhidapi-hidraw.so.0")]
internal static extern IntPtr hid_enumerate (ushort vendor_id, ushort product_id);
Calling hid_enumerate later then gives me a pointer. However, I am unsure how to proceed from here. I would like to turn this into a LinkedList of hid_device_info objects, or some other structure in C#.
I am quite new to C#, but have a lot of programming experience.
Could you please point me in the right direction?
Here's what I ended up doing. I made a struct in C# like this:
[StructLayout(LayoutKind.Sequential)]
private struct hid_device_info {
[MarshalAs(UnmanagedType.LPStr)]
public String path;
public ushort vendor_id;
public ushort product_id;
[MarshalAs(UnmanagedType.LPWStr)]
public String serial_number;
public ushort release_number;
[MarshalAs(UnmanagedType.LPWStr)]
public String manufacturer_string;
[MarshalAs(UnmanagedType.LPWStr)]
public String product_string;
public ushort usage_page;
public ushort usage;
public int interface_number;
public IntPtr next;
};
and when calling hid_enumerate, I did this to turn the pointer into an instance of the struct:
List<hid_device_info> list = new List<HidDeviceManager.hid_device_info> ();
IntPtr pDev = hid_enumerate (0, 0);
while (pDev != IntPtr.Zero) {
hid_device_info dev = new hid_device_info();
dev = (hid_device_info)Marshal.PtrToStructure (pDev, typeof(hid_device_info));
list.Add (dev);
pDev = dev.next;
}
It works well.
I am using 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.
The function I am trying to wrap is called InitBaseComponent, and I can call it, but it returns a "Error In Argument" enum back. So my guess is it is marshalling that creates this problem.
So the question is: I am doing this right?
Function: InitBaseComponent(...)
//C Function: InitBaseComponent(...)
ERROR InitBaseComponent(
Method_Interface* methodInterface, //[in]
void* methodInst, //[in]
ErrorCallBackFunction errorCallbackFunc, //[in]
void* ErrorCallbackInst, //[in]
Component* BaseComponent //[in, out]
);
//C# Function: InitBaseComponent(...)
[DllImport("externalSDK.dll", EntryPoint = "InitBaseComponent", CallingConvention = CallingConvention.Cdecl)]
public static extern ERROR InitBaseComponent(
Method_Interface methodInterface,
IntPtr methodInst,
ErrorCallBackFunction errorCallbackFunc,
IntPtr ErrorCallbackInst,
out Component BaseComponent
);
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: Method_Interface
//C struct: Method_Interface
typedef struct Method_Interface_S
{
void* (*Method1)(void* Inst, size_t size);
void* (*Method2)(void* Inst, size_t nelements, size_t bytes);
void* (*Method3)(void* Inst, void *pointer, size_t size);
void (*Method4)(void* Inst, void* pointer);
}Method_Interface;
//C# class: Method_Interface
[StructLayout(LayoutKind.Sequential)]
public class Method_Interface
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Method1_delegate(IntPtr Inst, uint size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Method2_delegate(IntPtr Inst, uint nelements, uint bytes);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Method3_delegate(IntPtr Inst, IntPtr pointer, uint size);
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void Method4_delegate(IntPtr Inst, IntPtr pointer);
public Method1_delegate Method1;
public Method2_delegate Method2;
public Method3_delegate Method3;
public Method4_delegate Method4;
}
Delegate: ErrorCallBackFunction
//C ErrorCallBackFunction
typedef void (*ErrorCallBackFunction)(void* errorCallBackInst, ERROR errorCode, const char* szMessage, const char* szDetail);
//C# delegate: ErrorCallBackFunction
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate void ErrorCallBackFunction(IntPtr errorCallBackInst, ERROR errorCode, string szMessage, string szDetail);
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 ulong number;
}
Anyone knows what I am doing wrong?
You have declared Component in the C# as a class. That means that it is already a reference. But then you passed it as an out parameter which adds an extra layer of indirection, one too many. So, you need to remove the out, just as you did for methodInterface.
[DllImport(...)]
public static extern ERROR InitBaseComponent(
Method_Interface methodInterface,
IntPtr methodInst,
ErrorCallBackFunction errorCallbackFunc,
IntPtr ErrorCallbackInst,
Component BaseComponent
);
Obviously you need to instantiate the Component object in your C# before you call InitBaseComponent.
Some other observations:
size_t is pointer sized, so your translation as uint will fail on 64 bit platforms.
C# long is 64 bits, but C++ long is 32 bits, on Windows. So your translation of the C++ Component struct is wrong. The number field must be declared with type uint.
I have DLL, written in C++. There is structure in this DLL.
typedef struct TransmitData
{
wchar_t szPath[MAX_PATH];
DWORD dwResult;
} *lpTransmitData;
And I have one function to fill this struct
extern "C" __declspec(dllexport) int GetData(struct TransmitData &data)
{
//I HAVE ONE THE SAME STRUCT IN THIS DLL
memcpy(&data, &transmitData, sizeof(struct TransmitData));
return ret_value;
}
In C# program I use functions with simple data types well, but for structure it doesn't work. There is code on C#:
public struct TransmitData
{
[MarshalAs(UnmanagedType.LPWStr, SizeConst = 260)] //260 = MAX_PATH
public string szPath;
public uint dwResult;
}
//...
[DllImport("MyDLL")]
public static extern int GetData(ref TransmitData data);
What am I doing wrong?
Thanks!
I suggest you to replace
[DllImport("MyDLL")]
with
[DllImport("MyDLL", CallingConvention = CallingConvention.Cdecl)]
and
extert "C"
with
extern "C"
Moreover, as Joe suggested, add
[StructLayout(LayoutKind.Sequential)]
before the declaration of the struct in C# code
A quick disclaimer: I'm very new to P/Invoke, so I apologize in advance if this is a silly question.
Here's my function signature in C++:
HRESULT SomeFunction(
_Out_ unsigned long *count,
_Outptr_result_buffer_(*count) GUID **ids,
_In_ const PCWSTR filter
)
And I'm trying to P/Invoke it as such in C#:
[StructLayout(LayoutKind.Sequential)]
struct GUID
{
public int a;
public short b;
public short c;
[MarshalAs(UnmanagedType.ByValArray, SizeConst=8)]
public byte[] d;
}
[DllImport("MyDll.dll", EntryPoint="SomeFunction")]
[return: MarshalAs(UnmanagedType.I8)]
private static extern Int64 SomeFunction
(
out ulong count,
[MarshalAs(UnmanagedType.LPArray)]
out GUID[] ids,
string filter
);
I know my code gets reaches the C++ function (I can see this in windbg) and there is no crash, but from what I can tell, the parameters aren't being passed correctly. My guess is that I've messed up my P/Invoke translation in C#, but I have no idea how to fix this. Any help would be appreciated!
Looks like I found my solution...
[DllImport("MyDll.dll", EntryPoint="SomeFunction")]
[return: MarshalAs(UnmanagedType.I4)]
private static extern int SomeFunction
(
out uint count,
[MarshalAs(UnmanagedType.LPArray)]
out GUID[] ids,
[InAttribute()]
[MarshalAsAttribute(UnmanagedType.LPWStr)]
string filter
);
I am referencing a DLL in my C# project as follows:
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin, string cout, string flimit,
string frate, string fwindow, string fincrement, string fbird,
string fparameter, string fvalidation, string fcoupon);
The FeeCalculation function is exported as follows in the DLL:
extern "C" __declspec(dllexport) void __stdcall FeeCalculation(char *cin,
char *cout, char *flimit, char *frate,
char *fwindow, char *fincrement, char *fbird,
char *fparameter, char *fvalidation, char *fcoupon);
The DLL function returns a reference to it's internal structures in the form of char * so if you were to reference this DLL in C++, you would do the following to do the calculation and get the returned structures:
FeeCalculation(buff, (char *)&fans, (char *)fl, (char *)ft, (char *)fw, (char *)fi, (char *)fe, (char *)&fm, (char *)val, (char *)cpn);
Now, how do I retrieve those values that are returned by reference using C#? Meaning, how do I do the same thing in C# to get the returned structures to get my returned calculation? I know I need to create an unsafe method, but I am unclear on how to deal with the memory addresses in C# like you would in C++.
Edit: Below states to use IntPtr but how do you place into identical structure so the fields of the structure can be referenced?
Edit: Here is the returned structure that I am interested in (cout):
struct feeAnswer {
unsigned int fee;
unsigned int tax1;
unsigned int tax2;
unsigned int tax3;
unsigned int tax4;
unsigned int surcharge1;
unsigned int surcharge2;
unsigned int validationFee;
unsigned int couponFee1;
unsigned int couponFee2;
unsigned int couponFee3;
unsigned int couponFee4;
unsigned short int dstay; //Day Stay
unsigned short int mstay; //Minute Stay
};
Here is the (cin) that I would pass along with other structures (they are zero byte at the moment, I want to get this to work first then I will implement the rest):
struct feeRequest {
unsigned char day;
unsigned char month;
unsigned int year; //2000 ~ 2099
unsigned char hour;
unsigned char minute;
unsigned char rate;
unsigned char validation;
unsigned char coupon1;
unsigned char coupon2;
unsigned char coupon3;
unsigned char coupon4;
};
The char* parameters in this case are not strings but pointers to chunks of raw bytes representing the data. You should marshal your parameters as instances of the IntPtr type, in conjunction with Marshal.AllocHGlobal to create a chunk of memory and then Marshal.PtrToStructure to convert that block of memory into a usable .NET type.
As an example:
[StructLayout(LayoutKind.Sequential)]
struct MyUnmanagedType
{
public int Foo;
public char C;
}
IntPtr memory = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(MyUnmanagedType)));
try
{
FeeCalculation(memory);
MyUnmanagedType result = (MyUnmanagedType)Marshal.PtrToStructure(
memory, typeof(MyUnmanagedType));
}
finally
{
Marshal.FreeHGlobal(memory);
}
Edit: now that we have structures to work with, a better solution is possible. Just declare structs in C# that match your C++ structs, and use them in the extern declaration
[StructLayout(LayoutKind.Sequential)]
public struct feeAnswer {
public uint fee;
public uint tax1;
public uint tax2;
public uint tax3;
public uint tax4;
public uint surcharge1;
public uint surcharge2;
public uint validationFee;
public uint couponFee1;
public uint couponFee2;
public uint couponFee3;
public uint couponFee4;
public ushort dstay; //Day Stay
public ushort mstay; //Minute Stay
};
[StructLayout(LayoutKind.Sequential, Pack=1)]
public struct feeRequest {
public byte day;
public byte month;
public uint year; //2000 ~ 2099
public byte hour;
public byte minute;
public byte rate;
public byte validation;
public byte coupon1;
public byte coupon2;
public byte coupon3;
public byte coupon4;
};
[DllImport ("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation (
feeRequest cin,
out feeAnswer cout,
...
....
original answer (before we had structs) below
It appears to me that these are not references to internal strings, but rather pointers to string buffers that will be filled in by the call. If you were returning string pointers, then these would be declared char** rather than char*.
So I think these are just standard out parameters. There's just a lot of them. So your C# interop would look like this
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string cout,
[MarshalAs(UnmanagedType.LPStr, SizeConst=100)]
out string flimit,
or this if your "strings" aren't really strings
[DllImport("FeeCalculation.dll", CallingConvention = CallingConvention.StdCall,
CharSet = CharSet.Ansi)]
public static extern void FeeCalculation(string cin,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] cout,
[MarshalAs(UnmanagedType.LPArray, SizeConst=100)]
out byte[] flimit,
....
To answer your Edit, you need to create a struct, and then use the StructLayoutAttribute on the fields in order to make the byte order and padding the same as the original dll did.