Solved

Automatic Update

Posted on 2000-03-10
21
216 Views
Last Modified: 2010-04-04
We have an application that needs autoupdate.  What needs to happen is that the application checks to see if its EXE is an old version.  If it is, it needs to start a second application WHICH WILL WAIT FOR THE **FIRST** APPLICATION TO CLOSE before it can copy the new EXE over the old one.  The SECOND application then restarts the FIRST application (which is now updated).

BTW solution must work for all 32-bit versions of Windows (95, 98, NT.....)
0
Comment
Question by:millerw
  • 9
  • 3
  • 3
  • +5
21 Comments
 
LVL 3

Expert Comment

by:ckaneta
ID: 2605241
I believe the newest version of Wise Installmaster has an autoupdate feature.

0
 
LVL 1

Author Comment

by:millerw
ID: 2605476
Not looking for an external product.
0
 

Expert Comment

by:tresto
ID: 2605568
Use this function in both your applications. The "YourAppName" string has to be the same in both apps (it can be any unique string).
The first application does not care what the result is, it just registers itself.
The second application does not start copying if the function returns true.

function AppStarted(YourAppName: string): boolean;
begin
CreateFileMapping($ffffffff,nil,PAGE_READONLY,0,32,pchar(YourAppName));
result:=GetLastError=183;
end;
0
 
LVL 1

Author Comment

by:millerw
ID: 2605691
PLEASE ONLY POST COMMENTS.

A solution that doesn't require polling is preferred.  EG a notificiation that the process has closed or that the EXE file can now be written to.  Polling is a waste of processor time.
0
 
LVL 3

Expert Comment

by:ckaneta
ID: 2605772
I think the answer lies in an ENUM_WINDOWS thing

use shellexecute or some like API call to start the 2nd program and then exit the first one

use EnumWindowsCallBack on a timer to determine when the first application ends, and then copy over the file and run it again.

here is a simple example of how to implement an EnumWindowsCallBack function:

function EnumWindowsCallBack(HWND: hwnd;LPARAM : lParam):boolean stdcall;
var capt:array[0..79] of char;
    ClassName:array[0..79] of char;
    i,j:integer;
 begin
  result:=true;
  GetWindowText(HWND, capt, 79); GetClassName(HWND,ClassName,79);
  if length(capt) > 0 then begin
   for i:=0 to prgfrm.fil.Items.Count-1 do begin
    j:=pos(uppercase(prgfrm.fil.items[i]),uppercase(classname));
    if j=0 then j:=pos(uppercase(prgfrm.fil.items[i]),uppercase(capt));
    if j<>0  then begin prgfrm.rnapp.items.add(Capt); end;
   end; //for
  end;//if capt not ''}
 end;

if you think this will work but want a code example more to what you had in mind, you'll have to wait til I get home in a couple of hours
0
 
LVL 1

Author Comment

by:millerw
ID: 2605871
EnumWindows is a sort of waste of time due to the fact that I have the HWND already considering I'm calling the second app from the first.  

Bear with me a second as I explain what I **think** is and should be able to be done.

The first app should be able to **TELL** the second any information it needs in order to "track" because its job is going to be to track **THE** first app.

So,
1.  First app realizes it is of old version.
2.  First app starts second app.
3.  First app informs second app of all information it needs to detect when first app is closed.
4.  Second app starts waiting for close of first app.
5.  First app then Terminates.

You should be able to get the rest.

I'm looking at the OpenProcess command, however SYNCHRONIZE *appears* to be only available on NT.  WaitForSingleObject with the process handle of the first app will wait infinitely for the first app to close before continuing execution in the second app.

Now, that is the theory of what I'm thinking.  Rip it apart if you please and tell me why it is not possible if it is not.  Otherwise, anyone want to venture to write the code to actually perform it?
0
 
LVL 1

Expert Comment

by:nrico
ID: 2606014
How about you reverse the order you run the programs in.

