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
);
Related
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);
I am working on a project where I need to PInvoke the secur32!AddSecurityPackageA function, but I am still learning the ins and outs of how to do this by hand and could use some help.
Here are a the references I am working with:
https://learn.microsoft.com/en-us/windows/desktop/api/sspi/nf-sspi-addsecuritypackagea
https://learn.microsoft.com/en-us/windows/desktop/api/sspi/ns-sspi-_security_package_options
And here's a sample of my code where I am trying to define the struct and call the function:
[DllImport("secur32.dll", EntryPoint = "AddSecurityPackageA")]
public static extern void AddSecurityPackageA(
ref string pszPackageName,
ref SECURITY_PACKAGE_OPTIONS[] Options
);
[StructLayout(LayoutKind.Sequential, CharSet =CharSet.Ansi)]
public class SECURITY_PACKAGE_OPTIONS
{
public ulong Size;
public ulong Type;
public ulong Flags;
public ulong SignatureSize;
public IntPtr Signature;
}
string dll = #"c:\temp\test.dll";
SECURITY_PACKAGE_OPTIONS[] pkgOpts = new SECURITY_PACKAGE_OPTIONS();
AddSecurityPackageA(ref dll, ref pkgOpts);
My questions are:
At lines 3 and 4, is this an appropriate use of ref and is this generally correct according to the MSDN docs?
At line 14, the C++ struct on MSDN has this as a void pointer, but while researching I found that the C# equivalent is an IntPtr. Is that correct or do I need to use unsafe?
In general, has anyone found any really good PInvoke tutorials outside of reading other people's code? I'm moving over from Python so it is quite a bit different and much of what I've found is either "draw a circle, draw the rest of the owl" or insanely lengthy MSDN documentation that makes a lot of assumptions.
Thank you!
Some comments:
Use the W function rather than the A function. You don't want to limit yourself to ANSI. This is a Unicode world.
The function has a return value. You must declare the function with a matching return value type. Presumably it is uint or int but you should check in the C++ header file.
ref string is wrong. It should be string.
ref SECURITY_PACKAGE_OPTIONS[] is wrong. It is not an array. It is a pointer to a struct. Since you declared SECURITY_PACKAGE_OPTIONS as a class, a reference type, you can replace ref SECURITY_PACKAGE_OPTIONS[] with SECURITY_PACKAGE_OPTIONS.
C++ unsigned long is 32 bits, so it should be uint in C#.
IntPtr is correct, but that leaves unresolved the question of how to declare the digital signature and obtain a pointer to it. I think it's outside the remit of this question for us to track down an example of how to do that.
This one works for me:
[DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint AddSecurityPackage(
string pszPackageName,
SECURITY_PACKAGE_OPTIONS Options
);
[DllImport("secur32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern uint DeleteSecurityPackage(
string pszPackageName
);
I've got a COM interface dll, and some documentation about the methods within the dll, however from what I can tell with the documentation, the method requires a custom struct be passed in as an out parameter, but the documentation doesn't provide any information about the struct itself.
The function I'm calling from c# is as follows:
API
UINT OpenRequest ([out] PHCOMS phComs,
[in] PCHAR pEndPointName,
[in] UINT fBlockingMode,
[in] UINT fOpenMode,
[in] UINT fDataType,
[in] HINSTANCE hInstance)
My issue is that PHCOMS is a type described in the documentation as:
The address of a variable of type HCOMS. The communications handle to be used by the other API calls, is returned in this variable. If an error occurs this will be NULL.
This function starts by allocating a comms handle (hComs), if
available, to the application. This handle is used for creating a new
connection object. The parameters of this function are assigned to the
relevant members. For this object, a pointer instance is created for
connecting to the interface, as well as the PC Headerpched
interface
I've had a look at various dll inspectors and other posts on SO but can't find anything that helps with how I can actually create a struct that's going to be validly accepted by the function.
I am also unsure if I'm misinterpreting what I need to do - is it possible that I just need to pass a standard handle through to the function or something similar? Is the PHComs type a non custom type that I just can't find info on in google?
Right now I'm attempting to call the function through c# using the DLLImport attribute over an extern function which seems to be ok except that the handle I get back is never valid, which I'm blindly (I've never done something like this before) trying to do like this
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint OpenRequest(ref SafeProcessHandle hComs, string host, uint fBlockingMode, uint fOpenMode, uint fDataType, SafeProcessHandle hInstance);
static void Main(string[] args)
{
IntPtr h = new IntPtr();
var handle = new SafeProcessHandle(h, true);
uint test = 0;
test = OpenRequest(ref handle, "Host01", 0, 0, 0, handle);
handle.Dispose();
}
Any assistance on how to setup my test properly would help too since I'm blindly guessing about the IntPtr and use of whatever Safe*Handle Classes.
Edit
I've changed it to be like this following Hans' comment:
As you said, there are other methods that use that handle, for example the ConnectRequest added below.
Any suggestion as to what the HInstance parameter refers to? I doubt it's correct that I'm just passing the same handle/IntPtr in again?
The documentation says:
hInstance HINSTANCE Windows programs must pass their instance handle.
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint OpenRequest(out IntPtr hComs, string host, uint fBlockingMode, uint fOpenMode, uint fDataType, IntPtr hInstance);
[DllImport("MyDll.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint ConnectRequest(IntPtr hComs, uint cTimeOut);
static void Main(string[] args)
{
IntPtr hComs = new IntPtr();
uint test_OpenRequest = 0;
test_OpenRequest = OpenRequest(out hComs, "Host01", 0, 0, 0, hComs);
}
Edit 2
[DllImport("WinCom32.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall)]
public static extern uint OpenRequest(out IntPtr hComs, string host, uint fBlockingMode, uint fOpenMode, uint fDataType, IntPtr hInstance);
static void Main(string[] args)
{
IntPtr hComs = new IntPtr();
uint test_Request = 0;
string hostName = ConfigurationManager.AppSettings["HostName"];
IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetEntryAssembly().GetModules()[0]);
test_Request = OpenRequest(out hComs, hostName, 0, 0, 1, hInstance);
Console.WriteLine("Request response value: " + test_Request.ToString());
}
There's a test application I can run that shows that this is supposed to work because it's able to connect to the host, so I've got to be doing something wrong.
I have a c++ function which I am calling from c# using pinInvoke. Following is my cpp method-
int test(DWORD verb,DWORD verb2 )
{
return verb2 *100;
}
My function is exposed as -
extern "C" {
__declspec(dllexport) int test(DWORD verb, DWORD verb2);
}
Following is my c# code where I am calling the above method:
public class API
{
[DllImport("mydll.dll", EntryPoint = "test", SetLastError = true, CallingConvention = CallingConvention.Cdecl)]
[return: MarshalAs(UnmanagedType.U4)]
public static extern uint test(
[MarshalAs(UnmanagedType.U8)]ulong verb,
[MarshalAs(UnmanagedType.U8)]ulong verb2);
static void Main(string[] args)
{
uint x = DPAPI.test(26,10);
Console.Write("result is-"+x);
}
}
Here the second value is getting passed as 0,so wrong result is coming. Am I doing something wrong while passing the value?
What I have tried:
I am relatively new to Pinvoke. So I tried debugging to see whether the value is not getting passed to c++ code or whether the c++ code is not returning proper values.I found that the value getting passed itself was wrong.
DWORD is a 32 but unsigned integer. You are mapping that to a 64 bit type. That is the problem. Your p/invoke should be:
[DllImport("mydll.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern int test(uint verb, uint verb2);
Note that I removed the needless EntryPoint and the erroneous SetLastError from the DllImport attribute.
I also wonder why you have selected DWORD. Unless you are passing those values onto Win32 functions it would likely make more sense to use intrinsic types. Why not use int or unsigned int in your C++ code?
I'm adding new disk device to system from my C# code, so I want to call
[System.Runtime.InteropServices.DllImport("Shell32.dll", CharSet = CharSet.Unicode, SetLastError = true)]
public extern static void SHChangeNotify(long wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
like below
MyWin32Functions.SHChangeNotify(0x00000100/*ADDRIVE*/, 0x0005/*PATHW*/, driveLetter, IntPtr.Zero);
dwItem1 is void* and we should pass a wchar_t* (pointing to null terminated string) containing drive root in this case; so driveLetter above is
string letter = "Z:\\";
byte[] data = Encoding.Default.GetBytes(letter);
byte[] zdata = new byte[data.Length + 1];
data.CopyTo(zdata, 0);
IntPtr p = System.Runtime.InteropServices.Marshal.AllocHGlobal(zdata.Length);
System.Runtime.InteropServices.Marshal.Copy(zdata, 0, p, zdata.Length);
(my code almost same as code in similiar case: How to call SHChangeNotify from C# without error 14007
but I get System.AccessViolationException)
Any suggestions what am I doing wrong?
The first parameter in your interop signature should be an int, not a long. Though the Win32 function is declared as LONG, a LONG in Win32 is 32-bit.
[System.Runtime.InteropServices.DllImport("Shell32.dll")]
public extern static void SHChangeNotify(int wEventId, uint uFlags, IntPtr dwItem1, IntPtr dwItem2);
This MSDN article shows the common mapping between Win32 types an .NET types for Platform Invoke.