当前位置: 首页 > news >正文

Windows 上的应用程序在运行期间可以给自己改名(可以做 OTA 自我更新)

程序如何自己更新自己呢?你可能会想到启动一个新的程序或者脚本来更新自己。然而 Windows 操作系统允许一个应用程序在运行期间修改自己的名称甚至移动自己到另一个文件夹中。利用这一点,我们可以很简单直接地做程序的 OTA 自动更新。

本文将介绍示例程序运行期间改名并解释其原理。


本文内容

    • 在程序运行期间手工改名
    • 不止是 exe 文件,dll 文件也是可以改名的
    • 为什么 Windows 上的可执行程序可以在运行期间改名?
    • 编写一个程序在运行期间自动改名

在程序运行期间手工改名

我们写一个简单的程序。

简单的程序

将它运行起来,然后删除。我们会发现无法删除它。

无法删除程序

但是,我们却可以很轻松地在资源管理器中对它进行改名,甚至将它从一个文件夹中移动到另一个文件夹中。

已经成功改名

值得注意的是,你不能跨驱动器移动此文件。

不止是 exe 文件,dll 文件也是可以改名的

实际上,不止是 exe 文件,在 exe 程序运行期间,即使用到了某些 dll 文件,这些 dll 文件也是可以改名的。

当然,一个 exe 的运行不一定在启动期间就加载好了所有的 dll,所以如果你在 exe 启动之后,某个 dll 加载之前改了那个 dll 的名称,那么会出现找不到 dll 的情况,可能导致程序崩溃。

为什么 Windows 上的可执行程序可以在运行期间改名?

Windows 的文件系统由两个主要的表示结构:一个是目录信息,它保存有关文件的元数据(如文件名、大小、属性和时间戳);第二个是文件的数据链。

当运行程序加载一个程序集的时候,会为此程序集创建一个内存映射文件。为了优化性能,往往只有实际用到的部分才会被加入到内存映射文件中;当需要用到程序集文件中的某块数据时,Windows 操作系统就会将需要的部分加载到内存中。但是,内存映射文件只会锁定文件的数据部分,以保证文件文件的数据不会被其他的进程修改。

这里就是关键,内存映射文件只会锁定文件的数据部分,而不会锁住文件元数据信息。这意味着你可以随意修改这些元数据信息而不会影响程序的正常运行。这就包括你可以修改文件名,或者把程序从一个文件夹下移动到另一个文件夹去。

但是跨驱动器移动文件,就意味着需要在原来的驱动器下删除文件,而这个操作会影响到文件的数据部分,所以此操作不被允许。

编写一个程序在运行期间自动改名

一般来说,需要 OTA 更新的程序是客户端程序,所以实际上真正需要此代码的是客户端应用。以下代码中我使用 .NET Core 3.0 来编写一个给自己改名的 WPF 程序。

using System.Diagnostics;
using System.IO;
using System.Windows;

namespace Walterlv.Windows.Updater
{
    public partial class App : Application
    {
        protected override void OnStartup(StartupEventArgs e)
        {
            base.OnStartup(e);
            var fileName = Process.GetCurrentProcess().MainModule.FileName;
            var newFileName = Path.Combine(Path.GetDirectoryName(fileName), "OldUpdater.exe");
            File.Move(fileName, newFileName);
            // 省略的代码:将新下载下载的程序改名成 fileName。
        }
    }
}

于是,程序自己在运行后会改名。

程序已经自己改名

顺便的,以上代码仅适用于 .NET Framework 的桌面应用程序或者 .NET Core 3.0 的桌面应用程序。如果是 .NET Core 2.x,那么以上代码在获取到进程名称的时候可能是 dotnet.exe(已发布的 .NET Core 程序除外)。


参考资料

  • c# - Why does rename a loaded .net assembly work? - Stack Overflow
  • windows 7 - Why can I rename a running executable, but not delete it? - Super User
  • deployment - How can we overwrite EXE files while users are running them? - Stack Overflow

