Want to protect your cyber security and still get fast solutions? Ask a secure question today.Go Premium

x
  • Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 383
  • Last Modified:

Timer in a dll file.

Hey,

How do you add a timer to a dll file. Setting a variable as TTimer is not possible.

Thnx!
0
TonyJiz
Asked:
TonyJiz
1 Solution
 
paulb1989Commented:
You will need to create a form in your DLL using File-New-Form and put the timer on there.

When you need to use the timer, create the form and you can use the timer that's on it.

Also remember to free the form when you're finished.
0
 
Wim ten BrinkSelf-employed developerCommented:
There are dozens of ways to do this. You could add a form or datamodule to the DL, drop a TTimer on it and just create the form/datamodule when you require the timer.
You could also just add a messageloop and the Windows API if you want a real minimal timer. Or you create the TTimer dynamically but then you need to pass a parenthandle to this timer, since it needs to hook into the messageloop.
0
 
wjmfzszCommented:
Give you a example:
1. add a variable tmpTimer:TTimer to your object;
2. create/destroy this TTime in your create/destroy produce.
   add "tmpTimer:=TTimer.Create(nil);" to your create produce.
   add "tmpTimer.Free" to your destroy produce.
3. assign a TTimer event to TTimer.OnTimer;
   declare a produce like this: produce EventTimer(Sender:TObject);
   assign this produce to TTimer.OnTimer: tmpTimer.OnTimer:=EventTimer;
0
Independent Software Vendors: We Want Your Opinion

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
TonyJizAuthor Commented:
Thnx, but anyone else? Using a form is not an option.
0
 
Wim ten BrinkSelf-employed developerCommented:
The alternative is to use the Windows API in your DLL. But in all situations you will need a messageloop to handle the message that the timer will be sending to your application. This means that you will have to create an (invisible) window and have a loop somewhere to process the messages for this window. Preferably in a separate thread but that would mean that the communications with your main thread will be troublesome.
Using a form or datamodule would remove the need for these Windows API solutions but  I agree, it adds a lot of overhead to your DLL.

At http://www.workshop-alex.org/Wallpaper/Wallpaper.html I have some simple trayicon example that uses the Windows API to run a timer. It shows you how to use a timer and how to respond on other messages too. The timer logic is encapsulated in the WindowProc routine, where I enable or disable the timer and also respond on the timer messages. (It also displays a trayicon and can change your desktop wallpaper.)

If your DLL or executable doesn't support a messageloop in any way then there is no solution. Even thread synchronisations won't work if you don't have any message handler in your application.

0
 
TonyJizAuthor Commented:
Hey,

if I add a form to the dll file, then put a timer on the form, then put a messagebox on the timer event, then run the dll from another program, then the messagebox wont show up.

Any ideas?
Thnx!
0
 
Wim ten BrinkSelf-employed developerCommented:
Did you create the form? DLL's don't generally create forms...
Anyway, I've tried it myself this time and you're right. I forgot. The form will still depend on the main thread having a messageloop. If your executable doesn't have one, it will never process the messages sent in your DLL.

For those who are interested, I created this tiny DLL, that doesn't need any DFM files or whatever:

library TimerDLL;
uses Windows, SysUtils, Classes, Forms, extctrls;

type
  TDLLForm = class( TForm )
  private
    Timer: TTimer;
  protected
  public
    procedure TimerAction( Sender: TObject );
  end;

function StartTimer: Pointer; stdcall; export;
var
  AForm: TDLLForm;
begin
  Result := nil;
  try
    AForm := TDLLForm.CreateNew( Application );
    AForm.Timer := TTimer.Create( AForm );
    AForm.Timer.Interval := 1000;
    AForm.Timer.OnTimer := AForm.TimerAction;
    AForm.Timer.Enabled := True;
    Result := AForm;
  except on E: Exception do MessageBox( GetDesktopWindow, PChar( E.Message ), 'Exception', MB_OK );
  end;
