I create a SAFEARRAY
storing VARIANT
s that are BYTE
s in C++.
When this structure is marshaled to C#, a weird thing happens.
If I print the content of this structure in C# to a WinForms ListBox
, e.g.:
byte data[]
TestSafeArray(out data);
lstOutput.Items.Clear();
foreach (byte x in data)
{
lstOutput.Items.Add(x); // Strange numbers
}
I get some numbers that seem unrelated to the original ones. Moreover, each time I run the C# client for a new test, I get a different set of numbers.
Note that if I inspect the content of that data
array with the Visual Studio debugger, I get the correct numbers, as the following screenshot shows:
However, if I CopyTo
the marshaled data
array to a new one, I get the correct numbers:
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! **
}
This is the C++ repro code I use to build the SAFEARRAY
(this function is exported from a native 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;
}
And this is the C# P/Invoke I used:
[DllImport("NativeDll.dll", PreserveSig = false)]
private static extern void TestSafeArray(
[Out, MarshalAs(UnmanagedType.SafeArray,
SafeArraySubType = VarEnum.VT_VARIANT)]
out byte[] result);
Note that if in C++ I create a SAFEARRAY
storing BYTE
s directly (instead of a SAFEARRAY(VARIANT)
), I get the correct values immediately in C#, without the intermediate CopyTo
operation.