惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

博客园 - 叶小钗
雷峰网
雷峰网
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Martin Fowler
Martin Fowler
MyScale Blog
MyScale Blog
博客园 - 聂微东
有赞技术团队
有赞技术团队
The Cloudflare Blog
T
Tailwind CSS Blog
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
月光博客
月光博客
T
The Blog of Author Tim Ferriss
D
Docker
L
LangChain Blog
Vercel News
Vercel News
C
Check Point Blog
博客园 - Franky
博客园 - 三生石上(FineUI控件)
Recent Announcements
Recent Announcements
H
Hackread – Cybersecurity News, Data Breaches, AI and More
量子位
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
V
V2EX
人人都是产品经理
人人都是产品经理

博客园 - AndrewCheng

TableAdapter和多组数据结果 让.Net验证控件与自定义验证合作无间 把你的应用程序添加到IE工具栏 - AndrewCheng - 博客园 拦截并分发运行时动态创建控件的事件 程序设计中的调试方法总结 从SQL Server 数据库转换为Access注意事项 昆明喷码机耗材科技有限公司 软件版本后缀所代表的含义 论坛网址 工作格言 加密 web.config - AndrewCheng - 博客园 C#正则表达式 - AndrewCheng - 博客园 论坛等级分类 - AndrewCheng - 博客园 SQL Server和Oracle的常用函数对比 屏蔽IE浏览器javasccipt的方法 VS2005断开VSS后再次连接上VSS时注意事项 asp.net2.0页面按钮点击无效的解决办法 delegate与event示例程序 C#实现的正余玄函数图象
即时查看程序输出的VS插件
AndrewCheng · 2008-12-31 · via 博客园 - AndrewCheng

 假设你正在会议室里演示即时编译的程序。有什么最佳的方式来进行演示呢?

你还在往代码窗口里输入代码?这样,你得依赖听众的想像力 ―― 他们在脑海中构造这个程序是怎样运行的。此外,你还得依赖他们相信你的代码真的像和你所说的一样运作。

      或者你要不停的运行你的代码,这样程序的输出窗口会弹出,然后听众可以看到代码的实际工作情况?这是有风险的,因为每次切换代码和输出窗口会中断流程。并且你得依赖听众会记得他们每次看到的输出信息所对应的代码。

我想这是一个可以被技术所解决的问题!我为Visual Studio 2008 写了一个小插件。它关注的是当前文本缓冲中所包含的代码,并在后台编译它,然后在前端窗口中显示输出。这一系列动作大概每2秒钟重复一次。你甚至不需要保存再重新编译代码就能看到输出 了。这个插件特意制做成独立的控制台程序因为它用不着输入。这是一幅截图:

 源代码很小而且简单易懂,并且可以从上面的链接中获得。

      有两个关键的地方。第一是要使用多线程进行处理。我想让源代码在后台线程中编译因此它并不会影响Visual Studio UI. 但是要获取当前缓冲的文本你必须使用UI线程,而且要显示输出你也得使用它。我使用System.Timers.Timer在后台线程触发它的事件,同时为需要UI线程的任务调用form.Invoke(…)。

      我还使用了“non-AutoReset”计时器。我让它获取源码并进行编译+运行+显示,暂停2秒后,再去获取源码并进行编译+运行+显示,如此循环。换句话说,计时器的时间间隔必须设为2 秒在处理完先前的定时事件之后。

''' <summary>

''' OnTimer 处理non-autoreset计时器信号。它运行在后台线程中。从当前的缓冲区中获取

''' 源代码并编译它,然后显示输出。

''' </summary>

''' <remarks></remarks>

Sub OnTimer() Handles t.Elapsed

    Try

        Dim oldsrc = src

                '我们在后台线程中。但只能从 UI 线程获取源...

        '此委托将获取源代码并将它存储在"src"字段中

        f.Invoke(New Action(AddressOf GetSource))

        If src <> oldsrc Then

            Dim oldoutput = output

            ' 在后台编译并运行

            output = CompileAndRun(src)

            If output <> "" OrElse oldoutput = "" Then

                ' 在屏幕上显示输出必须在UI线程中完成

                ' 此委托从"output"字段中得到要输出的内容并显示它

                f.Invoke(New Action(AddressOf ShowOutput))

            End If

        End If

    Finally

        t.Start()

    End Try

End Sub

       另一个关键时刻与如何运行代码并捕获它的输出有关。在“My”命名空间中,VB在这方面有很多有用的函数。我的主要问题是合理的处理异常不留任何遗漏。(注意:获取临时文件名的代码并不完全准确:事实上你在某个状态之前得到一个未使用的临时文件名并不意味着这个文件名会一直不被使用;同样,它也并不意味着添加了“.vb”扩展名的临时文件名会一直不被使用。但是把这种情况处理的更准确看直来并不十分划算;不管怎样,在异常处理中我们将所有的问题恢复到正常状态。)

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

        ' 准备编译

        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

        ' 编译

        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 ""

        ' 运行

        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

        ' 我们可以巧妙的关闭VBC进程

        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

        ' 我们可以巧妙的关闭EXE

        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

        ' 删除剩余的文件

        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