通过 Excel-DNA 将 Excel.Range 从 VBA 传递到 C#
Passing an Excel.Range from VBA to C# via Excel-DNA
我正在研究 proper/best 如何通过 Excel-DNA 将 Excel.Range 对象从 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 问题后一无所获,我写了这篇文章,实际上很惊讶它没有任何问题。
所以我想我的问题是那些在这方面有经验的人...这是proper/best解决问题的方法吗?
例如,我遇到过很多类似这样的帖子:
https://groups.google.com/forum/#!topic/exceldna/zqzEIos7ma0
..人们在处理 ExcelReference
对象并且不得不使用令人难以置信的晦涩代码将它们转换为 Excel.Range
对象时,我几乎感觉好像缺少了什么?
是的,这是正确的方法,只要您:
确定您的库项目未标记为“注册 COM 互操作”。您不希望构建进程直接将 .dll 注册为 XLServer.MyClass
COM 类型的 COM 服务器,而是让 .xll 作为 COM 服务器。这将确保您的 COM 对象与加载项的其余部分位于同一 AppDomain 中。
将ComServer
属性添加到.dna文件中的<ExternalLibrary>
标签,即
<ExternalLibrary Path="XlServer.dll" ComServer="true" ... />
.
注意所有 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
现在了解一些背景知识,为 Google 小组讨论提供一些背景信息:
大多数 Excel-DNA 内部结构与 Excel C API(由 Excel SDK 定义)有关,这对于制作高 -性能工作sheet UDF。在此设置中,描述 worksheet 引用的 C API 结构是 ExcelDna.Integration.ExcelReference
类型的包装器。这是一个包含 sheet 指针(称为 SheetId)和引用范围的小型数据结构。 ExcelReference
对象可以在多线程 UDF 中安全使用,并且可以来回传递给 C API。 Excel-DNA worksheet 函数可以配置为在适当的时候接收 ExcelReference
对象作为输入。
Range
COM 对象完全不同。它是围绕 sheet 引用的 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,并且只有 COM 类型统一才有意义自 .NET 4.0
我正在研究 proper/best 如何通过 Excel-DNA 将 Excel.Range 对象从 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 问题后一无所获,我写了这篇文章,实际上很惊讶它没有任何问题。
所以我想我的问题是那些在这方面有经验的人...这是proper/best解决问题的方法吗?
例如,我遇到过很多类似这样的帖子:
https://groups.google.com/forum/#!topic/exceldna/zqzEIos7ma0
..人们在处理 ExcelReference
对象并且不得不使用令人难以置信的晦涩代码将它们转换为 Excel.Range
对象时,我几乎感觉好像缺少了什么?
是的,这是正确的方法,只要您:
确定您的库项目未标记为“注册 COM 互操作”。您不希望构建进程直接将 .dll 注册为
XLServer.MyClass
COM 类型的 COM 服务器,而是让 .xll 作为 COM 服务器。这将确保您的 COM 对象与加载项的其余部分位于同一 AppDomain 中。将
ComServer
属性添加到.dna文件中的<ExternalLibrary>
标签,即<ExternalLibrary Path="XlServer.dll" ComServer="true" ... />
.注意所有 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
现在了解一些背景知识,为 Google 小组讨论提供一些背景信息:
大多数 Excel-DNA 内部结构与 Excel C API(由 Excel SDK 定义)有关,这对于制作高 -性能工作sheet UDF。在此设置中,描述 worksheet 引用的 C API 结构是 ExcelDna.Integration.ExcelReference
类型的包装器。这是一个包含 sheet 指针(称为 SheetId)和引用范围的小型数据结构。 ExcelReference
对象可以在多线程 UDF 中安全使用,并且可以来回传递给 C API。 Excel-DNA worksheet 函数可以配置为在适当的时候接收 ExcelReference
对象作为输入。
Range
COM 对象完全不同。它是围绕 sheet 引用的 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,并且只有 COM 类型统一才有意义自 .NET 4.0