Jump to content
Ian Branch

Restart the same App?

Recommended Posts

Hi Team,

I want to give the User of an App the ability to click on a button or a menu selection that will cause the app to close and restart.

Can this be done?

Thoughts/suggestions appreciated.

Regards & TIA,

Ian

Share this post


Link to post

Easy

 

1. Create the new process passing in the pid of the previous process. 

2. Close the previous process. 

3. When the new process starts, have it wait until the previous process has terminated before it shows UI. 

 

Share this post


Link to post

Hi David,

"Easy"  Says he who knows how to do it. ;-)

I have no idea how to do what you described.

 

Ian

  • Haha 1

Share this post


Link to post

Which part don't you know how to do? 

 

On Windows at least it's a call to CreateProcess, then close the app, and then a wait function call.

 

Do you know how to close your app? If so all that is left is CreateProcess and a wait call. 

Share this post


Link to post
1 hour ago, Ian Branch said:

Hi David,

"Easy"  Says he who knows how to do it. 😉

I have no idea how to do what you described.

1. Google create process

2 Google process pid

3. Google close process

4. Google wait for process

 

David gave all necessary steps... you (or anyone) just need to go through that list one by one finding out how to do each particular thing if you already don't know how to do that.

Share this post


Link to post
22 minutes ago, limelect said:

@David Heffernan sorry that not an answer when a guy needs help coding.

Give him a piece of code, this is his need.

Knowing what needs to be done is quite different than having that particular piece of code at hand for sharing.

Share this post


Link to post
6 minutes ago, Sherlock said:

@Dalija Prasnikar I would always add Delphi to the search query. But other than that, Ian should now be good to go.

Indeed.

 

I just always like to read MS documentation first. So those would be my first steps. 

 

After I collect all necessary API calls, I would search Delphi RTL/VCL source to see if anything is already implemented somewhere... and yes, then Google again for specific Delphi implementation if needed (I can generally manage using Windows API calls without additional searching, but that knowledge depends on how often one has to interact with Windows API directly - Delphi does good job at hiding and wrapping those parts so many developers don't have the need to do low level interactions)

  • Like 1

Share this post


Link to post

Hi All,

Thank you all for your pointers and consideration.

I have now amassed the following routines which I think will get me there with some massaging.

ExecuteAndWait.

TerminateProcessByID.

GetPIDByProgramName.

ExecuteProcess.

 

I will let you know how I go, or not. ;-)

 

Regards & Tks again,

Ian

 

  • Like 1

Share this post


Link to post
Posted (edited)
13 hours ago, Ian Branch said:

I have now amassed the following routines which I think will get me there with some massaging.

ExecuteAndWait.

TerminateProcessByID.

You definitely don't want to use those kind of functions in this situation.

Quote

GetPIDByProgramName.

You don't need that.  A process knows its own ID, you can query it directly.

 

You need something like this:

uses
  ..., Windows;

function RestartApp: Boolean;
var
  si: STARTUPINFO;
  pi: PROCESS_INFORMATION;
  CmdLine: string;
begin
  CmdLine := Format('"%s" /pid:%d', [ParamStr(0), GetCurrentProcessID()]);
  ZeroMemory(@si, sizeof(si));
  GetStartupInfo(@si);
  Result := CreateProcess(nil, PChar(CmdLine), nil, nil, False, 0, nil, nil, si, pi);
  if Result then
  begin
    CloseHandle(pi.hThread);
    CloseHandle(pi.hProcess);
    Application.Terminate;
  end;
end;

Then in your app's startup code, such as in your main DPR file, you can do this before initializing your UI or anything else:

uses
  ..., Windows;

var
  pid: string;
  h: THandle;
begin
  if FindCmdLineSwitch('pid', pid) then
  begin
    h := OpenProcess(SYNCHRONIZE, FALSE, StrToInt(pid));
    if h <> 0 then
    begin
      WaitForSingleObject(h, INFINITE);
      CloseHandle(h);
    end;
  end;

  ... continue starting up normally ...
end;

 

Edited by Remy Lebeau
  • Like 4

