Std error handle

Windows Console Docs Repo. Contribute to MicrosoftDocs/Console-Docs development by creating an account on GitHub.
title description author ms.author ms.topic keywords f1_keywords MS-HAID MSHAttr ms.assetid topic_type api_name api_location api_type

GetStdHandle function

Retrieves a handle to the specified standard device (standard input, standard output, or standard error).

miniksa

miniksa

article

console, character mode applications, command line applications, terminal applications, console api

processenv/GetStdHandle

winbase/GetStdHandle

GetStdHandle

_win32_getstdhandle

base.getstdhandle

consoles.getstdhandle

PreferredSiteName:MSDN

PreferredLib:/library/windows/desktop

23cd76e9-671a-48d0-9b82-2beda8917348

apiref

GetStdHandle

Kernel32.dll

API-MS-Win-Core-ProcessEnvironment-l1-1-0.dll

KernelBase.dll

API-MS-Win-Core-ProcessEnvironment-l1-2-0.dll

API-MS-Win-DownLevel-Kernel32-l1-1-0.dll

MinKernelBase.dll

DllExport

GetStdHandle function

Retrieves a handle to the specified standard device (standard input, standard output, or standard error).

Syntax

HANDLE WINAPI GetStdHandle(
  _In_ DWORD nStdHandle
);

Parameters

nStdHandle [in]
The standard device. This parameter can be one of the following values.

Value Meaning
STD_INPUT_HANDLE ((DWORD)-10) The standard input device. Initially, this is the console input buffer, CONIN$.
STD_OUTPUT_HANDLE ((DWORD)-11) The standard output device. Initially, this is the active console screen buffer, CONOUT$.
STD_ERROR_HANDLE ((DWORD)-12) The standard error device. Initially, this is the active console screen buffer, CONOUT$.

[!NOTE]
The values for these constants are unsigned numbers, but are defined in the header files as a cast from a
signed number and take advantage of the C compiler rolling them over to just under the maximum 32-bit value. When interfacing with these handles in a language that does not parse the headers and is re-defining the constants, please be aware of this constraint. As an example, ((DWORD)-10) is actually the unsigned number 4294967286.

Return value

If the function succeeds, the return value is a handle to the specified device, or a redirected handle set by a previous call to SetStdHandle. The handle has GENERIC_READ and GENERIC_WRITE access rights, unless the application has used SetStdHandle to set a standard handle with lesser access.

[!TIP]
It is not required to dispose of this handle with CloseHandle when done. See Remarks for more information.

If the function fails, the return value is INVALID_HANDLE_VALUE. To get extended error information, call GetLastError.

If an application does not have associated standard handles, such as a service running on an interactive desktop, and has not redirected them, the return value is NULL.

Remarks

Handles returned by GetStdHandle can be used by applications that need to read from or write to the console. When a console is created, the standard input handle is a handle to the console’s input buffer, and the standard output and standard error handles are handles of the console’s active screen buffer. These handles can be used by the ReadFile and WriteFile functions, or by any of the console functions that access the console input buffer or a screen buffer (for example, the ReadConsoleInput, WriteConsole, or GetConsoleScreenBufferInfo functions).

The standard handles of a process may be redirected by a call to SetStdHandle, in which case GetStdHandle returns the redirected handle. If the standard handles have been redirected, you can specify the CONIN$ value in a call to the CreateFile function to get a handle to a console’s input buffer. Similarly, you can specify the CONOUT$ value to get a handle to a console’s active screen buffer.

The standard handles of a process on entry of the main method are dictated by the configuration of the /SUBSYSTEM flag passed to the linker when the application was built. Specifying /SUBSYSTEM:CONSOLE requests that the operating system fill the handles with a console session on startup, if the parent didn’t already fill the standard handle table by inheritance. On the contrary, /SUBSYSTEM:WINDOWS implies that the application does not need a console and will likely not be making use of the standard handles. More information on handle inheritance can be found in the documentation for STARTF_USESTDHANDLES.

Some applications operate outside the boundaries of their declared subsystem; for instance, a /SUBSYSTEM:WINDOWS application might check/use standard handles for logging or debugging purposes but operate normally with a graphical user interface. These applications will need to carefully probe the state of standard handles on startup and make use of AttachConsole, AllocConsole, and FreeConsole to add/remove a console if desired.

Some applications may also vary their behavior on the type of inherited handle. Disambiguating the type between console, pipe, file, and others can be performed with GetFileType.

Handle disposal

It is not required to CloseHandle when done with the handle retrieved from GetStdHandle. The returned value is simply a copy of the value stored in the process table. The process itself is generally considered the owner of these handles and their lifetime. Each handle is placed in the table on creation depending on the inheritance and launch specifics of the CreateProcess call and will be freed when the process is destroyed.

Manual manipulation of the lifetime of these handles may be desirable for an application intentionally trying to replace them or block other parts of the process from using them. As a HANDLE can be cached by running code, that code will not necessarily pick up changes made via SetStdHandle. Closing the handle explicitly via CloseHandle will close it process-wide and the next usage of any cached reference will encounter an error.

Guidance for replacing a standard handle in the process table would be to get the existing HANDLE from the table with GetStdHandle, use SetStdHandle to place a new HANDLE in that is opened with CreateFile (or a similar function), then to close the retrieved handle.

There is no validation of the values stored as handles in the process table by either the GetStdHandle or SetStdHandle functions. Validation is performed at the time of the actual read/write operation such as ReadFile or WriteFile.

Attach/detach behavior

When attaching to a new console, standard handles are always replaced with console handles unless STARTF_USESTDHANDLES was specified during process creation.

If the existing value of the standard handle is NULL, or the existing value of the standard handle looks like a console pseudohandle, the handle is replaced with a console handle.

When a parent uses both CREATE_NEW_CONSOLE and STARTF_USESTDHANDLES to create a console process, standard handles will not be replaced unless the existing value of the standard handle is NULL or a console pseudohandle.

[!NOTE]
Console processes must start with the standard handles filled or they will be filled automatically with appropriate handles to a new console. Graphical user interface (GUI) applications can be started without the standard handles and they will not be automatically filled.

Examples

For an example, see Reading Input Buffer Events.

Requirements

   
Minimum supported client Windows 2000 Professional [desktop apps only]
Minimum supported server Windows 2000 Server [desktop apps only]
Header ProcessEnv.h (via Winbase.h, include Windows.h)
Library Kernel32.lib
DLL Kernel32.dll

See also

Console Functions

Console Handles

CreateFile

GetConsoleScreenBufferInfo

ReadConsoleInput

ReadFile

SetStdHandle

WriteConsole

WriteFile


Функция GetStdHandle



Функция GetStdHandle
извлекает дескриптор для стандартного
ввода данных, стандартного вывода или
стандартной ошибки устройства.

Синтаксис


HANDLE GetStdHandle(


DWORD nStdHandle
// ввод, вывод или ошибка устройства



);

Параметры



nStdHandle

[in] Стандартное устройство, для которого
дескриптор должен быть возвращен. Этот
параметр может быть одним из следующих
значений.


Именованная константа


Предназначение



STD_INPUT_HANDLE


Дескриптор стандартного устройства
ввода данных. Вначале, это — дескриптор
консольного буфера ввода,
CONIN$. 



STD_OUTPUT_HANDLE


Дескриптор устройства стандартного
вывода. Вначале, это — дескриптор
активного экранного буфера консоли,
CONOUT$. 



STD_ERROR_HANDLE


Дескриптор стандартной ошибки
устройства. Вначале, это — дескриптор
активного экранного буфера консоли,
CONOUT$. 

Возвращаемые значения


Если функция завершается успешно,
возвращаемое значение — дескриптор
определяемого устройства. Дескриптор имеет
права доступа
GENERIC_READ
и
GENERIC_WRITE,
если приложение не использовало функцию
SetStdHandle,
чтобы установить стандартный дескриптор с
меньшими правами доступа.


Если функция завершается с ошибкой,
возвращаемое значение — флажок
INVALID_HANDLE_VALUE.
Чтобы получить расширенные данные об
ошибках, вызовите
функцию
GetLastError.

Замечания


Дескрипторы, возвращенные функцией
GetStdHandle, могут
быть использованы прикладными программами,
которым нужно читают из или записывать в
консоль. Когда консоль создана,
дескриптором стандартного ввода является
дескриптор буфера ввода консоли, а
стандартного вывода и обработки
стандартной ошибки является дескриптор
активного экранного буфера консоли. Эти
дескрипторы могут быть использованы

