| Autor |
Beitrag |
Kay E.
      
Beiträge: 118
|
Verfasst: Fr 28.01.11 03:49
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
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: 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.
_________________ This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Fr 28.01.11 11:28
Vor allem funktioniert ohne with das Debuggen ordentlich.
|
|
Kay E. 
      
Beiträge: 118
|
Verfasst: 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.
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. |
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. 
      
Beiträge: 118
|
Verfasst: Fr 28.01.11 20:31
Oh mann, wie peinlich
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
      
Beiträge: 141
W7
D2010, XE2
|
Verfasst: 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
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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? 
|
|
Regan
      
Beiträge: 2157
Erhaltene Danke: 72
Java (Eclipse), Python (Sublimetext 3)
|
Verfasst: 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
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: 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. 
      
Beiträge: 118
|
Verfasst: 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
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 29.01.11 06:00
|
|
elundril
      
Beiträge: 3747
Erhaltene Danke: 123
Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
|
Verfasst: Sa 29.01.11 07:18
bzw hat man ja eh die vervollständigungsliste mit strg+leertaste.
_________________ This Signature-Space is intentionally left blank.
Bei Beschwerden, bitte den Beschwerdebutton (gekennzeichnet mit PN) verwenden.
|
|
jaenicke
      
Beiträge: 19346
Erhaltene Danke: 1754
W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: Sa 29.01.11 07:19
Oder ganz automatisch aufklappend mit den CnWizards. 
|
|
delfiphan
      
Beiträge: 2684
Erhaltene Danke: 32
|
Verfasst: 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 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.
|
|
|