Can I call a method which is placed in main application from dll code?
seems there is only one way to do it - create a callback object.
in your application you have to declare interface, wich describes your method, for example:
IMyMethodInterface = interface(IInterface)
procedure MyMethod(); stdcall;
end;
next you have to create class, wich implements this interface (and your method):
TMyMethodObject = class(TInterfacedObject, IMyMethodInterface)
public
procedure MyMethod(); stdcall;
end;
when you load DLL, you have to create TMyMethodObject instance and pass its IMyMethodInterface to dll; of course dll has to have corresponding method and export it (wich takes interface as parameter) SetMethodCallback wich stores interface reference:
vars:
var mmo : IMyMethodInterface;
dllHandle : THandle;
smc : procedure (mmi : IMyMethodInterface); stdcall;
code:
mmo := TMyMethodObject.Create();
dllHandle := LoadLibrary('mydll.dll');
smc := GetProcAddress(dllHandle, 'SetMethodCallback');
if assigned(smc) then
smc(mmo);
now, you can use IMyMethodInterface reference in your dll to call method.
of course you can statically link dll and use it directly:
procedure SetMethodInteface(mmi : IMyMethodInterface); stdcall; external 'mydll.dll';
here is an DLL sample code:
library Project3;
// uses YourMethodIntf.pas
{$R *.res}
var AppMethod : IMyMethodInterface;
procedure SetAppMethodCallback(mmi : IMyMethodInterface); stdcall;
begin
AppMethod := mmi;
end;
procedure AnotherDllMethod();
begin
//here you can use AppMethod.MyMethod();
end;
exports
SetAppMethodCallback name 'SetMethodcallback';
begin
end.
take into account that your mmo object (TMyMethodInterface) will not be destroyed until you set AppMethod in dll to nil (or FreeLibrary dll ), so be careful
Related
I have a dll that contains a class that implements a interface. The dll has an exported method that returns the interface.
I can explicit load the dll succefully, but when I try to use Free Library I get Access Violation. I did not tried use implicit link, because I need use the explicit mode.
If I just load the library and free right after, without geting the interface, everything works fine.
Dll
library Tef;
uses
uTTefFacade;
{$R *.res}
exports
CreateTef;
begin
end.
Interface in dll:
type
ITefFacade = interface
['{77691DD1-C6E9-4F75-951F-BFA1468DC36C}']
function IniciarTransacao(AParam: TTefIniciarTransacaoParamDTO): TTefIniciarTransacaoResultDTO;
end;
Class in dll:
type
TTefFacade = class (TInterfacedObject, ITefFacade)
private
function IniciarTransacao(AParam: TTefIniciarTransacaoParamDTO): TTefIniciarTransacaoResultDTO;
public
constructor Create;
destructor Free;
end;
function CreateTef: ITefFacade; export; stdcall;
function CreateTef: ITefFacade;
begin
Result := ITefFacade(TTefFacade.Create);
end;
Exe:
procedure TForm1.FormCreate(Sender: TObject);
var
CreateTef: function: ITefFacade; stdcall;
begin
try
FTef := nil;
FHTef := LoadLibrary('Tef.dll');
if (FHTef > 0) then
begin
#CreateTef := GetProcAddress(FHTef, 'CreateTef');
if (#CreateTef <> nil) then
FTef := CreateTef;
end;
if (FTef = nil) then
ShowMessage('Error.');
except
on E: Exception do
ShowMessage('Erro: ' + E.Message);
end;
end;
And here in the calling Free Library, access violation occurs.
procedure TForm1.FormDestroy(Sender: TObject);
begin
FreeLibrary(FHTef);
end;
You have to nil the FTef reference before releasing the DLL.
The object behind the interface lives in the DLL, you should respect this. If you try to unload the DLL without releasing the interface first, there will be problems when the object is accessed after the unload (such as when Delphi auto-nils the reference when it goes out of scope).
I am modulating my application to work with separate modules (plugin).
I have already successfully made my EXE application read and load the plugins, including the forms.
Now I need to do the inverse, export functions from the executable to DLL.
Example:
Inside my executable, it has a TMemo component. I want to create a function like this
function GetMemo(): widestring;
In my idea, whoever wrote the DLL plugin, when calling the function GetMemo(), would already take the contents of the TMemo in DLL.
It is possible?
The simplest way to handle this is to define a record of function pointers, and then have the EXE pass an instance of that record to each plugin while initializing it. The EXE can then implement the functions as needed and pass them to the plugins, without actually exporting them from its PE exports table like a DLL would.
For example:
type
PPluginExeFunctions = ^PluginExeFunctions;
PluginExeFunctions = record
GetMemo: function: WideString; stdcall;
...
end;
function MyGetMemoFunc: WideString; stdcall;
begin
Result := Form1.Memo1.Text;
end;
...
var
ExeFuncs: PluginExeFunctions;
hPlugin: THandle;
InitFunc: procedure(ExeFuncs: PPluginExeFunctions); stdcall;
begin
ExeFuncs.GetMemo := #MyGetMemoFunc;
...
hPlugin := LoadLibrary('plugin.dll');
#InitFunc := GetProcAddress(hPlugin, 'InitializePlugin');
InitFunc(#ExeFuncs);
...
end;
var
ExeFuncs: PluginExeFunctions;
procedure InitializePlugin(pExeFuncs: PPluginExeFunctions); stdcall;
begin
ExeFuncs := pExeFuncs^;
end;
procedure DoSomething;
var
S: WideString;
begin
S := ExeFuncs.GetMemo();
...
end;
unit uDoingExport;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm1 = class(TForm)
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
procedure testproc; stdcall;
implementation
{$R *.dfm}
procedure testproc;
begin
ShowMessage('testproc');
End;
exports
testproc;
end.
I simply added the method I want to publish from within my EXE in the unit's Interface, and on the Implementation I added exports (method name). I am using stdcall not cdecl.
In my child, I can loadlibrary the exe file... or you can go a little crazy like Apache does, and in the previous code, add a loadlibrary, which loads a DLL, which intern can loadlibrary the caller.
My point was to show, your EXE is simply like a DLL (just a different binary header) and vise versa. Just slap EXPORTS. Proof it works, I ran tdump against the EXE:
Exports from ProjDoingExport.exe
1 exported name(s), 1 export addresse(s). Ordinal base is 1.
Sorted by Name:
RVA Ord. Hint Name
-------- ---- ---- ----
0005294C 1 0000 testproc
I know, a late answer, but, a great question!
Our VCL Delphi application has a number of grids that we need to start to interact with via UIAutomation. There are a number of issues, not least that the TStringGrid doesn't implement any of the IUIAutomation patterns (IGridProvider or ITableProvider, or for that matter even IValueProvider).
I am trying to find out what I need to added to a TStringGrid to allow it to implement the providers (which in the System.Windows.Automation.Provider namespace in .NET).
Here are my steps ...
(The actual files are too large to post all of them, so this is a distillation of the major points).
ALSO - This still has major issues, probably of my own making, but it is enough for me to make progress.
Get the UIAutomationCore.idl (mine was as part of a Visual Studio installation).
Run midl.exe to create the type library.
Run tlibimp.exe from the command-line (as Delphi doesn't seem to like the .tlb created in step 3), and create the UIAutomationCore_TLB.pas file. This ends up being a rather large file, with all of the COM parts of UIAutomationCore defined in pascal.
There are methods in the original DLL that are not COM, and these need to be defined as well. These I added to the generated file from Step 3 - although probably they should be defined elsewhere in case this file is regenerated.
function UiaHostProviderFromHwnd(hwnd: HWND; provider: IRawElementProviderSimple): LRESULT; stdcall; external 'UIAutomationCore.dll' name 'UiaHostProviderFromHwnd';
function UiaReturnRawElementProvider(hwnd: HWND; wParam: WPARAM; lParam: LPARAM; element : IRawElementProviderSimple) : LRESULT; stdcall; external 'UIAutomationCore.dll' name 'UiaReturnRawElementProvider';
The component needs to implement the IRawElementProviderSimple interface, as well as any other providers - in the example case I have used ISelectionProvide, in order to illustrate what I did.
// IRawElementProviderSimple
function Get_ProviderOptions(out pRetVal: ProviderOptions): HResult; stdcall;
function GetPatternProvider(patternId: SYSINT; out pRetVal: IUnknown): HResult; stdcall;
function GetPropertyValue(propertyId: SYSINT; out pRetVal: OleVariant): HResult; stdcall;
function Get_HostRawElementProvider(out pRetVal: IRawElementProviderSimple): HResult; stdcall;
// ISelectionProvider
function GetSelection(out pRetVal: PSafeArray): HResult; stdcall;
function Get_CanSelectMultiple(out pRetVal: Integer): HResult; stdcall;
function Get_IsSelectionRequired(out pRetVal: Integer): HResult; stdcall;
These are implemented as follows ..
function TAutomationStringGrid.Get_ProviderOptions(
out pRetVal: ProviderOptions): HResult;
begin
pRetVal:= ProviderOptions_ClientSideProvider;
Result := S_OK;
end;
function TAutomationStringGrid.GetPatternProvider(patternId: SYSINT;
out pRetVal: IInterface): HResult;
begin
pRetval := nil;
if (patternID = UIA_SelectionPatternId) then
begin
result := QueryInterface(ISelectionProvider, pRetVal);
end
else
result := S_OK;
end;
function TAutomationStringGrid.GetPropertyValue(propertyId: SYSINT;
out pRetVal: OleVariant): HResult;
begin
if(propertyId = UIA_ControlTypePropertyId) then
begin
TVarData(pRetVal).VType := varWord;
TVarData(pRetVal).VWord := UIA_DataGridControlTypeId;
end;
result := S_OK;
end;
function TAutomationStringGrid.Get_HostRawElementProvider(
out pRetVal: IRawElementProviderSimple): HResult;
begin
result := UiaHostProviderFromHwnd (self.Handle, pRetVal);
end;
function TAutomationStringGrid.GetSelection(out pRetVal: PSafeArray): HResult;
begin
end;
function TAutomationStringGrid.Get_CanSelectMultiple(
out pRetVal: Integer): HResult;
begin
end;
function TAutomationStringGrid.Get_IsSelectionRequired(
out pRetVal: Integer): HResult;
begin
end;
In order to actually get the control, the WM_GETOBJECT message needs to be handled ...
procedure WMGetObject(var Message: TMessage); message WM_GETOBJECT;
This is implemented as follows ..
procedure TAutomationStringGrid.WMGetObject(var Message: TMessage);
begin
if (Message.Msg = WM_GETOBJECT) then
begin
QueryInterface(IID_IRawElementProviderSimple, FRawElementProviderSimple);
message.Result := UiaReturnRawElementProvider(self.Handle, Message.WParam, Message.LParam, FRawElementProviderSimple);
end
else
Message.Result := DefWindowProc(self.Handle, Message.Msg, Message.WParam, Message.LParam);
end;
Although I cannot provide the specific steps required to implement the automation capabilities you require on TStringGrid, I can say that based on the comments you have almost everything you need.
The article you found describing the basic implementation of UI Automation support for Win32 Unmanaged code is a good place to start.
The questions over what is and is not exposed through the IDL in UIAutomationCore.DLL are then addressed by the fact that the DLL in question is itself intended to be consumed by unmanaged code. It contains no managed code itself. At least not that is involved in an unmanaged use case.
What is does contain is a COM interface described by IDL, but also some functions simply exported by the DLL. As far as I know, IDL does not describe the exports table of a DLL. Even if it is capable of doing so, in the case of this DLL it does not (at least not in all cases).
For example, the UiaHostProviderFromHwnd() function that you have mentioned is a simple DLL export. Some of the additional functions exported in this way are described in this MSDN blog post describing creating a .net interop interface for this library. In that article they are called "flat API methods".
Using PE Explorer I can see 81 such functions exported by the UIAutomationCore.dll library.
Unfortunately a DLL exports table does not describe the parameters or return types of any exported function, only the names. So, in addition to the type library (produced from the IDL) you will also need to locate and convert the UIAutomationCore.h header file for use with Delphi (i.e. Pascal).
You should then have everything need to implement the UI Automation capabilities for any VCL control you desire.
After importing into delphi the com dll file, the delphi in turn generated a lib_tlb.pas file.
Inspecting the file it shows
Iinterface1 = interface(IDispatch)
function func: Integer; safecall;
procedure proc(param:Iinterface1);
end;
Cointerface1 = class
class function Create: Iinterface;
class function CreateRemote(const MachineName: string): Iinterface1;
end;
Tinterface1 = class(TOleServer)
function func: Integer;
procedure proc(param:Iinterface1);
end;
Now its clear to see that there is no connection between Tinterface1 and Iinterface1.
The problem comes when one calls proc with an Tinterface1. this will not compile Tinterface1 does not inheritce Iinterface1.
So what suggested to do? change the lib that is auto generated? or do you have a better idea of what to do when wanting to pass Tinterface1 to proc.
The example is a simplification of the code, in the code there is anther object that needs to be the one to be passed to proc, however that proc knows only its interface, which is the same problem.
update: as it seems the manual of the com dll file, says that proc should be
procedure proc(param:^Tinterface1);
where the interface is only in delphi point of view.
TInterface1.Proc() is expecting a pre-existing IInterface1 object to be passed to it as input. Use Cointerface1.Create() to create that object, eg:
var
intf: Iinterface1;
begin
intf := Cointerface1.Create;
TheOleServerInstance.proc(intf);
end;
Tinterface1 is a TOleServer descendant that does not directly inherit from Iinterface1 (but it does wrap an Iinterface1 internally), so you have to cast it whenever you want to pass it where an Iinterface1 is expected, eg:
var
intf: Iinterface1;
svr: Iinterface1;
begin
intf := Cointerface1.Create;
if Supports(TheOleServerInstance, Iinterface1, svr) then
intf.proc(svr);
end;
I know Delphi XE2 has the new TVirtualInterface for creating implementations of an interface at runtime. Unfortunately I am not using XE2 and I'm wondering what kind of hackery is involved in doing this sort of thing in older versions of Delphi.
Lets say I have the following interface:
IMyInterface = interface
['{8A827997-0058-4756-B02D-8DCDD32B7607}']
procedure Go;
end;
Is it possible to bind to this interface at runtime without the help of the compiler?
TMyClass = class(TObject, IInterface)
public
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
function _AddRef: Integer; stdcall;
function _Release: Integer; stdcall;
procedure Go; //I want to dynamically bind IMyInterface.Go here
end;
I've tried a simple hard cast:
var MyInterface: IMyInterface;
begin
MyInterface := IMyInterface(TMyClass.Create);
end;
but the compiler prevents this.
Then I tried an as cast and it at least compiled:
MyInterface := TMyClass.Create as IMyInterface;
So I imagine the key is to get QueryInterface to return a valid pointer to an Implementation of the interface being queried. How would I go about constructing one at runtime?
I've dug through System.pas so I'm at least vaguely familiar with how GetInterface, GetInterfaceEntry and InvokeImplGetter work. (thankfully Embacadero chose to leave the pascal source along with the optimized assembly). I may not be reading it right but it appears that there can be interface entries with an offset of zero in which case there is an alternative means of assigning the interface using InvokeImplGetter.
My ultimate goal is to simulate some of the abilities of dynamic proxies and mocks that are available in languages with reflection support. If I can successfully bind to an object that has the same method names and signatures as the interface it would be a big first step. Is this even possible or am I barking up the wrong tree?
Adding support for an interface to an existing class at runtime can theoretically be done, but it would be really tricky, and it would require D2010 or later for RTTI support.
Each class has a VMT, and the VMT has an interface table pointer. (See the implementation of TObject.GetInterfaceTable.) The interface table contains interface entries, which contain some metadata, including the GUID, and a pointer to the interface vtable itself. If you really wanted to, you could create a copy of the interface table, (DO NOT do this the original one; you're likely to end up corrupting memory!) add a new entry to it containing a new interface vtable with the pointers pointing to the correct methods, (which you could match by looking them up with RTTI,) and then change the class's interface table pointer to point to the new table.
Be very careful. This sort of work is really not for the faint of heart, and it seems to me it's of kind of limited utility. But yes, it's possible.
I'm not sure, what you want to accomplish and why you want to dynamically bind that interface, but here is a way to do it (don't know if it fits your need):
type
IMyInterface = interface
['{8A827997-0058-4756-B02D-8DCDD32B7607}']
procedure Go;
end;
TMyClass = class(TInterfacedObject, IInterface)
private
FEnabled: Boolean;
protected
property Enabled: Boolean read FEnabled;
public
constructor Create(AEnabled: Boolean);
function QueryInterface(const IID: TGUID; out Obj): HResult; stdcall;
procedure Go; //I want to dynamically bind IMyInterface.Go here
end;
TMyInterfaceWrapper = class(TAggregatedObject, IMyInterface)
private
FMyClass: TMyClass;
protected
property MyClass: TMyClass read FMyClass implements IMyInterface;
public
constructor Create(AMyClass: TMyClass);
end;
constructor TMyInterfaceWrapper.Create(AMyClass: TMyClass);
begin
inherited Create(AMyClass);
FMyClass := AMyClass;
end;
constructor TMyClass.Create(AEnabled: Boolean);
begin
inherited Create;
FEnabled := AEnabled;
end;
procedure TMyClass.Go;
begin
ShowMessage('Go');
end;
function TMyClass.QueryInterface(const IID: TGUID; out Obj): HResult;
begin
if Enabled and (IID = IMyInterface) then begin
IMyInterface(obj) := TMyInterfaceWrapper.Create(Self);
result := 0;
end
else begin
if GetInterface(IID, Obj) then
Result := 0
else
Result := E_NOINTERFACE;
end;
end;
And this is the corresponding test code:
var
intf: IInterface;
my: IMyInterface;
begin
intf := TMyClass.Create(false);
if Supports(intf, IMyInterface, my) then
ShowMessage('wrong');
intf := TMyClass.Create(true);
if Supports(intf, IMyInterface, my) then
my.Go;
end;