C# interop returning System.Access.Violation - c#

I am attempting to use a C function through C# Interop and I am receiving an access violation on one function. I have tried a number of things and I can't seem to solve this.
Here is the C code that needs to be changed into c# code:
typedef struct
{
char SerNo[64];
unsigned char hwVer;
HANDLE device; // Set by the API on return from SelectDevice()
} DeviceT;
This struct is used by the following function:
error = GetDevices(DeviceT *devices, unsigned int *numDevs, unsigned int maxDevs)
There is one other function in the C code:
error = SelectDevice(DeviceT *device)
So I began by defining DeviceT. I tried a few ways, but settled on this since it is simple:
[StructLayout(LayoutKind.Sequential)]
public struct DeviceT
{
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
public char[] SerNo;
public byte hwVer;
public IntPtr device;
}
The GetDevices function was set to this:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern ErrT GetDevices([In, Out] DeviceT[] devices, uint* numDevs, uint maxDev);
The SelectDevices function was set to this:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern ErrT SelectDevice([In, Out] DeviceT devices);
The code goes like this:
uint numDevs = 6;
uint maxDev = 6;
uint chosenIdx = 0;
DeviceT[] devices = new DeviceT[6];
err = GetDevices(devices, &NumberOfDevices, maxDev))
At this point everything is correct. The devices array has the correct information in it.
I now continue with (I just hard code select the first device)
chosenIdx = 0;
var chosenDevice = devices[chosenIdx];
err = SelectDevice(chosenDevice);
This last function returns a System.Access Violation
I tried a whole bunch of things but all end up with the same result. I suspect it has something to do with the HANDLE but I am not sure.
Thanks for any help.

SelectDevice takes a DeviceT *, but your P/Invoke signature takes a DeviceT. That is, you're passing in DeviceT by value rather than passing a pointer.
Try:
[DllImport("file.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ErrT SelectDevice([In, Out] ref DeviceT devices);
err = SelectDevice(ref chosenDevice);

Related

Pass a Struct array ByRef from C# to C++ and get it back

I've a very weird issue when calling a CPP dll.
I can call the method passing an struct array (with several positions) marshalled and loop it and set data to it as well. But when the code comes back to C#, the array has only one position.
For example, I create an array of my struct with 3 position (0, 1, 2), in the C++ Dll I receive the entire array and do nothing with it and when the execution returns to the C# Caller, the array has only one position on the array, with the first item.
The code:
C++ side:
extern "C" __declspec(dllexport) int ViewItems(int * nTotalItems, void ** paramStruct)
{
// TODO nothing here.
return 1;
}
C# side:
[DllImport(#"MyCustomLib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int ViewItems(ref int resultItemsCount, ref MyStruct[] MyStruct);
Invoke:
var resultSize = 3;
var resultStruct = new MyStruct[3];
for (int i = 0; i < resultSize; i++)
{
resultStruct[i].SomePropValue = i+2;
}
// Before this, the resultStruct.Lenght is 3
var resultCall = ViewItems(ref resultSize, ref resultStruct);
// After this, the resultStruct.Lenght is
Struct:
[StructLayout(LayoutKind.Sequential)]
public struct MyStruct
{
public int SomePropValue;
}
I tried the following with no success:
[MarshalAs(UnmanagedType.LPArray)]
[In, Out]
read other questions, but none of those had this issue.
Anyone have any idea of what I missed?
Thanks in advance!
I solved the issue.
I removed the ref from the DllImport and put the [MarshalAs(UnmanagedType.LPArray)] and [In, Out] decorator in MyStruct param.
Finally, I changed to void * paramStruct in C++ and finally all worked!

Passing a struct pointer in C# interop results in NULL

I am building a managed DLL for use in unmanaged environment (C/C++ app - FreeRDP). Interop works fine in most cases, but in one particular I am not able to pass a pointer to struct.
In the API I have a struct:
typedef struct _IWTSListenerCallback IWTSListenerCallback;
struct _IWTSListenerCallback
{
UINT(*OnNewChannelConnection)(IWTSListenerCallback* pListenerCallback,
IWTSVirtualChannel* pChannel,
BYTE* Data,
BOOL* pbAccept,
IWTSVirtualChannelCallback** ppCallback);
};
As well as a function I am calling:
UINT(*CreateListener)(IWTSVirtualChannelManager* pChannelMgr,
const char* pszChannelName,
ULONG ulFlags,
IWTSListenerCallback* pListenerCallback);
Both translated to C#:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ListenerCallbackNewConnectionDelegate(IntPtr listenerCallback, IntPtr channel, [MarshalAs(UnmanagedType.LPArray)] byte[] data, IntPtr accept, ref IntPtr channelCallback);
[StructLayout(LayoutKind.Sequential)]
public struct IWTSListenerCallback
{
[MarshalAs(UnmanagedType.FunctionPtr)]
public ListenerCallbackNewConnectionDelegate OnNewChannelConnection;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, ulong flags, IntPtr listenerCallback);
[MarshalAs(UnmanagedType.FunctionPtr)]
public ChannelManagerCreateListenerDelegate CreateListener;
And execution code:
var callback = new IWTSListenerCallback();
callback.OnNewChannelConnection = NewChannelConnection;
var pCallback = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(IWTSListenerCallback)));
Marshal.StructureToPtr(callback, pCallback, false);
var ret = channelManager.CreateListener(pChannelManager, "TestChannel", 0, pCallback);
And while pChannelManager (which is a pointer I obtain from unmanaged code calling my DLL) and the string are sent through without any problems, the pointer I create here (pCallback) is assigned successfuly in C#, but it results in a NULL in unmanaged code.
I assume the problem is either with how I defined the struct, or how I defined the function (although the function is being called successfuly in unmanaged code). I use the method to create the pointer in exact same way as in another part of the DLL, and it works perfectly fine there when passed to unmanaged function.
EDIT:
By #jdweng suggestion:
[StructLayout(LayoutKind.Sequential)]
public struct TestCall
{
public IntPtr channelManager;
[MarshalAs(UnmanagedType.LPStr)]
public string channelName;
public ulong flags;
public IntPtr listenerCallback;
}
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr testStructure);
var test = new TestCall();
test.channelManager = pChannelManager;
test.channelName = "TestChannel";
test.flags = 0;
test.listenerCallback = pCallback;
var pTest = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FreeRDPTypes.TestCall)));
Marshal.StructureToPtr(test, pTest, false);
var ret = channelManager.CreateListener(pTest);
Didn't work.
EDIT2: Workaround! Only if you have access to original unmanaged code. I rearranged the function arguments so the structure pointers are first, like this:
UINT(*CreateListener)(IWTSVirtualChannelManager* pChannelMgr,
IWTSListenerCallback* pListenerCallback,
const char* pszChannelName,
ULONG ulFlags);
And it works! Probably a problem with offset.
It was a matter of offset. C/C++ ULONG was typedef unsigned long which I wrongly assumed corresponded to C# ulong, but in fact the first one is 4 bytes in Visual, while the other is 8 bytes, which resulted in 4 bytes offset. Fixed by changing ulong to uint and adding [MarshalAs(UnmanagedType.U4)] for good measure. Final look of the function I was calling inside C#:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate uint ChannelManagerCreateListenerDelegate(IntPtr channelManager, [MarshalAs(UnmanagedType.LPStr)] string channelName, [MarshalAs(UnmanagedType.U4)] uint flags, IntPtr listenerCallback);

