Autor Beitrag
Xion
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 02.05.12 19:20 
Stichworte: FindWindow SendMessage PostMessage

Dieses Tutorial wurde auf Windows XP implementiert und getestet, ob dies auch in neueren Windows-Versionen funktioniert ist dem Autor unbekannt

1. Zielstellung
IPC im allgemeinen bezeichnet den Austausch von Daten zwischen Prozessen. Dies kann auf verschiedene Wege geschehen, etwa durch einen Socket oder durch eine Datei, die zunächst von einem Prozess geschrieben und anschließend vom anderen Prozess gelesen wird.

Unter Windows stellt uns das Betriebssystem zusätzlich sogenannte Messages zur Verfügung. Diese kann man sich als kleine Paketchen vorstellen, die vom Betriebssystem zugestellt werden. Da dies in Delphi gut gekapselt ist, muss sich der Entwickler nicht damit auseinandersetzen. Man kann jedoch dieses System blockieren, etwa durch eine zeitaufwändige Berechnung, so dass die Anwendung "nicht mehr reagiert". Durch den Befehl Application.ProcessMessages kann man nun von Hand veranlassen, dass die zugestellten Messages abgerufen und verarbeitet werden. All dies geschieht intern, wir bemerken davon meist nur, dass nun die Grafik neu gezeichnet oder ein Button-Klick erkannt wurde.

Wir werden nun im Folgenden verschiedene Gründe sehen, warum sich ein näherer Blick auf diese Messages dennoch lohnen kann.

_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 02.05.12 19:22 
2. Ein Image, welches auf die Maus reagiert
Dieses Kapitel soll einen kleinen Einstieg geben in das Thema. Wir wollen der TImage Komponente Events beifügen, welche es ermöglichen, Mouse-Hover-Effekte zu erzeugen.

Dazu benötigen wir zwei Events: OnMouseEnter und OnMouseLeave, wie sie von anderen Komponenten bekannt sind. Diese bietet die TImage Komponente jedoch nicht von sich aus (Stand: Delphi 2005). Die Klasse bekommt diese Messages bereits zugeschickt, wertet diese aber nicht aus. :? Dies wollen wir nun ändern.

Unsere neue Klase sieht wie folgt aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
type
  TAdvImage = class(TImage)
  private
    VOnMouseLeave: TNotifyEvent;
    VOnMouseEnter: TNotifyEvent;
    procedure Handle_MouseEnter(var Msg: TMessage); message CM_MOUSEENTER;
    procedure Handle_MouseLeave(var Msg: TMessage); message CM_MOUSELEAVE;
  published
    property OnMouseEnter: TNotifyEvent read VOnMouseEnter write VOnMouseEnter;
    property OnMouseLeave: TNotifyEvent read VOnMouseLeave write VOnMouseLeave;
  end;


Bis auf die Funktionen Handle_MouseEnter und Handle_MouseLeave zeigt dieser Code lediglich die Definition der Events.

Die beiden neuen Funktion sind dank der klaren Delphi-Syntax auch leicht zu verstehen. Trifft die Message CM_MOUSEENTER ein, so wird die Funktion Handle_MouseEnter automatisch aufgerufen. Wir erhalten dabei die Message als Parameter, diesen brauchen wir aber hier nicht, da wir lediglich die Callback-Funktion VOnMouseEnter aufrufen müssen. Nun haben wir schon ein funktionierendes Event. Das war einfach. :)
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 02.05.12 19:25 
3. Befehle erteilen
Im letzten Kapitel waren wir Empfänger einer Message. Nun wollen wir aktiv werden und selbst etwas mitteilen. Als Beispiel entwickeln wir nun eine ferngesteuerte Stoppuhr, die man etwa zum Messen von Berechnungszeiten nutzen könnte.
Dazu erstellen wir zunächst ein Programm, welches die Stoppuhr implementiert.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
type
  TStoppUhr = class(TForm)
     [...]
  private
     [...]
  public
     procedure Start(var msg: TMessage); message WM_CLOCKSTART;
     procedure Stop(var msg: TMessage); message WM_CLOCKSTOP;
     procedure Reset(var msg: TMessage); message WM_CLOCKRESET;
  end;


