Entwickler-Ecke

Delphi Language (Object-Pascal) / CLX - Interprozesskommunikation mit wm_copydata


hazelnoot - Do 03.08.06 14:13
Titel: Interprozesskommunikation mit wm_copydata
hi!

wie kann ich eine message von einem programm in ein anderes schicken? OHNE gui? wie geht das danke!

mfg hazelnoot


BenBE - Do 03.08.06 14:36

Um Window Messages zu verschicken, musst dein Empfänger mindestens ein solches Window haben. Das muss nicht zwangsläufig ein sichtbares Fenster sein, aber das Handle eines solchen benötigst Du ...


hazelnoot - Do 03.08.06 14:58

wie bekomm ich so ein handle hin ohne einer form? wie mache ich das...?

danke fuer eure hilfe mfg
hazelnoot!


Motzi - Do 03.08.06 15:16

Am einfachsten geht das mit der Funktion AllocateHWnd. Für Details siehe Delphi-Hilfe...

Gruß, Motzi


hazelnoot - Do 03.08.06 15:25

koennt ihr mir nicht einen beispielcode zeigen wie sowas funken koennte?

mit window handle erstellen ohne form!!!!

bidde bidde


Motzi - Do 03.08.06 15:30

Ich hab dir doch eh schon eine Funktion genannt. Mit der geht das mit einem einfachen Aufruf und Details findest du wie gesagt in der Delphi-Hilfe. Ein bisschen mehr Eigentinitiative könntest du schon an den Tag legen..


ManniTwo - Do 03.08.06 15:33

aus Interesse hab ich mal bei AllocateHWND nachgeschaut und in meiner Delphi-Hilfe (2005) hab ich es auch nicht gefunden, deshalb hier noch ein Link mit etwas Beispiel-Code, du musst nur ein paar Worte englisch verstehen, aber ich denke das ist machbar :-)

http://delphi.about.com/od/windowsshellapi/l/aa093003b.htm


hazelnoot - Do 03.08.06 15:38

aber ich versteh eins nicht waenn ich mit der funktion ein neues HWND erzeuge wie kann ich dann dieses zur kommunikation hernehmen? wie muss ich es einrichten damit ich das handle dann angeben kann beim sender der message?


Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
unit Unit1;

interface
  uses Classes, Graphics, Controls, Forms, Dialogs, Windows,
  StdCtrls, ExtCtrls;

type
  testClass = class(TObject)
    windowHandle : HWND;

    procedure createHandle();
    procedure doZeugs(Sender: TObject);
  end;

implementation
  procedure testClass.createHandle();
  begin
    windowHandle := AllocateHWnd(doZeugs);
  end;

  procedure testClass.doZeugs(Sender: TObject);
  begin

  end;
end.


soweit bin ich schon aber ich weis einfach nicht was ein TWndMethod ist was ich der AllocateHWnd mitgeben soll... weis jemand was das fuer ein argument ist... waenn dann das argument dann stimmt kann ich das windowHandle dann beim sender angeben? oder muss ich dann nochwas machen?

danke fuer eure hilfe

mfg hazelnoot!


ManniTwo - Do 03.08.06 15:47

also...
TWndMethod ist die Methode, die dann auf die empfangenen Nachrichten reagieren wird.
Deklariert in Classes:

Delphi-Quelltext
1:
  TWndMethod = procedure(var Message: TMessage) of object;                    

das heißt du schreibst eine Methode, die von der Signatur so aussieht:

Delphi-Quelltext
1:
 procedure WindowsMessagesReceiver(var Message: TMesssage);                    


und die reagiert dann auf die eintrudelnden Messages, aber nicht wie sonst nur auf bestimmte, sondern (siehe Quelle von dem Link vorhin)
Zitat:

This procedure is called for each and every Windows message, so we need to filter out only those that are interesting[...] For all other messages that are not handled by our application remember to call the default Windows procedure. Even if this is not so important for our non-visible non-interactive window it is a good practice and missing this step may yield to unpredictable behaviors.


Motzi - Do 03.08.06 16:45

Ok, mein Fehler.. in der Delphi2005-Hilfe steht nichts zu dieser Funktion (hab auch gerade nachgeschaut). Ich hab vorher nur in der D6-Hilfe geschaut und da findet man folgendes:
Zitat:

Delphi-Quelltext
1:
function AllocateHWnd(Method: TWndMethod): HWND;                    


Description

Call AllocateHWnd to create a window that is not associated with a windowed control. Typically, this method is used to create non-visual windows that respond to messages but that do not appear in the user interface. For example, the TTimer component uses this method to create a window that responds to timer messages from Windows.

