Passing byte[] as IntPtr by PInvoke to memset - c#

I need to pass a byte array to memset, which due to P/Invoke clunkiness takes IntPtr. Tested by hand, it works, but I am seeking theoretical confirmation. Is this method correct?
[DllImport("msvcrt.dll", EntryPoint = "memset", CallingConvention = CallingConvention.Cdecl, SetLastError = false)]
public static extern IntPtr MemSet(IntPtr dest, int c, int count);
static unsafe void ZeroMemset (byte[] data)
{
fixed (byte* bytes = data) {
MemSet ((IntPtr)bytes, 0, data.Length);
}
}

Your code is fine and will work correctly.
It would be perfectly reasonable, and much clearer in my view, to avoid unsafe and declare the parameter to memset to be byte[]. I'd declare it like this:
[DllImport("msvcrt.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern IntPtr memset(byte[] dest, int c, IntPtr count);
Note that the final parameter is size_t which is pointer sized.
I also do wonder why you are opting to do this at all in unmanaged code, but presumably you have your reasons.

Related

Use C++ DLL in C#

My program need control a hardware. Vendor provide a DLL which is design for C/C++ language.
Most of functions passed my test beside below function:
int32 ni845xSpiWriteRead(NiHandle DeviceHandle,
NiHandle ConfigurationHandle,
uInt32 WriteSize,
uInt8 * pWriteData,
uInt32 * pReadSize,
uInt8 * pReadData);
Document of this function is here:
Inputs NiHandle DeviceHandle
Device handle returned from ni845xOpen.
NiHandle ConfigurationHandle
The configuration handle returned from ni845xSpiConfigurationOpen.
uInt32 WriteSize
The number of bytes to write. This must be nonzero.
uInt8 * pWriteData
The data bytes to be written.
Outputs uInt32 * pReadSize
A pointer to the amount of bytes read.
uInt8 * pReadData
A pointer to an array of bytes where the bytes that have been read
are stored.
My code in C# is here:
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
long DeviceHandle, //In
long ConfigurationHandle, //In
int WriteSize, //In
IntPtr pWriteData, //In
IntPtr pReadSize, //Out
IntPtr pReadData //Out
);
I always got AccessViolationException exception. I am guessing that is caused by the pointer input/output parameter.
The code of invoke Swrapper.ni845xSpiWriteRead() i here:
public void WriteData(int length, int[] writeArray)
{
byte[] writeDate = new byte[8];
int writeSize = writeDate.Length;
try
{
//Define pointers
IntPtr writeDataPointer = Marshal.AllocHGlobal(writeDate.Length);
IntPtr readDataSizePointer = Marshal.AllocHGlobal(writeDate.Length);
IntPtr readDataPointer = Marshal.AllocHGlobal(writeDate.Length);
//Copy value to write data pointer
Marshal.Copy(writeDate, 0, writeDataPointer, writeDate.Length);
int state = Ni845xNativeMethods.ni845xSpiWriteRead(_niHandle, _niConfigrationHandle, writeSize, writeDataPointer,readDataSizePointer,readDataPointer);
this.CheckStatus(state);
}
catch (Exception)
{
throw;
}
}
Most likely the p/invoke should be:
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
IntPtr DeviceHandle,
IntPtr ConfigurationHandle,
uint WriteSize,
[In] byte[] WriteData,
out uint ReadSize,
[Out] byte[] ReadData
);
You'll need to allocate a byte[] of sufficient length to use as the ReadData parameter. Presumably you know how to do this.
I've taken at face value the statement that ReadSize is an output parameter. However, if it is both in and out, then declare it as ref.
The most likely thing is it's just the Out size parameter. So:
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
long DeviceHandle, //In
long ConfigurationHandle, //In
int WriteSize, //In
IntPtr pWriteData, //In
out uint ReadSize, //Out
IntPtr pReadData //Out
);
This is similar to #Marius, but the handles should be fine as integers. The trick is to get the size returned as an integer, so either 'out uint' or 'ref uint' to do that.
Much better if we could see the calling code.
Having seen the code, you could also try something like this.
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
long DeviceHandle, //In
long ConfigurationHandle, //In
int WriteSize, //In
[MarshalAs(UnmanagedType.LPArray)] long [] writeData, //In
out uint ReadSize, //Out
[MarshalAs(UnmanagedType.LPArray), Out] readData //Out
);
This avoids all that Marshal.Copy stuff. Make sure the readData buffer is big enough.
I think both of the existing answers are somewhat incorrect, so I will add another (probably also incorrect) answer.
If DeviceHandle and ConfigurationHandle are truly HANDLE types (or other pointer types) then you should use IntPtr as the type for P/Invoke.
Given the C function declaration, the ReadData buffer must be allocated by the caller. Also, it is likely that ReadSize must be initialized to the size of the buffer and will be set to the actual number of bytes read during the function call.
I think this may come close to working:
class Program
{
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]
public static extern int ni845xSpiWriteRead(
IntPtr DeviceHandle,
IntPtr ConfigurationHandle,
UInt32 WriteSize,
[MarshalAs(UnmanagedType.LPArray)][In] Byte[] pWriteData,
ref UInt32 ReadSize,
[MarshalAs(UnmanagedType.LPArray)][Out] Byte[] pReadData);
static void Main(string[] args)
{
IntPtr DeviceHandle = (IntPtr)123;
IntPtr ConfigurationHandle = (IntPtr)456;
Byte[] WriteData = new Byte[2324];
Byte[] ReadData = new Byte[8274];
UInt32 ReadSize = (UInt32)ReadData.Length;
int result = ni845xSpiWriteRead(DeviceHandle,
ConfigurationHandle,
(UInt32) WriteData.Length,
WriteData,
ref ReadSize,
ReadData);
}
}
Change your calling convention from Cdecl to stdCall and it will work now.
[DllImport("Ni845x", CallingConvention = CallingConvention.Cdecl)]

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 copy a array of unmanaged memory into the same unmanaged memory

