通过使用COM调用包装从C#到C结构的数组++ [英] passing an array of structs from c# to C++ using com callable wrapper

查看:233
本文介绍了通过使用COM调用包装从C#到C结构的数组++的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑下面的代码,就是要利用C使用COM

Consider the code below that is meant to be accessed by C++ using com

    namespace MarshalLib
    {
        //define an interface for account services
        [ComVisible(true)]
        [Guid("39B8A693-79BB-4638-92DE-245A88720953")]
        public interface IAccountStructLookup
        {
            AccountStruct RetrieveAccount(int acctId);
            void UpdateBalance(ref AccountStruct account);
            Alias[] GetRef();
        }

        //Implement an account struct
        [ComVisible(true)]
        [Guid("DB48C5B6-9646-491A-B030-C0CADCFC03E0")]
        public struct AccountStruct
        {
            public int AccountId;
            [MarshalAs(UnmanagedType.BStr)]
            public string AccountName;
            [MarshalAs(UnmanagedType.Currency)]
            public decimal Balance;

            //[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
            //[MarshalAs(UnmanagedType.SafeArray)]
            //public Alias[] Aliases;
        }

        [ComVisible(true)]
        [Guid("9829CAB3-4020-47EA-BE72-86EC7CFFAE1D")]
        public struct Alias
        {
            public string Name;
        }
        //implement a class to provide account services
        //using an AccountStruct
        [ComVisible(true)]
        [Guid("CEFE5CAA-5C7E-464F-8020-E0FC78180D9B")]
        [ClassInterface(ClassInterfaceType.None)]
        public class DniNetStructsObj : IAccountStructLookup
        {
            public AccountStruct RetrieveAccount(int acctId)
            {
                AccountStruct result = new AccountStruct();
                if (acctId == 123)
                {
                    result.AccountId = acctId;
                    result.AccountName = "myAccount";
                    result.Balance = 1009.95M;
                    //result.Aliases = new Alias[5];
                    //result.Aliases[0].Name = "1";
                    //result.Aliases[1].Name = "2";
                    //result.Aliases[2].Name = "3";
                    //result.Aliases[3].Name = "4";
                    //result.Aliases[4].Name = "5";

                }
                return result;
            }

            public void UpdateBalance(ref AccountStruct account)
            {
                //update the balance
                account.Balance += 500.00M;
            }
            public Alias[] GetRef( )
            {
                Alias[] al= new Alias[2];
                al[0].Name = "1";
                al[1].Name = "2";
                return al;
            }


}

和C ++的身边的事情

And the C++ side of things

#include "stdafx.h"
#include "ConsoleApplication1.h"
#import "D:\Source Code\MarshalLib\MarshalLib\bin\Debug\MarshalLib.tlb" raw_interface_only

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


// The one and only application object

CWinApp theApp;

using namespace std;
using namespace MarshalLib;

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
    int nRetCode = 0;

    HMODULE hModule = ::GetModuleHandle(NULL);

    if (hModule != NULL)
    {
        // initialize MFC and print and error on failure
        if (!AfxWinInit(hModule, NULL, ::GetCommandLine(), 0))
        {
            // TODO: change error code to suit your needs
            _tprintf(_T("Fatal Error: MFC initialization failed\n"));
            nRetCode = 1;
        }
        else
        {
            try
            {
            CoInitialize(NULL);
            IAccountStructLookupPtr api(__uuidof(DniNetStructsObj));
            api->GetRef();
            CoUninitialize();
            }
            catch (...)
            {
            }

        }
    }
    else
    {
        // TODO: change error code to suit your needs
        _tprintf(_T("Fatal Error: GetModuleHandle failed\n"));
        nRetCode = 1;
    }

    return nRetCode;
}



我得到一个错误,当我打电话API-GetRef()来获取一个数组的结构。请帮我从C#返回结构的数组,并在C ++中使用它。

I get an error when I call api-GetRef() to get an array of structs. Please help me return an array of structs from c# and use it in c++.

在此先感谢。

推荐答案

与返回数组的问题是,在C ++中,你会看到一个指向struct,没有关于数组大小的信息。你可以尝试元帅它作为一个SAFEARRAY,但IMO,SAFEARRAYS是在颈部疼痛

The problem with returning the array is that in the C++ you will see a pointer to struct and have no information about array size. You can try to marshal it as a SAFEARRAY, but IMO, SAFEARRAYs are pain in the neck.

我喜欢它建模为这样的:

I prefer to model it as this:

[ComVisible(true)]
[Guid("C3E38106-F303-46d9-9EFB-AD8A8CA8644E")]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct MyStruct
{
    public int Value;

    // I marshal strings as arrays! see note at the bottom
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
    public string Unit
}

[ComVisible(true),
Guid("BD4E6810-8E8C-460c-B771-E266B6F9122F"),
InterfaceType(ComInterfaceType.InterfaceIsIUnknown)
]
public interface IMyService
{
    int GetData([MarshalAs(UnmanagedType.LPArray)] out MyStruct[] data);
}



客户端代码是这样的:

The client code is like this:

Lib::MyStruct* data;
long size = svc->GetData(&data);

for(size_t i = 0; i < size; ++i)
{
  Lib::MyStruct& current = data[i];
  long val = current.Value;
  bstr_t unit = current.Unit;
  // ...
}                                           

// now you need to release the memory. However, if you marshal
// strings in struct as BSTRs, you need to first release them by
// calling SysFreeString. This is why I prefer to marshal strings
// as arrays whenever I can: you can still easily construct a bstr_t
// in your client code, but you don't need to release them explicitly
CoTaskMemFree(data);



对于有关 SAFEARRAY 评论小号:他们被要求只有当该接口必须是自动化兼容的,即后期绑定,即一个的IDispatch 接口即标记为 ComInterfaceType.InterfaceIsIDispatch 。如果不是这种情况下(我声明即 ComInterfaceType.InterfaceIsIUnknown 界面自定义),使用标准的数组是完全正常的,他们也同样可以支持作为 SAFEARRAY 秒。此外,自定义的结构的 SAFEARRAY s工作带来一些额外的 复杂性我倾向于避免。如果你不需要后期绑定,没有理由与 SAFEARRAY 秒。

With regard to comment about SAFEARRAYs: they are required only if the interface must be automation compliant i.e. late-bound i.e. an IDispatch interface i.e. marked as ComInterfaceType.InterfaceIsIDispatch. If this is not the case (and I declared the interface as custom i.e. ComInterfaceType.InterfaceIsIUnknown) using the standard arrays is perfectly fine and they are equally well supported as SAFEARRAYs. Furthermore, working with SAFEARRAYs of custom structs brings some additional complexity which I prefer to avoid. If you don't need late binding, there is no reason to fight with SAFEARRAYs.

打关于 CComSafeArray ,如记录,它不支持 VT_RECORD 这是需要支持的结构阵列(另一种选择是与 IRecordInfo 元帅为 VT_VARIANT ,但我甚至不会去成)。

With regard to CComSafeArray, as documented, it doesn't support VT_RECORD which is required to support arrays of structs (another option is to marshal it as VT_VARIANT with IRecordInfo but I won't even go into that).

这篇关于通过使用COM调用包装从C#到C结构的数组++的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