Delphi-Forum.de Delphi-Library.de
C-Sharp-Forum.de C-Sharp-Library.de
Registrieren Login Suche Hilfe Sitemap
Kommunikation von zwei Programmen über Messages
spacer
Autor Nachricht
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Mi 10.02.10 18:29  Antworten mit Zitat Beitrag melden
Hi,

hier ein kleines Tutorial darüber, wie man zwei Programme miteinander kommunizieren lassen kann.

Zuerst erstellen wir zwei neue Projekte in Delphi. Die Namen der Form ändern wir auf "TutorialForm1" bzw. "TutorialForm2", die Caption auf "Window of TutorialForm1" bzw "Window of TutorialForm2".

Anmerkung: Bei all den Beispielen die wir im folgenden machen, müssen (logischerweise) zum testen beide Programm laufen, damit sie sich unterhalten können ;)

Kapitel 1 - Befehle

Die einfachste Nachricht ist die, die keine Erklärung benötigt. Diese entspricht in etwa einer procedure ohne Parameter.

Zuerst müssen wir uns auf einen Namen einigen, damit beide Seiten wissen was denn überhaupt gemeint ist.

Dafür deklarieren wir const WM_HELLO=WM_USER+1; in beiden Programmen.

WM_USER stellt einen Bereich dar, der für den Programmierer freigehalten wird, so dass wir nicht in Konflikt mit den systeminternen Messages kommen (wie WM_KEYDOWN z.B.). Eine Liste der reservierten Messages ist in der Unit "messages" zu finden.

In Programm1 fügen wir einen Button und ein Memo-Feld ein und zudem folgenden Code für Button1.OnClick:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TTutorialForm1.Button1Click(Sender: TObject);
var H: THandle;
begin
h:=FindWindow('TTutorialForm2',nil);
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2 gefunden (Handle='+inttostr(h)+')');
PostMessage(H,WM_HELLO,0,0);
end
else
Memo1.Lines.Add('TTutorialForm2 nicht gefunden!');
end;


Dieser Code sucht erstmal nach dem Fenster, dass wir ansprechen möchten (Klasse "TTutorialForm2"). Wird es gefunden (h<>0), dann schicken wir die Nachricht "WM_HELLO" zu ihm.

In Programm2 fügen wir ein Memo-Feld ein. Jetzt fehlt nur noch die Empfangs-procedure.

ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
type
TTutorialForm2 = class(TForm)
Memo1: TMemo;
private
{ Private-Deklarationen }
public
procedure SomeoneSaysHello(var msg: TMessage); message WM_HELLO;
end;

//...

procedure TTutorialForm2.SomeoneSaysHello(var msg: TMessage);
begin
Memo1.Lines.Add('Someone says hello to you');
end;


Dieser Code ist auch sehr einfach. Die procedure SomeoneSaysHello wird immer dann aufgerufen, wenn die message WM_HELLO empfangen wird.


Moderiert von user profile iconChristian S.: Topic aus Windows API verschoben am Mi 10.02.2010 um 20:14
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)


Zuletzt bearbeitet von Xion am Sa 13.02.10 17:12, insgesamt 4-mal bearbeitet
Private Nachricht sendenPosting in privater Nachricht zitieren
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Mi 10.02.10 18:49  Antworten mit Zitat Beitrag melden
Kapitel 2 - Informationen austauschen

Die erweiterte Version besteht jetzt darin, den messages noch Informationen beizupacken. Dazu erweitern wir das vorherige Beispiel um die Funktion, dass Programm2 eine Antwort als String an Programm1 verschickt.

Dazu erweitern wir die Empfangs-Funktion in Programm2
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TTutorialForm2.SomeoneSaysHello(var msg: TMessage);
var h: THandle; cds: TCopyDataStruct; Txt: String;
begin

Memo1.Lines.Add('Someone says hello to you');

Txt:='Greetings from TTutorialForm2 - it''s now '+timetostr(Now);

cds.dwData:=0;
cds.cbData:=StrLen(PChar(Txt)) + 1;;
cds.lpData:=PChar(Txt);
h:=FindWindow(nil, 'Window of TutorialForm1');
if h<>0 then
begin
Memo1.Lines.Add('Nachricht an TTutorialForm1 (Handle='+inttostr(H)+') verschickt (Txt='+Txt+')');
SendMessage(H,WM_COPYDATA,Longint(TutorialForm2.Handle),Integer(@cds));
end
else
Memo1.Lines.Add('TTutorialForm1 nicht gefunden');
end;

