Solved

Params To Already Running Program?

Posted on 2002-04-08
49
271 Views
Last Modified: 2010-08-05
Hi Experts.
I have a program that only are allowed to have one instace of it self running. It reads some params (if they are there), problem is, how can i post params to main instace - if program are executed again??

Cheers Dennis
0
Comment
Question by:Dennis9
  • 16
  • 11
  • 6
  • +7
49 Comments
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925107
you could send a user defined message to your app like this:


DPR:

program Project1;

uses
  Forms,
  windows,
  Messages,
  Unit1 in 'Unit1.pas' {MyApp};

{$R *.RES}

begin
   if FindWindow('TMyApp',nil)>0 then
   begin
     SendMessage(FindWindow('TMyApp',nil),WM_USER+2,Longint(Pchar('Hello')),0);
     exit;
   end;

  Application.Initialize;
  Application.CreateForm(TMyApp, MyApp);
  Application.Run;
end.


PAS:
type
  TMyApp = class(TForm)
  private
    { Private declarations }
  public
    { Public declarations }
    Procedure ProcessMyMessage(var msg:Tmessage);Message WM_USER+2;
  end;

var
  MyApp: TMyApp;

implementation

{$R *.DFM}

{ TMyApp }

procedure TMyApp.ProcessMyMessage(var msg: Tmessage);
begin
   showmessage(Pchar(Msg.WParam));
end;

end.



Good luck!!


0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 6925123
Normally the second instance of your program closes itself when detecting the first instance.
Before that it often signals the first instance to do something like showing the main window.
You can either send your info there with WM_COPYDATA or use the signal to quit the first instance instead of the second one.
0
 
LVL 4

Expert Comment

by:jsweby
ID: 6925178
The proper way to do this is to use a Mutex, see the Windows API help for that. I'd explain it all but it's quite long winded and very similar to DrDelphi's suggestion. Maybe for a few more points...  ;)
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925200
You COULD use a mutex, true... but you'll still need the HWND of the running app to send a message.This is why I opted for FindWindow instead.


Good luck!!



0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925515
DrDelphi -> Your code work like i want, only problems is that it can't pass big string values like i need to :<
Is it possible to make it support bigger strings? Maybe be making a new Type where i also specify length of string. Also note that the string i send are pretty long and are encrypted!

Thanks for the interrest :)
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925640
The fact that the strings are long and encrypted shouldn't make any difference. You are only passing a pointer (PCHAR=pointer to array of Char). So you sould be able to pass any string you like. You *could* take a look at using a memory allocation for the string, such as GetMem or StrAlloc, but they shouldn't really be all that necessary. Try this for giggles:

Place a button on the form with the message handler. This is the underlying button click code (note that "overview.rtf" is 29k in size)

begin
     List:=TstringList.create;
     List.LoadFromFile('Z:\overview.rtf');
     SendMessage(Self.handle,WM_USER+2,Longint(Pchar(list.Text)),0);
     List.free;
end;


Works a charm for me.


Good luck!!



0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925718
Try send paramstr(0). It show strange things when i resive it, it should display my exe fullpath.

SendMessage(Self.handle,WM_USER+2,Longint(Pchar(paramstr(0))),0);
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925736
Hmmm... your code works perfectly here. You might want to try using Application.Exename instead of the ParamStr(0), although it really should not make any difference.


 SendMessage(Self.handle,WM_USER+2,Longint(Pchar(Application.exename)),0);
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925770
hmm it was just a example, i use paramstr(1) ofcause.
But i don't understand why it will not work here.
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925789
Are you actually passing any parameters to the EXE? ParamStr(0) will work even in the absence of any commandline paramaters... but after that you'll need to actually have parameters to read them.



0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925794
Try error Checking your code by reading the value of ParamCount:


 if ParamCount>=1 then  SendMessage(Self.handle,WM_USER+2,Longint(Pchar(Paramstr(1))),0);
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925809
I pass params yes. Et even show strange things using paramstr(0) as i said. But i will go check again to besure there not is any errors...
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925822
Strange thing is if i send a normal defined string like:
SendMessage(FindWindow('Tmyform', nil), WM_USER + 2, Longint(Pchar('Hello Crap World')), 0);