end;

procedure StopTimer( const ATimer: Pointer ); stdcall; export;
begin
  if Assigned( ATimer ) then TDLLForm( ATimer ).Free;
end;
exports StartTimer, StopTimer;

procedure TDLLForm.TimerAction( Sender: TObject );
begin
  MessageBox( GetDesktopWindow, 'Hello, World.', 'Timer', MB_OK );
end;

begin
  Application.Run;
end.

And it works quite nice if the application that loads this DLL contains a messageloop since the form in this DLL just hooks into the existing messageloop. But if there's no messageloop then this code just gets stuck.
0
 
Wim ten BrinkSelf-employed developerCommented:
Well, let me show you the nightmare code of the timer that does work in a DLL and it's even a very small solution too! (No forms!)

library DLL_Timer;

uses Windows;

const
  sTimerWndClass = 'Timer';
  ExStyle = WS_EX_TOOLWINDOW;
  Style = WS_CAPTION or WS_SYSMENU or WS_MINIMIZEBOX;
  TimerWndClass: TWndClass = (
    style: CS_CLASSDC or CS_PARENTDC;
    lpfnWndProc: nil;
    cbClsExtra: 0;
    cbWndExtra: 0;
    hInstance: 0;
    hIcon: 0;
    hCursor: 0;
    hbrBackground: color_btnface + 1;
    lpszMenuName: nil;
    lpszClassName: sTimerWndClass );

var
  AThread: THandle = 0;
  AThreadID: Cardinal;
  AHandle: THandle = 0;

procedure TimerProc( Handle: HWND; Ms, Event: UINT; Time: DWORD ); stdcall;
begin
  MessageBox( GetDesktopWindow, 'Hello, World.', 'Timer', MB_OKCANCEL );
end;

function ThreadProc( Value: Pointer ): Integer;
var
  ATimer: THandle;
  AMsg: TMsg;
begin
  AHandle := CreateWindowEx( ExStyle, sTimerWndClass, nil, Style, 0, 0, 10, 10, GetDesktopWindow, 0, hInstance, nil );
  UpdateWindow( AHandle );
  ATimer := SetTimer( AHandle, 0, 1000, @TimerProc );
  while ( GetMessage( aMsg, AHandle, 0, 0 ) ) do begin
    TranslateMessage( aMsg );
    DispatchMessage( aMsg );
  end;
  KillTimer( AHandle, ATimer );
  Result := 0;
end;

procedure StartTimer;
begin
  if ( AThread = 0 ) then begin
    AThread := BeginThread( nil, 0, ThreadProc, nil, 0, AThreadID );
  end;
end;

procedure StopTimer;
begin
  if not ( AThread = 0 ) then begin
    CloseWindow( AHandle );
    AThread := 0;
  end;
end;

exports StartTimer, StopTimer;

begin
  TimerWndClass.lpfnWndProc := @DefWindowProc;
  RegisterClass( TimerWndClass );
end.

Above DLL code compiles to less than 20 KB and will display a messagebox that you can close by pressing OK. Keep in mind that this code needs to use a thread since it needs a messageloop. I did not use the TThread class since there's no real need for it. We can't synchronise correctly with the main thread if the main thread has no messageloop anyway. But above code will work nicely in a simple console application.
Just be very, very aware of the multiple threading issues that you can and will encounter.

The sample console application that uses above code;

program Timer;
{$APPTYPE CONSOLE}
procedure StartTimer; external 'DLL_Timer.dll';
procedure StopTimer; external 'DLL_Timer.dll';
begin
  StartTimer;
  Write( 'Press <Enter> to quit.' );
  ReadLn;
  StopTimer;
end.

Wow, that looks easy. :-)
0
 
TonyJizAuthor Commented:
Thnx man!
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

Tackle projects and never again get stuck behind a technical roadblock.
Join Now