在没有管理员权限的情况下注册.Net COM DLL/regasm

在没有管理员权限的情况下注册.Net COM DLL/regasm,.net,vba,dll,registry,regasm,.net,Vba,Dll,Registry,Regasm,最近,我用C#编写了一个类库,用于Office应用程序,包括一个供大约70人使用的关键访问应用程序。对于具有管理员权限的用户来说,注册DLL很简单,但是让DLL在其他机器上工作是有问题的 注册DLL以使用管理员权限 在VisualStudio中构建DLL。确保在项目的应用程序选项卡上选择了这些选项: 输出类型:类库 程序集信息:使程序集COM可见:选中 使用提升的命令提示符注册程序集: RegAsm YourDll.dll/tlb/codebase 在VBA:Tools-->引用中添加

最近,我用C#编写了一个类库,用于Office应用程序,包括一个供大约70人使用的关键访问应用程序。对于具有管理员权限的用户来说,注册DLL很简单,但是让DLL在其他机器上工作是有问题的

注册DLL以使用管理员权限
  • 在VisualStudio中构建DLL。确保在项目的应用程序选项卡上选择了这些选项:
    • 输出类型:类库
    • 程序集信息:使程序集COM可见:选中
  • 使用提升的命令提示符注册程序集:
    • RegAsm YourDll.dll/tlb/codebase
  • 在VBA:Tools-->引用中添加对
    YourDll.tlb
    的引用
  • 验证是否可以创建对象的实例
发生了什么事? Regasm在这里做了几件事。首先,它创建了一个类型库(YourDLL.tlb),它提供了有关DLL中类的信息。其次,它将有关库的信息存储在注册表中,以便当您请求将类实例化为对象时,系统“知道”您的意思

要查看Regasm添加的注册表项,可以使用
/regfile
参数运行它:

Regasm YourDLL.dll/codebase/regfile

(带有
/tlb
参数的
/regfile
选项无效。)

如果
/codebase
选项告诉Regasm包含有关
YourDLL.dll
在磁盘上的位置的信息,这对于创建对象很重要

如果在文本编辑器中打开
YourDLL.reg
,您将看到Regasm正在添加到注册表的条目:
HKEY_Classes\u Root
(这实际上只是重定向到
HKLM\Software\Classes
)。不幸的是,您需要管理员权限才能修改
HKLM
,因此这对我们的其他用户不起作用

还有一些其他线程(例如,,)讨论了该问题,但它们的解决方案很复杂(例如,需要注册表重定向)或不完整(假设您已经知道至少一半的答案),或者不能在64/32位混合环境中工作(例如,Win64、Office32)

那么,如何在没有管理权限的情况下为当前用户注册在Visual Studio中创建的COM DLL,以便在VBA 32和64位环境中使用?

设置注册表文件 为了在32位和64位环境中注册组件,我们需要修改在测试用例中创建的注册表文件。用你最喜欢的文本编辑器打开它。条目应如下所示:

[HKEY_CLASSES_ROOT\\YourAssembly.Class] @="YourAssembly.Class" [HKEY_CLASSES_ROOT\\YourAssembly.Class\\CLSID] @="{YourClassGUID}"
Public Sub TestYourDLL()
  Dim x as AssemblyName.ClassName
  Set x = New AssemblyName.ClassName
  Debug.Print "Apparently this worked."
End Sub
确保事情不会发生 为了确保我们的更改有效(并且您的成功不仅仅是因为您最初向regasm进行了注册),我们需要确保VBA现在无法创建对象

打开您喜爱的VBA应用程序,并添加对
YourDLL.tlb
的引用。创建一个类似以下内容的新过程:

[HKEY_CLASSES_ROOT\\YourAssembly.Class] @="YourAssembly.Class" [HKEY_CLASSES_ROOT\\YourAssembly.Class\\CLSID] @="{YourClassGUID}"
Public Sub TestYourDLL()
  Dim x as AssemblyName.ClassName
  Set x = New AssemblyName.ClassName
  Debug.Print "Apparently this worked."
End Sub
运行
TestYourDLL
。您应该会收到以下错误:

Run-time error '429': ActiveX component can't create object 运行时错误“429”: ActiveX组件无法创建对象 (如果未收到错误,则DLL仍在注册中。)