Die verwendeten Messages sind dabei natürlich erstmal undefiniert. Wir müssen diesen Konstanten nun einen Zahlenwert zuordnen. Um nicht mit den Windows-internen Messages zu kollidieren, gibt es einen eigenen Nummernraum für uns Entwickler. Dieser beginnt bei WM_USER, so dass wir unsere Messages folgendermaßen definieren dürfen:
ausblenden Delphi-Quelltext
1:
2:
3:
const WM_CLOCKSTART=WM_USER+1;
      WM_CLOCKSTOP =WM_USER+2;
      WM_CLOCKRESET=WM_USER+3;


Schnell haben wir nun die drei Funktionen implementiert und einen Timer erstellt, welche die aktuelle Zeit darstellt. Da noch niemand unserer StoppUhr eine Message sendet, tut sich natürlich erstmal nichts. :( Um dies zu ändern, werden wir nun einen Sender programmieren.

Zunächst müssen wir im Sender die Konstanten mit gleichen Werten definieren, damit unsere Stoppuhr auch die empfangenen Nachrichten versteht und richtig bearbeitet. Im Anhang kann wieder der volle Code betrachtet werden, ich werde hier nur auf die wesentliche Methode eingehen. Diese Methode erhält als Parameter die Nachricht, die wir senden wollen (z.B. WM_CLOCKSTART) und sieht wie folgt aus:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
procedure TSender.Send(msg: cardinal);
var h: THandle;
begin
  h:=FindWindow('TStoppUhr',nil);  //das fremde Fenster finden und eine Referenz darauf erhalten
  if h<>0 then
    PostMessage(h,Msg,0,0//Sende die message an die gegebene Referenz
  else
    ShowMessage('Stoppuhr wurde leider nicht gefunden. Ist das entsprechende Programm gestartet?');
end;

Laufen beide Programme, so kann nun die Stoppuhr ferngesteuert werden. Das war auch einfach. ;)
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 02.05.12 19:28 
4. Datenaustausch
Wenn es nicht genügt, nur Befehle zu erteilen, sondern auch Daten auszutauschen, so kann dies mit Hilfe der Message WM_COPYDATA erfolgen. Delphi stellt dabei bereits die nötigen Datentypen bereit.

Erweitern wir also unsere Stoppuhr um eine Logging-Funktion. In unserem Hauptprogramm ist beispielsweise ein Fehler aufgetreten, den wir nicht lokalisieren können. Breakpoints sind ungeeignet, da die Schleife sehr oft durchlaufen wird. Der Debugger stürzt leider auch jedesmal ab (nicht unwahrscheinlich mit Delphi 2005 ;) ). Deshalb wollen wir in unserer Stoppuhr-Anwendung vermerken, bis zu welchem Wert die Schleife ordentlich gerechnet hat, um dem Fehler auf die Spur zu kommen. Wir erweitern also den Sender um folgende Funktion:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
procedure TSender.Log(text: String);
var h: THandle;
    cds: TCopyDataStruct;begin

  //--- den String verpacken
  cds.dwData:=0;
  cds.cbData:=StrLen(PChar(text)) + 1;
  cds.lpData:=PChar(text);
  //---

  h:=FindWindow('TStoppUhr',nil);
  if h<>0 then
    SendMessage(h,WM_COPYDATA,Sender.Handle,Integer(@cds));
end;


