Struggling to call the method from native DLL using C# - c#

Here is the method signature I am trying to call.
EXTERN_C
HRESULT
QueryData(
_Outptr_opt_result_bytebuffer_(*SizeOfData) PBYTE * Data,
_Out_opt_ UINT32* SizeOfData,
_In_ BOOL IsDataType
)
The above method is not my code it's vendor code and unfortunately I don't have enough knowledge how to call this method. All I know is it's suppose to get me a blob of data.
Here is what I have done so far.
[DllImport("DataGetter.dll")]
internal static extern int QueryData(IntPtr data, UIntPtr sizeOfData, bool isDataType);
IntPtr data= new IntPtr();
UIntPtr sizeOfData= new UIntPtr();
bool isDataType= true;
int hresult = QueryData(data, sizeOfData, isDataType);
My method doesn't fail but it doesn't return any thing in the data. Any idea how to call this weird method from C#?

You have two problems here: first is to get values set by QueryData into Data and sizeOfData, which get pointers to local variables. You can do it with ref or out keyword, so UINT32* SizeOfData in C++ becomes ref System.UInt32 SizeOfData. Key difference between them is that out arguments do not have to be initialized before function call. Second is to transfer unmanaged array defined in C++ into C#. You can do it with Marshall.Copy.
One thing remains unclear, but should be stated in documentation - whenever array returned from C++ is allocated dynamically and needs to be freed in C# or not. If it does you will have memory leak that will increase memory usage with every call of function. The easiest way to test for it is to call function 1000000 times and check memory usage.
Full code:
[DllImport("DataGetter.dll"]
internal static extern int QueryData(out IntPtr data, out System.UInt32 sizeOfData, bool isDataType);
void example()
{
IntPtr dataPtr;
System.UInt32 sizeOfData;
bool isDataType = false;
int hresult = QueryData(out dataPtr, out sizeOfData, isDataType);
var data = new byte[sizeOfData];
Marshal.Copy(dataPtr, data, 0, (int)sizeOfData);
// data now contains retreived bytes
}
OLD POST:
Try with.
[DllImport("DataGetter.dll")]
internal static extern int QueryData(ref IntPtr data, ref System.UInt32 sizeOfData, bool isDataType);
I'm not sure what is PBYTE but I suppose it is pointer to BYTE.
Function should change data and sizeOfData variables.

Related

C# interop services - using C dll - void*

I'm having trouble interoperating with a DLL written in C. I'm not sure what type of param to put in place of void*
This is how the API of given DLL looks like:
POSNET_API POSNET_STATUS __stdcall POS_SetDeviceParam ( POSNET_HANDLE hDevice,
unsigned long paramCode,
void * paramValue
)
this is how I was trying to import it in C#:
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(IntPtr hDevice, int POSNET_DEV_PARAM_IP, *type* paramValue);
in place of type I was putting:
[MarshalAs(UnmanagedType.LPStr)] string and other L*Str
[MarshalAs(UnmanagedType.LPArray)] char[] and other type of arrays including of type byte
IntPtr which where AllocHGlobal, GCHandle.Alloc allocated before
even preceeding the method with unsafe and type = void*, combining above alloc aso.
raw type: string, char[]...
I ran out of possibilities by myself.
paramValue should have a value of an IP in following format: "192.168.1.1" - this is how it looks like in a C demo:
string ip="10.33.44.6";
POS_SetDeviceParam(hDevice,POSNET_DEV_PARAM_IP,(void*)ip.c_str());
In C# code, the hDevice is not being initialized - POS_SetDeviceParam should initialize it with additional params.
Any suggestions are very welcome!
You will need to use this P/Invoke signature:
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(
IntPtr hDevice,
int paramCode,
IntPtr paramValue
);
But you'll have to do some work to that string in order to pass it through the IntPtr paramValue argument.
Perhaps you can try using Marshal.StringToHGlobalAnsi() as that will give you an IntPtr you can use. If using this method though be sure to free the memory once you've finished with it.
Thank you guys for all the suggestions, the code below solved my problem!
[DllImport(EXT_DLL)]
private static extern int POS_SetDeviceParam(IntPtr hDevice, UInt32 POSNET_DEV_PARAM_IP, IntPtr paramValue);
void Test() {
POS_SetDeviceParam(new IntPtr(), 0x00020005, Marshal.StringToHGlobalAnsi("192.168.1.1"));
}

Can I marshal System.Object as an inaccessible pointer to C and then back to C#?

In C I have a function which registers a callback and a state object and then passes the state object to the callback every time it is called, much like EventHandler<EventArgs> works in C#. I have a class in C# that registers a managed callback with the C function and currently passes IntPtr.Zero and I simply do not use it because I have not found a good and clean way of passing a managed object reference to C and getting it back to C#.
I do not want the managed object to be accessible in C, I just want to pass it to C and have it passed back (verbatim) to the managed callback every time it is called from C.
Looking for the "right" way of doing this, basically.
Here is my delegate for the callack:
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.SysInt)]
internal delegate IntPtr LuaAllocator(
[In, MarshalAs(UnmanagedType.AsAny)] object args,
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr ptr,
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr originalSize,
[In, MarshalAs(UnmanagedType.SysInt)] IntPtr newSize);
Here is the current DllImport I'm doing:
[DllImport(DllName, EntryPoint = "lua_newstate", ExactSpelling = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.SysInt)]
public static extern IntPtr NewState(
[In, MarshalAs(UnmanagedType.FunctionPtr)] LuaAllocator allocator,
[In, MarshalAs(UnmanagedType.AsAny)] object args);
And the error I'm getting right now is:
MarshalDirectiveException was unhandled:
Cannot marshal 'parameter #1': AsAny cannot be used on return types, ByRef parameters, ArrayWithOffset, or parameters passed from unmanaged to managed.
This error goes away if I keep the delegate's first argument as an IntPtr and use MarshalAs.SysInt but I then cannot reconstruct the managed object, but I do get with a value other than IntPtr.Zero which is curious and may be useful.
I have found a solution. The HandleRef comment pointed me to GCHandle which I'm already familiar with but had never done this with. By making a non-pinned GCHandle from the object you want to marshal you can call the static GCHandle.ToIntPtr(GCHandle) method to acquire an IntPtr representing that particular handle. Inside the callback I called GCHandle.FromIntPtr(IntPtr) to re-acquire the handle and then retrieved the System.Object from the GCHandle.Target property.
Here's how you set up the callback and the state object:
// Omitted
string test = "This is a test string.";
GCHandle handle = GCHandle.Alloc(test, GCHandleType.Normal);
LuaCore.NewState(allocateCallback, GCHandle.ToIntPtr(handle));
// Omitted
And here is how you utilize it in the callback:
private IntPtr Allocate(IntPtr sender, IntPtr ptr, IntPtr originalSize, IntPtr newSize)
{
GCHandle handle = GCHandle.FromIntPtr(sender);
string test = handle.Target as string;
// Omitted
}
I think what you're looking for is System.Runtime.InteropServices.GCHandle. You can allocate a GCHandle and convert it to a System.IntPtr. When you get it back, you can use GCHandle.FromIntPtr to get your GCHandle back, and from there, get a proper reference to the original object.
Caveat
There are potential issues using this method revolving around garbage collection and the variables that you are allocating. As is customary with Platform Invoke, you need to make sure that some things you allocate aren't collected. The parameter to GCHandle.Alloc should be cached somewhere with a strong reference, and of course, you will need to prevent the collection of the delegate that you pass to whatever native function you're calling. Also, note that the GCHandle must be freed when you are done with it to prevent memory leaks.
Below is an example demonstrating the procedure.
using System.Runtime.InteropServices;
using System.Security.Permissions;
using System.IO;
public delegate bool InteropCallback(int handle, System.IntPtr payload);
public class InteropApp
{
[DllImport("user32.dll")]
public static extern bool EnumWindows(InteropCallback cb, System.IntPtr payload);
public static void Main()
{
var tw = System.Console.Out;
var twhandle = GCHandle.Alloc(tw);
InteropCallback cb = CallbackFunc;
EnumWindows(cb, GCHandle.ToIntPtr(twhandle));
twhandle.Free();
}
private static bool CallbackFunc(int handle, System.IntPtr payload)
{
var gch = GCHandle.FromIntPtr(payload);
var tw = (TextWriter)gch.Target;
tw.WriteLine(handle);
return true;
}
}
I think you want to use HandleRef instead of IntPtr, if I understand correctly.

