AGB  ·  Datenschutz  ·  Impressum  







Anmelden
Nützliche Links
Registrieren
Thema durchsuchen
Ansicht
Themen-Optionen

Form in neuem Thread laufen lassen

Ein Thema von Hobbycoder · begonnen am 17. Apr 2017 · letzter Beitrag vom 27. Jul 2017
Antwort Antwort
Seite 1 von 2  1 2      
Hobbycoder

Registriert seit: 22. Feb 2017
1.016 Beiträge
 
#1

AW: Form in neuem Thread laufen lassen

  Alt 17. Apr 2017, 19:05
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
  Mit Zitat antworten Zitat
Michael II

Registriert seit: 1. Dez 2012
Ort: CH BE Eriswil
776 Beiträge
 
Delphi 11 Alexandria
 
#2

AW: Form in neuem Thread laufen lassen

  Alt 17. Apr 2017, 22:42
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 .

Delphi-Quellcode:
procedure BlockiertNichtIstAberMist;
var i : integer;
var
  i := 0;
  while i < 1000000000 do
  begin
      inc(i);
      Application.ProcessMessages;
  end;
end;

Gruss
M
Michael Gasser
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
10.043 Beiträge
 
Delphi 12 Athens
 
#3

AW: Form in neuem Thread laufen lassen

  Alt 18. Apr 2017, 05:09
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.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
1.016 Beiträge
 
#4

AW: Form in neuem Thread laufen lassen

  Alt 18. Apr 2017, 07:06
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.

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?
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
10.043 Beiträge
 
Delphi 12 Athens
 
#5

AW: Form in neuem Thread laufen lassen

  Alt 18. Apr 2017, 09:54
Hm....klinkt verlockend. Aber ich nehme mal an, du möchtest das nicht hier posten?
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.
Sebastian Jänicke
AppCentral
  Mit Zitat antworten Zitat
Blup

Registriert seit: 7. Aug 2008
Ort: Brandenburg
1.490 Beiträge
 
Delphi 12 Athens
 
#6

AW: Form in neuem Thread laufen lassen

  Alt 18. Apr 2017, 10:10
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.
  Mit Zitat antworten Zitat
Hobbycoder

Registriert seit: 22. Feb 2017
1.016 Beiträge
 
#7

AW: Form in neuem Thread laufen lassen

  Alt 18. Apr 2017, 10:42
Darf ich leider nicht....
Dachte ich mir schon.


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

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.
  Mit Zitat antworten Zitat
jus

Registriert seit: 22. Jan 2005
350 Beiträge
 
Delphi 2007 Professional
 
#8

AW: Form in neuem Thread laufen lassen

  Alt 20. Apr 2017, 18:15
Darf ich leider nicht....
Dachte ich mir schon.


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

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.
  Mit Zitat antworten Zitat
Benutzerbild von jaenicke
jaenicke

Registriert seit: 10. Jun 2003
Ort: Berlin
10.043 Beiträge
 
Delphi 12 Athens
 
#9

AW: Form in neuem Thread laufen lassen

  Alt 21. Apr 2017, 05:54
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.
Sebastian Jänicke
AppCentral

Geändert von jaenicke (21. Apr 2017 um 11:52 Uhr)
  Mit Zitat antworten Zitat
jus

Registriert seit: 22. Jan 2005
350 Beiträge
 
Delphi 2007 Professional
 
#10

AW: Form in neuem Thread laufen lassen

  Alt 29. Apr 2017, 02:06
...
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.
Möchte mal meine spartanische Machbarkeitsstudie von jaenicke's obiger Beschreibung mal reinstellen, wo ein Dialog-Form in einem eigenen Thread unabhängig vom VCL Hauptthread läuft. Habe das komplette Projekt auch als Zip-Anhang angehängt. Vielleicht kanns ja wer mal brauchen. Danke nochmals an alle Helfer!
Was drinnen noch fehlt sind die Wrapper für die Controls.