First you run program 2 (which would then become one, but we'll forget that for a moment :-), which version-checks program 1. Use the date/time stamp or a datafile or whatever for this.
Program 2 then informs the user that program 1 is out of date, removes and updates it and _THEN_ executes it normally.
If the program needs not be updated, the user would notice nothing from this and by used a predefined parameter you can prevent the user from running program 1 manually. E.g. program 2 runs "program1.exe /runnow" or something like that.

Rico
0
 
LVL 1

Author Comment

by:millerw
ID: 2606043
Ah, but actually (think if it as originally planned) first app updates the second app before calling the second app to update the first app :-)
0
 
LVL 7

Expert Comment

by:ahalya
ID: 2606520
If i understand your problem correctly, then you want a way for the second app to decide whether first app has terminated. (You're ok with all other stuff; Inter-App message communication, ShellExec etc).


I guess this can be done by using the ExitCode, as follows:

//in your second app.
repeat
  GetExitCodeProcess(ProcessHandle,ExitCode)
  Application.ProcessMessages; \\or sleep(for a while);
until (ExitCode <> STILL_ACTIVE);

//now go ahead and delete the first app, copy the upgrade
// and start it again.

where ProcessHandle is the handle of your First app.  Either your second app can find it, or your first app can tell it to your second app.  

If you don't like polling (above wouldn't consume too much of the resources, especially if you use a larger sleep interval), then you'll have to resort to ShellHooks I would guess; But that would be an overkill.
0
 
LVL 1

Author Comment

by:millerw
ID: 2606988
Well, the unfortunate thing is that I don't like polling.  But that doesn't mean I won't be forced to use it if all else fails......
0
Find Ransomware Secrets With All-Source Analysis

Ransomware has become a major concern for organizations; its prevalence has grown due to past successes achieved by threat actors. While each ransomware variant is different, we’ve seen some common tactics and trends used among the authors of the malware.

 
LVL 7

Expert Comment

by:ahalya
ID: 2607258
Well, I understand your concerns about wasting resources. But the following code:

repeat
     GetExitCodeProcess(ProcessHandle,ExitCode)
     Application.ProcessMessages;
    SLEEP(1000);
until (ExitCode <> STILL_ACTIVE);

will poll the system only once a second, and your first app will terminate within a few seconds.  So the consumed system resources will be pretty low.

On the other hand, you'll consume a lot more of the resources, if you go for hooking the system messages (via a SHELLHOOK).  I'd think that the above loop would be a lot better with respect to saving resources.
0
 

Expert Comment

by:perkley
ID: 2607373
Hey, I think you should take a look at this.  I used this component in one of my apps, and it works so well.  I tried several other more expensive AutoUpgrader options and I had so many problems.

TAutoUpgrader by UtilMind.  It works so easily on the client and the Internet side.  It is also small, allowing you not to have a bulky file.

http://www.utilmind.com/delphishareware.html#AutoUpgrader

You can track progress of the file, let the user know if no update is available, etc.
0
 

Expert Comment

by:JimboKern69
ID: 2608045
My vote is with nrico...

Write a shell application that is actually the app that your user launches.  This application will get the version information of your "real" application and compare to the "current" version.  If they are the same, it simply launches the "real" app and kills itself.  Should add only a negligable(sp?) amount of time to the startup sequence.

If they are different, it will save the current version of your "real" app as a backup, then download the new version.  If necessary, it would then launch the patch program, new install program, or perhaps just run the new executable.

You can write the "shell app" so that it can take "directions" from the web - in other words, it should be able to handle a multitude of installation mechanisms, taking it's orders from some sort of a mini-script file.

Jim Kern
0
 
LVL 1

Author Comment

by:millerw
ID: 2612831
ahalya:  Agreed, except for the fact that a WaitForSingleObject call does an even better job since it is instantaneous upon process close and enters an efficient wait cycle.  But, your in top running so far if my current code doesn't work.  Stick around, I just might give you guys some free code.......if I'm in a good mood--hope that tomorrow I am ;-)

