You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何修改.exe文件变量?C#生成含自定义值的单文件便携.exe

嘿,这个需求我刚好折腾过,给你两个靠谱的单文件方案,完全不用额外配置文件,都是直接修改便携exe的内容来嵌入用户输入的值:

方案一:嵌入便携exe为资源,替换占位符

这个方案简单易上手,核心思路是先在便携exe里留一个固定的占位符,主程序把便携exe当资源读出来,找到占位符后替换成用户输入的值,再写出新的exe。

步骤1:给便携项目设置占位符

在你的便携项目里,定义一个固定长度的占位符字符串,用它初始化一个公开的静态变量——这样编译后这个占位符会明确出现在exe的字节里,方便后续替换:

namespace PortableApp
{
    class Program
    {
        // 这里可以根据需求调整占位符的长度,比如留30个字符位
        private const string InputPlaceholder = "------------------------------";
        public static string UserInputValue = InputPlaceholder;

        static void Main(string[] args)
        {
            Console.WriteLine($"你输入的值是:{UserInputValue}");
            Console.WriteLine("按任意键退出...");
            Console.ReadKey();
        }
    }
}

编译这个便携项目,得到PortableApp.exe

步骤2:主程序嵌入便携exe资源

在主项目里:

  • 右键项目 → 添加 → 现有项,找到刚才编译好的PortableApp.exe
  • 选中这个exe,在属性面板里把“生成操作”改成嵌入的资源

步骤3:主程序替换占位符并生成新exe

主程序里读取嵌入的exe字节,找到占位符的位置,替换成用户输入的内容,最后写出新的单文件exe:

using System;
using System.IO;
using System.Reflection;
using System.Text;
using System.Windows.Forms;

namespace MainApp
{
    public partial class MainForm : Form
    {
        public MainForm()
        {
            InitializeComponent();
        }

        private void btnGenerate_Click(object sender, EventArgs e)
        {
            string userInput = txtUserInput.Text.Trim();
            if (string.IsNullOrEmpty(userInput))
            {
                MessageBox.Show("请输入要嵌入的值");
                return;
            }

            // 读取嵌入的便携exe资源(注意资源名格式:主项目名.便携exe文件名)
            var assembly = Assembly.GetExecutingAssembly();
            var resourceName = "MainApp.PortableApp.exe";
            using (var stream = assembly.GetManifestResourceStream(resourceName))
            {
                if (stream == null)
                {
                    MessageBox.Show("找不到嵌入的便携程序资源,请检查资源名是否正确");
                    return;
                }

                byte[] exeBytes = new byte[stream.Length];
                stream.Read(exeBytes, 0, exeBytes.Length);

                // 准备占位符和用户输入的字节数组(编码要统一,这里用UTF-8)
                string placeholder = "------------------------------";
                byte[] placeholderBytes = Encoding.UTF8.GetBytes(placeholder);
                byte[] inputBytes = Encoding.UTF8.GetBytes(userInput);

                // 处理输入长度:超过占位符就截断,不足就补空格
                if (inputBytes.Length > placeholderBytes.Length)
                {
                    Array.Resize(ref inputBytes, placeholderBytes.Length);
                }
                else if (inputBytes.Length < placeholderBytes.Length)
                {
                    var tempBytes = new byte[placeholderBytes.Length];
                    Array.Copy(inputBytes, tempBytes, inputBytes.Length);
                    Array.Fill(tempBytes, (byte)' ', inputBytes.Length, placeholderBytes.Length - inputBytes.Length);
                    inputBytes = tempBytes;
                }

                // 在exe字节里查找占位符的位置
                int placeholderIndex = FindByteSequence(exeBytes, placeholderBytes);
                if (placeholderIndex == -1)
                {
                    MessageBox.Show("未找到占位符,请检查便携项目的占位符是否正确");
                    return;
                }

                // 替换占位符为用户输入的字节
                Array.Copy(inputBytes, 0, exeBytes, placeholderIndex, inputBytes.Length);

                // 写出生成的exe到桌面
                string outputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "MyPortableTool.exe");
                File.WriteAllBytes(outputPath, exeBytes);

                MessageBox.Show($"便携程序已生成:\n{outputPath}");
            }
        }

        // 辅助方法:在字节数组里查找目标字节序列的位置
        private int FindByteSequence(byte[] source, byte[] target)
        {
            for (int i = 0; i <= source.Length - target.Length; i++)
            {
                bool isMatch = true;
                for (int j = 0; j < target.Length; j++)
                {
                    if (source[i + j] != target[j])
                    {
                        isMatch = false;
                        break;
                    }
                }
                if (isMatch) return i;
            }
            return -1;
        }
    }
}
方案二:用Mono.Cecil修改IL代码(更灵活)

如果需要支持任意长度的输入,或者不想受占位符长度限制,可以用Mono.Cecil这个库直接修改便携exe的IL代码,把静态变量的初始值改成用户输入的内容。

步骤1:安装Mono.Cecil

在主项目的NuGet包管理器里搜索并安装Mono.Cecil

步骤2:主程序修改便携exe的字段值

using System;
using System.IO;
using System.Windows.Forms;
using Mono.Cecil;

namespace MainApp
{
    public partial class MainForm : Form
    {
        private void btnGenerateAdvanced_Click(object sender, EventArgs e)
        {
            string userInput = txtUserInput.Text.Trim();
            if (string.IsNullOrEmpty(userInput))
            {
                MessageBox.Show("请输入要嵌入的值");
                return;
            }

            // 先把嵌入的便携exe临时写到磁盘(因为Mono.Cecil需要读取文件)
            var assembly = Assembly.GetExecutingAssembly();
            var resourceName = "MainApp.PortableApp.exe";
            string tempExePath = Path.Combine(Path.GetTempPath(), "TempPortable.exe");
            using (var stream = assembly.GetManifestResourceStream(resourceName))
            using (var fs = new FileStream(tempExePath, FileMode.Create))
            {
                stream.CopyTo(fs);
            }

            // 读取便携exe的程序集定义
            var assemblyDef = AssemblyDefinition.ReadAssembly(tempExePath);
            // 找到目标类型(便携项目里的Program类)
            var programType = assemblyDef.MainModule.Types.First(t => t.FullName == "PortableApp.Program");
            // 找到要修改的静态字段
            var userValueField = programType.Fields.First(f => f.Name == "UserInputValue");

            // 修改字段的初始值为用户输入的字符串
            userValueField.InitialValue = Encoding.UTF8.GetBytes(userInput);

            // 保存修改后的exe到桌面
            string outputPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Desktop), "MyPortableTool.exe");
            assemblyDef.Write(outputPath);

            // 删除临时文件
            File.Delete(tempExePath);

            MessageBox.Show($"便携程序已生成:\n{outputPath}");
        }
    }
}
注意事项
  • 方案一要注意占位符的长度,尽量预留足够的空间,避免截断用户输入;如果必须支持超长输入,可以考虑在便携exe里预留一块固定大小的字节区域,用结构体存储长度和内容。
  • 方案二如果便携exe是强名称签名的,修改后需要重新签名,否则生成的exe会无法运行。
  • 两种方案生成的exe都是单文件,直接拷贝到其他电脑就能运行,不需要额外文件。

内容的提问来源于stack exchange,提问作者Kreator

火山引擎 最新活动