How to pass byte array from C# to external DLL - c#

Given a method with this signature in a DLL
int32_t __stdcall ndUDSReadDataByIdentifier(
TD1 *diagRef,
uint16_t ID,
uint8_t dataOut[],
int32_t *len,
int8_t *success);
How does the C# external call look like?
I tried:
[DllImport("nidiagcs.dll")]
public static extern Int32 ndUDSReadDataByIdentifier(
ref TD1 diagRef,
[MarshalAs(UnmanagedType.U2)] UInt16 ID,
byte[] dataOut,
[MarshalAs(UnmanagedType.U4)] ref Int32 len,
[MarshalAs(UnmanagedType.U1)] ref byte success);
The call is executed but the dataOut is not filled.

Okay I found the solution.
[DllImport("nidiagcs.dll", CallingConvention = CallingConvention.StdCall)]
private static extern Int32 ndUDSReadDataByIdentifier(
ref TD1 diagRef,
UInt16 ID,
Byte[] dataOut,
ref Int32 len,
out byte success);
This is the correct way to call the function. It was a mistake from my side. One needs to supply a buffer array in dataOut and the according size of the buffer in len. I always set len to 0 which makes the library think the dataOut array has a size of zero so nothing is returned.
Thanks for everyones help!

Related

Get pointer (IntPtr) from a Span<T> staying in safe mode

I would like to use Span and stackalloc to allocate an array of struct and pass it to an interop call. Is it possible to retrieve a pointer (IntPtr) from the Span without being unsafe ?
Here is how i did it without unsafe:
i just changed lpBuffer to ref byte instead of byte[] (for c++ user COULD represent it as uint8_t*)
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool ReadProcessMemory(nint hProcess, nuint lpBaseAddress, ref byte lpBuffer, int dwSize, out int lpNumberOfBytesRead);
Span<byte> bytes = stackalloc byte[8];
// Get reference to first byte in the span (for c++ user we COULD represent it as `uint8_t*`)
ref byte bytesReference = ref MemoryMarshal.AsRef<byte>(bytes);
// You can pass `ref MemoryMarshal.AsRef<byte>(bytes)` to the function directly
bool success = Win32.ReadProcessMemory(_processHandle, address, ref bytesReference, cSize, out int numberOfBytesRead);
lpBuffer could be in byte instead of ref byte as that will allow you to use ReadOnlySpan
[DllImport("kernel32.dll", SetLastError = true)]
public static extern bool WriteProcessMemory(nint hProcess, nuint lpBaseAddress, in byte lpBuffer, int dwSize, out int lpNumberOfBytesWritten);
ReadOnlySpan<byte> bytes = stackalloc byte[8];
ref readonly byte bytesReference = ref MemoryMarshal.AsRef<byte>(bytes);
// You can pass `in MemoryMarshal.AsRef<byte>(bytes)` to the function directly
Win32.WriteProcessMemory(_processHandle, address, in bytesReference, bytes.Length, out int numberOfBytesWritten);
Notes:
[+] You could use GetPinnableReference instead of MemoryMarshal.AsRef but it is not intended to be called by user code.
[+] ReadProcessMemory Windows API function.
[+] ReadProcessMemory(kernel32) pinvoke Original implementation.

Returning a byte array from a C dll to C#

I'm trying to get a byte array from a DLL written in C in my C# program. The DLL is used to communicate with a National Instruments USB-8451. The function I'm trying to use returns the pointer to the array as an output parameter. Most of the questions/answer I've found online for this type of issue have the function returning the pointer to the array (not using a parameter).
The function in c has the following prototype.
int32 ni845xI2cWriteRead (
NiHandle DeviceHandle,
NiHandle ConfigurationHandle,
uInt32 WriteSize,
uInt8 * WriteData,
uInt32 NumBytesToRead,
uInt32 * ReadSize,
uInt8 * ReadData
);
In C# I have following code to access the DLL.
[DllImport("NI845x.dll")]
public static extern Int32 ni845xI2cWriteRead(
IntPtr DeviceHandle,
IntPtr ConfigurationHandle,
UInt32 WriteSize,
byte[] WriteData,
UInt32 NumBytesToRead,
out UInt32 ReadSize,
out IntPtr ReadData
);
The following is code is what I'm using to access the ni845xI2cWriteRead function.
Int32 err = 0;
IntPtr ptrToRead = IntPtr.Zero;
err = ni845xI2cWriteRead(DeviceHandle, I2CHandle, WriteSize,WriteData,
NumBytesToRead, out ReadSize, out ptrToRead);
byte[] rd = new byte[ReadSize];
Marshal.Copy(ptrToRead, rd,0, (int)ReadSize);
The problem I'm having is getting the ReadData array. The ReadSize is returning correctly. The byte array I get out seems to be fairly random. Sometimes all zeros, sometimes has (incorrect) values and sometimes I get an access violation error. I know that the command is correctly sending and receiving the data from the USB-8451 because I'm using NI I/O Trace so I can see the correct data going out and coming back.
What am I doing wrong? I can't see it and this has been really frustrating. Thanks.
Andro, you nailed it. Thank you! Sigh of relief. I had previously tried out byte[] ReadData and that did not work but had not tried just the byte[] ReadData. The correct DllImport is below.
[DllImport("NI845x.dll")]
public static extern Int32 ni845xI2cWriteRead(
IntPtr DeviceHandle,
IntPtr ConfigurationHandle,
UInt32 WriteSize,
byte[] WriteData,
UInt32 NumBytesToRead,
out UInt32 ReadSize,
byte[] ReadData
);

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