perkley:  One should never assume.  Who said I'm updating from the Web? :-)

JimboKern69:  How does the shell app then get updated if a change is made to it?  Sorry, but having a tech go around to x computers to update the system is a pain in the butt.  That and telling x busy users to run an updater is bound to get overlooked by someone.  Sorry, but that is the exact reason I didn't like nrico's solution.  Running a cost analysis of this situation results in comparing my time to fix the problem now and the time needed to update, let us say, 1000 user machines in the future.  Hands down, my time costs less.

Anyone here know anything about DuplicateHandle?
0
 
LVL 7

Expert Comment

by:ahalya
ID: 2612926
Millerw,

WaitForSingleObject is great. But, it requires a timeout value, if i remember right.  (Since its not easy to know the time an action requires in advance, I end up with specifying a "LARGE" timeout vale; say a minute).  Then the problem is that my code loses "resposiveness" & the user may feel as if the program has "locked up" -its only for the duration of the timeout, but still it ain't pretty. Is it ?

If I check the GetExitCode function, i can process all other messages, (application.processmessages) and the user interface would be pretty responsive.

I have never used DuplicateHandle. But have seen it at
http://msdn.microsoft.com/library/psdk/winbase/handobj_2pyd.htm
0
 
LVL 1

Author Comment

by:millerw
ID: 2613158
WaitForSingleObject has the ability to wait for infinity.  Just use the constant INFINITE as the timeout value.

As for responsiveness, I'm not too worried due to the fact that the actual wait time is less than a second...the time it takes for app1 to finish--which will be an actual code call, not user choice.  However, if it ends up being a concern, a simple thread will fix the issue...the thread waits while the app is responsive (however, app2 should not allow the user to just close it if app1 is still running).  As for being pretty, why don't you look below and see.

I have found that the SDK is incorrect.  The SYNCHRONIZE works on every machine I've tested so far.  Each and every machine has at least IE4 installed and it appears to contain the update so that the command works.  95 and 98 both work.  Thus, I don't need DuplicateHandle.

NO POINTS WILL BE AWARDED TO ANYONE SINCE I HAVE SOLVED MY OWN PROBLEM.  I WOULD LIKE YOUR OPINION ON THE CODE AND ANY IMPROVEMENTS SO STICK AROUND AND LET'S DISCUSS IT.  BELOW IS THE CODE I'M USING:

Both applications have a message called SX_PROCESSINFO defined.  The message is used to transmit the actual ProcessID of the first app over to the second.

Here is the code for app1:

function EnumerateTW(AWnd: HWND; PID: LongWord): Boolean; StdCall;
begin
  PostMessage(AWnd, SX_PROCESSINFO, PID, 0);
  Result := True;
end;

procedure TForm1.FormShow(Sender: TObject);
var
  zAppName: String;
  StartupInfo: TStartupInfo;
  ProcessInfo: TProcessInformation;
