You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用RegistrationServices.RegisterAssembly时依赖项找不到的问题求助

问题:COM注册时依赖程序集加载失败,手动regasm操作正常

开发插件解决方案,所有插件通过统一安装程序部署,每个插件实现System.Configuration.Install.Installer,由ManagedInstallerClass.InstallHelper执行安装。其中一个插件需要COM注册包含入口的MyInterface.dll,在Installer类中调用RegistrationServices.RegisterAssembly进行注册时,出现错误:无法加载程序集AddInAssistant, Version=19.23.516.10987, Culture=neutral, PublicKeyToken=null或其依赖项。该依赖DLL已存在于安装目录且版本匹配,但程序集绑定日志(FusLog)显示安装程序在自身目录和目标目录均未找到它,手动使用regasm.exe /codebase注册则无问题。

安装类调用代码

List<string> copiedFiles = CopyFiles(Session.InstallPath, installPath);

var interfaceDllPath = copiedFiles.FirstOrDefault(cf => cf.EndsWith(@"\MyInterface.dll"));
if (interfaceDllPath != null)
{
    try
    {
        var comRegistrar = new ComRegistrar(interfaceDllPath);
        bool registerSuccess = comRegistrar.Register();
        if (registerSuccess)
        {
            Context.LogMessage($"Successfully COM-registered DLL at {interfaceDllPath}");
            Context.LogMessage("Enabling AddIn Auto-Load");
            SetAutoLoadEntry();
        }
        else
        {
            Context.LogMessage("Failed to com-register DLL (unknown reason).");
        }
    }
    catch (Exception exception)
    {
        Context.LogMessage($"Error during com-registration: {exception}");
        throw;
    }
}

ComRegistrar实现代码

public class ComRegistrar
{
    private readonly string _assemblyPath;

    public ComRegistrar(string assemblyPath)
    {
        if (string.IsNullOrWhiteSpace(assemblyPath))
            throw new ArgumentException("Error in ComRegistrar: assemblyPath cannot be null or empty.");

        _assemblyPath = assemblyPath;
    }
    public bool Register()
    {
        if (String.IsNullOrEmpty(_assemblyPath))
        {
            throw new ArgumentException("Error in ComRegistrar: assemblyPath cannot be null or empty.");
        }
        RegistrationServices registrationService = new RegistrationServices();
        return registrationService.RegisterAssembly(Assembly.LoadFile(_assemblyPath), AssemblyRegistrationFlags.SetCodeBase);
    }

    public bool Unregister()
    {
        if (String.IsNullOrEmpty(_assemblyPath))
        {
            throw new ArgumentException("Error in ComRegistrar: assemblyPath cannot be null or empty.");
        }
        RegistrationServices registrationService = new RegistrationServices();
        registrationService.UnregisterAssembly(Assembly.LoadFile(_assemblyPath));

        //Force GarbageCollect to close file handle on Assembly before uninstall.
        registrationService = null;
        GC.Collect();
        GC.WaitForPendingFinalizers();
        return true;
    }
}

程序集绑定日志(FusLog)

*** Assembly Binder Log Entry  (21.09.2023 @ 14:23:18) ***

The operation failed.
Bind result: hr = 0x80070002. The system cannot find the file specified.

Assembly manager loaded from:  C:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
Running under executable  C:\1_Develop_\Repos\External.Interfaces\Interfaces.Installer\Interfaces.Installer\UI.TestShell\bin\Debug\TDM Interfaces Installer.exe
--- A detailed error log follows. 

=== Pre-bind state information ===
LOG: DisplayName = AddInAssistant, Version=19.23.516.10987, Culture=neutral, PublicKeyToken=null
 (Fully-specified)
LOG: Appbase = file:///C:/1_Develop_/Repos/External.Interfaces/Interfaces.Installer/Interfaces.Installer/UI.TestShell/bin/Debug/
LOG: Initial PrivatePath = NULL
LOG: Dynamic Base = NULL
LOG: Cache Base = NULL
LOG: AppName = TDM Interfaces Installer.exe
Calling assembly : TDMInterface, Version=2023.0.0.1, Culture=neutral, PublicKeyToken=null.

LOG: This bind starts in LoadFrom load context.
WRN: Native image will not be probed in LoadFrom context. Native image will only be probed in default load context, like with Assembly.Load().
LOG: Using application configuration file: C:\1_Develop_\Repos\External.Interfaces\Interfaces.Installer\Interfaces.Installer\UI.TestShell\bin\Debug\TDM Interfaces Installer.exe.Config
LOG: Using host configuration file: 
LOG: Using machine configuration file from C:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
LOG: Policy not being applied to reference at this time (private, custom, partial, or location-based assembly bind).
LOG: Attempting download of new URL file:///C:/1_Develop_/Repos/External.Interfaces/Interfaces.Installer/Interfaces.Installer/UI.TestShell/bin/Debug/AddInAssistant.DLL.
LOG: Attempting download of new URL file:///C:/1_Develop_/Repos/External.Interfaces/Interfaces.Installer/Interfaces.Installer/UI.TestShell/bin/Debug/AddInAssistant/AddInAssistant.DLL.
LOG: Attempting download of new URL file:///C:/1_Develop_/Repos/External.Interfaces/Interfaces.Installer/Interfaces.Installer/UI.TestShell/bin/Debug/AddInAssistant.EXE.
LOG: Attempting download of new URL file:///C:/1_Develop_/Repos/External.Interfaces/Interfaces.Installer/Interfaces.Installer/UI.TestShell/bin/Debug/AddInAssistant/AddInAssistant.EXE.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/ParentSoftware/AddIns/MyPluginFolder/AddInAssistant.DLL.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/ParentSoftware/AddIns/MyPluginFolder/AddInAssistant/AddInAssistant.DLL.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/ParentSoftware/AddIns/MyPluginFolder/AddInAssistant.EXE.
LOG: Attempting download of new URL file:///C:/Program Files (x86)/ParentSoftware/AddIns/MyPluginFolder/AddInAssistant/AddInAssistant.EXE.
LOG: All probing URLs attempted and failed.

