COM 加载项与用 C# 编写的自动化加载项的可选参数
Optional Argument of COM Add-in vs Automation Add-in Written in C#
我正在开发一个 COM 插件和 Excel 自动化插件的库,其核心代码是用 C# 编写的。我想为该函数设置一个可选参数,我知道这对于 C# 和 VBA,甚至 Excel WorksheetFunction 都是合法的。但我发现最终可选参数仅适用于 COM 和自动化加载项,这意味着如果一个加载项首先是 运行,则可以正常工作,但另一个的可选参数将不起作用。
请看下面的例子:
在 VS 2013 解决方案中,我有两个项目:一个名为 TestVBA
,另一个名为 TestExcel
。
TestVBA
用于 COM 加载项并通过 "Excel 2013 Add-in" 构建,并且有两个 .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# "Class Library" 构建,并且有两个 .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
.但是,当我尝试通过
调用 COM 加载项时
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!
.
如果有人能帮助解决这个奇怪的问题,那就太好了。
这完全是在摸黑,但您能否创建该方法的重载版本来模仿您在 C# 具有可选参数之前完成此操作的方式,看看是否可行?
public int TestAddExcel(int a, int b)
{
return a + b;
}
public int TestAddExcel(int a)
{
return a + 1;
}
public int TestAddExcel()
{
return 2;
}
我不确定您是如何在没有注册 COM 的情况下引用 class 库的?我现在明白了,您正在使用后期绑定。我不知道你可以这样做(不认为它会让你) 和 怀疑是问题所在,它也匹配 Type mismatch
错误。
按照我的 中的第二个解决方案,并确保你 注册 COM:
Click on the Build tab and check the check box that says “Register for COM Interop”. At this point you have an extra step if you are running on Windows Vista or higher. Visual Studio has to be run with administrator privileges to register for COM Interop. Save your project and exit Visual Studio. Then find Visual Studio in the Start menu and right click on it and choose “Run as Administrator”. Reopen your project in Visual Studio. Then choose “Build” to build the add-in.
可选地,如果上述方法不起作用,请按照我的回答中的第三个解决方案并参考自动化插件并使用早期绑定,我已经测试过它并且它完美地工作:
Sub TestVBA1_Click()
Dim addIn As COMAddIn
Dim TesthObj As Object
Set addIn = Application.COMAddIns("TestVBA")
Set TestObj = addIn.Object
Debug.Print TestObj.TestAddVBA(2, 3)
Debug.Print TestObj.TestAddVBA()
Dim dotNetClass As TestExcel.TestExcel
Set dotNetClass = New TestExcel.TestExcel
Debug.Print dotNetClass.TestAddExcel(7, 3)
Debug.Print dotNetClass.TestAddExcel()
End Sub
我正在开发一个 COM 插件和 Excel 自动化插件的库,其核心代码是用 C# 编写的。我想为该函数设置一个可选参数,我知道这对于 C# 和 VBA,甚至 Excel WorksheetFunction 都是合法的。但我发现最终可选参数仅适用于 COM 和自动化加载项,这意味着如果一个加载项首先是 运行,则可以正常工作,但另一个的可选参数将不起作用。
请看下面的例子:
在 VS 2013 解决方案中,我有两个项目:一个名为 TestVBA
,另一个名为 TestExcel
。
TestVBA
用于 COM 加载项并通过 "Excel 2013 Add-in" 构建,并且有两个 .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# "Class Library" 构建,并且有两个 .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!
.
如果有人能帮助解决这个奇怪的问题,那就太好了。
这完全是在摸黑,但您能否创建该方法的重载版本来模仿您在 C# 具有可选参数之前完成此操作的方式,看看是否可行?
public int TestAddExcel(int a, int b)
{
return a + b;
}
public int TestAddExcel(int a)
{
return a + 1;
}
public int TestAddExcel()
{
return 2;
}
我不确定您是如何在没有注册 COM 的情况下引用 class 库的?我现在明白了,您正在使用后期绑定。我不知道你可以这样做(不认为它会让你) 和 怀疑是问题所在,它也匹配 Type mismatch
错误。
按照我的
Click on the Build tab and check the check box that says “Register for COM Interop”. At this point you have an extra step if you are running on Windows Vista or higher. Visual Studio has to be run with administrator privileges to register for COM Interop. Save your project and exit Visual Studio. Then find Visual Studio in the Start menu and right click on it and choose “Run as Administrator”. Reopen your project in Visual Studio. Then choose “Build” to build the add-in.
可选地,如果上述方法不起作用,请按照我的回答中的第三个解决方案并参考自动化插件并使用早期绑定,我已经测试过它并且它完美地工作:
Sub TestVBA1_Click()
Dim addIn As COMAddIn
Dim TesthObj As Object
Set addIn = Application.COMAddIns("TestVBA")
Set TestObj = addIn.Object
Debug.Print TestObj.TestAddVBA(2, 3)
Debug.Print TestObj.TestAddVBA()
Dim dotNetClass As TestExcel.TestExcel
Set dotNetClass = New TestExcel.TestExcel
Debug.Print dotNetClass.TestAddExcel(7, 3)
Debug.Print dotNetClass.TestAddExcel()
End Sub