DLLImport -> how to handle a HANDLE in C#

in my C# code I want to import a C++ DLL. I use the dllimport and it works fine with a some of the functions. But in one function I get a HANDLE which I need later to call another function.
[DllImport("SiUSBXp.dll")]
public static extern int SI_Open(UInt32 deviceNum,ref IntPtr devHandle ); // this function gets the HANDLE
[DllImport("SiUSBXp.dll")]
public static extern int SI_Write([In]IntPtr devHandle, [In, Out] byte[] inputByte, UInt32 size,ref UInt32 bytesWritten); // this function needs the HANDLE
In my code these functions are called like this:
IntPtr devHandle = new IntPtr();
UInt32 bytesWritten = new UInt32();
byte[] byteArr = new byte[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
SI_Open(0, ref devHandle);
SI_Write(devHandle, byteArr, 10, ref bytesWritten);
If I do it like this I get an "System.AccessViolationException". I searched here and in the internet but didnt find a specific answer. How do I use the IntPtr correctly, so it works?
Best Regards
Toby
Your SI_Write function looks quite like Windows Kernel32's WriteFile.
So, I would do this:
[DllImport("SiUSBXp.dll", SetLastError = true)]
static extern int SI_Open(uint dwDevice, ref IntPtr cyHandle);
[DllImport("SiUSBXp.dll", SetLastError = true)]
static extern int SI_Write(IntPtr cyHandle, byte[] lpBuffer,
uint dwBytesToWrite, out uint lpdwBytesWritten);
EDIT: I found this documentation USBXPRESS® PROGRAMMER’S GUIDE on the web, and it states that the SI_Write prototype looks actually much closer to WriteFile than I thought. The doc states this:
SI_STATUS SI_Write (HANDLE Handle, LPVOID Buffer, DWORD NumBytesToWrite,
DWORD *NumBytesWritten, OVERLAPPED* o = NULL)
It means the .NET prototype should be this instead:
[DllImport("SiUSBXp.dll")]
static extern int SI_Write(IntPtr Handle, byte[] Buffer,
uint NumBytesToWrite, out uint NumBytesWritten, IntPtr o);
o is optional so you can pass IntPtr.Zero.
You are making a classic C programmer mistake, you don't check the return value of the functions. Which tells you whether or not the function failed. A likely scenario is that SI_Open() returned a failure code. You ignore it and use the uninitialized handle value anyway. A kaboom is not unusual.
The next possible mistake is that you don't use the CallingConvention property in the [DllImport] statement. It is fairly likely to be needed, Cdecl is the default unless the native function is declared with __stdcall. Also an excellent way to invoke a kaboom. If you still have trouble then you are going to have to debug the native code.
Btw, you get rid of the awkward syntax by using out instead of ref. In both functions.
[DllImport("SiUSBXp.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int SI_Open(UInt32 deviceNum, out IntPtr devHandle );
try this:
[DllImport("SiUSBXp.dll")]
public static extern int SI_Open(UInt32 deviceNum, ref IntPtr devHandle); // this function gets the HANDLE
[DllImport("SiUSBXp.dll")]
public static extern int SI_Write(IntPtr devHandle, ref byte[] inputByte, UInt32 size, ref UInt32 bytesWritten); // this function needs the HANDLE
EDIT:
#Hans Passant is right. This is the correct way to pass a byte[] into a LPVOID parameter. ref used to coerce an object into LPVOID, but isn't needed for an array. What happens when you try this?
[DllImport("SiUSBXp.dll")]
public static extern int SI_Write(IntPtr devHandle, byte[] inputByte, UInt32 size, ref UInt32 bytesWritten); // this function needs the HANDLE
Did you try the answer #Simon Mourier gave? He was first to provide this declaration and his answer deserves to be accepted.
bad: static extern void DoStuff(**byte[] inputByte**);
good: static extern void DoStuff(**[In, MarshalAs(UnmanagedType.LPArray)] byte[] inputByte**);

How to initialise an unsafe pointer in C# and convert it to a byte[]?

I put a post up yesterday, How does one create structures for C# originally written in C++.
Thank you for your responses.
I'm trying, without much success, to use DeviceIOControl on an ARM platform running WinCE 6.0 and .NET Compact framework 2.0 All I am trying to achieve is the control of a port pin and it's proving to be a nightmare.
The following is the PInvoke declaration:
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool DeviceIoControlCE(int hDevice,
int dwIoControlCode,
byte[] lpInBuffer,
int nInBufferSize,
byte[] lpOutBuffer,
int nOutBufferSize,
ref int lpBytesReturned,
IntPtr lpOverlapped);
The PInvoke declaration suggests a byte[] may be passed to it simply. Surely it's an easy matter to write the values to each member of a structure, convert it to an array of bytes and pass it to the dll.
I have the following:
[StructLayout(LayoutKind.Sequential)]
public struct pio_desc
{
unsafe byte* pin_name; //Length???
public uint pin_number; //4 bytes
public uint default_value; //4 bytes
public byte attribute; //1 byte
public uint pio_type; //4 bytes
}
and
pio_desc PA13 = new pio_desc();
So surely now it's a matter of doing something like:
PA13.pin_number = AT91_PIN_PA13; //Length 4 bytes
PA13.default_value = 0; //Length 4 bytes
PA13.attribtue = PIO_DEFAULT; //Length 1 byte
PA13.pio_type = PIO_OUTPUT; //Length 4 bytes
and to convert (pin_number for example) to a byte[]:
byte[] temp = BitConverter.GetBytes(PA13.pin_number); //uints are 4 bytes wide
byteArray[++NumberOfChars] = temp[0];
byteArray[++NumberOfChars] = temp[1];
byteArray[++NumberOfChars] = temp[2];
byteArray[++NumberOfChars] = temp[3]; //Will need to check on Endianess
Questions:
In the structure PA13, how do I initialise the unsafe pointer pin_name? The author of the driver notes that this is not used, presumably by his driver. Will Windows need this to be some value?
PA13.pin_name = ??????
Then, how do I convert this pointer to a byte to fit into my byte[] array to be passed to DeviceIOControl?
I've become quite disappointed and frustrated at how difficult it is to change the voltage level of a port pin - I've been struggling with this problem for days now. Because I come from a hardware background, I think it's going to be easier (and less eligant) for me to implement IO control on another controller and to pass control data to it via a COM port.
Thanks again for any (simple) assistance.
You will need to do a few different things here. First, replace this member:
unsafe byte* pin_name; //Length???
with:
[MarshalAs(UnmanagedType.LPStr)] public string pin_name;
Then replace the in/out buffers in the P/Invoke declaration from byte[] to IntPtr. Then you can use this code to convert the data:
pio_desc PA13;
// Set the members of PA13...
IntPtr ptr = IntPtr.Zero;
try {
var size = Marshal.SizeOf(PA13);
ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(PA13, ptr, false);
// Your P/Invoke call goes here.
// size will be the "nInBufferSize" argument
// ptr will be the "lpInBuffer" argument
} finally {
if (ptr != IntPtr.Zero) {
Marshal.DestroyStructure(ptr, typeof(pio_desc));
Marshal.FreeHGlobal(ptr);
}
}
You can make this a lot easier by lying about the [DllImport] declaration. Just declare the lpInBuffer argument as the structure type, the pinvoke marshaller will convert it to a pointer anyway. Thus:
[DllImport("coredll.dll", EntryPoint = "DeviceIoControl", SetLastError = true)]
internal static extern bool SetOutputPin(IntPtr hDevice,
int dwIoControlCode,
ref pio_desc lpInBuffer,
int nInBufferSize,
IntPtr lpOutBuffer,
int nOutBufferSize,
out int lpBytesReturned,
IntPtr lpOverlapped);
Using IntPtr for lpOutBuffer because the driver probably doesn't return anything. Pass IntPtr.Zero. Same idea with the structure. If the field isn't used then simply declare it as an IntPtr:
[StructLayout(LayoutKind.Sequential)]
public struct pio_desc
{
public IntPtr pin_name; // Leave at IntPtr.Zero
public uint pin_number; //4 bytes
public uint default_value; //4 bytes
public byte attribute; //1 byte
public uint pio_type; //4 bytes
}
Be careful about the Packing property, it makes a difference here because of the byte sized field. You may need 1 but that's just a guess without knowing anything about the driver. If you have working C code then test the value of sizeof(pio_desc) and compare with Marshal.SizeOf(). Pass Marshal.SizeOf(typeof(pio_desc)) as the nInBufferSize argument. If you would have posted the C declarations then this would have been easier to answer accurately.
Declare lpInBuffer and lpOutBuffer as IntPtr. Initialize them using Marshal.AllocHGlobal (don't forget to release them with Marshal.FreeHGlobal in the end). Fill these buffer and read it using different Marshal.Copy overloads.

I'm trying to marshal a struct from C to C#, not sure where to start

My struct in C++ is the following
/* this structure contains the xfoil output parameters vs angle of attack */
typedef struct xfoil_outputdata_struct
{
double *pAlfa;
double *pCL;
double *pCM;
double *pCDi;
double *pCDo;
double *pCPmax;
long nEntries;
} XFOIL_OUTPUT_DATA;
/* Here are the function prototypes for XFoil */
__declspec(dllexport) XFOIL_OUTPUT_DATA *xfoilResults(); /* get output from xfoil */
I use XFoilResults to pull this structure back into C#
My DLL Imports statement is the following:
[DllImport("xfoilapi.dll")]
public static extern void xfoilResults();
Is this correct? I have no control over the C++ code. I just need to be able to pull the struct into C#. The C# struct I have so far is the following
[StructLayout(LayoutKind.Sequential)]
public struct xfoilResults
{
IntPtr pAlfa;
IntPtr pCL;
IntPtr pCM;
IntPtr pCDi;
IntPtr pCDo;
IntPtr pCPmax;
long nEntries;
}
How can I populate this C# structure with the data from the C++ code?
StructLayout must be on a class.
This should do the trick:
[DllImport("xfoilapi.dll")]
public static extern IntPtr GetXfoilResults();
[StructLayout(LayoutKind.Sequential)]
public class XfoilResults
{
IntPtr pAlfa;
IntPtr pCL;
IntPtr pCM;
IntPtr pCDi;
IntPtr pCDo;
IntPtr pCPmax;
int nEntries; // thanks to guys for reminding me long is 4 bytes
}
XfoilResults xf == new XfoilResults();
Marshal.PtrToStructure(GetXfoilResults(), xf);
Firstly, the return type of your imported function should be either IntPtr or [MarshalAs(UnmanagedType.LPStruct)] xfoilResults_t.
A second important note is that, if xfoilResults() is allocating and populating the data in that struct, there should somewhere be a second function to clean up that memory. You must also import that - and call it as necessary, or you will end up with memory leaks.
If you're going to marshal this manually (ie, the import returns an IntPtr), You should be able to use
IntPtr retval = xfoilResults();
var results = (xfoilResults_t)Marshal.PtrToStructure(
retVal,
typeof(xfoilResults_t));
//Do the following for each IntPtr field
double[] pCL = new double[results.nEntries];
Marshal.Copy(results.pCL, pCL, 0, results.nEntries);
//Don't forget to call whichever function is cleaning up the unmanaged memory.

Categories