• Status: Solved
  • Priority: Medium
  • Security: Public
  • Views: 782
  • Last Modified:

Communication between two applications (On the same machine)?

I am trying to find the simplest method of allowing any application to send a message to mine and have one sent back, e.g. One app sends a message to my audio application and it returns the song name and length etc.

Any help with this is appreciated.

NOTE: This is two applications on the same machine, not through the internet or LAN using sockets.
0
HiImGlen
Asked:
HiImGlen
  • 3
  • 3
  • 2
  • +2
1 Solution
 
2266180Commented:
well .. probably using the windows messaging system would do it just fine for your case.
you define some custom messages and use them to play around.
here is a brief "howto": http://www.cryer.co.uk/brian/delphi/howto_send_custom_window_message.htm
0
 
KyleyHarrisCommented:
Why would you not use TCP/IP? It is certainly the most efficient and easy to manage solution, requiring extremely small overhead.
Your other options would include a memory mapped space, which works the same way you would manipulate a filehandle.
0
 
ZhaawZSoftware DeveloperCommented:
TCP/IP? And what if a PC does not have TCP/IP protocols installed? ;)

There's no problem with sending/receiving messages. For sending message you use SendMessage() function, for processing it you write a simple procedure. The problem is with exchanging larger amounts of data between applications (like some text). Here are few ways how it could be done:
1) using memory mapped files (as KyleyHarris said) - haven't used these
2) using OpenProcess, ReadProcessMemory, ... - I'll give an example of this (I used to use this for getting info from winamp without involving "winamp plugin" ;)
3) you may write info to a file from app nr.1 and then read this info from app nr.2 - quite rude sollution, I think
0
Free Tool: ZipGrep

ZipGrep is a utility that can list and search zip (.war, .ear, .jar, etc) archives for text patterns, without the need to extract the archive's contents.

One of a set of tools we're offering as a way to say thank you for being a part of the community.

 
Russell LibbySoftware Engineer, Advisory Commented:

Messaging would probably be the simplest, but when sending text data (regardless of size) to another process, you must be able to translate the data to a pointer that is valid in the other process's address space. Examples would be:

- Using memory mapped files
- Using the WM_COPYDATA message to send the data (data cannot be modified in the call)
- If data is under 255 chars, you could use the global atom table, and send the atom handle in a custom message

Examples can be provided if you have an idea of which one will suit your needs best.

Regards,
Russell
0
 
ZhaawZSoftware DeveloperCommented:
an example of sending/receiving data structure between apps ("client" and "server" application, i.e., sender and receiver):
http://lovelysoft.sytes.net/sending_data_between_applications.zip 
0
 
ZhaawZSoftware DeveloperCommented:
and here's the same (just in case if server dies or file gets removed from it)


----------- app nr.1
-------- Unit1.dfm
object Form1: TForm1
  Left = 192
  Top = 114
  Width = 247
  Height = 209
  AutoSize = True
  BorderIcons = [biSystemMenu]
  BorderWidth = 7
  Caption = 'sender'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  OnCreate = FormCreate
  PixelsPerInch = 96
  TextHeight = 13
  object Label1: TLabel
    Left = 0
    Top = 0
    Width = 22
    Height = 13
    Alignment = taCenter
    Caption = 'texts'
  end
  object Label2: TLabel
    Left = 0
    Top = 96
    Width = 40
    Height = 13
    Alignment = taCenter
    Caption = 'numbers'
  end
  object Edit1: TEdit
    Left = 0
    Top = 20
    Width = 225
    Height = 21
    TabOrder = 0
    Text = '1st txt'
  end
  object Edit2: TEdit
    Left = 0
    Top = 44
    Width = 225
    Height = 21
    TabOrder = 1
    Text = '2nd txt'
  end
  object Edit3: TEdit
    Left = 0
    Top = 68
    Width = 225
    Height = 21
    TabOrder = 2
    Text = '3rd txt'
  end
  object Edit4: TEdit
    Left = 0
    Top = 116
    Width = 225
    Height = 21
    TabOrder = 3
    Text = '4444444'
  end
  object Edit5: TEdit
    Left = 0
    Top = 140
    Width = 225
    Height = 21
    TabOrder = 4
    Text = '5555555'
  end
end

-------- Unit1.pas
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Classes, Forms, Controls, StdCtrls;

type
  TForm1 = class(TForm)
    Label1: TLabel;
    Label2: TLabel;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    Edit4: TEdit;
    Edit5: TEdit;
    procedure FormCreate(Sender: TObject);
    procedure onUserMsg(var msg : TMessage); message wm_user; // added manually
  end;

type
  TDataStruct = record
    txt1 : array [0..99] of char;
    txt2 : array [0..99] of char;
    txt3 : array [0..99] of char;
    num1 : cardinal;
    num2 : cardinal;
  end;

var
  Form1: TForm1;
  data : ^TDataStruct;

implementation

{$R *.dfm}

