从C#到C在COM中返回对象数组

3

我正在使用COM在C++和C#之间进行通信。

我在C#中有以下类:

  1. Email Address

    /// <summary>
    /// Email Address
    /// </summary>
    public class EmailAddress
    {
        /// <summary>
        /// SMTP Address
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]        
        public string Address;
    
        /// <summary>
        /// Name
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string Name;
    }
    
  2. Mailbox

    /// <summary>
    /// MailBox Entity
    /// </summary>   
    public struct MailBoxEntity
    {
        /// <summary>
        /// SMTP Address
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string SMTPAddress;
    
        /// <summary>
        /// Mailbox Display Name
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string Name;
    
        /// <summary>
        /// Mailbox Server Name
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string sServerName;
    }
    
  3. EmailEntity (yet to implement IEmailEntity which will contain properties for each field)

    /// <summary>
    /// Class for Email Entity
    /// </summary>
    public class EmailEntity : IEmailEntity
    {
        /// <summary>
        /// BccRecipients
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray)]
        public EmailAddress[] BccRecipients;
    
        /// <summary>
        /// Body
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string Body;
    
        /// <summary>
        /// CcRecipients
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray)]
        public EmailAddress[] CcRecipients;
    
        /// <summary>
        /// Culture
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string Culture;
    
        /// <summary>
        /// DateTimeCreated
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string DateTimeCreated;
    
        /// <summary>
        /// DateTimeReceived
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string DateTimeReceived;
    
        /// <summary>
        /// DateTimeSent
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string DateTimeSent;
    
        /// <summary>
        /// FromAddress
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string FromAddress;
    
        /// <summary>
        /// FromName
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string FromName;
    
    
        /// <summary>
        /// HasAttachments
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string HasAttachments;
    
        /// <summary>
        /// Id
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string Id;
    
        /// <summary>
        /// Importance
        /// </summary>
       [MarshalAs(UnmanagedType.BStr)]
        public string Importance;
    
        /// <summary>
        /// LastModifiedName
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string LastModifiedName;
    
        /// <summary>
        /// LastModifiedTime
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string LastModifiedTime;
    
        /// <summary>
        /// MimeContent
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string MimeContent;
    
        /// <summary>
        /// ParentFolderId
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string ParentFolderId;
    
        /// <summary>
        /// Original Mailbox
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public MailBoxEntity OriginalMailBox;
    
    
        /// <summary>
        /// ParentFolderName
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string ParentFolderName;
    
        /// <summary>
        /// ReceivedByAddress
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string ReceivedByAddress;
    
        /// <summary>
        /// ReceivedByName
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string ReceivedByName;
    
        /// <summary>
        /// Size
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string Size;
    
        /// <summary>
        /// Subject
        /// </summary>
        [MarshalAs(UnmanagedType.BStr)]
        public string Subject;
    
        /// <summary>
        /// ToRecipients
        /// </summary>
        [MarshalAs(UnmanagedType.ByValArray)]
        public EmailAddress[] ToRecipients;
    
    }
    
我该如何访问从C#函数返回的EmailEntity对象数组(在C++中)并读取EmailEntity类的字段。请注意,EmailEntity类包含像EmailAddress[]这样的成员,它本身是一个对象数组。 我打算实现包含访问EmailEntity字段属性的IEmailEntity接口,并在C++中使用它来访问字段。这是正确的方式吗?
我该如何访问C++中复杂的字段成员,比如EmailAddress(EmailAddress[] ToRecipients)数组。
请给出建议。
谢谢。
1个回答

5
您可以简化.NET代码,像这样(删除所有应该自动完成的MarshalAs):
[ComVisible(true)]
public class MyRootClass : IMyRootClass // some class to start with
{
    public IEmailEntity[] GetEntities()
    {
        List<IEmailEntity> list = new List<IEmailEntity>();
        for(int i = 0; i < 10; i++)
        {
            EmailEntity entity = new EmailEntity();
            List<IEmailAddress> addresses = new List<IEmailAddress>();
            addresses.Add(new EmailAddress { Name = "Joe" + i });
            entity.BccRecipients = addresses.ToArray();
            entity.Body = "hello world " + i;
            list.Add(entity);
        }
        return list.ToArray();
    }   
}

[ComVisible(true)]
public interface IMyRootClass
{
    IEmailEntity[] GetEntities();
}

public class EmailEntity : IEmailEntity
{
    public IEmailAddress[] BccRecipients { get; set; }
    public string Body { get; set; }
}

public class EmailAddress : IEmailAddress
{
    public string Address { get; set; }
    public string Name { get; set; }
}

[ComVisible(true)]
public interface IEmailAddress
{
    string Address { get; set; }
    string Name { get; set; }
}

[ComVisible(true)]
public interface IEmailEntity
{
    IEmailAddress[] BccRecipients { get; set; }
    string Body { get; set; }
    // to be continued...
}

要在C++中使用它,您需要像这里的类似答案所述那样注册DLL并构建.TLB(类型库文件):在未托管的C ++程序中实现C#DLL COM文件

然后,您可以像这样在C++中访问这些类:

#include "stdafx.h"
#import  "c:\MyPathToTheTlb\YourAssembly.tlb" // import the COM TLB

using namespace YourAssembly;

int _tmain(int argc, _TCHAR* argv[])
{
  CoInitialize(NULL);
  IMyRootClassPtr ptr(__uuidof(MyRootClass));
  CComSafeArray<IUnknown*> entities = ptr->GetEntities(); // CComSafeArray needs atlsafe.h in the PCH
  for(int i = entities.GetLowerBound(0); i <= entities.GetUpperBound(0); i++)
  {
    IEmailEntityPtr entity;
    entities.GetAt(i).QueryInterface(&entity);
    _bstr_t body = entity->Body;
    printf("%S\n", body.GetBSTR());

    CComSafeArray<IUnknown*> recipients = entity->BccRecipients;
    for(int j = recipients.GetLowerBound(0); j <= recipients.GetUpperBound(0); j++)
    {
      IEmailAddressPtr address;
      recipients.GetAt(j).QueryInterface(&address);
      _bstr_t name = address->Name;
      printf(" %S\n", name.GetBSTR());
    }
  }
  CoUninitialize();
}

你需要使用[InterfaceType]属性才能使这段代码正常工作。目前,接口只能在后期绑定时访问,默认值为ComInterfaceType.InterfaceIsIDispatch。 - Hans Passant
@HansPassant - 这段代码原样运行良好。我相信默认值是双重的。 - Simon Mourier
你是对的,让我感到有点尴尬。看起来我必须在这里和那里编辑一些答案 :( - Hans Passant
1
我得到了调试断言失败的错误。在atlsafe.h中,表达式psaSrc!=0。这是因为entity->BccRecepients返回了null吗? - Gags
这取决于你实际返回的内容。我的示例代码将一个数组放入BccRecipients中,因此我不会得到NULL。 - Simon Mourier
一个问题..在这个类中,我需要在BccRecipients上放置MarshalAs属性吗?如果是的话,它的值会是什么..public class EmailEntity:IEmailEntity { public IEmailAddress[] BccRecipients { get; set; } public string Body { get; set; } } - Gags

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接