![]() |
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 |
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. |
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:
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. |
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:
d.
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 |
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.
|
AW: was macht AllocateHWND genau?
TDLLTimer wird z.B. in einer DLL erzeugt. und diese DLL wird aus dem Kontext des Hauptthreads geladen
|
AW: was macht AllocateHWND genau?
Zitat:
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. |
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? |
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 ![]() |
AW: was macht AllocateHWND genau?
Zitat:
|
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:59 Uhr. |
Powered by vBulletin® Copyright ©2000 - 2025, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz