2

考虑下面的代码,它打算由 C++ 使用 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 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++ 中使用它。

提前致谢。

4

2 回答 2

5

返回数组的问题在于,在 C++ 中,您将看到一个指向 struct 的指针并且没有关于数组大小的信息。您可以尝试将其编组为 SAFEARRAY,但 IMO,SAFEARRAY 令人头疼。

我更喜欢这样建模:

[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。如果不是这种情况(并且我将接口声明为自定义 ie ComInterfaceType.InterfaceIsIUnknown),则使用标准数组非常好,并且它们与SAFEARRAYs 一样受到很好的支持。此外,使用SAFEARRAY自定义结构会带来一些额外的 复杂性,我希望避免这种复杂性。如果不需要后期绑定,就没有理由和SAFEARRAYs 打架。

关于CComSafeArray,如文档所述,它不支持VT_RECORD支持结构数组所需的内容(另一种选择是将其编组VT_VARIANTIRecordInfo但我什至不会讨论)。

于 2012-09-20T08:12:11.343 回答
0

您首先需要通过接口公开托管代码并使用 regasm 注册它并创建类型库(tlb 文件)。然后你可以在你的非托管代码中使用它。

参考这篇文章:http: //blogs.msdn.com/b/deeptanshuv/archive/2005/06/26/432870.aspx

于 2012-09-19T21:41:17.330 回答