保存并退出VBA应用程序

确保他们工作 现在,运行前面创建的
YourDLL.reg
,将条目导入注册表。(如果您收到“拒绝访问”消息,则您忘记从
HKEY\U CLASSES\U ROOT
更改为
HKEY\U CURRENT\U USER\Software\CLASSES

再次打开VBA应用程序,然后运行
TestYourDLL
。现在,您应该在直接窗口中看到“显然这起作用了”。祝贺你已经注册了DLL!(如果出现“自动错误:系统找不到指定的文件”类型错误,则注册表文件可能缺少
Codebase
项,或者它们没有指向DLL的实际位置。)

附加步骤
在我的例子中,我将在我的应用程序旁边的一堆其他用户的计算机上安装DLL,因此在安装时,我将更新
CodeBase
值以引用我安装DLL的位置,并且我还将通过代码而不是通过执行.reg文件来安装注册表项。但是,现在我知道了所需的条目,这样做很简单。

C怀特的答案很好,如果你能在每台计算机上手动操作的话

为了自动添加必要的注册表项,我使用以下代码。它在64位Windows上占32位Office,可以在以后进行清理

Public Sub RegisterDLL(DLLPath As String)
    Dim RegasmPath As String
    RegasmPath = "C:\Windows\Microsoft.NET\Framework\" & Dir("C:\Windows\Microsoft.NET\Framework\v4*", vbDirectory) & "\RegAsm.exe" 'Path to RegAsm, adjust to the version of the .Net framework you're targeting

    #If Win64 Then
        Const Win64 = True
    #Else
        Const Win64 = False
    #End If
    Dim LibraryPath As String
    LibraryPath = Left(DLLPath, Len(DLLPath) - 4)
    If Dir(DLLPath) = "" Then 'Check if file exists
        Err.Raise 1, Description:="Missing DLL!"
        Exit Sub
    End If
    CreateObject("WScript.Shell").Run """" & RegasmPath & """ """ & DLLPath & """ /codebase /regfile", 0, True 'Create .reg file using RegAsm
    Dim strNewRegPath As String
    If Not Win64 And Environ$("ProgramW6432") <> vbNullString Then
        strNewRegPath = "HKEY_CURRENT_USER\SOFTWARE\Classes\Wow6432Node" '32 bits Office on Win 64
    Else
        strNewRegPath = "HKEY_CURRENT_USER\SOFTWARE\Classes" 'Default registry path
    End If
    Dim strRegFileContent As String
    Dim fileNo As Integer
    fileNo = FreeFile
    Open LibraryPath & ".reg" For Binary Access Read Write As #fileNo
        strRegFileContent = String(LOF(fileNo), vbNullChar)
        Get #fileNo, , strRegFileContent 'Read reg contents
        strRegFileContent = Replace(strRegFileContent, "HKEY_CLASSES_ROOT", strNewRegPath) 'Change reg path
        Put #fileNo, 1, strRegFileContent 'Overwrite, always extends so no need to truncate
    Close #fileNo
    CreateObject("WScript.Shell").Run "regedit.exe /s """ & LibraryPath & ".reg""", 0, True  'Merge silently into registry
    Kill LibraryPath & ".reg" 'Clean up registry
End Sub

这些脚本不需要引用,也不需要管理员访问。它们通常应该运行得很快,并允许在应用程序启动时进行设置,以及在应用程序关闭时进行清理(如果可能存在冲突)。

我不久前尝试过类似的过程,但它对我不起作用,因为生成的reg文件。这可能不会影响您,但您应该意识到这一限制…@PetrSr:我的经验是,
regasm/regfile
中完全缺失的条目只有
CodeBase
条目,但
regasm/CodeBase/regfile
修复了这一问题。(当然,32位注册也丢失了,这就是为什么我必须自己创建这些条目,使用基本信息作为基础。)你能找出哪些条目丢失了吗?TypeLib和接口ID丢失了,没有它们我无法让我的DLL工作。如中所述,使用
/regfile
选项将始终缺少这些条目。我试图直接从注册表中获取丢失的etnries,但结果证明这是一项相当繁琐的任务。我最终放弃了,并要求我的老板使用他的管理员帐户为我注册DLL…:-