Maintain DLL loaded even when process closed - delphi

I'm developing a DLL file that will be loaded by my EXE... So the EXE will call the first DLL procedure and when this procedure get loaded I want to keep it openned even if the EXE get closed. The example is, I have a DLL with timer showing a 'Hello World' message.
DLL Code:
uses
SysUtils,
Classes,
Dialogs,
ExtCtrls;
{$R *.res}
type
TMyTimer = Class(TTimer)
public
procedure OnMyTimer(Sender: TObject);
end;
procedure DllMessage; export;
var
MyTimer: TMyTimer;
begin
MyTimer := TMyTimer.Create(nil);
MyTimer.Interval := 10000;
MyTimer.OnTimer := MyTimer.OnMyTimer;
end;
procedure TMyTimer.OnMyTimer(Sender: TObject);
begin
ShowMessage('Hello World');
end;
exports DllMessage;
begin
end.
The EXE is loading like this:
procedure DllMessage; external 'Message.dll'
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
begin
DllMessage;
end;
When I close the EXE I want the DLL keep running and showing the message every 10 seconds... Is that possible?

DLLs are loaded into processes and cannot exist without a process to host them. So what you ask is not possible.
If you want to close your process, but continue to execute code, you will need to start a new and separate process to execute that code.

u need Atach a A DLL to another process,
and hook ur code to execute on your processs !
this method is called Dll Injection and Code Hook,
easy ways using madcodehook component
athttp://www.madshi.net/
example injection
http://help.madshi.net/DllInjecting.htm
example code hooking
http://help.madshi.net/ApiCodeHooking.htm
or
creanting ur ways
http://www.codeproject.com/Articles/4610/Three-Ways-to-Inject-Your-Code-into-Another-Proces

Related

function EXE to DLL (Delphi)

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!

Delphi FreeLibrary freezes when using TTask in DLL

Here is my code in DLL:
procedure TTaskTest;
begin
TTask.Run(
procedure
begin
Sleep(300);
end);
end;
exports TTaskTest;
After calling this method in host app, then call FreeLibrary will freeze host app.
After debug , I found that the program freezes at if TMonitor.Wait(FLock, Timeout) then in TLightweightEvent.WaitFor , but the debugger cannot step into TMonitor.Wait.
How to solve?
This issue was reported (RSP-13742 Problem with ITask, IFuture inside DLL).
It was closed "Works as Expected" with a remark:
To prevent this failure using ITask or IFuture from a DLL, the DLL will need to be using its own instance of TThreadPool in place of the default instance of TThreadPool.
Here is an example from Embarcadero how to handle it:
library TestLib;
uses
System.SysUtils,
System.Classes,
System.Threading;
{$R *.res}
VAR
tpool: TThreadPool;
procedure TestDelay;
begin
tpool := TThreadPool.Create;
try
TTask.Run(
procedure begin
Sleep(300);
end,
tpool
);
finally
FreeAndNil(tpool);
end;
end;
exports
TestDelay;
begin
end.
Another way is to create the threadpool when the library is loaded, and add a release procedure, which you call before calling FreeLibrary.
// In dll
procedure TestDelay;
begin
TTask.Run(
procedure begin
Sleep(300);
end,
tpool
);
end;
procedure ReleaseThreadPool;
begin
FreeAndNil(tpool);
end;
exports
TestDelay,ReleaseThreadPool;
begin
tpool := TThreadPool.Create;
end.

COM Server application and GetObject - reusing the running application