cds ist das record, dass unsren String aufnimmt. Danach schicken wir eine message mit Pointer auf diesem Record an Programm1. Dem aufmerksamen Leser ist vielleicht aufgefallen, dieses mal wurde FindWindow mit dem zweiten Parameter verwendet, dieser verlangt statt der Klasse den Fenster-Titel.

Jetzt brauchen wir in Programm1 noch eine Empfangs-Procedure:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
type
TTutorialForm1 = class(TForm)
Button1: TButton;
Memo1: TMemo;
procedure Button1Click(Sender: TObject);
private
{ Private-Deklarationen }
public
procedure DataIncoming(var msg: TWMCopyData ); message WM_COPYDATA;
end;

//...

procedure TTutorialForm1.DataIncoming(var msg: TWMCopyData );
var sText: array[0..256] of Char;
begin
StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
Memo1.Lines.Add('Message incoming: '+sText);
end;

Hier wird die Information wieder extrahiert.
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)


Zuletzt bearbeitet von Xion am Sa 13.02.10 17:13, insgesamt 2-mal bearbeitet
Private Nachricht sendenPosting in privater Nachricht zitieren
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Mi 10.02.10 19:21  Antworten mit Zitat Beitrag melden
Kapitel 3 - Unfreiwillige Zugriffe

Machmal möchte man in einem fremden Programm Knöpfe drücken, Felder ausfüllen oder Edit-Felder auslesen. Dazu muss man allerdings die Struktur des fremden Programms zumindest grob kennen. Versuchen wir nun, ohne Programm2 abzuändern, in das Memo-Feld von Programm2 zu schreiben.

Dazu fügen wir erstmal einen zweiten Button in Programm1 ein und verwenden folgenden Code:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
procedure TTutorialForm1.Button2Click(Sender: TObject);
var H: THandle;
begin
h:=FindWindow(nil,'Window of TutorialForm2');
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2 gefunden (Handle='+inttostr(h)+')');
//???
end
else
Memo1.Lines.Add('TTutorialForm2 nicht gefunden!');
end;

Das handle finden wir über das Caption des Fensters, das man ja bei fremden Fenstern leicht sieht. Soweit so gut. Jetzt benötigen wir aber Informationen, wie es in Programm2 aussieht (angenommen wir würden den SourceCode davon nicht kennen). Dazu verwende ich das Programm "WinSpector", das ist kostenlos und ziemlich gut.

Der zeigt mir für Programm2 folgendes:



ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
procedure TTutorialForm1.Button2Click(Sender: TObject);
var H: THandle;
begin
h:=FindWindow(nil,'Window of TutorialForm2');
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2 gefunden (Handle='+inttostr(h)+')');
H:=FindWindowEx(H,0,'TMemo',nil);
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2.Memo gefunden (Handle='+inttostr(h)+')');
PostMessage(H,WM_KeyDown,190,0); //190 = '.'
end
else
Memo1.Lines.Add('TTutorialForm2.Memo nicht gefunden!');
end
else
Memo1.Lines.Add('TTutorialForm2 nicht gefunden!');
end;

FindWindowEx findet ein Sub-Handle von dem Fenster mit Handle H. Leider kann man Labels deshalb (soweit ich weiß) nicht auslesen, da sie kein eigenes Handle haben. Mit PostMessage schicken wir dann ein WM_KeyDown mit dem Zeichen 190 (=Punkt) an das Memo.

Statt einen Tastendruck zu simulieren ist es oft besser, besonders wenn man mehrere Zeichen einfügen will, den Text direkt zu setzen. Da jedoch WM_SETTEXT den alten Text ersetzen, lesen wir zuerst den Text aus, fügen dann in diesem Beispiel die Uhrzeit hinzu und schreiben es zurück.

ausblenden volle Höhe Delphi-Quelltext markieren
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:
procedure TTutorialForm1.Button2Click(Sender: TObject);
var H: THandle; S: String; TextLength: DWord;
begin
S:='';

