Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - HWND von Mainthread?
Kay E. - Fr 28.01.11 03:49
Titel: HWND von Mainthread?
Hallo!
ICh habe ein Programm, dass einen Thread startet. Diesem Thread will ich das Handle zum Mainthread mitgeben, damit der Nebenthread mit SendMessage/PostMessage Nachrichten an den Mainthread schicken kann.
Ich hab mir nen Wolf gesucht und ausprobiert, was mir eingefallen ist, aber ich komm nicht (sinnvoll) an das Handle. Mach ich die Initialisierung so
Delphi-Quelltext
1: 2: 3: 4: 5: 6:
| ThreadObj := MyThread.Create(True); with ThreadObj do begin FMainHwnd := Handle; Start; end; |
dann hat FMainHwnd immer den Wert 276;
Nehm ich statt Handle ClientHandle ist FMainHwnd immer 0;
Nur wenn ich über FindWindow nach dem Mainthread such, bekomm ich einen funktionierenden Handle auf den Mainthread. Das kann ja aber nicht die Krönung aller Lösungen sein. Spätestens bei 2 Instanzen meines Programms gehts schief.
In der Hilfe steht, dass Application.CreateHandle das Mainfenster aufbaut (sofern noch nicht existent) und den Wert des Handles in "Handle" schreibt. Davon merk ich nur leider nix. Aber davon ausgehend müsste Handle doch richtig sein.
Wie komm ich denn nun an den (eigentlich) wichtigsten Handle im kompletten Programm? Für Tipps bin ich wie immer sehr dankbar!
Grüße Kay
jaenicke - Fr 28.01.11 06:51
Also erstens solltest du den Thread gar nicht erst pausieren. Erstelle lieber einen eigenen Konstruktor, der die entsprechenden Parameter bekommt.
Delphi-Quelltext
1: 2: 3: 4: 5:
| constructor Create(ATargetHandle: HWND); begin FTargetHandle := ATargetHandle; inherited Create(False); end; |
Grundsätzlich geht es natürlich theoretisch auch so wie du es machst, aber dann musst du sehr aufpassen was du tust. Einfacher ist es so wie ich es geschrieben habe, weil so im Grunde nicht viel schiefgehen kann.
Ja, das Handle, das du benutzt, ist das deines Formulars, wenn du den geposteten Code in einer Formularmethode zu stehen hast. Wenn es das ist was du willst, dann ist das so richtig.
Bei mir hat das allerdings immer andere und deutlich größere Werte.
So aus dem Zusammenhang gerissen (also wo der Code eigentlich steht), kann aber hier auch niemand wissen woher Handle in dem Fall eigentlich kommt.
elundril - Fr 28.01.11 11:19
Außerdem sin diese with-konstrukte mMn mehr fehlerquelle als wirklich sinnvoll. Da schreib ich lieber ein paar mal mehr den objektnamen und weiß dann dafür von wo das feld bzw die variable herkommt.
jaenicke - Fr 28.01.11 11:28
Vor allem funktioniert ohne with das Debuggen ordentlich.
Kay E. - Fr 28.01.11 13:04
Ok, wenn ihr mehr Kontext, sollt ihr ihn bekommen :) Das hier ist ein kleiner Bildschirmtimer, den ich auf Threads umgebaut hab. Das eigentliche Projekt ist deutlich größer, aber das Prinzip bleibt das selbe.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68:
| unit Main; interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, TimerThread;
const WM_USERTICK = WM_USER + 0;
type TForm1 = class(TForm) private FTime: Integer; TimerThreadObj: TTimerThread; procedure MyStrToTime; procedure WMUSERTICK(var msg: TMessage); message WM_USERTICK; function MyTimeToStr(vTime: Integer): string; public end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.StartButtonClick(Sender: TObject); begin MyStrToTime; TimeLabel.Caption := MyTimeToStr(FTime); TimeLabel.Visible := true; MinsEdit.SetFocus;
if assigned(TimerThreadObj) then TimerThreadObj.Terminate;
TimerThreadObj := TTimerThread.Create(True); with TimerThreadObj do begin FMainThreadHandle := Handle; FreeOnTerminate := True; Start; end; end;
procedure TForm1.WMUSERTICK(var msg: TMessage); begin dec(FTime); TimeLabel.Caption := MyTimeToStr(FTime);
if FTime <= 0 then begin TimerThreadObj.Terminate; Form1.BringToFront; Form1.FormStyle := fsStayOnTop; ShowMessagePos('Countdown abgelaufen', Form1.Left, Form1.Top); Form1.FormStyle := fsNormal; TimeLabel.Visible := false; end; end;
end. |
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34:
| unit TimerThread; interface
uses Classes, Windows;
type TTimerThread = class(TThread) protected procedure Execute; override; public FMainThreadHandle: HWnd; end;
implementation
uses Main;
procedure TTimerThread.Execute; var vHwnd: Hwnd; begin vHwnd := FindWindow('TForm1', 'Bildschirm Timer'); while not Terminated do begin sleep(1000); PostMessage(vHwnd, WM_USERTICK, 0, 0); end; end;
end. |
Was die eigene Create-Methode angeht: Das ist im großen Programm ein mächtiger Packen Daten, den ich da übergeben muss. Wenn ich das alles in den Parameter von Create schreib... Und aus dem Thread heraus auf die Variablen zugreifen wollte ich eigentlich vermeiden, da der Thread autonom lauffähig sein sollte.
Das mit der With-Anweisung stimmt, aber in dem Fall ist es nicht weiter schlimm, weil TThread kein "Handle" besitzt und somit sicher auf Form1 zugegriffen wird. Aber ich werds im Hinterkopf behalten. Danke!
Grüße Kay
Kay E. - Fr 28.01.11 20:31
Oh mann, wie peinlich :oops:
Ihr hattet Recht mit der with-Anweisung. Das Problem besteht darin, dass TThread ja DOCH ein Handle-Property hat (muss ich mich falsch erinnert haben :\ ). Und von demher weise ich dem Thread-Feld seinen eigenen Threadhandle zu. Ohne With-Anweisung bzw. mit Referenzierer zum Mainform gehts einwandfrei.
Vielen Dank euch!
Grüße Kay
sky21 - Fr 28.01.11 20:36
Alternativ kann man auch einen eigenen Handle erzeugen:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| procedure WM_WMUSERTICK(var Msg: TMessage);
constructor TMainform.Create(); begin FMsgHandle := AllocateHWnd(WM_WMUSERTICK); end; |
jaenicke - Fr 28.01.11 20:52
Kay E. hat folgendes geschrieben : |
| Ihr hattet Recht mit der with-Anweisung. Das Problem besteht darin, dass TThread ja DOCH ein Handle-Property hat (muss ich mich falsch erinnert haben :\ ). |
Und genau das ist das Problem mit with. Man muss sehr genau hinschauen und findet Fehler schwer. Das ist mir ja hier auch nicht aufgefallen.
Und zudem kann man eben nicht direkt debuggen, sondern muss umständlich den kompletten Ausdruck manuell jedesmal zur Auswertung zusammenbasteln.
Und das alles nur weil man ein paar Zeichen weniger schreiben will? :lol:
Regan - Fr 28.01.11 22:02
Ich möchte hier bloß nochmal eine Lanze für das with brechen: with ist nützlich und praktisch, wenn man sich zum Beispiel innerhalb einer Klasse befindet.
AFAIK hätte sich das Problem auch über ein self lösen können.
jaenicke - Fr 28.01.11 22:39
Er ist ja innerhalb einer Klasse. Und genau durch das with hat er eben nicht mehr auf die Eigenschaft genau dieser Klasse zugegriffen.
Klar geht das auch mit Self. Nur muss man das eben auch sehen. Und genau das ist eben auch das Problem, wodurch with eben Fehler provoziert.
Und wie gesagt: Ich sehe keinen echten Vorteil an with, die paar Buchstaben wird man ja wohl noch tippen können. Ich halte with für einen der größten Fehler bei der Entwicklung von Delphi.
Kay E. - Sa 29.01.11 00:30
Ich finde with (mittlerweile *g*) nur noch bedingt gut. Es hat seine Daseinsberechtigung, solange man ausschließlich nur mit einem Objekt, und zwar mit dem der With-Anweisung, arbeitet. Zum Beispiel, wenn ich größere Sachen auf einer Canvas zeichnen will. Dann will ich nicht immer Image1.Canvas. davor schreiben, um ein MoveTo zu setzen.
Umständlich und fehleranfällig wirds dann, wenn andere Einflüsse von außen reinkommen, also wenn man zum Beispiel auf die Properties eines anderen Objekts zugreifen will. Das werd ich mir jetzt ziemlich schnell abgewöhnen, denke ich. Das hat mich jetzt einen Tag Zeit gekostet -.-
Grüße Kay
jaenicke - Sa 29.01.11 06:00
Kay E. hat folgendes geschrieben : |
| Dann will ich nicht immer Image1.Canvas. davor schreiben, um ein MoveTo zu setzen. |
Delphi-Quelltext
1: 2: 3: 4: 5:
| var MyCanvas: TCanvas; begin MyCanvas := Image1.Canvas; MyCanvas.MoveTo... |
Und wenn der Codeabschnitt nicht zu lang ist (ok, sollte eh nicht passieren), kann der Variablenname ja auch einfach C sein oder so. Ist immer noch übersichtlicher als with und gar nix davor. ;-)
elundril - Sa 29.01.11 07:18
bzw hat man ja eh die vervollständigungsliste mit strg+leertaste.
jaenicke - Sa 29.01.11 07:19
Oder ganz automatisch aufklappend mit den CnWizards. ;-)
delfiphan - Sa 29.01.11 10:50
Da hast du ganz Recht. SendMessage/PostMessage ist für GUI Anwendungen viel zu lowlevel. Das kann's wirklich nicht sein.
=> Gib dem Thread doch eine Referenz auf die Form und implementiere eine
Dispatcher [
http://msdn.microsoft.com/de-de/library/system.windows.threading.dispatcher.aspx] Klasse. Mit der kannst du dann entweder mit Invoke (synchron, wie SendMessage) oder BeginInvoke (asynchron, wie PostMessage) eine Methode auf der Form ausführen. => Am besten verwendest du anonyme Methoden, dann kannst du über Closures beliebige Methodensignaturen mit beliebigen Parameter aufrufen.
Alternativ dazu kannst du auch Synchronize bzw Queue verwenden und von dort aus den Call auf den Form machen. Parameter kannst du dann aber nicht direkt mitgeben.
Ausserdem macht dein Thread-Timer nicht viel mehr als ein ganz gewöhnlicher VCL Timer... Dafür brauchst du keine Threads.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!