整理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调试器检查该数据数组的内容,则会得到正确的数字,如以下屏幕快照所示:
但是,如果将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