h:=FindWindow(nil,'Window of TutorialForm2');
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2 gefunden (Handle='+inttostr(h)+')');
H:=FindWindowEx(H,0,'TMemo',nil);
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2.Memo gefunden (Handle='+inttostr(h)+')');

TextLength := SendMessage(H, WM_GETTEXTLENGTH, 0, 0) ;
Memo1.Lines.Add('Memo TextLength: '+inttostr(TextLength));

if TextLength>0 then
begin
SetLength(S,TextLength+1);

if SendMessage(H,WM_GETTEXT,TextLength+1,Integer(Pointer(S)))>0 then
Memo1.Lines.Add('Read from Memo: '+S);

S:=TrimRight(S);
end;

S:=S+' / '+timetostr(Now);
Memo1.Lines.Add('Sending to Memo: '+S);

SendMessage(H,WM_SETTEXT,0,Integer(Pointer(S)));
end
else
Memo1.Lines.Add('TTutorialForm2.Memo nicht gefunden!');
end
else
Memo1.Lines.Add('TTutorialForm2 nicht gefunden!');
end;

Mit WM_GETTEXTLENGTH lesen wir zuerst die Länge des bereits im Memo vorhandenen Textes. Danach holen wir diesen Text mit WM_GETTEXT, hängen die Uhrzeit an und setzen den Text des Memos mit WM_SETTEXT.
Wir fragen übrigens bei WM_GETTEXT ein Zeichen mehr ab, als wir abgefragt haben. Dieses Zeichen ist das abschließende #0 Zeichen. Dieses entfernen wir anschließend mit TrimRight().
Einloggen, um Attachments anzusehen!


Zuletzt bearbeitet von Xion am Sa 13.02.10 17:14, insgesamt 10-mal bearbeitet
Private Nachricht sendenPosting in privater Nachricht zitieren
Luckie
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic star

Beiträge: 11066
Erhaltene Danke: 2
Dabei seit: 30.08.2002


WindowsXP Professional SP2
BDS2006, VS C++ Express, Eclipse (Java), Flex (Flash, AS)
BeitragVerfasst: Mi 10.02.10 21:19  Antworten mit Zitat Beitrag melden
user profile iconXion hat folgendes geschrieben Zum zitierten Posting springen:
Die Namen der Form ändern wir auf "TutorialForm1" bzw. "TutorialForm2", die Caption auf "Window of TutorialForm1" bzw "Window of TutorialForm2".

Wenn du dir schon die Mühe machst und die Formulare umbenennst, dann mach es auch richtig und gib ihnen aussagekräftige namen wie Sender und Empfaenger oder so.

Den Rest werde ich mir wahrscheinlich nachher noch mal durchlesen.

_________________
Gruß Michael
Private Nachricht sendenPosting in privater Nachricht zitieren Webseite dieses Mitglieds besuchen
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Mi 10.02.10 21:48  Antworten mit Zitat Beitrag melden
Das mit dem Empfänger und Sender ist so ne tricky Sache...das Problem hatte ich in nem andren Posting schon, weil letztenendes senden ja beide (in Kapitel 2) und dann verwirrt das irgendwie total.

Edit:
Wenn du zufällig weißt, wie ich aus der Message rausbekomme, wer sie geschickt hat, würde ich das nocht mit einbauen...wäre sehr sinnvoll, wenn ein Programm von mehreren andren Programmen angesprochen werden kann und antworten soll

_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)
Private Nachricht sendenPosting in privater Nachricht zitieren
Luckie
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic star

Beiträge: 11066
Erhaltene Danke: 2
Dabei seit: 30.08.2002


WindowsXP Professional SP2
BDS2006, VS C++ Express, Eclipse (Java), Flex (Flash, AS)
BeitragVerfasst: Mi 10.02.10 22:04  Antworten mit Zitat Beitrag melden
Überleg dir ein Protokoll. Und verschick mit WM_COPYDATA einen Record mit deinen Daten.
www.michael-puff.de/...ts/WM_COPYDATA.shtml

Wenn ich zwei Programme über Nachrichten kommunizieren lassen würde, dann übrigens mit WM_COPYDATA, weil man damit auch Daten austauschen kann.

