![]() |
Form in neuem Thread laufen lassen
Hi,
ich möchte eine Information über vorhandene Updates einblenden lassen. Zu diesem Zweck habe ich eine Form ohne Rahmen, die ich oben rechts langsam in den Desktop ein- und ausscrollen lasse. Leider hat das den Nachteil, dass der Scrollvorgang in's stocken gerät, wenn z.b. ein Hint in der Mainform angezeigt wird, oder andere Rechenintensive Prozesse im Mainthread auflaufen. Also dachte ich mir, ich könnte ja gleich die Form in einem Thread laufen lassen. So schaut's aus:
Delphi-Quellcode:
So aufgerufen:
unit updatealert;
interface uses Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics, Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.Buttons, Vcl.StdCtrls, Vcl.ExtCtrls; type TFadeDirection=(dfIn, dfOut); TOnStep=procedure(Sender: TObject) of object; TOnFinished=procedure(Sender: TObject) of object; TThDisplayUpdateInformation=class(TThread) private FCaption: String; FTitle: string; FDuration: Integer; FWorkarea: TRect; public constructor Create(Suspended: Boolean; Caption, Title: string; Duration: Integer; WorkArea: TRect); protected procedure Execute; override; end; TThFadeIn=class(TThread) private FOnStep: TOnStep; FOnFinished: TOnFinished; FCancel: Boolean; FDirection: TFadeDirection; FForm: TForm; procedure DoStep; procedure DoFinished; procedure SetCancel(const Value: Boolean); published property OnStep: TOnStep read FOnStep write FOnStep; property OnFinished: TOnFinished read FOnFinished write FOnFinished; property Cancel: Boolean read FCancel write SetCancel; public constructor Create(Suspended: Boolean; Form: TForm; Direction: TFadeDirection = dfIn); protected procedure Execute; override; end; TOnStartUpdate=procedure(sender: TObject) of object; Tfrm_updatealert = class(TForm) pnl1: TPanel; lbl_title: TLabel; lbl_message: TLabel; btn1_close: TSpeedButton; btn_download: TSpeedButton; tmr1Duration: TTimer; procedure pnl1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); procedure btn1_closeClick(Sender: TObject); procedure FormClose(Sender: TObject; var Action: TCloseAction); procedure FormShow(Sender: TObject); procedure tmr1DurationTimer(Sender: TObject); procedure DoStartUpdate; procedure btn_downloadClick(Sender: TObject); private thIn: TThFadeIn; thOut: TThFadeIn; FOnStartUpdate: TOnStartUpdate; procedure OnStepFadeIn(Sender: TObject); procedure OnFinishedFadeIn(Sender: TObject); procedure OnStepFadeOut(Sender: TObject); procedure OnFinishedFadeOut(Sender: TObject); { Private-Deklarationen } public { Public-Deklarationen } published property OnStartUpdate: TOnStartUpdate read FOnStartUpdate write FOnStartUpdate; end; var frm_updatealert: Tfrm_updatealert; implementation {$R *.dfm} procedure Tfrm_updatealert.btn1_closeClick(Sender: TObject); begin thOut:=TThFadeIn.Create(True, self, dfOut); thOut.OnStep:=OnStepFadeOut; thOut.OnFinished:=OnFinishedFadeOut; thOut.Resume; end; procedure Tfrm_updatealert.btn_downloadClick(Sender: TObject); begin DoStartUpdate; end; procedure Tfrm_updatealert.DoStartUpdate; begin if Assigned(FOnStartUpdate) then FOnStartUpdate(Self); end; procedure Tfrm_updatealert.FormClose(Sender: TObject; var Action: TCloseAction); begin Action:=caFree; end; procedure Tfrm_updatealert.FormShow(Sender: TObject); begin thIn:=TThFadeIn.Create(True, self, dfIn); thIn.OnStep:=OnStepFadeIn; thIn.OnFinished:=OnFinishedFadeIn; thIn.Resume; end; procedure Tfrm_updatealert.OnFinishedFadeIn(Sender: TObject); begin thIn:=nil; tmr1Duration.Enabled:=True; end; procedure Tfrm_updatealert.OnFinishedFadeOut(Sender: TObject); begin thOut:=nil; Self.Close; end; procedure Tfrm_updatealert.OnStepFadein(Sender: TObject); begin if Self.Left>(Screen.WorkAreaRect.Right-Self.Width) then begin self.Left:=self.Left-1; end else begin Self.Left:=Screen.WorkAreaRect.Right-self.Width; if TThFadeIn(Sender)<>nil then TThFadeIn(Sender).Cancel:=True; end; end; procedure Tfrm_updatealert.OnStepFadeOut(Sender: TObject); begin if Self.Left<Screen.WorkAreaRect.Right then begin self.Left:=self.Left+1; end else begin Self.Left:=Screen.WorkAreaRect.Right; if TThFadeIn(Sender)<>nil then TThFadeIn(Sender).Cancel:=True; end; end; procedure Tfrm_updatealert.pnl1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin releasecapture; sendmessage(self.Handle, WM_NCLBUTTONDOWN, 2, 0); end; procedure Tfrm_updatealert.tmr1DurationTimer(Sender: TObject); begin btn1_closeClick(self); end; { TThFadeIn } constructor TThFadeIn.Create(Suspended: Boolean; Form: TForm; Direction: TFadeDirection); begin inherited Create(Suspended); self.NameThreadForDebugging('AlertFadeIn'); self.FreeOnTerminate:=True; FCancel:=False; FDirection:=Direction; FForm:=Form; end; procedure TThFadeIn.DoFinished; begin if Assigned(FOnFinished) then Synchronize(procedure begin FOnFinished(Self); end); end; procedure TThFadeIn.DoStep; begin if FForm<>nil then begin case FDirection of dfIn: begin if FForm.Left>(Screen.WorkAreaRect.Right-FForm.Width) then begin FForm.Left:=FForm.Left-1; end else begin FForm.Left:=Screen.WorkAreaRect.Right-FForm.Width; FCancel:=True; end; end; dfOut: begin if FForm.Left<Screen.WorkAreaRect.Right then begin FForm.Left:=FForm.Left+1; end else begin FForm.Left:=Screen.WorkAreaRect.Right; FCancel:=True; end; end; end; end else begin if Assigned(FOnStep) then Synchronize(procedure begin FOnStep(Self); end); end; end; procedure TThFadeIn.Execute; begin Try while (not FCancel) and (not Terminated) do begin DoStep; Sleep(2); end; Finally DoFinished; End; end; procedure TThFadeIn.SetCancel(const Value: Boolean); begin FCancel := Value; end; { TThDisplayUpdateInformation } constructor TThDisplayUpdateInformation.Create(Suspended: Boolean; Caption, Title: string; Duration: Integer; WorkArea: TRect); begin inherited Create(Suspended); self.FCaption:=Caption; self.FTitle:=Title; self.FDuration:=Duration; self.FWorkarea:=WorkArea; end; procedure TThDisplayUpdateInformation.Execute; function CalculateTextHeight(value: String; can: TCanvas): Integer; var lRect : TRect; lText : string; begin lRect.Left := 0; lRect.Right := 300; lRect.Top := 0; lRect.Bottom := 0; lText := value; Can.TextRect( {var} lRect, //will be modified to fit the text dimensions {var} lText, //not modified, unless you use the "tfModifyingString" flag [tfCalcRect, tfWordBreak] //flags to say "compute text dimensions with line breaks" ); ASSERT( lRect.Top = 0 ); //this shouldn't have moved Result := lRect.Bottom; end; var updateform: Tfrm_updatealert; begin updateform:=Tfrm_updatealert.Create(nil); updateform.lbl_title.Caption:=FTitle; updateform.lbl_message.Caption:=FCaption; updateform.tmr1Duration.Interval:=FDuration; updateform.Height:=163+CalculateTextHeight(updateform.lbl_message.Caption, updateform.Canvas); updateform.Top:=FWorkarea.Top; updateform.Left:=FWorkarea.Right; //updateform.OnStartUpdate:=StartUpdate; updateform.Show; while updateform.Showing and (not Terminated) do begin Sleep(100); end; end; end.
Delphi-Quellcode:
führt das zu dem Effect, dass a) die Updateform nur zu 20% eingescrollt wird und dann stoppt und b) wenn ich die Updateform einmal mit der Maus anklicke das ganze Programm nicht mehr reagiert.
UpdateCaption:='';
for i:=0 to Update.UpdateFiles.Count-1 do begin UpdateCaption:=UpdateCaption+'- '+Update.UpdateFiles[i].Filename+' Version: '+Update.UpdateFiles[i].NewVersion+#10#13; end; thDisplayUpdate:=TThDisplayUpdateInformation.Create(False, UpdateCaption, 'Es liegen Updates zum Download bereit', 10000, Screen.WorkAreaRect, self); Rufe ich das so auf, dass es im MainThread läuft (einfach für .Show), dann habe ich die oben aufgeführten Einschränkungen. (So läuft es zur Zeit). Im Grunde bin ich mit dem wie es jetzt läuft ja auch ganz zufrieden, aber eben das stocken des scrollen stört das Look-And-Feel schon sehr, den der User soll ja während des Einblenden und Ausblenden ganz normal weiterarbeiten können. Gruß Hobbycoder |
AW: Form in neuem Thread laufen lassen
Ein Form einfach mal so in einem neuen Thread "laufen zu lassen" ist mit der VCL nicht wirklich möglich; ein
Delphi-Quellcode:
ist in jedem Falle notwendig. Dein
Synchronize
Delphi-Quellcode:
z.b. synchronisiert den Zugriff auf
DoStep
Delphi-Quellcode:
nicht korrekt.
TForm.Left
Hier wiederrum ergibt sich das Problem, dass Synchronize sowieso wieder den Workload in den Main-Thread auslagert, weshalb du bei deinem FadeIn/Out praktisch nichts gewonnen hast. Dafür würde ich eher einen Timer verwenden. |
AW: Form in neuem Thread laufen lassen
Habe ich nicht mit einem Timer das gleiche Problem?
Denn wenn die Application während des ein und ausscrollen viel rechenleistung benötigt, wird das verschieben der Form (nicht der timer) ja auch stocken. Genau das wollte ich damit eigentlich in einen eigenen Thread packen. |
AW: Form in neuem Thread laufen lassen
Ich würde eher die rechenintensiven Dinge in eigene Threads verlegen. Dann hast du im Haupthread für jene Dinge, welche du dem User anzeigen willst mehr Saft :-).
|
AW: Form in neuem Thread laufen lassen
Wie oben bereits geschrieben, kommt es schon zu Stockungen, wenn ein Hint angezeigt wird (z.b. Bei einem Speedbutton). Wie sollte ich das in einen Thread auslagern.
|
AW: Form in neuem Thread laufen lassen
Wie Zacherl schreibt:
Wenn du einen Thread startest und von diesem Thread aus VCL Dinge tun willst, dann musst du zwingend Synchronize verwenden. Infos findest du zum Beispiel hier: ![]() Ich würde für ein Scrollen niemals inc und sleep verwenden. Du musst bedenken, dass das Betriebssystem nicht nur deinem Programm Zeit z.V. stellt; dein Programm wird nur ab und zu aufgerufen und darf wieder ein wenig weiter rechnen. Wenn ein Kunde Beispiel einen Rechner mit nur 2 Kernen hat, dann ruckelt die ganze Sache eventuell bereits aus Gründen, welche du gar nicht beeinflussen kannst. Verwende besser einen genauen "Zeitmesser". Du merkst dir die "Scroll - Startzeit" und berechnest dann jeweils aufgrund der verstrichnen Zeit die neue Position des Fensters. So entsteht für den Betrachter eine wesentlich flüssigere Bewegung. Dem Thread kannst du eine höhere Priorität zuweisen. |
AW: Form in neuem Thread laufen lassen
Zitat:
Delphi-Quellcode:
sowieso wieder im Haupt-Thread arbeitest und zusätzlich noch Context-Switches und anderen Sync-Overhead erzeugst.
Synchronize
Aber ganz ehrlich und nicht böse gemeint ... wer hat denn heutzutage auch noch so einen Holz-Computer, dass beim Anzeigen eines Hints ernsthaft CPU Leistung fehlt :lol: |
AW: Form in neuem Thread laufen lassen
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:
|
AW: Form in neuem Thread laufen lassen
Mit einem solchen Thread könntest du dein Formular ruckelfrei einblenden:
Delphi-Quellcode:
unit Unit45;
interface uses Vcl.Forms, System.Types, System.Classes; type TScroll = class(TThread) private { Private-Deklarationen } anzeigepos : TPoint; function Zeit : Cardinal; function berechnepos( anteil : extended ) : TPoint; protected procedure Execute; override; procedure Ausgeben; public meinupdateform : TForm; startpos, zielpos : TPoint; startzeit, scrollzeit : Cardinal; end; implementation uses unit44; procedure TScroll.Ausgeben; begin meinupdateform.Left := anzeigepos.x; meinupdateform.top := anzeigepos.y; end; function TScroll.Zeit : Cardinal; begin Result := GetTickCount; // du könntest hier auch einen anderen Zeitmesser einbauen... end; function TScroll.berechnepos( anteil : extended ) : TPoint; begin if anteil >= 1 then begin Result.X := zielpos.X; Result.Y := zielpos.Y; end else begin Result.X := round(anteil*(zielpos.X - startpos.X)+startpos.X); Result.Y := round(anteil*(zielpos.Y - startpos.Y)+startpos.Y); end; end; procedure TScroll.Execute; var lastanteil, anteil : extended; lastp : TPoint; begin startzeit := Zeit; meinupdateform.Left := startpos.X; meinupdateform.Top := startpos.Y; meinupdateform.Show; lastanteil := -1; repeat anteil := ( GetTickCount - startzeit )/scrollzeit; // läuft von 0..1 if anteil > lastanteil then begin lastanteil := anteil; anzeigepos := berechnepos( anteil ); Synchronize( Ausgeben ); end; until ( anteil >= 1 ) or terminated; end; end. So würdest du die Sache aufrufen:
Delphi-Quellcode:
Ich hab's mit eingeblendetem Hint gecheckt. Bei mir ruckelt nix.
procedure TForm43.updatescroll;
begin scroll := TScroll.Create( true ); scroll.FreeOnTerminate := true; scroll.Priority := tpHigher; scroll.startpos := Point( -form44.Width, -0 ); // startpunkt scroll.zielpos := Point( 0, 0 ); // zielpunkt scroll.scrollzeit := 300; // in Millisekunden scroll.meinupdateform := form44; scroll.Start; end; Natürlich könnte man den Code schöner schreiben... ;-). |
AW: Form in neuem Thread laufen lassen
Aber es ist wie dir Zacherl geschrieben hat: Mit Synchronize musst du automatisch in den Hauptthread - und wenn dich dort was ausbremst, dann nützt dir die Nutzung eines Threads rein gar nix.
Du könntest natürlich eine eigenständige exe schreiben, welche einfach deine Updateinfp einblendet ;-). |
AW: Form in neuem Thread laufen lassen
Zitat:
|
AW: Form in neuem Thread laufen lassen
Danke Michael,
Genauso hatte ich das vor. Ich hatte das Scrollen selbst ja in einem Thread, und hatte irrtümlicherweise angenommen ein synchronize würde nur zwischen den beteiligten Threads synchronisieren. Ich werde das so mal umsetzen und ausprobieren. Der Gedanke mit einer eigenen Exe hatte ich auch schon, hab den aber wieder verworfen, weil ich das noch so schön finde. Gruß Hobbycoder |
AW: Form in neuem Thread laufen lassen
Hallo Hobbycoder
für Windows 10 könntest du auch das Windows Notification Center nutzen. Ein Beispiel findest du hier: ![]() Noch einmal zu deinem Scrollen: Wenn du aufwändige Arbeiten durchführen musst, dann lagere diese immer in einen Thread aus. Selbst wenn du im Hauptthread sowas wie
Delphi-Quellcode:
aufrufst: Dein "Scroll Thread" wird blockiert, bis die "procedure BenoetigtVielZeit" beendet ist.
procedure BenoetigtVielZeit;
var i : integer; var i := 0; while i < 1000000000 do begin inc(i); end; end; Natürlich könntest du all deine zeitaufwändigen Dinge im Hauptthread laufen lassen und immer wieder Application.ProcessMessages aufrufen (dann würde dein Scroll Thread weiter laufen) - das ist aber absolut nicht empfehlenswert. Ein Programm, welches Application.Processmessages verwendet gehört nicht auf eine Festplatte :-D.
Delphi-Quellcode:
procedure BlockiertNichtIstAberMist;
var i : integer; var i := 0; while i < 1000000000 do begin inc(i); Application.ProcessMessages; end; end; Gruss M |
AW: Form in neuem Thread laufen lassen
Wir benutzen für Threadausgaben auch NonVCL-Fenster. Das ist einer der wenigen Fälle, in denen das wirklich Sinn macht. Denn da kann man wirklich aus dem Thread heraus das Fenster komplett ohne Synchronisation verwenden. Wenn man dann mehrere Threads hat und mehrere solcher Fenster und die auch ohne Blockierungen zu verursachen bewegen kann, ist das schon sehr angenehm.
Gut, die Entwicklung der Fenster ist nicht so schön, aber mit ein paar Wrapperklassen lässt sich das quasi wie ein VCL-Formular nutzen. |
AW: Form in neuem Thread laufen lassen
Zitat:
Und noch einmal zum Thema Scrollen: Das passiert z.B. wenn ein Hint angezeigt wird, und teilweise bei bestimmten Aktionen der verwendeten Fremdkomponenten. Am deutlichsten ist es aber bei Hints. Darauf habe ich keinen Einfluss und kann das nicht in einen Thread auslagern. Ich denke der Weg die Updatemeldung in eine Thread zu packen ist schneller zu realisieren. Ansonsten läuft schon alles, was in irgendeiner Form Last verursacht bereits in einem Thread. Aber ein paar Interaktionen müssen schon noch im Hauptthread laufen. Und alle Proceduren, die im Hauptthread laufen, sind bisher nicht für das stocken verantwortlich, das habe ich geprüft. Zitat:
|
AW: Form in neuem Thread laufen lassen
Zitat:
Aber da steckt eigentlich nicht viel dahinter, wenn man es richtig macht. Das Fenster selbst steckt ja in einer Ressource. Das schöne ist nun, dass nach der Anzeige eines solchen Fensters dessen Controls ja alle da sind. Man findet die also direkt. Die Klasse für solch einen Dialog ist nun von TThread abgeleitet. Beim Start des Threads zeigt der das Fenster mit ShowWindow an. Danach findet man mit EnumChildWindows die Controls, wobei als Userpointer einfach der eigene Dialog angegeben ist. So bekommt die Threadinstanz einen Methodenaufruf pro gefundenem Control. Mit GetClassName bekommt man nun den Typ des Controls heraus und erstellt eine passende Wrapperklasse, alle abgeleitet von einer Basisklasse. Die Instanz kann man sich dann direkt merken um über die ID an die entsprechende Wrapperinstanz zu kommen. Das Basiscontrol braucht sich nur das Fensterhandle usw. zu merken. Und ein Wrapper für ein Edit-Control muss eine property Text haben, die auf WM_GETTEXT, SetDlgItemText usw. geht. |
AW: Form in neuem Thread laufen lassen
Du könntest eine zweite Instanz deiner Anwendung starten, die auf Grund eines Parameters nur das Scroll-Window erzeuugt und steuert. Damit sparrst du dir eine zusätzliche Exe und brauchst nicht auf die VCL verzichten.
|
AW: Form in neuem Thread laufen lassen
Zitat:
Zitat:
ich werde mich da mal dran versuchen. Wenn man sich erst mal so eine Wrapperklasse zusammengestrickt hat, kann man die ja auch ganz nett weiterverwenden. Mal schauen wie weit ich komme ;-) Zitat:
|
AW: Form in neuem Thread laufen lassen
Zitat:
Ja das Thema würde mich aktuelle auch interessieren. Vielleicht könnten wir uns da mal was austauschen. Ich benötige aktuell wie der jaenicke einsetzt eine einfache Windowsanzeige, der Statusmeldungen und Progressbar in einem Thread unabhändig vom Haupthread anzeigt. |
AW: Form in neuem Thread laufen lassen
Würde denn Interesse bestehen an dem bestehenden Quelltext weiterzuarbeiten? Das ist nicht viel bisher, nur was ich geschildert habe ausprogrammiert und nur Wrapper für einen Button, ein Edit und ein Memo (mehr brauchten wir bisher nicht ;-)). Alle 5 Units zusammen umfassen nur rund 350 Zeilen. Aber es funktioniert so wie es ist.
Es gibt vielleicht die Möglichkeit das ganze unter GPL/LGPL/MPL Triple Lizenz zur Verfügung zu stellen sofern Interesse besteht gemeinsam daran zu arbeiten. Das müsste ich dann bei uns klären. // EDIT: Die Möglichkeit bestünde, wenn sich noch andere finden, die an dem Projekt mitarbeiten möchten. |
AW: Form in neuem Thread laufen lassen
Zitat:
Danke fürs Angebot, Interesse ja, aber ich weiss nicht, ob es nicht für mich zu steil ist, da mit zu arbeiten. :oops: lg, jus |
AW: Form in neuem Thread laufen lassen
Zitat:
|
AW: Form in neuem Thread laufen lassen
Was ich bisher in Luckie Tutorial gelesen habe, sollte das ganze mehr oder weniger problemlos aus dem Tutorial abzuleiten sein. Das ganze könnte man sicherlich gut in eine/mehrere Klassen verpacken und eben wie schon Jaenicke sagt einen schicken Wrapper erstellen, so dass man sich damit dann leicht hier und da ein nonVCL-Dialog ganz nach den verschiedenen Ansprüchen erstellen kann.
Sicherlich hat jeder hier, der Interesse bekundet, etwas andere Anforderungen. Natürlich ist es nicht ganz so mal eben gemacht, aber ein interessantes Thema. Wenn Jaenicke seinen Wrapper zur Verfügung stellt, dann müsste jeder auch erst mal schauen, in wie weit er damit klar kommt, bzw. ob die darin enthaltenen Möglichkeiten für sein Vorhaben bereits ausreicht. Ich muss mir Luckie's Tutorial mal ausdrucken (kann besser Papier lesen, als am Bildschirm). Und dann fange ich einfach mal an. Ein Fenster mit 2 Buttons hat schon mal ganz gut geklappt. Leider hatte ich in den letzten Tage nicht die Zeit, mich damit weiter ausgiebig zu beschäftigen. Werde ich aber zeitnah nachholen. |
AW: Form in neuem Thread laufen lassen
Liste der Anhänge anzeigen (Anzahl: 1)
Hallo,
naja, dann zeige ich mal, was ich bisher habe, viel ist es nicht und ich weiss nicht einmal, ob es überhaupt so richtig ist. Das Projekt besteht aus 2 Units. Habe das gesamte Projekt auch Zip Anhang angehängt. Habe probeweise auf die Hauptunit folgende Unit1.pas ein Button, Memo und Progressbar draufgeklatscht.
Delphi-Quellcode:
In der folgenden Unit2.pas ist der Thread drin:
unit Unit1;
interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls,unit2, ComCtrls; type TForm1 = class(TForm) Button1: TButton; Memo1: TMemo; ProgressBar1: TProgressBar; procedure FormCreate(Sender: TObject); procedure Button1Click(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form1: TForm1; DataThread: TDataThread; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var i,j: Integer; begin for I := 0 to 100000 do begin for j := 0 to 100 do progressBar1.Position:=j; end; end; procedure TForm1.FormCreate(Sender: TObject); begin DataThread := tDataThread.Create(''); end; end.
Delphi-Quellcode:
Das Formular im Thread enhält ein Button und eine Listbox wie folgt in main.rc definiert.
unit Unit2;
interface uses classes, windows, Messages; type TDataThread = class(TThread) private hdlg: DWORD ; protected procedure Execute; override; public constructor Create(const Title:String); reintroduce; end; function dlgfunc(hwnd: hwnd; umsg: dword; wparam: wparam; lparam: lparam): bool; stdcall; implementation {$R main.res} //hier kommt die Vorlage rein function dlgfunc(hwnd: hwnd; umsg: dword; wparam: wparam; lparam: lparam): bool; stdcall; begin result := true; case umsg of WM_CLOSE: EndDialog(hWnd, 0); WM_DESTROY: PostQuitMessage(0); WM_COMMAND: if hiword(wparam) = BN_CLICKED then begin case loword(wparam) of IDOK: begin messagebox(hwnd, 'OK Button gedrückt', 'Meldung', 0); sendmessage(hwnd, WM_CLOSE, 0, 0); // PostQuitMessage(0); end; end; end; else result := false; end; end; constructor TDataThread.Create(const Title: String); begin inherited Create(False); hdlg := CreateDialog(HInstance, MAKEINTRESOURCE(100), Self.Handle, @DlgFunc); ShowWindow(hdlg, SW_SHOW); end; procedure TDataThread.Execute; var Msg: TMsg; begin while not terminated do begin if GetMessage(msg,0,0,0) then begin if not(IsDialogMessage(hdlg, Msg)) then begin TranslateMessage(Msg); DispatchMessage(Msg); end; end; end; end; end.
Code:
Der TDataThread ladet einfach per Windows API "CreateDialog" das Threaddialogfenster rein und mit "ShowWindow" wird es angezeigt. Wie man in Unit2.pas sieht, habe ich brutal im Thread eine Nachrichtenschleifen eingebaut. Das Threaddialogfenster reagiert sogar, wenn man auf den Button drückt. Ich muß zugeben, dass ich aber nicht weiß, ob es so überhaupt richtig ist, oder komplett auf dem Holzweg bin. Außerdem habe ich festgestellt, dass wenn ich in Unit1.pas auf Button1 klicke, womit der Hauptthread auf meinem Rechner kurzfristig überlastet wird, das Threaddialogfenster in dieser Zeit auch nicht mehr reagiert. Normalerweise sollte doch das Threaddialogfenster unabhängig vom Hauptfenster sein? :gruebel:
#define IDC_LIST 1002
LANGUAGE LANG_NEUTRAL, SUBLANG_NEUTRAL 100 DIALOGEX 6, 18, 264, 200 STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | WS_SYSMENU EXSTYLE WS_EX_TOOLWINDOW CAPTION "Fenster-Caption" FONT 8, "Arial" BEGIN PUSHBUTTON "OK", IDOK, 61, 65, 140, 14 LISTBOX IDC_LIST,7,25,138,106,LBS_NOINTEGRALHEIGHT | LBS_EXTENDEDSEL | WS_VSCROLL | WS_TABSTOP END Bitte um Unterstützung. :oops: lg, jus |
AW: Form in neuem Thread laufen lassen
Erzeuge alles im execute, das create ist noch im hauptthread, du musst das Window in dem Tread erzeugen in dem es benutzt werden soll.
|
AW: Form in neuem Thread laufen lassen
Außerdem wäre mein Anspruch, die Forms nicht über Resourcen zu definieren. Damit wäre es dynamischer einsetzbar.
|
AW: Form in neuem Thread laufen lassen
Hallo HobbyCoder
hier findest du u.a. auch ein Beispiel "Fenster ohne VCL via sep. Thread steuern": ![]() Du siehst auf dieser Seite auch, wie du Buttons und Co - wie du es gern hättest - zur Laufzeit generierst. (Ich hatte dir den Link via PM gesendet, du hast ihn wohl übersehen :-D.) |
AW: Form in neuem Thread laufen lassen
Danke Michael. Hatte ich auch schon gesehen.
Ich werde das auch erst mal so stumpf umsetzen, damit ich die Anforderungen, die ich in meiner Anwendung habe, erst einmal am Laufen habe. Da ich aber denke, dass ich sowas öfter mal gebrauchen könnte, werde ich mir daraus mal was basteln, was dann leicht wieder verwendbar ist. Wenn das soweit ist, dann werde ich das hier mal posten. Wird aber noch etwas dauern, weil ich leider nicht meine komplette Zeit dafür zur Verfügung stellen kann. |
AW: Form in neuem Thread laufen lassen
Hey :)
Da mich dieses Thema auch schon lange interessiert hat; man könnte sich bei solchen Wrapperklassen auch noch einen weiteren Vorteil sichern: Das Verwenden mehrerer Threads. Das Argument "Synchronisierung" kann man damit tatsächlich auch erschlagen. Windows bietet von Haus aus schon eine Messagequeue für die Nachrichtenbehandlung der Fensterereignisse an (klar, muss ja intern auch irgendwie funktionieren). Das MSDN schreibt dazu: ![]() Zitat:
![]()
Delphi-Quellcode:
vor. Man registriert eine Callback Funktion auf einen
WndProc
Delphi-Quellcode:
-Code, sodass man dann einfach jedes Mal, wenn man der GUI etwas mitteilen möchte, einfach
Msg
Delphi-Quellcode:
aufruft (oder wenn man das an ein
PostThreadMessage
Delphi-Quellcode:
binden möchte, eben
hwnd
![]()
Delphi-Quellcode:
/
SendMessage
![]()
Delphi-Quellcode:
) und die Synchronisierung einfach über Windowsboardmittel durchführt. Wisst ihr was ich meine? Macht das für euch auch Sinn? Ich denke, dass man so zum Beispiel ein Log in einem Memo führen könnte, da die Messages ja immer hübsch einzeln "reinblubbern" - und das egal wie viele Threads im Hintergrund beteiligt sind.
PostMessage
Brighty P.S. Habe gerade interessehalber ![]() |
AW: Form in neuem Thread laufen lassen
Zitat:
Delphi-Quellcode:
heraus Daten an das Hauptformular zu schicken ohne
TThread.Execute
Delphi-Quellcode:
zu verwenden.
Synchronize
|
AW: Form in neuem Thread laufen lassen
Zitat:
Nun zur eigentlichen Frage, ich habe die folgende Messagequeue mit IsDialogMessage, TranslateMessage u. DispatchMessage für den eigenständigen Nonvcl Thread mit:
Delphi-Quellcode:
überhaupt richtig gemacht, oder würdet ihr es anders lösen? oder anders gefragt, braucht man die überhaupt? :gruebel:
procedure TDataThread.Execute;
var Msg: TMsg; begin hdlg := CreateDialog(HInstance, MAKEINTRESOURCE(100), Self.Handle, @DlgFunc); ShowWindow(hdlg, SW_SHOW); while not terminated do begin if GetMessage(msg,0,0,0) then begin if not(IsDialogMessage(hdlg, Msg)) then begin TranslateMessage(Msg); DispatchMessage(Msg); end; end; end; end; lg, jus |
AW: Form in neuem Thread laufen lassen
Zitat:
Delphi-Quellcode:
notwendig? Glaube das kannst du weglassen.
IsDialogMessage
|
AW: Form in neuem Thread laufen lassen
Die Methode TThread.Create(...) rufts du ja im Hauptthread auf. Wo soll das Fenster also anders "laufen" als im Hauptthread?
|
AW: Form in neuem Thread laufen lassen
Zitat:
|
AW: Form in neuem Thread laufen lassen
Liste der Anhänge anzeigen (Anzahl: 1)
Wenn du zum Beispiel eine Methode Test hättest und diese aufrufst, würdest du ja auch nicht erwarten, dass diese in einem anderen Thread läuft. TThread.Create ist auch nur eine normale Methode, die als Konstruktor fungiert. Dementsprechend wird diese auch im Hauptthread bleiben und ausgeführt werden, wenn du diese im Hauptthread aufrufst.
Execute rufst du nicht selbst auf, sondern wird intern aus dem neuen Thread heraus aufgerufen. Deshalb ist das nicht mehr im Kontext des Hauptthreads. Übrigens siehst du in der Liste der Threads im Debugger auch in welchem du dich gerade befindest, wenn du gerade debuggst. Ich werde dann einmal ein Repository vorbereiten und die Units dort zur Verfügung stellen inkl. Demo. Im Anhang mal die Demo... (das Hauptfenster ist VCL, die Threadfenster nicht) Deren Quelltext sieht so aus:
Delphi-Quellcode:
Beim Beenden knallt die Demo, wenn noch Threadfenster offen sind. Das liegt daran, dass ich aktuell die Threads vor dem Beenden nicht beende, das werde ich noch sauber machen.
TThread.CreateAnonymousThread(procedure
var ExampleDialog: TNonVclDialog; ExampleControl: TNonVclEdit; i, j: Integer; begin TThread.NameThreadForDebugging('Testtext'); ExampleDialog := TNonVclDialog.Create(1901); try ExampleDialog.Show; ExampleControl := ExampleDialog.Get<TNonVclEdit>(4001); ExampleDialog.Get<TNonVclButton>(IDOK).OnClick := DialogButtonClick; for i := 0 to 10 do begin for j := 0 to 10 do begin ExampleControl.Text := ExampleControl.Text + 'abc'; Sleep(100); end; ExampleControl.Text := ExampleControl.Text + 'abc'#13#10; end; finally ExampleDialog.Free; end; end).Start; |
AW: Form in neuem Thread laufen lassen
Achso. Jetzt habe ich den Kommentar von Luckie verstanden.
Die Methode Create läuft noch im Hauptthread, wohingegen die Methode Execute breites im Thread läuft. |
AW: Form in neuem Thread laufen lassen
Zitat:
Zitat:
lg, jus |
AW: Form in neuem Thread laufen lassen
Zitat:
Was mich nur wundert, ist, daß bei jeder Erzeugung eines neuen Threadfensters der Threadzähler im Taskmanager um 2 inkrementiert wird. Ganz fein wären Thread-Showmessages, und dann sogar zwei Arten davon: Ein blockierendes und ein nichtblockierendes. Input- und Messageboxen wären auch nicht schlecht. Aber ich bin ein wenig unverschämt, ich weiß...es sind nur Anregungen, die aufzeigen, auf was für Fensterfunktionalität - neben den "normalen" Formularen - in Threads (bisher?) so alles verzichtet werden muß. |
AW: Form in neuem Thread laufen lassen
Code:
Ein anderer Ansatz wäre die VCL-Forms in eine Dll zu legen. Ohne Packages! Die Dll kann dann in einen Thread geladen werden. Man spart sich den den NonVcl Kram und kann seine Formulare ganz normal designen.
Ganz fein wären Thread-Showmessages, und dann sogar zwei Arten davon: Ein blockierendes und ein nichtblockierendes. Input- und Messageboxen wären auch nicht schlecht. Aber ich bin ein wenig unverschämt, ich weiß...es sind nur Anregungen, die aufzeigen, auf was für Fensterfunktionalität - neben den "normalen" Formularen - in Threads (bisher?) so alles verzichtet werden muß.
Wir benutzen diesen Ansatz eigentlich immer. Funktioniert auch in mixed Umgebungen (Delphi VC++) etc sehr gut. |
AW: Form in neuem Thread laufen lassen
Zitat:
Delphi-Quellcode:
Ist doch total simpel?
procedure ShowMessageThreaded(const Msg: string; const TitleCaption: string = '');
begin TThread.CreateAnonymousThread( procedure begin Winapi.Windows.MessageBox(0, PChar(Msg), PChar(TitleCaption), 0); end ).Start; end; procedure TForm2.FormCreate(Sender: TObject); begin ShowMessageThreaded('Hello'); end; Einfach im Thread die entsprechend gewünschten Windowsfunktionen aufrufen? |
Alle Zeitangaben in WEZ +1. Es ist jetzt 17:29 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-2025 by Thomas Breitkreuz