procedure TForm1.FormCreate(Sender: TObject);
begin
Edit1.MaxLength := 99;
Edit2.MaxLength := 99;
Edit3.MaxLength := 99;
// just to be sure that user does not write any "invalid chars" into Edit4 and Edit5 (which should contain only numbers)
with Edit4 do SetWindowLong(handle, gwl_style, GetWindowLong(handle, gwl_style) or es_number);
with Edit5 do SetWindowLong(handle, gwl_style, GetWindowLong(handle, gwl_style) or es_number);
end;

// added manually
procedure TForm1.onUserMsg(var msg : TMessage);
begin
with msg do
case wParam of
  0 : begin
    GetMem(data, sizeof(TDataStruct));
    lstrcpy(data^.txt1, @Edit1.Text[1]);
    lstrcpy(data^.txt2, @Edit2.Text[1]);
    lstrcpy(data^.txt3, @Edit3.Text[1]);
    if Edit4.Text = '' then data.num1 := 0 else data.num1 := StrToInt(Edit4.Text);
    if Edit5.Text = '' then data.num2 := 0 else data.num2 := StrToInt(Edit5.Text);
    case lParam of
      0 : result := integer(data); // send pointer to "data" as a result of message
      1 : result := integer(@data^.txt1); // send pointer to "txt1" as a result of message
      2 : result := integer(@data^.txt2); // send pointer to "txt2" as a result of message
      3 : result := integer(@data^.txt3); // send pointer to "txt3" as a result of message
    end;
  end;
  1 : FreeMem(data);
  2 : if Edit4.Text = '' then result := 0 else result := StrToInt(Edit4.Text);
  3 : if Edit5.Text = '' then result := 0 else result := StrToInt(Edit5.Text);
end;
end;

end.

----------- app nr.2
-------- Unit1.dfm
object Form1: TForm1
  Left = 192
  Top = 114
  Width = 142
  Height = 190
  AutoSize = True
  BorderIcons = [biSystemMenu]
  BorderWidth = 7
  Caption = 'receiver'
  Color = clBtnFace
  Font.Charset = DEFAULT_CHARSET
  Font.Color = clWindowText
  Font.Height = -11
  Font.Name = 'MS Sans Serif'
  Font.Style = []
  OldCreateOrder = False
  PixelsPerInch = 96
  TextHeight = 13
  object Button1: TButton
    Left = 0
    Top = 0
    Width = 120
    Height = 22
    Caption = '1st number'
    TabOrder = 0
    OnClick = Button1Click
  end
  object Button2: TButton
    Left = 0
    Top = 24
    Width = 120
    Height = 22
    Caption = '2nd number'
    TabOrder = 1
    OnClick = Button2Click
  end
  object Button3: TButton
    Left = 0
    Top = 48
    Width = 120
    Height = 22
    Caption = 'all data'
    TabOrder = 2
    OnClick = Button3Click
  end
  object Button4: TButton
    Left = 0
    Top = 72
    Width = 120
    Height = 22
    Caption = '1st text'
    TabOrder = 3
    OnClick = Button4Click
  end
  object Button5: TButton
    Left = 0
    Top = 96
    Width = 120
    Height = 22
    Caption = '2nd text'
    TabOrder = 4
    OnClick = Button5Click
  end
  object Button6: TButton
    Left = 0
    Top = 120
    Width = 120
    Height = 22
    Caption = '3rd text'
    TabOrder = 5
    OnClick = Button6Click
  end
end
-------- Unit1.pas
unit Unit1;

interface

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

type
  TButton = class(StdCtrls.TButton)
    procedure onFocus(var msg : TMessage); message wm_setfocus;
  end;
  TForm1 = class(TForm)
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Button4: TButton;
    Button5: TButton;
    Button6: TButton;
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
    procedure Button4Click(Sender: TObject);
    procedure Button5Click(Sender: TObject);
    procedure Button6Click(Sender: TObject);
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

procedure TButton.onFocus(var msg : TMessage);
begin
; // i hate that "focus rectangle" on buttons, so I block wm_setfocus message to projects, when using buttons
end;

procedure TForm1.Button1Click(Sender: TObject);
var
  wnd : cardinal;
  res : cardinal;
begin
wnd := FindWindow('TForm1', 'sender');
if wnd > 0 then begin
  res := SendMessage(wnd, wm_user, 2, 0);
  ShowMessageFmt('The number in 1st box is %d', [res]);
end else ShowMessage('Can''t find a window');
end;

procedure TForm1.Button2Click(Sender: TObject);
var
  wnd : cardinal;
  res : cardinal;
begin
wnd := FindWindow('TForm1', 'sender');
if wnd > 0 then begin
  res := SendMessage(wnd, wm_user, 3, 0);
  ShowMessageFmt('The number in 2nd box is %d', [res]);
end else ShowMessage('Can''t find a window');
end;

procedure TForm1.Button3Click(Sender: TObject);
type
  TDataStruct = record
    txt1 : array [0..99] of char;
    txt2 : array [0..99] of char;
    txt3 : array [0..99] of char;
    num1 : cardinal;
    num2 : cardinal;
  end;
var
  wnd : cardinal;
  pid : cardinal;
  prc : cardinal;
  res : pointer;
  dat : TDataStruct;
begin
wnd := FindWindow('TForm1', 'sender');
if wnd > 0 then begin
  // get PID - identifier of the process to open
  GetWindowThreadProcessId(wnd, @pid);
  // open process
  prc := OpenProcess(process_all_access, false, pid);
  // send message with 0 as wParam and lParam
  // this is manually processed message, so you may use your own values as wParam and lParam
  // message can be wm_user, wm_user+1, wm_user+5 or any other user definied message
  // you must process this message in a 'sender app'
  // in this case wParam 0 means that 'sender app' should prepare info (i.e., reserve memory and write data to it)
  // in this case lParam 0 means that 'sender app' should send pointer to all data structure
  res := pointer(SendMessage(wnd, wm_user, 0, 0));
  // read memory from 'external process' (i.e., from sender app - that's why OpenProcess() were needed)
  ReadProcessMemory(prc, res, @dat, sizeof(dat), cardinal(nil^));
  // process object is not needed anymore (i.e., MS Windows stuff that was created with OpenProcess), so destroy it
  CloseHandle(prc);
  // send message that instructs 'sender app' that it should free memory
  SendMessage(wnd, wm_user, 1, 0);
  // show received data
  with dat do
  ShowMessageFmt(
    'txt1: %s'#13#10+
    'txt2: %s'#13#10+
    'txt3: %s'#13#10+
    'num1: %d'#13#10+
    'num2: %d'#13#10,
    [txt1, txt2, txt3, num1, num2]
  );
end else ShowMessage('Can''t find a window');
end;

procedure TForm1.Button4Click(Sender: TObject);
var
  wnd : cardinal;
  pid : cardinal;
  prc : cardinal;
  res : pointer;
  txt : array [0..99] of char;
begin
wnd := FindWindow('TForm1', 'sender');
if wnd > 0 then begin
  GetWindowThreadProcessId(wnd, @pid);
  prc := OpenProcess(process_all_access, false, pid);
  res := pointer(SendMessage(wnd, wm_user, 0, 1));
  ReadProcessMemory(prc, res, @txt, sizeof(txt), cardinal(nil^));
  CloseHandle(prc);
  SendMessage(wnd, wm_user, 1, 0);
  ShowMessage(txt);
end else ShowMessage('Can''t find a window');
end;

procedure TForm1.Button5Click(Sender: TObject);
var
  wnd : cardinal;
  pid : cardinal;
  prc : cardinal;
  res : pointer;
  txt : array [0..99] of char;
begin
wnd := FindWindow('TForm1', 'sender');
if wnd > 0 then begin
  GetWindowThreadProcessId(wnd, @pid);
  prc := OpenProcess(process_all_access, false, pid);
  res := pointer(SendMessage(wnd, wm_user, 0, 2));
  ReadProcessMemory(prc, res, @txt, sizeof(txt), cardinal(nil^));
  CloseHandle(prc);
  SendMessage(wnd, wm_user, 1, 0);
  ShowMessage(txt);
end else ShowMessage('Can''t find a window');
end;

procedure TForm1.Button6Click(Sender: TObject);
var
  wnd : cardinal;
  pid : cardinal;
  prc : cardinal;
  res : pointer;
  txt : array [0..99] of char;
begin
wnd := FindWindow('TForm1', 'sender');
if wnd > 0 then begin
  GetWindowThreadProcessId(wnd, @pid);
  prc := OpenProcess(process_all_access, false, pid);
  res := pointer(SendMessage(wnd, wm_user, 0, 3));
  ReadProcessMemory(prc, res, @txt, sizeof(txt), cardinal(nil^));
  CloseHandle(prc);
  SendMessage(wnd, wm_user, 1, 0);
  ShowMessage(txt);
end else ShowMessage('Can''t find a window');
end;

end.
0
 
HiImGlenAuthor Commented:
Iv'e found something here: http://delphi.about.com/od/windowsshellapi/a/wm_copydata.htm

About WM_COPYDATA, does this look like it will get the job done?  
0
 
2266180Commented:
looks like it, yes. but then again it's not so different from the other windows messaging implementation up here AND it's been suggested by rllibby ;)
0
 
HiImGlenAuthor Commented:
Suggested yes but that site shows an actual example, or atleast enough to work off of, either way iv'e figured it all out now so I suppose i'll accept rlibby.
0
 
2266180Commented:
well .. it would be a big waste of time if we come up with examples/implementation on every question with every possibility. some of can do it from time to time, but generally (at least me) preffer to suggest and after the asker decides on what he preffers, come with an example if needed ;)
0

Featured Post

Free Tool: Port Scanner

Check which ports are open to the outside world. Helps make sure that your firewall rules are working as intended.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

  • 3
  • 3
  • 2
  • +2
Tackle projects and never again get stuck behind a technical roadblock.
Join Now