TLDR: मैं .NET COM dll से डेल्फी क्लाइंट .exe से async कॉलबैक कॉल करने का प्रयास कर रहा हूं, लेकिन ये पंजीकरण-मुक्त COM में ठीक से काम नहीं कर रहा है, जबकि सिंक्रोनस कॉलबैक काम करते हैं, और एसिंक कॉलबैक काम कर रहे हैं जब यह एक reg-free COM नहीं है।पंजीकरण-मुक्त (साइड-बाय-साइड) में .NET COM dll से डेल्फी क्लाइंट से कॉलबैक COM
मेरा वैश्विक मामला यह है कि मेरे पास एक विदेशी बंद स्रोत है। नेट डेल जो कुछ सार्वजनिक घटनाओं को उजागर करता है। मुझे इन घटनाओं को डेल्फी ऐप में पास करने की आवश्यकता है। तो मैंने एक इंटरमीडिएट। डीएल बनाने का फैसला किया जो मेरे ऐप और एक और डीएल के बीच एक COM पुल के रूप में काम करेगा। यह ठीक काम करता है जब मेरा डीएल शासन के माध्यम से पंजीकृत होता है, लेकिन जब मैं reg-free COM पर स्विच करता हूं तो चीजें बदतर हो रही हैं। मैंने अपने मामले को छोटे प्रतिलिपि बनाने वाले उदाहरण में छोटा कर दिया जो अन्य डीएल पर निर्भर नहीं है, इसलिए मैं इसे नीचे पोस्ट कर दूंगा। तब
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 के माध्यम से इसे पंजीकृत:
पर this answer आधार पर मैं एक सार्वजनिक इंटरफ़ेस ICallbackHandler
जो मैं डेल्फी ग्राहक app से प्राप्त करने की उम्मीद कर दिया।
अब मैं डेल्फी क्लाइंट में बदल गया। मैं 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;
दिया और एक हैंडलर और दो बटन के साथ किसी न किसी रूप और ज्ञापन बातें परीक्षण करने के लिए बनाया Component > Import Component > Import a Type Library
का उपयोग कर कोड बनाने के लिए:
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.
तो, मैं चलाने यह, दबाया 'सिंक 'और' async 'बटन और सब कुछ अपेक्षित के रूप में काम किया। ध्यान दें कि कैसे एक टास्क के धागे आईडी (यह भी Thread.Sleep
की वजह से कुछ देरी के साथ) 'समाप्ति async कॉल' पंक्ति के बाद आता है:
भाग एक का अंत। अब मैं पंजीकरण-मुक्त (साइड-बाय-साइड) COM का उपयोग करने के लिए स्विच किया गया। पर this answer आधार पर मैं अपने डेल्फी अनुप्रयोग प्रकट करने के लिए dependentAssembly
हिस्सा कहा:
<dependency>
<dependentAssembly>
<assemblyIdentity name="ComDllNet" version="1.0.0.0" publicKeyToken="f31be709fd58b5ba" processorArchitecture="x86"/>
</dependentAssembly>
</dependency>
mt.exe tool का उपयोग करते हुए मैं अपने 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>
तब मैं dll अपंजीकृत और एप्लिकेशन को चलाने के। और मैंने पाया कि कॉलबैक का केवल तुल्यकालिक भागों काम कर रहे हैं:
संपादित करें: नोट आप /tlb
विकल्प के साथ पंजीकरण रद्द करना है, अन्यथा यह स्थानीय मशीन पर काम कर रहा है, जैसे कि dll था जारी रहेगा अभी भी पंजीकृत (see)।
मैंने पहले से ही कई चीजें थक चुकी हैं, और मुझे यकीन नहीं है कि आगे क्या करना है। मैं इस बात पर संदेह कर रहा हूं कि प्रारंभिक दृष्टिकोण बिल्कुल काम नहीं करना चाहिए और मुझे डेल्फी ऐप की ओर कुछ थ्रेडिंग लागू करने की आवश्यकता है। लेकिन मुझे यकीन नहीं है कि क्या और कैसे। किसी भी सहायता की सराहना की जाएगी!
प्रॉक्सी को पंजीकृत करना सही बात है, लेकिन @ मिखाइल को भी 'mandler.Callback (GetThreadInfo()) को कॉल नहीं करना चाहिए, सीधे पूल पूल से, COM marshaling के बिना। जब तक कि उनकी डेल्फी पक्ष यादृच्छिक धागे पर कॉलबैक प्राप्त करने की अपेक्षा न करे। – Noseratio
मैंने 'comInterfaceExternalProxyStub' तत्व जोड़ने की कोशिश की लेकिन डीडी तत्व को संदर्भित किया। अब मैंने आपके प्रस्ताव को .tlb के लिए sepearate 'file' तत्व के साथ करने की कोशिश की है और इसमें कुछ भी नहीं बदला है, लेकिन अधिक पर्याप्त लगता है, इसलिए मैं इसे रखना चाहूंगा। – Mikhail
@ नोसेरेटियो: यह वास्तव में मामला प्रतीत होता है। क्या आप उचित मार्शलिंग कार्यान्वयन पर मुझे मार्गदर्शन कर सकते हैं? – Mikhail