PInvoke with pointers - C++ to C# - c#

The following is a PInvoke block in C++ that I'm trying to convert to C#:
typedef PVOID JHANDLE ;
typedef UINT32 JRET ;
#define JEXPORT __declspec(dllimport) JRET
JEXPORT
JInitialize (
OUT JHANDLE* ppHandle,
IN PVOID context,
IN UINT32 flags
) ;
I tried the following call, but it threw a PInvokeStackImbalance exception saying that the signatures don't match:
[DllImport("jhi.dll")]
public static extern UInt32 JHI_Initialize(out IntPtr ppHandle, IntPtr context, UInt32 flags);
Obviously a pointer of a pointer is handled different, but I'm not exactly sure how it translates.

You are missing the CallingConvention in your [DllImport] declaration, it is Cdecl.

Related

How to PInvoke CMark's cmark_markdown_to_html

I'm trying to PInvoke the following function from GitHub's fork of CMark
char *cmark_markdown_to_html(const char *text, size_t len, int options)
and here's my PInvoke signature:
[DllImport("cmark.dll")]
public static extern IntPtr cmark_markdown_to_html(
[In()] [MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.SysUInt)] uint len,
[MarshalAs(UnmanagedType.SysInt)] int options);
I call it as follows:
var retValue = cmark_markdown_to_html(markdown, 0, 0);
However, it throws a Marshaling exception the with the message:
Cannot marshal 'parameter #2':
Invalid managed/unmanaged type combination
(Int32/UInt32 must be paired with I4, U4, or Error).
OK, so I change the signature to:
[DllImport("cmark.dll")]
public static extern IntPtr cmark_markdown_to_html(
[In, MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.U4)] uint len,
[MarshalAs(UnmanagedType.I4)] int options);
Now it throws a PInvokeStackImbalance error
The name '$exception' does not exist in the current context
Native stuff is a mystery to me. Can someone help?
Stack imbalance
The reason for stack imbalance is described in Why are Cdecl calls often mismatched in the "standard" P/Invoke Convention?.
Basically you need to specify proper calling convention in the DllImport that will probably be cdecl:
[DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
Marshalling size_t
That one is discussed in what is equal to the c++ size_t in c# .
Assuming that CMark will always be native (32 bit on x86, 64 bit on x64), you should use (U)IntPtr.
If it is always 32 bit, then Int32 should be used.
PInvokeStackImbalance
Presumably CMark is using the default calling convention. The default calling convention for C code is "Cdecl", while the default used for P/invoke calls is "StdCall". The calling convention specifies how the arguments and return value are placed on the call stack. A mismatch between the used calling convention causes the stack to become imbalanced.
You can specify the calling convention to use from C# code in the DllImport attribute.
[DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cmark_markdown_to_html(
[In, MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.U4)] uint len,
[MarshalAs(UnmanagedType.I4)] int options);
Marshalling
In case you want your code to be compatible with architectures other than 32-bit, you would want to marshall size_t with UIntPtr. (Read #Eugene Podskal answer)
[DllImport("cmark.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr cmark_markdown_to_html(
[In, MarshalAs(UnmanagedType.LPStr)] string text,
[MarshalAs(UnmanagedType.U4)] UIntPtr len,
[MarshalAs(UnmanagedType.I4)] int options);
Then you can call the method like so
var retValue = cmark_markdown_to_html(markdown, UIntPtr.Zero, 0);

Marshal void** c#

I try to use C dll in C#
typedef enum M_STATUS
{
// Unknown error. Should not be returned.
M_UNKNOWN = -1,
// Successful.
M_OK = 0
} M_STATUS;
M_STATUS WINAPI M_Create(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal);
In C# I have
[DllImport("Y:\\libs\\Min.x86.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern M_STATUS M_Create(IntPtr pTarget, IntPtr pDetour, ref IntPtr ppOriginal);
But I always get PinvokeStackImbalance Exception
Where is mistake?
Adding as an actual answer so it won't get lost.
The C function is declared as WINAPI, but the calling convention specified in the DllImport attribute is Cdecl. The conventions must match, so change it to either StdCall or Winapi.

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

How to get IntPtr to a boolean value

For a function marshalled like that:
/*************************************************
* DWORD WINAPI WlanHostedNetworkSetProperty(
* _In_ HANDLE hClientHandle,
* _In_ _WLAN_HOSTED_NETWORK_OPCODE OpCode,
* _In_ DWORD dwDataSize,
* _In_ PVOID pvData,
* _Out_opt_ P_WLAN_HOSTED_NETWORK_REASON pFailReason,
* _Reserved_ PVOID pvReserved
* );
*************************************************/
[DllImport("Wlanapi.dll", SetLastError = true)]
public static extern UInt32 WlanHostedNetworkSetProperty(
[In] IntPtr hClientHandle,
[In] _WLAN_HOSTED_NETWORK_OPCODE OpCode,
[In] UInt32 dwDataSize,
[In] IntPtr pvData,
[Out] out _WLAN_HOSTED_NETWORK_REASON pFailReason,
[In, Out] IntPtr pvReserved
);
Microsoft documentation says that when I pass
_WLAN_HOSTED_NETWORK_OPCODE._WLAN_HOSTED_NETWORK_OPCODE_enable
as a parameter of OpCode, the value of pvData should be a pointer to a Boolean value.
Hereis the documentation for that function
I have no idea though how to get an IntPtr to point to a Boolean?
Should it be done in a similar way to when I pass a pointer to a struct as pvData :
int size = Marshal.SizeOf(settings); //*settings* is a struct with some data
IntPtr pSettings = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(settings, pSettings, true);
/* use the IntPtr */
Marshal.FreeHGlobal(pSettings);
But instead I marshal the Boolean? Or is there an easier way?
Thanks for your help all lovely people.
Win32 BOOL is DWORD. You can define pvData as ref UInt32 or ref Int32 for this case. Or leave pvData as IntPtr, allocate unmanaged memory with Marshal.AllocHGLobal(Marshal.SizeOf(Int32)), and fill this memory with Marshal.WriteInt32.
int size = Marshal.SizeOf(Int32);
IntPtr pBool = Marshal.AllocHGlobal(size);
Marshal.WriteInt32(pBool, 0, 1); // last parameter 0 (FALSE), 1 (TRUE)
/* use the IntPtr */
Marshal.FreeHGlobal(pBool);
Oh, yes, there's a much easier way. This can be very elegantly done in C# because it supports method overloading. A feature that doesn't exist in C and thus requires the PVOID argument type. You can simply declare the argument as ref bool. The marshaller will create the storage for the BOOL before calling the native function and pass a pointer to it. Converting and copying the result back into the bool variable you pass after the call.
If you have additional calls for this function that require a different data type then just repeat the declaration. This time specifying, say, ref uint if you are supposed to pass a pointer to a DWORD. Etcetera. The C# compiler's method overload resolution feature ensures that the correct pinvoke declaration is used.

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"));
}

Categories