【问题标题】:Return Array from C# COM Library to VBA从 C# COM 库返回数组到 VBA
【发布时间】:2016-01-25 06:50:25
【问题描述】:

我花了很长时间试图找到一个解决方案,但我似乎无法让它发挥作用。情况如下:

我正在设置一个供 vba 宏使用的 c# com 库。有问题的 c# 库方法将两个数组作为参数,并返回一个大小和类型与第一个参数相同的数组。这是c#代码(接口和实现):

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Windows.Forms;

namespace Aufbereiten.Base.Controller
{
    [Guid("A341A58B-96CA-42B8-8256-B8AC16586176")]
    public interface IExposer
    {
        [DispId(1)]
        void Connect(String macroName, String inputFileName, String functionType);
        [DispId(2)]
        Boolean Run();
        [DispId(3)]
        object[] StringCleaner(object lineOut, object headerOut);
    }

    [Guid("C66FF152-60E2-4AF3-9D65-655C224A241E")]
    [Serializable(), ClassInterface(ClassInterfaceType.None), ComVisible(true)]
    public class Library : IExposer
    {
        #region Fields
        private MainController _mc;
        #endregion

        #region Properties
        public MainController MC
        {
            get { return _mc; }
            set { _mc = value; }
        }
        #endregion

        #region Methods
        public void Connect(String macroName, String fileName, String functionType)
        {
            MC = new MainController(macroName, fileName, functionType);
        }

        public Boolean Run()
        {
            return MC.Run();
        }

        /// <summary>
        /// Cleans input string of unwanted characters
        /// </summary>
        /// <param name="lineOut">String Array</param>
        /// <param name="headerOut">Integer Array</param>
        /// <returns>String array</returns>
        public object[] StringCleaner(object lineOut, object headerOut)
        {
            // Convert input objects to List and convert return List to string array directly
            return MC.Generic.StringCleaner(ComObjectToStringList(lineOut), ComObjectToIntList(headerOut)).ToArray<string>();
        }

        /// <susgbomary>
        /// Converts a com object into a string list
        /// </summary>
        /// <param name="comObj">object</param>
        /// <returns>String list</returns>
        private List<string> ComObjectToStringList(object comObj)
        {
            List<string> result = new List<string>();

            // Convert COM object (VBA array) to string list
            Type comObjType = comObj.GetType();

            object[] args = new object[1];
            int numEntries = (int)comObjType.InvokeMember("Length", BindingFlags.GetProperty, null, comObj, null);
            for (int i = 0; i < numEntries; i++)
            {
                args[0] = i;
                string currentEntry = (string)comObjType.InvokeMember("GetValue", BindingFlags.InvokeMethod, null, comObj, args);
                if (currentEntry == null)
                {
                    result.Add("");
                }
                else
                {
                    result.Add(currentEntry);
                }
            }

            return result;
        }

        /// <summary>
        /// Converts a com object into an int list
        /// </summary>
        /// <param name="comObj">object</param>
        /// <returns>Int32 list</returns>
        private List<int> ComObjectToIntList(object comObj)
        {
            List<int> result = new List<int>();

            // Convert COM object (VBA array) to int list
            Type comObjType = comObj.GetType();

            object[] args = new object[1];
            int numEntries = (int)comObjType.InvokeMember("Length", BindingFlags.GetProperty, null, comObj, null);
            for (int i = 0; i < numEntries; i++)
            {
                args[0] = i;
                result.Add((int)comObjType.InvokeMember("GetValue", BindingFlags.InvokeMethod, null, comObj, args));
            }

            return result;
        }
        #endregion
    }
}

Generic.StringCleaner 方法将执行清除参数数组“lineOut”中不需要的字符的实际工作。以下是调用库方法的 VBA 代码摘录:

Dim arrLineOut() As Variant
Dim dicInToOut As New Scripting.Dictionary

...

Dim lib As New Aufbereiten.library
lib.Connect ThisWorkbook.Name, "_Library_Test_custFile.xlsx", "CustImport"
arrLineOut = lib.StringCleaner(arrLineOut, dicInToOut.Items)

c# 中的所有操作都可以完美运行,除了返回结果、清理后的数组。

当我运行宏时,我得到一个

运行时错误80131533:数组的运行时类型与元数据中记录的子类型不匹配。

谁能告诉我我在这里做错了什么?

【问题讨论】:

  • 您返回的是字符串数组而不是对象,为什么要声明为对象数组?试试看:string[] StringCleaner(object lineOut, object headerOut);
  • 我试过 @Meehow,但这会在 Excel 中给我一个运行时错误:无法分配给数组,即使我声明了一个新的变体数组来分配返回数组到:Dim tmp() As Variant tmp = lib.StringCleaner(arrLineOut, dicInToOut.Items)
  • 这很奇怪。尝试一些非常简单的方法,例如 public string[] GetArrayFromCSharp() { return new List&lt;string&gt; {"foo", "boo"}.ToArray(); } 和 VBA 中的 Dim arr As Variant arr = lib.GetArrayFromCSharp() Debug.Print arr(0), arr(1)
  • 这确实有效!我现在将我的代码行 return MC.Generic.StringCleaner(ComObjectToStringList(lineOut), ComObjectToIntList(headerOut)).ToArray&lt;string&gt;(); 更改为 return MC.Generic.StringCleaner(ComObjectToStringList(lineOut), ComObjectToIntList(headerOut)).ToArray(); 所以只是根据您的示例代码省略了末尾的 ,瞧:我现在可以将数组分配给 VBA 中新声明的变量数组。谢谢@Meehow!
  • @Gess - 请回答自己并关闭此问题

标签: c# vba com interop


【解决方案1】:

根据@Meehow 的建议,我已经进行了调整

public object[] StringCleaner(object lineOut, object headerOut)

public string[] StringCleaner(object lineOut, object headerOut)

return MC.Generic.StringCleaner(ComObjectToStringList(lineOut), ComObjectToIntList(headerOut)).ToArray<string>();

return MC.Generic.StringCleaner(ComObjectToStringList(lineOut), ComObjectToIntList(headerOut)).ToArray();

在这些更改之后,我能够将 c# 库中的返回值分配给 VBA 中新声明的变量数组:

Dim tmp As Variant
tmp = lib.StringCleaner(arrLineOut, dicInToOut.Items)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-09-28
    • 2011-10-15
    • 1970-01-01
    • 2011-01-02
    • 2011-09-14
    • 2011-02-05
    • 2010-11-02
    相关资源
    最近更新 更多