Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   was macht AllocateHWND genau? (https://www.delphipraxis.net/162161-macht-allocatehwnd-genau.html)

snook 10. Aug 2011 11:19

Delphi-Version: 5

was macht AllocateHWND genau?
 
hey,

wie der titel schon sagt, würde mich mal interessieren was AllocateHwnd genau bewirkt. hintergrund ist der, dass isch nen DLL-TImer geschrieben hab, der in nem eigenen Thread (aus der DLL gestartet) läuft und auf einen waitabletimer wartet. da ich in DLL's kein synchronize benutze, lasse ich den Timer via PostMessage sich mitteilen, wann er fertig ist.

und hier jetzt das interessante:

ich habe den thread in eine weitere klasse gekapselt, die via AllocateHWND ein fensterhandle bekommt, den thread startet und in ihrer MessageProc auf den timer reagiert. wenn ich das ganze in ner DLL laufen lasse, dann hab ich mich jetzt im nachhinein gewundert, dass die Message aus dem thread (ist ja via Postmessage gesendet) überhaupt ankommt. es gibt ja keine schleife die diese Message verarbeiten könnte. kann es sein, dass sich AllocateHWnd IMMER an das Application-Objekt aus der Hauptanwendung wendet, egal ob es in einer DLL aufgerufen wurde? wenn ja, wie geht das? woher kennt AllocateHwnd mein Application-Object?

viele grüße Basti

himitsu 10. Aug 2011 11:35

AW: was macht AllocateHWND genau?
 
Du hast eine Professional, damit Zugriff auf den VCL-Quellcode und demnach auch auf das, was diese Funktion macht.

Wenn du in einem Thread ein Fenster erzeugst, dann mußt du in diesem Thread auch eine Nachrichtenschleife/-verarbetung einbauen, welche diese Nachrichten verarbeitet.

SirThornberry 10. Aug 2011 11:52

AW: was macht AllocateHWND genau?
 
eine ziemlich wirre Beschreibung. Hier würde mich mal der quelltext interessieren.
wie erzeugst du den Thread? Und an welcher Stelle erzeugst du dann den Timer (TTimer?).

Zitat:

...eine weitere klasse gekapselt, die via AllocateHWND ein fensterhandle bekommt, ...und in ihrer MessageProc auf den timer reagiert.
(wenn ich das ganze in ner DLL laufen lasse, dann hab ich mich jetzt im nachhinein gewundert, dass die Message aus dem thread (ist ja via Postmessage gesendet) überhaupt ankommt. es gibt ja keine schleife die diese Message verarbeiten könnte. ...)
Läuft in dem Thread in dem du AllocateHWND aufgerufen hast kein MessageLoop? Wenn die Klasse im gleichen Thread erzeugt wurde in dem auch dein TApplication.Run der Hauptanwendung läuft dann gibt es auch einen Messageloop.
Ein Messageloop bekommt (mit GetMessage etc.) alle Messages der Fenster des Threads in dem der Loop läuft.
Dabei ist es egal in welchem Programmmodul (dll, exe etc.) das Fensterhandel angefordert wurde. Ausschlaggebend ist einzig und allein der Thread.

snook 10. Aug 2011 12:00

AW: was macht AllocateHWND genau?
 
okay, sry war wohl noch etwas zu früh am morgen :) , ich häng mal die unit rein
Delphi-Quellcode:
unit ExtDLLTimer;

interface

uses Windows, SysUtils, Classes, messages;

const
  CTI_MULT_FACTOR    = 10000;
  CTI_MAXINTERVAL    = $8000000000000000;
  CTI_MAXINTERVAL_MS = CTI_MAXINTERVAL div CTI_MULT_FACTOR;

// Timeout to wait for finishing timer-thread
  CTI_EXITTIMEOUT    = 1000;

type
  TAPIWaitableTimer = class(TThread)
  private
    FOnTimer   : TNotifyEvent;
    FTimer,
    FCloseEvent,
    FPauseEvent,
    FExitEvent : Cardinal;
    FInterval  : Int64;
    FRunning,
    FContinuous : boolean;
    FHandle    : HWND;
    procedure CLoseHandles;
    function GetEnabled: boolean;
    procedure ThrowTimerEvent;
    procedure SetEnabled(const Value: boolean);
    procedure SetInterval(const Value: Int64);
  protected
    procedure Execute; override;
    procedure StartTimer;
    procedure StopTimer;
  public
    constructor Create(AContinuous: boolean; ATimerObject: HWND;
      AExitEvent: Cardinal); reintroduce;
    destructor Destroy; override;
    property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
    property Enabled: boolean read GetEnabled write SetEnabled;
    property CloseEvent: Cardinal read FCloseEvent;
    property Interval: Int64 read FInterval write SetInterval;
  end;

  TDLLTimer = class(TObject)
  private
    FHandle   : HWND;
    FExitEvent : Cardinal;
    FOnTimer  : TNotifyEvent;
    FTimer    : TAPIWaitableTimer;
    FContinuous: boolean;
    FInterval : Cardinal;
    function GetEnabled: boolean;
    procedure OnAPITimer(Sender: TObject);
    procedure APITimerTerminated(Sender: TObject);
  public
    constructor Create(AContinuous: boolean); reintroduce;
    destructor Destroy; override;
    procedure WndProc(var Msg: TMessage);
    procedure StartTimer(AInterval: Int64);
    procedure StopTimer;
    property OnTimer: TNotifyEvent read FOnTimer write FOnTimer;
    property Enabled: boolean read GetEnabled;
  end;

