如何修改.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




