本文目录
Powergui中有个工具,可以将PowerShell脚本转换成独立的可执行程序EXE。所以,我想写一个PowerShell函数,能够将一个Ps1脚本文件转换成同名的可执行文件。
知识点分析
- 关键应当使用到.Net动态编译类Microsoft.CSharp.CSharpCodeProvider。在内存中编译,输出为可执行程序EXE。
- 编译不通过时,输出编译错误信息,包含行号和列号;编译通过时,输出应用程序路径。
- 将要编译的脚本作为Resource文件嵌入到目标应用程序中。
- 为了确保最大的兼容性,不适用c#执行PowerShell脚本,直接使用Process打开PowerShell.exe ,将脚本文件存到临时目录传递过去运行。
- 将Process运行过程中产生的标准输出,异步重定向应用程序。
源脚本(Convert-PS1ToExe.ps1)
function Convert-PS1ToExe { param( [Parameter(Mandatory=$true)] [ValidateScript({$true})] [ValidateNotNullOrEmpty()] [IO.FileInfo]$ScriptFile ) if( -not $ScriptFile.Exists) { Write-Warning "$ScriptFile not exits." return } [string]$csharpCode = @' using System; using System.IO; using System.Reflection; using System.Diagnostics; namespace LoadXmlTestConsole { public class ConsoleWriter { private static void Proc_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e) { Process pro = sender as Process; Console.WriteLine(e.Data); } static void Main(string[] args) { // Set title of console Console.Title = "Powered by PSTips.Net"; // read script from resource Assembly ase = Assembly.GetExecutingAssembly(); string scriptName = ase.GetManifestResourceNames()[0]; string scriptContent = string.Empty; using (Stream stream = ase.GetManifestResourceStream(scriptName)) using (StreamReader reader = new StreamReader(stream)) { scriptContent = reader.ReadToEnd(); } string scriptFile = Environment.ExpandEnvironmentVariables(string.Format("%temp%\\{0}", scriptName)); try { // output script file to temp path File.WriteAllText(scriptFile, scriptContent); ProcessStartInfo proInfo = new ProcessStartInfo(); proInfo.FileName = "PowerShell.exe"; proInfo.CreateNoWindow = true; proInfo.RedirectStandardOutput = true; proInfo.UseShellExecute = false; proInfo.Arguments = string.Format(" -File {0}",scriptFile); var proc = Process.Start(proInfo); proc.OutputDataReceived += Proc_OutputDataReceived; proc.BeginOutputReadLine(); proc.WaitForExit(); Console.WriteLine("Hit any key to continue..."); Console.ReadKey(); } catch (Exception ex) { Console.WriteLine("Hit Exception: {0}", ex.Message); } finally { // delete temp file if (File.Exists(scriptFile)) { File.Delete(scriptFile); } } } } } '@ # $providerDict $providerDict = New-Object 'System.Collections.Generic.Dictionary[[string],[string]]' $providerDict.Add('CompilerVersion','v4.0') $codeCompiler = [Microsoft.CSharp.CSharpCodeProvider]$providerDict # Create the optional compiler parameters $compilerParameters = New-Object 'System.CodeDom.Compiler.CompilerParameters' $compilerParameters.GenerateExecutable = $true $compilerParameters.GenerateInMemory = $true $compilerParameters.WarningLevel = 3 $compilerParameters.TreatWarningsAsErrors = $false $compilerParameters.CompilerOptions = '/optimize' $outputExe = Join-Path $ScriptFile.Directory "$($ScriptFile.BaseName).exe" $compilerParameters.OutputAssembly = $outputExe $compilerParameters.EmbeddedResources.Add($ScriptFile.FullName) > $null $compilerParameters.ReferencedAssemblies.Add( [System.Diagnostics.Process].Assembly.Location ) > $null # Compile Assembly $compilerResult = $codeCompiler.CompileAssemblyFromSource($compilerParameters,$csharpCode) # Print compiler errors if($compilerResult.Errors.HasErrors) { Write-Host 'Compile faield. See error message as below:' -ForegroundColor Red $compilerResult.Errors | foreach { Write-Warning ('{0},[{1},{2}],{3}' -f $_.ErrorNumber,$_.Line,$_.Column,$_.ErrorText ) } } else { Write-Host 'Compile succeed.' -ForegroundColor Green "Output executable file to '$outputExe'" } }
测试
新建PowerShell脚本文件,命名为second.ps1,输入内容:
1..5 | foreach { Get-Random sleep -Milliseconds 500 } $date=get-date "Hello,now is $date"
在控制台中运行:
Convert-PS1ToExe -ScriptFile .\second.ps1
输出为:
双击运行second.exe,输出:
补充
当我写完脚本,准备名称成ps2exe时,发现有人早已捷足先登。
本文链接: https://www.pstips.net/convert-ps1toexe.html
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!
请尊重原作者和编辑的辛勤劳动,欢迎转载,并注明出处!
.ps1在Powershell控制台执行时会受到安全执行策略的限制,请问用上面的方法编译成.exe后,还受这种限制吗?
出于安全,是限制的。但是,你可以将脚本中嵌入的c#代码,第57行 proInfo.Arguments = string.Format(” -ExecutionPolicy Unrestricted -File {0}”,scriptFile),就可以跳过这个限制。
请问为什么总是提示not exists呢?明明存在呢。。。
你是测试文中的代码吗,遇到这样的问题吗?
试了下,成功了,只是那个什么另外一个 ps1 里面引用到一个 绝对路径 ,路径中含有中文。
编译显示成功,运行EXE的时候,显示脚本里面的中文路径为乱码?这是为什么? 求解答。
另外,脚本单独运行时没有问题的。是不是编译的时候,字符串的编码有点问题。
哎,不能用百度啊。找到强大的一个ps2exe,试了下好强大,最近才更新的。给楼主分享下。
https://gallery.technet.microsoft.com/PS2EXE-Convert-PowerShell-9e4e07f1
假设之前可以带外部参数的,转成EXE后,似乎不可以再传参数了。
这个实际上是用C#控制台执行Powershell的脚本,今天测试发现无法隐藏C#这个控制台,执行时会有两个控制台窗体。有无办法隐藏掉本身的窗体呢?
我这边执行时,只有一个窗口,而且PowerShell控制台本身的那个窗口被我用这段code给隐藏掉了。
你如果不想看到c#本身的控制台窗口,可以把脚本这样更改。
用winform程序代替windows控制台。
但是在winform窗体程序中本身,并不打开任何窗体,而是把PowerShell start 起来,这样肯定只有powershell一个窗口了。
运行完没有exe出来,也没有报错额。
运行完没有exe没报错,什么鬼
我也是,求解
测试了一下,当运行到ps1脚本中的pause时,无法在继续,有cls时也无法进行清屏。
这个怎么解决?
我也是
mark
请问一下执行的exe文件如何在运行结束后自动关闭? 不想要输入any key后才关闭。
删掉这句即可:
Console.WriteLine(“Hit any key to continue…”);
Console.ReadKey();
请问有参数的话也能按照powershell的参数方式传递吗?
请问,我写了一个用来重置域用户密码的PS脚本(需要交互),然后使用您的函数转成exe文件(没有任何报错)后,双击运行exe文件,弹出了一个窗口,然后没有任何其他动作。
请问这种情况要怎么弄呢?求指点。
您好
感谢您代码。
编译生成包含中文的脚本时,EXE运行出来会显示乱码。
比如 write-host “你好”
不知是否有解决方案呢?