I reserved memory 10 items of 128 bytes
IntPtr dst = Marshal.AllocHGlobal (10 * 128);
IntPtr src1 = Marshal.AllocHGlobal (128);
// .... init scr1 from DLL
IntPtr src2 = Marshal.AllocHGlobal (128);
// .... init scr2 from DLL
I need to copy the 128 bytes elements of src1 and src2 to dst at the specified offset.
Marshal.Copy not suitable for such purposes.
Since the src and dst in unmanaged memory area.
The Window's API function memcopy should do the trick.
[DllImport("msvcrt.dll", EntryPoint = "memcpy",
CallingConvention = CallingConvention.Cdecl,
SetLastError = false)]
public static extern IntPtr memcpy(IntPtr dest, IntPtr src, UIntPtr count);
Also, check this out:
https://stackoverflow.com/a/2658394/558018
As it claims, you can use unsafe context to manually transfer necessary bytes.
If you want to use the Windows API to do this, use MoveMemory.
[DllImport("Kernel32.dll", EntryPoint="RtlMoveMemory", SetLastError=false)]
static extern void MoveMemory(IntPtr dest, IntPtr src, int size);

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 can I pass a pointer to an array using p/invoke in C#?

Example C API signature:
void Func(unsigned char* bytes);
In C, when I want to pass a pointer to an array, I can do:
unsigned char* bytes = new unsigned char[1000];
Func(bytes); // call
How do I translate the above API to P/Invoke such that I can pass a pointer to C# byte array?
The easiest way to pass an array of bytes is to declare the parameter in your import statement as a byte array.
[DllImport EntryPoint="func" CharSet=CharSet.Auto, SetLastError=true]
public extern static void Func(byte[]);
byte[] ar = new byte[1000];
Func(ar);
You should also be able to declare the parameter as an IntPtr and Marshal the data manually.
[DllImport EntryPoint="func" CharSet=CharSet.Auto, SetLastError=true]
public extern static void Func(IntPtr p);
byte[] ar = new byte[1000];
IntPtr p = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(byte)) * ar.Length);
Marshal.Copy(ar, 0, p, ar.Length);
Func(p);
Marshal.FreeHGlobal(p);
You can use unsafe code:
unsafe
{
fixed(byte* pByte = byteArray)
IntPtr intPtr = new IntPtr((void *) pByte);
Func(intPtr);
}
If you need to use safe code, you can use a few tricks:
IntPtr intPtr = Marshal.AllocHGlobal(Marshal.SizeOf(byteArray));
Marshal.Copy(byteArray, 0, intPtr, Marshal.SizeOf(byteArray));
Func(intPtr);
Marshal.FreeHGlobal(intPtr);
However, the safe code is going to be slow IMHO.
Here is the appropriate signature for the native function.
[System.Runtime.InteropServices.DllImportAttribute("<Unknown>", EntryPoint="Func")]
public static extern void Func(System.IntPtr bytes) ;

Categories