Link to home
Start Free TrialLog in
Avatar of Stuart_Johnson
Stuart_Johnson

asked on

Data Exchange Between Two Apps

Hi All,

I need to be able to pass data from one program to another running on the same system.  I dont want to use drop files or UDP.  I dont mind using the registry, but the host application needs to be notifed when the registry is updated.

Is there any way I can do this?  DDE seems like a viable option, but Im stuffed if I can work out how to use it (manaul?  Not worth the paper its printed on!).

I know there is a message which you can post to the system when you update the registry, but I cant remember where I saw it.  THis could be another option.

Any help would be REALLY appreciated!!!

Stuart.
Avatar of Stuart_Johnson
Stuart_Johnson

ASKER

Oh!  I thought I better add this too - the data is just an integer - nothing more.  Its just an identifier.
Hi Stu

Do you have the window handle of the second app?  If you do you can send a Windows message to the 2nd app.

E.g.
const  WM_MY_MSG =  WM_USER + 1234;

PostMessage(OtherAppHandle, WM_MY_MSG, YourInt, 0);

Cheers,
JB
Hi JB,

Thanks for the quick response.  No,  I dont have its handle.  Is there an easy way to retrieve the handle?

How do I get the posted message from within the host app?  Just watch for WM_MY_MSG???

Thanks again,

Stuart.
Hi all,
I want to extend what Jim suggested as follows

BroadcastSystemMessage(BSF_POSTMESSAGE, BSM_APPLICATIONS, WM_MY_MSG, YourInt1, YourInt2);

regards, igor
In your delphi help just search for OnMessage event of the TApplication and look its example, it is just you seek...
igor
Yes, it seems Inter has already provided my response.

TApplication's OnMessage has the following parameters:
    var Msg: TMsg; var Handled: Boolean

You would use Msg to see if Msg.Message = WM_MY_MSG then get the integer from Msg.WParam.

JB
Hi Inter,

I dont know if Im thick or not, but I just copied the BroadCastSystemMessage code into my app to see what I could come up with, and I cant compile it.  Its saying "Incompatible Types: Integer and PDWORD".  I have copied what you said exactly, and the help file says that BSM_APPLICATIONS is the recipients' parameter type.

Any ideas???

Im gunna be a pain in the butt and ask for some code here :)  I've never worked with messages, except for limiting the size of windows.  This is all totally new territory.

Stu.
I don't like BroadcastSystemMessage.   :-(
There's another possibility. The program that is to receive the message could write his main window handle to registry at initialization and delete it again at finalization. So the program that has to send the message could just read the handle from the registry and then address the message directly to the receiving window.

There are several disadvantages of the broadcast method:
(1) Broadcasting messages is a little bit slower.
(2) Broadcasting is a little bit dangerous because there could be another application that uses exactly this message number and make problems receiving your message.
(3) With broadcasting you can't wait for response (that means you can't use something like SendMessage).
(4) And because of that you can't use the message WM_COPYDATA which is quite useful if you need to transport more than just an integer.

Hope this help... Madshi.
Anyway I try to explain,
var
  Temp : DWord;
begin
  Temp := BSM_APPLICATIONS;
  //now
  BroadcastSystemMessage(BSM_POST, @Temp, etc.....)
end;
I hope this solves...
Inter.

I managed that bit just after I posted the message - sorry :)

So, how do I recieve it at the other end??? Do I use something like:

private
   Procedure WMDataSent(var Msg: TMessage); message WM_MY_MSG;

If so, what do I test for in the procedure??

Sorry to sound so dumb, but as I said, this is all new ground to me.

Stu
Just do the following(modified from delphi help). Add AppMessage to your public part as
  TForm1 = class(TForm)
  ....
  public:
   procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
  end;

then in implementation

procedure TForm1.FormCreate(Sender: TObject);

begin
  Application.OnMessage := AppMessage;
end;

procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
  if Msg.message = WM_MY_MSG then
  begin
    //your ints are in Msg.wparam and Msg.lparam
   // do what you want here
   
    // we handle only this message so others are processed normally
    Handled := True;
  end;
  { for all other messages, Handled remains False }
  { so that other message handlers can respond }
end;
igor
Me again :)

Ok..  I have that in the code.  Still no luck. Heres what Im doing (cut down form)..
  public
     procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
    { Public declarations }
  end;

var
  Form1: TForm1;

Const
   WM_MY_MSG = WM_USER+1234;


implementation

{$R *.DFM}

procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
   if Msg.message = WM_MY_MSG then begin
      Handled := True;
      ShowMessage(IntToStr(Msg.wParam));
      ShowMessage(IntToStr(Msg.lParam));
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var P: DWord;
    Int1,
    Int2: Integer;

begin
   P := BSM_APPLICATIONS;
   Int1 := 0;
   Int2 := 1;
   BroadcastSystemMessage(BSF_POSTMESSAGE, @P, WM_MY_MSG, Int1, Int2);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnMessage := AppMessage;
end;

What have I dont wrong???

Thanks,

Stu.
Me again :)

Ok..  I have that in the code.  Still no luck. Heres what Im doing (cut down form)..
  public
     procedure AppMessage(var Msg: TMsg; var Handled: Boolean);
    { Public declarations }
  end;

var
  Form1: TForm1;

Const
   WM_MY_MSG = WM_USER+1234;


implementation

{$R *.DFM}

procedure TForm1.AppMessage(var Msg: TMsg; var Handled: Boolean);
begin
   if Msg.message = WM_MY_MSG then begin
      Handled := True;
      ShowMessage(IntToStr(Msg.wParam));
      ShowMessage(IntToStr(Msg.lParam));
  end;
end;

procedure TForm1.Button1Click(Sender: TObject);
var P: DWord;
    Int1,
    Int2: Integer;

begin
   P := BSM_APPLICATIONS;
   Int1 := 0;
   Int2 := 1;
   BroadcastSystemMessage(BSF_POSTMESSAGE, @P, WM_MY_MSG, Int1, Int2);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  Application.OnMessage := AppMessage;
end;

What have I dont wrong???

Thanks,

Stu.
Oops, you can't broadcast msgs > WM_USER.

Try setting WM_MY_MSG to WM_USER - 1 and it should work.  Of course this is dangerous...

It might be an idea to use the FindWindow API to find the 2nd app and use PostMessage to send a msg directly to that app.
E.g.
hApp := FindWindow(PChar('TForm1'), PChar('App Caption'));
if (hApp > 0) then
  PostMessage(hApp, WM_MY_MSG, MyInt1, MyInt2);

Cheers,
JB

Fabulous!!  Worked that time.

Hay, any reason why it processes the message twice?  I noticed when I click the button, it seems to send the message twice, or not clear the message queue.  Is this correct??

Regardless, it works like a charm!!  Thanks fellas..

Who wants the points?  Or do you want to do a split 50/50?

Stu.
Yes, Jim is right
(if you have a difficulty in FindWindow, give your form a weird class name and just call
FindWindow('TMyWeirdForm',nil); )
Inter,

That worked beautifully.  Thanks mate!!

Stu.
This is JimBob's turn, I have already interrupt several of his solutios by mistake ;-)
igor
Wow!   Generosity!  Well done.  Im happy to give you half each if you like.

Stu.
Hi Stu

If you change the app msg thing to the following:  (I.e. get rid of Application.OnMessage.)

In form
  TForm1 = class(TForm)
  public
    procedure WndProc(var Msg: TMessage); override;
  end;

procedure TForm1.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_MY_MSG:
      ShowMessage('WndProc: ' + IntToStr(Msg.wParam));
  end;
  inherited;
end;

Then you can use "PostMessage(HWND_BROADCAST, WM_MY_MSG, Int1, Int2);" instead of BroadcastSystemMessage and the 2nd app will only get the msg once.

JB

Inter - thanks, but I don't hold any grudges from the other "mistakes".  As I said - these things happen.

Stu - when you're happy I'll answer the Q.

JB

Mate, Im stoked!!!  Post an answer whenever your ready!

Thanks guys! You have made this job bloody easy!!  I really appreciate how quick you responded.

Stu

thanks, nice to work with all of you :-)
ASKER CERTIFIED SOLUTION
Avatar of JimBob091197
JimBob091197

Link to home
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
Start Free Trial
Well, here by the grade then! :)

Thanks again guys!  Very very happy!

Stu
:-)
As always, a pleasure!

Dave
Dave,

A question regarding this.  For some reason, if this application has been sent to the tray, it is ignoring all my messages.  It works fine when its in a normal state.

Any suggestions at all?

Stuart.
Which method are you using?  BroadcastSystemMessage or PostMessage(HWND_BROADCAST...)?

I haven't tried with a tray app, but I'll have a look now for you.

Dave
Hi Dave,

Im using PostMessage(hApp, WM_MY_MSG, Int1, Int2).  Its working fine if it the app is not sent to the tray.


Stu.
Hi Stu

This is odd.  I have written a small test app (well 2 actually, a tray app & a caller app).
And it seems to work, even when the tray app is "in the tray".

My caller app's "Send Msg to Tray App" button click looks like this: (My tray app's main form is "TfrmTest".)

var
  hTrayApp: THandle;
begin
  hTrayApp := FindWindow('TfrmTest', 'Test');
  if (hTrayApp > 0) then
    SendMessage(hTrayApp, WM_USER + 1234, 1, 2);
end;

My tray app's WndProc looks like this:

procedure TfrmTest.WndProc(var Msg: TMessage);
begin
  case Msg.Msg of
    WM_USER + 1234:
      ShowMessage(IntToStr(Msg.WParam) + ', ' + IntToStr(Msg.LParam));
  end;
  inherited;
end;

The following works even after I have run the following code to send the tray app to the aforementioned tray:

procedure TfrmTest.Button1Click(Sender: TObject);
begin
  // Show tray icon.
  // "trayTest" is my TTray component on the main form.
  trayTest.Enabled := True;

  // Hide main app window.
  ShowWindow(Application.Handle, SW_HIDE);

  // Hide the tray app's main form.
  Self.Hide;
end;

After all this the tray app still responds to the caller app's PostMessage.
Dave