将一组自定义对象从C#.NET DLL传回MFC

时间:2017-08-17 10:04:23

标签: c# com

以下是一个示例XML数据文件:

<?xml version="1.0" encoding="utf-8"?>
<AssignmentHistory Version="171804">
    <W20160104>
        <StudentItems>
            <Item>
                <Name Counsel="13" NextCounsel="0" Completed="1">Name 1</Name>
                <Type>Bible Reading (Main)</Type>
            </Item>
        </StudentItems>
    </W20160104>
    <W20160111>
        <StudentItems>
            <Item>
                <Name Counsel="9" NextCounsel="9" Completed="0">Name 2</Name>
                <Type>Bible Reading (Main)</Type>
            </Item>
            <Item Description="Initial Call">
                <Name Counsel="37" NextCounsel="38" Completed="1">Name 1</Name>
                <Type>#1 Student (Main)</Type>
            </Item>
            <Item>
                <Name>Name 3</Name>
                <Type>Assistant</Type>
            </Item>
            <Item>
                <Name Counsel="48" NextCounsel="49" Completed="1">Name 4</Name>
                <Type>#2 Student (Main)</Type>
            </Item>
            <Item>
                <Name>Name 5</Name>
                <Type>Assistant</Type>
            </Item>
            <Item>
                <Name Counsel="27" NextCounsel="30" Completed="1">Name 6</Name>
                <Type>#3 Student (Main)</Type>
            </Item>
            <Item>
                <Name>Name 7</Name>
                <Type>Assistant</Type>
            </Item>
        </StudentItems>
    </W20160111>
</AssignmentHistory>

我编写了一些代码来读取XML数据文件并找到给定名称的未来分配历史记录。例如,如果周是2016年1月4日,并且我们正在获取名称1的历史记录,那么我的代码将返回一个条目列表(在本例中,仅为1,2016年1月11日的一周)。

我的代码:

我的方法表现良好:

public void ExtractFutureStudentHistory(String strStudent, DateTime datWeekOfMeeting, String strHistoryDatabase, out DateTime[] aryFutureDates, out string[] aryFutureAssignTypes, out int[] aryFutureStudyNo)
{
    XmlDocument docAssignHistory = new XmlDocument();

    aryFutureDates = null;
    aryFutureAssignTypes = null;
    aryFutureStudyNo = null;

    List<DateTime> listFutureDates = new List<DateTime>();
    List<string> listFutureAssignTypes = new List<string>();
    List<int> listFutureStudyNo = new List<int>();

    try
    {
        docAssignHistory.Load(strHistoryDatabase);

        // The data in the XML should already be in ascending date order

        // The data we want:

        // AssignmentHistory/<WYYYYMMDD>/StudentItems/Item/Name
        // AssignmentHistory/<WYYYYMMDD>/StudentItems/Item/Type
        XmlNodeList listHistory = docAssignHistory.SelectNodes("AssignmentHistory/*/StudentItems/Item[Name='" + strStudent + "']");
        foreach(XmlNode nodeHistoryItem in listHistory)
        {
            XmlNode weekNode = nodeHistoryItem.ParentNode.ParentNode;
            String strWeekDate = weekNode.Name.Substring(1); // This skips the preceding "W"

            DateTime dateHistoryItemWeekOfMeeting = new DateTime(Convert.ToInt32(strWeekDate.Substring(0, 4)),
                                    Convert.ToInt32(strWeekDate.Substring(4, 2)),
                                    Convert.ToInt32(strWeekDate.Substring(6, 2)));

            if (dateHistoryItemWeekOfMeeting.Date > datWeekOfMeeting.Date)
            {
                // We need to include it
                listFutureDates.Add(dateHistoryItemWeekOfMeeting);
                listFutureStudyNo.Add(Convert.ToInt32(nodeHistoryItem.SelectSingleNode("Name").Attributes["Counsel"].Value));
                listFutureAssignTypes.Add(nodeHistoryItem.SelectSingleNode("Type").InnerText);
            }
        }

        aryFutureDates = listFutureDates.ToArray();
        aryFutureStudyNo = listFutureStudyNo.ToArray();
        aryFutureAssignTypes = listFutureAssignTypes.ToArray();
    }
    catch (Exception ex)
    {
        SimpleLog.Log(ex);
    }
}

可能逻辑可以简化,但它可以工作。我的问题是我的方法是C#.NET DLL的一部分。目前,我有这种公共接口方法:

