加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

将SAFEARRAY从c返回到c#

发布时间:2020-12-16 09:58:23 所属栏目:百科 来源:网络整理
导读:我有一个创建,填充和返回SAFEARRAY的c方法: 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? 我有以下几点:
我有一个创建,填充和返回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?

我有以下几点:

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++: %dn",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,%dn",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派生接口,……)>除非有错误,否则不支持此代码.认为它是一个概念证明.出于好奇,我已经失去了不同的时间.你打破它,你修复它.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读