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.
Related
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.
I am trying to call a function from a dll. Description of the function in C ++:
BOOL WINAPI PDLCSGetPropertyEx(LPCTSTR lpszProjectName, LPCTSTR lpszPictureName, LPCTSTR lpszObjectName, LPCTSTR lpszPropName, VARTYPE vt, LPVOID pvProp, DWORD dwFlags, LPVOID pData, PCMN_ERROR pError);
In C # I have written:
[DllImport("pdlcsapi.dll", EntryPoint = "PDLCSGetPropertyEx", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi)]
public static extern bool PDLCSGetPropertyEx(
[In] String lpszProjectName,
[In] String lpszPictureName,
[In] String lpszObjectName,
[In] String lpszPropName,
VarEnum vt,
IntPtr pvProp,
[In] UInt32 dwFlags,
[In] IntPtr pData,
[In,Out] [MarshalAs(UnmanagedType.LPStruct)] CMN_ERROR_MANCLASS pError
);
and the call in the application:
WinCCODK_PDLCS.CMN_ERROR_MANCLASS errPdl;
errPdl = new WinCCODK_PDLCS.CMN_ERROR_MANCLASS();
IntPtr p = new IntPtr();
String propName = listBox2.SelectedItem.ToString();
String objName = listBox1.SelectedItem.ToString();
bool ret = WinCCODK_PDLCS.CPDLCSWrapper.PDLCSGetPropertyEx(
"C:\\DemoProjectV72_Light\\DemoProjectV72_Light.mcp"
, "io.pdl"
, objName
, propName
, VarEnum.VT_I4
, p
, 0
, IntPtr.Zero
, errPdl );
if (ret){
listBox2.Items[listBox2.SelectedIndex] = listBox2.Items[listBox2.SelectedIndex] + val.ToString();
}else{
MessageBox.Show(errPdl.szErrorText);
}
After calling this function, the application falls (there is a standard Windows window stopped working program)
How to pass parameters to a function?
Other features of this library are working fine. .
I think that does not work correctly with the parameters vt and PvProp. These are described in the documentation:
vt
Data type of the value passed with pvProp. Valid types are defined
in the "VARENUM" enumeration within the "wtypes.h" Include file
belonging to the compiler. No VT_VARIANT, VT_DISPATCH or other
references should be used.
PvProp
Pointer to a tag to which the property value is saved. The data
type of the value is determined by vt. For pvProp you can specify any
value defined for the Variant data type; refer to the values contained
in the "wtypes.h" Include file belonging to your compiler. For types
which have a buffer (e.g. BSTR), the buffer is allocated by the
function and must be cleared afterwards by the calling application.
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);
I'm trying to call the WinAPI function CalculatePopupWindowPosition in C# using P/Invoke. From
http://msdn.microsoft.com/en-us/library/windows/desktop/dd565861(v=vs.85).aspx
I see that it's syntax is:
BOOL WINAPI CalculatePopupWindowPosition(
_In_ const POINT *anchorPoint,
_In_ const SIZE *windowSize,
_In_ UINT flags,
_In_opt_ RECT *excludeRect,
_Out_ RECT *popupWindowPosition
);
I then tried to import it using the following code in C#
[DllImport("User32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)]
public static extern bool CalculatePopupWindowPosition
(
[In] ref POINT anchorPoint,
[In] ref SIZE windowSize,
[In] ref UInt32 flags,
[In,Optional] ref RECT excludeRect,
[Out] out SIZE popupWindowPosition
);
I also implemented the RECT, POINT and SIZE structures and initialized them. Finally I called the function like so.
CalculatePopupWindowPosition(ref nIconPos, ref windowSize, ref flags, ref nIconRect, out windowSize);
This doesn't seem to work though, windowSize contains nothing but zeros, which it shouldn't. Any ideas what I'm doing wrong here?
The flags parameter needs to be passed by value rather than by reference:
[DllImport("User32.dll", SetLastError = true)]
public static extern bool CalculatePopupWindowPosition
(
ref POINT anchorPoint,
ref SIZE windowSize,
uint flags,
ref RECT excludeRect,
out RECT popupWindowPosition
);
Some general advice. When an API call fails, check the return value. In this case if the function returns false then call Marshal.GetLastWin32Error to find out the error status code.
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**);