GetStdHandle .NET

  • Thread starter Thread starter Dragon
  • Start date Start date
D

Dragon

Hello all,
I ran into trouble with GetStdHandle API. When I'm trying to get standard
output with GetStdHandle(-11I), it returns strange values such as 1548,
1876. If I try to WriteConsole() into these handles, it does not work, and
GetLastError returns ERROR_INVALID_HANDLE. But when I CloseHandle() them, it
succeeds! I tested this in VC++ and VBA, and GetStdHandle(-11) always
returns 7, which is valid. There is the same problem with standard error,
but no problems with standard input.
I declare GetStdHandle as follows:
Declare Function GetStdHandle Lib "kernel32.dll" (ByVal nStdHandle As _
Int32) As IntPtr

Any suggestions? Thank you in advance.
 
Dragon,
Not sure what your error is about, as strand__sure suggests, can you show
more of your code you are attempting to use.

Rather then use Win32 APIs from .NET, have you considered using the
System.Console class instead? It encapsulates most of the Win32 Console API
functions. With .NET 2.0 (aka VS 2005, aka Whidbey, due out later in 2005.
See http://lab.msdn.microsoft.com/vs2005/ for details) providing even more
support.

You can use Console.OpenStandardOutput in VS 2002 & 2003 to get the standard
output stream. You can use Console.SetOut to set tell Console.Write what
TextWriter (Stream) to use.

Hope this helps
Jay

| Hello all,
| I ran into trouble with GetStdHandle API. When I'm trying to get standard
| output with GetStdHandle(-11I), it returns strange values such as 1548,
| 1876. If I try to WriteConsole() into these handles, it does not work, and
| GetLastError returns ERROR_INVALID_HANDLE. But when I CloseHandle() them,
it
| succeeds! I tested this in VC++ and VBA, and GetStdHandle(-11) always
| returns 7, which is valid. There is the same problem with standard error,
| but no problems with standard input.
| I declare GetStdHandle as follows:
| Declare Function GetStdHandle Lib "kernel32.dll" (ByVal nStdHandle As _
| Int32) As IntPtr
|
| Any suggestions? Thank you in advance.
|
|
|
|
|
 
stand__sure said:
can you give us more code? how are you dereferencing the value?
Well, that's my code:
~
Private Declare Function AllocConsole Lib "kernel32.dll" () As Int32

Private Declare Function FreeConsole Lib "kernel32.dll" () As Int32

Private Declare Function GetStdHandle Lib "kernel32.dll" (ByVal nStdHandle
As Int32) As IntPtr

Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As
IntPtr) As Int32

Private Declare Function WriteConsole Lib "kernel32.dll" Alias
"WriteConsoleA" (ByVal hConsoleOutput As IntPtr, ByVal lpBuffer As IntPtr,
ByVal nNumberOfCharsToWrite As Int32, ByRef lpNumberOfCharsWritten As Int32,
ByVal lpReserved As IntPtr) As Int32

Private Declare Function GetLastError Lib "kernel32.dll" () As Int32

Private Const STD_OUTPUT_HANDLE As Int32 = -11I

Private Sub SomeButton_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles SomeButton.Click

If AllocConsole() = 0 Then Debug.WriteLine("AllocConsole failed: " &
GetLastError.ToString)

Dim hOutPut As IntPtr = GetStdHandle(STD_OUTPUT_HANDLE)

If hOutPut.ToInt32 = 0 Then Debug.WriteLine("GetStdHandle failed: " &
GetLastError.ToString)

Debug.WriteLine("Standard output: " & hOutPut.ToString)

Dim str As String = "It's working!"

Dim written As Int32

Dim handle As IntPtr = Marshal.StringToHGlobalAnsi(str)

If WriteConsole(hOutPut, handle, str.Length, written, New IntPtr(0)) = 0
Then Debug.WriteLine("WriteConsole failed: " & GetLastError)

If CloseHandle(hOutPut) = 0 Then Debug.WriteLine("CloseHandle failed: " &
GetLastError)

Threading.Thread.Sleep(1000)

If FreeConsole() = 0 Then Debug.WriteLine("FreeConsole failed: " &
GetLastError.ToString)

End Sub

~

When I click SomeButton for first time I get following output:

Standard output: 1912 (or any other bad value)

WriteConsole failed: 6

If I keep clicking it prints:

Standard output: 1912

WriteConsole failed: 6

CloseHandle failed: 6

When I restart application number 1912 changes to another number and
situation repeats.
 
I am unable to duplicate your problem (i.e the code works)... the only
change that I made is to throw your code into a separate module file
from the form (this was so that I could just modify the click event on
an existing form).

I called your function in a module that I named StdHandle as follows
from within a click handler for a button on my form:
StdHandle.SomeButton_Click(sender, e)

since it works on my dev box and not on yours I suspect that something
else may be going on. take a look at your debug output window to see
if you notice anything else (e.g. exceptions). also, test your code in
a new windows form app...
 
Dragon,
As stand__sure suggests, this works on my dev box also; that is if I run it
without the debugger (VS.NET = "Debug - Start Without Debugging").

If I attempt to run it inside the debugger it fails! Failing inside the
debugger is curious in that I would not expect it to actually fail, I would
expect different handle values (as actual handle values are opaque, in that
they represent a HANDLE to a resource & their actual value has no meaning).
Remember VB.NET redirects console output to the Debug window, it would
appear when it does this it causes GetStdHandle to behave oddly...

If you call Console.Write instead of the WriteConsole API in your sample,
the output goes to the Debugger's output window, as evidence of the above
redirection.

Unfortunately I have not used consoles enough to offer any suggestions on
how to "work around" the above behavior... Reading the Remarks of
GetStdHandle offers a suggestion. Open CONIN$ or CONOUT$ to get a handle to
a "console's active screen buffer":

http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dllproc/base/getstdhandle.asp


Rather then Declare GetLastError, you need to call
System.Runtime.InteropServices.Marshal.GetLastWin32Error, for details see:

http://msdn.microsoft.com/library/d...ervicesmarshalclassgetlastwin32errortopic.asp


Something like:

Imports System.Runtime.InteropServices

Private Declare Function AllocConsole Lib "kernel32.dll" () As Int32

Private Declare Function FreeConsole Lib "kernel32.dll" () As Int32

Private Declare Function GetStdHandle Lib "kernel32.dll" (ByVal
nStdHandle As Int32) As IntPtr

Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject
As IntPtr) As Int32

Private Declare Function WriteConsole Lib "kernel32.dll" Alias
"WriteConsoleA" (ByVal hConsoleOutput As IntPtr, ByVal lpBuffer As IntPtr,
ByVal nNumberOfCharsToWrite As Int32, ByRef lpNumberOfCharsWritten As Int32,
ByVal lpReserved As IntPtr) As Int32

'Private Declare Function GetLastError Lib "kernel32.dll" () As Int32

Private Const STD_OUTPUT_HANDLE As Int32 = -11I

Public Shared Sub SomeButton_Click(ByVal sender As Object, ByVal e As
System.EventArgs)

If AllocConsole() = 0 Then Debug.WriteLine("AllocConsole failed: " &
Marshal.GetLastWin32Error().ToString)

Dim hOutPut As IntPtr = GetStdHandle(STD_OUTPUT_HANDLE)

If hOutPut.ToInt32 = 0 Then Debug.WriteLine("GetStdHandle failed: "
& Marshal.GetLastWin32Error().ToString)

Debug.WriteLine("Standard output: " & hOutPut.ToString)

Dim str As String = "It's working!"

Dim written As Int32

Dim handle As IntPtr = Marshal.StringToHGlobalAnsi(str)

If WriteConsole(hOutPut, handle, str.Length, written, Intptr.Zero) =
0 Then Debug.WriteLine("WriteConsole failed: " &
Marshal.GetLastWin32Error())