I'm trying to automate an application (Windows 8, Delphi XE.) For my testing I'm doing the following:
Created a small test application, consisting of a form and a memo (Form1)
Added a new ActiveX Object, CoClass name TestOLE, Threading mode Apartment, Instancing Multiple (as per this article.)
Added one method Method1 which only adds some text to the memo control in Form1
I then start the application and double click on a file named test.vbs which contains the following code:
dim obj
set obj = GetObject("", "Project1.TestOLE")
obj.AddSomeText "Hola mundo"
When the application is running, I see that a new form is created, the text is added and then it exits.
What I want to accomplish is that the opened application should have its memo text changed.
I've repeated creating new projects with both MultipleInstance and SingleInstance, and in an outburst of heuristic anger, I even changed the threading model to single, to no avail.
I see two flags in the type library editor: "Replaceable" and "Aggregatable." However, selecting "Replaceable" ends up in an error in the generated RIDL file.
I've been reading a lot about GetObject. It appears that its documentation is even wrong (it says you can omit the first parameter but I've found that doesn't work).
Is this the right way to write an automation server in Delphi that can be reused?
Well, I got it working (I hope.)
Reading more of the same article cited above, found the following:
Know how to implement servers that support GetActiveObject.
Adding a global object, and registering in the Running Object Table (ROT) accomplishes the desired task of having the COM call passed to the running application:
Project file:
program TestOLEProject3;
uses
Forms,
Unit1 in 'Unit1.pas' {Form1},
TestOLEProject3_TLB in 'TestOLEProject3_TLB.pas',
Unit2 in 'Unit2.pas' {TestOLE: CoClass},
Unit3 in 'Unit3.pas';
{$R *.TLB}
{$R *.res}
begin
Application.Initialize;
RegisterGlobalTestOLE;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
Unit2.pas:
unit Unit2;
{$WARN SYMBOL_PLATFORM OFF}
interface
uses
ComObj, ActiveX, TestOLEProject3_TLB, StdVcl;
type
TTestOLE = class(TAutoObject, ITestOLE)
protected
procedure Method1; safecall;
procedure Quit; safecall;
end;
implementation
uses ComServ, Unit1, Unit3;
procedure TTestOLE.Method1;
begin
Form1.Memo1.Lines.Add('Wheeee');
end;
procedure TTestOLE.Quit;
begin
RevokeGlobalTestOLE;
end;
initialization
TAutoObjectFactory.Create(ComServer, TTestOLE, CLASS_TestOLE, ciMultiInstance,
tmApartment);
end.
Unit3.pas (functions to register and unregister the global object):
unit Unit3;
interface
procedure RegisterGlobalTestOLE;
procedure RevokeGlobalTestOLE;
implementation
uses TestOLEProject3_TLB, ComObj, ActiveX;
var
GlobalTestOLEHandle: longint = 0;
procedure RegisterGlobalTestOLE;
var
GlobalTestOLE: ITestOLE;
begin
GlobalTestOLE := CoTestOLE.Create;
OleCheck(RegisterActiveObject(GlobalTestOLE, CLASS_TestOLE,
ACTIVEOBJECT_STRONG, GlobalTestOLEHandle));
end;
procedure RevokeGlobalTestOLE;
begin
if (GlobalTestOLEHandle <> 0) then
begin
OleCheck(RevokeActiveObject(GlobalTestOLEHandle, nil));
GlobalTestOLEHandle := 0;
end;
end;
end.

Delphi: Access violation after calling procedure in dll

