对Ingo Karstein的出色脚本的增强,增加了GUI支持。GUI输出和输入通过一个开关激活,生成真正的Windows可执行文件。仅编译Powershell 5.x兼容脚本。带有可选的图形前端Win-PS2EXE。
模块版本。
您可以在以下位置找到基于脚本的版本:https://github.com/MScholtes/TechNet-Gallery
作者:Markus Scholtes
版本:1.0.18
日期:2026-06-06
PS C:\> Install-Module ps2exe
或从此处下载:https://www.powershellgallery.com/packages/ps2exe/
Invoke-ps2exe .\source.ps1 .\target.exe
或
ps2exe .\source.ps1 .\target.exe
将"source.ps1"编译为可执行文件target.exe(如果省略".\target.exe",则输出写入".\source.exe")。
或使用以下命令启动Win-PS2EXE图形前端:
Win-PS2EXE
ps2exe [-inputFile] '<file_name>' [[-outputFile] '<file_name>']
[-prepareDebug] [-x86|-x64] [-lcid <id>] [-STA|-MTA] [-noConsole] [-conHost] [-UNICODEEncoding]
[-credentialGUI] [-iconFile '<filename>'] [-$embedFiles <hashtable>] [-title '<title>'] [-description '<description>']
[-company '<company>'] [-product '<product>'] [-copyright '<copyright>'] [-trademark '<trademark>']
[-version '<version>'] [-configFile] [-noOutput] [-noError] [-noVisualStyles] [-exitOnCancel]
[-DPIAware] [-requireAdmin] [-supportOS] [-virtualize] [-longPaths]
inputFile = 要转换为可执行文件的Powershell脚本(文件必须是UTF8或UTF16编码)
outputFile = 目标可执行文件名或文件夹,默认为inputFile,扩展名为'.exe'
prepareDebug = 创建有助于调试的信息
x86或x64 = 仅为32位或64位运行时编译
lcid = 编译后可执行文件的区域设置ID。如果未指定,则使用当前用户区域设置
STA或MTA = '单线程单元'或'多线程单元'模式
noConsole = 生成的可执行文件将是没有控制台窗口的Windows窗体应用程序
conHost = 强制使用conhost作为控制台而不是Windows终端(禁用重定向)
UNICODEEncoding = 在控制台模式下将输出编码为UNICODE
credentialGUI = 在控制台模式下使用GUI提示输入凭据
iconFile = 编译后可执行文件的图标文件名
embedFiles = 要嵌入的文件,以哈希表形式给出,将提取到哈希表的键,源文件名必须唯一
(例如 -embedFiles @{'目标文件路径'='源文件路径'})
title = 标题信息(显示在Windows资源管理器属性对话框的详细信息选项卡中)
description = 描述信息(不显示,但嵌入可执行文件中)
company = 公司信息(不显示,但嵌入可执行文件中)
product = 产品信息(显示在Windows资源管理器属性对话框的详细信息选项卡中)
copyright = 版权信息(显示在Windows资源管理器属性对话框的详细信息选项卡中)
trademark = 商标信息(显示在Windows资源管理器属性对话框的详细信息选项卡中)
version = 版本信息(显示在Windows资源管理器属性对话框的详细信息选项卡中)
configFile = 写入配置文件(<输出文件>.exe.config)
noOutput = 生成的可执行文件将不产生标准输出(包括详细信息和信息通道)
noError = 生成的可执行文件将不产生错误输出(包括警告和调试通道)
noVisualStyles = 禁用生成的Windows GUI应用程序的视觉样式(仅与 -noConsole 一起使用)
exitOnCancel = 当在Read-Host输入框中选择取消或"X"时退出程序(仅与 -noConsole 一起使用)
DPIAware = 如果显示缩放已激活,GUI控件将尽可能缩放(仅与 -noConsole 一起使用)
requireAdmin = 如果UAC已启用,编译后的可执行文件仅在提升的上下文中运行(如果需要,会显示UAC对话框)
supportOS = 使用最新Windows版本的功能(执行 [Environment]::OSVersion 查看差异)
virtualize = 启用应用程序虚拟化(强制使用x86运行时)
longPaths = 如果操作系统启用了长路径(>260个字符),则启用长路径(仅适用于Windows 10)
生成的可执行文件具有以下保留参数:
-? [<MODIFIER>] 可执行文件内部脚本的Powershell帮助文本。可选参数组合
"-? -detailed", "-? -examples" 或 "-? -full" 可用于获取相应的帮助文本。
-debug 强制对可执行文件进行调试。它调用 "System.Diagnostics.Debugger.Launch()"。
-extract:<FILENAME> 提取可执行文件内部的Powershell脚本并保存为FILENAME。
脚本将不会被执行。
-wait 在脚本执行结束时显示 "Hit any key to exit..." 并等待按键。
-end 所有后续选项将传递给可执行文件内部的脚本。
所有前置选项将由可执行文件本身使用,并不会传递给脚本。
PS2EXE可以与Powershell Core一起使用。只需按照上述描述在Powershell Core中安装PS2EXE模块即可。但由于.Net Core不附带编译器,因此会使用.Net Framework的编译器(Windows包含.Net Framework和Powershell 5.1)。
因此,PS2EXE只能编译Powershell 5.1兼容的脚本,并生成.Net 4.x二进制文件,但仍可直接在每个受支持的Windows操作系统上使用,无需依赖项。
使用参数 -embedFiles 后跟一个包含文件路径的哈希表,这些文件将嵌入到编译后的可执行文件中。 在可执行文件启动时,这些文件将被写入磁盘到指定路径,例如 -embedFiles @{'目标文件路径1'='源文件路径1';'目标文件路径2'='源文件路径2'}。 源文件名必须唯一。允许绝对路径和相对路径。对于目标路径,以 '.\' 开头的相对路径被解释为相对于可执行文件的路径,不加前导 '.\' 则相对于运行时的当前路径。 必要时,在启动时会自动创建目录。在目标路径中,cmd.exe 表示法中的环境变量如 %TEMP% 或 %APPDATA% 会在运行时展开。 如果创建嵌入文件之一失败,将立即停止编译后的可执行文件的执行。
基本的输入/输出命令已用C#重写以适应PS2EXE。未实现的是控制台模式下的 Write-Progress(工作量太大)和 Start-Transcript/Stop-Transcript(微软没有提供合适的参考实现)。
默认情况下,Powershell中命令的输出是逐行格式化的(作为一个字符串数组)。当您的命令生成10行输出,并且您使用GUI输出时,将出现10个消息框,每个等待点击确定。要防止这种情况,请将您的命令通过管道传递给 Out-String 命令符。这会将输出转换为一个包含10行的字符串数组,所有输出将显示在一个消息框中(例如:dir C:\ | Out-String)。
PS2EXE可以创建与生成的可执行文件同名且加上 ".config" 的配置文件。在大多数情况下,这些配置文件不是必需的,它们是一个清单,指示使用哪个.Net Framework版本。由于您通常会使用实际版本的.Net Framework,请尝试在没有配置文件的情况下运行可执行文件。
编译后的脚本处理参数的方式与原始脚本相同。一个限制来自Windows环境:对于所有可执行文件,所有参数的类型都是STRING,如果您的参数类型没有隐式转换,则必须在脚本中显式转换。您甚至可以通过管道向可执行文件传递内容,但具有相同的限制(所有传递的值类型都是STRING)。
永远不要将密码存储在编译后的脚本中!用户可以使用 -extract 参数轻松反编译脚本。例如
Output.exe -extract:C:\Output.ps1
将反编译存储在Output.exe中的脚本。请注意:脚本(故意地)以明文形式存储在可执行文件中!
由于PS2EXE将脚本转换为可执行文件,因此脚本相关的变量不再可用。变量 $MyInvocation 被设置为与脚本不同的值。
特别是变量 $PSScriptRoot 为空 - 您可以使用 $ScriptRoot 作为替代。
您可以使用以下代码行独立于是否编译来获取 $PSScriptRoot:
if (!$PSScriptRoot) { $PSScriptRoot = $ScriptRoot }
当在 -noConsole 模式下的脚本中打开外部窗口时(例如,用于 Get-Credential 或需要 cmd.exe shell 的命令),下一个窗口将在背景中打开。
原因是关闭外部窗口时,Windows 会尝试激活父窗口。由于编译后的脚本没有窗口,因此会激活编译后脚本的父窗口,通常是资源管理器或Powershell的窗口。
为了解决这个问题,$Host.UI.RawUI.FlushInputBuffer() 会打开一个不可见的窗口,该窗口可以被激活。随后调用 $Host.UI.RawUI.FlushInputBuffer() 会关闭此窗口(依此类推)。
以下示例将不再在背景中打开窗口,而不是像 "ipconfig | Out-String" 的单次调用那样:
$Host.UI.RawUI.FlushInputBuffer()
ipconfig | Out-String
$Host.UI.RawUI.FlushInputBuffer()