C# PInvoke、指针和数组副本

C# PInvoke、指针和数组副本,c#,.net,pointers,pinvoke,bytearray,C#,.net,Pointers,Pinvoke,Bytearray,我们正在c#、.NET4.0、Win7x64上构建一个应用程序,目标是x32 我们在应用程序中使用第三方库。我们理解这个库是用C++编写的。然而,为了让c#开发人员使用这个库,他们使用P/Invoke对它进行了包装,所以我们就是这样调用API函数的 其中一个API调用如下所示: ReadFromDevice(int deviceAddress, int numBytes, Byte[] data); 此函数从外部设备读取数字节的数据,并将其放入data[]中。如您所见,它希望看到一个C#字节数

我们正在c#、.NET4.0、Win7x64上构建一个应用程序,目标是x32

我们在应用程序中使用第三方库。我们理解这个库是用C++编写的。然而,为了让c#开发人员使用这个库,他们使用P/Invoke对它进行了包装,所以我们就是这样调用API函数的

其中一个API调用如下所示:

ReadFromDevice(int deviceAddress, int numBytes, Byte[] data);
此函数从外部设备读取数字节的数据,并将其放入data[]中。如您所见,它希望看到一个C#字节数组作为第三个参数。现在,我们的问题是,我们希望将数据读取到预声明数组中的任意位置。例如:

Byte[] myData = new Byte[1024*1024*16];
ReadFromDevice(0x100, 20000, &myData[350]) // Obviously not possible in C#
如果我们使用C/C++,这将是微不足道的。考虑到底层API是用C++编写的,我觉得我们应该能够在C语言中这样做,但是,我不能理解如何在C语言中这样做。也许我们可以不通过提供的P/Invoke接口调用底层库并编写自定义接口

任何想法都将不胜感激


关于,

您仍然能够在c#中执行指针运算

如果您重新声明DLL导入(反对C++库)使用一个ItPtR传递字节数组,则可以使用Add方法(实际上返回一个新的ItpTR)

来增加350。
这里有一个类似的例子:

您可以使用指针,但需要在选中不安全模式的情况下进行构建

Byte[] myData = new Byte[1024*1024*16];
fixed( Byte * pB = &myData[350])
{
   ReadFromDevice(0x100, 20000,pB  ) 
} 
但首先需要将导出的方法签名更改为

ReadFromDevice(int deviceAddress, int numBytes, Byte * data);
就这么简单:

ReadFromDevice(int deviceAddress, int numBytes, ref byte data);


这个问题的解决办法似乎是显而易见的

虽然确实可以简单地创建自己的包装器类和DllImport并编写自己的包装器类,但解决方案更简单

ReadFromDevice(int deviceAddress, int numBytes, Byte[] data); 
在post的上下文中,数据是一个输出参数。因此,解决方案是向它发送一个大小合适的空字节数组,然后将填充数组放入现有数组中

您所要做的就是确定返回数组的大小,然后填充和替换


我将直截了当地告诉您,只有在发送数组中特定元素的地址时,您使用现有数组的想法才可能有效(这当然是可能的)但是,你只限于C++库所期望的,它可能只是字节数组本身的地址。

而这里的其他答案都是相近的,没有一个是完全的。 首先,您只需要声明自己的p/invoke声明。这是关于p/invoke的美妙之处;从来没有一种方法可以做到这一点

[DllImport("whatever.dll")]
unsafe extern static void ReadFromDevice(int deviceAddress, int numBytes, byte* data);
现在你可以用

unsafe static void ReadFromDevice(int deviceAddress, byte[] data, int offset, int numBytes)
{
    fixed (byte* p = data)
    {
        ReadFromDevice(deviceAddress, numBytes, p + offset);
    }
}

没有从
byte*
byte[]
的转换,此代码甚至不会编译。如果他控制C源,那是一回事,但听起来不像他原来的问题。那就是说,在再次阅读问题之后,听起来好像OP也可以访问C++库。因此,您应该简单地建议他添加自己的DllImport,并将签名更改为接受
字节*
而不是
字节[]
,这样就行了。“我们正在构建一个C 4,NET第三的应用程序,在Wi7x64上,目标是X32。我们在我们的应用程序中使用了一个第三方库。我们理解这个库是用C++编写的。但是,为了让C开发者使用这个库,他们用p/Unjk把它包起来,这就是我们调用API函数的方式。”所以他们控制C语言,正在为C++库编写包装器!但是您更改了ReadFromDevice()的声明。我没有那个密码;它来自第三方库。如果它是.net,则始终具有代码xD。这看起来像一个DLLIMPART,打开反射镜并将2行复制到应用程序中。第三方库提供了C++编译文件(显然不能更改)和C.*包装器类。请看我的答案,找到一个明显的解决办法。我不明白。这是反射器输出:
public int ReadFromDevice(int deviceAddress,int numBytes,byte[]data){return libdeviceinvoke.ReadFromDevice(this.handle,deviceAddress,numBytes,data);}
反编译libDeviceInvoke.ReadFromDevice这一点是为了避免这些复制操作,因为它们成本高昂(就时间而言,此应用程序处理的数据超过30MB/秒)。如果我们按照您描述的方式执行,那么,对于从外部设备读取的每个数据,第三方API都会将数据复制到小数组(ReadFromDevice调用中的第三个参数),然后,我们必须执行另一个复制操作,以将小数组中的数据写入大数组。因此,对于从外部设备读取的每30 MB数据,将有60 MB的数据移动。出于性能原因,我们希望只写入一次数据。
unsafe static void ReadFromDevice(int deviceAddress, byte[] data, int offset, int numBytes)
{
    fixed (byte* p = data)
    {
        ReadFromDevice(deviceAddress, numBytes, p + offset);
    }
}