gpt4 book ai didi

c# - 将 SAFEARRAY 从 C++ 返回到 C#

转载 作者:太空狗 更新时间:2023-10-29 20:11:49 28 4
gpt4 key购买 nike

我有一个创建、填充和返回 SAFEARRAY 的 C++ 方法:

SAFEARRAY* TestClass::GetResult(long& size) 
{
return GetSafeArrayList(size);
}

How should I export that function in a DLL so that c# could take it
How should I write c# method signature?

我在 C++ 中有一些类似的东西:

extern "C" __declspec(dllexport) void GetResult(SAFEARRAY*& data, long& size)
{
size = 0;
data = handle->GetResult(size);
}

它是正确的,不是吗?

感谢您的帮助!

编辑:

C#调用:

public static extern void GetResult(IntPtr handle, [MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_USERDEFINED)] TestStruct[] data, ref int size);

最佳答案

使用 SAFEARRAY(int) C#->C++->C# 的完整示例(因此数组在 C# 中用一些数据初始化,传递给 C++,在那里修改并返回给 C#) .

C++:

// For the various _t classes for handling BSTR and IUnknown
#include <comdef.h>

struct ManagedUDT
{
BSTR m_str01;
int m_int01;

~ManagedUDT()
{
::SysFreeString(m_str01);
m_str01 = NULL;
}
};

extern "C" __declspec(dllexport) void GetResult(SAFEARRAY*& data)
{
if (data != NULL)
{
// Begin print content of SAFEARRAY
VARTYPE vt;
HRESULT hr = SafeArrayGetVartype(data, &vt);

if (SUCCEEDED(hr))
{
// To make this code simple, we print only
// SAFEARRAY(VT_I4)
if (vt == VT_I4)
{
int *pVals;
hr = SafeArrayAccessData(data, (void**)&pVals); // direct access to SA memory

if (SUCCEEDED(hr))
{
long lowerBound, upperBound; // get array bounds
SafeArrayGetLBound(data, 1, &lowerBound);
SafeArrayGetUBound(data, 1, &upperBound);

long cnt_elements = upperBound - lowerBound + 1;

for (int i = 0; i < cnt_elements; i++) // iterate through returned values
{
int val = pVals[i];
printf("C++: %d\n", val);
}

SafeArrayUnaccessData(data);
}
else
{
// Error
}
}
}
else
{
// Error
}
// End print content of SAFEARRAY

// Delete the SAFEARRAY if already present
SafeArrayDestroy(data);
data = NULL;
}

{
// Creation of a new SAFEARRAY
SAFEARRAYBOUND bounds;
bounds.lLbound = 0;
bounds.cElements = 10;

data = SafeArrayCreate(VT_I4, 1, &bounds);
int *pVals;

HRESULT hr = SafeArrayAccessData(data, (void**)&pVals); // direct access to SA memory

if (SUCCEEDED(hr))
{
for (ULONG i = 0; i < bounds.cElements; i++)
{
pVals[i] = i + 100;
}
}
else
{
// Error
}
}
}

C#

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl)]
private static extern void GetResult([MarshalAs(UnmanagedType.SafeArray, SafeArraySubType = VarEnum.VT_I4)] ref int[] ar);

var data = new int[] { 1, 2, 3, 4, 5 };
GetResult(ref data);

if (data != null)
{
for (int i = 0; i < data.Length; i++)
{
Console.WriteLine("C#: {0}", data[i]);
}
}
else
{
Console.WriteLine("C#: data is null");
}

部分代码取自 https://stackoverflow.com/a/12484259/613130https://stackoverflow.com/a/3735438/613130


安全阵列(VT_RECORD)

这是可行的……非常困难……但可行。请不要这样做。你不能恨这个世界去做这件事。我希望你不要!

C++:

// For the _com_util
#include <comdef.h>

extern "C"
{
__declspec(dllexport) void GetResultSafeArray(SAFEARRAY *&psa)
{
// All the various hr results should be checked!
HRESULT hr;

// Begin sanity checks
if (psa == NULL)
{
// Error
}

VARTYPE pvt;
hr = ::SafeArrayGetVartype(psa, &pvt);

if (pvt != VT_RECORD)
{
// Error
}

UINT size;
size = ::SafeArrayGetElemsize(psa);

if (size != sizeof(ManagedUDT))
{
// Error
}

// From tests done, it seems SafeArrayGetRecordInfo does a AddRef
_com_ptr_t<_com_IIID<IRecordInfo, NULL> > prinfo;
// The_com_ptr_t<>::operator& is overloaded
hr = ::SafeArrayGetRecordInfo(psa, &prinfo);

// From tests done, it seems GetName returns a new instance of the
// BSTR
// It is ok to use _bstr_t.GetAddress() here, see its description
_bstr_t name1;
hr = prinfo->GetName(name1.GetAddress());

const _bstr_t name2 = _bstr_t(L"ManagedUDT");

if (name1 != name2)
{
// Error
}

// End sanity checks

long lowerBound, upperBound; // get array bounds
hr = ::SafeArrayGetLBound(psa, 1, &lowerBound);
hr = ::SafeArrayGetUBound(psa, 1, &upperBound);
long cnt_elements = upperBound - lowerBound + 1;

// Begin print
ManagedUDT *pVals;
hr = ::SafeArrayAccessData(psa, (void**)&pVals);

printf("C++:\n");

for (int i = 0; i < cnt_elements; ++i)
{
ManagedUDT *pVal = pVals + i;

// If you are using a recent VisualC++, you can
// #include <memory>, and then
//std::unique_ptr<char[]> pstr(_com_util::ConvertBSTRToString(pVal->m_str01));
// and you don't need the char *pstr line and the delete[]
// line
char *pstr = _com_util::ConvertBSTRToString(pVal->m_str01);
printf("%s, %d\n", pstr, pVal->m_int01);
delete[] pstr;
}

hr = ::SafeArrayUnaccessData(psa);
// End print

// Begin free
SAFEARRAYBOUND sab;
sab.lLbound = 0;
sab.cElements = 0;

// SafeArrayRedim will call IRecordInfo::RecordClear
hr = ::SafeArrayRedim(psa, &sab);
// End Free

// Begin create
int numElements = 10;
sab.cElements = numElements;
hr = ::SafeArrayRedim(psa, &sab);

hr = ::SafeArrayAccessData(psa, (void**)&pVals);

for (int i = 0; i < numElements; i++)
{
ManagedUDT *pVal = pVals + i;

char pstr[100];
sprintf(pstr, "Element #%d", i);
pVal->m_str01 = _com_util::ConvertStringToBSTR(pstr);

pVal->m_int01 = 100 + i;
}

hr = ::SafeArrayUnaccessData(psa);
// End create
}

__declspec(dllexport) void GetResultSafeArrayOut(SAFEARRAY *&psa, ITypeInfo *itypeinfo)
{
// All the various hr results should be checked!
HRESULT hr;

// Begin sanity checks
if (psa != NULL)
{
// Begin free
// SafeArrayDestroy will call IRecordInfo::RecordClear
// if necessary
hr = ::SafeArrayDestroy(psa);
// End Free
}

// Begin create
int numElements = 10;

SAFEARRAYBOUND sab;
sab.lLbound = 0;
sab.cElements = numElements;

// The_com_ptr_t<>::operator& is overloaded
_com_ptr_t<_com_IIID<IRecordInfo, NULL> > prinfo;
hr = ::GetRecordInfoFromTypeInfo(itypeinfo, &prinfo);

psa = ::SafeArrayCreateVectorEx(VT_RECORD, 0, numElements, prinfo);

ManagedUDT *pVals;
hr = ::SafeArrayAccessData(psa, (void**)&pVals);

for (int i = 0; i < numElements; i++)
{
ManagedUDT *pVal = pVals + i;

char pstr[100];
sprintf(pstr, "Element #%d", i);
pVal->m_str01 = _com_util::ConvertStringToBSTR(pstr);

pVal->m_int01 = 100 + i;
}

hr = ::SafeArrayUnaccessData(psa);
// End create
}
}

C#:

[ComVisible(true)]
[Guid("BBFE1092-A90C-4b6d-B279-CBA28B9EDDFA")]
[StructLayout(LayoutKind.Sequential)]
public struct ManagedUDT
{
[MarshalAs(UnmanagedType.BStr)]
public string m_str01;
public Int32 m_int01;
}

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void GetResultSafeArray([MarshalAs(UnmanagedType.SafeArray)] ref ManagedUDT[] array);

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi)]
static extern void GetResultSafeArrayOut([MarshalAs(UnmanagedType.SafeArray)] out ManagedUDT[] array, IntPtr itypeinfo);

[DllImport("NativeLibrary.dll", CallingConvention = CallingConvention.Cdecl, CharSet = CharSet.Ansi, EntryPoint = "GetResultSafeArrayOut")]
static extern void GetResultSafeArrayRef([MarshalAs(UnmanagedType.SafeArray)] ref ManagedUDT[] array, IntPtr itypeinfo);

var arr = new[]
{
new ManagedUDT { m_str01 = "Foo", m_int01 = 1},
new ManagedUDT { m_str01 = "Bar", m_int01 = 2},
};

{
Console.WriteLine("C#:");

for (int i = 0; i < arr.Length; i++)
{
Console.WriteLine("{0}, {1}", arr[i].m_str01, arr[i].m_int01);
}
}

{
Console.WriteLine();

var arr2 = (ManagedUDT[])arr.Clone();

GetResultSafeArray(ref arr2);

Console.WriteLine();

Console.WriteLine("C#:");

for (int i = 0; i < arr2.Length; i++)
{
Console.WriteLine("{0}, {1}", arr2[i].m_str01, arr2[i].m_int01);
}
}

{
Console.WriteLine();

ManagedUDT[] arr2;

IntPtr itypeinfo = Marshal.GetITypeInfoForType(typeof(ManagedUDT));
GetResultSafeArrayOut(out arr2, itypeinfo);

Console.WriteLine();

Console.WriteLine("C#:");

for (int i = 0; i < arr2.Length; i++)
{
Console.WriteLine("{0}, {1}", arr2[i].m_str01, arr2[i].m_int01);
}
}

{
Console.WriteLine();

var arr2 = (ManagedUDT[])arr.Clone();

IntPtr itypeinfo = Marshal.GetITypeInfoForType(typeof(ManagedUDT));
GetResultSafeArrayRef(ref arr2, itypeinfo);

Console.WriteLine();

Console.WriteLine("C#:");

for (int i = 0; i < arr2.Length; i++)
{
Console.WriteLine("{0}, {1}", arr2[i].m_str01, arr2[i].m_int01);
}
}

GetResultSafeArray 有一个重要警告:您必须从 C# 传递至少一个空数组(如 new ManagedUDT[0])。这是因为要在 C++ 中从无到有地创建一个 SAFEARRAY(ManagedUDT),您需要一个 IRecordInfo 对象。我不知道如何从 C++ 中检索它。如果您已经有了 SAFEARRAY(ManagedUDT),那么显然它已经设置了 IRecordInfo,所以没有问题。在给出的示例中,在 C++ 中首先进行一些健全性检查,然后打印传递的数组,然后将其清空,然后重新填充。 GetResultSafeArrayOut/GetResultSafeArrayRef“欺骗”:它们从 C# 接收一个 ITypeInfo 指针(在 C# 中很容易检索,使用 Marshal .GetITypeInfoForType()),C++ 可以从中获取 IRecordInfo 接口(interface)。

一些注意事项:

  • 我编写了 Ansi-charset-C++。通常对于我自己,我总是编写 Unicode-ready C++(或直接 Unicode-C++,因为所有 Windows NT 都支持 Unicode),但我注意到我是一个异常(exception)......所以在代码的各个部分都有转换BSTR->Ansi->BSTR.

  • 我正在检索所有函数调用的 HRESULT。应该检查它们,并处理故障。

  • C++/COM 中最复杂的事情是知道什么时候释放某些东西...通常总是释放/Release() 一切!(不管是BSTR/IUnknown 派生接口(interface),...)

  • 除非有错误,否则不支持此代码。将其视为概念证明。出于好奇,我已经在上面浪费了好几个小时。你打破它,你修复它。

关于c# - 将 SAFEARRAY 从 C++ 返回到 C#,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31011617/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com