ReadConsoleOutput and ERROR_INVALID_HANDLE

D

Doug Perkes

There was a post a couple of months ago entitled "ReadConsoleOutput -
ReadConsoleOutputCharacter APIs". I have some questions in relation to
that post.

I have an existing console application that I need to be able to connect
to and read the output of the console app as well as write to it. Every
time I try to call the ReadConsoleOutput function, I receive a
Win32Error ERROR_INVALID_HANDLE (error code 6, "The handle is invalid").

Here's what I am doing from a Windows forms application:
1. I create and start a process to test with (in production this process
will already be running). For this example, cmd.exe.
2. Call AttachConsole with the id of the process I just created.
3. Get a handle to the console output using
GetStdHandle(STD_HANDLES.STD_OUTPUT_HANDLE).
4. To test that I have the correct handle I change the title of the
console using SetConsoleTitle. This works, so I know I have the right
process id and the right console attached.
5. Call ReadConsoleOutput. This returns false every time.
6. I then call Marshal.GetLastWin32Error() and the error is error code
6, "The handle is invalid".

Any ideas why ReadConsoleOutput fails?

Thanks,

Doug


Here's my VB.NET Form code:
Imports System.ComponentModel
Imports System.Runtime.InteropServices
Imports System.Diagnostics
Imports System.Threading

Public Class ConsoleControllerTest
Inherits System.Windows.Forms.Form

#Region " Windows Form Designer generated code "

Public Sub New()
MyBase.New()

'This call is required by the Windows Form Designer.
InitializeComponent()

'Add any initialization after the InitializeComponent() call

End Sub

'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As
Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub

'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer

'NOTE: The following procedure is required by the Windows Form
Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents Button1 As System.Windows.Forms.Button
Friend WithEvents TextBox1 As System.Windows.Forms.TextBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub
InitializeComponent()
Me.Button1 = New System.Windows.Forms.Button
Me.TextBox1 = New System.Windows.Forms.TextBox
Me.SuspendLayout()
'
'Button1
'
Me.Button1.Location = New System.Drawing.Point(8, 8)
Me.Button1.Name = "Button1"
Me.Button1.TabIndex = 0
Me.Button1.Text = "Button1"
'
'TextBox1
'
Me.TextBox1.Location = New System.Drawing.Point(8, 40)
Me.TextBox1.Multiline = True
Me.TextBox1.Name = "TextBox1"
Me.TextBox1.Size = New System.Drawing.Size(512, 280)
Me.TextBox1.TabIndex = 1
Me.TextBox1.Text = ""
'
'ConsoleControllerTest
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(536, 366)
Me.Controls.Add(Me.TextBox1)
Me.Controls.Add(Me.Button1)
Me.Name = "ConsoleControllerTest"
Me.Text = "ConsoleControllerTest"
Me.ResumeLayout(False)

End Sub

#End Region

Public Enum STD_HANDLES
STD_INPUT_HANDLE = -10
STD_OUTPUT_HANDLE = -11
STD_ERROR_HANDLE = -12
End Enum

<StructLayout(LayoutKind.Explicit, CharSet:=CharSet.Auto)> _
Public Structure CHAR_TYPE
<FieldOffset(0)> _
Public UnicodeChar As Short

<FieldOffset(0)> _
Public AsciiChar As Byte
End Structure

<StructLayout(LayoutKind.Sequential, CharSet:=CharSet.Auto)> _
Public Structure CHAR_INFO
Public [Char] As CHAR_TYPE
Public Attributes As Short
End Structure

<StructLayout(LayoutKind.Sequential)> _
Public Structure COORD
Public X As Short
Public Y As Short

Public Sub New(ByVal X As Short, ByVal Y As Short)
Me.X = X
Me.Y = Y
End Sub
End Structure

<StructLayout(LayoutKind.Sequential)> _
Public Structure SMALL_RECT
Public Left As Short
Public Top As Short
Public Right As Short
Public Bottom As Short