implementation

{ TAPIWaitableTimer }

constructor TAPIWaitableTimer.Create(AContinuous: boolean; ATimerObject: HWND;
  AExitEvent: Cardinal);
begin
  inherited create(false);
  FHandle    := ATimerObject;
  FExitEvent := AExitEvent;
  FTimer     := CreateWaitableTimer(nil, false, PChar(''));
  FCloseEvent := CreateEvent(nil, false, false, PChar(''));
  FPauseEvent := CreateEvent(nil, false, false, PChar(''));
  FRunning   := false;
  FContinuous := AContinuous;
  FreeOnTerminate := true;
  SetInterval(1000);
end;

destructor TAPIWaitableTimer.Destroy;
begin
  CloseHandles;          
  SetEvent(FExitEvent);
  inherited;
end;

procedure TAPIWaitableTimer.CloseHandles;
begin
  if FTimer > 0 then
    closehandle(FTimer);
  if FCLoseEvent > 0 then
    closehandle(FCloseEvent);
  if FPauseEvent > 0 then
    closehandle(FPauseEvent);
  FTimer     := 0;
  FCloseEvent := 0;
end;

procedure TAPIWaitableTimer.Execute;
var objs : Array[0..2] of Cardinal;
    lQuit: boolean;
begin
  lQuit := false;
  if (FTimer > 0) and (FCloseEvent > 0) then
  begin
    objs[0] := FTimer;
    objs[1] := FCloseEvent;
    objs[2] := FPauseEvent;
    repeat

      case WaitForMultipleObjects(3, @objs, false, INFINITE) of
        WAIT_OBJECT_0    :
          begin
//            synchronize(ThrowTimerEvent);
            PostMessage(FHandle, WM_TIMER, 0, 0);
            FRunning := false;
            if FContinuous then StartTimer;
          end;
        WAIT_OBJECT_0 + 1: lQuit := true;
        WAIT_OBJECT_0 + 2:
          case Enabled of
            true : StopTimer;
            false : StartTimer;
          end;
      end;
    until lQuit;
  end;
  Terminate;
end;

function TAPIWaitableTimer.GetEnabled: boolean;
begin
  result := (FTimer > 0) and (FCloseEvent > 0) and FRunning;
end;

procedure TAPIWaitableTimer.SetEnabled(const Value: boolean);
begin
  if Value <> Enabled then
    SetEvent(FPauseEvent);
  if suspended then
    resume;
end;

procedure TAPIWaitableTimer.SetInterval(const Value: Int64);
begin
  FInterval := Value;
  if Enabled then StartTimer;
end;

procedure TAPIWaitableTimer.StartTimer;
const WaitDur = 10;
var Duration: TLargeInteger;
    Per    : Integer;
begin
  // not sure why have to wait, but else timer sometimes behaves corrupted
  sleep(WaitDur);
  if abs(FInterval) > abs(CTI_MAXINTERVAL_MS) then
    raise Exception.Create('Maximum Interval exceeded!');
  FInterval := FInterval - WaitDur;
  Duration := (-1) * FInterval * CTI_MULT_FACTOR;
  Per      := 0;
  FRunning := SetWaitableTimer(FTimer, Duration, Per, nil, nil, true);
  if not FRunning then
    raise Exception.Create(SysErrorMessage(GetLastError));
  if suspended then
    resume;
end;

procedure TAPIWaitableTimer.StopTimer;
begin                                                    
  FRunning := false;
  if not CancelWaitableTimer(FTimer) then
    raise Exception.Create(SysErrorMessage(GetLastError));
end;

procedure TAPIWaitableTimer.ThrowTimerEvent;
begin
  if FRunning and Assigned(FOnTimer) then
    FOnTimer(self);
end;

{ TDLLTimer }

constructor TDLLTimer.Create(AContinuous: boolean);
begin
  inherited create;
{$IFDEF MSWINDOWS}
  FHandle := Classes.AllocateHWnd(WndProc);
{$ENDIF}
{$IFDEF LINUX}
  FHandle := WinUtils.AllocateHWnd(WndProc);
{$ENDIF}
  FContinuous := AContinuous;
  FExitEvent := CreateEvent(nil, false, false, '');
  FTimer := TAPIWaitableTimer.Create(AContinuous, FHandle, FExitEvent);
  FTImer.OnTimer := OnAPITimer;
  FTimer.OnTerminate := APITimerTerminated;
end;

