I've written a program that updates itself if it finds a newer version on a server but I'm finding it hard to implement cleanly.
我写了一个程序,如果它在服务器上找到一个更新的版本,它会更新自己,但我发现它很难干净地实现。
Checking a remote server and downloading the file are easy enough but then what? It can't simply File.Copy("newversion.exe","myprogram.exe") because myprogram is running and the file is locked.
检查远程服务器并下载文件很简单但是那又如何呢?它不能简单地File.Copy(“newversion.exe”,“myprogram.exe”),因为myprogram正在运行且文件被锁定。
I do it by downloading two files newversion and update. myprogram then launches update and exits. update waits for two seconds, does the copy, then launches myprogram (which is now the updated version) and exits. myprogram looks for update when it starts and deletes it and newversion if it finds them. mypogram is now the new version with no files left laying around.
我通过下载两个文件newversion和更新来做到这一点。 myprogram然后启动更新并退出。更新等待两秒钟,复制,然后启动myprogram(现在是更新版本)并退出。 myprogram在启动时查找更新并删除它,如果找到它则返回newversion。 mypogram现在是新版本,没有留下任何文件。
There must be a better way than this so what's the .net way for a program to update itself?
必须有一个比这更好的方法,那么程序更新自己的.net方式是什么?
PS. Sorry if this question appears twice - I got a 'stack overflow is broken' page the first time I submitted it.
PS。对不起,如果这个问题出现两次 - 我第一次提交时遇到'堆栈溢出已损坏'页面。
5 个解决方案
#2
7
I looked at both Wix and ClickOnce and didn't love them. I wanted something simpler, for one-file "xcopy deployment" type WPF apps.
我看着Wix和ClickOnce并不喜欢它们。对于单文件“xcopy部署”类型的WPF应用程序,我想要更简单的东西。
So I built one. It's an AppUpdater class that you can drop into any WPF app, automatically updates itself when necessary.
所以我建了一个。这是一个AppUpdater类,您可以将其放入任何WPF应用程序中,并在必要时自动更新。
Checking for a version number and downloading the latest version is easy.
检查版本号并下载最新版本很容易。
The next part is less easy. How do you replace a running app? The approach I took was this:
下一部分不太容易。你如何替换正在运行的应用程序?我采取的方法是这样的:
-
every time it runs, the app checks a well-known URL for a version. If the version is NOT later than the version of the currently executing app, then there's nothing more to do.
每次运行时,应用程序都会检查一个众所周知的URL版本。如果版本不晚于当前正在执行的应用程序的版本,则无需再做任何事情。
-
If the version IS later, then the app grabs a named Mutex, copies itself (Assembly.GetExecutingAssembly().Location) to a temporary location, then starts a new process (process #2) from that temp location, passing a command-line argument that says "you are trying to update" and giving the original exe location.
如果以后版本为IS,那么app会抓取一个名为Mutex,将自身(Assembly.GetExecutingAssembly()。Location)复制到临时位置,然后从该临时位置启动一个新进程(进程#2),并传递一个命令行说“你正在尝试更新”并给出原始exe位置的参数。
-
process #1 shuts itself down.
进程#1关闭自己。
-
process #2 starts up - it sees that it is trying to update, so it downloads the updated image, and places it in the original location (the location that was passed on the command line). Rather than waiting 2 seconds, or any arbitrary time, it uses the named Mutex to determine when process #1 has exited.
进程#2启动 - 它看到它正在尝试更新,因此它下载更新的映像,并将其放在原始位置(在命令行上传递的位置)。它不是等待2秒或任何任意时间,而是使用命名的互斥锁来确定进程#1何时退出。
-
process #2 then starts process #3 - using the original location.
然后,进程#2启动进程#3 - 使用原始位置。
-
Process #2 shuts down, releasing its named Mutex.
进程#2关闭,释放其命名的Mutex。
-
process #3 starts up. It checks the version URL - sees that it is current. No update necessary. Process #3 also waits for process #2 to release its Mutex, and then removes the temporary copy of the EXE - the one that was used by process #2.
进程#3启动。它会检查版本URL - 看到它是最新的。无需更新。进程#3还等待进程#2释放其Mutex,然后删除EXE的临时副本 - 进程#2使用的临时副本。
EDIT
编辑
In response to the comment, yes, it sounds complicated. But the logic that does all that is in the AppUpdater module. To use it from a WPF app is pretty simple:
在回应评论时,是的,这听起来很复杂。但是AppUpdater模块中所有这一切的逻辑。要从WPF应用程序中使用它非常简单:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
// add this line in your constructor
if (UpdaterCheck()) this.Hide();
}
// this is all boilerplate
private Ionic.AppUpdater.Wpf.Updater _updater;
private string _manifestUrl = "http://example.org/AppUpdates/MyApplication/Manifest.xml";
private string _infoUrl = "http://example.org/MyApplication.html";
private string _description = "MyApplication is a delightful application; it does x, y, and z. ";
// Obtain the publicKey XML from the ManifestTool that ships with Ionic AppUpdater.
// The following string is Ionic's public key. You will need a different one.
// Consult the Readme for more information.
private string _publicKeyXml = "<RSAKeyValue><Modulus>sZqhvF0KX6m4blah..blah...=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
// add this boilerplate method
private bool UpdaterCheck()
{
_updater = new Ionic.AppUpdater.Wpf.Updater("This is MyApplication.",
_infoUrl,
_description,
_manifestUrl,
_publicKeyXml);
return _updater.Status.IsUpdating;
}
...
Details and code you can run, here.
您可以在这里运行的详细信息和代码。
It works only with WPF apps, but it would be pretty simple to build a WinForms version of the thing. Same idea, though some of the logic that generates the UI would have to change.
它仅适用于WPF应用程序,但构建WinForms版本的东西会非常简单。同样的想法,虽然生成UI的一些逻辑必须改变。
#4
2
Its easy when you know the "workaround". Your program cant download itself over itself because the file is locked by the running process. The workaround is that it still is possible to RENAME the file. So here is what you do:
当你知道“解决方法”时很容易。您的程序无法自行下载,因为该文件已被正在运行的进程锁定。解决方法是仍然可以重命名该文件。所以这就是你做的:
IF your programs name are "myprogram.exe":
如果您的程序名称是“myprogram.exe”:
- myprogram.exe starts
- myprogram.exe启动
- Myprogram.exe download a new version of itself but under the name "newversion.bak"
- Myprogram.exe下载了自己的新版本,但名称为“newversion.bak”
- Check that newversion.bak has downloaded correct (md5-check or something)
- 检查newversion.bak是否已下载正确(md5-check或其他)
- myprogram.exe renames the "myprogram.exe" to "myprogram.old"
- myprogram.exe将“myprogram.exe”重命名为“myprogram.old”
- myprogram.exe renames the "newversion.bak" to "myprogram.exe"
- myprogram.exe将“newversion.bak”重命名为“myprogram.exe”
- myprogram.exe restarts itself, and voila, its updated.
- myprogram.exe重新启动,瞧,它更新了。
- (optional) myprogram.exe deletes "myprogram.old" if exists when started.
- (可选)myprogram.exe在启动时删除“myprogram.old”。
This will allow you to update your program without having an external updater.
这将允许您在没有外部更新程序的情况下更新程序。
#5
0
You may want to look into .NET's No Touch Deployment. It was replaced, in marketing at least, by Click-Once.
您可能希望了解.NET的No Touch Deployment。它至少在营销中被Click-Once取代。
I wrote an application that used this. It pulled its assembly from a local intranet server. It used sockets to listen to a specific port, so I was able to force upgrades by sending a message, which spawned another program to kill it/restart it. Probably not the best solution but it worked.
我写了一个使用它的应用程序。它从本地Intranet服务器中取出了它的程序集。它使用套接字来侦听特定端口,因此我可以通过发送消息来强制升级,这会生成另一个程序来杀死它/重新启动它。可能不是最好的解决方案但它有效。
I'd say Click-Once is a better solution, but I thought I'd mention No Touch.
我说Click-Once是一个更好的解决方案,但我想我会提到No Touch。
#1
#2
7
I looked at both Wix and ClickOnce and didn't love them. I wanted something simpler, for one-file "xcopy deployment" type WPF apps.
我看着Wix和ClickOnce并不喜欢它们。对于单文件“xcopy部署”类型的WPF应用程序,我想要更简单的东西。
So I built one. It's an AppUpdater class that you can drop into any WPF app, automatically updates itself when necessary.
所以我建了一个。这是一个AppUpdater类,您可以将其放入任何WPF应用程序中,并在必要时自动更新。
Checking for a version number and downloading the latest version is easy.
检查版本号并下载最新版本很容易。
The next part is less easy. How do you replace a running app? The approach I took was this:
下一部分不太容易。你如何替换正在运行的应用程序?我采取的方法是这样的:
-
every time it runs, the app checks a well-known URL for a version. If the version is NOT later than the version of the currently executing app, then there's nothing more to do.
每次运行时,应用程序都会检查一个众所周知的URL版本。如果版本不晚于当前正在执行的应用程序的版本,则无需再做任何事情。
-
If the version IS later, then the app grabs a named Mutex, copies itself (Assembly.GetExecutingAssembly().Location) to a temporary location, then starts a new process (process #2) from that temp location, passing a command-line argument that says "you are trying to update" and giving the original exe location.
如果以后版本为IS,那么app会抓取一个名为Mutex,将自身(Assembly.GetExecutingAssembly()。Location)复制到临时位置,然后从该临时位置启动一个新进程(进程#2),并传递一个命令行说“你正在尝试更新”并给出原始exe位置的参数。
-
process #1 shuts itself down.
进程#1关闭自己。
-
process #2 starts up - it sees that it is trying to update, so it downloads the updated image, and places it in the original location (the location that was passed on the command line). Rather than waiting 2 seconds, or any arbitrary time, it uses the named Mutex to determine when process #1 has exited.
进程#2启动 - 它看到它正在尝试更新,因此它下载更新的映像,并将其放在原始位置(在命令行上传递的位置)。它不是等待2秒或任何任意时间,而是使用命名的互斥锁来确定进程#1何时退出。
-
process #2 then starts process #3 - using the original location.
然后,进程#2启动进程#3 - 使用原始位置。
-
Process #2 shuts down, releasing its named Mutex.
进程#2关闭,释放其命名的Mutex。
-
process #3 starts up. It checks the version URL - sees that it is current. No update necessary. Process #3 also waits for process #2 to release its Mutex, and then removes the temporary copy of the EXE - the one that was used by process #2.
进程#3启动。它会检查版本URL - 看到它是最新的。无需更新。进程#3还等待进程#2释放其Mutex,然后删除EXE的临时副本 - 进程#2使用的临时副本。
EDIT
编辑
In response to the comment, yes, it sounds complicated. But the logic that does all that is in the AppUpdater module. To use it from a WPF app is pretty simple:
在回应评论时,是的,这听起来很复杂。但是AppUpdater模块中所有这一切的逻辑。要从WPF应用程序中使用它非常简单:
public partial class Window1 : Window
{
public Window1()
{
InitializeComponent();
// add this line in your constructor
if (UpdaterCheck()) this.Hide();
}
// this is all boilerplate
private Ionic.AppUpdater.Wpf.Updater _updater;
private string _manifestUrl = "http://example.org/AppUpdates/MyApplication/Manifest.xml";
private string _infoUrl = "http://example.org/MyApplication.html";
private string _description = "MyApplication is a delightful application; it does x, y, and z. ";
// Obtain the publicKey XML from the ManifestTool that ships with Ionic AppUpdater.
// The following string is Ionic's public key. You will need a different one.
// Consult the Readme for more information.
private string _publicKeyXml = "<RSAKeyValue><Modulus>sZqhvF0KX6m4blah..blah...=</Modulus><Exponent>AQAB</Exponent></RSAKeyValue>";
// add this boilerplate method
private bool UpdaterCheck()
{
_updater = new Ionic.AppUpdater.Wpf.Updater("This is MyApplication.",
_infoUrl,
_description,
_manifestUrl,
_publicKeyXml);
return _updater.Status.IsUpdating;
}
...
Details and code you can run, here.
您可以在这里运行的详细信息和代码。
It works only with WPF apps, but it would be pretty simple to build a WinForms version of the thing. Same idea, though some of the logic that generates the UI would have to change.
它仅适用于WPF应用程序,但构建WinForms版本的东西会非常简单。同样的想法,虽然生成UI的一些逻辑必须改变。
#3
#4
2
Its easy when you know the "workaround". Your program cant download itself over itself because the file is locked by the running process. The workaround is that it still is possible to RENAME the file. So here is what you do:
当你知道“解决方法”时很容易。您的程序无法自行下载,因为该文件已被正在运行的进程锁定。解决方法是仍然可以重命名该文件。所以这就是你做的:
IF your programs name are "myprogram.exe":
如果您的程序名称是“myprogram.exe”:
- myprogram.exe starts
- myprogram.exe启动
- Myprogram.exe download a new version of itself but under the name "newversion.bak"
- Myprogram.exe下载了自己的新版本,但名称为“newversion.bak”
- Check that newversion.bak has downloaded correct (md5-check or something)
- 检查newversion.bak是否已下载正确(md5-check或其他)
- myprogram.exe renames the "myprogram.exe" to "myprogram.old"
- myprogram.exe将“myprogram.exe”重命名为“myprogram.old”
- myprogram.exe renames the "newversion.bak" to "myprogram.exe"
- myprogram.exe将“newversion.bak”重命名为“myprogram.exe”
- myprogram.exe restarts itself, and voila, its updated.
- myprogram.exe重新启动,瞧,它更新了。
- (optional) myprogram.exe deletes "myprogram.old" if exists when started.
- (可选)myprogram.exe在启动时删除“myprogram.old”。
This will allow you to update your program without having an external updater.
这将允许您在没有外部更新程序的情况下更新程序。
#5
0
You may want to look into .NET's No Touch Deployment. It was replaced, in marketing at least, by Click-Once.
您可能希望了解.NET的No Touch Deployment。它至少在营销中被Click-Once取代。
I wrote an application that used this. It pulled its assembly from a local intranet server. It used sockets to listen to a specific port, so I was able to force upgrades by sending a message, which spawned another program to kill it/restart it. Probably not the best solution but it worked.
我写了一个使用它的应用程序。它从本地Intranet服务器中取出了它的程序集。它使用套接字来侦听特定端口,因此我可以通过发送消息来强制升级,这会生成另一个程序来杀死它/重新启动它。可能不是最好的解决方案但它有效。
I'd say Click-Once is a better solution, but I thought I'd mention No Touch.
我说Click-Once是一个更好的解决方案,但我想我会提到No Touch。