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

Spring4D在Delphi动态库中依赖注入的异常问题及解决咨询

Spring4D在Delphi动态库中依赖注入的问题解决

问题现象

主程序中注册IOutputHandler接口与实现类TOutputHandler,通过容器解析调用正常;但将主程序的容器传入动态库后,DLL中调用LocalContainer.Resolve<IOutputHandler>()抛出EResolveException,提示无法解析该类型。

原因分析

Delphi中,主程序与动态库作为独立模块,会为同一接口生成独立的TypeInfo实例。Spring4D默认以接口的TypeInfo作为服务注册与解析的标识,因此DLL中的IOutputHandler TypeInfo和主程序的不匹配,容器无法找到对应的注册项。

解决方案

方法1:基于GUID注册与解析服务

利用接口的GUID作为服务标识,绕过TypeInfo的差异问题,这是最直接的解决方案。

步骤:

  • 注册服务时,显式指定接口的GUID作为服务键
  • 解析服务时,同样通过GUID获取服务

完整代码示例

接口定义(可放在共享单元,主程序和DLL都引用)

type
  IOutputHandler = interface ['{B6C24A62-971F-4D44-85E5-61D1EFC09469}']
    procedure WriteMessage(const AMessage: string);
  end;

实现类(主程序中)

type
  TOutputHandler = class(TInterfacedObject, IOutputHandler)
    procedure WriteMessage(const AMessage: string);
  end;

implementation

procedure TOutputHandler.WriteMessage(const AMessage: string);
begin
  WriteLn(AMessage);
end;

主程序代码

uses
  Spring.Container, Spring.Guard;

type
  TInitDIProc = procedure(const AContainer: TContainer);
  TWriteMessageProc = procedure(const AMessage: string);

var
  container: TContainer;
  handle: THandle;
  initDI: TInitDIProc;
  writeMessage: TWriteMessageProc;
begin
  container := GlobalContainer();
  // 用GUID注册服务,替代默认的TypeInfo标识
  container.RegisterType<TOutputHandler>.Implements<IOutputHandler, '{B6C24A62-971F-4D44-85E5-61D1EFC09469}'>();
  container.Build();

  // 主程序内部调用验证
  var service := container.Resolve<IOutputHandler>();
  service.WriteMessage('Message from main application');

  // 加载DLL并传入容器
  handle := SafeLoadLibrary('DynamicLibrary.dll');
  if handle <> 0 then
  try
    initDI := TInitDIProc(GetProcAddress(handle, 'InitDI'));
    writeMessage := TWriteMessageProc(GetProcAddress(handle, 'WriteMessage'));
    if Assigned(initDI) and Assigned(writeMessage) then
    begin
      initDI(container);
      writeMessage('Message from dynamic library');
    end;
  finally
    FreeLibrary(handle);
  end;
end.

动态库代码

uses
  Spring.Container, Spring.Guard;

var
  LocalContainer: TContainer;

procedure InitDI(const AContainer: TContainer);
begin
  Guard.CheckNotNull(AContainer, 'AContainer');
  LocalContainer := AContainer;
end;

procedure WriteMessage(const AMessage: string);
var
  service: IOutputHandler;
begin
  // 通过GUID解析服务,避免TypeInfo差异导致的匹配失败
  service := LocalContainer.Resolve<IOutputHandler>('{B6C24A62-971F-4D44-85E5-61D1EFC09469}');
  service.WriteMessage(AMessage);
end;

exports
  InitDI,
  WriteMessage;

begin
end.

方法2:使用共享运行时包(BPL)

将接口定义放在一个运行时包中,主程序和DLL都引用该包。这样接口的TypeInfo会被所有引用包的模块共享,Spring4D的默认注册/解析方式即可正常工作。

注意:主程序和DLL必须使用相同版本的包,且包文件需正确部署到程序运行目录。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.15 11:34:53