Calling non-managed DLL

I am going mad trying to call a DLL function for days from a C# application.
Here is the definition of the DLL call:
phStatus_t phbalReg_Rd70xUsbWin_Init
( phbalReg_Rd70xUsbWin_DataParams_t * pDataParams,
uint16_t wSizeOfDataParams )
Here is the definition of phbalReg_Rd70xUsbWin_DataParams_t:
And here is my C# code for calling the DLL:
public static data_params parameters;
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct data_params
{
internal ushort wId; //Layer ID for this BAL component, NEVER MODIFY!
internal byte ucTxSeq; //Sequence counter for packets.
[MarshalAs(UnmanagedType.LPStr)]
String pDeviceName;
internal IntPtr pDeviceHandle; //Handle to the USB device.
internal IntPtr pPipeOut; //Handle to Usb Out-pipe.
internal IntPtr pPipeIn; //Handle to Usb In-pipe.
internal ushort wTimeoutWrMs; //TO value for Usb Write pipe transfer.
internal ushort wTimeoutRdMs; //TO value for Usb Read pipe transfer.
}
[DllImport("NxpRdlib.dll", EntryPoint = "phbalReg_Rd70xUsbWin_Init")]
public static extern uint phbalReg_Rd70xUsbWin_Init(ref data_params data_parameters,
public static unsafe uint connectToPegoda()
{
parameters = new data_params();
parameters.wId = 0x05;
parameters.ucTxSeq = 0;
parameters.pDeviceHandle = IntPtr.Zero;
parameters.pPipeOut = IntPtr.Zero;
parameters.pPipeIn = IntPtr.Zero;
parameters.wTimeoutWrMs = 0xFFFF;
parameters.wTimeoutRdMs = 0xFFFF;
return phbalReg_Rd70xUsbWin_Init(ref parameters, (uint)Marshal.SizeOf(parameters));
}
The problem is that I receive a PInvokeStackImbalance exception.
I tried to change type of paramaters with different things and never achieved to get this work. I am sure I am doing something wrong with types, but can't find what. Can someone help me?
The most common explanation is a calling convention mis-match. As written, the unmanaged function declaration uses cdecl. You did not specify a calling convention in your p/invoke and so the default of stdcall is used.
To fix this, specify cdecl in your p/invoke:
[DllImport("NxpRdlib.dll", CallingConvention = CallingConvention.Cdecl)]
You also specified only part of the p/invoke declaration. You missed the second parameter. The full declaration should be:
[DllImport("NxpRdlib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint phbalReg_Rd70xUsbWin_Init(
ref data_params data_parameters,
ushort wSizeOfDataParams
);
The other unknown here is phStatus_t. You've translated that as uint, an unsigned 32 bit integer. We can only take your word that the translation is correct.
Update: From your comment to the question, phStatus_t should be translated as ushort. So, finally, we have:
[DllImport("NxpRdlib.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ushort phbalReg_Rd70xUsbWin_Init(
ref data_params data_parameters,
ushort wSizeOfDataParams
);

Odd Errors from PInvoke struct/function

Im currently writing a C# wrapper for a C++ API, but a specific struct and a function that relies on this struct have been giving very strange errors when debugging.
C++ Struct:
typedef struct
{
unsigned __int handle;
char name[80];
unsigned int unique_ID;
} DeviceInfo;
Followed by this function:
int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);
The struct and function is imported as such:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]
public String name;
public UInt32 unique_ID;
}
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(ref DeviceInfo di, int length_of_di_array, ref numValidDevices);
The intended use of this struct and function is just to obtain some device info from the board Im accessing. Currently I do not have access to the function body in C++, so I can only assume it's working 100% (works fine in C++).
The issue is that when I use the function to run through an array of structs, it outputs the data I'm looking for, but also will begin to fail at runtime, giving me various error windows.
C# code:
static void Main()
{
int numValidDevices = 0; //initialize variable
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data
Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);
}
Console.ReadLine(); //stops console from closing prematurely
API_close(); //custom close function from the C++ API
}
Errors while debugging (information is still shown):
"An unhandled exception of type 'System.Threading.ThreadStateException' occurred in System.dll
Additional information: Thread has not been started."
"An unhandled exception of type 'System.ExecutionEngineException' occurred in mscorlib.dll"
Error while debugging (information is not shown, program fails to execute):
"An unhandled exception of type 'System.AccessViolationException' occurred in mscorlib.dll
Additional information: Attempted to read or write protected memory. This is often an indication that other memory is corrupt."
When closing the console window:
"The instruction at '0x7c9113c0' referenced memory at '0x00000000'. The memory could not be 'written'." (sometimes says 'read' instead of 'written').
I've done a lot of research on PInvoke and came across the Microsoft InteropAssistant application, various stack overflow articles such as this one, and this post seems even closer to what Im doing, but I'm still digging into how to use the Marshal.CoTaskMemAlloc/Free, and see if it even will do anyhting...
Thus far what I have for my struct and function are correct, I've tried changing the struct to use an IntPtr but that does not return a di.name value and the di.unique_ID becomes jibberish (oddly enough the di.handle stays valid)
C# code:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
IntPtr p_name;
public String name { get { return Marshal.PtrToStringAnsi(p_name); } }
public UInt32 unique_ID;
}
Intended output:
Handle: 3126770193
Name: DEVICE_A
Unique ID: 12345678
IntPtr output:
Handle: 3126770193
Name:
Unique ID: 1145128264
Oddly enough, using an IntPtr results in none of the errors above, and runs fine.
This leads me to believe the issue lies with marshaling over the C++ char to a string, but I'm not sure if the issue lies with the marshaling, memory management (there is none?), or something I'm not catching entirely.
Any and all feedback would be really appreciated, I've been stumped on this for a number of weeks now...
The exceptions you get indicate that the unmanaged code you are pinvoking is destroying the garbage collected heap. It isn't crystal why, but you don't give the pinvoke marshaller much of a chance to do the Right Thing. It cannot properly pin the array. Start by declaring the function properly, it takes an array so declare one:
[DllImportAttribute("MyC++API.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
DeviceInfo[] di,
int length_of_di_array,
out int p_numValidDevices
);
Your first declaration of DeviceInfo is correct, the 2nd isn't since the string isn't a pointer.
Something's not adding up, here. It's not clear to me how the function is supposed to be called.
In particular, this declaration:
int __stdcall get_device_info(DeviceInfo di[], const int length_of_di_array, int* p_numValidDevices);
doesn't match how you're using it:
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(ref DeviceInfo di, const int length_of_di_array, int* p_numValidDevices);
...
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 16, ref numValidDevices); //accesses each device element and returns the data
}
You're telling it that the array length is 16, starting at index i, which is wrong. Did you mean to only pass one element of the array at a time?
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
rc = get_device_info(ref di[i], 1, ref numValidDevices); //accesses each device element and returns the data
}
Or did you mean to pass the entire array once?
DeviceInfo[] di = new DeviceInfo[16]; //max of 16 devices
rc = get_device_info(ref di[0], 16, ref numValidDevices); //accesses each device element and returns the data
for (int i = 0; i < numValidDevices; ++i) //sorts through all validated devices
{
Console.WriteLine(...);
}
P.S. I would consider changing your p/invoke declaration to be:
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
int length_of_di_array,
ref int p_numValidDevices);
So, as pointed out in the replies below, I had two problems:
1. I wasn't calling my DllImport correctly, the way I had it was in an attempt to hack together an output, in doing so I screwed up the memory allocation to the array of structs.
2. I tried to hack together an output and screwed up the code even more (tried to pass in the DeviceInfo array di as a single element di[number] instead of as a whole).
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi)]
public struct DeviceInfo
{
public UInt32 handle;
[MarshalAsAttribute(UnmanagedType.ByValTStr, SizeConst = 80)]
public String name;
public UInt32 unique_ID;
}
[DllImportAttribute("MyC++API.dll", EntryPoint = "get_device_info", CallingConvention = CallingConvention.StdCall)]
public static extern int get_device_info(
[In, Out] [MarshalAs(UnmanagedType.LPArray, SizeParamIndex=1)] DeviceInfo[] di,
int length_of_di_array,
ref int p_numValidDevices);
static void Main()
{
int numValidDevices = 0;
DeviceInfo[] di = new DeviceInfo[16];
get_device_info(di, 16, ref numValidDevices);
for (int i = 0; i < numValidDevices; ++i)
{
Console.WriteLine("Handle: {0}\nName: {1}\nUnique ID: {2}", di[i].handle, di[i].name, di[i].unique_ID);
}
Console.ReadLine();
API_close();
}

