英文:
Marshalling LPDWORD into C# for P/Invoke
问题
Sure, here's the translated part of your text:
我有一个具有以下签名的C++函数(WinAPI):
__declspec(dllimport) LPDWORD WINAPI MyFunction(HDET hDet, WORD wStartChan, WORD wNumChans,
LPDWORD lpdwBuffer, LPWORD lpwRetChans, LPDWORD lpdwDataMask,
LPDWORD lpdwROIMask, LPCSTR lpszAuth);
我有一个C++中调用它的示例,如下所示:
::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是一个DWORD数组。(声明为DWORD * pdwChans;)
现在,我可以传递除第四个参数之外的所有参数。它是指向`DWORD`数组的指针。这使我认为是`int **`。然而,当我传递`ref int[]`时,我得到`null`。这是我的C#声明:
[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);
我还尝试过`IntPtr`和简单的`int[]`。 `Int[]`返回一个全是0的数组。 `IntPtr`很有趣(有趣但是错误的)- 它给我返回了奇怪的值,非零但明显不正确。而且每次我运行时都会改变,所以我认为我在随机内存中徘徊。我为缓冲区分配内存并将其复制如下:
/***删除部分***/
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]} ");
}
在这种情况下,缓冲区作为IntPtr
进行了封送。
我尝试了一些其他封送方式,但效果不大。IntPtr
似乎是最有希望的。我还尝试使用ref
到IntPtr
(几乎相同的代码),它给我返回了与单个间接引用类似的结果。
我会感激您对此的任何想法 - 我不习惯使用封送技术。
英文:
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.
答案1
得分: 1
这不是一个 **
指向指针。这只是一个指针。所以你需要传递一个缓冲区。因此,它是 int[]
而不是 ref int[]
。
缓冲区大小似乎是作为 nChan
传递的,而缓冲区的使用长度则通过 rchan
返回。你可以使用 SizeParamIndex
来指定这一点。
你还需要在最后一个字符串参数上指定 LPStr
。
另外两个 ref
参数的用法不清楚,它们可能应该是 [In] in
或 [Out] out
中的一种。此外,HDET
的大小也不清楚。
[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);
你可以像这样使用它:
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, "");
请注意,如果你确实需要使用 Marshal.AllocHGlobal
,你必须确保在 finally
中释放内存,以防止内存泄漏。
英文:
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论