Tek-Tips is the largest IT community on the Internet today!

Members share and learn making Tek-Tips Forums the best source of peer-reviewed technical information on the Internet!

  • Congratulations SkipVought on being selected by the Tek-Tips community for having the most helpful posts in the forums last week. Way to Go!

I Want to Automate the Update 1

Status
Not open for further replies.

Steve Meyerson

Programmer
Sep 17, 2020
317
1
18
US
Three users currently run my Barter.exe. I place an updated version in a common folder. Barter.exe checks for a newer version. If it finds one, the user is notified. Then I want to

1. Quit the exe
2. Overwrite the current exe with the newer one.
3. Restart the new MyApp.exe.

There used to be (a long time ago) a QUIT TO command which I could use to accomplish the update.

A batch file might do the trick, but I'd rather not have to start my exe with a batch file.

Any other ideas?

Steve
 
One option would be to use ShellExecute().

It would work like this:

1. In your main app, notify the user that an update is available.

2. If they agree to the update, use ShellExecute() to run a small VFP update program.

3. Immediately quit your main program.

You would write the update program in VFP. It would do the following:

1. Wait for the user to press a key to continue (this is essential, otherwise there is a risk that it will start running before your main progam has quit).

2. Overwrite the old main EXE with the new one.

3. Use ShellExecute() to run the main EXE.

Having said all that, I can't help thinking that a batch file would be easier. Do you have any special reason for not wanting to use one?

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike said:
Do you have any special reason for not wanting to use one?
Not really. It's just been a long time since I've done one.

use ShellExecute() to run a small VFP update program.
Just for my understanding, Won't it have to run AFTER quitting the Program? Otherwise Barter.exe would still be open, causing an error. (Or does it actually run AFTER the program quits?)

Thanks for your heelp.

Steve
 
i use an inno script to create an update.exe

the main program
[ul]
[li]detects an update is required (by comparing the currentversion number of the .exe with a version number in a table)[/li]
[li]Edit: Asks/tells user we're about to update[/li]
[li]downloads the update.exe[/li]
[li]launches (shellexecute) the update.exe (with /SP /SILENT /FORCECLOSEAPPLICATIONS /SUPRESSMSGBOXES /NORESTART) [/li]
[li]quits [/li]
[/ul]
the update.exe then
[ul]
[li]prompts for UAC if required[/li]
[li]silently installs the new version of main.exe into the same folder (from a registry setting)[/li]
[li]launches the now updated main .exe[/li]
[li]quits[/li]
[/ul]

hth

