I try to exchange data between two applications in windows. I use an example from Zarko Gajic. It uses windows messaging and the example works great. There are a sender and a receiving application and some shared data: all coded for VCL. The code is shown below.
unit SenderMain;
{ How to send information (String, Image, Record) between two Delphi applications
http://delphi.about.com/od/windowsshellapi/a/wm_copydata.htm
Learn how to send the WM_CopyData message between two Delphi
applications to exchange information and make two applications
communicate. The accompanying source code demonstrates how to
send a string, record (complex data type) and even graphics
to another application.
~Zarko Gajic
About Delphi Programming
http://delphi.about.com
}
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, TlHelp32,
shared_data;
type
TSenderMainForm = class(TForm)
Button_Send_Data: TButton;
Log: TListBox;
procedure Button_Send_DataClick (Sender: TObject);
protected
procedure Loaded; override;
procedure SendString (send_string: aString);
end; // Class: TSenderMainForm //
var
SenderMainForm: TSenderMainForm;
implementation
{$R *.dfm}
{**************** NextWindow ****************}
function NextWindow (wnd: Thandle; list: Tstringlist):boolean; stdcall;
{This is the callback function which is called by EnumWindows procedure
for each top-level window. Return “true” to keep retrieving, return
“false” to stop EnumWindows from calling}
var
title: array [0..255] of char;
receiverHandle: HWND;//THandle;
win_name: PChar;
s: AnsiString;
begin
getwindowtext (wnd, title, 256);
s := AnsiString (pchar(@title));
if (s <> ”) and (list.indexof (string (s)) < 0) then
begin
win_name := PaString (s);
receiverHandle := FindWindow (win_name, nil); // Find receiving app
s := AnsiString (Format (‘%s (%d)’, [s, receiverHandle]));
list.add (string (s));
end; // if
result:=true;
end;
procedure TSenderMainForm.Loaded;
begin
inherited Loaded;
enumwindows (@nextwindow, lparam (Log.Items)); {pass the list as a parameter}
end;
procedure TSenderMainForm.SendString (send_string: aString);
var copyDataStruct: TCopyDataStruct; { Declared in Windows.pas: TCopyDataStruct}
receiverHandle: THandle;
res: integer;
begin
// Copy string to CopyDataStruct
copyDataStruct.dwData := 1; //use it to identify the message contents
copyDataStruct.cbData := (1 + Length (send_string)) * SizeOf (Char);
copyDataStruct.lpData := PaString (send_string);
receiverHandle := FindWindow (PaString (cClassName), nil); // Find receiving app
if receiverHandle = 0 then // not found
begin
Log.Items.Add (‘CopyData Receiver NOT found!’);
end else // found, send message
begin
res := SendMessage (receiverHandle, WM_COPYDATA, Integer(Handle), Integer(@copyDataStruct));
Log.Items.Add (Format (‘String sent, len = %d, result = %d’, [copyDataStruct.cbData, res]));
Log.Items.Add (‘”‘ + PaString (copyDataStruct.lpData) + ‘”‘);
end; // if
end; // SendString
procedure TSenderMainForm.Button_Send_DataClick (Sender: TObject);
begin
SendString (ParamStr (0));
end;
====================== Unit copyDataReceiver ================
unit ReceiverMain;
{ How to send information (String, Image, Record) between two Delphi applications
http://delphi.about.com/od/windowsshellapi/a/wm_copydata.htm }
interface
uses
Windows, Messages,
SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, ExtCtrls, shared_data;
type
TReceiverMainForm = class (TForm)
Log: TListBox;
procedure FormCreate(Sender: TObject);
private
procedure WMCopyData (var Msg: TWMCopyData); message WM_COPYDATA;
procedure WMSignalClose (var Msg: TMessage); message WM_SIGNAL_CLOSE;
procedure HandleCopyDataString (copyDataStruct: PCopyDataStruct);
end;
var ReceiverMainForm: TReceiverMainForm;
implementation
{$R *.dfm}
procedure TReceiverMainForm.FormCreate (Sender: TObject);
begin
Log.Clear;
end;
procedure TReceiverMainForm.WMSignalClose (var Msg: TMessage);
var pfn: PaString;
fn: aString;
begin
Log.Items.Add (Format (‘Signal received, WParam = %d, LParam = %d’, [Msg.WParam, Msg.LParam]));
pfn := PaString (Msg.LParam);
fn := aString (pfn);
Log.Items.Add (fn);
end;
procedure TReceiverMainForm.WMCopyData (var Msg: TWMCopyData);
var copyDataType: Int32;
begin
copyDataType := Msg.CopyDataStruct.dwData;
//Handle of the Sender
Log.Items.Add (Format (‘WM_CopyData (type: %d) from: %d’, [copyDataType, msg.From]));
HandleCopyDataString (Msg.CopyDataStruct);
//Send something back
msg.Result := Log.Items.Count;
end;
procedure TReceiverMainForm.HandleCopyDataString (copyDataStruct: PCopyDataStruct);
var mess: aString;
begin
mess := aString (PaString (copyDataStruct.lpData));
Log.Items.Add (Format (‘Received string of length %d at %s’, [Length (mess), DateToStr (Now)]));
Log.Items.Add (‘”‘ + mess + ‘”‘);
end;
end.
================ unit shared_data ==========================
unit shared_data;
interface
uses Messages;
const
WM_SIGNAL_CLOSE = WM_APP + 2012;
ARG_AMI_1 = 285;
ARG_AMI_2 = 1;
cClassName = ‘TReceiverMainForm’;
type
aString = string;
PaString = PChar;
implementation
end.
The crux of the sender application is that it sends a WM_COPYDATA to the receiver. In order to find the receiver, FindWindow is used with the name of the receiving application (hard-coded) which returns a handle to the window. If the handle is zero, an error is shown.
When I duplicate this in an FMX application there are troubles. The FMX receiving part does not work, while the VCL receiver can receive messages from either the VCL sender or the FMX sender. The code of the FMX receiver is shown below.
Because I wasn’t sure about the name of the windows I enumerated all windows, added the numeric handle to each window name and showed it in a listbox in the sender. All handles are zero. I have two questions:
Why are all handles zero in the enumeration?
Why can’t I send a message to the FMX receiving applation?
Any help would be greatly appreciated.
unit copyDataReceiver;
interface
uses
System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Layouts, FMX.ListBox,
Windows, Messages, shared_data;
type
TReceiverMainForm = class (TForm)
Log: TListBox;
procedure FormCreate(Sender: TFMXObject);
private
procedure WMCopyData (var Msg: TWMCopyData); message WM_COPYDATA;
procedure WMSignalClose (var Msg: TMessage); message WM_SIGNAL_CLOSE;
procedure HandleCopyDataString (copyDataStruct: PCopyDataStruct);
end;
var ReceiverMainForm: TReceiverMainForm;
implementation
{$R *.fmx}
procedure TReceiverMainForm.FormCreate (Sender: TFMXObject);
begin
Log.Clear;
end;
procedure TReceiverMainForm.WMSignalClose (var Msg: TMessage);
var pfn: PaString;
fn: aString;
begin
Log.Items.Add (Format (‘Signal received, WParam = %d, LParam = %d’, [Msg.WParam, Msg.LParam]));
pfn := PaString (Msg.LParam);
fn := aString (pfn);
Log.Items.Add (fn);
end;
procedure TReceiverMainForm.WMCopyData (var Msg: TWMCopyData);
var copyDataType: Int32;
begin
copyDataType := Msg.CopyDataStruct.dwData;
//Handle of the Sender
Log.Items.Add (Format (‘WM_CopyData (type: %d) from: %d’, [copyDataType, msg.From]));
HandleCopyDataString (Msg.CopyDataStruct);
//Send something back
msg.Result := Log.Items.Count;
end;
procedure TReceiverMainForm.HandleCopyDataString (copyDataStruct: PCopyDataStruct);
var mess: aString;
begin
mess := aString (PaString (copyDataStruct.lpData));
Log.Items.Add (Format (‘Received string of length %d at %s’, [Length (mess), DateToStr (Now)]));
Log.Items.Add (‘”‘ + mess + ‘”‘);
end;
end.