编程语言
首页 > 编程语言> > 整理SAFEARRAY变量,这些变量是C#的BYTE

整理SAFEARRAY变量,这些变量是C#的BYTE

作者:互联网

我创建了一个SAFEARRAY,用于存储在C中为BYTE的变量.

当此结构编组到C#时,发生了一件奇怪的事情.

如果我在C#中将此结构的内容打印到WinForms ListBox中,例如:

byte data[]
TestSafeArray(out data);

lstOutput.Items.Clear();    
foreach (byte x in data)
{
    lstOutput.Items.Add(x); // Strange numbers
}

我得到一些似乎与原始数字无关的数字.此外,每次我运行C#客户端进行新测试时,都会得到一组不同的数字.

请注意,如果我使用Visual Studio调试器检查该数据数组的内容,则会得到正确的数字,如以下屏幕快照所示:

VS debugger shows the correct numbers

但是,如果将CopyTo编组的数据数组复制到一个新数组,则会得到正确的数字:

        byte[] data;
        TestSafeArray(out data);

        // Copy to a new byte array
        byte[] byteData = new byte[data.Length];
        data.CopyTo(byteData, 0);

        lstOutput.Items.Clear();
        foreach (byte x in byteData)
        {               
            lstOutput.Items.Add(x); // ** WORKS! **
        }

这是我用来构建SAFEARRAY的C repro代码(此函数从本地DLL导出):

extern "C" HRESULT __stdcall TestSafeArray(/* [out] */ SAFEARRAY** ppsa)
{
    HRESULT hr = S_OK;
    try 
    {
        const std::vector<BYTE> v{ 11, 22, 33, 44 };

        const int count = static_cast<int>(v.size());
        CComSafeArray<VARIANT> sa(count);

        for (int i = 0; i < count; i++)
        {
            CComVariant var(v[i]);

            hr = sa.SetAt(i, var);
            if (FAILED(hr))
            {
                return hr;
            }
        }

        *ppsa = sa.Detach();
    } 
    catch (const CAtlException& e)
    {
        hr = e;
    }

    return hr;
}

这是我使用的C#P / Invoke:

[DllImport("NativeDll.dll", PreserveSig = false)]
private static extern void TestSafeArray(
    [Out, MarshalAs(UnmanagedType.SafeArray, 
                    SafeArraySubType = VarEnum.VT_VARIANT)]
    out byte[] result);

请注意,如果在C中创建一个直接存储BYTE的SAFEARRAY(而不是SAFEARRAY(VARIANT)),那么我将立即在C#中获得正确的值,而无需中间的CopyTo操作.

解决方法:

[Out, MarshalAs(UnmanagedType.SafeArray, 
                SafeArraySubType = VarEnum.VT_VARIANT)]
out byte[] result);

你开玩笑了您告诉编组器,您想要一组变量,这些变量确实与C编译器产生的变量兼容.它将忠实地产生一个object [],object是VARIANT的标准封送处理.数组的元素是装箱的字节.

但这并没有愚弄调试器,它忽略了程序声明,而是查看了数组类型和发现的object [],这些在屏幕快照中很容易看到.因此,它可以正确访问装箱的字节.而且它没有欺骗Array.CopyTo(),它接受Array参数,因此被迫查看元素类型.并将装箱的字节正确转换为字节,它知道该怎么做.

但是C#编译器被愚弄了.它不知道它需要发出一个拆箱指令.实际不确定出什么问题,您可能正在获取对象地址的低字节.确实是相当随机的:)

在pinvoke声明中进行纤维化非常有用.如果数组包含实际对象(例如字符串,数组或接口指针),则在这种特殊情况下效果很好.警官没有尖叫血腥谋杀的可能原因.不在这里,拳击很糟糕.您必须修复声明,使用object [].

标签:byte,com-interop,safearray,c,c-4
来源: https://codeday.me/bug/20191118/2024756.html