将LPDWORD封送到C#以供P/Invoke使用

huangapple go评论72阅读模式
英文:

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似乎是最有希望的。我还尝试使用refIntPtr(几乎相同的代码),它给我返回了与单个间接引用类似的结果。
我会感激您对此的任何想法 - 我不习惯使用封送技术。

英文:

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 DWORDs. 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.

huangapple
  • 本文由 发表于 2023年6月15日 03:25:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/76476943.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定