Zunächst verpacken wir den String in eine Struktur. Diese senden wir dann an die Stoppuhr-Anwendung. Achtung: SendMessage blockiert den Programmfluss nun solange, bis die Empfangsfunktion abgearbeitet ist.
Als Empfangsfunktion in unserer StoppUhr-Anwendung verwenden wir eine Funktion, die durch WM_COPYDATA ausgelöst wird. Diese liest den String ein und gibt ihn in einem Memo aus.

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TStoppUhr.Log(var msg: TWMCopyData );
var sText: array [0..256of Char;
begin
  StrLCopy(sText, Msg.CopyDataStruct.lpData, Msg.CopyDataStruct.cbData);
  Memo1.Lines.Add(inttostr(msg.From)+': '+sText);
  //sleep(1000); //hier können wir sehen, dass die sendende Applikation wartet bis dieser Code abgearbeitet ist.
end;


Im Anhang ist das Beispiel wieder als funktionsfähiges Projekt zu finden.
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 02.05.12 19:30 
5. Strukturierter Datenaustausch
(Dank an user profile iconLuckie für sein Code-Snippet auf seiner Website)

Wir können bereits Strings zwischen Programmen austauschen. Theoretisch könnte man damit bereits alles realisieren, auch wenn es etwas Umstände machen würde, verschiedene Datentypen in einen String zusammenzufassen und beim Empfangen wieder zu zerlegen. Allerdings bietet uns Delphi eine einfachere Möglichkeit. Denn wir übergeben ja nur einen Pointer und eine Größe. Ob sich dahinter nun ein String versteckt oder ein record, ist unerheblich. Wichtig ist nur, dass beide Gesprächspartner die selbe Sprache sprechen, d.h. das selbe Format verwenden. Deshalb können wir auch ohne weiteres ein strukturierte Datenmenge als Record übergeben. Achtung: Im übergebenen Record sollten keine Referenzen/Pointer (z.B. auch dynamische arrays und strings) gespeichert sein, da diese in der empfangenden Anwendung keine Gültigkeit besitzen, da beide Prozesse in verschiedenen Speicherräumen arbeiten und nicht auf fremden Speicher zugreifen können.

Das vorherige Beispiel können wir nun flexibler durch ein Record realisieren:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
type
  TLogInfo = packed record
    Index: integer;
    Sum: real;
    Hint: string[32];
  end;
  PLogInfo = ^TLogInfo; //ein Pointer auf unser record sei als Typ PLogInfo definiert


Dieses record deklarieren wir in beiden Programmen. Wir verwenden dabei (optional) ein packed record, da dieses weniger Speicherplatz benötigt. Der Zugriff darauf ist zwar tendenziell langsamer, jedoch müssen so weniger Daten mit der Nachricht transportiert werden.

Die Sende-Funktion muss nun etwas verändert werden, da wir explizit einen Pointer auf ein Record benötigen:
ausblenden 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:
procedure TSender.Log(index:integer; Sum: real);
var h: THandle;
    cds: TCopyDataStruct;
    rec: PLogInfo;
begin

  GetMem(rec, sizeof(TLogInfo)); //Speicher reservieren
  try
    //Record füllen
    rec.Index := Index;
    rec.Sum := Sum;

    //CopyDataStruct füllen
    cds.dwData := 0;
    cds.cbData := sizeof(TLogInfo);
    cds.lpData := rec;

    h:=FindWindow('TStoppUhr',nil);
    if h<>0 then
      SendMessage(h,WM_COPYDATA,Sender.Handle,Integer(@cds));
  finally
    FreeMem(rec, sizeof(TLogInfo)); //Speicher freigeben
  end;
end;


In der Empfangsfunktion können wir nun explizit auf die einzelnen Elemente des Records zugreifen.
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
procedure TStoppUhr.Log(var msg: TWMCopyData );
var
  rec: TLogInfo;
  sum: real;
  index: integer;
  hint: String[32];
begin
  index := PLogInfo(msg.CopyDataStruct.lpData)^.index;
  sum := PLogInfo(msg.CopyDataStruct.lpData)^.sum;
  hint := PLogInfo(msg.CopyDataStruct.lpData)^.hint;

  Memo1.Lines.Add('Status: "'+hint+'" Index:"'+inttostr(index)+' Sum: "'+floattostr(Sum)+'"');
end;


Man kann sich leicht vorstellen, dass mit dieser Methode auch komplexere Protokolle realisiert werden können. Bei all den Möglichkeiten sollte man jedoch nicht vergessen, dass Messages lediglich innerhalb des Systems transportiert werden können, weshalb eine Nutzung von Sockets flexibler ist, falls die andere Anwendung eventuell irgendwann auf einem anderen Rechner laufen soll.
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 02.05.12 19:32 
6. RegisterWindowMessage
(Dank an user profile iconBenBE für den Hinweis auf diese Funktion)

Wie ihr vermutlich bereits gemerkt habt, haben wir bisher keinerlei Sicherheit was unseren Gesprächspartner angeht. Ein "Angreifer" (durchaus auch unbeabsichtigt) könnte sehr leicht unsere Stoppuhr manipulieren, wenn er nur die richtigen Message-Nummern an unser Programm sendet. Die statischen Nummern führen auch zu gewissen Problemen mit der Organisation der Kommunikation. Es muss immer jedem Gesprächspartner der aktuelle Satz an Messages bekannt sein, damit eine korrekte Kommunikation stattfinden kann. In unserem Beispiel ist dies noch kein Problem, da wir beide Programme selbst schreiben, jedoch kann dies in dynamischen Szenarien durchaus problematisch werden.

Um diesen Problemen entgegenzuwirken gibt es die Funktion RegisterWindowMessage. Diese bildet uns einen String auf eine eindeutige Nummer ab. Rufen wir die Funktion mit dem selben Parameter mehrmals auf, so erhalten wir jeweils die gleiche Nummer (dies kann auch unser Gesprächspartner tun und erhält die selbe Nummer wie wir). Umgekehrt werden verschiedenen Strings auch verschiedene Nummern zugewiesen.

ausblenden Delphi-Quelltext
1:
WM_CLOCKSTART:=RegisterWindowMessage('Give me the ClockStart-Message-Number');					


Es gibt jetzt allerdings ein kleines Problem. Da WM_LOG nun offentsichtliche keine Konstante mehr sein kann, können wir nicht die schöne Delphi-Syntax nutzen:

ausblenden Delphi-Quelltext
1:
procedure Start(var msg: TMessage); // message WM_CLOCKSTART;					


Wir müssen somit den Aufruf von Start per Hand auslösen. Dazu überschreiben wir die Message-Handler-Funktion, also die Funktion, bei der intern die Messages ankommen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
type
  TStoppUhr = class(TForm)
    [...]
     // Die Default WndProc überschreiben
     procedure WndProc(var msg : TMessage); override;
  end;

procedure TStoppUhr.WndProc(var msg : TMessage);
begin
  if msg.msg=WM_CLOCKSTART then
    begin
      Start(msg);
    end
  else
    inherited WndProc(msg);
end;

Wir machen uns das Leben hierbei einfach: Ist die angekommene Message die richtige, so bearbeiten wir diese. Alle anderen geben wir einfach an die alte (inherited) Funktion weiter, diese wird sich dann darum kümmern, genau wie bisher auch. :zustimm:

Im Anhang findet ihr wieder den Code zu diesem Kapitel.
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)
Xion Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
EE-Maler
Beiträge: 1952
Erhaltene Danke: 128