Die folgende Unit1.pas ist die normale VCL.
Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls,unit2, ComCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    ProgressBar1: TProgressBar;
    Button2: TButton;
    procedure FormCreate(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  i,j: Integer;
begin
  for I := 0 to 50000 do
  begin
    for j := 0 to 100 do progressBar1.Position:=j;
  end;
end;

procedure TForm1.Button2Click(Sender: TObject);
begin
  DialogThread.Show;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  DialogThread := TDialogThread.Create;
  WorkerThread := TWorkerThread.Create;
end;

end.


Die folgende Unit2.pas enthält 2 Threads, TDialogThread und TWorkerThread. TDialogThread ist für die Anzeige des eigenständigen ThreadDialogs zuständig. TWorkerThread berechnet beispielhaft irgendwas und zeigt dann die Ergebnisse in ThreadDialog an.
Delphi-Quellcode:
unit Unit2;

interface

uses classes, windows, Messages,SysUtils;
type
  TDialogThread = class(TThread)
  private
    hdlg: DWORD ;
  protected
    procedure Execute; override;
  public
    constructor Create;
    procedure Show;
    procedure MemoAdd(s:String);
    procedure ProgressBarPosition(Pos: Integer);
    class function GetCompnentHandleByID(ID: Integer): DWORD;
  end;

  TWorkerThread = class(TThread)
  private
    procedure BerechneWas;
  protected
    procedure Execute; override;
  public
    constructor Create;
  end;

  RComponentList = record
    ID: Integer;
    ClassName: String;
    Name: String;
    Handle: DWORD;
  end;
  ARComponentList = array of RComponentList;

  function dlgfunc(hwnd: hwnd; umsg: dword; wparam: wparam; lparam: lparam): bool; stdcall;

var
  ThreadComponentList: ARComponentList;
  DialogThread: TDialogThread;
  WorkerThread: TWorkerThread;

implementation

uses CommCtrl;

{$R main.res} //hier kommt die Dialogresource rein

function dlgfunc(hwnd: hwnd; umsg: dword; wparam: wparam; lparam: lparam): bool; stdcall;
var
  ProgressHandle:DWORD;
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, PChar('OK Button gedrückt. '+IntToStr(ProgressHandle)), 'Meldung', 0);
            end;
        end;
      end;
  else result := false;
  end;
end;

function EnumChildProc(const AhWindow : DWORD;const ADummy : PDWORD) : Boolean; stdcall;
var
  pBuffer : PChar;
  dwSize : DWORD;

begin
  SetLength(ThreadComponentList, Length(ThreadComponentList)+1);
  Result := true;
  dwSize := 255;
  pBuffer := AllocMem(dwSize);
  try
    if GetClassName(AhWindow,pBuffer,dwSize) = 0 then
    begin
      exit;
    end;
    ThreadComponentList[High(ThreadComponentList)].ID := GetDlgCtrlID(AhWindow);
    ThreadComponentList[High(ThreadComponentList)].ClassName := StrPas(pBuffer);
    ThreadComponentList[High(ThreadComponentList)].Handle := AhWindow;
  finally
    FreeMem(pBuffer,dwSize);
  end;
end;


{ TDialogThread }

constructor TDialogThread.Create;
begin
  inherited Create(False);
  FreeOnTerminate := TRUE;
end;

procedure TDialogThread.Execute;
var
  Msg: TMsg;
  ProgressHandle: DWORD;

begin
  hdlg := CreateDialog(HInstance, MAKEINTRESOURCE(100), Self.Handle, @DlgFunc);
  ShowWindow(hdlg, SW_SHOW);

  //hole mal alle Komponenten
  EnumChildWindows(hdlg,@EnumChildProc,0);

  ProgressHandle := GetCompnentHandleByID(3000);
  if ProgressHandle>0 then
  begin
    SendMessage(ProgressHandle,PBM_SETRANGE,0,MAKELPARAM(0,100)); //Setze den Bereich von ProgressBar auf 0..100
    SendMessage(ProgressHandle,PBM_SETSTEP,1,0); //Setze den Bereich von ProgressBar auf 0..100
  end;

  while not terminated do
  begin
    if GetMessage(msg,0,0,0) then
    begin
      TranslateMessage(Msg);
      DispatchMessage(Msg);
    end;
  end;