begin
  FillChar(StartupInfo, Sizeof(StartupInfo), #0);
  StartupInfo.cb := Sizeof(StartupInfo);
  StartupInfo.dwFlags := STARTF_USESHOWWINDOW;
  StartupInfo.wShowWindow := SW_SHOWDEFAULT;
  zAppName := ExtractFilePath(Application.EXEName)+'AppTwo.Exe';
  CreateProcess(nil, PChar(zAppName), nil, nil, False, CREATE_NEW_CONSOLE or
    NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo);
  WaitForInputIdle(ProcessInfo.hProcess, INFINITE);
  EnumThreadWindows(ProcessInfo.dwThreadId, @EnumerateTW, GetCurrentProcessID);
end;

The second app then accepts the message and runs the following code in its message handler:

procedure TForm1.SXProcessInfo(var Message: TMessage);
var
  PID: LongWord;
  PHND: LongWord;
begin
  With Message do
  begin
    PID := LongWord(WParam);
    If PID <> 0 then
    begin
      PHND := OpenProcess(SYNCHRONIZE, False, PID);
      If PHND <> 0 then
      begin
        WaitForSingleObject(PHND, INFINITE);
        ShowMessage('Application was terminated');
        CloseHandle(PHND);
      end;
    end;
    Result := 1;
  end;
end;

You should be able to figure out where to place all the code that is missing from this...eg where to close the first app, where to run the code in the second app for the update, where to place the thread routines if you want to do it that way.

Let me know what you think.
0
 
LVL 2

Accepted Solution

by:
Ten13 earned 100 total points
ID: 2662300
KISS! (keep it simple stupid :o)

If I understand you correctly (and I belive I do ;-) I have done this myself without problems. It is tested on both Win95 and WinNT4.0.

OK! Ready? Here we go:

Application 1: (this is psoudo code off the top of my head. May not compile!)

uses
  ShellAPI,
  Windows,
 
// You might want to do this in your mainform (TForm1.FormCreate)
begin
  Application.Initialize;
  if CheckVersionInfo then begin // is the version OK?
    // Yes! Continue as normal
    Application.CreateForm(TForm1, Form1);
    Application.Run;
  end
  else begin
    // Nope! Run the second app and exit!
    // execute: "application2.exe /p:xxx"
    ShellExecute(0, nil, 'Application2.exe', '/p:$' + IntToHex(GetCurrentProcessId(), 8), nil, SW_SHOW);
  end;
end.


Application 2: (this is tested and can compile!)

// TForm1 is the mainform.
procedure TForm1.FormCreate(Sender: TObject);
var
  bOK : boolean; // is the procces terminated
  nPID : integer; // the process we want to wait for
  hProc : THandle; // handle to the very same process
  dwResult : DWORD; // WaitForSingleObject result
begin
  bOK:= false; // assume the process is running
  if Copy(ParamStr(1), 1, 3) = '/p:' then begin // is a PID passed as a commandline parameter?
    nPID:= StrToInt(Copy(ParamStr(1), 4, Length(ParamStr(1)))); // Get the PID from the commandline parameter
    hProc:= OpenProcess(PROCESS_ALL_ACCESS, FALSE, nPID); // get a handle of the process we want to wait for
    if hProc = 0 then begin // is it a valid handle?
      bOK:= true; // the process does not exist. It is terminated!
    end
    else begin
      dwResult:= WaitForSingleObject(hProc, INFINITE); // wait for ever for the process to end
//      dwResult:= WaitForSingleObject(hProc, 5000); // return after 5 secs
//      if dwResult = WAIT_TIMEOUT then begin // we timed out. THe process is proberbly still running
//        ShowMessage('Timeout!');
//      end;
      if dwResult = WAIT_OBJECT_0 then begin // YES!!! The process terminated
        bOK:= true;
      end;
    end;
    CloseHandle(hProc); // remeber to clean up!
  end;

  if bOK then begin
    ShowMessage('Process is terminated!');
  end;
end;

T-T-That's all folks! Nothing to it! I'll keep an eye out for any questions :o)

- Martin
0
 
LVL 1

Author Comment

by:millerw
ID: 2669619
Martin:

Thanks for the reply.  I do like your solution...it is much simpler than mine :-).  

You seem to be a knowledgeable fellow so let me ask your opinion since you have done something like this already.  My application is a DB app that uses the database to store and update its own files.  The problem, is I would like to make it so that the Updater doesn't have to have database access.  Thus, I'd like for the EXE to "download" the data from the database and then someway tell the updater "here is the data for update" then quit.  One issue here is that the EXE update is not necessarily one file...at this time it is a collection of 3 but that is of course subject to change :-).