_________________
Gruß Michael
Private Nachricht sendenPosting in privater Nachricht zitieren Webseite dieses Mitglieds besuchen
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Mi 10.02.10 22:20  Antworten mit Zitat Beitrag melden
Jo, das mache ich ja auch in Kapitel 2

Ach, das mit dem record ist ja einfach :) cool, danke :think:

_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)
Private Nachricht sendenPosting in privater Nachricht zitieren
Luckie
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic star

Beiträge: 11066
Erhaltene Danke: 2
Dabei seit: 30.08.2002


WindowsXP Professional SP2
BDS2006, VS C++ Express, Eclipse (Java), Flex (Flash, AS)
BeitragVerfasst: Mi 10.02.10 22:26  Antworten mit Zitat Beitrag melden
Ok, ich habe mir die Quellcodes mal angeguckt, die Erklärungen habe ich nicht gelesen.

Bei WM_COPYDATA gibst du als wParam Application.Handle an. Da würde ich das Fensterhandle, also TForm.Handle angeben. Application ist das unsichtbare Fenster der VCL.

Text schreibt man mit WM_SETTEXT in ein Memo und mit den anderen entsprechenden Nachrichten in Listboxen usw.

_________________
Gruß Michael
Private Nachricht sendenPosting in privater Nachricht zitieren Webseite dieses Mitglieds besuchen
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Mi 10.02.10 23:35  Antworten mit Zitat Beitrag melden
user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:

Bei WM_COPYDATA gibst du als wParam Application.Handle an. Da würde ich das Fensterhandle, also TForm.Handle angeben. Application ist das unsichtbare Fenster der VCL.

ok, hab ich geändert

user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:

Text schreibt man mit WM_SETTEXT in ein Memo und mit den anderen entsprechenden Nachrichten in Listboxen usw.

Ja, das war eigentlich Absicht. Zum einen braucht man KeyDown häufiger meiner Meinung nach. Zum andren hab ich so meine Probleme mit WM_SETTEXT...das überschreibt alles was vorher im Memo stand

_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)
Private Nachricht sendenPosting in privater Nachricht zitieren
Boldar
ontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic starofftopic star

Beiträge: 1262
Dabei seit: 14.06.2008
Wohnort: root

Win7 Enterprise 64bit, Win XP SP2
Turbo Delphi
BeitragVerfasst: Mi 10.02.10 23:42  Antworten mit Zitat Beitrag melden
Das kann man sich ja vorher mit WM_GETTEXT holen.
Private Nachricht sendenPosting in privater Nachricht zitieren
Luckie
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic star

Beiträge: 11066
Erhaltene Danke: 2
Dabei seit: 30.08.2002


WindowsXP Professional SP2
BDS2006, VS C++ Express, Eclipse (Java), Flex (Flash, AS)
BeitragVerfasst: Mi 10.02.10 23:44  Antworten mit Zitat Beitrag melden
Hol dir den text, häng eine Zeile an und setzt den Text wieder. Dann hast du auch gleich gezeigt, wie man Text ausliest.

Warum braucht man WM_KEYDOWN häufiger? Schaltflächen klickt man an, in dem man die entsprechende Nachricht an das Fenster oder die Schaltfläche schickt.

_________________
Gruß Michael
Private Nachricht sendenPosting in privater Nachricht zitieren Webseite dieses Mitglieds besuchen
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Mi 10.02.10 23:49  Antworten mit Zitat Beitrag melden
user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:
Hol dir den text, häng eine Zeile an und setzt den Text wieder. Dann hast du auch gleich gezeigt, wie man Text ausliest.

Ok, das kann ich machen...die Frage ist, ob das nicht viel aufwändiger und langsamer ist als KeyDown ^^

user profile iconLuckie hat folgendes geschrieben Zum zitierten Posting springen:

Warum braucht man WM_KEYDOWN häufiger? Schaltflächen klickt man an, in dem man die entsprechende Nachricht an das Fenster oder die Schaltfläche schickt.

Liegt vielleicht daran, was ich so mache...z.B. mein Fernseh-Programm den Sender umschalten (das geht nur dadurch, dass ich ihm direkt sage welche Taste angeblich geklickt wurde...auch eben um wenige Zeichen in ein Textfeld zu schreiben mache ich das gerne, da es irgendwie für mich intuitiver und einfacher ist

_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)
Private Nachricht sendenPosting in privater Nachricht zitieren
Luckie
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic star

