Tastendruck abfragen aus Unterprogramm

Für Fragen rund um die Ide und zum Debugger
Antworten
br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Tastendruck abfragen aus Unterprogramm

Beitrag von br_klaus »

Hallo,
ich habe wieder ein Problem.
Aus einer Form heraus wird ein sehr zeitaufwendiges Unterprogramm gestartet (kann eine Minute und mehr dauern je nach Dateigröße der geladenen Datei). Leider scheint da die Tastaturabfrage nicht mehr zu funktionieren, sodaß man nicht das Programm vorzeitig abbrechen kann.
Ich habe eine Boolean-Variable stop. Sobald die auf TRUE gesetzt wird, werden alle übergeordeneten Programme bis einschließlich des Unterprogramms selber beendet und die Form.free aufgerufen. Dann müßte eigentlich die Anwendung schließen.

Ich habe verschiedene Versuche gestartet:
Wenn ich ich ein Onkeydown-Ereignis erstelle und dann abfrage

Code: Alles auswählen

   if key = 27 then stop := true; // Escape  
dann wird dieses nie aktiviert, auch wenn ich während des Abarbeitens des Unterprogramms die Escape-Taste drücke.

Ich habe es versucht (in Windows) mit

Code: Alles auswählen

  if GetKeystate(VK_ESCAPE)<0 then stop:=true;
aber auch da geschieht nichts.

Und das Eigenartige: wenn ich das Programm kompiliere bzw das kompilierte Programm laufen lasse und dann die Maus oder eine Taste drücke, dann bleibt das Programm einfach hängen, und ich kann es nur noch mit dem Windows-Taskmanager beenden (das Programm bzw den Debugger).

In DOS gab es die Möglichkeit mit INT 16, aber das geht in Win32 nicht mehr. Außerdem gab es die Möglichkeit, direkt auf den Tastaturpuffer zuzugreifen und diesen "von Hand" auszulesen. Ist das jetzt auch noch möglich?

Oder bleibt mir da nur, zwei unabhängige Threads zu programmieren, sodaß der Tastendruck die Variable stop auf TRUE setzt?

Weiß jemand, wie ich das Problem lösen kann?

Herzlichen Dank!

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Tastendruck abfragen aus Unterprogramm

Beitrag von Michl »

Weiss nicht, ob das bei Dir geht?! Bei einigen Projekten habe ich auch sehr rechenaufwendige Proceduren am laufen. Diese habe ich nun mittlerweile alle in eigene Threads ausgelagert, damit der MainThread reaktionsfähig bleibt.

Alternativ, hast Du bei dem "Unterprogramm" Application.ProcessMessages mit eingebaut oder dauert das Laden der Datei schon eine Minute und mehr?

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Tastendruck abfragen aus Unterprogramm

Beitrag von Michl »

Habe es aber mal getestet, auch wenn ich das nicht schön finde (rechenaufwendige Proceduren sollten in einen eigenen Thread), funktioniert das Vorgehen bei mir wie folgt:

Form1.KeyPreview im Objektinspektor auf True stellen:

Code: Alles auswählen

var
  Stopp: Boolean = False;  //z.B. Globale Variable
 
procedure TForm1.Button1Click(Sender: TObject);
const
  i: Integer = 0;
begin
  while not Stopp do
    begin
      //... mach irgendwas z.B. erhöhe i
      inc(i);
      Application.ProcessMessages;
    end;
end;
 
procedure TForm1.FormKeyDown(Sender: TObject; var Key: Word; Shift: TShiftState
  );
begin
  case Key of
    27:Stopp:=True;  //Escape gedrückt
  end;
end;

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

br_klaus
Beiträge: 244
Registriert: Do 21. Jan 2010, 22:33
OS, Lazarus, FPC: Windows Vista (L 0.9.31 FPC 2.5.1)
CPU-Target: 32Bit
Wohnort: z.z. Brasilien, sonst 82335 Berg-Leoni (südlich von München)

Re: Tastendruck abfragen aus Unterprogramm

Beitrag von br_klaus »

Habe eine Möglichkeit gefunden!

Bei der Untersuchung der verschiedenen Prozeduren processmessage(s) und Suche nach PeekMessage mit PM_REMOVE (was immer das zu bedeuten mag - es gibt da noch PM_NoREMOVE und YIELD), habe ich gesehen, daß die Grundstruktur immer folgende ist:
wenn PeekMessage(Msg, 0, 0, 0, PM_Remove), dann wird aufgerufen TranslateMessage(msg) und schließlich DispatchMessage(msg), dazwischen die Abfrage. So müßte es genügen, eine Funktion zu schreiben:

Code: Alles auswählen

 function RunMessage(var msg: TMsg):Boolean;
   begin 
     if PeekMessage(Msg, 0,0,0, PM_REMOVE) then
       begin
         result:=true;
         TranslateMessage(msg);
         with msg do
           case message of
               WM_KEYDOWN:
                   case wparam of
                        27: stop:=true; //Escape    
                   end;
           end;
         DispatchMessage(msg);
       end;
  end;
Und es klappt!
Ich brauche dann nur irgendwo im Code aufzurufen

Code: Alles auswählen

RunMessage(msg); // oder: if RunMessage(msg) then
            if stop then...
 
Ich habe das v.a. bei KOL.processmessage gesehen (die Unit KOL hat den Vorteil, abgesehen von der viel kleineren Größe der exe-Dateien, daß bei den Messages immer auch gleich als deren Ursprung der Handle des jeweiligen PControl angegeben wird, es gibt es da ein Event OnMessage als Eigenschaft eines PControl, mit dem man alle Messages dieses PControls abfangen kann, was bei Lazarus ohne KOL anscheinend nicht geht.

Und wenn man dann noch abfragen will, ob SHIFT, CONTROL oder ALT gedrückt wurde, dann geht das mit

Code: Alles auswählen

if GetKeyState(VK_SHIFT { bzw. VK_CTRL, VK_MENU} ) <0 then ...
Zuletzt geändert von Lori am Di 24. Dez 2013, 22:28, insgesamt 1-mal geändert.
Grund: richtiger Highlighter

Antworten