• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            兔子的技術博客

            兔子

               :: 首頁 :: 聯系 :: 聚合  :: 管理
              202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

            留言簿(10)

            最新評論

            閱讀排行榜

            評論排行榜

            因為QTP的需要,同事寫了通過進程來調用Plink進行Telnet連接的接口。我測試的時候發現,他那個調用.Net 里面的Process進程的方法,通過重定向獲取標準輸出流的辦法有點不好,就是調用了流動Read()函數之后,就會一直阻塞在那里,知道流中有數據才能正確返回,而peek函數又不能正確的監測到流中是否有數據可以讀。我先去翻翻了MSDN中那個StreamReader類的辦法,好像確實沒有辦法,反倒是在Process的StandardOutput屬性的說明那里,明顯寫著,如果標準輸出里面沒有數據的話,read函數就會無限時的阻塞在那里知道有數據可以讀才行,然后他還提到了一些導致死鎖的問題。

            我去寫了個簡單的.Net程序來測試了一下,可以知道那個StreamReader是一個FileStream來的,而且那個CanTimeout等屬性都表明不是一個可以異步讀取的流。難道真沒有辦法監測到這個流中是否有數據可讀的狀態嗎? 根據常識知道,這個流應該是“匿名管道”來的,去找了一下MSDN中關于管道的api函數,還真讓我找到了一個,那就是PeekNamedPipe http://msdn.microsoft.com/en-us/library/aa365779(VS.85).aspx

            根據它的說明,來看看這個

            The PeekNamedPipe function is similar to the ReadFile function with the following exceptions:

            • The data is read in the mode specified with CreateNamedPipe. For example, create a pipe with PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE. If you change the mode to PIPE_READMODE_BYTE with SetNamedPipeHandleStateReadFile will read in byte mode, but PeekNamedPipe will continue to read in message mode.
            • The data read from the pipe is not removed from the pipe's buffer.
            • The function can return additional information about the contents of the pipe.
            • The function always returns immediately in a single-threaded application, even if there is no data in the pipe. The wait mode of a named pipe handle (blocking or nonblocking) has no effect on the function.

            Note   The PeekNamedPipe function can block thread execution the same way any I/O function can when called on a synchronous handle in a multi-threaded application. To avoid this condition, use a pipe handle created for asynchronous I/O.

            If the specified handle is a named pipe handle in byte-read mode, the function reads all available bytes up to the size specified in nBufferSize. For a named pipe handle in message-read mode, the function reads the next message in the pipe. If the message is larger than nBufferSize, the function returns TRUE after reading the specified number of bytes. In this situation, lpBytesLeftThisMessage will receive the number of bytes remaining in the message.

            這個函數不管命名管道是不是阻塞模式的,都會立即返回(除了在多線程環境下的某種情況下會阻塞,大概就是http://my.donews.com/yeyanbo/tag/peeknamedpipe/這個文章發現的問題。),文檔又說所有的”匿名管道“其實都是一個“命名管道”來實現的,所以操作“命名管道”的函數對“匿名管道”也是有效的。這個函數明顯是我想要的,可以用來檢測process標準輸出流中是否有數據可以讀,又不會阻塞。在vb.net的測試代碼里面試了一下,應該是可以工作,測試代碼如下:

            Imports System.Diagnostics.Process
            Public Class Form1
                
            Declare Function SetNamedPipeHandleState Lib "kernel32" (ByVal hNamedPipe As IntegerByRef lpMode As IntegerByRef lpMaxCollectionCount As IntegerByRef lpCollectDataTimeout As IntegerAs Integer
                
            Declare Function PeekNamedPipe Lib "kernel32" (ByVal hNamedPipe As IntegerByRef lpBuffer As IntegerByVal nBufferSize As IntegerByRef lpBytesRead As IntegerByRef lpTotalBytesAvail As IntegerByRef lpBytesLeftThisMessage As IntegerAs Integer
                
            Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
                    
            Dim p As Process = New Process
                    p.StartInfo.FileName 
            = "c:\windows\system32\cmd.exe"
                    p.StartInfo.RedirectStandardInput 
            = True
                    p.StartInfo.RedirectStandardOutput 
            = True
                    p.StartInfo.UseShellExecute 
            = False
                    p.Start()
                    p.StandardInput.WriteLine(
            "hostname")
                    
            Dim f As System.IO.FileStream = p.StandardOutput.BaseStream

                    
            Dim mode As Integer
                    mode 
            = 1 ' no-wait
                    Dim count As Integer
                    
            '修改這個命名管道為 異步的,是不能成功的
                    ''mode = SetNamedPipeHandleState(f.Handle, mode, System.IntPtr.Zero, System.IntPtr.Zero)
                    '不過這個PeekNamePipe 函數是可以得到 管道里面有多少字節可以讀到,執行完之后count里面是對的,可以讀取的數據
                    mode = PeekNamedPipe(f.Handle, System.IntPtr.Zero, 0, System.IntPtr.Zero, count, System.IntPtr.Zero)
                    p.StandardOutput.Read()
                    
            While p.StandardOutput.Peek > 0
                        p.StandardOutput.Read()
                    
            End While
                    
            '在這個地方的時候就不能再read了,read就無限阻塞直到有數據來才能返回了。
                    mode = p.StandardOutput.Peek() '這個時候的peek返回 -1是對的
                    mode = PeekNamedPipe(f.Handle, System.IntPtr.Zero, 0, System.IntPtr.Zero, count, System.IntPtr.Zero) '這個count得到0 是對的,管道里面沒有消息的了
                    p.StandardInput.WriteLine("hostname")
                    mode 
            = p.StandardOutput.Peek() '這個還是返回 -1是不對的,
                    mode = PeekNamedPipe(f.Handle, System.IntPtr.Zero, 0, System.IntPtr.Zero, count, System.IntPtr.Zero) '這個返回正確的count,表明管道里面有數據是對的
                    p.StandardOutput.Read()
                    p.StandardOutput.Peek() 
            'peek函數一定要在read成功調用過一次之后才能正確的得到管道的狀態。但Read一次又可能引起無限時間的阻塞!!!!所以只有PeekNamedPipe才能正確的無阻塞的檢測到管道的數據
                End Sub
            End Class

            總結一下 :感覺。Net對這個“命名管道“”匿名管道“的支持明顯不夠,API中都有監測到管道是否有數據可以讀到函數。.Net里面卻連管道對應的類都沒有實現,所以相應的這種阻塞情況就也沒法處理了。可能這部分的封裝有待完善吧。

            后來有用Reflector工具反匯編看了看系統Process幾個類的相應實現代碼,可以看到他是CreatePipe創建一個管道,然后DuplicateHandle復制了一個文件句柄的,跟我事先的猜測是一樣的。Linux的輸出重定向使用也要類似的這樣兩個步驟。如果你自己看他的代碼,可以看到創建的是一個只讀的、不支持異步的FileSteam來的,他代碼是這樣寫的:

            public bool Start()
            {
                
            this.Close();
                ProcessStartInfo startInfo 
            = this.StartInfo;
                
            if (startInfo.FileName.Length == 0)
                {
                    
            throw new InvalidOperationException(SR.GetString("FileNameMissing"));
                }
                
            if (startInfo.UseShellExecute)
                {
                    
            return this.StartWithShellExecuteEx(startInfo);
                }
                
            return this.StartWithCreateProcess(startInfo);
            }

            private bool StartWithCreateProcess(ProcessStartInfo startInfo)
            {
                
            if ((startInfo.StandardOutputEncoding != null&& !startInfo.RedirectStandardOutput)
                {
                    
            throw new InvalidOperationException(SR.GetString("StandardOutputEncodingNotAllowed"));
                }
                
            if ((startInfo.StandardErrorEncoding != null&& !startInfo.RedirectStandardError)
                {
                    
            throw new InvalidOperationException(SR.GetString("StandardErrorEncodingNotAllowed"));
                }
                
            if (this.disposed)
                {
                    
            throw new ObjectDisposedException(base.GetType().Name);
                }
                StringBuilder cmdLine 
            = BuildCommandLine(startInfo.FileName, startInfo.Arguments);
                NativeMethods.STARTUPINFO lpStartupInfo 
            = new NativeMethods.STARTUPINFO();
                SafeNativeMethods.PROCESS_INFORMATION lpProcessInformation 
            = new SafeNativeMethods.PROCESS_INFORMATION();
                SafeProcessHandle processHandle 
            = new SafeProcessHandle();
                SafeThreadHandle handle2 
            = new SafeThreadHandle();
                
            int error = 0;
                SafeFileHandle parentHandle 
            = null;
                SafeFileHandle handle4 
            = null;
                SafeFileHandle handle5 
            = null;
                GCHandle handle6 
            = new GCHandle();
                
            try
                {
                    
            bool flag;
                    
            if ((startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput) || startInfo.RedirectStandardError)
                    {
                        
            if (startInfo.RedirectStandardInput)
                        {
                            
            this.CreatePipe(out parentHandle, out lpStartupInfo.hStdInput, true);
                        }
                        
            else
                        {
                            lpStartupInfo.hStdInput 
            = new SafeFileHandle(NativeMethods.GetStdHandle(-10), false);
                        }
                        
            if (startInfo.RedirectStandardOutput)
                        {
                            
            this.CreatePipe(out handle4, out lpStartupInfo.hStdOutput, false);
                        }
                        
            else
                        {
                            lpStartupInfo.hStdOutput 
            = new SafeFileHandle(NativeMethods.GetStdHandle(-11), false);
                        }


            中間省略一部分

               if (startInfo.RedirectStandardInput)

                {
                    this.standardInput = new StreamWriter(new FileStream(parentHandle, FileAccess.Write, 0x1000, false), Encoding.GetEncoding(NativeMethods.GetConsoleCP()), 0x1000);
                    this.standardInput.AutoFlush = true;
                }
                if (startInfo.RedirectStandardOutput)
                {
                    Encoding encoding = (startInfo.StandardOutputEncoding != null) ? startInfo.StandardOutputEncoding : Encoding.GetEncoding(NativeMethods.GetConsoleOutputCP());
                    this.standardOutput = new StreamReader(new FileStream(handle4, FileAccess.Read, 0x1000, false), encoding, true, 0x1000);
                }
                if (startInfo.RedirectStandardError)
                {
                    Encoding encoding2 = (startInfo.StandardErrorEncoding != null) ? startInfo.StandardErrorEncoding : Encoding.GetEncoding(NativeMethods.GetConsoleOutputCP());
                    this.standardError = new StreamReader(new FileStream(handle5, FileAccess.Read, 0x1000, false), encoding2, true, 0x1000);
                }
                bool flag3 = false;
                if (!processHandle.IsInvalid)
                {
                    this.SetProcessHandle(processHandle);
                    this.SetProcessId(lpProcessInformation.dwProcessId);
                    handle2.Close();
                    flag3 = true;
                }
                return flag3;
            }

            private void CreatePipe(out SafeFileHandle parentHandle, out SafeFileHandle childHandle, bool parentInputs)
            {
                NativeMethods.SECURITY_ATTRIBUTES lpPipeAttributes = new NativeMethods.SECURITY_ATTRIBUTES();
                lpPipeAttributes.bInheritHandle = true;
                SafeFileHandle hWritePipe = null;
                try
                {
                    if (parentInputs)
                    {
                        CreatePipeWithSecurityAttributes(out childHandle, out hWritePipe, lpPipeAttributes, 0);
                    }
                    else
                    {
                        CreatePipeWithSecurityAttributes(out hWritePipe, out childHandle, lpPipeAttributes, 0);
                    }
                    if (!NativeMethods.DuplicateHandle(new HandleRef(this, NativeMethods.GetCurrentProcess()), hWritePipe, new HandleRef(this, NativeMethods.GetCurrentProcess()), out parentHandle, 0, false, 2))
                    {
                        throw new Win32Exception();
                    }
                }
                finally
                {
                    if ((hWritePipe != null) && !hWritePipe.IsInvalid)
                    {
                        hWritePipe.Close();
                    }
                }
            }

            private static void CreatePipeWithSecurityAttributes(out SafeFileHandle hReadPipe, out SafeFileHandle hWritePipe, NativeMethods.SECURITY_ATTRIBUTES lpPipeAttributes, int nSize)
            {
                if ((!NativeMethods.CreatePipe(out hReadPipe, out hWritePipe, lpPipeAttributes, nSize) || hReadPipe.IsInvalid) || hWritePipe.IsInvalid)
                {
                    throw new Win32Exception();
                }
            }

            public override int Peek()
            {
                if (this.stream == null)
                {
                    __Error.ReaderClosed();
                }
                if ((this.charPos != this.charLen) || (!this._isBlocked && (this.ReadBuffer() != 0)))
                {
                    return this.charBuffer[this.charPos];
                }
                return -1;
            }
            轉自:http://hi.baidu.com/widebright/item/f58e2516a6bb41dcbf9042a4
            posted on 2012-08-16 13:30 會飛的兔子 閱讀(4421) 評論(0)  編輯 收藏 引用 所屬分類: 系統API,底層技術
            99久久婷婷国产综合精品草原| 久久精品无码一区二区三区| 亚洲欧美另类日本久久国产真实乱对白| 久久精品国产99久久香蕉| 一本色道久久88综合日韩精品 | 国产精品久久久久久| 国产福利电影一区二区三区久久久久成人精品综合 | 久久午夜免费视频| 国产精品一区二区久久不卡| 久久男人中文字幕资源站| 国内精品伊人久久久久av一坑 | 久久久久久久91精品免费观看| 国产精品无码久久综合| 久久亚洲欧洲国产综合| 97超级碰碰碰久久久久| 久久香综合精品久久伊人| 国产精自产拍久久久久久蜜| 久久国产精品77777| 精品久久久中文字幕人妻| 久久成人国产精品一区二区| 国内精品久久人妻互换| 精品久久亚洲中文无码| 亚洲国产香蕉人人爽成AV片久久 | 久久九九久精品国产免费直播| 热re99久久精品国产99热| 久久中文骚妇内射| 久久久久99这里有精品10| 久久久久国色AV免费看图片| 国产免费福利体检区久久| 91久久九九无码成人网站| 99麻豆久久久国产精品免费| 久久国产精品成人片免费| 久久99国产综合精品| 久久婷婷五月综合色高清| 午夜久久久久久禁播电影| 亚洲精品无码成人片久久| 亚洲精品乱码久久久久久按摩| 久久久亚洲裙底偷窥综合| 久久精品无码一区二区WWW| 久久久久亚洲AV无码观看| 亚洲精品国精品久久99热一|