The Method parameter specifies the window procedure that the generated window uses to respond to messages.

AllocateHWnd returns the handle of the newly created window.

Note: Use the DeallocateHWnd procedure to free windows that are created using AllocateHWnd.


TWndMethod ist ein Methodenzeiger, wie das mit diesem funktioniert hat ja schon ManniTwo erklärt...

Gruß, Motzi


hazelnoot - Fr 04.08.06 08:10

hi!

danke fuer eure hilfe hat mir extrem geholfen!!!! ich habe nur ein weiteres problem:

mein code bis jetzt:

der sender:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
program Sender;

{$APPTYPE CONSOLE}

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
  StdCtrls, ExtCtrls;

var
  data : TCopyDataStruct;
  test : PChar;
  wnd: HWND;

begin
  while True do
  begin
     Writeln('Um zu senden Durecken Sie enter');
     Readln;
     Write('Sende...');

     test := 'C:\Dokumente und Einstellungen\Sickinger\Desktop\Sockutil\UdpSockUtil.pas';

     data.dwData := 0;
     data.cbData := strlen(test) + 1;
     data.lpData := test;

     SendMessage(wnd_broadcast, WM_COPYDATA, 1,
              Longint(@data));

     Writeln('OK');
  end;
end.


und der empfaenger:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
unit Unit4;

interface

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

type
  TTestThread = class(TThread)
  private
    FSignalShutdown: boolean;
    FWinHandle: HWND;
  protected
    procedure Execute; override;
    procedure WndProc(var msg: TMessage);
  public
    constructor Create;
    destructor Destroy; override;
    //procedure PrintMsg;
  end;


implementation

var
  WM_SHUTDOWN_THREADS: Cardinal;

{ TTestThread }

constructor TTestThread.Create;
begin
  FSignalShutdown := False;
  FWinHandle := AllocateHWND(WndProc);
  inherited Create(False);
end;

destructor TTestThread.Destroy;
begin
  DeallocateHWnd(FWinHandle);
  inherited;
end;

procedure TTestThread.Execute;
begin
  FreeOnTerminate := True;
  while NOT FSignalShutdown do
  begin
    Application.HandleMessage;
  end;
  //Synchronize(PrintMsg);
end;


procedure TTestThread.WndProc(var msg: TMessage);
begin
  Writeln('halloasdfasdfasfdasdfadsaf');
end;

end.


waenn ich dieses ausführe dann kommt im thread zwar die message 'halloasdfasdfasfdasdfadsaf' an, aber ich will nicht 'C:\Dokumente und Einstellungen\Sickinger\Desktop\Sockutil\UdpSockUtil.pas' einnen pfad angeben muessen ich will einen normalen text senden!!!! weiters wie kann ich den thread/windowHandler eindeutig identifizieren? mit SendMessage(wnd_broadcast, WM_COPYDATA, 1,Longint(@data)); funkt das nicht! was muss ich machen damit ein text an den thread geschickt wird und damit ich den thread eindeutig identifizieren kann!!!

danke!


ManniTwo - Fr 04.08.06 08:40

toll....super antwort hatte ich geschrieben mit Zitaten und so ;-) , aber ich habe wohl zu lange gebraucht, da wollte er beim absenden, dass ich mich wieder einlogge und dann war die nachricht weg....naja, egal also nochma kurz:

dass 'hallo....' ankommt wundert nicht, denn das ist ja fest verdrahtet im Client-Code, muss also gar nicht ankommen. Du musst den Msg-Parameter auswerten.

Du kannst statt der Pfadangabe einen anderen Text schreiben.

Identifikation erfolgt durch das Handle, den ersten Parameter bei SendMessage.
Vielleicht einfach mit FindWindow versuchen das entsprechende Handle zu bekommen


hazelnoot - Fr 04.08.06 08:52

danke fuer deine promte antwort!

also ich hab das nun probiert:
meine neue sendmessage im sender:

Delphi-Quelltext
1:
SendMessage(FindWindow('FWinHandle'nil), WM_COPYDATA, 1, Longint(@data));                    


das FWinHandle deswegen wiel im empfaenger das handle so heisst:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
constructor TTestThread.Create;
begin
  FSignalShutdown := False;
  FWinHandle := AllocateHWND(WndProc); //hier wird der handler angelegt!!
  inherited Create(False);
end;


es kommt aber nichts an! ich hab schon 100 andere sachen statt FWinHandle probiert auch den klassennamen usw es will einfach nicht funken! stimmt der parameter WM_COPYDATA? es muss auch nicht eine struktur sein es wuerde auch schon reichen einen einfachen text zu senden!

