Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 是否需要将数组参数固定到.NET中的COM RCWs中?_C#_.net_Com_Com Interop - Fatal编程技术网

C# 是否需要将数组参数固定到.NET中的COM RCWs中?

C# 是否需要将数组参数固定到.NET中的COM RCWs中?,c#,.net,com,com-interop,C#,.net,Com,Com Interop,我正在调用成员函数,该函数具有以下自动生成的.NET签名: void get_checksum(uint cbData, out uint pcbData, byte[] pbData); unsafe void get_checksum(uint cbData, out uint pcbData, byte *pbData); 请注意,参数pbData是一个out参数。当有人调用get\u checksum时,我是否需要担心固定传入这里的数组 (背景: 我继承了如下代码: unsafe {

我正在调用成员函数,该函数具有以下自动生成的.NET签名:

void get_checksum(uint cbData, out uint pcbData, byte[] pbData);
unsafe void get_checksum(uint cbData, out uint pcbData, byte *pbData);
请注意,参数
pbData
是一个out参数。当有人调用
get\u checksum
时,我是否需要担心固定传入这里的数组

(背景:

我继承了如下代码:

unsafe
{
    fixed (byte* p = scratch_hash)
    {
        sourceFile.get_checksum(c, out c, scratch_hash);
    }
}
void get_checksum(
    uint cbData, 
    out uint pcbData, 
    IntPtr data);
哪些pin
scratch\u hash
p
,但从未实际使用过
p
。我以前从未见过类似的情况,考虑到周围代码的状态,我怀疑pin在这里是完全不必要的


)

不,在这种情况下你不必这么做——甚至你也不能

但是,如果遇到性能问题,您可能需要更改该签名,因为封送拆收器在将字节数组传递给本机方法时需要做大量工作—它必须分配新内存,将托管字节数组复制到该内存,然后再次释放内存

这意味着您发布的代码完全是胡说八道,因为虽然它确实修复了
scratch\u hash
数组,但它无论如何都不使用该固定指针

如果您只是偶尔调用该方法一次,而字节数组相对较小,则可以安全地忽略此问题。但是,如果您确实发现这会对应用程序造成不必要的压力,修复可能会有所帮助,但您必须更改签名:

void get_checksum(uint cbData, out uint pcbData, byte[] pbData);
unsafe void get_checksum(uint cbData, out uint pcbData, byte *pbData);
然后你可以使用这个:

fixed (byte *p = &scratch_hash[0])
  sourceFile.get_checksum(c, out c, p);
综上所述,请注意,修复托管对象可能会导致其自身的问题(例如,如果您的对象位于堆的顶部,它几乎会扼杀堆压缩的任何机会)。修复工作应该少做,而且只能在极短的时间内完成——唯一其他合理的选择是尽早分配对象,并将它们保持在一起(例如,当使用异步I/O时,您需要一个固定的引用,通常只要应用程序运行就可以了——因此,只要尽早分配缓冲区,就可以了)


另外,我仍然发现该签名有点疯狂。您可以将头文件中签名的定义与当前定义共享吗?

void get_checksum(uint cbData, out uint pcbData, byte[] pbData);
您不需要pin
pbData
。但是,您需要为返回的数据预先分配一个数组,但您事先不知道大小。如果通过
cbData
传递的数组大小不够大,并且您当前的方法签名不允许查找缓冲区大小,则该方法将失败

该法确实允许:

HRESULT get_checksum ( 
   DWORD  cbData,
   DWORD* pcbData,
   BYTE   data[]
);
data[in,out]用校验和字节填充的缓冲区。若 此参数为NULL,则pcbData返回字节数 必需的

因此,一种更有效的方法可能是这样声明和使用它:

unsafe
{
    fixed (byte* p = scratch_hash)
    {
        sourceFile.get_checksum(c, out c, scratch_hash);
    }
}
void get_checksum(
    uint cbData, 
    out uint pcbData, 
    IntPtr data);

如果您不想(或不能)使用
不安全的
代码,这里有一个替代方案:

// get size
uint size;
obj.get_checksum(0, out size, IntPtr.Zero);

// get the data
byte[] buff;
var p = Marshal.AllocHGlobal((int)size);
try
{
    uint cbData;
    obj.get_checksum(size, out cbData, p);
    if (size < cbData)
        throw new InvalidOperationException("cbData");
    buff = new byte[cbData];
    Marshal.Copy(p, buff, 0, (int)cbData);
}
finally
{
    Marshal.FreeHGlobal(p);
}
//获取大小
单位尺寸;
对象获取校验和(0,out size,IntPtr.Zero);
//获取数据
字节[]buff;
var p=Marshal.AllocHGlobal((int)size);
尝试
{
uint-cbData;
对象获取校验和(大小、输出cbData、p);
如果(大小
这是每个人都喜欢的封送方法。但事实并非如此,一个字节*与一个字节[]不一样。C数组太过破碎,无法允许这种情况发生,从而导致10亿个错误和缓冲区溢出攻击。只有安全数组封送到字节[]。哦,这是COM互操作。所以你甚至不能更改签名,是吗?你是使用
tlbimp
创建包装库,还是在C++/CLI中使用自己的包装库?据我所知
tlbimp
不允许你传递可变长度数组(你必须手动编辑IL才能使用该功能),所以我不得不问-这段代码真的有效吗?@Hans:我不明白你的意思。这就是我的签名。@Luaan:似乎是的。你没有这个签名,当编译器需要字节[]时,你不能传递字节*。这只在C中有效,C编译器不允许。COM互操作的工作方式也不允许。试着自己编写这样的代码就可以看到,它总是会在CS1503上消失。我不能只为
byte[]
int size;get\u校验和(0,out size,null);byte[]result=new byte[size];get\u校验和传递null(size,out size,result);返回result;
@BillyONeal,如果您使用
[MarshalAs(UnmanagedType.LPArray)]byte[]pbData
签名,您可能可以这样做。尝试并发布您自己的答案。在任何情况下,您都不需要锁定它,除非您将其作为
IntPtrHRESULT get\u校验和传递给COM(DWORD cbData、DWORD*pcbData、字节*data)