If CloseHandle(hOutPut) = 0 Then Debug.WriteLine("CloseHandle
failed: " & Marshal.GetLastWin32Error())

Threading.Thread.Sleep(1000)

If FreeConsole() = 0 Then Debug.WriteLine("FreeConsole failed: " &
Marshal.GetLastWin32Error().ToString)

End Sub


Hope this helps
Jay

|
| "stand__sure" <[email protected]> ???????/???????? ? ????????
| ?????????: | > can you give us more code? how are you dereferencing the value?
| >
| Well, that's my code:
| ~
| Private Declare Function AllocConsole Lib "kernel32.dll" () As Int32
|
| Private Declare Function FreeConsole Lib "kernel32.dll" () As Int32
|
| Private Declare Function GetStdHandle Lib "kernel32.dll" (ByVal nStdHandle
| As Int32) As IntPtr
|
| Private Declare Function CloseHandle Lib "kernel32.dll" (ByVal hObject As
| IntPtr) As Int32
|
| Private Declare Function WriteConsole Lib "kernel32.dll" Alias
| "WriteConsoleA" (ByVal hConsoleOutput As IntPtr, ByVal lpBuffer As IntPtr,
| ByVal nNumberOfCharsToWrite As Int32, ByRef lpNumberOfCharsWritten As
Int32,
| ByVal lpReserved As IntPtr) As Int32
|
| Private Declare Function GetLastError Lib "kernel32.dll" () As Int32
|
| Private Const STD_OUTPUT_HANDLE As Int32 = -11I
|
| Private Sub SomeButton_Click(ByVal sender As Object, ByVal e As
| System.EventArgs) Handles SomeButton.Click
|
| If AllocConsole() = 0 Then Debug.WriteLine("AllocConsole failed: " &
| GetLastError.ToString)
|
| Dim hOutPut As IntPtr = GetStdHandle(STD_OUTPUT_HANDLE)
|
| If hOutPut.ToInt32 = 0 Then Debug.WriteLine("GetStdHandle failed: " &
| GetLastError.ToString)
|
| Debug.WriteLine("Standard output: " & hOutPut.ToString)
|
| Dim str As String = "It's working!"
|
| Dim written As Int32
|
| Dim handle As IntPtr = Marshal.StringToHGlobalAnsi(str)
|
| If WriteConsole(hOutPut, handle, str.Length, written, New IntPtr(0)) = 0
| Then Debug.WriteLine("WriteConsole failed: " & GetLastError)
|
| If CloseHandle(hOutPut) = 0 Then Debug.WriteLine("CloseHandle failed: " &
| GetLastError)
|
| Threading.Thread.Sleep(1000)
|
| If FreeConsole() = 0 Then Debug.WriteLine("FreeConsole failed: " &
| GetLastError.ToString)
|
| End Sub
|
| ~
|
| When I click SomeButton for first time I get following output:
|
| Standard output: 1912 (or any other bad value)
|
| WriteConsole failed: 6
|
| If I keep clicking it prints:
|
| Standard output: 1912
|
| WriteConsole failed: 6
|
| CloseHandle failed: 6
|
| When I restart application number 1912 changes to another number and
| situation repeats.
|
|
 
very good tips.

the code also works in a vs debug on my box (I'm still running 2005
beta 1 though -- been too busy to install beta 2...) the output is to
a new console window in a debug session (and this is not the console
window that vs knows -- I had it open as well). one quirk is that the
technique in the OP doesn't seem to work in a console application (I am
assuming that only one console window is allowed???)
 
stand__sure,
| the code also works in a vs debug on my box (I'm still running 2005
| beta 1 though -- been too busy to install beta 2...) the output is to
| a new console window in a debug session (and this is not the console
It sounds like 2005 "fixes" the odd behavior that I am seeing in 2003...

I'll try this on beta 2 later tonight or Monday & report back.

Hopefully Dragon finds a solution...


| one quirk is that the
| technique in the OP doesn't seem to work in a console application (I am
| assuming that only one console window is allowed???)

My understanding is that a Console application can only have a single
Console window open at a time. Based on the remarks on AllocConsole.

Jay


| very good tips.
|
| the code also works in a vs debug on my box (I'm still running 2005
| beta 1 though -- been too busy to install beta 2...) the output is to
| a new console window in a debug session (and this is not the console
| window that vs knows -- I had it open as well). one quirk is that the
| technique in the OP doesn't seem to work in a console application (I am
| assuming that only one console window is allowed???)
|
 
stand__sure,
I tried Dragon's code on VS 2005 Beta 2, it works as Dragon expects.

Seeing as Dragon's code works outside of VS 2003, & it works inside or
outside of VS 2005. It appears VS 2003 has a quirk/bug that is preventing
Dragon's code from working...

My testing was on Windows XP Pro.

NOTE to Dragon. You only need to the Declare the AllocConsole & FreeConsole
API calls, all the other Console API calls are handled by the System.Console
class...

Hope this helps
Jay

| that's also my understanding, but it comes from a non .NET source...
|
 
Back
Top