функциями ReadFile
и WriteFile,
или любой из консольных функций, которые
обращаются к консольному буферу ввода или
экранному буферу (например,
функциям
ReadConsoleInput,
WriteConsole,
или
GetConsoleScreenBufferInfo).

Стандартные дескрипторы
процесса могут быть переназначен вызовом
функции
SetStdHandle,
в этом случае функция
GetStdHandle
возвращает переназначенный дескриптор.
Если стандартные дескрипторы были
переназначены, Вы можете задать значение
CONIN$
при вызове к функции
CreateFile,
чтобы получить дескриптор для буфера ввода
консоли. Точно так же Вы можете задать
значение
CONOUT$,
чтобы получить дескриптор для активного
экранного буфера консоли.


Код примера


Пример смотри в статье

Чтение событий
буфера вводимых данных
.

Смотри также


Обзор консольных приложений,
Функции
консоли
, CreateFile,
GetConsoleScreenBufferInfo,
ReadConsoleInput,
ReadFile,
SetStdHandle,
WriteConsole,
WriteFile


Размещение и совместимость GetStdHandle


Windows. NET Server


Да


Windows XP


Да


Windows 2000


Да


Windows NT


Да


Windows Me


Да


Windows 98


Да

Windows 95


Да


Используемая библиотека


Kernel32.lib


Заголовочный файл

 

— объявлено в


Wincon.h


— включено в


Windows.h


Unicode


Нет


Замечания по платформе


Не имеется

Hosted by uCoz

Contents

  • 1 Exception handling
    • 1.1 Capture and storage of exception objects
    • 1.2 Handling of failures in exception handling
    • 1.3 Handling of exception specification violations (removed in C++17)
  • 2 Exception categories
  • 3 Error numbers
  • 4 System error
  • 5 Assertions
  • 6 Stacktrace
  • 7 See also

[edit] Exception handling

The header <exception> provides several classes and functions related to exception handling in C++ programs.

Defined in header <exception>

exception

base class for exceptions thrown by the standard library components
(class) [edit]
Capture and storage of exception objects

uncaught_exceptionuncaught_exceptions

(removed in C++20)(C++17)

checks if exception handling is currently in progress
(function) [edit]

exception_ptr

(C++11)

shared pointer type for handling exception objects
(typedef) [edit]

make_exception_ptr

(C++11)

creates an std::exception_ptr from an exception object
(function template) [edit]

current_exception

(C++11)

captures the current exception in a std::exception_ptr
(function) [edit]

rethrow_exception

(C++11)

throws the exception from an std::exception_ptr
(function) [edit]

nested_exception

(C++11)

a mixin type to capture and store current exceptions
(class) [edit]

throw_with_nested

(C++11)

throws its argument with std::nested_exception mixed in
(function template) [edit]

rethrow_if_nested

(C++11)

throws the exception from a std::nested_exception
(function template) [edit]
Handling of failures in exception handling

Defined in header <exception>

terminate

function called when exception handling fails
(function) [edit]

terminate_handler

the type of the function called by std::terminate
(typedef) [edit]

get_terminate

(C++11)

obtains the current terminate_handler
(function) [edit]

set_terminate

changes the function to be called by std::terminate
(function) [edit]

bad_exception

exception thrown when std::current_exception fails to copy the exception object
(class) [edit]
Handling of exception specification violations (removed in C++17)

unexpected

(removed in C++17)

function called when dynamic exception specification is violated
(function) [edit]

unexpected_handler

(removed in C++17)

the type of the function called by std::unexpected
(typedef) [edit]

get_unexpected

(C++11)(removed in C++17)

obtains the current unexpected_handler
(function) [edit]

set_unexpected

(removed in C++17)

changes the function to be called by std::unexpected
(function) [edit]

[edit] Exception categories

Several convenience classes are predefined in the header <stdexcept> to report particular error conditions. These classes can be divided into two categories: logic errors and runtime errors. Logic errors are a consequence of faulty logic within the program and may be preventable. Runtime errors are due to events beyond the scope of the program and can not be easily predicted.

Defined in header <stdexcept>

logic_error

exception class to indicate violations of logical preconditions or class invariants
(class) [edit]

invalid_argument

exception class to report invalid arguments
(class) [edit]

domain_error

exception class to report domain errors
(class) [edit]

length_error

exception class to report attempts to exceed maximum allowed size
(class) [edit]

out_of_range

exception class to report arguments outside of expected range
(class) [edit]

runtime_error

exception class to indicate conditions only detectable at run time
(class) [edit]

range_error

exception class to report range errors in internal computations
(class) [edit]

overflow_error

exception class to report arithmetic overflows
(class) [edit]

underflow_error

exception class to report arithmetic underflows
(class) [edit]

tx_exception

(TM TS)

exception class to cancel atomic transactions
(class template)

[edit] Error numbers

[edit] System error

The header <system_error> defines types and functions used to report error conditions originating from the operating system, streams I/O, std::future, or other low-level APIs.

Defined in header <system_error>

error_category

(C++11)

base class for error categories
(class) [edit]

generic_category

(C++11)

identifies the generic error category
(function) [edit]

system_category

(C++11)

identifies the operating system error category
(function) [edit]

error_condition

(C++11)

holds a portable error code
(class) [edit]

errc

(C++11)

the std::error_condition enumeration listing all standard <cerrno> macro constants
(class) [edit]

error_code

(C++11)

holds a platform-dependent error code
(class) [edit]

system_error

(C++11)

exception class used to report conditions that have an error_code
(class) [edit]

[edit] Assertions

Assertions help to implement checking of preconditions in programs.

aborts the program if the user-specified condition is not true. May be disabled for release builds
(function macro) [edit]

[edit] Stacktrace

[edit] See also

We can perform console I/O with the ReadFile() and WriteFile() functions, but it’s easier to use the specific console I/O functions, ReadConsole() and WriteConsole(). The ReadConsole() and WriteConsole() functions both accept TCHAR, or generic characters. TCHAR characters can be used to describe either ANSI single byte characters or wide double byte Unicode characters; thus, the TCHAR type allows us to be narrow/wide neutral.

The GetStdHandle() function gives us a mechanism for retrieving the standard input, STDIN, the Standard Output, STDOUT, and the standard error handles, STDERR. The GetStdHandle() function takes a single parameter that can be one of three values, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, and STD_ERROR_HANDLE.

#include "stdafx.h"
#include <Windows.h>

int main()
{
 HANDLE hStdin, hStdout, hStderr;

 hStdin = GetStdHandle(STD_INPUT_HANDLE);
 if (hStdin != INVALID_HANDLE_VALUE) {
 printf("Handle to standard input acquired.n");
 }
 //combine the steps
 if ((hStdout = GetStdHandle(STD_OUTPUT_HANDLE)) != INVALID_HANDLE_VALUE) {
 printf("Handle to standard output acquired.n");
 }

 if ((hStderr = GetStdHandle(STD_ERROR_HANDLE)) != INVALID_HANDLE_VALUE){
 printf("Handle to standard error acquired.n");
 }

 return 0;
}

The SetConsoleMode() and GetConsoleMode() functions allow us to modify and view the console mode for the input or screen buffer. The console mode describes how characters are processed via flags; five commonly used flags, which are enabled by default, are ENABLE_LINE_INPUT, ENABLE_ECHO_INPUT, ENABLE_PROCESSED_INPUT, ENABLE_PROCESSED_OUTPUT, and ENABLE_WRAP_AT_EOL_OUTPUT. Both the SetConsoleMode() and GetConsoleMode() functions return a Boolean value indicating success or failure.

 #include "stdafx.h"
#include <Windows.h>

void PrintConsoleInputFlags(DWORD dwFlags);
void PrintConsoleOutputFlags(DWORD dwFlags);

