This is, from what I know, the lest code you will need to write, to make PowerShell create a exe-file that do what you want it to do. It uses the VB compiler that is installed together with the .NET Framework together with the VB-specific CreateCompiler function, also installed with the .NET Framework.
Before you run it, make sure that the folder you write your exe to really exists. In my case, C:\temp.
First, let’s load an assembly. Remember, no line breaks here.
#Load the Visual Basic assembly go get the code #that creates a compiler. [System.Reflection.Assembly]::Load("Microsoft.VisualBasic, Version=184.108.40.206, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a")
Then we create the objects we need. The second line will cause some problems. The object returned by the CreateCompiler function has a method that isn’t exposed, so we can’t call it directly. Also note that we are specifying an output path here.
$VB = New-Object Microsoft.VisualBasic.VBCodeProvider $Compiler = $VB.CreateCompiler() $Params = New-Object System.CodeDom.Compiler.CompilerParameters $Params.GenerateExecutable = $true $Params.OutputAssembly = "C:\temp\myprogram.exe"
This is the code I want in my exe:
[String]$S = "Imports System`n" $S = $S + "Module MyModule`n" $S = $S + "Sub Main(ByVal Args() As String)`n" $S = $S + "Console.WriteLine(`"Hello! Press Enter!`")`n" $S = $S + "Console.ReadLine()`n" $S = $S + "End Sub`n" $S = $S + "End Module`n"
Now, I must use reflection to call the unexposed method. In C# or VB, this is not a problem because we can use interfaces. PowerShell can’t. Since the method expects two arguments, I will create an array with the two elements. Then, the Invoke method takes the object where the function resides and the arguments. Typing is important so that the correct function overload will be found.
#Extract the method "CompileAssemblyFromSource". (No line break here!) $CompileMethod= [System.CodeDom.Compiler.ICodeCompiler].GetMethod("CompileAssemblyFromSource") #We must pass in the parameters and the source #when calling. $Args = New-Object System.Object 2 $Args = [System.CodeDom.Compiler.CompilerParameters]$Params $Args = $S $Res = $CompileMethod.Invoke($Compiler, $Args); $VB.Dispose()
Now, you have your own executable file that greets you when you start it!