Public Sub New(ByVal Left As Short, ByVal Top As Short, ByVal
Right As Short, ByVal Bottom As Short)
Me.Left = Left
Me.Top = Top
Me.Right = Right
Me.Bottom = Bottom
End Sub
End Structure

Public Declare Function GetStdHandle Lib "kernel32" ( _
ByVal nStdHandle As STD_HANDLES) As IntPtr

Public Declare Auto Function ReadConsoleOutput Lib "kernel32" ( _
ByVal hConsoleOutput As IntPtr, _
ByRef lpBuffer As CHAR_INFO, _
ByVal dwBufferSize As COORD, _
ByVal dwBufferCoord As COORD, _
ByRef lpReadRegion As SMALL_RECT) As Boolean

Public Declare Auto Function AttachConsole Lib "kernel32" ( _
ByVal dwProcessId As UInt32) As Boolean

Public Declare Auto Function SetConsoleTitle Lib "kernel32" (ByVal
lpConsoleTitle As String) As Boolean


Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As
System.EventArgs) Handles Button1.Click
'create a new process to imitate the process that will already
be running
Dim myProcess As New Process
myProcess.StartInfo.FileName = "cmd.exe"
myProcess.Start()

'wait for the process to start
Thread.Sleep(1000)

'attach to the process just stared
If (Not AttachConsole(Convert.ToUInt32(myProcess.Id))) Then
MessageBox.Show("Could not attach to console")
Return
End If

'get a handle to the output of the command window
Dim hConsoleOutput As IntPtr =
GetStdHandle(STD_HANDLES.STD_OUTPUT_HANDLE)
'get the first 7 characters of the first line
Dim Buffer(6) As CHAR_INFO
Dim BufferSize As New COORD(7, 1)
Dim BufferCoord As New COORD(0, 0)
Dim ReadRegion As New SMALL_RECT(0, 0, 6, 1)

If Not SetConsoleTitle("My New Title") Then
Dim errCode As Integer = Marshal.GetLastWin32Error()
Dim ex As Win32Exception = New Win32Exception
Dim errMsg As String = ex.Message
Me.TextBox1.Text &= errMsg

End If


If ReadConsoleOutput(hConsoleOutput, Buffer(0), BufferSize,
BufferCoord, ReadRegion) Then
For i As Integer = 0 To Buffer.Length - 1
Me.TextBox1.Text &= Chr(Buffer(i).Char.UnicodeChar)
Next
Else
'keeps failing with error code 6 [ERROR_INVALID_HANDLE], The
handle is invalid
Dim errCode As Integer = Marshal.GetLastWin32Error()
Dim ex As Win32Exception = New Win32Exception
Dim errMsg As String = ex.Message
Me.TextBox1.Text &= errMsg

End If

End Sub
End Class
 
T

Tom Shelton

Doug Perkes said:
There was a post a couple of months ago entitled "ReadConsoleOutput -
ReadConsoleOutputCharacter APIs". I have some questions in relation to
that post.

I have an existing console application that I need to be able to connect
to and read the output of the console app as well as write to it. Every
time I try to call the ReadConsoleOutput function, I receive a
Win32Error ERROR_INVALID_HANDLE (error code 6, "The handle is invalid").

Here's what I am doing from a Windows forms application:
1. I create and start a process to test with (in production this process
will already be running). For this example, cmd.exe.
2. Call AttachConsole with the id of the process I just created.
3. Get a handle to the console output using
GetStdHandle(STD_HANDLES.STD_OUTPUT_HANDLE).
4. To test that I have the correct handle I change the title of the
console using SetConsoleTitle. This works, so I know I have the right
process id and the right console attached.
5. Call ReadConsoleOutput. This returns false every time.
6. I then call Marshal.GetLastWin32Error() and the error is error code
6, "The handle is invalid".

Any ideas why ReadConsoleOutput fails?

Thanks,

Doug

Doug,

Could it be your running the program in the IDE with F5? See, the thing
is your code below does work - as long as you run it without the
debugger attached. It seems that the debugger process is interfering
some how. When I run your code, with Ctrl-F5 - it works.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top