int main()
{
 HANDLE hStdin, hStdout;
 DWORD dwStdinFlags, dwStdoutFlags;
 //get handles to stdin and stdout
 hStdin = GetStdHandle(STD_INPUT_HANDLE);
 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

 if (hStdin == INVALID_HANDLE_VALUE || hStdout == INVALID_HANDLE_VALUE) {
 printf("Error getting stdin or stdout.n");
 exit(EXIT_FAILURE);
 }

 //GetConsoleMode returns Boolean value indicating success
 if (GetConsoleMode(hStdin, &dwStdinFlags)) {
 printf("Console mode for stdin acquired.n");
 }

 if (GetConsoleMode(hStdout, &dwStdoutFlags)) {
 printf("Console mode for stdout acquired.n");
 }

 printf("nFor stdin: n");
 PrintConsoleInputFlags(dwStdinFlags);
 

 printf("nFot stdout: n");
 PrintConsoleOutputFlags(dwStdoutFlags);

 return 0;
}

void PrintConsoleInputFlags(DWORD dwFlags) {
 if (dwFlags & ENABLE_ECHO_INPUT) {
 printf("Echo input enabled.n");
 }
 if (dwFlags & ENABLE_INSERT_MODE) {
 printf("Insert mode enabled.n");
 }
 if (dwFlags & ENABLE_LINE_INPUT) {
 printf("Line input enabled.n");
 }
 if (dwFlags & ENABLE_MOUSE_INPUT) {
 printf("Mouse input enabled.n");
 }
 if (dwFlags & ENABLE_PROCESSED_INPUT) {
 printf("Processed input enabled.n");
 }
}

void PrintConsoleOutputFlags(DWORD dwFlags) {
 if (dwFlags & ENABLE_PROCESSED_OUTPUT) {
 printf("Processed output enabled.n");
 }
}

The ReadConsole() and WriteConsole() functions both return TRUE if and only if the action succeeds. The ReadConsole() function takes a handle to the input buffer, and then two length parameters indicating lengths in generic characters, not bytes. The WriteConsole() function is essentially the same, except the buffer is a pointer to a constant.

#include "stdafx.h"
#include <Windows.h>


int main()
{
 const int Buffer_Length = 2056;

 HANDLE hStdin, hStdout;
 DWORD dwCharsRead, dwCharsWritten;
 TCHAR tszBuffer[Buffer_Length];

 BOOL bSuccess;

 //get handles for standard input
 //and standard output
 hStdin = GetStdHandle(STD_INPUT_HANDLE);
 hStdout = GetStdHandle(STD_OUTPUT_HANDLE);

 if (hStdin == INVALID_HANDLE_VALUE) {
 exit(EXIT_FAILURE);
 }
 else {
 printf("Handle to stdin acquired.n");
 }

 if (hStdout == INVALID_HANDLE_VALUE) {
 exit(EXIT_FAILURE);
 }
 else {
 printf("Handle to stdout acquired.n");
 }

 printf("Please enter in some text!n");

 bSuccess = ReadConsole(hStdin, tszBuffer, Buffer_Length - 2, &dwCharsRead, NULL);

 //replace the enter with the null character
 if (bSuccess) {
 printf("Successfully read from console.n");
 printf("%d chars read.n", dwCharsRead);
 tszBuffer[dwCharsRead - 2] = '';
 }

 bSuccess = WriteConsole(hStdout, tszBuffer, _tcslen(tszBuffer), &dwCharsWritten, NULL);

 if (bSuccess) {
 printf("nSuccessfully wrote to console.n");
 printf("%d chars written.n", dwCharsWritten);
 }


 //close the handles!
 CloseHandle(hStdin);
 CloseHandle(hStdout);

 return 0;
}

Note that a Windows process can only have one console.

Creating and deleting directories involves a pair of thankfully simple functions, CreateDirectory() and RemoveDirectory(). The CreateDirectory() function takes two arguments, the first is the pathname to the directory to create, and the second is a pointer to a SECURITY_ATTRIBUTES structure. For the second argument, we can simply put NULL.  The RemoveDirectory() function has only one parameter, the name of the directory to be removed. Both functions return a Boolean value stating the success or failure of the function call.

#include <Windows.h>
#include <stdlib.h>
#include <stdio.h>

int _tmain(int argc, char argv[]){

	if((CreateDirectory("exampleDir", NULL))==FALSE){
		printf("Error creating directory.n");
		exit(EXIT_FAILURE);
	} else {
		printf("Directory created.n");
	}

	if((RemoveDirectory("exampleDir"))==FALSE){
		printf("Error removing directory.n");
		exit(EXIT_FAILURE);
	} else {
		printf("Directory deleted.n");
	}

	return 0;

}

A process has a working directory. We can both get and set the working directory. The GetCurrentDirectory() function takes two arguments, the size of the buffer in characters, and the buffer to write the full pathname of the directory to. The SetCurrentDirectory() function has one parameter, the name of the directory we wish to set as the current working directory.

#include <Windows.h>
#include <stdio.h>
#include <stdlib.h>

int _tmain(int argc, char argv[]){

	TCHAR szBuffer[MAX_PATH + 1];
	TCHAR szNewDir[] = _T("testdir");
	DWORD dwRValue = 0;
	CreateDirectory(szNewDir, NULL);

	dwRValue = GetCurrentDirectory(MAX_PATH, szBuffer);

	if(dwRValue != 0){
		_tprintf(_T("Current directory is %sn"), szBuffer);
	} else {
		printf("Error getting current directory.n");
		exit(EXIT_FAILURE);
	}

	if(!(SetCurrentDirectory(szNewDir))){
		printf("Could not change directory.n");
		exit(EXIT_FAILURE);
	} else {
		printf("Directory changed.n");
	}

	if(!(GetCurrentDirectory(MAX_PATH, szBuffer))){
		printf("Could not get working directory.n");
		exit(EXIT_FAILURE);
	} else {
		_tprintf(_T("Current directory is %sn"), szBuffer);
	}

	return 0;

}

The ReadConsole() and WriteConsole() functions are used to read and write from the console. Both the ReadConsole() and WriteConsole() functions take five arguments; the first argument is a HANDLE to standard input or standard output.  We get a standard handle using the GetStdHandle() function, which takes a single parameter that can be one of three DWORD values, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, and STD_ERROR_HANDLE .

#include <Window.h>
#include <stdlib.h>
#include <stdio.h>

int _tmain(int argc, char argv[]){

	//create a handle to standard output
	HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
	TCHAR *szMsg = "Nice boat!";
	DWORD dwCount;
	BOOLEAN bRValue;

	if(hOut==INVALID_HANDLE_VALUE){
		printf("Could not open a file handle to standard output.n");
		exit(EXIT_FAILURE);
	}

	WriteConsole(hOut, szMsg, _tcslen(szMsg),  &dwCount, NULL);

	if(dwCount == _tcslen(szMsg)){
		printf("nThe full message has been printed.n");
	} else {
		printf("nError! The full message has not printed.n");
	}

	CloseHandle(hOut);

	return 0;

}

Note that a process can only have one console at a time.

Like *nix, a Windows process has three standard devices for input, output, and error reporting; to access these standard devices, Windows needs a HANDLE that is acquired with the GetStdHandle() function. The GetStdHandle() function accepts a DWORD argument and returns a HANDLE on success and INVALID_HANDLE_VALUE otherwise.

The parameter can be one of three values, STD_INPUT_HANDLE, STD_OUTPUT_HANDLE, and STD_ERROR_HANDLE.

#include <Windows.h>
#include <stdio.h>

int _tmain(int argc, _TCHAR* argv[])
{
	printf("%x n", STD_INPUT_HANDLE);
	printf("%x n", STD_OUTPUT_HANDLE);
	printf("%x n", STD_ERROR_HANDLE);

	return 0;
}

The GetStdHandle() function does not create a new or duplicate handle on a standard device. No matter how many times we call GetStdHandle(), the function returns the same handle.

#include 
#include 

int _tmain(int argc, _TCHAR* argv[])
{
	char *szString = "For great justice! ";
	DWORD dwBytesWritten;
	HANDLE hStdin = GetStdHandle(STD_OUTPUT_HANDLE);

	if(hStdin == INVALID_HANDLE_VALUE){
		printf("Error accessing standard input.n");
	}

	if(WriteFile(hStdin, szString, strlen(szString), &dwBytesWritten, NULL)){
		printf("No problems writing to standard input.n");
	}

	return 0;
}

The SetStdHandle() function enables us to redirect standard input, standard output, and standard error. The SetStdHandle() function takes two arguments; the first argument is a DWORD value indicating whether we are redirecting standard input, standard output, or standard error, and the second value is a handle to the file we want to redirect to.

#include <Windows.h>
#include <stdio.h>