I have created a procedure in a dll that opens a form and then prints a report.
This procedure works perfectly from an exe.
I have wrapped the unit that contains this procedure and forms in a dll and exported the procedure as follows:
{$R *.res}
Procedure PrintTopSellers; stdcall;
begin
Form1 := TForm1.create(nil);
GetMonth := TGetMonth.create(nil);
Form1.PrintTopSellers;
end;
exports PrintTopSellers;
begin
end.
Now I call this procedure PrintTopSellers from an exe as follows:
procedure TForm1.Button5Click(Sender: TObject);
type
TRead_iButton = function :integer;
var
DLL_Handle: THandle;
Read_iButton: TRead_iButton;
Begin
DLL_Handle := LoadLibrary('c:\Catalog.dll');
if DLL_Handle <> 0 then
begin
#Read_iButton:= GetProcAddress(DLL_Handle, 'PrintTopSellers');
Read_iButton;
end;
application.ProcessMessages;
FreeLibrary(DLL_Handle);
end;
The call to the procedure works perfectly. However, after I close the calling exe, I get an access violation - "Access violation at address 00BAC89C. Read of address 00BAC89C."
Appreciate any assistance. I am using Delphi 7.
Thanks
You are creating Form1, a windowed control, in the DLL. But you never destroy it. Then you unload the DLL which unloads the code that implements the window procedures for all windows created by the DLL. Presumably when the process shuts down, the window procedures are called, but there is no code there anymore.
Fix the problem by destroying all objects that the DLL creates. It looks to me like the best approach is to do that when PrintTopSellers terminates.
Procedure PrintTopSellers; stdcall;
begin
Form1 := TForm1.create(nil);
try
GetMonth := TGetMonth.create(nil);
try
Form1.PrintTopSellers;
finally
GetMonth.Free;
end;
finally
Form1.Free;
end;
end;
In the code that loads the DLL, TRead_iButton is declared incorrectly. It should be
TRead_iButton = procedure; stdcall;
But that doesn't actually explain the problem here since the signature mismatch is benign for a parameterless procedure.
"TRead_iButton = function: integer; register;"
"Procedure PrintTopSellers; stdcall;"
Absolutely different conventions/types, ain't them ?
Make them the same.
And better ditch DLL and use packages (BPL), then compiler would make you safe from such errors
We also don't see the code neither in Form1.PrintTopSellers nor in TGetMonth. The all can leave some dangling pointers in the host exe, that would get accesses after DLL unloaded.
Show exactly chain of function calls leading to AV - it is called stack trace.
Debug info + some excaption interrupt like Jedi CodeLibrary (used by Delphi IDE) madExcept, EurekaLog, synopse log and a lot of other exist.
Display the call stack in a Delphi Win32 application
Does DLL or EXE use Runtime packages ?

GetDir in Delphi2010 not working under Windows 7?

I have the following sequence of commands in Delphi2010:
var netdir:string;
....
OpenDialog1.InitialDir:=netdir;
....
OpenDialog1.Execute...
....
GetDir(0,netdir);
....
After executing OpenDialog I should have in string netdir the directory where I finished
my OpenDialog.Execute. And in the next OpenDialog.Execute it should start from that
directory.
It works fine on XP, but not on Windows 7?
It always starts from directory where the program is installed.
Any idea what might be wrong?
Thanks.
Your question cannot be answered as it stands, because it lacks several crucial details.
Is netdir a global constant, or does it go out of scope every now and then?
Do you set netdir to something prior to OpenDialog1.Execute?
Is the question about what directory GetDir return (as your title suggests), or about how to make the open dialog remember the last visited directory (as the body matter suggests)?
I will assume that 1) netdir is a global constant, that 2) you do not set it initially, and that 3) you want the open dialog to remember the last visited folder. Thus you have something like
unit Unit3;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs;
type
TForm3 = class(TForm)
OpenDialog1: TOpenDialog;
procedure FormClick(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form3: TForm3;
var
netdir: string;
implementation
{$R *.dfm}
procedure TForm3.FormClick(Sender: TObject);
begin
OpenDialog1.InitialDir := netdir;
OpenDialog1.Execute;
GetDir(0, netdir);
end;
end.
Then the solution is to let Windows remember the directory for you, that is, simply do
procedure TForm3.FormClick(Sender: TObject);
begin
OpenDialog1.Execute;
end;
alone! But why doesn't your method work? Well, GetDir doesn't return what you want. If you need explicit control, do
procedure TForm3.FormClick(Sender: TObject);
begin
OpenDialog1.InitialDir := netdir;
OpenDialog1.Execute;
netdir := ExtractFilePath(OpenDialog1.FileName)
end;
If you not wan´t opendialog you can do as below to get dir under your program.
yourdir:=ExtractFilePath(Application.ExeName);
I have done it in Vista and it work.
This is the solution for the problem
openDialog1.Options := [ofFileMustExist];
if openDialog1.Execute then
begin
end;

Resources