Beiträge: 11066
Erhaltene Danke: 2
Dabei seit: 30.08.2002


WindowsXP Professional SP2
BDS2006, VS C++ Express, Eclipse (Java), Flex (Flash, AS)
BeitragVerfasst: Mi 10.02.10 23:52  Antworten mit Zitat Beitrag melden
Finde ich nicht. Du simulierst einen Menschen, aber warum? Mach es doch so, wie Windows es macht. das ist wesentlich zuverlässiger. Ein Industrieroboter macht ja auch menschliche Tätigkeit, sieht aber eher nicht wie ein Mensch aus, sondern ist an seine Tätigkeit perfekt angepasst.

ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TForm2.Button1Click(Sender: TObject);
var
ParentWindow: THandle;
Button: THandle;
ButtonID: DWORD;
begin
ParentWindow := FindWindow(nil, 'Portscanner 1.0');
if ParentWindow <> 0 then
begin
Button := FindWindowEx(ParentWindow, 0, nil, '&Info');
if Button <> 0 then
begin
SendMessage(Button, BM_CLICK, 0, 0);
end
else
ShowMessage(SysErrorMessage(GetLastError));
end
else
ShowMessage(SysErrorMessage(GetLastError));
end;

_________________
Gruß Michael
Private Nachricht sendenPosting in privater Nachricht zitieren Webseite dieses Mitglieds besuchen
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Do 11.02.10 00:21  Antworten mit Zitat Beitrag melden
ausblenden Delphi-Quelltext markieren
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:
procedure TTutorialForm1.Button2Click(Sender: TObject);
var H: THandle; S: String; TextLength: DWord;
begin
h:=FindWindow(nil,'Window of TutorialForm2');
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2 gefunden (Handle='+inttostr(h)+')');
H:=FindWindowEx(H,0,'TMemo',nil);
if h<>0 then
begin
Memo1.Lines.Add('TTutorialForm2.Memo gefunden (Handle='+inttostr(h)+')');

TextLength := SendMessage(H, WM_GETTEXTLENGTH, 0, 0) ;
Memo1.Lines.Add('Memo TextLength: '+inttostr(TextLength));
SetLength(S,TextLength);

SendMessage(H,WM_GETTEXT,TextLength+1,Integer(Pointer(S)));
Memo1.Lines.Add('Read from Memo: '+S);

S:=S+' / '+timetostr(Now);
Memo1.Lines.Add('Sending to Memo: '+S);

SendMessage(H,WM_SETTEXT,0,Integer(Pointer(S)));
end
else
Memo1.Lines.Add('TTutorialForm2.Memo nicht gefunden!');
end
else
Memo1.Lines.Add('TTutorialForm2 nicht gefunden!');
end;


Musste erst bisschen recherchieren, bin Pointer-technisch ganz schlecht :D Warum muss ich denn bei WM_GETTEXT eins mehr als die Länge des Strings abfragen? Die Dokumentation von Microsoft sagt dazu nichts aus.

_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)
Private Nachricht sendenPosting in privater Nachricht zitieren
Luckie
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starhalf offtopic starofftopic star

Beiträge: 11066
Erhaltene Danke: 2
Dabei seit: 30.08.2002


WindowsXP Professional SP2
BDS2006, VS C++ Express, Eclipse (Java), Flex (Flash, AS)
BeitragVerfasst: Do 11.02.10 01:02  Antworten mit Zitat Beitrag melden
Das abschließende #0 Zeichen.

Aber du hast keinerlei Fehlerbehandlung drin:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
procedure TForm3.Button1Click(Sender: TObject);
var
Memo: THandle;
len: Integer;
s: String;
begin
Memo := FindWindowEx(Handle, 0, 'TMemo', nil);
if Memo <> 0 then
begin
len := SendMessage(Memo, WM_GETTEXTLENGTH, 0, 0);
if len > 0 then
begin
SetLength(s, len);
if SendMessage(Memo, WM_GETTEXT, len+1, Integer(@s[1])) > 0 then
ShowMessage(s);
s := TrimRight(s);
s := s + #13#10 + 'Mein Text';
SendMessage(Memo, WM_SETTEXT, 0, Integer(@s[1]));
end;
end
else
ShowMessage(SysErrorMessage(GetLastError));
end;