[Guid("xx")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
[ComVisible(true)]
public interface IMSAToolsLibraryInterface
{
    void ExtractFutureStudentHistory(String strStudent, DateTime datWeekOfMeeting, String strHistoryDatabase, out DateTime[] aryFutureDates, out string[] aryFutureAssignTypes, out Int32[] aryFutureStudyNo);

 }

工作正常。在C ++ MFC中,我有三个SAFEARRAY**个对象,一个类型为BSTR,一个类型为DATE,另一个类型为int。它本身没问题。

我的问题是,我的功能可以更改为输出单个对象列表吗?例如,如果我创建了一个类:

StudentItem有三个成员变量用于日期,作业类型和研究编号。

我尝试将我的函数参数更改为out List<StudentItem>,但这不起作用。然后我将其更改为out StudentItem[],我仍然无法使用它。

我将StudentItem声明为具有三名成员的基本struct。声明此对象的正确方法是什么,以便我可以将其作为数组传回给MFC使用?

感谢。

更新

第1步:

我在DLL项目中添加了一个新对象:

[Guid("xx")]
[ComVisible(true)]
public struct StudentItem
{
    public string Type { get; set; }
    public DateTime Week { get; set; }
    public int Study { get; set; }
}

第2步:

我在界面中添加了一个引用:

void ExtractFutureStudentHistory2(String strStudent, DateTime datWeekOfMeeting, String strHistoryDatabase, out StudentItem[] aryStudentItems);

第3步:

我添加了调整后的方法:

public void ExtractFutureStudentHistory2(String strStudent, DateTime datWeekOfMeeting, String strHistoryDatabase, out StudentItem[] aryStudentItems)
{
    XmlDocument docAssignHistory = new XmlDocument();

    aryStudentItems = null;

    List<StudentItem> listStudentItems = new List<StudentItem>();

    try
    {
        docAssignHistory.Load(strHistoryDatabase);

        // The data in the XML should already be in ascending date order

        // The data we want:

        // AssignmentHistory/<WYYYYMMDD>/StudentItems/Item/Name
        // AssignmentHistory/<WYYYYMMDD>/StudentItems/Item/Type
        XmlNodeList listHistory = docAssignHistory.SelectNodes("AssignmentHistory/*/StudentItems/Item[Name='" + strStudent + "']");
        foreach (XmlNode nodeHistoryItem in listHistory)
        {
            XmlNode weekNode = nodeHistoryItem.ParentNode.ParentNode;
            String strWeekDate = weekNode.Name.Substring(1); // This skips the preceding "W"

            DateTime dateHistoryItemWeekOfMeeting = new DateTime(Convert.ToInt32(strWeekDate.Substring(0, 4)),
                                    Convert.ToInt32(strWeekDate.Substring(4, 2)),
                                    Convert.ToInt32(strWeekDate.Substring(6, 2)));

            if (dateHistoryItemWeekOfMeeting.Date > datWeekOfMeeting.Date)
            {
                StudentItem oItem = new StudentItem();
                oItem.Week = dateHistoryItemWeekOfMeeting;
                oItem.Type = nodeHistoryItem.SelectSingleNode("Type").InnerText;
                oItem.Study = Convert.ToInt32(nodeHistoryItem.SelectSingleNode("Name").Attributes["Counsel"].Value);

                listStudentItems.Add(oItem);
            }
        }

        aryStudentItems = listStudentItems.ToArray();
    }
    catch (Exception ex)
    {
        SimpleLog.Log(ex);
    }
}

第4步:

我编译DLL。我遇到了问题:

  

1&gt; C:\ Program Files(x86)\ Microsoft Visual   工作室\ 2017年\社区\的MSBuild \ 15.0 \斌\ Microsoft.Common.CurrentVersion.targets(4556,5):   警告:键入库导出器警告处理   'MSAToolsLibrary.StudentItem.k__BackingField,MSAToolsLibrary'。   警告:公共结构包含一个或多个非公共字段   将被出口。

2 个答案:

答案 0 :(得分:1)

这里有两个选项:

删除自动实现的属性。这以一种众所周知的方式公开您的结构,COM客户端可以使用它:

[Guid("xx")]
[ComVisible(true)]
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct StudentItem
{
    [MarshalAs(UnmanagedType.BStr)]
    public string Type;
    public DateTime Week;
    public int Study;
}

...或使用界面。 COM接口完全支持属性:

[Guid("xx")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface StudentItem
{
    string Type { get; set; }
    DateTime Week { get; set; }
    int Study { get; set; }
}

作为旁注,您可以考虑按如下方式更改方法:

StudentItem[] ExtractFutureStudentHistory2(String strStudent, DateTime datWeekOfMeeting, String strHistoryDatabase);

这使得在客户端中使用该方法变得更加容易,因为现在该数组被声明为标准返回参数。

答案 1 :(得分:-2)

您可以返回结构分配,如下面的代码所示。我使用xml linq将整个xml解析为一个列表。您可以查询列表对象

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;


namespace ConsoleApplication73
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {

            XDocument doc = XDocument.Load(FILENAME);

            List<Assignment> assignments = doc.Root.Elements().Select(x => new
            {
                items = x.Descendants("Item").Select(y => new Assignment()
                {
                    date = DateTime.ParseExact(x.Name.LocalName.Substring(1),"yyyyMMdd",System.Globalization.CultureInfo.InvariantCulture),
                    _type = (string)y.Descendants("Type").FirstOrDefault(),
                    name = (string)y.Descendants("Name").FirstOrDefault(),
                    counsel = (string)y.Descendants("Name").FirstOrDefault().Attribute("Counsel"),
                    nextCounsil = (string)y.Descendants("Name").FirstOrDefault().Attribute("NextCounsel"),
                    completed = y.Descendants("Name").FirstOrDefault().Attribute("Completed") == null ? false :
                       ((int)y.Descendants("Name").FirstOrDefault().Attribute("Completed")) == 0 ? false : true
                }).ToList()
            }).SelectMany(x => x.items).ToList();
        }
    }
    public struct Assignment
    {
        public string name { get; set; }
        public DateTime date { get; set; }
        public string counsel { get; set; }
        public string nextCounsil { get; set; }
        public Boolean completed { get; set; }
        public string _type { get; set; }
    }

}