通过Excel DNA传递Excel。范围从VBA到C#
我正在研究如何正确/最佳地传递Excel。通过Excel DNA将对象从Excel中的VBA扩展到C#,然后通过传递的引用与Excel对象模型交互 示例代码:通过Excel DNA传递Excel。范围从VBA到C#,c#,vba,vsto,excel-dna,C#,Vba,Vsto,Excel Dna,我正在研究如何正确/最佳地传递Excel。通过Excel DNA将对象从Excel中的VBA扩展到C#,然后通过传递的引用与Excel对象模型交互 示例代码: '***************** VBA code ******************** Sub test2() Dim rng As Excel.Range Set rng = Worksheets("test_output").Range("D9") Dim m
'***************** 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问题,却什么都没想到之后,我写了这篇文章,实际上很惊讶它居然没有问题 所以我想我的问题是,对于那些在这方面有经验的人来说……这是正确的/最好的方法吗? 例如,我遇到许多类似的帖子:
…当人们处理
ExcelReference
对象时,不得不使用令人难以置信的晦涩代码将这些对象转换为Excel.Range
对象,这几乎让人感觉我缺少了什么?是的,这是正确的方法,只要您:
XLServer.MyClass
COM类型的COM服务器的过程,而是将.xll用作COM服务器。这将确保COM对象与其他外接程序位于同一AppDomain中
ComServer
属性添加到.dna文件中的
标记中,如下所示
现在让我们来了解一下谷歌小组讨论的背景: 大多数exceldna内部都与excelcapi(由excelsdk定义)有关,它非常适合制作高性能的工作表udf。在此设置中,描述工作表引用的C API结构是
ExcelDna.Integration.ExcelReference
类型中的包装器。这是一个小型数据结构,用于封装图纸指针(称为SheetId)和引用的范围ExcelReference
对象可以安全地用于多线程UDF,并且可以来回传递到C API。Excel DNA工作表函数可配置为在适当时接收ExcelReference
对象作为输入
Range
COM对象是完全不同的东西。它是围绕这样一个表引用的COM对象包装器,受所有COM线程、生存期和其他限制的约束。Microsoft不支持在.xll中定义的UDF函数中使用COM对象模型(以及因此而产生的范围
对象),虽然它基本上是有效的,但尝试在多线程函数中使用这些对象可能会导致严重的问题
在放入Excel DNA加载项的宏或功能区回调(作为异步触发的宏,由ExcelAsyncUtil.QueueAsMacro启动)中,您可以安全地使用C API和COM对象模型
从精简的ExcelReference
对象转换为COM范围
对象并不难,但这取决于是否要支持多区域范围。如果不是,那么简单如下:
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之后才可用。这就是为什么我选择堆栈溢出而不是Google组的原因……这是我不了解的。平心而论,这条线索最初始于2011年,但2014年10月14日发布的问题仍然难以解决,而且他来自c#,所以我不明白为什么他不能直接获得范围参考。也许他的插件是基于DNA而不是VSTO的?这有意义吗?我只是在学习,所以没有任何线索。事实上,戈弗特现在也不知道那个家伙在说什么,因为我重读了它……谢谢你的全面回答,这正是我想要知道的。我非常想了解更多关于您提到的特定COM注意事项,如果您手头有任何链接,我将不胜感激。(“您不希望构建过程直接将.dll注册为XLServer.MyClass COM类型的COM服务器,而是将.xll用作COM服务器。这将确保COM对象与其他外接程序位于同一AppDomain中。”)假设您想要在工作表UDF和VBA中使用的COM对象之间共享某种缓存。如果UDF部件和COM对象位于同一AppDomain中,那么共享就容易多了。为COM访问单独注册.dll将导致.NET运行时在默认AppDomain中激活它,而Excel DNA加载项在其自己的AppDomain中运行。另外-Excel DNA注册不需要管理员权限。是否有任何理由不在功能区回调中使用ExcelAynscUtil.QueueAsMacro?我想同时使用COM和C API,在QueueAsMacro中对我的函数的包装调用允许我使用XlCall.Excel(XlCall.xlfGetCell,53,参考)。如果我没有使用它,我就无法找到成功调用该函数的方法。我的Ribbon/AddIn与应用程序事件挂钩,并且需要在某些处理程序中保持一点状态,因此使我的所有回调保持静态,以便在它们上使用application.Run()变得太复杂了。此外,当QueueAsMacro委托引发错误时,谁捕捉到该异常?它似乎不是我的功能区回调或ExcelIntegration.RegisterUnhandledExceptionHandler()方法。