int _tmain(int argc, _TCHAR* argv[])
{

	HANDLE hFile;

	hFile = CreateFile("textfile.txt", GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

	if(hFile == INVALID_HANDLE_VALUE){
		printf("Error opening file.");
		return -1;
	}

	if(SetStdHandle(STD_OUTPUT_HANDLE, hFile)==FALSE){
		printf("Error setting standard handle.n");
		return -2;
	}

	//will print to the file
	//not to the console
	printf("I survived the Kobayashi Maru!n");
	printf("Photons be free!n");
	
	return 0;
}

Note that if you call GetStdHandle() after redirecting the standard input, output or error you will get the redirected handle! To recover standard output to the console screen, call CreateFile() with CONOUT$, likewise, a call to CreateFile() with CONIN$ will recover a handle to standard input.

Организация низкоуровнего консольного ввода-вывода

Низкий
уровень консольного ввода-вывода по
сравнению с высоким уровнем и обладает
более широкими и гибкими возможностями.
Низкоуровневые функции консольного
ввода-вывода обеспечивают прямой доступ
к входному и экранным буферам консоли,
предоставляя приложению доступ к
событиям мыши и клавиатуры, а также к
информации об изменении размеров окна
консоли. Функции низкоуровневого
ввода-вывода позволяют приложению иметь
доступ по чтению-записи к указанному
числу последовательных символьных
ячеек в экранном буфере или к прямоугольному
блоку символьных ячеек в указанной
позиции экранного буфера.

Поддержка работы с мышью в консоли

Большое
достоинство консольных приложений —
встроенная средствами Windows поддержка
мыши. Она реализуется с помощью функции
ReadConsolelnput. Важно отметить, что эта функция
используется для получения информация
о событиях не только мыши, но и о событиях
клавиатуры, изменении размера окна и
так далее.

B00L
ReadConsoleInput(

HANDLE
hConsolelnput,

PINPUT_RECORD
lpBuffer,

DWORD
nLength,

LPDWORD
lpNumberOfEventsRead);

hConsolelnput
— стандартный
дескриптор
ввода,
полученный
функцией
GetStdHandle;

lpBuffer
— указатель на буфер, в который
записывается информация о событии мыши,
— эта область памяти имеет структуру,
называемую INPUT_ RECORD;

nLength
— размер во входных записях буфера, на
который указывает указатель lpBuffer;

lpNumberOfEventsRead
— определяет переменную, в которую
записывается
действительное число
прочитанных записей входного буфера.

Расширенная поддержка клавиатуры в консоли

Функции
работы с текстом высокого уровня не
дают других возможностей работы с
клавиатурой, кроме как примитивного
ввода текста. При разработке программ
текстового режима часто требуется
информация о состоянии управляющих
клавиш, о факте удержания клавиши, что
может свидетельствовать о желании
пользователя повторить ввод некоторого
символа или просто о желании получить
тривиальный скан-код клавиши. Эти и
другие события клавиатуры доступны
программе посредством функции
ReadConsolelnput. События клавиатуры генерируются
при нажатии любой клавиши. Процесс их
обработки аналогичен обработке событий
мыши. В первую очередь заполняется о
нажатии некоторых управляющих клавиш.
Для всех остальных клавиш просто
фиксируется факт нажатия. При этом
необходимо помнить, что однократному
нажатию клавиши реально соответствуют
два события — нажатие и отпускание
клавиши. В связи с этим программа выводит
два сообщения. На практике этого можно
избежать, анализируя поле bKeyDown: bKeyDown=l,
когда клавиша нажата; bKeyDown=0, когда
клавиша отпущена. Выход из программы —
при выполнении любых действий с мышью.

Описание используемых функций

 Wsprintf

Функция  форматирует
и хранит ряд символов и значений в
буфере. Любые параметры преобразуются
и копируются в буфер выводимых данных
согласно соответствующей спецификации
формата в форматируемой строке. Функция
добавляет в конец символ завершающего
нуля к символам, которые она пишет, но
в возвращаемом значении она не включает
его в число символов.

int wsprintf(      

    LPTSTR lpOut,

    LPCTSTR lpFmt,

     

);

lpOut

указатель
на буфер, который получит форматированный
вывод данных. Максимальный размер буфера
составляет 1024 байта.

lpFmt
указатель
на строку с завершающим нулем, которая
содержит в себе спецификации управления
форматом. В дополнение к обычным
символам ASCII спецификация
формата для каждого параметра показывается
в этой строке.

… –
указывает
один или несколько дополнительных
параметров. Число и тип характеристик
параметра зависят от соответствующих
спецификаций управления форматом в
параметре lpFmt.

GetStdHandle

Функция
извлекает дескриптор для стандартного
ввода данных, стандартного вывода или
стандартной ошибки устройства.

HANDLE
GetStdHandle(

DWORD nStdHandle 

);

nStdHandle
стандартное
устройство, для которого дескриптор
должен быть возвращен.

На
вход функции GetStdHandle должно быть подано
одно из следующих значений:

  • STD_INPUT_HANDLE
    = -10 — дескриптор стандартного входного
    потока;

  • STD_OUTPUT_HANDLE
    = -11 — дескриптор стандартного выходного
    потока;

  • STD_ERROR_HANDLE
    = -12 — дескриптор стандартного потока
    ошибок.

Дескрипторы,
возвращенные функцией GetStdHandle, могут
быть использованы прикладными программами,
которым нужно читать из консоли или
записывать в консоль. Когда консоль
создана, дескриптором стандартного
ввода является дескриптор буфера ввода
консоли, а стандартного вывода и обработки
стандартной ошибки является дескриптор
активного экранного буфера консоли.
Эти дескрипторы могут быть использованы
функциями ReadFile и WriteFile,
или любой из консольных функций, которые
обращаются к консольному буферу ввода
или экранному буферу.

Стандартные
дескрипторы процесса могут быть
переназначен вызовом функции SetStdHandle,
в этом случае функция GetStdHandle возвращает
переназначенный дескриптор. Если
стандартные дескрипторы были переназначены,
можно задать значение CONIN$
при вызове к функции CreateFile,
чтобы получить дескриптор для буфера
ввода консоли. Точно так же можно задать
значение CONOUT$,
чтобы получить дескриптор для активного
экранного буфера консоли.

WriteConsole

Функция
записывает символьную строку в экранный
буфер консоли, начинающийся с текущей
позиции курсора.

BOOL
WriteConsole(

HANDLE hConsoleOutput,          

CONST VOID * lpBuffer,          

DWORD nNumberOfCharsToWrite,    

LPDWORD lpNumberOfCharsWritten, 

LPVOID lpReserved  );

hConsoleOutput

дескриптор
экранного буфера консоли.

lpBuffer

указатель
на буфер, содержащий символы, которые
будут записаны в экранный буфер консоли.
Общий размер должен быть меньше чем
64КБ.

nNumberOfCharsToWrite
число TCHARs для
чтения.

lpNumberOfCharsWritten

указатель
на переменную, которая принимает число
фактических записей TCHARs.

lpReserved


зарезервировано,
должно быть ПУСТО (NULL).

Функция WriteConsole записывает
символы в экранный буфер консоли. Она
ведет себя подобно функции WriteFile и,
кроме того, может записывать или в
режиме ANSI или УНИКОДЕ.
Чтобы создать приложение, которое
поддерживает единственный набор
источников информации, совместимых с
обоими режимами, используют
функцию WriteConsole, а
не WriteFile.
Хотя WriteConsole может
быть использована только с дескриптором
экранного буфера консоли,  WriteFile может
быть использована с другими дескрипторами
(такими как файлы или каналы). 
Функция WriteConsole завершается
ошибкой, если используется со стандартным
дескриптором, который был переназначен,
чтобы быть несколько другим, чем
консольный дескриптор.

Функция WriteConsole записывает
символы в экранном буфере консоли в
текущей позиции курсора. Позиция курсора
продвигается вперед, по мере написания
символов. 

Символы
пишутся, с использованием атрибутов
цвета текста и цвета фона, связанных с
экранным буфером консоли. 

SetConsoleCursorPosition

Функция
устанавливает
позицию курсора в заданном экранном
буфере консоли.

BOOL
SetConsoleCursorPosition(

HANDLE hConsoleOutput, 

COORD dwCursorPosition );

hConsoleOutput

дескриптор
экранного буфера консоли.

dwCursorPosition
структура COORD,
которая устанавливает новую позицию
курсора. Координаты — столбец и ряд
символьного знакоместа экранного
буфера. Координаты должны быть в пределах
границ экранного буфера консоли.

Если
новая позиция курсора не в пределах
границ окна экранного буфера консоли,
начало координат окна изменяется, чтобы
сделать курсор видимым.

SetConsoleTitle

Функция
предназначена
для отображения заголовка окна консоли

B00L
SetConsolеTitle(LPCTSTR lpConsoleTitle) ;

Функция
SetConsoleTitle имеет один параметр — указатель
на строку с заголовком консоли,
заканчивающуюся нулем.

FreeConsole

Функция  отключает
вызывающий процесс от его консоли.

BOOL
FreeConsole(VOID);

Если
другие процессы совместно используют
консоль, консоль не разрушается, но
вызывающий процесс не может ссылаться
на неё.

Процесс
может использовать функцию FreeConsole,
чтобы отключить себя от своей текущей
консоли, а затем он может
вызывать функцию AllocConsole,
чтобы создать новую консоль
или AttachConsole,
чтобы подключиться на другую консоль.

AllocConsole

Функция 
назначает новую консоль для вызывающего
процесса.

BOOL
AllocConsole(void);

Процесс
может быть связан с только одной консолью,
так что функция AllocConsole завершается
ошибкой, если вызывающий процесс уже
имеет консоль. Процесс может
использовать функцию FreeConsole,
чтобы отключить себя от своей текущей
консоли, затем он может
вызывать функцию AllocConsole,
чтобы создать новую консоль
или функцию AttachConsole,
чтобы подключиться к другой консоли.

Если
вызывающий процесс создает дочерний
процесс, ребенок наследует новую консоль.

Функция AllocConsole инициализирует
стандартный ввод данных, стандартный
вывод и обработку стандартной ошибки
для новой консоли. Дескриптор стандартного
ввода – это дескриптор буфера ввода
консоли, а дескрипторы стандартного
вывода, и стандартной ошибки – это
дескрипторы экранного буфера консоли.
Чтобы получить эти дескрипторы,
используйте функцию GetStdHandle.

CharToOemA

Функция  преобразует
строку в определенный OEM набор
символов.

BOOL CharToOem(      

LPCTSTR lpszSrc,

LPSTR lpszDst

);

lpszSrc
–указатель
на преобразуемую строку с завершающим
нулем.

lpszDst

указатель
на буфер для транслируемой строки. Если
функция CharToOem используется
как функция ANSI,
строка может быть преобразована на
месте путем установки параметра lpszDst для
того же самого адреса, что и параметр lpszSrc.
Этого нельзя делать, если CharToOem используется
как широкосимвольная (Unicode)
функция.

SetConsoleTextAttribute

Функция  устанавливает
атрибуты символов, записанных в экранный
буфер консоли функцией WriteFile или WriteConsole,
или повторенных в эхо
режиме функцией ReadFile или ReadConsole.
Эта функция воздействует на текст,
записанный после вызова функции.

BOOL
SetConsoleTextAttribute(

HANDLE hConsoleOutput, 

WORD wAttributes       

);

hConsoleOutput
дескриптор
экранного буфера консоли.

wAttributes

цвет текста
и фона

Чтобы
выяснять текущие атрибуты цвета экранного
буфера, необходимо
вызвать функцию GetConsoleScreenBufferInfo.

ReadConsoleInputA

Функция  читает
данные из консольного буфера ввода и
удаляет их из буфера.

BOOL
ReadConsoleInput(

HANDLE hConsoleInput,    

PINPUT_RECORD lpBuffer , 

DWORD nLength ,          

LPDWORD lpNumberOfEventsRead 

);

hConsoleInput

дескриптор
консольного буфера ввода.

lpBuffer
указатель
на массив структур INPUT_RECORD,
который принимает данные буфера ввода.
Общий размер требуемого массива должен
быть меньше чем 64КБ.

nLength
размер
массива, указанного параметром lpBuffer,
в элементах массива.

lpNumberOfEventsRead
указатель
на переменную, которая принимает число
прочитанных записей вводимых данных.

Если
число записей, предписываемых
в параметре nLength, превышает
число записей, доступных в буфере,
читается доступное число. Функция не
возвращает значения до тех пор, пока,
по крайней мере, не будет прочитана одна
запись вводимых данных.

Процесс
может установить консольный дескриптор
буфера ввода в одной из функций
ожидания,
чтобы выяснить, когда имеется непрочитанный
консольный ввод данных. Когда буфер
ввода не пуст, состояние консольного
дескриптора буфера ввода является
сигнальным.

Чтобы
выяснять число непрочитанных записей
вводимых данных в буфере ввода консоли,
используют  функцию GetNumberOfConsoleInputEvents.
Чтобы
читать записи вводимых данных из
консольного буфера ввода без воздействия
на число непрочитанных записей,
используют функцию PeekConsoleInput.
Чтобы сбросить все непрочитанные записи
в буфере ввода консоли,
используют функцию FlushConsoleInputBuffer.

ExitProcess

Функция  заканчивает
работу процесса и всех его потоков.

VOID ExitProcess(

UINT uExitCode 

);

uExitCode
определяет
код выхода для процесса, и для всех
потоков, которые завершают работу в
результате вызова этой функции. Для
того, чтобы получить значение выхода
из процесса необходимо используют
функцию GetExitCodeProcess.
Для того, чтобы получить значение выхода
из потока необходимо использовать
функцию GetExitCodeThread.

Функция ExitProcess 
предпочтительный метод завершения
процесса. Эта функция обеспечивает
чистое отключение процесса. Такое
завершение включает в себя вызов функций
точек входа всех связанных динамически
подключаемых библиотек (DLL)
со значениями, указывающими, что процесс
отключается от  DLL.
Если процесс заканчивается путем
вызова TerminateProcess, DLL,
к которым процесс подключен, не
уведомляются о завершении процесса.
После того, как все связанные DLL исполнили
любое значение завершения, эта функция
завершает работу текущего процесса.

Завершение
процесса происходит по нижеследующим
причинам:

1.
Все дескрипторы объектов, открытые
процессом, закрываются.

2.
Все потоки в процессе завершают свою
работу по исполнению кода.

3.
Состояние объекта процесса становится
сигнальным, удовлетворяя любые потоки,
которые ждали завершения процесса.

4.
Состояния всех потоков процесса,
становятся сигнальными, удовлетворяя
любые потоки, которые ждали завершения
работы потоков.

5.
Состояние завершения процесса изменяется
из STILL_ACTIVE в
значение выхода процесса.

Завершение
процесса не заставляет дочерние процессы
закончить свою работу.

Завершение
процесса необязательно удаляет объект
процесса из операционной системы. Объект
процесса удаляется тогда, когда
закрывается последний дескриптор
процесса.

KEY_EVENT
— клавиатурное
событие

Смещение

Длина

Значение

+4

4

При
нажатии клавиши значение поля больше
нуля.

+8

2

Количество
повторов при удержании клавиши.

+10

2

Виртуальный
код клавиши.

+12

2

Скан-код
клавиши.

+14

2

Для
функции ReadConsoleInputA-младший байт
равен ASCII-коду клавиши. Для
функции ReadConsoleInputW слово содержит
код клавиши в двухбайтной кодировке
(Unicode).

+16

4

Содержится
состояния управляющих клавиш. Может
являться суммой следующих
констант:
RIGHT_ALT_PRESSED equ 1h
LEFT_ALT_PRESSED
equ 2h
RIGHT_CTRL_PRESSED equ 4h
LEFT_CTRL_PRESSED equ
8h
SHIFT_PRESSED equ 10h
NUMLOCK_ON equ 20h
SCROLLLOCK_ON
equ 40h
CAPSLOCK_ON equ 80h
ENHANCED_KEY equ 100h
Смысл
констант очевиден.

MOUSE_EVENT
– событие
с мышью

Смещение

Длина

Значение

+4

4

Младшее
слово — Х-координата курсора мыши,
старшее
слово — Y-координата мыши.

+8

4

Описывает
состояние кнопок мыши. Первый бит —
левая кнопка, второй бит — правая
кнопка, третий бит — средняя кнопка.
Бит установлен — кнопка нажата.

+12

4

Состояние
управляющих клавиш. Аналогично
предыдущей таблице.

+16

4

Может
содержать следующие значения:
MOUSE_MOV
equ 1h; было движение мыши
DOUBLE_CL equ 2h;
был двойной щелчок

Overview

In addition to clearing the console screen, this lesson teaches you some about PInvoking, marshaling, and memory management. Also you will learn additional techniques like clearing a specific portion of the screen, and changing the cursor position. Moreover, you will dig into IL and see how System.Console.Clear() method do it. More than that you will learn how to reverse-engineer a .NET assembly and discover the code inside.
In addition, the sample application shows how to perform I/O operations on console using Win32 API calls, and how to show/hide the console cursor. Moreover, it teaches you how to move the text around the console screen.
  

Table of Contents 

Table of contents of this article:

  • Overview
  • Table of Contents
  • Introduction
  • GetStdHandle() Function
  • A Note about Marshaling
  • GetConsoleScreenBufferInfo() Function
  • A Note about Memory Management
  • A Look Inside the Memory
  • FillConsoleOutputCharacter() Function
  • SetConsoleCursorPosition() Function
  • Putting Things Together
  • Clearing the Console Screen
  • A Look Inside the .NET Library
  • References
  • Code Sample (The Tiny Console Library)
  • Summary 

Introduction

I am one of the huge supporters of console when it comes to the fact that we need simple and fast application such as tools and utilities.

One of the common needs of console is performing clear-screens (CLSs) when necessary.

When dealing with the Command Prompt you can order it to clear the screen by submitting the command «cls» and pressing Enter.

But, programmatically, you have no way to order the command «cls,» so you must find a substitute (or more.)

Starting from version 2.0 of the .NET Framework, System.Console has been added a new method, it is the Clear() method for clearing the console screen.

For versions before 2.0, or if you need to do it the hard-way, or even if you want to have more control over the clearing progress such as clearing a specific portion of the screen, you must dig into Win32 API and call special functions for doing so.

Clearing the console screen done through four Win32 API functions, GetStdHandle(), GetConsoleScreenBufferInfo(), FillConsoleOutputCharacter() and SetConsoleCursorPosition(). It is worth noting that all these functions located in Kernel32.dll library.

Be sure that the System.Console.Clear() method in .NET 2.0 and descendant call these four functions -and more- initially. 

GetStdHandle() Function

This method is the gate for dealing with console via the API. Almost every console operation requires a call to GetStdHandle() first.

GetStdHandle() simply returns the handle for the standard input, output, error device («stream» in .NET methodology.) This function takes a single argument specifies which standard device (stream) we wish to retrieve the handle for.

The syntax of this function -in C- is as following:

HANDLE GetStdHandle(
  DWORD nStdHandle
); 

The nStdHandle argument can take one of three values:

  • STD_INPUT_HANDLE = -10
    Specifies the standard input device (stream.)
  • STD_OUTPUT_HANDLE = -11
    Specifies the standard output device (stream.)
  • STD_ERROR_HANDLE = -12
    Specifies the standard error device (stream.) It is always the output device (stream.)

As its names implies, input device used for receiving user input, output device used for writing output to the user, and error device used too for writing error information to the user. In .NET, you access these devices (streams) via nice wrappers. They are the static properties In, Out, and Error of the Console class. I guess that after playing with these nice wrappers you now know what standard devices are.

Because there’s no such way for .NET to interoperate with Win32 API directly, you need to create wrappers for the Win32 API functions you use, and this is done through PInvoking.

PInvoke stands for Platform Invocations. It is the mechanism for the .NET to interoperate with its «girlfriend» Win32 API.

The PInvoke method for GetStdHandle() is as following — code assumes that you add a using statement for the System.Runtime.InteropServices namespace:

[DllImport("Kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle); 

The DllImport attribute is required for PInvoke methods so that you can specify the DLL which the function resides in. Also DllImport have a very important role when you need to change the name of the method. If you need to change the name of the PInvoke method then you must set the property EntryPoint of DllImportAttribute to the original name of the function in the DLL. If you have not set this property, .NET will search the DLL for the method and will throw a System.EntryPointNotFoundException exception if it is not found.
«static» and «extern» are modifiers required for PInvoke methods.
Because some data types do not exist in .NET, you need to find a substitute. In other words, you need to marshal unmanaged Win32 data types to the new managed .NET types. So we have marshaled HANDLE to System.IntPtr and DWORD to System.Int32.

Only a single output handle serves the console application (the same for input and error devices,) so do not close that opened handle using the CloseHandle() function because it will result that you cannot write to the console any more, unless you open it again.

A Note about Marshaling 

Because there are plenty of Win32 data types that do not have correspondents in .NET, you must do Marshaling. Marshaling is the process of creating a bridge between .NET types and unmanaged types. Marshaling converts .NET types into unmanaged types. As we have seen in the last code, .NET does not contain DWORD, and because DWORD is a 32-bit unsigned integer we have to marshal it as System.UInt32. However, we marshaled it in GetStdHandle() as System.Int32! While unsigned integers (inculding DWORD) do not accept negative numbers, GetStdHandle() requires DWORD. Actually, it is OK because negative values can be stored in DWORD in a special way. For example, -10 stored as FFFFFFF6, -11 stored as FFFFFFF5, and so on. And that what GetStdHandle() actually does.

In addition, you may notice that we have marshaled HANDLE as System.IntPtr, because IntPtr is the best type fits to any Win32 handle. Moreover, .NET supports managed handles via the abstract SafeHandle and CriticalHandle classes.

It is worth mentioning that System.Runtime.InteropServices.MarshalAsAttribute attribute can have a noticeable effect over the marshaling process.

Good resources describe the marshaling process and the Win32 data types can be found in the references section.

Managed code is the .NET code that runs inside the CLR (Common Language Runtime,) because CLR manages and controls this code. Conversely, unmanaged code is the code other than the managed code. It is legal that you write code in .NET that runs outside the CLR such as direct memory management and that code called Unsafe Code because it is error-prone and may result to an unpredictable behavior. Also, MC++ allows you to write unmanaged code together with the managed code.

GetConsoleScreenBufferInfo() Function

The third method we will use is the GetConsoleScreenBufferInfo(). This method retrieves information about a specific console screen buffer (in other words, device.) This information is like console screen buffer size, the location and bounds of the console window, and the cursor position inside the screen.

The definition of this function is as following:

BOOL GetConsoleScreenBufferInfo(
  HANDLE hConsoleOutput,
  [out] SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo
);

struct CONSOLE_SCREEN_BUFFER_INFO {
  COORD dwSize;
  COORD dwCursorPosition;
  WORD wAttributes;
  SMALL_RECT srWindow;
  COORD dwMaximumWindowSize;
}

struct COORD {
  SHORT X;
  SHORT Y;
}

struct SMALL_RECT {
  SHORT Left;
  SHORT Top;
  SHORT Right;
  SHORT Bottom;
}

Lengthy, isn’t it? Yeah, that’s true. Beside the GetConsoleScreenBufferInfo(), there’re three more structures, because the type of second argument of the function is the first structure, and the first structure in turn references the second and third structures.

The CONSOLE_SCREEN_BUFFER_INFO structure represents the console information. This information is:

  • The size of the console screen buffer in character rows and columns.
  • The position of the cursor in the console screen in character rows and columns.
  • Characteristics of the character written to the console like the fore color and back color.
  • The location and bounds of the console window.
  • The maximum size for the console window if we take into account the font size and the screen buffer size.

What is screen buffer and window size and what is the difference? Start the Command Prompt and right click its icon in the taskbar and go to the layout tab of the properties dialog box. Take some time playing with the values in this tab.
Window size is the size of the console window -like any other window.- Screen buffer size determines the amount of text which the console screen can hold, so you always see the vertical scroll bar because the buffer height is bigger than the window height.

Here’s the PInvoke method and the marshaling types because .NET does not have such those three structures:

[DllImport("Kernel32.dll")]
public static extern int GetConsoleScreenBufferInfo
    (IntPtr hConsoleOutput,
    out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public COORD dwSize;
    public COORD dwCursorPosition;
    public ushort wAttributes;
    public SMALL_RECT srWindow;
    public COORD dwMaximumWindowSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
    public short Left;
    public short Top;
    public short Right;
    public short Bottom;
}

Because .NET does not contain such these three structures, and we do not have any substitutes, so we have to marshal it, we have created it manually and mapped unmanaged to the managed data types. And because memory layout of such these object is very important, we added the StructLayout attribute and specified LayoutKind.Sequential that informs the marshaller that this structure will laid-out in memory sequentially so the first field comes before the second and so on. Also you might notice that WORD unmanaged data type is an unsigned 32-bit integer so we marshaled it as System.UInt32. Also SHORT is signed 16-bit integer so it is very easy to marshal it as System.Int16. Refer to the references section for more information about Win32 data types.

BOOL defined as a 32-bit signed integer, so we have marshaled the return value of the function as Int32 not Boolean because Boolean reserves 4-bits only. It will work well with Boolean, but it is better using Int32. Though, if you want to use Boolean, is helpful decorating the return value with the MarshalAs attribute.

Also, it is very efficient to use the System.Runtime.InteropServices.InAttribute and System.Runtime.InteropServices.OutAttribute to give a notation to the marshaller. The code sample demonstrates this.

A Note about Memory Management 

You already know that every object reserves space in memory. And the memory itself divided into two parts, Heap and Stack. Objects -directly or indirectly- inherit from System.ValueType are stack-based (like structures, enumerations and primitive data types.) Conversely, most objects -directly or indirectly- inherit from System.Object are heap-based (like most objects.) And you know also that heap objects managed by the Garbage Collector (GC) and they may remain in the memory for a long time -actually even if you called System.GC many times.- On the other hand, stack objects are removed from the memory immediately when their scope ends. In addition, you need to know that stack is filled downwards. See figure 1.

Stack Memory

When interoperating with unmanaged code and marshaling types between the .NET and Win32 API (for instance) you need to know how to layout your type in memory. Unmanaged code assumes that types are laid sequentially based on the order of fields. For our example, the CONSOLE_SCREEN_BUFFER_INFO must be laid-out so dwSize comes before dwCursorPosition and so on. See figure 2.

CONSOLE_SCREEN_BUFFER_INFO in Memory

We can dig into more details and see how Windows will store the CONSOLE_SCREEN_BUFFER_INFO structure in the stack and how it will be rendered. See figure 3.

console_screen_buCONSOLE_SCREEN_BUFFER_INFO in Memory _ DetailsCONSOLE_SCREEN_BUFFER_INFO in Memory _ Details

From the diagrams we learn that:

  • Objects are stored downwards in the stack by the order they were created.
  • Object containing objects also stored downwards by the order they were declared.
  • Every object has a size. And this size determined by its containing objects -if it is not a primitive type of course.-

You can get the size of an object using two ways, the sizeof keyword, and System.Runtime.InteropServices.Marshal.SizeOf() method. The second method is preferred. Do you know why? Try, think, and then answer.

Now, we know why the StructLayout attribute is required. And why sequential layout is required. But what if you prefer to change the order of fields? Answer is very simple. Change the LayoutKind to Explicit and decorate every field with FieldOffset attribute and specify its location from the beginning of the structure. In the following code fields are reversed but they perfectly laid-out in memory using the FieldOffset attribute:

[StructLayout(LayoutKind.Explicit)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    [FieldOffset(18)]
    public COORD dwMaximumWindowSize;
    [FieldOffset(10)]
    public SMALL_RECT srWindow;
    [FieldOffset(8)]
    public ushort wAttributes;
    [FieldOffset(4)]
    public COORD dwCursorPosition;
    [FieldOffset(0)]
    public COORD dwSize;
}

If you set the LayoutKind to Auto, you will not be able to interoperate with unmanaged code using the type. Although, if you have omitted the whole StructLayoutAttribute.

Also, from the diagrams illustrated -specially the last one- we can also write our CONSOLE_SCREEN_BUFFER_INFO structure to be as following and remove the other two structures:

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public short dwSizeX;
    public short dwSizeY;
    public short dwCursorPositionX;
    public short dwCursorPositionY;
    public ushort wAttributes;
    public short srWindowLeft;
    public short srWindowTop;
    public short srWindowRight;
    public short srWindowBottom;
    public short dwMaximumWindowSizeX;
    public short dwMaximumWindowSizeY;
} 

Sounds odd, isn’t it? You can also set the LayoutKind to Explicit and start working.

It is also possible to union two fields -or more- into one. For example, merging the two 16-bit integers dwSizeX and dwSizeY into one 32-bit integer, dwSize. It will work very well! In addition, you can divide them in your code using System.Collections.Specialized.BitVector32 structure.

A Look Inside the Memory

Now, this time we are going to do something interesting. We are going to look on our structure in the memory.

For simplicity, we are going to use these types:

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public COORD dwSize;
    public COORD dwCursorPosition;
    public ushort wAttributes;
    public SMALL_RECT srWindow;
    public COORD dwMaximumWindowSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
    public short Left;
    public short Top;
    public short Right;
    public short Bottom;
}

Next, we will initialize the structures with some data to see how it stored in the memory.

unsafe static void Main()
{
    CONSOLE_SCREEN_BUFFER_INFO info
        = new CONSOLE_SCREEN_BUFFER_INFO();

    info.dwSize = new COORD();
    info.dwSize.X = 0xA1;
    info.dwSize.Y = 0xA2;

    info.dwCursorPosition = new COORD();
    info.dwCursorPosition.X = 0xB1;
    info.dwCursorPosition.Y = 0xB2;

    info.wAttributes = 0xFFFF;

    info.srWindow = new SMALL_RECT();
    info.srWindow.Left = 0xC1;
    info.srWindow.Top = 0xC2;
    info.srWindow.Right = 0xC3;
    info.srWindow.Bottom = 0xC4;

    info.dwMaximumWindowSize = new COORD();
    info.dwMaximumWindowSize.X = 0xD1;
    info.dwMaximumWindowSize.Y = 0xD2;

    uint memoryAddress =
        (uint)&info;

    Console.WriteLine(
        "Memory Address: 0x{0:X}",
        memoryAddress);

    Console.WriteLine("Press any key . . .");
    Console.ReadKey(true);

    
    
    System.Diagnostics.Debugger.Break();
}

This code assumes that you enabled the unsafe code from the Build tab of the project properties. Also, it assumes that you run your code in the debugging mode by pressing F5, or by clicking Start Debugging from the Debug menu.

Now, and after breaking the code, click Debug -> Windows -> Memory -> Memory 1 to open a memory window. Figure 4 shows how to open a memory window. And figure 5 shows the memory window opened. Click on the figures to enlarge.

Showing Memory Window

Memory Window

Now, you can locate your structure in memory by typing its memory address. Figure 6 shows the structure in the memory window.

Memory Window + CONSOLE_SCREEN_BUFFER_INFO

Now, take your time looking on how the structure -and its contained structures- laid-out in memory.

FillConsoleOutputCharacter() Function

Last but not least, this is the fourth function we will use. This function fills a specific console buffer portion with a specific character. Passing a white space as the character means clearing this portion.

The syntax of this function is as following:

BOOL FillConsoleOutputCharacter(
  HANDLE hConsoleOutput,
  TCHAR cCharacter,
  DWORD nLength,
  COORD dwWriteCoord,
  [out] LPDWORD lpNumberOfCharsWritten
);

This function takes four input arguments and a single output one, and it returns a value determines whether the function succeeded or failed. If the return value is non-zero (true) then the function succeeded, otherwise failed (that’s for GetConsoleBufferInfo() and SetConsoleCursorPosition() also.)

The five arguments are:

  • hConsoleOutput:
    A handle to an opened console output device to write to.
  • cCharacter:
    The character which to fill the buffer portion with.
  • nLength:
    The number of characters to write.
  • dwWriteCoord:
    A COORD structure defines where to begin writing (the first cell.)
  • lpNumberOfCharsWritten:An output parameters determines the number of characters written.

Here’s the PInvoke method of this function:

We have omitted the COORD structure because we have created it earlier.

[DllImport("Kernel32.dll")]
public static extern int FillConsoleOutputCharacter
    (IntPtr hConsoleOutput, char cCharacter, uint nLength,
    COORD dwWriteCoord, out uint lpNumberOfCharsWritten);

Notice that the unmanaged data types DWORD and LPDOWRD are marshaled to System.UInt32. For more information about unmanaged data types see the references section.

SetConsoleCursorPosition() Function

This function is used to set the cursor position in the console screen buffer.

The syntax of this function is as following:

BOOL SetConsoleCursorPosition(
  HANDLE hConsoleOutput,
  COORD dwCursorPosition
); 

This function takes two arguments the first is a handle for an opened console output device. The second is a value specifies the new cursor position. Note that the new cursor position must be inside the console screen buffer.

The PInvoke method for this function is:

[DllImport("Kernel32.dll")]
public static extern int SetConsoleCursorPosition
    (IntPtr hConsoleOutput, COORD dwCursorPosition);

Putting Things Together 

After all, we have learned the required functions and created the PInvoke method, so we can mix it up and start coding. Here’s the full code:

public const int STD_OUTPUT_HANDLE = -11;
public const char WHITE_SPACE = ' ';

[DllImport("Kernel32.dll")]
public static extern IntPtr GetStdHandle(int nStdHandle);

[DllImport("Kernel32.dll")]
public static extern int GetConsoleScreenBufferInfo
    (IntPtr hConsoleOutput,
    out CONSOLE_SCREEN_BUFFER_INFO lpConsoleScreenBufferInfo);

[DllImport("Kernel32.dll")]
public static extern int FillConsoleOutputCharacter
    (IntPtr hConsoleOutput, char cCharacter, uint nLength,
    COORD dwWriteCoord, out uint lpNumberOfCharsWritten);

[DllImport("Kernel32.dll")]
public static extern int SetConsoleCursorPosition
    (IntPtr hConsoleOutput, COORD dwCursorPosition);

[StructLayout(LayoutKind.Sequential)]
public struct CONSOLE_SCREEN_BUFFER_INFO
{
    public COORD dwSize;
    public COORD dwCursorPosition;
    public ushort wAttributes;
    public SMALL_RECT srWindow;
    public COORD dwMaximumWindowSize;
}

[StructLayout(LayoutKind.Sequential)]
public struct COORD
{
    public short X;
    public short Y;
}

[StructLayout(LayoutKind.Sequential)]
public struct SMALL_RECT
{
    public short Left;
    public short Top;
    public short Right;
    public short Bottom;
}

Clearing the Console Screen 

And this is the code that clears the console screen:

static void Main()
{
    Console.WriteLine("Writing some text to clear.");

    Console.WriteLine("Press any key to clear . . . ");
    Console.ReadKey(true);

    ClearConsoleScreen();
}

public static void ClearConsoleScreen()
{
    
    IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);

    
    CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(handle, out info);

    
    Console.WriteLine("Console Buffer Info:");
    Console.WriteLine("--------------------");
    Console.WriteLine("Cursor Position:");
    Console.WriteLine("t{0}, {1}",
        info.dwCursorPosition.X, info.dwCursorPosition.Y);
    
    Console.WriteLine("Maximum Window Size:");
    Console.WriteLine("t{0}, {1}",
        info.dwMaximumWindowSize.X,
        info.dwMaximumWindowSize.Y);
    
    Console.WriteLine("Screen Buffer Size:");
    Console.WriteLine("t{0}, {1}",
        info.dwSize.X, info.dwSize.Y);
    Console.WriteLine("Screen Buffer Bounds:");
    Console.WriteLine("t{0}, {1}, {2}, {3}",
        info.srWindow.Left, info.srWindow.Top,
        info.srWindow.Right, info.srWindow.Bottom);
    Console.WriteLine("--------------------");

    
    COORD location = new COORD();
    location.X = 0;
    location.Y = 0;
    
    
    

    
    uint numChars;

    FillConsoleOutputCharacter(handle, WHITE_SPACE,
        (uint)(info.dwSize.X * info.dwSize.Y),
        location, out numChars);

    
    COORD cursorLocation = new COORD();
    cursorLocation.X = 0;
    cursorLocation.Y = 0;

    SetConsoleCursorPosition(handle, cursorLocation);
}