I would like to perform the operation in a secure fashion (although the speed of the operation would be almost too fast for user "mistakes" :-), eg passing the downloaded file(s) handle(s) or something of that sort to where a user cannot gain access to the file(s).  I do have the ability to "compress" the three files into a single proprietary file format and pass only one handle or something of that sort.  

I have thought of creating *.~XXX file(s) that is/are simply renamed to *.XXX but that can cause problems if a user opens the ~XXX file before the updater gets to it.  That could, however, cause the updater to exit out abnormally of course instead of restarting the EXE...which would start the update procedure all over again :-)

So what do you think?  

Scott
0
 
LVL 2

Expert Comment

by:Ten13
ID: 2676709
I presume that your application is to run in a big organization. You want to launch updates transparently from the users and the users may not know that an update has been deployed or where to find the update file.
Well I have a couple of ideas. Concept if you will. Starting with the spimplest:

1. Have a file placed on a networkdrive. Your "update.exe" app. will then just copy this file to the client pc, overwriteting the main application. You could encrypt the file somehow to avoid nosy people to poke or "steal".

2. Supply your  "update.exe" with DB support and fetch the updated aplication from the DB. The DB could be password protected to avoid nosy people... etc...

3. Do your company have an intertnet (or even better intranet) web server in the house? If so, put your updated application on the FTP part of the web server. From your "update.exe" app just do an simple ftp://ftp.company.com/DBApp/latestrelease.exe You could password protect the ftp access and even encrypt the latestrelease.exe file to protectect nosy... you get the drift I hope ;-)

4. Write your own server program to run on your server. A program that constantly runs on your server. Acces this program from "update.exe" with TCP/IP and transfer the latestrelease.exe. Encryption, password etc.

Depending on the security you want I'll go for number 1 or 3. Number 1 is the absolutely the easist one. With some encryption you don't have to wory about the users poking around. Futhermore you don't need to map a networkdrive. Just fetch the file from \\server\DBApp\latestupdate.exe. You can even password protect the \\server\DBApp share so your users can't browse it.
Number 3 has a very hight security factor, but requires that your company has a webserver running in the house.

Make up your mind and please come back with any quyestions and I'll try to give you a hint or two.
Good luck

- Martin
0
 
LVL 2

Expert Comment

by:Ten13
ID: 2799995
So!

How goes? Did you solve your problem? Which solution did you use?

- Martin
0
 
LVL 1

Author Comment

by:millerw
ID: 2825635
The solution used is as follows:

1.  Check for the EXE and all EXE support file versions being old.
2.  If old, download all new versions into a proprietary compressed file format.
3.  Launch the updater (a glorified uncompressor really).
4.  Exit.

The updater then uncompresses everything and relaunches the executable.

I've been meaning to come back here and award you the points....I did use your "parameter method" to pass information which also solves the problem of the Updater being executed outside of the EXE calling it.  However, I did use the compressed file instead of the ideas above because it gives security as well as allowing the updater to not require access to the database--password concerns and such.

So far, the algorithm takes only about 1-3 seconds for a complete update (all files of the application are being updated all at once).  

My project manager is glowing :-)

Scott
0

Featured Post

Better Security Awareness With Threat Intelligence

See how one of the leading financial services organizations uses Recorded Future as part of a holistic threat intelligence program to promote security awareness and proactively and efficiently identify threats.

Join & Write a Comment

Creating an auto free TStringList The TStringList is a basic and frequently used object in Delphi. On many occasions, you may want to create a temporary list, process some items in the list and be done with the list. In such cases, you have to…
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…
Here's a very brief overview of the methods PRTG Network Monitor (https://www.paessler.com/prtg) offers for monitoring bandwidth, to help you decide which methods you´d like to investigate in more detail.  The methods are covered in more detail in o…
This video demonstrates how to create an example email signature rule for a department in a company using CodeTwo Exchange Rules. The signature will be inserted beneath users' latest emails in conversations and will be displayed in users' Sent Items…

760 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

20 Experts available now in Live!

Get 1:1 Help Now