Wah On Terra .NET: Martin Granell's Blog

Attacking the scary bits in .NET

Home Contact Syndicate this Site (RSS 2.0) Syndicate this Site (Atom) Login
  29 Posts :: 0 Stories :: 68 Comments :: 3210 Trackbacks

Archives

Post Categories

Readify Blogs


This post and comments can now be found on my new blog here

One of the “nice” things about the unix shell is the ability to create a script file that contains the program that will run the script at the top of the file. This allows all kinds of extensibility in the shell, and the ability to plug in your favourite script interpreter. This post shows how to create the same effect for .NET 1.1 on windows.

I was just reading Ken Schaefer’s blog about using Windows Scripting Host, and it reminded me of some old code I had written that was a self-compiling VB.NET WSF script. An example script is below:

' vbc /r:System.Windows.Forms.dll
'<job id="IncludeExample"><script language="JScript">var WshShell = WScript.CreateObject("WScript.Shell");var filespec = WshShell.ExpandEnvironmentStrings("%TEMP%")+"\\" +WScript.ScriptName+".exe";var fso=WScript.CreateObject("Scripting.FileSystemObject");if (fso.FileExists(filespec)) fso.DeleteFile(filespec);var ts=fso.OpenTextFile(WScript.ScriptFullName, 1);var comp = ts.ReadLine();comp=comp.substring(comp.indexOf(" ")+1);ts.Close();var f=fso.GetFolder(WshShell.ExpandEnvironmentStrings("%windir%\\Microsoft.NET\\Framework\\"));if (!f) WScript.Echo("You must have the .NET Runtime installed");var netVersion="";fc = new Enumerator(f.SubFolders);for (;!fc.atEnd(); fc.moveNext()){if (fc.item().name > netVersion)netVersion = fc.item().Path;}oExec = WshShell.Exec(netVersion + "\\" + comp + " /out:"+filespec+" /target:exe /debug "+WScript.ScriptFullName);while (oExec.Status == 0){if (!oExec.StdOut.AtEndOfStream){WScript.Echo(oExec.StdOut.ReadAll());}WScript.Sleep(100);};if (!oExec.StdOut.AtEndOfStream){WScript.Echo(oExec.StdOut.ReadAll());}if (!fso.FileExists(filespec)) WScript.Quit(1);oExec = WshShell.Exec(filespec);while (oExec.Status == 0){if (!oExec.StdOut.AtEndOfStream){WScript.Echo(oExec.StdOut.ReadAll());}if (!oExec.StdErr.AtEndOfStream){WScript.Echo(oExec.StdErr.ReadAll());}WScript.Sleep(100);};if (!oExec.StdOut.AtEndOfStream){WScript.Echo(oExec.StdOut.ReadAll());}if (!oExec.StdErr.AtEndOfStream){WScript.Echo(oExec.StdErr.ReadAll());}</script></job>
'
' A "Hello World!" program in Visual Basic.
Imports System
Imports Microsoft.VisualBasic
Imports System.Windows.Forms
Module Hello
   Sub Main()
        ' Console class from .NET
        Console.Write("To the console...")
   

        ' Intrinsic VB compatible code
        MsgBox("Hello World!")   ' Display message on computer screen.   

        ' Example of using the shell object
        Dim shell as Object = CreateObject("WScript.Shell")
        shell.Popup("hello2")     

        ' Example of using a referenced assembly
        MessageBox.Show("This is using System.Windows.Forms")
    End Sub
End Module

The magic in this script comes from the second line, which contains a valid WSF fragment. It is also VB.NET commented, so that the file is a valid VB.NET file. The code searches for the latest version of the .NET runtime, reads the first line of itself for the compiler and any additional arguments (e.g. references to other assemblies), compiles the .exe into the TEMP directory, and then runs the executable, making sure to copy standard out and error out into the console window.

To run the example, copy it into a file named foo.wsf, and run:

cscript foo.wsf

If you wanted to use the c# compiler instead, it would be a small matter of changing the compiler in the first line, and making the code compatible (of course you couldn’t use the late binding support available within VB.NET). Now you have the full power of .NET without having to run the compiler manually…

 

posted on Tuesday, August 09, 2005 11:27 AM