millerw
asked on
Automatic Update
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.....)
BTW solution must work for all 32-bit versions of Windows (95, 98, NT.....)
I believe the newest version of Wise Installmaster has an autoupdate feature.
ASKER
Not looking for an external product.
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($fffffff f,nil,PAGE _READONLY, 0,32,pchar (YourAppNa me));
result:=GetLastError=183;
end;
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($fffffff
result:=GetLastError=183;
end;
ASKER
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.
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.
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,ClassNam e,79);
if length(capt) > 0 then begin
for i:=0 to prgfrm.fil.Items.Count-1 do begin
j:=pos(uppercase(prgfrm.fi l.items[i] ),uppercas e(classnam e));
if j=0 then j:=pos(uppercase(prgfrm.fi l.items[i] ),uppercas e(capt));
if j<>0 then begin prgfrm.rnapp.items.add(Cap t); 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
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,ClassNam
if length(capt) > 0 then begin
for i:=0 to prgfrm.fil.Items.Count-1 do begin
j:=pos(uppercase(prgfrm.fi
if j=0 then j:=pos(uppercase(prgfrm.fi
if j<>0 then begin prgfrm.rnapp.items.add(Cap
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
ASKER
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?
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?
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
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
ASKER
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 :-)
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(Process Handle,Exi tCode)
Application.ProcessMessage s; \\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.
I guess this can be done by using the ExitCode, as follows:
//in your second app.
repeat
GetExitCodeProcess(Process
Application.ProcessMessage
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.
ASKER
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......
Well, I understand your concerns about wasting resources. But the following code:
repeat
GetExitCodeProcess(Process Handle,Exi tCode)
Application.ProcessMessage s;
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.
repeat
GetExitCodeProcess(Process
Application.ProcessMessage
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.
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.
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.
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
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
ASKER
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?
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?
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.processmessag es) 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
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.processmessag
I have never used DuplicateHandle. But have seen it at
http://msdn.microsoft.com/library/psdk/winbase/handobj_2pyd.htm
ASKER
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(Applicatio n.EXEName) +'AppTwo.E xe';
CreateProcess(nil, PChar(zAppName), nil, nil, False, CREATE_NEW_CONSOLE or
NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo);
WaitForInputIdle(ProcessIn fo.hProces s, INFINITE);
EnumThreadWindows(ProcessI nfo.dwThre adId, @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.
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(Applicatio
CreateProcess(nil, PChar(zAppName), nil, nil, False, CREATE_NEW_CONSOLE or
NORMAL_PRIORITY_CLASS, nil, nil, StartupInfo, ProcessInfo);
WaitForInputIdle(ProcessIn
EnumThreadWindows(ProcessI
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.
ASKER CERTIFIED SOLUTION
membership
This solution is only available to members.
To access this solution, you must be a member of Experts Exchange.
ASKER
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
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
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\latestupdat e.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
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\latestupdat
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
So!
How goes? Did you solve your problem? Which solution did you use?
- Martin
How goes? Did you solve your problem? Which solution did you use?
- Martin
ASKER
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
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