Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   GUI-Design mit VCL / FireMonkey / Common Controls (https://www.delphipraxis.net/18-gui-design-mit-vcl-firemonkey-common-controls/)
-   -   Delphi Form in neuem Thread laufen lassen (https://www.delphipraxis.net/192420-form-neuem-thread-laufen-lassen.html)

Hobbycoder 17. Apr 2017 12:55

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:
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.
So aufgerufen:
Delphi-Quellcode:
  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);
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.

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

Zacherl 17. Apr 2017 13:05

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:
Synchronize
ist in jedem Falle notwendig. Dein
Delphi-Quellcode:
DoStep
z.b. synchronisiert den Zugriff auf
Delphi-Quellcode:
TForm.Left
nicht korrekt.

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.

Hobbycoder 17. Apr 2017 13:27

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.

Michael II 17. Apr 2017 13:30

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

Hobbycoder 17. Apr 2017 13:51

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.

Michael II 17. Apr 2017 14:24

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: http://docwiki.embarcadero.com/Libra...ad.Synchronize oder in diesem Forum.

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.

Zacherl 17. Apr 2017 15:46

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Hobbycoder (Beitrag 1367961)
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.

Dagegen wirst du nichts machen können. Durch die Thread-Lösung wird es im Vergleich zum Timer aber nur noch schlimmer werden, da du wie gesagt durch das
Delphi-Quellcode:
Synchronize
sowieso wieder im Haupt-Thread arbeitest und zusätzlich noch Context-Switches und anderen Sync-Overhead erzeugst.

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:

Uwe Raabe 17. Apr 2017 16:04

AW: Form in neuem Thread laufen lassen
 
Liste der Anhänge anzeigen (Anzahl: 2)
Zitat:

Zitat von Zacherl (Beitrag 1367967)
wer hat denn heutzutage auch noch so einen Holz-Computer

Sieht doch schick aus, oder?

Michael II 17. Apr 2017 16:18

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:
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;
Ich hab's mit eingeblendetem Hint gecheckt. Bei mir ruckelt nix.
Natürlich könnte man den Code schöner schreiben... ;-).

Michael II 17. Apr 2017 16:40

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 ;-).

a.def 17. Apr 2017 16:58

AW: Form in neuem Thread laufen lassen
 
Zitat:

scroll.startpos := Point( -form44.Width, -0 ); // startpunkt
scroll.zielpos := Point( 0, 0 ); // zielpunkt
Wenn man einen Monitor links neben dem Hauptmonitor hat, dann würde das hier ziemlich hässlich aussehen.

Hobbycoder 17. Apr 2017 19:05

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

Michael II 17. Apr 2017 22:42

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:
http://docwiki.embarcadero.com/CodeE...cations_Sample


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:
procedure BenoetigtVielZeit;
var i : integer;
var
  i := 0;
  while i < 1000000000 do
  begin
      inc(i);
  end;
end;
aufrufst: Dein "Scroll Thread" wird blockiert, bis die "procedure BenoetigtVielZeit" beendet ist.

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

jaenicke 18. Apr 2017 05:09

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.

Hobbycoder 18. Apr 2017 07:06

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Michael II (Beitrag 1368004)
Hallo Hobbycoder

für Windows 10 könntest du auch das Windows Notification Center nutzen.

Ein Beispiel findest du hier:
http://docwiki.embarcadero.com/CodeE...cations_Sample


Noch einmal zu deinem Scrollen:
Wenn du aufwändige Arbeiten durchführen musst, dann lagere diese immer in einen Thread aus.

An das Windows Notifacation Center hatte ich auch schon gedackt. Ich glaube nur Knapp 5-10% der Kundenrechner arbeiten schon unter Win10 (Für einen Großteil der verwendeten Anwendungen gibt es noch keine Freigabe für Win10). Von daher kann ich die nicht überall nutzen. Und auf Updates aufmerksam machen kann ich nicht von Win10 abhängig machen.

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:

Zitat von jaenicke (Beitrag 1368008)
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.

Hm....klinkt verlockend. Aber ich nehme mal an, du möchtest das nicht hier posten?:wink:

jaenicke 18. Apr 2017 09:54

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Hobbycoder (Beitrag 1368011)
Hm....klinkt verlockend. Aber ich nehme mal an, du möchtest das nicht hier posten?:wink:

Darf ich leider nicht...
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.

Blup 18. Apr 2017 10:10

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.

Hobbycoder 18. Apr 2017 10:42

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von jaenicke (Beitrag 1368035)
Darf ich leider nicht....

Dachte ich mir schon. :-)


Zitat:

Zitat von jaenicke (Beitrag 1368035)
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.

Ich habe mir das schon mal in Luckie's Tutorial angesehen. So in der Theorie klingt das alles nicht so schwer. Allerdings in der Praxis sieht das dann doch etwas anders aus ;-)
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:

Zitat von Blup (Beitrag 1368045)
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.

Das wär dann Plan B.

jus 20. Apr 2017 18:15

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Hobbycoder (Beitrag 1368056)
Zitat:

Zitat von jaenicke (Beitrag 1368035)
Darf ich leider nicht....

Dachte ich mir schon. :-)


Zitat:

Zitat von jaenicke (Beitrag 1368035)
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.

Ich habe mir das schon mal in Luckie's Tutorial angesehen. So in der Theorie klingt das alles nicht so schwer. Allerdings in der Praxis sieht das dann doch etwas anders aus ;-)
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:

Zitat von Blup (Beitrag 1368045)
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.

Das wär dann Plan B.

@Hobbycoder:
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.

jaenicke 21. Apr 2017 05:54

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.

jus 22. Apr 2017 22:14

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von jaenicke (Beitrag 1368527)
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.

@jaenicke:
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

Delphi-Laie 22. Apr 2017 22:53

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von jaenicke (Beitrag 1368527)
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.

Ich lese diese Diskussion auch mit. Soweit ich das verstehe, besteht die Möglichkeit, Formulare (ob nun VCL oder eher doch nicht) in Nicht-VCL-Threads zu nutzen. Daran bin auch ich interessiert, denn sogar ein schödes Showmessage oder auch nur die Änderung einer VCL-Formulartitelleiste, ja sogar einfache Pieptöne auszugeben, alles ist in Extra-Threads nur unter umständlich-verrenkter Benutzung der Synchronisation möglich. Das würde sich mit einem solchen Projekt anscheinend ändern. Also, Interesse ja, aber ob meine Kenntnisse ausreichen, bei der Erstellung mitzuwirken, halte ich für fraglich, leider.

Hobbycoder 23. Apr 2017 00:38

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.

jus 23. Apr 2017 01:58

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:
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.
In der folgenden Unit2.pas ist der Thread drin:
Delphi-Quellcode:
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.
Das Formular im Thread enhält ein Button und eine Listbox wie folgt in main.rc definiert.
Code:
#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
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:

Bitte um Unterstützung. :oops:

lg,
jus

Fritzew 23. Apr 2017 08:18

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.

Hobbycoder 23. Apr 2017 08:50

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.

Michael II 23. Apr 2017 12:19

AW: Form in neuem Thread laufen lassen
 
Hallo HobbyCoder


hier findest du u.a. auch ein Beispiel "Fenster ohne VCL via sep. Thread steuern":

http://stackoverflow.com/questions/1...hout-using-vcl

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.)

Hobbycoder 23. Apr 2017 12:32

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.

BrightAngel 23. Apr 2017 15:00

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:

The function fails if the specified thread does not have a message queue. The system creates a thread's message queue when the thread makes its first call to one of the User or GDI functions. For more information, see the Remarks section.
Man könnte die PostThreadMessage Funktion also benutzen, um kustomisiert auf bestimmte Threadereignisse zu reagieren; oder sehe ich das falsch? Mir schwebt sowas wie ein "Callback" für die
Delphi-Quellcode:
WndProc
vor. Man registriert eine Callback Funktion auf einen
Delphi-Quellcode:
Msg
-Code, sodass man dann einfach jedes Mal, wenn man der GUI etwas mitteilen möchte, einfach
Delphi-Quellcode:
PostThreadMessage
aufruft (oder wenn man das an ein
Delphi-Quellcode:
hwnd
binden möchte, eben
Delphi-Quellcode:
SendMessage
/
Delphi-Quellcode:
PostMessage
) 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.