Marshalling native function

I have a function in a C DLL that performs SCrypt key derivation, but I'm having real trouble marshalling the values into my C# program.
The function declaration is as follows in C:
__declspec(dllexport) int scrypt(const uint8_t * passwd, size_t passwdlen, const uint8_t * salt, size_t saltlen, uint64_t N, uint32_t r, uint32_t p,uint8_t * buf, size_t buflen);
passwd and salt are both passed in a pointers to uint8 arrays, N, r and p are tuning values for the algorithm. Buflen is the size you wish the output buffer to be. buf is the output buffer (and so either needs to be a ref or an out I think);
I've tried various approaches, with the most recent being Marshal.Copy to move data out of the IntPtrs into byte arrays (and vice-versa), however as these are UInt pointers as opposed to IntPtr I don't know if that is right. Currently it crashes when I try and copy the data out of the buf IntPtr and back into the array.
I'd really appreciate any assistance.
class Program
{
[DllImport("SCrypt.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
public static extern int scrypt(IntPtr passwd, UIntPtr passwdlen, IntPtr salt, UIntPtr saltlen, UInt64 N, UInt32 r, UInt32 p, out IntPtr buf, UIntPtr buflen);
static void Main(string[] args)
{
byte[] encoded = new byte[32];
IntPtr encodedptr;
byte[] password = System.Text.Encoding.Unicode.GetBytes("test");
byte[] salt = System.Text.Encoding.Unicode.GetBytes("ThisistheSaltweareusingforthiskey");
IntPtr passwordptr, saltptr;
passwordptr = Marshal.AllocHGlobal(password.Length);
Marshal.Copy(password, 0, passwordptr, password.Length);
saltptr = Marshal.AllocHGlobal(salt.Length);
Marshal.Copy(salt, 0, saltptr, salt.Length);
int returnVal = scrypt(passwordptr, (UIntPtr)password.Length, saltptr, (UIntPtr)salt.Length, 262144, 8, 1,out encodedptr,(UIntPtr) 32);
Marshal.Copy(encodedptr, encoded, 0, 32);
Console.WriteLine(BitConverter.ToString(encoded));
Console.ReadKey();
}
}
I'd probably use the following function declaration:
public static extern int scrypt(byte[] passwd, uint passwdlen, byte[] salt, uint saltlen, ulong N, uint r, uint p, byte[] buf, uint buflen).
Keep in mind that there are multiple different ways of marshalling certain types, but in this case the above variant is probably the clearest because it maps directly to the respective types.
You can trivially convert a string to a byte[] by asking an Encoding to do so for you. In the case of passwords as long as you convert to Unicode everything should be fine. But make sure to use the same variant in every place that might need to convert a password to a byte[].

AccessViolationException in PInvoke function call

I'm trying to write a wrapper for C library but I'm really struggling with this error.
I tried many approaches, here is one of them:
[DllImport(DRIVER_FILENAME)]
[return: MarshalAs(UnmanagedType.U4)]
private static extern uint GetData(IntPtr handle,
[MarshalAs(UnmanagedType.LPArray), In()] int[] buffer,
[MarshalAs(UnmanagedType.U4)] uint size);
Here is function GetData from the library documentation:
LONG GetData(
IN HANDLE Handle,
OUT PULONG Buffer,
IN ULONG Size
);
Function returns continuous data (about 16KB/s) in buffer which size is given in bytes. Buffer is int[16384]. My code looks like this:
public static uint GetLibData(IntPtr handle, int[] buffer, uint size)
{
size *= 4;
uint sizeRead = GetData(handle, buffer, size);
sizeRead /= 4;
return sizeRead;
}
Problematic argument is buffer, I tried manage it previously in other ways, such as IntPtr bufferPtr and then allocating memory by Marshal.AllocHGlobal but I was getting the same error:
Attempted to read or write protected memory. This is often an
indication that other memory is corrupt.
How to correctly invoke this function?
The appropriate p/invoke declaration is
[DllImport(DRIVER_FILENAME)]
private static extern uint GetData(
IntPtr handle,
[Out] uint[] buffer,
uint size
);
It is your responsibility to allocate the buffer before you call the function:
uint[] buffer = new uint[16384];
uint bufferSize = buffer.Length*Marshal.SizeOf(typeof(uint));
uint sizeRead = GetData(handle, buffer, bufferSize);
uint lenRead = sizeRead/Marshal.SizeOf(typeof(uint));
The only thing that's not 100% clear is the calling convention. I'd guess that this library uses cdecl which would mean your DllImport should be
[DllImport(DRIVER_FILENAME, CallingConvention=CallingConvention.Cdecl)]
Try with the following PInvoke:
[DllImport(DRIVER_FILENAME)]
private static extern Int32 GetData
(
[In] IntPtr handle,
[Out] out IntPtr buffer,
[In] UInt32 size
);

Categories