I have a C++ function (WinAPI) with the following signature:
__declspec(dllimport) LPDWORD WINAPI MyFunction(HDET hDet, WORD wStartChan, WORD wNumChans,
LPDWORD lpdwBuffer, LPWORD lpwRetChans, LPDWORD lpdwDataMask,
LPDWORD lpdwROIMask, LPCSTR lpszAuth);
I have one example of it being called in C++, which is as follows:
::MyFunction((HDET)pUConn->GetHDET(), (WORD)lStartChan, (WORD)lNumChans,
&(m_DataCache[lView].pdwChans[lStartChan]), &wRetChans,
&(m_DataCache[lView].dwDataMask), &(m_DataCache[lView].dwROIMask),
pUConn->GetAuthorization());
//m_DataCache[lView].pdwChans is an array of DWORDs. (Declared as DWORD * pdwChans;)
Now, I can marshal all of the parameters except the 4th. It's a pointer to an array of DWORD
s. That makes me think int **
. However, when I pass in a ref int[]
I get back null
. Here's my C# declaration:
[System.Runtime.InteropServices.DllImport("MCBCIO32.dll", EntryPoint = "MIOGetData")]
public static extern IntPtr MIOGetData(int hDet, ushort sChan, ushort nChan,
ref int[] Buffer, ref short rchan, ref int datamask, ref int roimask, string auth);
I've also tried IntPtr
and a simple int[]
. Int[]
returns an array of 0s. IntPtr
is interesting, (interesting but wrong) - it gives me back weird values, non-zero but clearly incorrect. And they change every time I do another run, so I think I'm wandering around in random memory. I marshal memory for the buffer and copy it over as follows:
/***Snip***/
int arrsize = 100;
int[] buffer = new int[arrsize];
short rchan = -1;
IntPtr p = Marshal.AllocHGlobal(arrsize * sizeof(int));
int datamask = 0, roimask = 0;
MCBWrapper.FUNC(hDet, 0, 1, p, ref rchan, ref datamask, ref roimask, "");
Marshal.Copy(p, buffer, 0, arrsize);
Marshal.FreeHGlobal(p);
p = IntPtr.Zero;
for (int r = 0; r < arrsize; r++)
{
Console.Write($"{buffer[r]} ");
}
In this case, the buffer is marshaled as IntPtr
.
I have tried a few other ways of marshaling but to little effect. IntPtr
seems the most promising. I've also tried using a ref
to an IntPtr
(with nearly identical code), and it gives me similar results to a single indirection.
I'd appreciate any thoughts on this - I'm not used to working with marshaling.
It's not a **
pointer-to-pointer. It's just a pointer. So you need to pass in a buffer. So it's int[]
not ref int[]
.
The buffer size looks like it's being passed in as nChan
, and the used length of the buffer is passed back in rchan
. You specify that using SizeParamIndex
.
You also need to specify LPStr
on the final string parameter.
The usage of the other two ref
parameters are unclear, they should probably be either [In] in
or [Out] out
. Also HDET
is unclear what size it is.
[DllImport("MCBCIO32.dll")]
public static extern IntPtr MIOGetData(
int hDet,
ushort sChan,
ushort nChan,
[MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 4)] int[] Buffer,
[Out] out short rchan,
ref int datamask,
ref int roimask,
[MarshalAs(UnmanagedType.LPStr)] string auth);
You use it like this
int arrsize = 100;
int[] buffer = new int[arrsize];
int datamask = 0, roimask = 0;
var result = MCBWrapper.FUNC(hDet, 0, 1, buffer, out var rchan, ref datamask, ref roimask, "");
for (int r = 0; r < arrsize; r++)
{
Console.Write($"{buffer[r]} ");
}
Note that if you did need to use Marshal.AllocHGlobal
, you must make sure to free the memory in a finally
to prevent leaks.