使用 com 可调用包装器将结构数组从 c# 传递到 C++ [英] passing an array of structs from c# to C++ using com callable wrapper

查看:25
本文介绍了使用 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++ 方面

#include "stdafx.h"
#include "ConsoleApplication1.h"
#import "D:Source CodeMarshalLibMarshalLibinDebugMarshalLib.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
"));
            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
"));
        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,SAFEARRAY 令人头疼.

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.

我更喜欢这样建模:

[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);
}

客户端代码是这样的:

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);

关于 SAFEARRAYs 的评论:仅当接口必须是自动化兼容即后期绑定即 IDispatch 接口即标记为 时才需要它们ComInterfaceType.InterfaceIsIDispatch.如果不是这种情况(并且我将接口声明为自定义,即 ComInterfaceType.InterfaceIsIUnknown),则使用标准数组非常好,并且它们与 SAFEARRAY 一样受到很好的支持.此外,使用自定义结构的 SAFEARRAY 会带来 一些额外的复杂度宁愿避免.如果你不需要后期绑定,那么没有理由和SAFEARRAYs争吵.

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(另一种选择是将其编组为 VT_VARIANTIRecordInfo 但我什至不会深入探讨).

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天全站免登陆