Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Algorithmen, Datenstrukturen und Klassendesign (https://www.delphipraxis.net/78-algorithmen-datenstrukturen-und-klassendesign/)
-   -   Messages abfangen/mitlesen (https://www.delphipraxis.net/187980-messages-abfangen-mitlesen.html)

Schwedenbitter 20. Jan 2016 21:54

Messages abfangen/mitlesen
 
Hallo,

ich habe folgendes Problem: ich will/muss eine große Anzahl von bmp-Dateien in jpg-/png-Dateien umwandeln. Das wollte ich in einen Thread auslagern, u.a. um die Forms flüssig laufen zu lassen. Allerdings scheitert das an dem nicht threadsicheren TBitmap(.Canvas).

Also habe ich die Idee, die Berechnung in einem (unsichtbaren) Konsolenprogramm ausführen zu lassen und dieses wiederum über Messages vom Hauptprogramm zu steuern. Den Rumpf stelle ich mir so vor:
Delphi-Quellcode:
Program Packer;

{$APPTYPE CONSOLE}

{$R *.res}

{$DEFINE DEBUG}

Uses
   Winapi.Windows, System.SysUtils, System.Classes, Vcl.Graphics;

Var
   bRet            : Integer;
   Msg            : Winapi.Windows.TMsg;

Begin
   While True Do
   Try

      // Hier kommt das Laden und Komprimieren des Bitmap

      Sleep(50);
   Except
      On E: Exception Do
      With TStringList.Create Do
      Try
         Try LoadFromFile('Log.txt'); Except End;
         Append(E.ClassName + ': ' + E.Message);
         SaveToFile('Log.txt');
      Finally
         Free;
      End;

   End;
End.
Wo/wie kann ich die Messages abfangen?

Wie das mit dem Message Loop grundsätzlich funktioniert, habe ich anhand diverser Beispiel-Codes gesehen (
Delphi-Quellcode:
GetMessage
,
Delphi-Quellcode:
TranslateMessage
,
Delphi-Quellcode:
DispatchMessage
). Dann hätte ich die Messages. Allerdings habe ich Test-Code an diversen Stellen eingefügt, ohne dass dieser ausgeführt würde...


Gruß, Alex

Medium 20. Jan 2016 23:41

AW: Messages abfangen/mitlesen
 
Da du uns leider deinen Testcode nicht gezeigt hast, zunächst mal eine generelle Info: Um Fensternachrichten (so heissen die) zu empfangen, brauchst du zwingend auch ein Fenster. (Das Konsolenfenster zählt hierbei nicht, da es ja zum Prozess cmd.exe gehört, nicht zu deinem Programm.) Das Fenster kann durchaus unsichtbar sein, das wichtige ist nur, dass du von Windows ein Fenster-Handle bekommst an das die Messages geschickt werden können.
Man kann das natürlich zu Fuß über die WinAPI machen, aber wenn die Größe des Tools jetzt nicht unbedingt wenige kB sein muss, würde ich mir die Gemütlichkeit antun einfach eine VCL-Anwendung draus zu machen.

Wenn du bei der Konsole bleiben willst: Mit Konsolenanwendungen kommuniziert man besser mittels Hier im Forum suchenNamed Pipes.

zagota 21. Jan 2016 06:44

AW: Messages abfangen/mitlesen
 
Warum eine Konsole-Anwendung?

Ein Windowsprogramme mit ShowMainForm:=False wäre doch sicherlich auch möglich.
Such mal nach "interprocess communication delphi", da dürfte es zwecks Kommunikation einiges zu lesen geben.

cu

Schwedenbitter 21. Jan 2016 07:05

AW: Messages abfangen/mitlesen
 
Danke für die ausführlich Antwort!
Zitat:

Zitat von Medium (Beitrag 1327646)
Da du uns leider deinen Testcode nicht gezeigt hast, ...

Es gibt keinen "Testcode" als solches. Ich habe folgenden Code mehrfach gefunden und gehe deshalb davon aus, dass er so erst einmal korrekt ist:
Delphi-Quellcode:
Var
   bRet            : Integer;
   Msg            : Winapi.Windows.TMsg;

Begin

      { your program logic }   // [1]

   Repeat
      bRet:= Integer(GetMessage(Msg, 0, 0, 0));
      If (bRet = -1) Then
      Begin
         // error      [2]
         Break;
      End
      Else
      Begin
         //   [3]
         TranslateMessage(Msg);
         // [4]
         DispatchMessage(Msg);
         // [5]
      End
      //   [6]
   Until (bRet = 0);
End.
  1. fällt aus, weil es nur genau einmal ausgeführt wird.
  2. fällt aus, weil hier nur Fehler behandelt werden.
  3. wenn ich hier
    Delphi-Quellcode:
    With TStringList.Create Do
    Try
       SaveToFile('Ich_Lebe.txt');
    Finally
       Free;
    End;
    einfüge, wird die Datei trotzdem nicht erzeugt.
  4. Siehe 3.
  5. Siehe 3.
  6. Siehe 3.
Meine Vorstellung war/ist, dass ich in Msg die Messages mitlausche. Damit sollte ich merken, wenn mein VCL Programm mir etwas mitteilt.

Aber ich greife den Vorschlag gern auf und es sieht mir auch einfacher aus, es über ein VCL-Programm zu machen, dessen Fenster einfach nicht sichtbar ist. So gesehen eine gut Idee - hätte ich auch selbst drauf kommen können :roll: Von der Theorie her würde mich aber der Weg über ein Konsolen-Programm dennoch interessieren.

Luckie 21. Jan 2016 07:18

AW: Messages abfangen/mitlesen
 
Lange nicht mehr gemacht. Aber da fehlt dir die Fenster-Prozedur, an die die Nachricht weitergeleitet und verarbeitet wird. Würde ich jetzt mal so sagen. Das, was du da hast, ist nur die Nachrichtenschleife.

Medium 21. Jan 2016 20:04

AW: Messages abfangen/mitlesen
 
Naja, das Ding ist halt, dass man mit einer reinen Konsolenanwendung niemals Window-Messages empfangen können wird. Die landen sozusagen überhaupt nicht im Verteiler. Es muss ein Fenster geben, und dieses muss innerhalb seiner so genannten Bei Google suchenWndProc die Nachrichtenschleife abarbeiten. (Der Inhalt der WndProc sieht dem was du geposted hast ähnlich, aber ohne Fenster hilft das überhaupt nichts.)

Man müsste sich also per WinAPI ein Fenster-Handle besogren, und für dieses eine Nachrichtenschleife implementieren (=WndProc). Naja, und im Kern ist das ein wesentlicher Teil dessen, was das VCL TForm für uns kapselt und mundgerecht in Form von Ereignissen serviert. Der Schritt von Konsolenprogramm mit Fenster mit WndProc hin zu einem VCL Programm ist fast schon als nur noch kosmetisch anzusehen. (Ja, die VCL macht einen ganzen Haufen, aber im Kern geht es um "hier hast du Fenster".)

Schwedenbitter 21. Jan 2016 20:29

AW: Messages abfangen/mitlesen
 
Mir ist das zu aufwendig. Also werde ich den "Packer" auch als (unsichtbares) VCL machen. Ich habe bereits gesucht. Und da ich bloß Strings austauschen will, wurde ich hier fündig. Dieser Code funktioniert. Ich erhalte - innerhalb ein und desselben Programms - eine Zeichenkette mit Inhalt.

Ich habe nun versucht, das Ganze in 2 Programm zu splitten. Aber irgendwie kommt immer nur ein leerer String an:
Delphi-Quellcode:
const
   MY_MESSAGE = WM_USER + 4242;

type
   TForm1 = class(TForm)
     Edit1: TEdit;
     Button1: TButton;
     procedure Button1Click(Sender: TObject);
end;

var
   Form1: TForm1;

implementation

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
Var
   H               : HWND;
   txt            : String;
begin
   txt:= Edit1.Text;
   SendMessage(FindWindow('TForm1', 'Packer'), MY_MESSAGE, 0, DWORD(PChar(txt)));
end;
und der Empfänger
Delphi-Quellcode:
Const
   MY_MESSAGE   = WM_USER + 4242;

Type
   TForm1      = Class(TForm)
                     Memo1   : TMemo;
                 Private
                     Procedure GotMessage(Var Msg: TMessage); Message MY_MESSAGE;
                 End;

Var
   Form1   : TForm1;

Implementation

{$R *.dfm}

Procedure TForm1.GotMessage(Var Msg: TMessage);
Var
   MsgStr         : PChar;
   txt            : String;
Begin
   MsgStr:= PChar(msg.lParam);
   Msg.Result:= 1;
   ShowMessage(MsgStr);   // exakt der gleiche Code - trotzdem leere Zeichenkette

   With Memo1.Lines Do
   Try
      BeginUpdate;
      txt:= String(MsgStr);
      Add(txt);
   Finally
      EndUpdate;
   End;
End;
Was mache ich falsch?

bepe 21. Jan 2016 20:35

AW: Messages abfangen/mitlesen
 
Nur mal so als Anregung: Wie wäre es mit Startparametern? Einfach pro Bild das Programm einmal starten. Und fertig. Ggf. als zusätzlichen Parameter noch das Handle deiner Hauptform mit gegeben. Dann kann das "Packer" Programm dein Hauptprogramm noch per Botschaft informieren, wenn es fertig ist. Dürfte einfacher sein und weniger "Angriffsfläche" für Fehler bieten.

Luckie 21. Jan 2016 21:05

AW: Messages abfangen/mitlesen
 
Das wird so nicht funktionieren. Wenn dann mit WM_COPYDATA. Siehe hier: http://michael-puff.de/Programmierun...COPYDATA.shtml

Medium 21. Jan 2016 22:27

AW: Messages abfangen/mitlesen
 
Bei dem String versuchst du einen Pointer zwischen zwei Prozessen auszutauschen, aber jeder Prozess hat seinen eigenen Speicherbereich. Das was in einem Programm auf einen gültigen String zeigt, zeigt im anderen auf irgend etwas - höchstwahrscheinlich Müll. Mit der Ausnahme von WM_COPYDATA kann mit Messages immer nur wParam und lParam, also zwei 32 Bit Integer übergeben werden. Und WM_COPYDATA hat auch so seine eigenen kleinen Eigenheiten.
Nur um's noch mal in den Raum zu werfen: Bei Google suchenNamed Pipes!

Monday 22. Jan 2016 10:04

AW: Messages abfangen/mitlesen
 
Zitat:

Zitat von Schwedenbitter (Beitrag 1327639)
Hallo,

ich habe folgendes Problem: ich will/muss eine große Anzahl von bmp-Dateien in jpg-/png-Dateien umwandeln. Das wollte ich in einen Thread auslagern, u.a. um die Forms flüssig laufen zu lassen. Allerdings scheitert das an dem nicht threadsicheren TBitmap(.Canvas).

Also habe ich die Idee, die Berechnung in einem (unsichtbaren) Konsolenprogramm ausführen zu lassen und dieses wiederum über Messages vom Hauptprogramm zu steuern. Den Rumpf stelle ich mir so vor:

Ich habe eine andere Idee:
Wenn die Messages nicht viel oder zu komplex sind; Vielleicht kannst du die Daten über einfache Textdateien austauschen?

Mavarik 22. Jan 2016 10:10

AW: Messages abfangen/mitlesen
 
Warum machst Du es nicht in einer DLL? Soweit ich weiß, hat jede DLL eine eigene VCL-Instance...

Theoretisch solltest Du die DLL 4x Laden können und hättest so 4 Worker-Threads...

Mavarik

Oder?

Delphi-Laie 22. Jan 2016 17:05

AW: Messages abfangen/mitlesen
 
Kann man nicht Nachrichten per postthreadmessage an den (Haupt-)Thread des Konsolenprogrammes schicken?

Zacherl 23. Jan 2016 00:21

AW: Messages abfangen/mitlesen
 
Zitat:

Zitat von Delphi-Laie (Beitrag 1327872)
Kann man nicht Nachrichten per postthreadmessage an den (Haupt-)Thread des Konsolenprogrammes schicken?

Die API nimmt kein Thread-Handle, sondern eine Thread-Id. Thread-Ids sind aber Prozess-spezifisch. Demnach vermute ich, dass man nur Messages an Threads im eigenen Prozess schicken kann.

Delphi-Laie 23. Jan 2016 09:34

AW: Messages abfangen/mitlesen
 
Zitat:

Zitat von Zacherl (Beitrag 1327896)
Zitat:

Zitat von Delphi-Laie (Beitrag 1327872)
Kann man nicht Nachrichten per postthreadmessage an den (Haupt-)Thread des Konsolenprogrammes schicken?

Die API nimmt kein Thread-Handle, sondern eine Thread-Id. Thread-Ids sind aber Prozess-spezifisch. Demnach vermute ich, dass man nur Messages an Threads im eigenen Prozess schicken kann.

Was meinst Du mit "prozeßspezifisch"? Vermutlich "prozeßeigen", dem gleichen Prozesse zugehörig o.ä.

Diese (per Threadschnappschuß beschaffbaren) Thread-IDs sind genauso systemeinmalig wie einige andere Werte (Prozeß-IDs, Heap-IDs, Fensterhandle, sicher gibt es noch mehr).

Ich hatte mal mit postthreadmessage experimentiert, weil ich das Wissen um diese Problematik für ein anderes Programm benötigte, und konnte sehr wohl Messages auch an "prozeßfremde" Threads schicken. Da die Frage für mich beantwortet war, habe ich das Programm schon wieder gelöscht, es war auch nicht allzu kompliziert.

Delphi-Laie 23. Jan 2016 15:00

AW: Messages abfangen/mitlesen
 
Liste der Anhänge anzeigen (Anzahl: 1)
Angehängtes Programm habe ich soeben noch einmal schnell zusammengebastelt, ganz so ähnlich wie damals.

Man kann damit vom VCL-Thread aus einem weiteren Thread des eigenen Programmes eine Botschaft schicken (einfach Thread-ID von einem Edit zum anderen hinüberkopieren).

Startet man das Prgramm zweimal und kopiert die Threadnummern "über Kreuz", so kann man Messages auch munter zum Thread eben eines anderen Programmes versenden.

Schwedenbitter 27. Jan 2016 13:29

AW: Messages abfangen/mitlesen
 
Danke für Eure Hilfe bis hierhin.
Ich habe es jetzt so gelöst, dass ich die erforderlichen Daten an ein Fenster ("normale", zweite VCL-Anwendung) sende und in diesem dann die Grafikbearbeitung nebst Umwandlung in png bzw. jpeg erfolgt. Das jeweilige Fensterhandle bekomme ich dabei mittels
Delphi-Quellcode:
FindWindow();
.

Jetzt beschäftige ich mich bereits damit, dass beide Programme jeweils nur einmal gestartet werden können. Ansonsten wäre Datensalat vorprogrammiert. Das wiederum bewerkstellige ich über einen Mutex. Dieser liefert mir eine aus meiner Sicht eindeutigeres Handle.
Jetzt ist meine Idee, FindWindow nicht mehr zu benutzen und stattdessen das Handle über
Delphi-Quellcode:
OpenMutex()
zu erfragen.
Bevor ich das jetzt ausprobiere und es evtl. zufällig funktioniert und dann irgendwann einmal nicht mehr:

Kann man das so machen?

Es ist ja theoretisch nicht auszuschließen, dass ein anderes Programm durch Zufall denselben Fensternamen trägt...

SneakyBagels 15. Mai 2017 20:30

AW: Messages abfangen/mitlesen
 
Da das hier am besten reinpasst frage ich hier.

Wenn ich mit PeekMessage eine Message entgegennehme, ist nach abarbeiten des Codes der Aufruf von TranslateMessage und DispatchMessage dann zwingend erforderlich?

Zacherl 15. Mai 2017 20:35

AW: Messages abfangen/mitlesen
 
Zitat:

Zitat von SneakyBagels (Beitrag 1371525)
Wenn ich mit PeekMessage eine Message entgegennehme, ist nach abarbeiten des Codes der Aufruf von TranslateMessage und DispatchMessage dann zwingend erforderlich?

Nein, zwingend ist das nicht. Nur, wenn du die jeweilige Funktionalität benötigst:
Zitat:

Zitat von TranslateMessage
Translates virtual-key messages into character messages. The character messages are posted to the calling thread's message queue, to be read the next time the thread calls the GetMessage or PeekMessage function.

Zitat:

Zitat von DispatchMessage
Dispatches a message to a window procedure. It is typically used to dispatch a message retrieved by the GetMessage function.

MSDN-Library durchsuchenPeekMessage entfernt die Nachricht allerdings nicht aus der Message-Queue. Wenn du das brauchst, dann nimm MSDN-Library durchsuchenGetMessage.

SneakyBagels 15. Mai 2017 21:11

AW: Messages abfangen/mitlesen
 
GetMessage zusammen mit einem WM_QUIT am Ende klappt in der Tat besser als PeekMessage.
Ich verwende GetMessage innerhalb eines Threads und PostThreadMessage von außerhalb.
Muss ich die MessageQueue im Thread auch leeren?

himitsu 15. Mai 2017 21:28

AW: Messages abfangen/mitlesen
 
ACHTUNG: GetMessage und PeekMessage geben dir nur Messages von PostMessage raus.
SendMessage wird direkt innerhalb dieser Funktionen sofort verarbeitet.

Drum kommt bei Application.OnMessage und TApplicationMessageEvents.OnMessage auch nur PostMessage an.
Wer auch SendMessage wissen will, der braucht einen Message-Hook.

https://msdn.microsoft.com/en-us/lib...(v=vs.85).aspx
WH_CALLWNDPROC and WH_CALLWNDPROCRET
WH_GETMESSAGE
WH_FOREGROUNDIDLE

SneakyBagels 15. Mai 2017 21:34

AW: Messages abfangen/mitlesen
 
Um an einen Thread zu senden nutze ich PostThreadMessage, hole dort die Messages mit GetMessage ab und entblocke den Thread am Ende seiner Laufzeit mit WM_QUIT und sicherherheitshalber WM_CLEAR.


Alle Zeitangaben in WEZ +1. Es ist jetzt 07:29 Uhr.

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