(it recently got a bit fancy in that if the main program detects it is not insatlled into c:\program files (x86) it downloads a version of the update.exe that doesn't prompt for elevation as some users don;t have UAC rights on their PC)

n



 
My objective is transparency for the user. Here's how: On startup, the user is notified if an update is available. The user clicks Ok and continues using the program after the update is executed without user intervention. Maybe I'll notify the user the update was successful.

Steve
 
I keep a binaries file in my .exe, which has an updater.exe that I write out when the .exe is run.

The main exe then looks to see if there is a newer version in a user definable location and
it it finds it downloads a copy to a temporary location on the PC in question.

It then runs the updater and immediately quits. The updater looks for the newly downloaded file,
compares it with the main exe and if it is newer, overwrites it. Then it runs the main exe and quits
itself.

One little wrinkle, because of UAC it can be better to present a message to the user that they need to
restart the application as it is often the case that this behaviour triggers elevation and the main exe
may start as Administrator and not have the right drive mappings (which is why the newer version is downloaded
to a local folder by the original program).

Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
Just for my understanding, Won't it have to run AFTER quitting the Program?

No. ShellExecute() will launch the update program straight away. Your main program will then continue to run. So the very next thing it should do after the ShellExecute() is to quit.

This is why I said that your update program should tell the user to press a key in order to continue: to give the main program the chance to quit before doing the copy.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
In fact, the way I do it is slightly different. I have a small loader program whose only job is to look for an update. If it finds one, it copies it to the main executable. Whether it finds an update or not, it then launches the main executable.

The user is not aware of any of this. When they want to run the app, they simply launch the loader from a shortcut or menu option.

In my case, the loader will copy the new version without the user being aware of it. But if you want to ask the user's permission to do that, the loader could easily handle that as well.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
This is the code I use in my main exe
Code:
** Write out the updater
SELECT 0
USE ("D:\MYDEVVFP9\MYAPP\BINARY")
LOCATE FOR BINARY.NAME = "UPDATER"
STRTOFILE(BINARY.PAYLOAD,m.WHEREAMI+m.SYSTEMNAME+"Updater.EXE")
USE
** Check for system updates...
IF !EMPTY(m.UPDATELOCATION)
	ON ERROR DO FILEERR
	** IF THERE IS A FILE
	IF MYFILE(m.UPDATELOCATION+m.SYSTEMNAME+".EXE")
		** IF THE FILE IS NEWER
		IF ADIR(AUPDATES,m.UPDATELOCATION+m.SYSTEMNAME+".EXE") = 1
			IF ADIR(AEXISTING,m.WHEREAMI+m.SYSTEMNAME+".EXE") = 1
				IF AUPDATES(1,3) >= AEXISTING(1,3)
					IF AUPDATES(1,4) > AEXISTING(1,4) .OR. AUPDATES(1,3) > AEXISTING(1,3)
						IF MYFILE(m.WHEREAMI+m.SYSTEMNAME+"Updater.EXE")
							IF !DIRECTORY(m.WHEREAMI+"TEMP")
								ON ERROR DO FILEERR
								MD (m.WHEREAMI+"TEMP")
								ON ERROR DO USUAL WITH LINENO(),PROGRAM()
							ENDIF
							IF UPPER(m.UPDATELOCATION+m.SYSTEMNAME+".EXE") <> UPPER(m.WHEREAMI+"TEMP\"+m.SYSTEMNAME+".EXE")
								WAIT "Copying Update to local machine" WINDOW NOWAIT TIMEOUT 2
								COPY_FILE(m.UPDATELOCATION+m.SYSTEMNAME+".EXE",m.WHEREAMI+"TEMP\"+m.SYSTEMNAME+".EXE")
								WAIT "Copied Update to local machine" WINDOW TIMEOUT 2
							ENDIF
							BROWSER(m.WHEREAMI+m.SYSTEMNAME+"Updater.EXE")
							QUIT
						ENDIF
						** UPDATE AVAILABLE
					ENDIF
				ENDIF
			ENDIF
		ENDIF
		RELEASE AUPDATES
		RELEASE AEXISTING
	ENDIF
	ON ERROR DO USUAL WITH LINENO(),PROGRAM()
ENDIF

MyFile() is similar to File() but eliminates the false positives cause by PATH
The FILEERR routine is just a 'do nothing' if there is an OS error
m.WhereAmi is the current location of the exe that is running
m.SystemName = is the name of the application
m.UpdateLocation is where the update is downloaded from
COPY_FILE() copies a file!
Browser() is a function that opens a file, app or whatever using shellexecute

The updater code looks like this:
Code:
	ON ERROR DO FILEERR
	** IF THERE IS A FILE
	IF MYFILE(m.UPDATELOCATION+m.SYSTEMNAME+".EXE")
		** IF THE FILE IS NEWER
		IF ADIR(AUPDATES,m.UPDATELOCATION+m.SYSTEMNAME+".EXE") = 1
			IF ADIR(AEXISTING,m.WHEREAMI+m.SYSTEMNAME+".EXE") = 1
				IF AUPDATES(1,3) >= AEXISTING(1,3)
					IF AUPDATES(1,4) > AEXISTING(1,4) .OR. AUPDATES(1,3) > AEXISTING(1,3)
						ERASE (m.WHEREAMI+m.SYSTEMNAME+".EXE")
						** put a tiny delay in between the deletion and the test - to allow
						** for anti-virus etc to let the system know that the file has gone
						IF MYFILE(m.WHEREAMI+m.SYSTEMNAME+".EXE")
							CLEAR TYPEAHEAD
							INKEY(1)
							** try to delete again
							IF MYFILE(m.WHEREAMI+m.SYSTEMNAME+".EXE")
								ERASE (m.WHEREAMI+m.SYSTEMNAME+".EXE")
							ENDIF
						ENDIF
						IF !MYFILE(m.WHEREAMI+m.SYSTEMNAME+".EXE")
							WAIT "Copying Update" WINDOW NOWAIT TIMEOUT 10
							COPY_FILE(m.UPDATELOCATION+m.SYSTEMNAME+".EXE",m.WHEREAMI+m.SYSTEMNAME+".EXE")
							MESSAGEBOX("Copied Update File",48,"Done")
							IF MYFILE(m.WHEREAMI+m.SYSTEMNAME+".EXE")
								** TEST FOR OPERATING SYSTEM VERSION
								** IF LESS THAN 6 (VISTA) THEN RELOAD SYSTEM
								** OTHERWISE JUST ASK THE USER TO
								IF VAL(OS(3)) < 6.0
									BROWSER(m.WHEREAMI+m.SYSTEMNAME+".EXE")
								ELSE
									MESSAGEBOX("Very Sorry - You must manually reload "+M.SYSTEMNAME+M.CRLF+"This is a Microsoft Security Wrinkle to help prevent virus invasions"+M.CRLF+"Click on 'Ok' to exit",48,"Quit and Manually Reload System")
									QUIT
								ENDIF
							ELSE
								MESSAGEBOX("Failed to update system",48,"Failed")
							ENDIF
						ELSE
							MESSAGEBOX("Unable to delete existing copy of "+m.systemname+m.crlf+"You may need to update manually - contact support",48,"Failed")
						ENDIF
						** UPDATE AVAILABLE
					ENDIF
				ENDIF
			ENDIF
		ENDIF
		RELEASE AUPDATES
		RELEASE AEXISTING
	ENDIF
	ON ERROR DO USUAL WITH LINENO(),PROGRAM()
	QUIT




Regards

Griff
Keep [Smile]ing

There are 10 kinds of people in the world, those who understand binary and those who don't.

I'm trying to cut down on the use of shrieks (exclamation marks), I'm told they are !good for you.
 
I second Mikes idea of a loader because you can't overwrte an exe from itself, it has to be another process. Therfore a loader typically also is a batch file (bat or cmd file).

There's a minor issue, if your users want to keep their shortcut to the EXE itself or pin the application to the taskbar when it runs and use that pinned icon to start your application.

Well, introduce a parameter in yourapp.exe, which decides whether it calls the loader.exe and quits right away or whether it continues.

The first few lines of yourapp.exe main.prg could be these:

Code:
LPARAMETERS tcSwitch

If Pcount()=0 OR NOT tcSwitch == 'UPDATED'
   Run /N loader.exe
   Quit

* continue as normal

The loader.exe should check for an update, mdo it and start yourapp.exe

Code:
*check for file dates or version numbers and update or not
If FDate('x:\updates\yourapp.exe',1) > FDate('c:yourapp.yourapp.exe',1)
   Copy File 'x:\updates\yourapp.exe' TO 'c:yourapp\yourapp.exe' && or wherever the locatoin of your app is. 
   * Notice to copy to "c:\Program Files" the loader would need elevation and normal users can't provide that.
Endif 
* Whether there was an update or not, always finish by calling yourapp.exe with 
RUN /N "c:yourapp.yourapp.exe" UPDATED

I recommend this, as needing to start a loader means the pinned taskbar shortcut of an app will circumvent the loader and the users using pinned shortcuts will not update their EXE.

Chriss
 
Steve, just to add a couple of thoughts ...

My loader program (described above) doesn't only check for new versions of the executable. It also looks for new versions of any file in the executable directory. I do that mainly so that I can deal with updated Help files, but it also works with new versions of DLLs or any other relevant file.

Another feature of my loader: When it installs a new EXE, it passes a flag to the main program (I do this via a global config file, but it can be done with any suitable mechanism). If the flag is set, the main program displays a message to the user to advise them that they have a new version and to ask them if they would like to see a "What's New" page. If they agree, the program then displays the relevant page in the Help file (using VFP's HELP command). It then clears the flag.

I know none of this is directly related to your original question, but if you get as far as creating a loader program, they are features that you might want to add, perhaps at a later stage.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Thanks guys for your great help!

I'm using the loader file approach (code below) which seems to work ok now without user intervention. My main problem was after the [RUN /N Barter.exe] command, the main exe kept reopening as if in a loop. After much trouble shooting, reboots and head-scratchings, the problem was in the main exe, not the loader. A reBUILD of the main exe (without change) fixed it. I believe it got corrupted somehow on my computer (it worked fine on my client's computer).

Steve

Code:
SET RESOURCE OFF
_SCREEN.Themes = .F.
_SCREEN.Visible = .F.

****************** Get file names
USE Lconfig  && User's local configuration file
newFile = ADDBS(ALLTRIM(ServerPath)) + 'Barter.exe'
oldFile = 'Barter.exe'  && In current folder
USE

****************** Update only if both files available
IF FILE(m.newFile) AND FILE(m.oldFile)
 SET HOURS TO 24  && In case update during same day
 newTime = DTOC(FDATE(m.newFile),1)+FTIME(m.newFile)
 oldTime = DTOC(FDATE(m.oldFile),1)+FTIME(m.oldFile)
 IF m.newTime > m.oldTime  && Need to update?
  ******* For message to user
  oldVersion = DTOC(FDATE(m.oldFile)) + '_' + FTIME(m.oldFile)
  newVersion = DTOC(FDATE(m.newFile)) + '_' + FTIME(m.newFile)
  SET SAFETY OFF  && Overwrite without user confirmation
  ok = .T.
  TRY
   COPY FILE(m.newFile) TO (m.oldFile)
  CATCH
   ok = .F.
  ENDTRY
  IF m.ok
   =MESSAGEBOX('Update successful.' ;
     +CHR(13)+CHR(13) + 'Old Version: '+m.oldVersion+'.' ;
     +CHR(13)+CHR(13) + 'New Version: '+m.newVersion+'.',0,'Update')
  ELSE
   =MESSAGEBOX('Unable to update Barter.exe.',0,'Update')
  ENDIF
 ENDIF
ENDIF

RUN /N Barter.exe
QUIT
 
Instead of SET RESOURCE OFF in the PRG have RESOURCE=OFF and SCREEN=OFF in a config.fpw file and include that into the loader.exe build.

Your starting problem can be a timing problem, you should wait after COPY TO before RUN, the file system may signal to VFP it can continue before the copy is complete, especially on Vista or later. That's also a reason using a DBF right after PACK fails, another reason is antivirus checking the EXE, copying an executable obviously is a suspicious activity that could point out a virus or malware unpacking or copying itself.

Then it's just lucky or unlucky whether RUN works and the successive run afer a rebuild might just be a coincidence, but not the reason it worked with the new EXE.

Chriss
 
Chris,

Code:
wait after COPY TO before RUN
Good Idea. I'll build in a 2 (or so) second delay after COPY TO to be safe.

have RESOURCE=OFF and SCREEN=OFF in a config.fpw file
Is there an advantage to this? (I think it would require placement of that file in each user's folder.)

Steve
 
Steve, the main reason to do RESOURCE=OFF is that it is executed before your program is instantiated, and thus completely inhibits the creation of the FoxUser resource files. By contrast, with SET RESOURCE OFF, the files have already been created and it is too late to prevent them.

This is important if the resource files would be created in a location that is visible to the user: specifically, their desktop. If you (or the user) runs the app directly from the desktop, and if the app is allowed to create the resource files, the user will see those files on the desktop. At best, that could cause a certain amount of confusion or uncertainty on the user's part. At worst, it will mean phone calls from the user wanting to know why those files have suddenly appeared (it has happened to me).

Similarly, SCREEN=OFF is executed before your program is instantiated, whereas _SCREEN.Visible = .F. is executed after the program's main window has been displayed. If you rely on _SCREEN.Visible = .F., the user would see the window flash on and off again. Given that the loader program is not supposed to have a user interface[sup][see note][/sup], the user might find that disconcerting.

Note: While on the subject of user interface, I would question your use of the message boxes in the loader. But I'll leave that for another post.

Mike

__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike, thanks for the comments. They are all good points and make much sense to me (except maybe the msg box in the loader?).

To prevent that I could flag the update with a field (or fields) in the local config file instead of displaying the message box. Then on main exe startup, show the message if an update occurred.

Question: If I put RESOURCE=OFF, etc, in Config.fpw, should I INCLUDE Config.fpw in the project? If so, do I still need to distribute that file?

Incidentally, I would never have a user run the app from the desktop - only from a shortcut on the desktop. Anyway the main reason you stated is good enough for me.

Thanks for your response.

Steve

 
If I put RESOURCE=OFF, etc, in Config.fpw, should I INCLUDE Config.fpw in the project?

Well, that's what I have always done - rightly or wrongly. And if you do that, you don't need to distribute the file separately, as it will be bound into the EXE.

I would never have a user run the app from the desktop

I wouldn't either. But you never know what a user is going to do when you are looking the other way. I did once have a case of an admin moving the entire application to the users' desktops - don't ask me why - after which I was constantly being asked what those FoxUser files were for. But you're right. It's probably not worth worrying about.

Re the messageboxes. This is not a big deal. It's just that the messages are slightly out of context (from the user's point of view). They are launching the app, and suddenly they see messages about an update. What do they know about updates? They didn't ask for an update. They just wanted to run the app.

"Update successful" is not so bad, but "Unable to run update" could be a problem. It is telling them that something is wrong, without any indication of what to do about it (or, specifically, without telling them that they can continue to run the old version as normal).

Personally, I would prefer the launcher program to be completely silent. If you want to advise the user that they now have a new version, let the main program deal with that. And if the update fails, I would keep quiet about it. There's nothing they can do about it, and the chances are it will work the next time they launch the program. (You might want to advise an administrator if an update fails, but the launcher is not the place to do that.)

Well, that's my personal opinion. Feel free to ignore it if you prefer.

Mike


__________________________________
Mike Lewis (Edinburgh, Scotland)

Visual FoxPro articles, tips and downloads
 
Mike has answered your questions to me very well.

And regarding the update message - it depends whether users are aware or not of coming updates. Some companies maintain their apps and let their users = their employies order features, then an update may be eagerly awaited.

I suggest: Just display a new version notice after an update. It's a fine idea to offer the "What's new" topic, but don't force this news on the users. If an update fails, this information is only really relevant for you and should be put into an error log. Then it would only annoy users to get such a notice if this just means the old version is kept and works for them.

On the other side, a fix might be eagerly awaited, so there are pros and cons, but a generic message like "unable to update..." may just cause worries and confusion about the stability of the kept version, while it just means a delay in the update.

Indeed you're not the only one looking at this from the developer's perspective. You know when users see such a message you'll get them reported. You're open and make this transparent instead of just logging it only for you or admins. That might be interpreted as sweeping such problems under the carpet. But not all transparency and openness is necessary or good information for users, it can be enough to be open towards admins, not all users.

Chriss
 
Status
Not open for further replies.

Part and Inventory Search

Sponsor

Back
Top