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

本文关键字:Excel Range VBA DNA 通过 | 更新日期: 2023-09-27 17:59:26

我正在研究传递Excel的正确/最佳方式。通过Excel DNA将对象从Excel中的VBA扩展到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/#!主题/exceldna/zqzEIos7ma0

人们在处理ExcelReference对象时,不得不使用令人难以置信的模糊代码将其转换为Excel.Range对象,而我几乎感觉缺少了什么?

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

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

  1. 确保您的库项目不是标记为";注册COM互操作";。您不希望生成直接将.dll注册为XLServer.MyClass COM类型的COM服务器的进程,而是将.xll用作COM服务器。这将确保COM对象与外接程序的其余部分位于同一AppDomain中。

  2. ComServer属性添加到.dna文件中的<ExternalLibrary>标签,从而作为

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

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

关于这个话题最好的参考文献是Mikael Katajamäki的走读文章,我想你已经找到了:

  • http://mikejuniperhill.blogspot.fi/2014/03/interfacing-c-and-vba-with-exceldna-no.html
  • http://mikejuniperhill.blogspot.fi/2014/03/interfacing-c-and-vba-with-exceldna_16.html

现在为谷歌小组讨论提供一些背景:

大多数Excel-DNA内部都与ExcelC API(由ExcelSDK定义)有关,后者非常适合制作高性能工作表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类型统一