我有一些用 C# 编写的位于用户任务栏中的 UI 应用程序。 该工具的 EXE 已签入我们的许多使用该工具的项目的源代码控制系统,因此我们可以通过签入更新的 EXE 来更新它们运行的版本。
问题是,当用户获取最新版本的 exe 时,该程序经常在运行,并且在他们的计算机上同步失败。 我想修复它,以便程序在运行时不会锁定 exe 和任何依赖的 DLL,这样它们就可以同步,而无需关闭程序。
目前,我有一个程序,它将可执行文件作为参数,并通过提前将程序集内容读入内存来从内存中启动它。 不幸的是,当涉及到程序所需的 DLL 时,这完全失败了。
我现在的代码看起来像这样:
public class ExecuteFromMemory
{
public static void Main(string[] args)
{
//Figure out the name of the EXE to launch and the arguments to forward to it
string fileName = args[0];
string[] realArgs = new string[args.Length - 1];
Array.Copy(args, 1, realArgs, 0, args.Length - 1);
//Read the assembly from the disk
byte[] binary = File.ReadAllBytes(fileName);
//Execute the loaded assembly using reflection
Assembly memoryAssembly = null;
try
{
memoryAssembly = Assembly.Load(binary);
}
catch (Exception ex)
{
//Print error message and exit
}
MethodInfo method = memoryAssembly.EntryPoint;
if (method != null && method.IsStatic)
{
try
{
method.Invoke(null, new object[] { realArgs });
}
catch(Exception ex)
{
//Print error message and exit
}
}
else
{
//Print error message and exit
}
}
}
我的问题是,我在做一些完全愚蠢的事情吗? 有更好的方法来处理这个问题吗? 如果没有,我应该做什么来支持处理外部依赖项?
例如,如果您尝试运行使用“Bar.dll”中的函数的“Foo.exe”,则上述代码无法加载任何依赖文件,“Foo.exe”将被覆盖,但“Bar.dll”仍然存在已锁定且无法覆盖。
我尝试从加载的程序集上的“GetReferencedAssemblies()”方法获取引用程序集的列表,但这似乎没有给出任何指示应从何处加载程序集...我需要自己搜索它们吗? 如果是这样,最好的方法是什么?
似乎其他人以前可能遇到过这个,我不想重新发明轮子。
-
更新:
EXE 已签入,因为这就是我们将内部工具分发给使用它们的团队的方式。 对于这个用例来说它不是最佳的,但我没有机会改变这个策略。
I have some UI application that lives in the user's task bar that is written in C#. The EXE for the tool is checked in to our source control system on a number of projects that use it so we are able to update the version they run with by checking in updated EXE.
The problem is that when the users get the latest revision of the exe, the program is often running, and the sync fails on their machine. I want to fix it so the program doesn't lock the exe and any dependent DLL's when it runs so they can sync without having to shut down the program.
Currently, I have a program that takes an executable as a parameter and will launch it from memory by reading the assembly contents into memory ahead of time. Unfortunetly, this totally fails when it comes to the DLL's that the program requires.
The code I have right now looks something like this:
public class ExecuteFromMemory
{
public static void Main(string[] args)
{
//Figure out the name of the EXE to launch and the arguments to forward to it
string fileName = args[0];
string[] realArgs = new string[args.Length - 1];
Array.Copy(args, 1, realArgs, 0, args.Length - 1);
//Read the assembly from the disk
byte[] binary = File.ReadAllBytes(fileName);
//Execute the loaded assembly using reflection
Assembly memoryAssembly = null;
try
{
memoryAssembly = Assembly.Load(binary);
}
catch (Exception ex)
{
//Print error message and exit
}
MethodInfo method = memoryAssembly.EntryPoint;
if (method != null && method.IsStatic)
{
try
{
method.Invoke(null, new object[] { realArgs });
}
catch(Exception ex)
{
//Print error message and exit
}
}
else
{
//Print error message and exit
}
}
}
My question is, am I doing something totally stupid? Is there a better way to handle this? If not, what should I do to support handling external dependencies?
For example, the above code fails to load any dependent files if you try to run 'Foo.exe' that uses functions from 'Bar.dll', the 'Foo.exe' will be overwriteable, but 'Bar.dll' is still locked and can't be overwritten.
I tried getting the list of referenced assemblies from the 'GetReferencedAssemblies()' method on the loaded assmebly, but that doesn't seem to give any indication where the assemblies should be loaded from... Do I need to search for them myself? If so, what's the best way to do this?
It seems like other people might have come across this before, and I don't want to re-invent the wheel.
-
Update:
The EXE is checked in because thats how we distribute our in-house tools to the teams that use them. Its not optimal for this use-case, but I don't have the opportunity to change that policy.
发布评论
评论(3)
尽管迈克尔的答案是做到这一点的一种方法,但有一些工具明确用于管理桌面上安装的内容。
您对签入源代码管理的 exe 所做的操作是不正常的。 如果您有 Windows 域控制器,则可以使用组策略将程序下推到客户端。 或者,您可以使用 Altiris 之类的工具来处理它。
如果您必须继续走现在的路,那么您有两种选择。 一,使用帮助程序/加载程序应用程序在启动时进行版本检查。 这与 Firefox 的工作原理类似。
第二种方法是构建一个位于内存中并经常轮询更新的辅助服务。 这就是 Google Chrome、Adobe 等的工作原理。
Although Michael's answer is one way of doing this, there are tools out there that are explicitly for managing what's installed on the desktop.
What you are doing with the exe being checked into source control is not normal. If you have a windows domain controller, you can use Group Policy to push programs down to the client. Alternatively, you could use something like Altiris to handle it.
If you must continue the way you are going then you have two options. One, using a helper / loader app which does a version check on launch. This is similar to how firefox works.
The second way is to build a helper service that sits in memory and polls every so often for updates. This is how Google Chrome, Adobe, etc work.
免责声明:我不使用 Windows,尽管我熟悉它奇怪的锁定方式。
为了在应用程序运行时更新它,您可能需要两个进程:可执行文件本身,以及将完成更新过程的更新“帮助程序”应用程序。 假设您的应用程序是 ProcessA.exe,您的更新助手是 Updater.exe。 您的主程序将下载可执行文件的新副本,并以随机名称保存。 然后运行更新程序,该程序监视当前进程的终止。 当您的进程终止时,它会显示一个快速窗口,显示更新的状态,将新的可执行文件移动到旧的可执行文件的位置,然后重新启动该程序。
如果能够模拟 POSIX 文件系统语义并能够删除当前正在运行的进程磁盘映像并将其替换为新文件,那就更优雅了,但我不知道这在 Windows 上是否可行。 在 POSIX 系统上,您可以删除正在使用的文件,并且在关闭所有剩余文件句柄之前,该文件实际上不会被删除,但您可以重新使用该文件名。
您可能想查看 CodeProject 上写的一篇文章,其中 讨论此内容。 它还具有后续文章。
祝你好运!
Disclaimer: I don't use Windows, though I am familiar with its strange way of locking things.
In order to update your application while it is running, you'll likely need to have two processes: The executable itself, and an update “helper” application that will finish the update process. Let's say that your application is ProcessA.exe and your update helper is Updater.exe. Your main program will download a new copy of the executable, saving it with a random name. Then you run your updater program, which watches for the termination of your current process. When your process terminates, it displays a quick window showing the status of the update, moving the new executable into the place of the old one, and then restarting that program.
It'd be more elegant to be able to emulate POSIX filesystem semantics and be able to delete the currently-running process disk image and replace it with a new file, but I don't know if that is even possible on Windows. On a POSIX system, you can delete an in-use file and it won't actually be deleted until any remaining file handles are closed, though you can then reuse the filename.
You might want to check out an article written at CodeProject that talks about this. It also has a follow-up article.
Good luck!
更新时程序是否需要保持运行?
通常,要更新正在运行的程序,您可以将要替换的任何文件复制到临时文件夹中。 然后关闭旧实例,将其删除并将新文件移至正确的位置,然后重新启动它。
这可以最大限度地减少应用程序的停机时间,因为最长的部分通常是副本,并且如果临时文件夹位于同一逻辑驱动器上,文件移动速度非常快。
Does the program need to keep running while updating?
Typically to update a program which is running you would copy over any of the files that are to be replaced to a temporary folder. Then shut down the old instance, delete it and move the new files over to the correct locations then re-launch it.
This allows for minimal down time of the application since the longest part is usually the copy and the file move is very fast if the temporary folder is on the same logical drive.