P/Invoke C# struct with strings to C void*

I'm having a problem creating a C# P/invoke wrapper around a third party C library. In particular, the library has a method with the signature
int command(SomeHandle *handle, int commandNum, void *data, int datasize);
It is a wildcard method that does different things depending on commandNum. data can be a pointer to anything, like a single integer, or a char[], or a struct of some kind (my problem).
I have declared the wrapper as follows:
[DllImport("LIBRARY.DLL", EntryPoint = "command")]
public static extern int Command(IntPtr Handle, int CommandNum, [In, Out] IntPtr Data, int DataSize);
Now, when i call it with an opcode to fill a byte[] it works:
//WORKS, Buffer contains "library 1.0" after the call
const int BUFFER_SIZE = 128;
byte[] Buffer = new byte[BUFFER_SIZE];
int BytesWritten = 0;
GCHandle BufferHandle = GCHandle.Alloc(Buffer, GCHandleType.Pinned);
try
{
BytesWritten = Command(MyHandle, GET_VERSION, BufferHandle.AddrOfPinnedObject(), BUFFER_SIZE);
}
finally
{
BufferHandle.Free();
}
However, when I try the same with a simple struct, I cannot make it work, no matter what I try. the struct looks like this:
public struct FormatInfoType
{
public int Format;
public IntPtr Name; //const char*
public IntPtr Extension; //const char*
}
Here I am supposed to fill "Format" with an int (say, 1) and then the call to "command(...)" is meant to give me back the name and extension fields
If I pass this struct, the code compiles and runs correctly, but the values in the struct are never modified. If I change the IntPtr's to Strings or StringBuilders (and I've tried a myriad of MarshalAs attributes), then I cannot get the IntPtr to the struct because the it becomes non-blittable and the GCHandle line throws an exception.
Any help on this would be greatly appreciated.
EDIT:
I've tried many ways to call command() with the structure, but currently it looks like this:
FormatInfoType f = new FormatInfoType();
f.Format = 1;
f.Name = IntPtr.Zero;
f.Extension = IntPtr.Zero;
GCHandle fHandle = GCHandle.Alloc(f, GCHandleType.Pinned);
try
{
Command(MyHandle, GET_FORMAT_INFO, fHandle.AddrOfPinnedObject(), Marshal.SizeOf(f));
}
finally
{
fHandle.Free();
}
You can overload p/invoke signatures, try:
[DllImport("LIBRARY.DLL", EntryPoint = "command")]
public static extern int Command(IntPtr Handle, int CommandNum, ref FormatInfoType Data, int DataSize);

Categories