Then i resive it. But if i use paramstr(0) it just shows the words "IM<<"
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925835
That doesn't seem to make any sense. Have you tried assigning ParamStr(0) to a variable and then passing that variable?


Var StrToPass:string;

begin
  StrToPass:=Paramstr(0);
  SendMessage(FindWindow('Tmyform', nil), WM_USER + 2, Longint(Pchar(StrToPass)), 0);

end;
 


0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925867
yep i also tried that. I can't understand this....
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925871
if i try
showmessage(paramstr(0));

It also shows the right path
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925889
What does the message handler code look like?


0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6925895
Post your code for me to see.

0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925900
 if FindWindow('TSCPMainform', nil) <> 0 then
  begin
    if ParamCount > 0 then
    begin
      PostMessage(FindWindow('TSCPMainform', nil), WM_USER + 2, Longint(Pchar(paramstr(0))), 0);
      exit;
    end;
    exit;
  end;


//On resive
procedure TSCPMainform.ProcessScpMessage(var msg: Tmessage);
begin
  showmessage(Pchar(Msg.WParam));
end;
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6925992
u have any ideas?
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6926012
I wonder if the problem lies in the fact that you are using PostMessage rather than SendMessage. From the Win32API help file:

The PostMessage function places (posts) a message in the message queue associated with the thread that created the specified window and then returns WITHOUT waiting for the thread to process the message.



The SendMessage function sends the specified message to a window or windows. The function calls the window procedure for the specified window and DOES NOT RETURN UNTIL the window procedure has processed the message.



Give it a shot and let me know, okay?


0
 
LVL 1

Author Comment

by:Dennis9
ID: 6926029
i tried both, still the same message is resived
0
 
LVL 1

Expert Comment

by:slavikn
ID: 6926210
Use this component. It always works.

http://www.delphipages.com/result.cfm?SR=abfsoft&AO=and&RequestTimeout=500

From the left-hand menu choose your Delphi version. Install it and use the "abfOneInstance" component. It is very easy to use.

P.S. This package includes many cool components!
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6926251
I don't need a component to do so my app only can have one Instance. I asked how to pass params, and for some reason this don't work well, and nonone has any ideas why.
0
How to run any project with ease

Manage projects of all sizes how you want. Great for personal to-do lists, project milestones, team priorities and launch plans.
- Combine task lists, docs, spreadsheets, and chat in one
- View and edit from mobile/offline
- Cut down on emails

 
LVL 3

Accepted Solution

by:
SteveWaite earned 50 total points
ID: 6926536
This is what you do:

Sending app:

var
 MessageString: String;
 MessageAtom: TAtom;
 Buffer: array[0..255] of Char;

//in create
WmMyStringData := RegisterWindowMessage(‘MyStringData’);
//..

//send message
MessageString := YourParamString;
MessageAtom := GlobalAddAtom(StrPcopy(Buffer, MessageString));
SendMessage(HWnd_Broadcast, WmMyStringData, 0, MessageAtom);
GlobalDeleteAtom(MessageAtom);


Receiving app:

//in create
WmMyStringData := RegisterWindowMessage(‘MyStringData’);

//TForm1
public
   { Public declarations }
procedure DefaultHandler(var Message); override;

procedure TForm1.DefaultHandler(var Message);
var
 Buffer: array [0..255] of Char;
 MessageString: String;
begin
 inherited DefaultHandler(Message);
 with TMessage(Message) do
 begin
   if  Msg = WmMyStringData then
     if not (GlobalGetAtomName(lParam, Buffer, 255) = 0)
       then MessageString := StrPas(Buffer);
   // if ..
 end;
end;


0
 
LVL 3

Expert Comment

by:SteveWaite
ID: 6926541
do the following in application unit before        

Application.Initialize;

to determine if second instance:

  CreateMutex(nil, False, ProgName);
  if (GetLastError = ERROR_ALREADY_EXISTS) then Issecondinstance = true:
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 6927186
Sending a pointer to data is only allowed for WM_COPYDATA.
The two processes do not share an address space so the pointer is meaningless in the receiving app. WM_COPYDATA is specially handled by Windows and the data sent with it are copied to the address space of the receiving app.
SendMessage is also required for sending with WM_COPYDATA.
0
 
LVL 10

Expert Comment

by:Jacco
ID: 6927362
DrDelphi's method should work. (Nice method!)

You should use SendMessage (like he says) other wise Delphi destroys the string.

What happens if you first put the string in a member field of the object (then verify if it still there) and then sendmessage it away. This way you make sure Delphi doesn't get rid of the string.

Maybe you can try {$O-} {$O+} around the sending code to disable Delphi's optimization?

Regards Jacco
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 6927430
No, sending a PChar to another program CANNOT work except with WM_COPYDATA.
0
 
LVL 6

Expert Comment

by:DrDelphi
ID: 6927788
robert_marquardt,
 I really hate to disagree with you, particularly after the fine help you gave me with USB stuff, but the fact of the matter is that as long as the Sending application retians a pointer to the String being sent, my method DOES work. I will not ever post code that I have not personally tested. The PChar is a pointer to an address in memory. It's for that reason you'd use PChar in DLL's versus String, for example. WM_COPYDATA works as well, there's no denying that.... but the code that I posted has been tested and DOES work too. Please be careful when making blanket statements about the validity of my code in the future. Thanks!


DrDelphi
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 6927844
I wonder why this code could work. The PChar pointer you are sending is relative to the virtual address space of the sending application.
In the receiving application the same address value points to another memory location.
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6927965
DrDelphi -> YOur code work fine, if i enter the string in the program code. But when it needs to load it from eg paramstr(0) then it shows strange things.

SteveWaite -> Not testet your code yet
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 6928019
It could be that a string constant 'blabla' is stored in the code segment which the program instances share. so the address is the same in both virtual address spaces.
0
 
LVL 3

Expert Comment

by:SteveWaite
ID: 6928192
my code works fine!
;-)
0
 
LVL 11

Expert Comment

by:robert_marquardt
ID: 6928291
Yes, because you move the string to globally shared memory.
0
 

Expert Comment

by:bloodbird
ID: 6930079
listen
0
 
LVL 33

Expert Comment

by:Slick812
ID: 6930360
If you use the SendMessage without the WM_COPYDATA, sometimes it works and sometimes it don't. Try running more appications (wordpad, Excel, Delphi, ect) and the times that it will work goes down. Using WM_COPYDATA will almost always work except if there is NO memory to use. Two different processes (programs) have their own memory space. This means that a pointer that is works in process A may NOT be valid in process B, because it points to some memory in process A. The with the WM_COPYDATA method, the  message uses memory mapped files created internally by the OS to transport the data to the receiving process. You could create you own memory mapped files for the string.

 Another way is to write as much data (Strings, integers) to a special place in the registry and send a user defined message to the other program with a lParam and wParam telling it which new values were written to the registry. And then read those from the registry.
0
 
LVL 1

Expert Comment

by:slavikn
ID: 6931022
PostMessage always works. Try to use it.
0
 
LVL 5

Expert Comment

by:Gwena
ID: 6932585
Hi Dennis9 :-)

So is this what you want to do?


Start your program and send it a param..like so
MyProgram.exe ThisIsTheParam

your exe now has the param string 'ThisIsTheParam'
and somehow you want that paramstring to be available
to your program the next time it runs... and to do
so without ever having to send it the param again?

So from now on whenever you run MyProgram.exe it just remembers the param that was once sent to it?


OR... are you wanting your program which can only have a single instance at one time running to be able to tell a second instance of the program about the value of the param
in that split second before it shuts down because it has sensed that an instance is already running ?


I think both of these can be done easily but I'm just not sure what you are looking for... please tell me if one of these is what you want to do... if it is maybe I can email you a demo... it would be a very long explanation to post here.. a demo would be better I think :-)
0
 
LVL 5

Expert Comment

by:Gwena
ID: 6932697
Hi :-)

I did a kwik demo for a program that remembers any param used to start it.... the next time you run it it will display the param used before... no need to enter it again.
If you start it using a new param it will replace any old param.
If you start it with a param and then run a second instance then the second instance will know the param that you started the first one with...and will display it :-)


doing this
project1 hello

will run the demo and display 'hello'

running a second instance now will display 'hello'

shutting the program down and starting it again with no param will still display 'hello'

running it with a new param will replace the 'hello' with the new one.

No ini files are used and no registry changes are made... no new files are written to disk

If this looks useful let me know and I will add comments to the source and post it here.

the file is here http://www.geocities.com/gacarpenter386/paramdemo.zip

you may have to right click and choose save as to download the file... :-)
0
 
LVL 5

Expert Comment

by:Gwena
ID: 6932714
I added the comments... here is the source that does this..I hope it formats correctly :-)




uses ExeMod;

procedure TForm1.FormCreate(Sender: TObject);
var Temp: string;
begin
ExtractFromExe('MyParam',Temp);  //see if a param already exists
if Temp <> '' then Label1.Caption := Temp;  //if it does then display it
If Paramstr(1) = 'deltemp' then exit; //just trust me..u need this line
If Paramstr(1) <> '' then  // has a new param been used?
begin
  Temp := Paramstr(1);  //store the new param into temp
  ReadExe;   // get the exe file from disk
  DelFromString('MyParam',Exe);  //remove any old param from it
  Add2String('MyParam',Temp,Exe); //stuff the new param in the exe
  AlterExe; // go ahead now and change the exe... u r done :-)
end;
end;

0
 
LVL 1

Author Comment

by:Dennis9
ID: 6933805
Hi Gwena :)
It is like your "Or.." example. Where i only want one instace of the program running all the time - but if a second instace starts, i need to send the params from the new runned program, to the one that already was running, And then just shut the new runned program down.

Program Start and check if there already is a copy running - If so, it will pass on the params to the running program and terminate it self again.

Hope u can understand.

Anyway thanks for the exe saving code :)

Dennis
0
 
LVL 1

Expert Comment

by:slavikn
ID: 6934600
Where can I find the "ExeMod" library?
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6934611
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6934681
0
 
LVL 5

Expert Comment

by:Gwena
ID: 6934790
Hi dennis9 :-)

OK I see what you need now... I thought that you needed the new 2nd instance of the exe to somehow be instantly aware of the parameter that had been sent to the first instance when it started.... so I got it exactly backwards...sorry about that :-)


hmmmm.... this is complicated

I think that a 2nd instance of an exe does not place a new image of itself in memory but works with the image that is already in ram... I know how to directly read that ram using hinstance ... but I have never had a reason to write to it... that is mostly just useful to hackers breaking copy protections... I bet madshi knows how to do it :-)
if I can figure out how to write to this ram I will add a routine to ExeMod and then this exe to exe communication will be easy... maybe I will post a Q for madshi...

I am beginning to suspect that postmessage or similar may be the easy way to do this...  I will keep thinking about this and if I come up with a crazy idea that might work I will post it :-)

..Gwen..(thinking hard)
0
 
LVL 3

Expert Comment

by:SteveWaite
ID: 6935617
I cannot understand what the fuss is about.
Check out my example, experts.
0
 
LVL 1

Author Comment

by:Dennis9
ID: 6936562
SteveWaite -> I got your code added and it work for me :)
Thanks everyone very much.

Dennis
0
 
LVL 3

Expert Comment

by:SteveWaite
ID: 6937920
Thanks Dennis.
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

This article explains how to create forms/units independent of other forms/units object names in a delphi project. Have you ever created a form for user input in a Delphi project and then had the need to have that same form in a other Delphi proj…
Introduction Raise your hands if you were as upset with FireMonkey as I was when I discovered that there was no TListview.  I use TListView in almost all of my applications I've written, and I was not going to compromise by resorting to TStringGrid…
In this seventh video of the Xpdf series, we discuss and demonstrate the PDFfonts utility, which lists all the fonts used in a PDF file. It does this via a command line interface, making it suitable for use in programs, scripts, batch files — any pl…
Illustrator's Shape Builder tool will let you combine shapes visually and interactively. This video shows the Mac version, but the tool works the same way in Windows. To follow along with this video, you can draw your own shapes or download the file…

708 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

11 Experts available now in Live!

Get 1:1 Help Now