Mik*_*ail 9 .net c# delphi com registration-free-com
TLDR:我正在尝试从.Net COM dll调用异步回调到Delphi客户端.exe,但这些似乎在免注册COM中无法正常工作,而同步回调确实有效,并且异步回调正在工作时不是一个免注册的COM.
我的全球案例是,我有一个外国的闭源.Net dll暴露了一些公共事件.我需要将这些事件传递给Delphi应用程序.所以我决定制作一个中间的.dll,它可以作为我的app和另一个dll之间的COM桥.当我的dll通过regasm注册时,它运行得很好,但是当我切换到免注册COM时情况变得更糟.我将我的情况缩短为可重复的小例子,它不依赖于其他dll,所以我将在下面发布它.
基于这个答案我创建了一个公共接口ICallbackHandler,我希望从Delphi客户端应用程序获得:
namespace ComDllNet
{
[ComVisible(true)]
[Guid("B6597243-2CC4-475B-BF78-427BEFE77346")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface ICallbackHandler
{
void Callback(int value);
}
[ComVisible(true)]
[Guid("E218BA19-C11A-4303-9788-5A124EAAB750")]
public interface IComServer
{
void SetHandler(ICallbackHandler handler);
void SyncCall();
void AsyncCall();
}
[ComVisible(true)]
[Guid("F25C66E7-E9EF-4214-90A6-3653304606D2")]
[ClassInterface(ClassInterfaceType.None)]
public sealed class ComServer : IComServer
{
private ICallbackHandler handler;
public void SetHandler(ICallbackHandler handler) { this.handler = handler; }
private int GetThreadInfo()
{
return Thread.CurrentThread.ManagedThreadId;
}
public void SyncCall()
{
this.handler.Callback(GetThreadInfo());
}
public void AsyncCall()
{
this.handler.Callback(GetThreadInfo());
Task.Run(() => {
for (int i = 0; i < 5; ++i)
{
Thread.Sleep(500);
this.handler.Callback(GetThreadInfo());
}
});
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后,我给dll一个强名,并通过Regasm.exe注册它.
现在我转向Delphi客户端.我使用Component > Import Component > Import a Type Library它创建了tlb包装器代码
ICallbackHandler = interface(IUnknown)
['{B6597243-2CC4-475B-BF78-427BEFE77346}']
function Callback(value: Integer): HResult; stdcall;
end;
IComServer = interface(IDispatch)
['{E218BA19-C11A-4303-9788-5A124EAAB750}']
procedure SetHandler(const handler: ICallbackHandler); safecall;
procedure SyncCall; safecall;
procedure AsyncCall; safecall;
end;
IComServerDisp = dispinterface
['{E218BA19-C11A-4303-9788-5A124EAAB750}']
procedure SetHandler(const handler: ICallbackHandler); dispid 1610743808;
procedure SyncCall; dispid 1610743809;
procedure AsyncCall; dispid 1610743810;
end;
Run Code Online (Sandbox Code Playgroud)
并创建了一个处理程序和一些带有两个按钮和备忘录的Form来测试事物:
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ComDllNet_TLB, StdCtrls;
type
THandler = class(TObject, IUnknown, ICallbackHandler)
private
FRefCount: Integer;
protected
function Callback(value: Integer): HResult; stdcall;
function QueryInterface(const IID: TGUID; out Obj): HRESULT; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
public
property RefCount: Integer read FRefCount;
end;
type
TForm1 = class(TForm)
Memo1: TMemo;
syncButton: TButton;
asyncButton: TButton;
procedure FormCreate(Sender: TObject);
procedure syncButtonClick(Sender: TObject);
procedure asyncButtonClick(Sender: TObject);
private
{ Private declarations }
handler : THandler;
server : IComServer;
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function THandler._AddRef: Integer;
begin
Inc(FRefCount);
Result := FRefCount;
end;
function THandler._Release: Integer;
begin
Dec(FRefCount);
if FRefCount = 0 then
begin
Destroy;
Result := 0;
Exit;
end;
Result := FRefCount;
end;
function THandler.QueryInterface(const IID: TGUID; out Obj): HRESULT;
const
E_NOINTERFACE = HRESULT($80004002);
begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
function THandler.Callback(value: Integer): HRESULT;
begin
Form1.Memo1.Lines.Add(IntToStr(value));
Result := 0;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
handler := THandler.Create();
server := CoComServer.Create();
server.SetHandler(handler);
end;
procedure TForm1.syncButtonClick(Sender: TObject);
begin
Form1.Memo1.Lines.Add('Begin sync call');
server.SyncCall();
Form1.Memo1.Lines.Add('End sync call');
end;
procedure TForm1.asyncButtonClick(Sender: TObject);
begin
Form1.Memo1.Lines.Add('Begin async call');
server.AsyncCall();
Form1.Memo1.Lines.Add('End async call');
end;
end.
Run Code Online (Sandbox Code Playgroud)
所以,我运行它,按下"同步"和"异步"按钮,一切都按预期工作.注意任务的线程ID如何在"结束异步调用"行之后出现(也因为有一些延迟Thread.Sleep):

第一部分结束.现在我切换到使用Rregistration-free(并排)COM.基于这个答案,我dependentAssembly在我的Delphi app清单中添加了一部分:
<dependency>
<dependentAssembly>
<assemblyIdentity name="ComDllNet" version="1.0.0.0" publicKeyToken="f31be709fd58b5ba" processorArchitecture="x86"/>
</dependentAssembly>
</dependency>
Run Code Online (Sandbox Code Playgroud)
使用mt.exe工具我为我的dll生成了一个清单:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
<assemblyIdentity name="ComDllNet" version="1.0.0.0" publicKeyToken="f31be709fd58b5ba" processorArchitecture="x86"/>
<clrClass clsid="{F25C66E7-E9EF-4214-90A6-3653304606D2}" progid="ComDllNet.ComServer" threadingModel="Both" name="ComDllNet.ComServer" runtimeVersion="v4.0.30319"/>
<file name="ComDllNet.dll" hashalg="SHA1"/>
</assembly>
Run Code Online (Sandbox Code Playgroud)
然后我取消注册dll并运行应用程序.我发现只有回调的同步部分正在工作:

编辑:请注意,您必须取消注册/tlb选项,否则它将继续在本地计算机上工作,就好像dll仍然已注册(请参阅参考资料).
我已经厌倦了很多事情,我不知道下一步该做什么.我盯着怀疑初始方法根本不起作用,我需要在Delphi应用程序端实现一些线程.但我不确定是什么以及如何.任何帮助,将不胜感激!
您必须注册该ICallbackHandler界面.因此,在您拥有clrClass元素的同一文件中,但作为元素的兄弟file,添加:
<comInterfaceExternalProxyStub iid="{B6597243-2CC4-475B-BF78-427BEFE77346}"
name="ICallbackHandler"
tlbid="{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"/>
Run Code Online (Sandbox Code Playgroud)
这告诉COM使用外部代理/存根,类型库封送程序({00020424-0000-0000-C000-000000000046}),它告诉类型库封送程序查找您的类型库({XXXXXXXX-XXXX-XXXX- XXXX-XXXXXXXXXXXX}).此GUID是程序集的GUID,可在项目的属性中找到(请检查AssemblyInfo.cs).
您需要生成此类型库.既然你想要免注册的COM,我认为TLBEXP.EXE非常适合你的账单,你可以把它设置为post build事件.
最后,您可以保留单独的类型库文件,也可以将其嵌入到程序集中.我建议你把它分开,如果你的组装很大,那就更好了.
无论哪种方式,您都需要将其放入清单中.这是使用单独的.TLB文件的示例:
<file name="ComDllNet.tlb">
<typelib tlbid="{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
version="1.0"
helpdir="."
flags=""/>
</file>
Run Code Online (Sandbox Code Playgroud)
如果嵌入了类型库,请将以下内容添加为<file name="ComDLLNet.dll"/>元素的子元素:
<typelib tlbid="{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
version="1.0"
helpdir="."
flags=""/>
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
1792 次 |
| 最近记录: |