随笔 - 89, 评论 - 163, 引用 - 33

导航

关于

标签

每月存档

最新留言

广告

System.Diagnostics.Process: 重定向标准输入、输出和错误

[原文作者]:Lucian Wischik

[原文链接]:System.Diagnostics.Process: redirect StandardInput, StandardOutput, StandardError

    有时我们运行一个外部适用程序,向里面输入数据然后获取它的输出。这往往很容易发生这样的死锁:

     ' BAD CODE

     Using p As New System.Diagnostics.Process

     p.StartInfo.FileName = "cat"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardInput = True

     p.Start()

     p.StandardInput.Write("world" & vbCrLf & "hello")

    ' 这里将发生死锁,如果P写到输出接口的量达到12k

     p.StandardInput.Close()

     Dim op = p.StandardOutput.ReadToEnd()

     p.WaitForExit()

     p.Close()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     End Using

    这段程序将发生死锁,因为“Cat”首先从标准输入接口中读取,然后写到标准输出接口,最后再读取它,这样一直循环下去直到没有任何东西可读取。但是如果它的标准输出接口已经填满了,并且没有对象去读取,这样就不能继续往里面写东西,从而出现阻塞。

    这里的12k仅是一个随机数,我不会关注于它...

     ' BAD CODE

     Using p As New System.Diagnostics.Process

     p.StartInfo.FileName = "findstr"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardError = True

     p.Start()

    ' deadlock here if p needs to write more than 12k to StandardError

     Dim op = p.StandardOutput.ReadToEnd()

     Dim err = p.StandardError.ReadToEnd()

     p.WaitForExit()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     Console.WriteLine("ERROR:") : Console.WriteLine(err)

     End Using

     MSDN文档中说:你可以通过异步读取操作来避免这些依赖和潜在的死锁;或者你可以通过创建两个线程,让其中一个独立线程来读取输出流来避免死锁。因此我们将这样来做:

     使用线程来重定向就不会死锁

     '  GOOD CODE: 这里不会发生死锁.

     Using p As New Diagnostics.Process

     p.StartInfo.FileName = "sort"

     p.StartInfo.UseShellExecute = False

     p.StartInfo.RedirectStandardOutput = True

     p.StartInfo.RedirectStandardInput = True

     p.Start()

     Dim op = ""

     ' do NOT WaitForExit yet since that would introduce deadlocks.

      p.InputAndOutputToEnd("world" & vbCrLf & "hello", op, Nothing)

     p.WaitForExit()

     p.Close()

     Console.WriteLine("OUTPUT:") : Console.WriteLine(op)

     End Using

     ''' <summary>

     ''' InputAndOutputToEnd: a handy way to use redirected input/output/error on a p.

     ''' </summary>

     ''' <param name="p">The p to redirect. Must have UseShellExecute set to false.</param>

     ''' <param name="StandardInput">This string will be sent as input to the p. (must be Nothing if not StartInfo.RedirectStandardInput)</param>

     ''' <param name="StandardOutput">The p's output will be collected in this ByRef string. (must be Nothing if not StartInfo.RedirectStandardOutput)</param>

     ''' <param name="StandardError">The p's error will be collected in this ByRef string. (must be Nothing if not StartInfo.RedirectStandardError)</param>

     ''' <remarks>This function solves the deadlock problem mentioned at http://msdn.microsoft.com/en-us/library/system.diagnostics.p.standardoutput.aspx</remarks>  

     <Runtime.CompilerServices.Extension()> Sub InputAndOutputToEnd(ByVal p As Diagnostics.Process, ByVal StandardInput As String, ByRef StandardOutput As String, ByRef StandardError As String)

     If p Is Nothing Then Throw New ArgumentException("p must be non-null")

     ' Assume p has started. Alas there's no way to check.

      If p.StartInfo.UseShellExecute Then Throw New ArgumentException("Set StartInfo.UseShellExecute to false")

      If (p.StartInfo.RedirectStandardInput <> (StandardInput IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Input only when StartInfo.RedirectStandardInput")

      If (p.StartInfo.RedirectStandardOutput <> (StandardOutput IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Output only when StartInfo.RedirectStandardOutput")

     If (p.StartInfo.RedirectStandardError <> (StandardError IsNot Nothing)) Then Throw New ArgumentException("Provide a non-null Error only when StartInfo.RedirectStandardError")

 

     Dim outputData As New InputAndOutputToEndData

     Dim errorData As New InputAndOutputToEndData

 

     If p.StartInfo.RedirectStandardOutput Then

     outputData.Stream = p.StandardOutput

     outputData.Thread = New Threading.Thread(AddressOf InputAndOutputToEndProc)

     outputData.Thread.Start(outputData)

     End If

     If p.StartInfo.RedirectStandardError Then

     errorData.Stream = p.StandardError

     errorData.Thread = New Threading.Thread(AddressOf InputAndOutputToEndProc)

     errorData.Thread.Start(errorData)

     End If

 

     If p.StartInfo.RedirectStandardInput Then

     p.StandardInput.Write(StandardInput)

     p.StandardInput.Close()

    End If

'

    If p.StartInfo.RedirectStandardOutput Then outputData.Thread.Join() : StandardOutput = outputData.Output

    If p.StartInfo.RedirectStandardError Then errorData.Thread.Join() : StandardError = errorData.Output

    If outputData.Exception IsNot Nothing Then Throw outputData.Exception

    If errorData.Exception IsNot Nothing Then Throw errorData.Exception

    End Sub

    Private Class InputAndOutputToEndData

    Public Thread As Threading.Thread

    Public Stream As IO.StreamReader

    Public Output As String

    Public Exception As Exception

    End Class

    Private Sub InputAndOutputToEndProc(ByVal data_ As Object)

    Dim data = DirectCast(data_, InputAndOutputToEndData)

    Try : data.Output = data.Stream.ReadToEnd : Catch e As Exception : data.Exception = e : End Try

    End Sub

posted on 2009-02-20 14:19:00 by vbcti  评论(0) 阅读(3632)

PInvoke 和COM对象

 

   我偶尔会觉得有必要把COM的互操作和PInvoke结合起来。在某些场景中,用PInvoke的声明和方法会更容易编码一些。在这些场景中包括进COM对象,并且在签名上加上合适的Marshal标记也是合法的。

    最简单的完成这些场景的方法是有本地的签名只暴露IUnkown实例。在托管代码这边,用一个对象声明并且标记上MarshalAs(UnmanagedType.IUnknown)。例如:

[DllImport("SomeDll.dll")]

[return: MarshalAs(UnmanagedType.IUnknown)]

public static extern object GetSomeComObject();

     有一条需要记住,在这种场景中怎么处理ref这个关键字。在任何情况下,如果一个COM对象被当作来自于PInvoke的签名,CLR会假定他会去调用IUKnown::Release()。 相对的本地代码必须考虑这种情况,适当的对这个对象AddRef()。

    这已经包括了任何的场景,像上面的代码,COM对象返回的值是function [1].

posted on 2009-01-23 14:25:09 by vbcti  评论(0) 阅读(3902)

在调试时怎样禁止JIT优化

 

[原文作者]:Jaredpar

[原文链接]:Disabling JIT optimizations while debugging

 

     如果你正在调试一个托管应用程序,却发现不能查看任何局部变量或者函数参数的值,这是因为

     托管代码在编译时已经被优化了。下面的内容将告诉你怎样解决这个问题。我会教大家一种非常简单的小技巧来利用.ini文件禁止代码优化。它不需要你重新编译你的应用程序并且只要几秒钟就能实现。

     创建一个.ini文件并添加如下内容:

     [.NET Framework Debugging Control]

     GenerateTrackingInfo=1

     AllowOptimize=0

     这个小技巧真的能帮助你节省调试的时间。虽然已经有其他人把它写进博客里,但是在过去几周我总是需要去搜索它,于是我就干脆也把它写进自己博客了以防以后需要。

posted on 2009-01-23 13:59:45 by vbcti  评论(0) 阅读(3404)

你知道吗?你可以在异常中解退调用栈

 

[原文作者]:Bill Horst

[原文链接]:Did you know? You can unwind the call stack from exceptions (Bill Horst)

    解退一个异常堆栈的能力是Visual Basic.NET 2005的一个新引进的特性。当调式器触发了一个异常,你可以解退这个堆栈以便于使用代码编辑器修复这个异常并继续调式 修改后的代码。这个异常辅助用户界面有一个“Enable Editing”选项,这个选项可以在当前Solution中展开调式器到代码堆栈的最顶端。

 

  

 

 

    当一个异常未被处理的时候,unwind将会自动发生,但是这个特性可以在Options dialog(在Tools下面)被开启或关闭。(见下图)

  

  

 

    如果你试图在一个异常被触法后去编辑代码,并在解退栈之前,你可以被允许去使用“解退栈并且编辑代码”,“终止代码调试的session”,或“取消编辑”这三个选项。(见下图)

 

    

 

      用户还可以从call stack窗口中进行解退栈,通过在需要的Call stack中单击右键并选择“Unwind To This Frame”。这个特性只是当异常已经被处发并且没有被解退栈时才有效,并且只有当在堆栈的足够靠前的的地方才有效。(见下图)

 

  

     我们希望这个特性可以给您提供更有效率的代码调试,并可以帮助您加强在Visual Basic中使用“Edit and Continue”能力。

posted on 2009-01-23 13:34:31 by vbcti  评论(1) 阅读(3062)

VB Catch …When 为什么这么特别?

 

    [原文作者]:Jared Parsons

    [原文链接]:VB Catch ... When: Why so special?

    VB Catch 的语法有一个独特的特点:When。它允许用户通过表达式筛选一些情况,而不是仅仅是筛选它的类型。 任何的代码都可以添加When去决定是否要处理某个异常。

    Sub Sub1()

    Try

    DoSomeAction()

    Catch ex As Exception When Filter(ex)

    Stop

    End Try

    End Sub

    新闻组通常会问,“为什么这么特别呢?”我也可以通过C#做同样的处理,例如:

static void Sub1()
{
    try
    {
        DoSomeAction();
    }
    catch (Exception ex)
    {
        if (Filter(ex))
        {
            throw;
        }
        HandleException();
    }
}
 
在某种程度上来说,确实是这样的。两种情况下代码都通过调用筛选程序来做决定是否要处理这个异常。细微的不同是在调用筛选的时候。
 
在VB中,When语句是作为IL异常来执行的。当异常被抛出,异常的筛选程序在堆栈展开之前就在进行了。这表示如果筛选方法创建了包括当时的堆栈的错误报告,它就可以展示出异常在什么地方发生的。
 
例如,在上面的代码中,如果DoSomeAction()被抛出,堆栈在筛选程序的表达式里面被检查,下面的堆栈就会被展示出来。
 
 
                
 
 注意,怎么使得DosomeAction这个方法如此清晰可见的?这对错误报告和调查都是很有力的帮助。它也允许在真正出错的语句上设置断点,而不仅仅只是一个验尸报告。
 
 在C#里面执行的代码是发生在堆栈展开之后。只要你不是在执行最优化的代码,仍然可以通过堆栈得到异常的原代码。但是你就不能在错误出现时检查代码了。
 
 
 
                
 
 
 

posted on 2009-01-23 11:23:39 by vbcti  评论(0) 阅读(3069)

什么时候可以捕捉到StackOverflowException(堆栈溢出)异常?

[原文作者]Mary Lee
       答案是:当你抛出它的时候
       从CLR 2.0开始,触发StackOverflowException异常的条件就改变了,用户不能触发这个异常(备注1),如果发生异常的话取而代之的是CLR会直接结束掉发生异常的进程。
但是事实并不是100%如此,用户代码仍然能够人为抛出StackOverflowException异常,当然不是通过真正的溢出错误,而是用户手动调用。这和文档上说得不一样,这里我们用一个不太规范但是很简单的程序来说明这种情况。(见备注)
      当然,这种用法没什么特别的,但是我还是觉得有必要提到这点,因为最近我看了一个新讨论组的谈话记录,有人发布了些简单的异常触发代码,碰巧里面提到StackOverflowException。他的代码里直接抛出了这个异常,而且他也有足够的理由相信这个异常在产品代码中也可以被抛出,对此我真的感到非常惊讶。
      请不要认为这篇文章提倡用户来抛出StackOverflowException异常(你当然不应该这么做),所有介绍都只是我的一点兴趣。坦白说我希望任何情形这个异常都是不能被捕捉的。
      public static void CatchStackOverflow1() {
    try {
        throw new StackOverflowException();
    } catch (StackOverflowException ex) {
        // Executes and handles the exception.  User code continues
        Console.WriteLine(ex.Message);
    }
   }
 
   static int CreateRealOverflow(int p1) {
    return 42 + CreateRealOverflow(p1 + 1);
    }
 
   public static void CatchStackOverflow2() {
    try {
        CreateRealOverflow(42);
    } catch (StackOverflowException ex) {
        // Will not execute
        Console.WriteLine(ex.Message);
    }
   }
 
   static void Main(string[] args) {
    CatchStackOverflow1();
    CatchStackOverflow2();
  
}
      [1]除非你可以在CLR托管的情况下执行一些回收机制,这当然是一个例外而不是规则规定的
 
 

posted on 2008-11-28 09:42:11 by VBCTI  评论(0) 阅读(6575)

正确操作windows窗体的Timer事件

[原文作者]Jared Parsons                                       
 
     Windows窗体的时间(Timer)类允许用户在一个时间段内执行特定的操作。时间对象会在设定的时间内开始一个Tick事件,用户可以容易的响应这个事件。如果软件开发者想在在一个指定的时间段内检查一个特定的条件(比如2秒钟,我会在这篇文章中用两秒作为范例),这时这个事件就会非常有用。
 
     偶尔用户会惊奇的发现Tick事件会比期望当中的更快地被触发。比较在两个调用期间等待两秒钟而言,取而代之的是Tick事件几乎是在一个被调用的过程完成后就被立即被触发了。
 
     这里所发生的是这个事件在事件循环机制作用下所产生的状态变化。时间事件的间隔期是由现实世界的时间来计算的。所以直白的讲,每隔两秒钟windows将认为时间间隔结束,然后发送一个新的Tick事件消息。下一时间段的windows窗体事件就不会再执行代码,一个tick事件会被触发【1】。
 
     现在我们可以想象一下我们已经有如下的代码。
 
     Private Sub OnTimerTick () Handles m_timer.Tick
         RunSomeOperation ()
     End Sub
 
     考虑一下如果 RunSomeOperation 用时超过2秒钟将发生什么。Tick事件会在RunSomeOperation过程当中被实时的触发,另外一个Tick事件会等候处理。一旦我们离开OnTimerTick过程,我们会回到Windows窗体代码,这些代码会巡视到Tick事件并且促使触发它,让我们重新回到OnTimerTick过程。
 
     这是与大多数人的期望矛盾的。大多数人会期望Tick事件应该在窗体代码完成后的两秒被触发。
 
     要想解决这个矛盾,开发者可以在处理一个timer事件的时候停止时间计数器的计数。然后再退出事件处理程序后从新激活计数器。这会使windows从激活计数器时才开始计算时间间隔。这样做的结果是使得timer事件在代码停止执行后的两秒钟才被触发。请参考以下代码:
 
    Private Sub OnTimerTick () Handles m_timer.Tick
      M_timer.stop ()
      Try
             RunSomeOperation ()
       Finally
             M_timer.Start ()
       End Try
    End Sub
 
 
 
      【1】这不是100%正确。不过这确实是无论什么时候应用程序开始又一次的循环事件时的情况。循环事件(更加确切的说是当循环事件发生或没有发生的时候)也与我们的这篇讨论相关联。

posted on 2008-11-07 14:35:41 by VBCTI  评论(0) 阅读(3102)

IntPtr(long)被悄无声息地截短了吗?

 

[原文作者]jaredpr
      一言以蔽之:不会,任何情况都不回被截短。
     我和同事前几天聊到一个有趣的案例,它涉及到IntPtr, Pinvoke以及64bit整体概念的准确性。 最终这场讨论把我们引向对IntPtr处理long类型的构造函数。令我吃惊的是,这个构造函数的是这么写的:
     public unsafe IntPtr(long value) { this.m_value = (void*) ((int) value); }
     问题在于long类型的值被肆意地截短成一个int值。这将直接导致丢失一切越过4G的内存地址 (换言之,没有64bit的寻址)。这个确确实实放在眼前的大漏洞,使我想到这是不是有可能是个反编译器(disassembler)bug。于是我用了.NET ReflectorIL模式。
    L_0000: ldarg.0
    L_0001: ldarg.1
    L_0002: conv.ovf.i4
    L_0003: conv.i
    L_0004: stfld void* System.IntPtr::m_value
    L_0009: ret
     这段IL证实了long值确确实实是被截短了(还在开始处做了一个溢出检测)。但是且慢,mscorlib.dll是个与处理器运行模式紧密相关的特殊的DLL,这一切有可能仅仅是32位操作系统惹得祸。于是我切换到一台64bit的机器,重新打开Reflector,令我气恼的是面对我的依旧是惊人相同的代码。
     几分钟后我想到打开任务管理器(task manager)看看,竟然看到reflector是个运行在WoW64的进程。这意味着reflector还是调用了32位版本的mscorlib.dll。顺利成章地,我用ildasm打开了64位的mscorlib,同时看到在64位模式,内存地址将不再被截短。
 IL_0000: ldarg.0
 IL_0001: ldarg.1
 IL_0002: conv.u
 IL_0003: stfld      void* System.IntPtr::m_value
 IL_0008: ret
      conv.u 是一个非托管的unsigned int的惯用方法,在64位平台上,conv.u将是一个unsigned 8 字节数值 (详见:OpCodes.Conv_U
概括一下,对于开发人员来说,IntPtr(long)的在不同的平台上做着完全正确的事,实现因平台不同而异。在32位操作系统上,如果一个非4GB的内存地址被当做参数传入的话,实现代码将(正确地)抛出一个异常。在64位的世界,实现代码将按照程序员的意愿返回一个正确的地址,不去截短任何值。

posted on 2008-11-07 14:18:47 by VBCTI  评论(0) 阅读(2666)

Powered by: Joycode.MVC引擎 0.5.2.0