end;

class function TDialogThread.GetCompnentHandleByID(ID: Integer): DWORD;
var
  i: Integer;
begin
  result:=0;
  if Length(ThreadComponentList)<1 then exit;
  for i := 0 to High(ThreadComponentList) do
  begin
    if ThreadComponentList[i].ID = ID then
    begin
      result := ThreadComponentList[i].Handle;
      break;
    end;
  end;
end;

procedure TDialogThread.MemoAdd(s: String);
var
  MemoHandle: DWORD;
  MemoCount: Integer;
  SelStart, LineLen: Integer;
  Line: string;
begin
  MemoHandle := GetCompnentHandleByID(4001);
  if MemoHandle>0 then
  begin
    //Bestimme die Count Anzahl
    MemoCount := SendMessage(MemoHandle, EM_GETLINECOUNT, 0, 0);
    if SendMessage(MemoHandle, EM_LINELENGTH, SendMessage(MemoHandle, EM_LINEINDEX, MemoCount - 1, 0), 0) = 0 then Dec(MemoCount);
    if MemoCount >= 0 then
    begin
      SelStart := SendMessage(MemoHandle, EM_LINEINDEX, MemoCount, 0);
      if SelStart >= 0 then Line := S + #13#10 else
      begin
        SelStart := SendMessage(MemoHandle, EM_LINEINDEX, MemoCount - 1, 0);
        if SelStart < 0 then Exit;
        LineLen := SendMessage(MemoHandle, EM_LINELENGTH, SelStart, 0);
        if LineLen = 0 then Exit;
        Inc(SelStart, LineLen);
        Line := #13#10 + s;
      end;
      SendMessage(MemoHandle, EM_SETSEL, SelStart, SelStart);
      SendMessage(MemoHandle, EM_REPLACESEL, 0, Longint(PChar(Line)));
    end;
  end;
end;

procedure TDialogThread.ProgressBarPosition(Pos: Integer);
var
  ProgressHandle: DWORD;
begin
  ProgressHandle := GetCompnentHandleByID(3000);
  if ProgressHandle>0 then
  begin
    SendMessage(ProgressHandle, PBM_SETPOS, Pos, 0);
  end;
end;

procedure TDialogThread.Show;
begin
  ShowWindow(hdlg, SW_SHOW);
end;


{ TWorkerThread }

procedure TWorkerThread.BerechneWas;
begin
  Sleep(800);
end;

constructor TWorkerThread.Create;
begin
  inherited Create(TRUE);
  FreeOnTerminate := TRUE;
  Resume;
end;

procedure TWorkerThread.Execute;
var
  i: Integer;
begin
  inherited;
  i:=0;
  while not terminated do
  begin
    BerechneWas;
    DialogThread.MemoAdd('Test'+IntToStr(i));
    if i<=100 then
    begin
      DialogThread.ProgressBarPosition(i);
      inc(i);
    end
    else i:=0;
  end;
end;

end.
lg,
jus
Angehängte Grafiken
Dateityp: jpg screenshot1.jpg (28,1 KB, 21x aufgerufen)
Dateityp: jpg screenshot2.jpg (34,1 KB, 20x aufgerufen)
Angehängte Dateien
Dateityp: zip thread_non_vcl.zip (226,5 KB, 15x aufgerufen)
  Mit Zitat antworten Zitat
Antwort Antwort
Seite 1 von 2  1 2      


Forumregeln

Es ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.

BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus.
Trackbacks are an
Pingbacks are an
Refbacks are aus

Gehe zu:

Impressum · AGB · Datenschutz · Nach oben
Alle Zeitangaben in WEZ +1. Es ist jetzt 14:11 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