Share this post


Link to post

Hi Remy,

Thanks for that.  I will give it a go.

While trying to get the other routines I found going, I discovered the need to change Winapi.Windows to just Windows in the Uses area.

I note your indication of just Windows in the uses area.

What's the difference?  It would seem they both can't be in the uses.

Regards & Tks again,

Ian

Share this post


Link to post

Tks Remy,

Works exactly as desired/needed.

I had to change..

  GetStartupInfo(@si);
  GetStartupInfo(si);

No biggie. :-)

Regards & Tks again.  And to the others for your interest/support.

Ian

Share this post


Link to post
On 7/19/2019 at 3:52 PM, Ian Branch said:

While trying to get the other routines I found going, I discovered the need to change Winapi.Windows to just Windows in the Uses area.

That would only be needed if you are using Delphi Xe or earlier.  In XE2 and later, you should use "Winapi.Windows".  Though, you can omit the "Winapi" prefix if you have "Winapi" included in your project's "Unit scope names" list.

On 7/19/2019 at 3:52 PM, Ian Branch said:

I note your indication of just Windows in the uses area.

Because I wasn't targeting any specific Delphi version.  The code I suggested should work in older versions, too.  Well, XE and later anyway, since 2010 and earlier did not have a version of FindCmdLineSwitch() that returns a string value, but that is not hard to write a manual replacement for if needed.

On 7/19/2019 at 3:52 PM, Ian Branch said:

What's the difference?  It would seem they both can't be in the uses.

"Windows" and "Winapi.Windows" are the same unit, so you can't "use" it twice in the same unit.  The latter simply has a "Unit scope name" applied to it in XE2 and later.

Share this post


Link to post
On 7/19/2019 at 4:22 PM, Ian Branch said:

I had to change..


  GetStartupInfo(@si);

  GetStartupInfo(si);

That is what I get for not checking Delphi's pascal declarations before posting code examples.

  • Haha 1

Share this post


Link to post

Hi Remy,

Thank you for the clarifications and the assist.  Much appreciated.

Regards,

Ian

Share this post


Link to post

If this works well would be nice if final code would be published in Github etc. I think many would need it and if there would be ready to run code, it would help community.

 

-Tee-

Share this post


Link to post

Hi Tee,

It does work well and I have integrated it into 3 Apps.

I haven't checked it for memory leaks or anything under the hood.  It does what I needed.

As for publishing to GitHub, I'm afraid that's not in my knowledge base. :-(

Regards,

Ian

  • Like 1

Share this post


Link to post
Posted (edited)
18 minutes ago, Lars Fosdal said:

Awaiting ridicule for the use of ShellExecute 

Why do you think ShellExecute throws exceptions? It doesn't. If you want proper error handling use ShellExecuteEx, and check the return value. Even ShellExecuteEx would be wrong here though. Use CreateProcess.

 

As for services, why use cmd.exe to run net.exe? Why not run it directly? Of course, the API is the right way to start a service. 

Edited by David Heffernan

Share this post


Link to post

@David Heffernan

I found restarting a service from itself to be troublesome since you still are in the context of the running service.  Issuing a net start just at the time that you exit the service works well.

 

The exception handling is probably a cargo cult remnant.

 

I'd rewrite for CreateProcess for the application, but it works, so I won't fix it.

Share this post


Link to post
Posted (edited)
4 minutes ago, Lars Fosdal said:

but it works, so I won't fix it.

Except that it doesn't work. Doesn't handle errors at all. 

 

Also, it's fine that you don't want to disturb your code. But if you are going to offer up code as an example, it should be exemplary. 

Edited by David Heffernan

Share this post


Link to post

Academically you may be correct, but empirically it simply works.

I have several hundred installations that have worked for many years and keep on working, and zero complaints that it ever failed.


The apps/services check the file version against a repository and if it finds a change,

- deletes any *.old.* copies that it finds

- copies the new file to a .new temp file,

- renames itself to *.old.<handle>

- renames the new temp file to the ordinary name

- Calls Restart...OnExit and exits.

Voila: App / Service is running the new version.

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×