【发布时间】:2018-08-22 20:56:06
【问题描述】:
我正在开发一个 COM 插件库和 Excel 自动化插件库,其核心代码是用 C# 编写的。我想为函数设置一个可选参数,我知道这对于 C# 和 VBA 甚至 Excel WorksheetFunction 都是合法的。但我发现最后可选参数只适用于 COM 和自动化插件,这意味着如果一个插件首先运行,然后运行良好,但另一个的可选参数将不起作用。
请看下面的例子:
在VS 2013解决方案中,我有两个项目:一个叫TestVBA,另一个叫TestExcel。
TestVBA 用于 COM 插件,通过“Excel 2013 插件”构建,有两个.cs 文件:
ThisAddIn.cs
这个文件是自动生成的,稍作修改。代码是
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
using Excel = Microsoft.Office.Interop.Excel;
using Office = Microsoft.Office.Core;
using Microsoft.Office.Tools.Excel;
namespace TestVBA
{
public partial class ThisAddIn
{
private void ThisAddIn_Startup(object sender, System.EventArgs e)
{
}
private void ThisAddIn_Shutdown(object sender, System.EventArgs e)
{
}
private ExcelVBA oExcelVBA;
protected override object RequestComAddInAutomationService()
{
if (oExcelVBA == null)
{
oExcelVBA = new ExcelVBA();
}
return oExcelVBA;
}
#region VSTO generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InternalStartup()
{
this.Startup += new System.EventHandler(ThisAddIn_Startup);
this.Shutdown += new System.EventHandler(ThisAddIn_Shutdown);
}
#endregion
}
}
TestVBA.cs
该文件是COM插件的主要计算文件。代码是
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using System.Reflection;
namespace TestVBA
{
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class ExcelVBA
{
public int TestAddVBA(int a = 1, int b = 1)
{
return a + b;
}
}
}
另一个 TestExcel 用于 Excel 自动化加载项并通过 C#“类库”构建,并且有两个 .cs 文件:
BaseUDF.cs
这个文件定义了两个属性的装饰。代码是
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Runtime.InteropServices;
using Microsoft.Win32;
namespace BaseUDF
{
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public abstract class BaseUDF
{
[ComRegisterFunctionAttribute]
public static void RegisterFunction(Type type)
{
// Add the "Programmable" registry key under CLSID.
Registry.ClassesRoot.CreateSubKey(
GetSubKeyName(type, "Programmable"));
// Register the full path to mscoree.dll which makes Excel happier.
RegistryKey key = Registry.ClassesRoot.OpenSubKey(
GetSubKeyName(type, "InprocServer32"), true);
key.SetValue("",
System.Environment.SystemDirectory + @"\mscoree.dll",
RegistryValueKind.String);
}
[ComUnregisterFunctionAttribute]
public static void UnregisterFunction(Type type)
{
// Remove the "Programmable" registry key under CLSID.
Registry.ClassesRoot.DeleteSubKey(
GetSubKeyName(type, "Programmable"), false);
}
private static string GetSubKeyName(Type type,
string subKeyName)
{
System.Text.StringBuilder s =
new System.Text.StringBuilder();
s.Append(@"CLSID\{");
s.Append(type.GUID.ToString().ToUpper());
s.Append(@"}\");
s.Append(subKeyName);
return s.ToString();
}
// Hiding these methods from Excel.
[ComVisible(false)]
public override string ToString()
{
return base.ToString();
}
[ComVisible(false)]
public override bool Equals(object obj)
{
return base.Equals(obj);
}
[ComVisible(false)]
public override int GetHashCode()
{
return base.GetHashCode();
}
}
}
TestExcel.cs
此文件是 Excel 自动化插件的主要计算文件。代码是
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using Excel = Microsoft.Office.Interop.Excel;
using Extensibility;
namespace TestExcel
{
[Guid("7127696E-AB87-427a-BC85-AB3CBA301CF3")]
[ClassInterface(ClassInterfaceType.AutoDual)]
[ComVisible(true)]
public class TestExcel : BaseUDF.BaseUDF
{
public int TestAddExcel(int a = 1, int b = 1)
{
return a + b;
}
}
}
构建完成后,这两个插件已经在系统中注册,在Excel中我们可以成功使用了。
对于自动化加载项,我们在电子表格中将它们称为=TestAddExcel(2,3) 和=TestAddExcel(),它们都工作得很好,并给出了正确的结果5 和2。但是,当我尝试通过
Sub TestVBA_Click()
Dim addIn As COMAddIn
Dim TesthObj As Object
Set addIn = Application.COMAddIns("TestVBA")
Set TestObj = addIn.Object
Range("Output").Value2 = TestObj.TestAddVBA(2, 3)
Range("Output").Offset(1, 0).Value2 = TestObj.TestAddVBA()
End Sub
所有参数都存在的第一个调用运行良好,但第二个缺少参数的调用显示错误Type mismatch。
有趣的是,当我关闭测试excel文件并再次打开它时,这次我先测试COM插件,仍然通过上面的VBA代码,两个调用都很好。然后,当我测试两个曾经运行良好的电子表格函数时,只有第一个很好,第二个缺少参数=TestAddExcel() 失败并显示#VALUE!。
如果有人能帮助解决这个奇怪的问题,那就太好了。
【问题讨论】:
-
您好,您需要我的回答吗?
-
@JeremyThompson,非常感谢您的回答。然而,我的妻子这周刚刚生下了我们的孩子。这几天完全是一团糟。我会尽快尝试您的建议并尽快回复您。
标签: c# excel vsto add-in excel-addins