_________________
Gruß Michael
Private Nachricht sendenPosting in privater Nachricht zitieren Webseite dieses Mitglieds besuchen
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Do 11.02.10 11:48  Antworten mit Zitat Beitrag melden
Ok, habs geändert. (Posting 3, letzter Code)

_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)
Private Nachricht sendenPosting in privater Nachricht zitieren
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Sa 13.02.10 16:00  Antworten mit Zitat Beitrag melden
Kapitel 4 - RegisterWindowMessage
(Danke an user profile iconBenBE für den Hinweis auf diese Funktion)

Nachdem wir jetzt wissen wie man messages verwendet müssen wir aufpassen, dass wir kein durcheinander anrichten, da die messages WM_USER+x von mehreren Programmen mit unterschiedlichster Bedeutung verwendet werden. Mit RegisterWindowMessage können wir uns für die Kommunikation unsrer zwei Programme eine eindeutige ID für unsre Message holen.

Erweitern wir die in Kapitel1 verwendete Funktion so, dass wir RegisterWindowMessage verwenden.

Zuerst benötigen wir eine Variable (in beiden Programmen):
ausblenden Delphi-Quelltext markieren
1:
2:
var
WM_HELLOregistered: cardinal;

Im Gegensatz zu vorher ist dieser Wert nicht konstant, da wir den vom System erst zugewiesen bekommen.

Schreiben wir in den Konstruktor (in beiden Programmen):
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
procedure TTutorialForm1.FormCreate(Sender: TObject);
begin
WM_HELLOregistered:=RegisterWindowMessage('WM_HELLOregistered');
Memo1.Lines.Add('RegisterWindowMessage() gab '+inttostr(WM_HELLOregistered)+' zurück');
end;

RegisterWindowMessage weißt uns eine ID für unsre message zu. Ist sie schon registriert (z.B. wenn Programm1 vor Programm2 gestartet wurde), bekommen wir trotzdem die ID zurück. Beide Programme haben also jetzt die gleiche ID für die message, und diese message-ID ist im ganzen System einmalig.

Platzieren wir jetzt noch einen weiteren Button in Programm1. Das senden funktioniert analog Kapitel1:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
procedure TTutorialForm1.Button3Click(Sender: TObject);
var H: THandle;
begin

h:=FindWindow(nil,'Window of TutorialForm2');

if (WM_HELLOregistered<>0) and( h<>0 ) then
begin
Memo1.Lines.Add('TTutorialForm2 gefunden (Handle='+inttostr(h)+')');
PostMessage(H,WM_HELLOregistered,0,0);
end
else
Memo1.Lines.Add('TTutorialForm2 nicht gefunden!');

end;


Das empfangen jedoch ist etwas anders...da wir keine konstante haben, können wir nicht direkt eine procedure deklarieren, die immer aufgerufen wird, wenn die message ankommt.
Wir müssen also die procedure, die alle messages empfängt so verändern, dass wir dort auf unsre message reagieren können.

Die entsprechende procedure heißt WndProc(TMessage); Wir überschreiben (ersetzen) diese in Programm2 mit eigenem Code:
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
type
TTutorialForm2 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen }
public
procedure SomeoneSaysHello(var msg: TMessage); message WM_HELLO;

// Die Default WndProc überschreiben
procedure WndProc(var msg : TMessage); override;
end;

//...

procedure TTutorialForm2.WndProc(var msg : TMessage);
begin
if msg.msg=WM_HELLOregistered then
begin
SomeOneSaysHello(msg);
end
else
inherited WndProc(msg);
end;


(kurze Erklärung, was überschreiben bedeuted):
Das override überschreibt die procedure WndProc. Vereinfacht erklärt: Angenommen eine Person hat einen Zettel in der Hand und macht immer das was dort drauf steht. Wir nehmen ihr jetzt einfach den Zettel ab und geben ihr einen neuen. Die Person macht jetzt das, was auf dem neuen Zettel steht. Jetzt können wir aber (mit inherited) auch noch auf den alten Zettel zugreifen. Das passiert in dem Stückchen Code.