Also we can step further and write code that clears a specific portion of the console screen buffer, try this code:

static void Main()
{
    
    AuthenticateUser();
}

public static void AuthenticateUser()
{
    Console.WriteLine("Please enter your password:");
    Console.Write("> "); 

    string input = Console.ReadLine();

    while (input != "MyPassword")
    {
        COORD location = new COORD();
        
        location.X = 2;
        
        location.Y = 1;
        ClearConsoleScreen(location);
        input = Console.ReadLine();
    }

    
    Console.WriteLine("Authenticated!");
}

public static void ClearConsoleScreen
    (COORD location)
{
    
    IntPtr handle = GetStdHandle(STD_OUTPUT_HANDLE);

    
    CONSOLE_SCREEN_BUFFER_INFO info;
    GetConsoleScreenBufferInfo(handle, out info);

    
    uint numChars;

    FillConsoleOutputCharacter(handle, WHITE_SPACE,
        (uint)(info.dwSize.X * info.dwSize.Y),
        location, out numChars);

    SetConsoleCursorPosition(handle, location);
}

A Look Inside the .NET Library 

Here we will look inside the library mscorlib.dll and see how it implements System.Console.Clear() method. This library is the core library that every .NET application must reference, it defines the core classes and components that are essential for every application. Also, it contains classes that are required by the CLR (Common Language Runtime.)

