gpt4 book ai didi

c# - 在免注册(并排)COM 中从 .Net COM dll 回调到 Delphi 客户端

转载 作者:太空狗 更新时间:2023-10-29 17:51:52 25 4
gpt4 key购买 nike

TLDR:我正在尝试从 .Net COM dll 调用异步回调到 Delphi 客户端 .exe,但这些在免注册 COM 中似乎无法正常工作,而同步回调可以工作,并且当它不是无注册 COM 时,异步回调也工作。


我的全局案例是我有一个公开一些公共(public)事件的外国闭源 .Net dll。我需要将这些事件传递给 Delphi 应用程序。所以我决定制作一个中间 .dll,作为我的应用程序和另一个 dll 之间的 COM 桥梁。当我的 dll 通过 regasm 注册时它工作得很好,但是当我切换到无注册 COM 时情况变得更糟。我将我的案例缩短为不依赖于其他 dll 的小型可重现示例,因此我将在下面发布它。

基于 this answer我制作了一个公共(public)接口(interface) 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());
}
});
}
}
}

然后,我给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;

并创建了一个处理程序和一些带有两个按钮和备忘录的表单来测试:

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.

所以,我运行它,按下“同步”和“异步”按钮,一切都按预期进行。请注意任务的线程 ID 如何出现在“结束异步调用”行之后(由于 Thread.Sleep 也有一些延迟):

all works via registration-COM

第一节结束。现在我改用免注册(并行)COM。基于this answer我将 dependentAssembly 部分添加到我的 Delphi 应用程序 list 中:

<dependency>
<dependentAssembly>
<assemblyIdentity name="ComDllNet" version="1.0.0.0" publicKeyToken="f31be709fd58b5ba" processorArchitecture="x86"/>
</dependentAssembly>
</dependency>

使用 mt.exe tool我为我的 dll 生成了 list :

<?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>

然后我取消注册 dll 并运行该应用程序。而且我发现只有回调的同步部分在工作:

enter image description here

编辑: 请注意,您必须注销 /tlb 选项,否则它将继续在本地机器上工作,因为如果 dll 仍被注册(see)。

我已经厌倦了很多事情,我不确定下一步该做什么。我开始怀疑最初的方法根本不起作用,我需要在 Delphi 应用程序端实现一些线程。但我不确定是什么以及如何。任何帮助将不胜感激!

最佳答案

您必须注册 ICallbackHandler界面。因此,在您拥有 clrClass 的同一个文件中元素,但作为 file 的 sibling 元素,添加:

    <comInterfaceExternalProxyStub iid="{B6597243-2CC4-475B-BF78-427BEFE77346}"
name="ICallbackHandler"
tlbid="{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
proxyStubClsid32="{00020424-0000-0000-C000-000000000046}"/>

这告诉 COM 使用外部代理/ stub ,类型库编码器 ({00020424-0000-0000-C000-000000000046}),并告诉类型库编码器查找您的类型库 ({XXXXXXXX-XXXX -XXXX-XXXX-XXXXXXXXXXXX})。此 GUID 是程序集的 GUID,可在项目属性中找到(检查 AssemblyInfo.cs)。

您需要生成此类型库。由于您需要免注册 COM,我认为 TLBEXP.EXE 非常适合您,您可以将其设置为构建后事件。

最后,您可以保留一个单独的类型库文件,也可以将其嵌入您的程序集中。我建议您将它分开,如果您的程序集很大,则更是如此。

无论哪种方式,您都需要将其放入 list 中。下面是一个使用单独的 .TLB 文件的示例:

    <file name="ComDllNet.tlb">
<typelib tlbid="{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
version="1.0"
helpdir="."
flags=""/>
</file>

如果嵌入类型库,请将以下内容添加为 <file name="ComDLLNet.dll"/> 的子项元素:

        <typelib tlbid="{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}"
version="1.0"
helpdir="."
flags=""/>

关于c# - 在免注册(并排)COM 中从 .Net COM dll 回调到 Delphi 客户端,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/28738324/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com