I am trying to import a C++ function in my C# code.
This function is defined as:
int SetPointers(int* ID, int* BufferID, int** Pointer, double** Time, int NumberOfPointers);
with ID an array of int,
BufferId an array of int,
Pointer an array of int,
Time an array of double, and
NumberOfPointers an int.
I have tried to use IntPtr without success.
Here is the latest code I tried:
[DllImport("open.dll", SetLastError = true, CharSet = CharSet.Auto, CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern int SetPointers(int* ID, int* BufferID, ref IntPtr Pointer, ref IntPtr Time, int NumberOfPointers);
public unsafe int _SetPointers(int[] ID, int[] BufferID, ref int[] Pointer, ref double[] Time, int NumberOfPointers)
{
IntPtr fQueue = IntPtr.Zero;
IntPtr fTime = IntPtr.Zero;
int breturn = -1;
fixed (int* fId = ID)
fixed (int* fBufferID = BufferID)
fQueue = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(int)) * Pointer.Length);
fTime = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(double)) * Timestamp.Length);
breturn = SetPointers(fId, fBufferID, ref fQueue, ref fTime, NumberOfPointers);
return breturn;
}
How can I do this?
First all, you might want to use IntPtr for your parameters rather than int[].
After this, I haven't tried it but to marshal pointers on pointers a "ref IntPtr" or "out IntPtr" would work.
public unsafe int _SetPointers(IntPtr ID, IntPtr BufferID, ref IntPtr Pointer, ref IntPtr Time, int NumberOfPointers);
Also have a look to this other question: How do I marshall a pointer to a pointer of an array of structures?
Related
I have to pass an Byte array containing an MAC-Address to a C++ Method. Since I don't have much experience with working with c
C++ APIsI don't know how to do this. I've tried to pass the array itself, but got an invalid parameter code as response from the API. I've also tried to create an IntPtr but to no avail.
I know that the problem is that C++ can't handle managed datatypes such as arrays, so I've to create a unmanaged array somehow, I think.
Here is the definition of the C++ Method:
ll_status_t LL_Connect(
ll_intf_t intf,
uint8_t address[6]);
The array in C# is defined the following way:
Byte[] addr = new Byte[6];
Of course, the array is not empty.
For example:
C++
extern "C"
{
__declspec(dllexport) void GetData(uint8_t* data, uint32_t length)
{
for (size_t i = 0; i < length; ++i)
data[i] = i;
}
}
C#
[DllImport("LibName.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void GetData([In, Out] [MarshalAs(UnmanagedType.LPArray)] byte[] data, uint length);
And use in C#
byte[] data = new byte[4];
GetData(data, (unit)data.Lenght);
If you have an array fixed length, for example:
C++
extern "C"
{
__declspec(dllexport) void GetData(uint8_t data[6])
{
for (size_t i = 0; i < 6; ++i)
data[i] = i;
}
}
C#
[DllImport("LibName.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern void GetData([In, Out] [MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] data);
And use in C#
byte[] data = new byte[6];
GetData(data);
For your case:
[DllImport("LibName.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
public static extern int LL_Connect(byte intf, [In, Out] [MarshalAs(UnmanagedType.LPArray, SizeConst = 6)] byte[] address);
I have been trying to call an API from DLL like below:
[DllImport(#"TELCompress.dll", EntryPoint = "TELMonDecode", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int TELMonDecode(ref bool a, ref bool b, byte[] ab, System.IntPtr pDestBuf, int j, int byteCount);
Call from C# code
int returnval = TELMonDecode(ref a, ref b, bytes, destPnt, k, bytesRec);
C++ code in the DLL
__declspec(dllexport) int TELMonDecode(bool *bUnicode, bool *bCompress, BYTE *pSourceBuf, wchar_t* pDestBuf, int pDestBufSize,int byteCount)
{
...
CString decodedMsg = _T("<Empty>");
int erc = DecodeByteStream(bUnicode, bCompress, pSourceBuf, &decodedMsg);
::MessageBox(NULL,L"Decoding byte done",L"Caption",0);
pDestBuf = decodedMsg.GetBuffer();
::MessageBox(NULL,pDestBuf,L"Caption in TELMonDecode",0);
...
}
I have referred to many links here but still I am unable to figure out what wrong I am doing.
Please guide.
Thanks for the comments. It was helpful.
Now the code works as below
C# code
[DllImport(#"TELCompress.dll", EntryPoint = "TELMonDecode", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Unicode)]
public static extern int TELMonDecode(ref bool a, ref bool b, byte[] ab, ref String pDestBuf, int j, int byteCount);
... //Some code here
//Call to the C++ function
int returnval = TELMonDecode(ref a, ref b, bytes, ref receiveStr, k, bytesRec);
C++ code in the TELCompress.dll
__declspec(dllexport) int TELMonDecode(bool *bUnicode, bool *bCompress, BYTE *pSourceBuf, BSTR* pDestBuf, int pDestBufSize,int byteCount)
{
CString decodedMsg = _T("<Empty>");
//Code to copy data in decodedMsg
CComBSTR tempBstrString(decodedMsg.GetBuffer()); //test
tempBstrString.CopyTo(pDestBuf);
.... //Some more code
return 0;
}
And it works, the string is seen in the C# code which was earlier showing an empty string.
Thanks a lot for all valuable feedback and comments.
-Megha
Use BSTR* instead on wchar_t* and you should be able to use ref String at C# side.
I am calling into native code from managed code and I am having a bit of trouble figuring out how to properly marshal my code. In C, I have the following:
struct cHandle {
unsigned long handleLo;
unsigned long handleHi;
}
struct cBuffer {
unsigned long bufferSize;
unsigned long bufferType;
__field_bcount(bufferSize) void *bufferPtr;
}
struct cBufferDesc {
unsigned long bufferVersion;
unsigned long bufferCount;
_field_ecount(bufferCount) cBuffer *buffers;
}
uint __stdcall CMethod(
__in_opt cHandle* handle1,
__in_opt cHandle* handle2,
__in_opt wchar_t* wstr,
__in unsigned long long1,
__in unsigned long resevered1, // Reserved, always 0
__in unsigned long long2,
__in_opt cBufferDesc* inputBufferPtr,
__in unsigned long reserved2, // Reserved, always 0
__inout_opt cHandle* outHandle,
__inout_opt cBufferDesc* outputBufferPtr,
__out unsigned long * outLong,
__out_opt TimeStampStruct* timeStamp);
The behaviour of CMethod is as follows. outputBufferPtr will always output a value. If inputBufferPtr is NULL, handle2 should also be null and CMethod should follow different logic to give an initial output buffer, and if not CMethod should calculate the output buffer based on the data in the input buffer. I am having trouble getting my initial call to work. Additionally, I don't care about the timestamp, so I will not detail that struct, or make a C# equivalent. I have tried the following marshalling in C#:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Handle {
private IntPtr HandleLo;
private IntPtr HandleHi;
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct Buffer {
public uint Size; // Possibly unknown
public uint Type; // Always set.
public IntPtr Buffer; // Possibly unknown
}
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct BufferDesc {
public uint Count; // Always 1 for my purposes
public uint Version; // Always set
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 1)]
public Buffer[] BufferArray; // Will always be a size 1 array.
}
// Used for calling when we have an existing input buffer
[DllImport("mylib.dll", ExactSpelling = "true", CharSet = CharSet.Unicode, SetLastError = true)]
uint CMethod(
[In] ref Handle handle1,
[In] ref Handle handle2,
[In] IntPtr wstr,
[In] uint long1, // C# uint == C ulong
[In] uint reserved1,
[In] uint long2,
[In] ref BufferDesc inputBufferPtr,
[In] uint reserved2,
[In, Out] ref Handle outHandle,
[In, Out] ref BufferDesc outputBufferPtr,
[Out] out IntPtr outLong,
[Out] out IntPtr timestamp);
// Used for calling when we do not have an existing input buffer
// Here IntPtr.Zero will be passed in for handle2 and inputBufferPtr
[DllImport("mylib.dll", ExactSpelling = "true", CharSet = CharSet.Unicode, SetLastError = true)]
private static extern uint CMethod(
[In] ref Handle handle1,
[In] IntPtr handle2,
[In] IntPtr wstr,
[In] uint long1, // C# uint == C ulong
[In] uint reserved1,
[In] uint long2,
[In] IntPtr inputBufferPtr,
[In] uint reserved2,
[In, Out] ref Handle outHandle,
[In, Out] ref BufferDesc outputBufferPtr,
[Out] out IntPtr outLong,
[Out] out IntPtr timestamp);
public static void WrapperMethod(
ref Handle handle1,
ref Handle handle2,
string wstr,
byte[] inputBuffer,
ref Handle outHandle,
out byte[] outputBuffer)
{
BufferDesc inputBufferDesc;
BufferDesc outputBufferDesc;
outputBufferDesc.Count = 1;
outputBufferDesc.Version = 0; // Real data not shown
outputBufferDesc.BufferArray = new Buffer[0];
outputBufferDesc.BufferArray[0].Count = 0;
outputBufferDesc.BufferArray[0].Type = 2; // Real data not shown
outputBufferDesc.BufferArray[0].Buffer = IntPtr.Zero;
IntPtr wstrPtr = Marshal.StringToCoTaskMemUni(wstr);
IntPtr ignoredOutLong;
IntPtr ignoredTimestamp;
if (null != inputBuffer)
{
inputBufferDesc.Count = 1;
inputBufferDesc.Version = 0; // Real data not shown
inputBufferDesc.BufferArray = new Buffer[1];
inputBufferDesc.BufferArray[0].Size = inputBuffer.Length;
inputBufferDesc.BufferArray[0].Type = 2; // Real data not shown
inputBufferDesc.BufferArray[0].Buffer = GCHandle.Alloc(inputBuffer, GCHandleType.Pinned).AddrOfPinnedObject();
CMethod(
ref handle1,
ref handle2,
wstrPtr,
0, // Real data not shown
0,
0, // Real data not shown
ref inputBufferDesc,
0,
ref outHandle,
ref outputBufferDesc,
out ignoreOutLong,
out ignoreTimestamp);
}
else
{ ///////////////////////////////////////////////////////////////////////
// This is the call I am taking and also where the code is crashing. //
CMethod( //
ref handle1, //
IntPtr.Zero, //
wstrPtr, //
0, // Real data not shown //
0, //
0, // Real data not shown //
IntPtr.Zero, //
0, //
ref outHandle, //
ref outputBufferDesc, //
out ignoreOutLong, //
out ignoreTimestamp); //
///////////////////////////////////////////////////////////////////////
}
// Do Cleanup. Not reached at this point.
}
The error that I am getting is that I am trying to access read or write protected memory. If there is anything you can see which is obviously wrong with how I am marshalling or if I am pinning wrong, or just not pinning where I should be please, or if you can see any other issues let me know.
The issue was with my output buffer. I wasn't assigning an empty array of size one to outputBufferDesc.Buffers, and the native code tried to write to memory that wasn't allocated for that purpose. I also couldn't marshal it as a byvalue array. Instead my struct looks like this:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct BufferDesc
{
public uint Version;
public uint Count;
public IntPtr Buffers;
}
And I pin an empty SecurityBuffer array of size 1 and give the address to Buffers.
I have this function in C++ and i want import it in c#
BS_RET_CODE BS_ReadImage( int handle, int imageType, unsigned char* bitmapImage, int* imageLen)
I've tried
[DllImport("BS_SDK.dll",CharSet = CharSet.Ansi, EntryPoint = "BS_ReadImage")]
public static extern int BS_ReadImage(int handle, int imageType, IntPtr bitmapImage, ref int imageLen);
IntPtr image = new IntPtr();
int len = 0;
BSSDK.BS_ReadImage(m_Handle, 0xff, image, ref len);
byte[] _imageTemp = new byte[len];
Marshal.Copy(image, _imageTemp, 0, len);
but i get an Access Violation Exception
Maybe you should check that len is infact equal to or greater than the size of image??
Where is the exception occurring? What line exactly?
This is the signature of the native c method:
bool nativeMethod1
(unsigned char *arrayIn,
unsigned int arrayInSize,
unsigned char *arrayOut,
unsigned int *arrayOutSize);
I have no idea why arrayOutSize is a pointer to unsigned int but not int itself.
This is how I invoke it from C#:
byte[] arrayIn= Encoding.UTF8.GetBytes(source);
uint arrayInSize = (uint)arrayIn.Length;
byte[] arrayOut = new byte[100];
uint[] arrayOutSize = new uint[1];
arrayOutSize[0] = (uint)arrayOut.Length;
fixed (byte* ptrIn = arrayIn, ptrOut = arrayOut)
{
if (nativeMethod1(ptrIn, arrayInSize, ptrOut, arrayOutSize))
{
Console.WriteLine("True");
}
else
{
Console.WriteLine("False");
}
}
and some DllImport code
[DllImport(#"IcaCert.dll", EntryPoint = "CreateCert2", CallingConvention = CallingConvention.Cdecl)]<br>
public unsafe static extern bool CreateCert2WithArrays(
byte* data, uint dataSize,<br>
byte* result, uint[] resultSize);
According to the documentation, native method should return arrayOut fulfilled with the values depending on arrayIn. If its size is less than needed, it returns false. True otherwise. I figured that it's needed 850 elements in arrayOut. So, when I create new byte[100] array, function should return false, but it always returns true. WHY?
You don't need unsafe code and fixed here. The standard P/Invoke marshaller is more than up to the task:
[DllImport(#"IcaCert.dll", EntryPoint = "CreateCert2", CallingConvention = CallingConvention.Cdecl)]
public static extern bool CreateCert2WithArrays(
byte[] arrayIn,
uint arrayInSize,
byte[] arrayOut,
ref uint arrayOutSize
);
byte[] arrayIn = Encoding.UTF8.GetBytes(source);
uint arrayInSize = (uint)arrayIn.Length;
uint arrayOutSize = 0;
CreateCert2WithArrays(arrayIn, arrayInSize, null, ref arrayOutSize);
byte[] arrayOut = new byte[arrayOutSize];
CreateCert2WithArrays(arrayIn, arrayInSize, arrayOut, ref arrayOutSize);
I don't know for sure what the protocol of the function is, but it is normal for such functions to be able to receive NULL if the output array has size 0.
I don't think an array is what you're looking for. It's a pointer to the size of the array, not a pointer to an array of sizes. Try this:
[DllImport(#"IcaCert.dll", EntryPoint = "CreateCert2", CallingConvention = CallingConvention.Cdecl)]
public unsafe static extern bool CreateCert2WithArrays(
byte* data, uint dataSize,
byte* result, ref uint resultSize);
byte[] arrayIn= Encoding.UTF8.GetBytes(source);
uint arrayInSize = (uint)arrayIn.Length;
byte[] arrayOut = new byte[100];
uint arrayOutSize = (uint)arrayOut.Length;
CreateCert2WithArrays (arrayIn, (uint) arrayIn.Length, arrayOut, ref arrayOutSize);
uint[] arrayOutSize = new uint[1];
arrayOut = new byte[(int)arrayOut];
CreateCert2WithArrays (arrayIn, (uint) arrayIn.Length, arrayOut, ref arrayOutSize);