Reputation: 11
Could you guys please help me solve the following issue? I have a C++ function dll, and it will be called by another C# application. One of the functions I needed is as follow:
unsigned long makeArray(unsigned char* sendArr, unsigned long sendArrLen, unsigned char *recvArr, unsigned long *recvArrLen);
I wrote the following code in C#:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, byte[] recvArr, ulong recvArrLen);
private byte[] MakeArray()
{
byte[] arrSend = new byte[] { 0x00, 0x12, 0x34 };
ulong nRecvArrLen = 0;
byte[] arrRecv = null; // assign in c++ dll function (variable size)
if(makeArray(arrSend, (ulong)arrSend.Length, arrRecv, nRecvArrLen) == 1)
{
return arrRecv;
}
return null;
}
Unfortunately, the above code is not working... May I know how can I pass a pointer-to-pointer to the C++ func? If it is not possible, is there any workaround?
Thank you.
Upvotes: 1
Views: 1880
Reputation: 2551
Where is your 'test.dll'? I think it is a path problem...
The file must be located at the one of following directories..
[%SystemRoot%] (Windows directory)
[%SystemRoot%]\system32\(32 bit) or
[%SystemRoot%]\sysWOW64\(64 bit)
The same location with your executable file
PATH variable
Or it can be a type mismatch ... refer to the [site].
I matched the ulong type of csharp to unsigned __int64 in c/c++ on windows.
Declaration of the C# code is a little bit changed.
[DllImport(@"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern ulong makeArray
(
byte[] sendArr,
ulong sendArrLen,
[Out] byte[] recvArr,
ref ulong recvArrLen
);
Here are the testdll.cpp abd testdll.h i tested
#include "testdll.h"
unsigned __int64 makeArray(
unsigned char* sendArr,
unsigned __int64 sendArrLen,
unsigned char *recvArr,
unsigned __int64 *recvArrLen
)
{
int i;
for(i=0; i < sendArrLen; i++)
{
recvArr[i] = sendArr[i];
}
memcpy(recvArrLen, &sendArrLen, sizeof(unsigned __int64));
return i;
}
testdll.h code.
#pragma once
#ifdef EXPORT_TESTDLL
#define TESTDLL_API __declspec(dllexport)
#else
#define TESTDLL_API __declspec(dllimport)
#endif
extern "C" TESTDLL_API unsigned __int64 makeArray(
unsigned char* sendArr,
unsigned __int64 sendArrLen,
unsigned char *recvArr,
unsigned __int64 *recvArrLen
);
Finally, C# code of console application as follows, call the native dll function in c++ - testdll.dll print items on the console.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;
namespace ConsoleApplication2
{
class Program
{
[DllImport(@"testdll.dll", CallingConvention = CallingConvention.Cdecl)]
static extern ulong makeArray(byte[] sendArr, ulong sendArrLen, [Out] byte[] recvArr, ref ulong recvArrLen);
static byte[] MakeArray()
{
byte[] arrSend = new byte[] { 0x00, 0x12, 0x34 };
ulong nRecvArrLen = 0;
ulong ret = 0;
byte[] arrRecv = new byte[3]; // assign in c++ dll function (variable size)
try
{
if ((ret = makeArray(arrSend, (ulong)arrSend.Length, arrRecv, ref nRecvArrLen)) > 0)
{
if(arrRecv != null)
Console.WriteLine("nRecvArrLen2============>" + arrRecv.Length);
return arrRecv;
}
}
catch (DllNotFoundException dne)
{
Console.WriteLine("============> dll not found....");
}
return null;
}
static void Main(string[] args)
{
byte[] retbytes = MakeArray();
if (retbytes != null)
{
Console.WriteLine("=====LEN=======>" + retbytes.Length);
for (int i = 0; i < retbytes.Length; i++)
Console.WriteLine("====ITEM========>" + retbytes[i]);
}
else
Console.WriteLine("=====NULL=======>");
}
}
}
Upvotes: 1
Reputation: 42924
unsigned long
in MSVC is a 32-bit unsigned integer, so you should map it to the System.UInt32
.NET type, corresponding to the uint
keyword in C#.
C# ulong
is an unsigned 64-bit integer, corresponding to MSVC's unsigned __int64
or unsigned long long
.
The unsigned long *recvArrLen
parameter should me mapped using ref
in the C# PInvoke declaration, as you have a level of indirection via pointer.
It also seems that the arrRecv
array parameter should be allocated by the caller (C# in your case), and filled by the DLL function.
If the DLL function allocates the buffer, you should add another level of indirection (unsigned char **recvArr
), and you should provide a way to release the allocated memory (e.g. the DLL should export a release function as well).
I would try with something like this for PInvoke:
[DllImport("test.dll", CallingConvention = CallingConvention.Cdecl)]
public static extern uint makeArray(
byte[] sendArr,
uint sendArrLen,
[Out] byte[] recvArr,
ref uint recvArrLen
);
Upvotes: 2