我的博客会首发于 https://blog.walterlv.com/,而 CSDN 会从其中精选发布,但是一旦发布了就很少更新。

如果在博客看到有任何不懂的内容,欢迎交流。我搭建了 dotnet 职业技术学院 欢迎大家加入。

知识共享许可协议

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:https://walterlv.blog.csdn.net/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请与我联系。

相关文章:

  • 为 WPF 程序添加 Windows 跳转列表的支持
  • 在 Windows 系统上降低 UAC 权限运行程序(从管理员权限降权到普通用户权限)
  • 专栏素材
  • Visual Studio 如何能够不进行编译就调试 .NET/C# 项目(用于解决大项目编译缓慢的问题)
  • 仅反射加载(ReflectionOnlyLoadFrom)的 .NET 程序集,如何反射获取它的 Attribute 元数据呢?
  • 全局或为单独的项目添加自定义的 NuGet 源
  • 电脑总是意外从睡眠状态唤醒,可以找出原因然后解决
  • 我收集的各种公有 NuGet 源
  • 制作一个极简的 .NET 客户端应用自安装或自更新程序
  • 在 MSBuild 编译项目时阻止输出所有的警告信息
  • 编写 MSBuild 内联编译任务(Task)用于获取当前编译环境下的所有编译目标(Target)
  • 如何在 csproj 中用 C# 代码写一个内联的编译任务 Task
  • 安装和运行 .NET Core 版本的 PowerShell
  • 让你的 Windows 应用程序在任意路径也能够直接通过文件名执行
  • 如何为你的 Windows 应用程序关联一种或多种文件类型
  • SegmentFault for Android 3.0 发布
  • Docker入门(二) - Dockerfile
  • express如何解决request entity too large问题
  • interface和setter,getter
  • Java 内存分配及垃圾回收机制初探
  • Mithril.js 入门介绍
  • Yii源码解读-服务定位器(Service Locator)
  • 编写符合Python风格的对象
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 技术:超级实用的电脑小技巧
  • 扑朔迷离的属性和特性【彻底弄清】
  • 浅谈web中前端模板引擎的使用
  • 网页视频流m3u8/ts视频下载
  • 文本多行溢出显示...之最后一行不到行尾的解决
  • 小程序开发之路(一)
  • 一个JAVA程序员成长之路分享
  • 一起参Ember.js讨论、问答社区。
  • ​​​​​​​Installing ROS on the Raspberry Pi
  • ​比特币大跌的 2 个原因
  • # Panda3d 碰撞检测系统介绍
  • #Java第九次作业--输入输出流和文件操作
  • #常见电池型号介绍 常见电池尺寸是多少【详解】
  • $Django python中使用redis, django中使用(封装了),redis开启事务(管道)
  • %check_box% in rails :coditions={:has_many , :through}
  • (2)(2.4) TerraRanger Tower/Tower EVO(360度)
  • (3)(3.2) MAVLink2数据包签名(安全)
  • (七)Java对象在Hibernate持久化层的状态
  • (一)搭建springboot+vue前后端分离项目--前端vue搭建
  • (转)Oracle 9i 数据库设计指引全集(1)
  • .NET WebClient 类下载部分文件会错误?可能是解压缩的锅
  • .NET 命令行参数包含应用程序路径吗?
  • .Net程序猿乐Android发展---(10)框架布局FrameLayout
  • .Net下C#针对Excel开发控件汇总(ClosedXML,EPPlus,NPOI)
  • /etc/fstab和/etc/mtab的区别
  • @SpringBootApplication 包含的三个注解及其含义
  • [.NET]桃源网络硬盘 v7.4
  • [1] 平面(Plane)图形的生成算法
  • [BROADCASTING]tensor的扩散机制
  • [C++ 从入门到精通] 12.重载运算符、赋值运算符重载、析构函数
  • [C++]:for循环for(int num : nums)