Wir haben uns sozusagen eine Abzweigung eingebaut. Ist die msg=WM_HELLOregistered, dann "biegen" wir ab in unsre procedure. Sonst wird einfach die original WndProc aufgerufen.
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)


Zuletzt bearbeitet von Xion am Sa 13.02.10 17:14, insgesamt 2-mal bearbeitet
Private Nachricht sendenPosting in privater Nachricht zitieren
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
EE-Maler

(Threadstarter)

Beiträge: 1343
Erhaltene Danke: 2
Dabei seit: 23.02.2006
Wohnort: Mitte Deutschlands / A**** der Welt

Windoof 2000, XP
Delphi 6 Enterprise / Delphi 2005 Prof
BeitragVerfasst: Sa 13.02.10 16:42  Antworten mit Zitat Beitrag melden
Kapitel 5 - Strukturierte Datenmengen verschicken
(Dank an user profile iconLuckie für sein Code-Snippet auf seiner Website)

Manchmal möchte man mehrere Werte übertragen. Jetzt könnte man natürlich mehrere messages verschicken. Man kann aber auch ein ganzes record auf einmal verschicken.

Erstellen wir zuerst ein packed record (in beiden Programmen)
ausblenden Delphi-Quelltext markieren
1:
2:
3:
4:
5:
6:
 type
TMyRecord = packed record
Text: string[255];
Handle: cardinal;
end;
PMyRecord = ^TMyRecord;

Ein normales record funktioniert auch, allerdings ist das packed record speichereffizienter.

Wir werden diesesmal zusätzlich zum String noch das Handle unsrer Form mitschicken. Damit könnte man schon eine Art Server/Client Struktur bauen, wobei der Server immer genau weiß, welche Anfrage von welcher Form kam.

Platzieren wir jetzt einen weiteren Button auf die Form von Programm1
ausblenden Delphi-Quelltext markieren
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:
procedure TTutorialForm1.Button4Click(Sender: TObject);
var
MyRecord: PMyRecord;
cds: TCopyDataStruct;
h: THandle;
begin
GetMem(MyRecord, sizeof(TMyRecord));
try
//Record füllen
MyRecord.Handle := TutorialForm1.Handle;
MyRecord.Text := 'Hello world';

//CopyDataStruct füllen
cds.dwData := 0;
cds.cbData := sizeof(TMyRecord);
cds.lpData := MyRecord;
H := FindWindow(nil, 'Window of TutorialForm2');
if H<>0 then
begin
SendMessage(H, WM_COPYDATA, TutorialForm1.Handle, Integer(@cds));
Memo1.Lines.Add('Record sent');
end;
finally
FreeMem(MyRecord, sizeof(TMyRecord));
end;
end;

Dieser Code füllt das CopyDataStruct mit den Record-Daten und schickt es dann via WM_COPYDATA zu Programm2.

Fügen wir jetzt in Programm2 den Empfangsteil ein
ausblenden Delphi-Quelltext markieren
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:
type
TTutorialForm2 = class(TForm)
Memo1: TMemo;
procedure FormCreate(Sender: TObject);
private
{ Private-Deklarationen }
public
//...

procedure DataIncoming(var msg: TWMCopyData ); message WM_COPYDATA;
end;

//...

procedure TTutorialForm2.DataIncoming(var msg: TWMCopyData );
var
MyRecord: PMyRecord;
H: cardinal;
s: String[255];
S2: array [0..255]of Char;
begin
H := PMyRecord(msg.CopyDataStruct.lpData)^.Handle;
S := PMyRecord(msg.CopyDataStruct.lpData)^.Text;

Memo1.Lines.Add('Record incoming: Text='+S+' Handle='+inttostr(H));
GetWindowText(H,S2,255);
Memo1.Lines.Add('Record von Handle='+inttostr(H)+' erhalten ("'+S2+'")');
end;


Erst lesen wir das Array aus, dann lesen wir noch aus dem mitgelieferten handle den Fenstertext aus.
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
F steht für Feuer das wütet und lodert U steht für unfairer Kampf N steht für nukleares Waffenarsenal (Plankton)
Private Nachricht sendenPosting in privater Nachricht zitieren
Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.


Werbung ausblenden? Dann registriere Dich kostenlos. Weitere Gründe für eine Registrierung.
Beiträge vom vorherigen Thema anzeigen:   
home home