从VBA访问COM加载项代码

从VBA访问COM加载项代码,com,excel,vsto,vba,Com,Excel,Vsto,Vba,我已经使用VisualStudio2005 Office工具为Excel2003创建了一个COM加载项。外接程序代码如下所示: [Guid("EAC0992E-AC39-4126-B851-A57BA3FA80B8")] [ComVisible(true)] [ProgId("NLog4VBA.Logger")] [ClassInterface(ClassInterfaceType.AutoDual)] public class Logger { public double Debug(

我已经使用VisualStudio2005 Office工具为Excel2003创建了一个COM加载项。外接程序代码如下所示:

[Guid("EAC0992E-AC39-4126-B851-A57BA3FA80B8")]
[ComVisible(true)]
[ProgId("NLog4VBA.Logger")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Logger
{
    public double Debug(string context, string message)
    {
        Trace.WriteLine(message);
        return message.Length;
    }

    [ComRegisterFunctionAttribute]
    public static void RegisterFunction(Type type)
    {
        Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
        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)
    {
        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();
    }
}
Private Sub CommandButton1_Click()

        'Declare an object variable using the referenced lib.
        'if all is well, intellisense will tell you what the proper object name is:
        Dim objLogger as NLog4VBA

        'Create an instance of the object
        Set objLogger = New NLog4VBA

        'Now use the object
        objLogger.Object.Debug "My Context", "My Message"
End Sub
我已将项目设置为注册COM互操作,并已将DLL注册到:

regasm.exe /tlb NLog4VBA.dll
打开Excel时,我进入工具->加载项,单击自动化,然后添加NLog4VBA.Logger。然后我可以转到Insert->Function,从类别列表中选择NLogVBA.Logger,然后选择Debug

最终结果是一个包含以下内容的单元格:

=Debug("My Context","My Message")
。。。以及显示值:

10
这一切都是应该的。在我的VBA代码中,我可以转到工具->参考并添加NLog4VBA。然后,我将以下代码添加到工作表上的按钮:

Private Sub CommandButton1_Click()
        Application.COMAddIns("NLog4VBA.Logger").Object.Debug "My Context", "My Message"
End Sub
此操作失败,因为COMAddIns(“NLog4VBA.Logger”)在以下情况下失败:

Run-time error '9': Subscript out of range
有人能告诉我需要做些什么才能使我的VBA代码可以访问Debug()方法(这比从单元格中调用该方法更有用)

我肯定我错过了一些简单的东西

编辑2010/09/07:我已经更新了代码片段,以包含Jim建议的[ProgId]属性;问题依然存在。我可以在注册表中看到对象:

[HKEY_CLASSES_ROOT\CLSID\{EAC0992E-AC39-4126-B851-A57BA3FA80B8}]
@="NLog4VBA.Logger"

[HKEY_CLASSES_ROOT\CLSID\{EAC0992E-AC39-4126-B851-A57BA3FA80B8}\Implemented Categories]

[HKEY_CLASSES_ROOT\CLSID\{EAC0992E-AC39-4126-B851-A57BA3FA80B8}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]

[HKEY_CLASSES_ROOT\CLSID\{EAC0992E-AC39-4126-B851-A57BA3FA80B8}\InprocServer32]
@="C:\\WINDOWS\\system32\\mscoree.dll"
"ThreadingModel"="Both"
"Class"="NLog4VBA.Logger"
"Assembly"="NLog4VBA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="file:///C:/projects/nlog4vba/NLog4VBA/bin/Debug/NLog4VBA.dll"

[HKEY_CLASSES_ROOT\CLSID\{EAC0992E-AC39-4126-B851-A57BA3FA80B8}\InprocServer32\1.0.0.0]
"Class"="NLog4VBA.Logger"
"Assembly"="NLog4VBA, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"
"RuntimeVersion"="v2.0.50727"
"CodeBase"="file:///C:/projects/nlog4vba/NLog4VBA/bin/Debug/NLog4VBA.dll"

[HKEY_CLASSES_ROOT\CLSID\{EAC0992E-AC39-4126-B851-A57BA3FA80B8}\ProgId]
@="NLog4VBA.Logger"

[HKEY_CLASSES_ROOT\CLSID\{EAC0992E-AC39-4126-B851-A57BA3FA80B8}\Programmable]
此外,ProgID在“加载项”对话框中可见:


我仍然不知道为什么这不起作用:-(

COMAddIns集合通过数字索引或作为所需组件的ProgId的字符串进行索引。请确保您的ProgId实际上是“NLog4VBA.Logger”(通过.NET中的
ProgId
属性),并验证对象是否已使用此id注册(您可以很容易地在注册表中查找指定的GUID)。

结果是我的VBA代码完全错误;答案是:

我想你需要做点什么 像这样:

[Guid("EAC0992E-AC39-4126-B851-A57BA3FA80B8")]
[ComVisible(true)]
[ProgId("NLog4VBA.Logger")]
[ClassInterface(ClassInterfaceType.AutoDual)]
public class Logger
{
    public double Debug(string context, string message)
    {
        Trace.WriteLine(message);
        return message.Length;
    }

    [ComRegisterFunctionAttribute]
    public static void RegisterFunction(Type type)
    {
        Registry.ClassesRoot.CreateSubKey(GetSubKeyName(type, "Programmable"));
        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)
    {
        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();
    }
}
Private Sub CommandButton1_Click()

        'Declare an object variable using the referenced lib.
        'if all is well, intellisense will tell you what the proper object name is:
        Dim objLogger as NLog4VBA

        'Create an instance of the object
        Set objLogger = New NLog4VBA

        'Now use the object
        objLogger.Object.Debug "My Context", "My Message"
End Sub

吉姆:谢谢你的建议,但还是失败了。我已经用属性更新了代码段,并包含了相关注册表项的转储。看看奥斯特曼的博客,他解释了ProgId工作所需的最小注册表项集:-看起来你缺少HKEY_类\u根\NLog4VBA.Logger和HKEY_类\u根\NLog4VBA.Logger\CLSID,实际上是两个Larry调用的“progid所需的最小注册表项集”。您可以尝试手动添加这两个注册表项作为开始,看看是否有帮助。