LiveRun – a VS plugin to see the output of your program immediately


Say you’re demonstrating a compiler at a conference. What’s the best way to do it?


Should you just type in code in the code window? Doing this, you’re relying on the audience’s imagination — that they form a mental picture of how the program will behave. You’re also relying on their trust that your code really does what you say it does.


Or should you execute your code every minute or so, so the program’s output window pops up and the audience can see that the code really works? Here it’s risky because each time you switch it breaks the flow. And you’re relying on the audience to remember what the code each time they look at the output.


I think this is one of those problems that can be solved by technology! I wrote small plugin for Visual Studio 2008. It looks at what the current text buffer contains, compiles it in the background, and displays the output in a topmost window. It does this every two seconds or so. You don’t even need to save or recompile to see the output. It only makes sense for standalone console programs that don’t take input. Here’s a screenshot:


screenshot of LiveRun


 



 


The source code is small and straightforward, and available for download at the link above.


 


There were two “gotcha” moments. The first was to do with multi-threading. I wanted the source code to be compiled in a background thread so it wouldn’t interfere with the Visual Studio UI. But to grab the text of the current buffer you have to be in the UI thread, and also to display the output you have to be in the UI thread. I used a System.Timers.Timer, which fires its events in the background thread, and called form.Invoke(…) for any tasks that needed the UI thread.


I also used a “non-AutoReset” timer. I wanted it to get the source code and compile+run+display it, then pause for two seconds, then get the source code and compile+run+display it, then pause for two seconds, and so on. In other words the timer interval has to be two seconds after the end of handling the previous timer event.



”’ <summary>


”’ OnTimer handles the non-autoreset timer signal. It runs in a background thread. It gets the source


”’ code from the current buffer, and compiles it, and displays the output.


”’ </summary>


”’ <remarks></remarks>


Sub OnTimer() Handles t.Elapsed


    Try


        Dim oldsrc = src


        ‘ We’re in a background thread. But the source can only be obtained from the UI thread…


        ‘ This delegate will get the source and store it in the “src” field


        f.Invoke(New Action(AddressOf GetSource))


        If src <> oldsrc Then


            Dim oldoutput = output


            ‘ We want to compile-and-run in the background thread


            output = CompileAndRun(src)


            If output <> “” OrElse oldoutput = “” Then


                ‘ Displaying the output on-screen must be done in the UI thread.


                ‘ This delegate gets the content of the “output” field and displays it


                f.Invoke(New Action(AddressOf ShowOutput))


            End If


        End If


    Finally


        t.Start()


    End Try


End Sub


 


The other “gotcha” moment had to do with how to execute the code and capture its output. VB has very nice helper functions surrounding this, in the “My” namespace. My main concern was to recover from exceptions gracefully without leaving any mess. (Note: the code for getting a temporary filename isn’t quite correct: the mere fact that you got a temporary unused filename one statement ago does not mean that the filename will still be unused; nor does it mean that the filename with “.vb” appended to it will be unused. But doing it more correctly didn’t seem worth the bother; in any case, the exception handling means we’ll recover okay from problems.)


 



Function CompileAndRun(ByVal src As String) As String


    Dim fn_exe = “”


    Dim fn_src = “”


    Dim vbc As System.Diagnostics.Process = Nothing


    Dim exe As System.Diagnostics.Process = Nothing


    Try


        ‘ Prepare for compilation


        fn_src = My.Computer.FileSystem.GetTempFileName() & “.vb”


        My.Computer.FileSystem.WriteAllText(fn_src, src, False)


        fn_exe = My.Computer.FileSystem.GetTempFileName() & “.exe”


        Dim framework = Environment.ExpandEnvironmentVariables(“%windir%\Microsoft.Net\Framework”)


        Dim latest_framework = (From d In My.Computer.FileSystem.GetDirectories(framework) Where d Like “*\v*” Select d).Last


 


        ‘ Compile it


        vbc = System.Diagnostics.Process.Start(New ProcessStartInfo _


                            With {.CreateNoWindow = True, _


                                  .UseShellExecute = False, _


                                  .FileName = latest_framework & “\vbc.exe”, _


                                  .Arguments = String.Format(“/out:””{0}”” /target:exe “”{1}”””, fn_exe, fn_src)})


        Dim vbc_done = vbc.WaitForExit(3000)


        If Not vbc_done Then Return “”


        If vbc.ExitCode <> 0 Then Return “”


 


        ‘ Execute it


        Dim pinfo = New ProcessStartInfo With {.CreateNoWindow = True, _


                                               .UseShellExecute = False, _


                                               .FileName = fn_exe, _


                                               .RedirectStandardOutput = True}


        exe = New System.Diagnostics.Process With {.StartInfo = pinfo}


        exe.Start()


        Dim output = exe.StandardOutput.ReadToEnd


        Dim exe_done = exe.WaitForExit(3000)


        If Not exe_done Then Return “”


        Return output


    Finally


        ‘ Close the VBC process as neatly as we can


        If vbc IsNot Nothing Then


            If Not vbc.HasExited Then


                Try : vbc.Kill() : Catch ex As Exception : End Try


                Try : vbc.WaitForExit() : Catch ex As Exception : End Try


            End If


            Try : vbc.Close() : Catch ex As Exception : End Try


            vbc = Nothing


        End If


 


        ‘ Close the EXE as neatly as we can


        If exe IsNot Nothing Then


            If Not exe.HasExited Then


                Try : exe.Kill() : Catch ex As Exception : End Try


                Try : exe.WaitForExit() : Catch ex As Exception : End Try


            End If


            Try : exe.Close() : Catch ex As Exception : End Try


            exe = Nothing


        End If


 


        ‘ Delete leftover files


        Try : My.Computer.FileSystem.DeleteFile(fn_exe) : Catch ex As Exception : End Try


        Try : My.Computer.FileSystem.DeleteFile(fn_src) : Catch ex As Exception : End Try


    End Try


End Function


 


As always, I love to hear suggestions and bugfixes and code improvements and comments!

Comments (1)

  1. DonXML says:

    Nice idea, but it would be even cooler if you used a MSUnit project type and displayed the test results (and Console if you had to).  It would encourage folks to write unit tests.

    I prefer using MSUnit (or any xUnit framework) over console projects to demo code.