danke fuer eure geduld :-) !
mfg hazelnoot!


ManniTwo - Fr 04.08.06 09:48

Wenn man mit AllocateHWND diese "Pseudo-Fenster" erzeugt, registriert Delphi als Klassennamen 'TPUtilWindow'. Ich hab keine Ahnung wieso, jedenfalls habe ich es bei mir durchdebuggt und wenn du FindWindow('TPUtilWindow'nil) übergibst dann sollte es klappen.

[Edit] ich nehme alles zurück, da findet er dann auch ein Handle, wenn der Receiver nicht läuft. Sorry, langsam weiß ich auch net mehr...hab auch leider net die Zeit nach ner Lösung zu suchen...vielleicht am WE oder vielleicht hat bis dahin jemand anders eine gute Lösung für dein Problem[/Edit]


hazelnoot - Fr 04.08.06 10:06

hi!

super das funkt!!

is ja egal ob er dann auch ein handle findet!!

weisst du zufaellig noch wie man dann aus "procedure TTestThread.WndProc(var msg: TMessage);" die message die ich uebergeben habe parst? das waer noch ideal dann wuerds perfekt sein!!!

danke nochmal hazelnoot!


ManniTwo - Fr 04.08.06 10:42

also grundsätzlich erhälst du ja eine TMessage
und da kommst du sicherlich über Message.lparam wieder an die Daten heran, die du da reingesteckt hast, dann musst du das da natürlich umständlich herauspulen,
ich könnte mir vorstellen, hab es aber nicht getestet, dass du die Message auch als TWMCopyData casten kannst, denn du hast ja eine WM_COPYDATA verschickt.


Delphi-Quelltext
1:
2:
3:
4:
5:
TWMCopyData = packed record
    Msg: Cardinal;
    From: HWND;
    CopyDataStruct: PCopyDataStruct;
    Result: Longint;



Dann kämst du ganz leicht wieder an deine CopyDataStruct und daraus über lpdata wieder an deinen text, also:


Delphi-Quelltext
1:
TWMCopyData(Message).CopyDataStruct                    


Motzi - Fr 04.08.06 10:51

1) wozu der Thread? Die VCL ist nicht thread-safe und man sollte niemals eine Form innerhalb eines Threads (der nicht der primäre Thread ist) erzeugen und/oder benutzen!

2) es ist nicht egal ob er auch ein Fenster findet wenn dein Receiver nicht läuft, denn das bedeutet, dass es in deinem System dann mehrere Fenster mit demselben Klassennamen gibt, und in so einem Fall ist nicht vorhersehbar welches Fensterhandle FindWindow zurückliefert. Es kannt also sein, dass obwohl dein Receiver läuft, dein Sender ein falsches Fensterhandle erhält, von einem Fenster das mit der WM_COPYDATA Message nichts anfangen kann.
Mein Lösungsvorschlag - im Receiver:

Delphi-Quelltext
1:
2:
3:
4:
5:
const
  sWndTitle = 'MyReceiver';

FWinHandle := AllocateHWND(WndProc); //hier wird der handler angelegt!!
SendMessage(FWinHandle, WM_SETTEXT, 0, Integer(Pointer(sWndTitle))); // Fenster-Titel ändern

Im Sender:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
var
  wnd: HWND;

wnd := FindWindow('TPUtilWindow''MyReceiver');
if wnd <> 0 then
  SendMessage(wnd, WM_COPYDATA, 1, Longint(@data));


Gruß, Motzi


hazelnoot - Fr 04.08.06 11:00

hoi!

ich hab das nun so gemacht:

Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
procedure TTestThread.WndProc(var msg: TMessage);
var
  data : PCopyDataStruct;
begin

  data := TWMCopyData(msg).CopyDataStruct;

  Writeln('out:'String(data.lpData));

end;


aber da kommt nach out nix mehr woran kann das liegen den typecast hab ich doch richtig gemacht oder?


Motzi - Fr 04.08.06 11:07

Deine WndProc wird für JEDE Message die an dein Fenster geschickt wird aufgerufen (und solche gibt es seeehr viele)! Du musst also ausfiltern auf welche du reagieren willst und welche nicht..

Delphi-Quelltext
1:
2:
3:
4:
5:
procedure TTestThread.WndProc(var msg: TMessage);
begin
  if msg.Msg = WM_COPYDATA then
    Writeln('out:', PChar(PCopyDataStruct(msg.lParam)^.lpData));
end;

Außerdem solltest du die Punkte beachten die ich in meinem vorherigen Posting angesprochen habe...


hazelnoot - Fr 04.08.06 11:27

super danke es funkt!!!