Brighty

P.S. Habe gerade interessehalber diesen Artikel gelesen. Vielleicht interessiert er in diesem Kontext den ein oder anderen. :) (Falls jemand sprachliche Unterstützung möchte, kann ich btw. gerne übersetzend aktiv werden...)

Zacherl 23. Apr 2017 17:16

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von BrightAngel (Beitrag 1368744)
Macht das für euch auch Sinn?

Das ist in der Tat sogar eine sehr beliebte Synchronisierungsmethode. Kann man auch ohne Probleme verwenden, um aus einem
Delphi-Quellcode:
TThread.Execute
heraus Daten an das Hauptformular zu schicken ohne
Delphi-Quellcode:
Synchronize
zu verwenden.

jus 23. Apr 2017 22:42

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Fritzew (Beitrag 1368696)
Erzeuge alles im execute, das create ist noch im hauptthread, du musst das Window in dem Tread erzeugen in dem es benutzt werden soll.

@Fritzew: Tatsächlich, habe den Createdialog ins Execute verschoben und schon reagiert das Fenster unabhängig von der Auslastung vom Hauptthread. Bin beeindruckt, habe nicht gewußt, dass anscheinend TThread.Create noch im Hauptthread läuft. :oops: Vielen Dank für dem essentiellen Hinweis! :thumb:

Nun zur eigentlichen Frage, ich habe die folgende Messagequeue mit IsDialogMessage, TranslateMessage u. DispatchMessage für den eigenständigen Nonvcl Thread mit:
Delphi-Quellcode:
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;
überhaupt richtig gemacht, oder würdet ihr es anders lösen? oder anders gefragt, braucht man die überhaupt? :gruebel:

lg,
jus

Zacherl 24. Apr 2017 00:09

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von jus (Beitrag 1368766)
braucht man die überhaupt? :gruebel:

Ja, die brauchst du in jedem Falle. Aber ist das
Delphi-Quellcode:
IsDialogMessage
notwendig? Glaube das kannst du weglassen.

Luckie 24. Apr 2017 01:44

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?

Hobbycoder 24. Apr 2017 07:34

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Luckie (Beitrag 1368771)
Die Methode TThread.Create(...) rufts du ja im Hauptthread auf. Wo soll das Fenster also anders "laufen" als im Hauptthread?

Was heißt das jetzt?

jaenicke 24. Apr 2017 07:49

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

Hobbycoder 24. Apr 2017 07:55

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.

jus 24. Apr 2017 11:09

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Luckie (Beitrag 1368771)
Die Methode TThread.Create(...) rufts du ja im Hauptthread auf. Wo soll das Fenster also anders "laufen" als im Hauptthread?

@Luckie: Yup, klingt natürlich logisch, ich war aber bisher der falschen Meinung, dass es bereits schon im Thread läuft. :oops:

Zitat:

Zitat von jaenicke (Beitrag 1368782)
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:
  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;
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.

@jaenicke: auf den Code bin ich schon sehr gespannt, vielen Dank mal im Voraus!

lg,
jus

Delphi-Laie 24. Apr 2017 12:26

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von jaenicke (Beitrag 1368782)
Im Anhang mal die Demo... (das Hauptfenster ist VCL, die Threadfenster nicht)

Hallo Sebastian, danke, das sieht interessant aus!

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ß.

Fritzew 24. Apr 2017 12:56

AW: Form in neuem Thread laufen lassen
 
Code:
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ß.
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.
Wir benutzen diesen Ansatz eigentlich immer. Funktioniert auch in mixed Umgebungen (Delphi VC++) etc sehr gut.

TiGü 24. Apr 2017 13:01

AW: Form in neuem Thread laufen lassen
 
Zitat:

Zitat von Delphi-Laie (Beitrag 1368858)
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ß.

Delphi-Quellcode:
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;
Ist doch total simpel?
Einfach im Thread die entsprechend gewünschten Windowsfunktionen aufrufen?


Alle Zeitangaben in WEZ +1. Es ist jetzt 23:14 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz