通过Excel-DNA将Excel.Range从VBA传递到C#

时间:2015-06-24 23:08:22

标签: c# vba vsto excel-dna

我正在研究如何通过Excel-DNA将Excel.Range对象从Excel中的Excel传递到C#,然后通过传递的引用与Excel对象模型进行交互的正确/最佳方式。

示例代码:

    '***************** VBA code ********************
    Sub test2()
        Dim rng As Excel.Range
        Set rng = Worksheets("test_output").Range("D9")

        Dim myDLL As New XLServer.MyClass
        Call myDLL.test_ExcelRangePassedAsObject(rng)
        Call myDLL.test_ExcelRangePassedAsRange(rng)
    End Sub



//**************** C# Code **********************
using System;
using ExcelDna.Integration;
using ExcelDna.ComInterop;
using System.Runtime.InteropServices;
using Microsoft.Office.Interop;
using Excel = Microsoft.Office.Interop.Excel;

namespace XLServer
{
    [ComVisible(false)]
    class ExcelAddin : IExcelAddIn
    {
        public void AutoOpen()
        {
            ComServer.DllRegisterServer();
        }
        public void AutoClose()
        {
            ComServer.DllUnregisterServer();
        }
    }    

    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.AutoDual)]
    public class MyClass                            
    {
        public void test_ExcelRangePassedAsObject(object range)
        {
            Excel.Range rng = (Excel.Range)range;
            rng.Worksheet.Cells[1, 1].Value = "Specified range was: " + rng.Address.ToString();
        }

        public void test_ExcelRangePassedAsRange(Excel.Range range)
        {
            Excel.Range rng = range;
            rng.Worksheet.Cells[2, 1].Value = "Specified range was: " + rng.Address.ToString();
        }

当我运行时,我得到预期的输出:
A1:指定范围:$ D $ 9
A2:指定范围为:$ D $ 9

花了大量的时间在谷歌上搜索Excel-DNA问题并且没有任何结果,我只是写了这篇文章并且实际上感到惊讶的是它没有任何问题。

所以我想我的问题是那些有经验的人...... 这是正确的/最好的方法吗?

例如,我遇到了许多类似的帖子:
https://groups.google.com/forum/#!topic/exceldna/zqzEIos7ma0

....人们在处理ExcelReference个对象并且不得不使用令人难以置信的模糊代码将它们转换为Excel.Range个对象,而且几乎感觉就像我缺少的东西?

1 个答案:

答案 0 :(得分:6)

是的,这是正确的方法,只要你:

  1. 确保您的图书馆项目标记为“注册COM互操作”。您不希望构建进程以直接将.dll注册为XLServer.MyClass COM类型的COM服务器,而是将.xll用作COM服务器。这将确保您的COM对象与其他加载项位于同一AppDomain中。

  2. ComServer属性添加到.dna文件中的<ExternalLibrary>标记,因此

    <ExternalLibrary Path="XlServer.dll" ComServer="true" ... />

  3. 负责所有COM接口版本控制规则。

  4. 关于这个主题的最佳参考是MikaelKatajamäki的演练文章,我认为你发现:

    现在有一些背景知识可以为Google小组讨论提供一些背景信息:

    大多数Excel-DNA内部都与Excel C API(由Excel SDK定义)有关,这对于制作高性能工作表UDF非常有用。在此设置中,描述工作表引用的C API结构是ExcelDna.Integration.ExcelReference类型的包装器。这是一个包含表单指针(称为SheetId)的小数据结构,以及引用的范围。 ExcelReference个对象可以安全地用在多线程UDF中,并且可以来回传递给C API。可以将Excel-DNA工作表函数配置为在适当时接收ExcelReference个对象作为输入。

    Range COM对象完全不同。它是这种工作表引用的COM对象包装器,受所有COM线程,生命周期和其他限制的约束。 Microsoft不支持在.xll中定义的UDF函数内使用COM对象模型(因此使用Range对象),虽然它主要起作用,但尝试在多线程函数中使用它们可能会导致严重的问题

    在放入Excel-DNA加载项的宏或功能​​区回调(由ExcelAsyncUtil.QueueAsMacro启动的异步触发宏)中,您可以安全地使用C API和COM对象模型

    从瘦ExcelReference对象转换为COM Range对象非常困难,但这取决于您是否要支持多区域范围。如果没有,它就像:

    一样简单
    static Range ReferenceToRange(ExcelReference xlref)
    {
        string refText = (string)XlCall.Excel(XlCall.xlfReftext, xlref, true);
        dynamic app = ExcelDnaUtil.Application;
        return app.Range[refText];
    } 
    

    这个帮助程序不属于ExcelReference类型的唯一原因是Excel-DNA仍然支持.NET 2.0,并且只有.NET 4.0才能使用可以理解这种情况的COM类型统一