If you are using .NET 2.0 or higher you can continue this section, otherwise, you may pass this section because the Clear() method is new with the .NET 2.0.

MSIL, CIL, and IL all refer to the same thing the intermediate Language.

MSCORLIB stands for Microsoft Common Object Runtime Library.

Using the IL Disassembler 

.NET Framework comes with a tool that allows you to reverse-engineer any assembly and view its MSIL (Microsoft Intermediate Language) instructions. This tool called IL Disassembler (ILDasm). You can find this tool in the .NET install directory in %ProgramFiles%Microsoft SDKsWindows<WindowsVersion>Bin for the version 3.5, and <VSInstallDir>Common7bin for versions before.

After opening ILDasm.exe you can open the file mscorlib.dll inside. Click File -> Open and select the library file which located in %WinDir%Microsoft.NETFramework<RuntimeVersion>. Figure 7 shows IL Disassembler with the mscorlib.dll opened.

ILDASM + mscorlib_dll

Now open the namespace System, then step down to the System.Console class and double-click the Clear() method to show the IL instructions. Take a look on how the Clear() method do it. 

Using Another Tool

If MSIL seems odd, you may try another perfect tools that can reverse-engineer your assembly to your preferred language (like C# or VB.NET for instance.)

Some famous tools are Lutz Roeder’s .NET Reflector and XenoCode Fox.
 

For me, I prefer the first tool because it is faster, simpler, and supports many features.

Of course you can reflect (reverse-engineer) an assembly and learn nice tricks from the code inside.

References

  • Win32 Data Types
  • GetStdHandle() function
  • GetConsoleScreenBufferInfo() function
  • FillConsoleOutputCharacter() function
  • SetConsoleCursorPosition() function
  • CONSOLE_SCREEN_BUFFER_INFO structure
  • COORD structure
  • SMALL_RECT structure

Sample Code (The Tiny Console Library)

This sample code demonstrates some of the hidden functionality of Console applications, such as moving a text around the screen and clearing a specific portion of the screen.

Download the Sample Code

Summary

So you have learned how to clear the console screen using Win32 API. In addition, you learned many techniques including PInvoking, marshaling, memory management, and how to reverse-engineer a .NET assembly and get its code out. Moreover, you learned many ideas to apply when working with unmanaged code via .NET.  Be sure to check the sample application — it provides you with many features that you will not find in the .NET Framework SDK (nor many other SDKs I think.) It illustrates many techniques including how to clear a specific portion of the screen and how to move a text around the screen. In addition, it shows all common console operations, like reading and writing to the screen buffer, done through the Win32 API and .NET. D 

Mohammad Elsheimy is a developer, trainer, and technical writer. He is a MCP, MCTS (WinForms), MCPD (Windows Apps), MCSA (SQL Server), MCSE (Data Analytics), and MCT expertized in .NET Framework technologies, data management and analytics. He is also a Project Management Professional (PMP) and a Quranic Readings Institute (Al-Azhar) graduate specialized in Quranic readings, Islamic legislation, and the Arabic language.

Понравилась статья? Поделить с друзьями:
  • Stb does not support the inserted smart card error 8
  • Stay out как изменить ник
  • Statuscode 500 error internal server error message an internal server error occurred
  • Statuscode 400 error bad request message invalid cookie value перевод
  • Status stack buffer overrun ошибка