Autor Beitrag
Kay E.
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 118



BeitragVerfasst: 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
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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.
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 28.01.11 11:28 
Vor allem funktioniert ohne with das Debuggen ordentlich.
Kay E. Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 118



BeitragVerfasst: 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.

ausblenden volle Höhe 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;  //MainThread

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
    { Private declarations }
    FTime: Integer;
    TimerThreadObj: TTimerThread;
    procedure MyStrToTime;
    procedure WMUSERTICK(var msg: TMessage); message WM_USERTICK;
    function MyTimeToStr(vTime: Integer): string;
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.StartButtonClick(Sender: TObject);
begin
  MyStrToTime;  //Berechnet die Zeit aus mehreren EditFeldern zusammengesetzte Zeit, speichert Ergebnis in FTime
  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);  //Ftime wird in Sekunden gespeichert
  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.


ausblenden volle Höhe 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; //Thread-Unit

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'); //Liefert den richtigen Handle; 6-7 stellige Zahl
  while not Terminated do
  begin
    sleep(1000);
   {PostMessage(FMainThreadHandle, WM_USERTICK, 0, 0);} // FMainThreadHandle hat immer den Wert 276

    PostMessage(vHwnd, WM_USERTICK, 00);
  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. Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 118



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 141

W7
D2010, XE2
BeitragVerfasst: Fr 28.01.11 20:36 
Alternativ kann man auch einen eigenen Handle erzeugen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure WM_WMUSERTICK(var Msg: TMessage); // handler for incoming messages (should be public procedure of class)


constructor TMainform.Create();
begin
  FMsgHandle := AllocateHWnd(WM_WMUSERTICK); // call DeallocateHWnd() in destructor
end;
jaenicke
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Fr 28.01.11 20:52 
user profile iconKay E. hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic starofftopic star
Beiträge: 2157
Erhaltene Danke: 72


Java (Eclipse), Python (Sublimetext 3)
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: 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. Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 118



BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 29.01.11 06:00 
user profile iconKay E. hat folgendes geschrieben Zum zitierten Posting springen:
Dann will ich nicht immer Image1.Canvas. davor schreiben, um ein MoveTo zu setzen.
ausblenden 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
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3747
Erhaltene Danke: 123

Windows Vista, Ubuntu
Delphi 7 PE "Codename: Aurora", Eclipse Ganymede
BeitragVerfasst: 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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 19346
Erhaltene Danke: 1754

W11 x64 (Chrome, Edge)
Delphi 12 Pro, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
BeitragVerfasst: Sa 29.01.11 07:19 
Oder ganz automatisch aufklappend mit den CnWizards. ;-)
delfiphan
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2684
Erhaltene Danke: 32



BeitragVerfasst: 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.