destructor TDLLTimer.Destroy;
begin
{$IFDEF MSWINDOWS}
  Classes.DeallocateHWnd(FHandle);
{$ENDIF}
{$IFDEF LINUX}
  WinUtils.DeallocateHWnd(FHandle);
{$ENDIF}
  FTimer.OnTerminate := nil;
  ResetEvent(FExitEvent);
  SetEvent(FTimer.CloseEvent);
  case WaitForSingleObject(FExitEvent, CTI_EXITTIMEOUT) of
    WAIT_OBJECT_0: FTimer := nil;
    WAIT_TIMEOUT : raise Exception.Create('Error releasing DLLTimer');
  end;
  inherited;
end;

procedure TDLLTimer.APITimerTerminated(Sender: TObject);
var lEnabled: boolean;
begin
  lEnabled := FTimer.Enabled;
  if Assigned(FTimer.FatalException) then
    raise Exception.Create(Exception(FTimer.FatalException).Message)
  else
  begin
    ResetEvent(FExitEvent);
    FTimer := TAPIWaitableTimer.Create(FContinuous, FHandle, FExitEvent);
    FTImer.OnTimer := OnAPITimer;
    FTimer.OnTerminate := APITimerTerminated;
    FTimer.Interval   := FInterval;
    if lEnabled then
      FTimer.StartTimer;
  end;
end;

function TDLLTimer.GetEnabled: boolean;
begin
  result := FTimer.Enabled;
end;

procedure TDLLTimer.OnAPITimer(Sender: TObject);
begin
  if Assigned(FOnTimer) then
    FOnTimer(Sender);
end;

procedure TDLLTimer.StartTimer(AInterval: Int64);
begin
  FInterval := AInterval;
  FTimer.Interval := AInterval;
  FTimer.Enabled := true;;
end;

procedure TDLLTimer.StopTimer;
begin
  FTimer.Enabled := false;
end;

procedure TDLLTimer.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_TIMER: OnApiTimer(FTimer);
  end;
end;

en
d.

SirThornberry 10. Aug 2011 12:12

AW: was macht AllocateHWND genau?
 
Und in welchem Thread wird TDllTimer erzeugt? Davon ist abhängig wer/was die Nachrichten in die WindowProc von TDLLtimer befördert.

snook 10. Aug 2011 12:18

AW: was macht AllocateHWND genau?
 
TDLLTimer wird z.B. in einer DLL erzeugt. und diese DLL wird aus dem Kontext des Hauptthreads geladen

SirThornberry 10. Aug 2011 12:20

AW: was macht AllocateHWND genau?
 
Zitat:

TDLLTimer wird z.B. in einer DLL erzeugt. und diese DLL wird aus dem Kontext des Hauptthreads geladen
Dann steht der Grund ja in meinem ersten Post in diesem Thema.
Es ist unerheblich in welchem Programmmodul der Code zum erzeugen liegt und es ist auch unerheblich in welchem Threadcontext die DLL geladen wurde.
Der Threadcontext in dem das Fensterhandle angefordert wird ist maßgebend.

snook 10. Aug 2011 12:22

AW: was macht AllocateHWND genau?
 
ooohh krass. ich hab die ganze zeit gedacht die message loop ist vom modul abhängig. perfekt, dank dir

ähem, dann hab ich wohl einiges anderes auch noch nicht verstanden. wieso bekommt denn dann der klassische TTimer aus delphi kein WM_TIMER Event geschickt, wenn er in einer DLL erzeugt wurde?

himitsu 10. Aug 2011 14:01

AW: was macht AllocateHWND genau?
 
Nein, die Message-Behandlung ist immer vom threadabhängig.

Die Message-Behandlung in Komponenten ist von dem Thread abhängig, in welchem sie erstellt werden.

Grundsätzlich hat erstmal jeder Thread keine Message-Queue.
Sobald die erste anfrage an diesen Thread gestellt wird, wird für diesen Thread eine solche Queue (Warteschleife) eingerichtet.

PostThreadMessage schickt die Nachricht direkt an die Queue eines Threads und PostMessage/SendMessage schicken die Nachrichten an den Thread, in welchem diese Komponente erstellt wurde.

Und eine "Message-Loop" verarbeitet nur Messages des Threads, in welchem sie läuft.
Für den Hauptthread stellt Delphi-Referenz durchsuchenApplication eine "Message-Loop" bereit und für andere Threads müßte man das selber machen.

sirius 10. Aug 2011 16:23

AW: was macht AllocateHWND genau?
 
Zitat:

Zitat von sebastian paeckel (Beitrag 1115972)
ähem, dann hab ich wohl einiges anderes auch noch nicht verstanden. wieso bekommt denn dann der klassische TTimer aus delphi kein WM_TIMER Event geschickt, wenn er in einer DLL erzeugt wurde?

Weil der sich an das Application-Objekt wendet. Und diese sind verschieden in DLL und Exe.


Alle Zeitangaben in WEZ +1. Es ist jetzt 14:41 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz