SAFEARRAY* TestClass::GetResult(long& size) { return GetSafeArrayList(size);}How should I export that function in a DLL so that c# Could take itHow should I write c# method signature?
我有以下几点:
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,&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,&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/613130和https://stackoverflow.com/a/3735438/613130
SAFEARRAY(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,&lowerBound); hr = ::SafeArrayGetUBound(psa,&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,numElements,prinfo); ManagedUDT *pVals; hr = ::SafeArrayAccessData(psa,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",CharSet = CharSet.Ansi)]static extern voID GetResultSafeArrayOut([MarshalAs(UnmanagedType.SafeArray)] out ManagedUDT[] array,IntPtr itypeinfo);[Dllimport("Nativelibrary.dll",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},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},arr2[i].m_int01); }}{ Console.Writeline(); var arr2 = (ManagedUDT[])arr.Clone(); IntPtr itypeinfo = Marshal.GetITypeInfoForType(typeof(ManagedUDT)); GetResultSafeArrayRef(ref arr2,arr2[i].m_int01); }}
GetResultSafeArray有一个很大的警告:你必须从C#传递至少一个空数组(比如新的ManagedUDT [0]).这是因为要在C中从空创建SAFEARRAY(ManagedUDT),您需要一个IRecordInfo对象.我不知道如何从C中检索它.如果你已经有了SAFEARRAY(ManagedUDT),那么显然它已经设置了IRecordInfo,所以没有问题.在给出的示例中,在C中首先进行一些健全性检查,然后打印传递的数组,然后将其清空,然后重新填充. GetResultSafeArrayOut / GetResultSafeArrayRef“作弊”:他们从C#接收一个ITypeInfo指针(很容易在C#中检索,使用Marshal.GetITypeInfoForType()),并且从Caht可以检索到IRecordInfo接口.
一些说明:
>我写了Ansi-charset-C.通常对我自己来说,我总是编写Unicode-ready C(或者直接使用Unicode-C,因为所有的windows NT都支持Unicode),但是我注意到我是一个例外…所以在代码的各个部分都有转换BSTR-> ANSI-> BSTR.>我正在检索所有函数调用的HRESulT.应该检查它们,并处理失败.> C/C++OM中最复杂的事情是知道何时释放某些东西……一般来说,总是免费/释放()一切! (无论是BSTR / IUnkNown派生接口,……)>除非有错误,否则不支持此代码.认为它是一个概念证明.出于好奇,我已经失去了不同的时间.你打破它,你修复它.
总结以上是内存溢出为你收集整理的将SAFEARRAY从c返回到c#全部内容,希望文章能够帮你解决将SAFEARRAY从c返回到c#所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)