Windows XP
Delphi (2005, SmartInspect), SQL, Lua, Java (Eclipse), C++ (Visual Studio 2010, Qt Creator), Python (Blender), Prolog (SWIProlog), Haskell (ghci)
BeitragVerfasst: Mi 02.05.12 19:36 
7. Unfreiwilliger Zugriff
Dies ist das letzte und wohl auch das spannendste Kapitel. Vielleicht kennt ihr Programme wie PantsOff, mit denen man (manche) Passwort-Felder als Klartext darstellen kann, falls man mal wieder sein Passwort vergessen hat. ;)

Dieses Szenario ist nicht wie unsere bisherigen Anwendungen, welche dafür ausgelegt waren, miteinander zu sprechen. Oft sieht man sich dem Problem gegenüber, dass ein fremdes Programm Funktionalitäten bietet, die man für sein eigenes Programm in irgend einer Form nutzen möchte, bzw. dass man eine Erweiterung für dieses Programm entwickeln möchte, dies jedoch nicht unterstüzt wird.

Messages bieten da gewisse Möglichkeiten, in manchen Fällen doch an die gewünschten Informationen zu kommen oder Aktionen durchzuführen. Da auch User-Interaktionen über Messages verbreitet werden (wie wir bei unserem TImage gesehen haben), können wir einen User simulieren. Es gibt dort auch einige mächtigere Instrumente, wie z.B. Text aus einem Edit-Feld auslesen. Das ganze hat jedoch seine Grenzen, da wir nur Nachrichten an Komponenten senden können, welche ein Handle besitzen. Dies ist bei einem Label z.B. nicht der Fall, so dass wir von diesem leider keine Informationen gewinnen können. :(

In unserem Beispiel (siehe Anhang) haben wir ein Programm, welches eine Berechnung durchführt und ausgibt. Die Berechnung kann eine Weile dauern und man muss schnell hinschauen um sie zu sehen. Uns wäre es lieber, wenn das Tool die Möglichkeit bieten würde, die Ausgabe z.B. in eine Datei zu loggen, doch dies ist nicht gegeben.

Durch unsere neuen Kenntnisse haben wir aber eine Möglichkeit, das Ergebnis zu loggen.

Wir nutzen also FindWindow (Nutzen wir den zweiten Parameter, so können wir leicht den Fenstertitel verwenden) um das Handle zu bekommen. Mit FindWindowEx können wir ein "Unterfenster" ermitteln, welches auch ein Edit-Feld wie in unserem Beispiel sein kann.

Anschließend senden wir eine Nachricht an das Edit, um die Textlänge zu ermitteln und lesen anschließend den Text.

ausblenden 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:
procedure TSniffer.Timer1Timer(Sender: TObject);
var h: THandle; TextLength: integer; Text: String;
begin
  if CheckBox1.Checked then
    begin
      h := FindWindow('TDeepThought',nil);
      if (h<>0then
        begin
          h:=FindWindowEx(h,0,'TEdit',nil);
          if (h<>0then
            begin
              TextLength := SendMessage(h, WM_GETTEXTLENGTH, 00) ;   if TextLength>0 then
              if (TextLength<>0then
                begin
                  SetLength(Text,TextLength+1);

                  if SendMessage(H,WM_GETTEXT,TextLength+1,Integer(Pointer(Text)))>0 then
                    if Pos('The Answer Is:',Text)>0 then
                      Memo1.Lines.Add(Text);
                end;
            end;
        end;
    end;
end;



Mit folgender Zeile können wir auch etwas in das Edit einfügen, was allerdings in diesem Fall wenig Sinn macht:
ausblenden Delphi-Quelltext
1:
SendMessage(h,WM_SETTEXT,0,Integer(Pointer(Text)))					


Da man bei fremden Programmen nicht die Struktur und die Klassen kennt, kann man das kostenlose Programm WinSpector nutzen.

Mit diesen einfachen Mitteln habt ihr ein mächtiges Werkzeug an der Hand und könnt nun kreativ fremde Programme um nützliche Fähigkeiten erweitern. Ihr könnt auch Tastatur-Kommandos senden und so z.B. Aktionen starten oder Buttons drücken. In der unit Messages kann man sich von einer großen Liste Messages inspirieren lassen.

Ich hoffe euch hat dieses Tutorial weitergeholfen und zumindest etwas Spaß gemacht.
Einloggen, um Attachments anzusehen!
_________________
a broken heart is like a broken window - it'll never heal
In einem gut regierten Land ist Armut eine Schande, in einem schlecht regierten Reichtum. (Konfuzius)

Für diesen Beitrag haben gedankt: bole, DonManfred
glotzer
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 393
Erhaltene Danke: 49

Win 7
Lazarus
BeitragVerfasst: Mi 02.05.12 21:36 
WOW wirklich tolles Tutorial, großes Lob dafür :D

_________________
ja, ich schreibe grundsätzlich alles klein und meine rechtschreibfehler sind absicht
Jensen K.
Hält's aus hier
Beiträge: 1



BeitragVerfasst: Mo 07.05.12 16:03 
user profile iconglotzer hat folgendes geschrieben Zum zitierten Posting springen:
WOW wirklich tolles Tutorial, großes Lob dafür :D


Stimme ich total zu ! Hat mir sehr geholfen. Danke :)
uall@ogc
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 1826
Erhaltene Danke: 11

Win 2000 & VMware
Delphi 3 Prof, Delphi 7 Prof
BeitragVerfasst: Di 08.05.12 00:18 
Leicht umständlich das mit dem LogInfo oder nicht?
Wie wärs mit:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
procedure TSender.Log(index:integer; Sum: real);
var
  h: THandle;
  cds: TCopyDataStruct;
  rec: TLogInfo;
begin
    //Record füllen
  rec.Index := Index;
  rec.Sum := Sum;
  rec.Hint := 'irgendwas';

    //CopyDataStruct füllen
  cds.dwData := 0;
  cds.cbData := sizeof(rec);
  cds.lpData := @rec;

  h:=FindWindow('TStoppUhr',nil);
  if h <> 0 then
    SendMessage(h,WM_COPYDATA,Sender.Handle,Integer(@cds));
end;


und

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
procedure TStoppUhr.Log(var msg: TWMCopyData );
var
  rec: PLogInfo;
begin
  rec := PLogInfo(msg.CopyDataStruct.lpData);
  Memo1.Lines.Add('Status: "'+rec.hint+'" Index:"'+inttostr(rec.index)+' Sum: "'+floattostr(rec.Sum)+'"');
end;

_________________
wer andern eine grube gräbt hat ein grubengrabgerät
- oder einfach zu viel zeit