將PowerShell腳本編譯成EXE

Powergui中有個工具,能夠將PowerShell腳本轉換成獨立的可執行程序EXE。因此,我想寫一個PowerShell函數,可以將一個Ps1腳本文件轉換成同名的可執行文件。shell

知識點分析

  • 關鍵應當使用到.Net動態編譯類Microsoft.CSharp.CSharpCodeProvider。在內存中編譯,輸出爲可執行程序EXE。c#

  • 編譯不經過時,輸出編譯錯誤信息,包含行號和列號;編譯經過時,輸出應用程序路徑。異步

  • 將要編譯的腳本做爲Resource文件嵌入到目標應用程序中。ide

  • 爲了確保最大的兼容性,不適用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'"
     }
}


用法爲ui

Convert-PS1ToExe -ScriptFile .\second.ps1spa

相關文章
相關標籤/搜索