解决方案

问题根源

使用Assembly.LoadFile加载程序集时,CLR的LoadFrom上下文不会自动探测目标程序集所在目录的依赖项;而手动执行regasm.exe时,regasm会自动将当前工作目录切换到目标DLL所在路径,CLR会优先探测当前目录的依赖,因此不会出现加载失败。


方案1:临时切换工作目录到目标安装目录

注册前将当前工作目录切换到MyInterface.dll所在的安装目录,注册完成后恢复原目录,让CLR自动探测该目录下的依赖:

public bool Register()
{
    if (String.IsNullOrEmpty(_assemblyPath))
    {
        throw new ArgumentException("Error in ComRegistrar: assemblyPath cannot be null or empty.");
    }
    
    // 保存原始工作目录
    var originalWorkingDir = Directory.GetCurrentDirectory();
    var targetAssemblyDir = Path.GetDirectoryName(_assemblyPath);
    
    try
    {
        // 切换到目标程序集所在目录
        Directory.SetCurrentDirectory(targetAssemblyDir);
        
        RegistrationServices registrationService = new RegistrationServices();
        // 使用Assembly.LoadFrom代替LoadFile,更适合探测当前目录依赖
        return registrationService.RegisterAssembly(Assembly.LoadFrom(_assemblyPath), AssemblyRegistrationFlags.SetCodeBase);
    }
    finally
    {
        // 恢复原始工作目录,避免影响其他操作
        Directory.SetCurrentDirectory(originalWorkingDir);
    }
}

优点:实现简单,完全利用CLR的默认探测逻辑,无需额外处理依赖加载;
注意:多线程场景下需注意工作目录切换的并发问题,确保同一时间只有一个操作切换目录。


方案2:手动处理依赖加载(AssemblyResolve事件)

通过订阅AppDomain.CurrentDomain.AssemblyResolve事件,在依赖程序集加载失败时手动从目标目录加载:

public bool Register()
{
    if (String.IsNullOrEmpty(_assemblyPath))
    {
        throw new ArgumentException("Error in ComRegistrar: assemblyPath cannot be null or empty.");
    }
    
    var targetAssemblyDir = Path.GetDirectoryName(_assemblyPath);
    // 注册AssemblyResolve事件
    ResolveEventHandler resolveHandler = (sender, args) =>
    {
        var assemblyName = new AssemblyName(args.Name);
        var assemblyPath = Path.Combine(targetAssemblyDir, $"{assemblyName.Name}.dll");
        
        if (File.Exists(assemblyPath))
        {
            return Assembly.LoadFrom(assemblyPath);
        }
        return null;
    };
    
    AppDomain.CurrentDomain.AssemblyResolve += resolveHandler;
    
    try
    {
        RegistrationServices registrationService = new RegistrationServices();
        return registrationService.RegisterAssembly(Assembly.LoadFile(_assemblyPath), AssemblyRegistrationFlags.SetCodeBase);
    }
    finally
    {
        // 取消事件订阅,避免内存泄漏
        AppDomain.CurrentDomain.AssemblyResolve -= resolveHandler;
    }
}

优点:无需切换工作目录,适合复杂场景下的依赖加载;
注意:需确保事件处理逻辑能正确匹配依赖程序集的名称和路径,避免加载错误版本。


方案3:调用regasm.exe进程代替RegistrationServices

直接启动regasm.exe进程执行注册操作,完全模拟手动注册的流程,自动处理所有依赖:

using System.Diagnostics;
using System.Runtime.InteropServices;

public bool Register()
{
    if (String.IsNullOrEmpty(_assemblyPath))
    {
        throw new ArgumentException("Error in ComRegistrar: assemblyPath cannot be null or empty.");
    }
    
    // 获取对应.NET版本的regasm路径
    var regasmPath = Path.Combine(RuntimeEnvironment.GetRuntimeDirectory(), "regasm.exe");
    // 构造regasm参数:/codebase指定程序集路径,/silent静默执行
    var arguments = $"\"{_assemblyPath}\" /codebase /silent";
    
    var processStartInfo = new ProcessStartInfo(regasmPath, arguments)
    {
        UseShellExecute = false,
        CreateNoWindow = true,
        RedirectStandardError = true,
        RedirectStandardOutput = true,
        // 确保regasm以管理员权限运行(如果需要)
        Verb = "runas"
    };
    
    using (var process = Process.Start(processStartInfo))
    {
        process.WaitForExit();
        
        if (process.ExitCode != 0)
        {
            var errorMsg = process.StandardError.ReadToEnd();
            throw new InvalidOperationException($"Regasm注册失败: {errorMsg}");
        }
        return true;
    }
}

优点:兼容性最好,完全复现手动注册的成功逻辑,无需处理CLR加载上下文问题;
注意:需确保安装程序有足够权限执行regasm,且regasm路径对应正确的.NET版本(如32/64位)。


内容的提问来源于stack exchange,提问作者PouncingPiggy

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 18:45:53