Creation of a Console and Creating a Child Process with Redirected Input and Output

Creation of a Console

  • 发现带控制台的父进程创建的控制台子进程的控制台不显示或者显示了之后,printf cout 都看不到
  • 猜想可能是被父亲的控制台影响了?
  • 因为父亲是一个win32的gui程序,控制台是我通过AllocConsole 启动的
  • 于是,我关闭了AllocConsole ,让父进程没控制台
  • 通过CREATE_NEW_CONSOLE 就能让子进程打开自己的控制台并且输出到上面去了

Creation of a Console

  • 控制台应用可以选择是集成父亲的控制台还是自己创建一个新的控制台

  • The system creates a new console when it starts a console process, a character-mode process whose entry point is the main function. For example, the system creates a new console when it starts the command processor. When the command processor starts a new console process, the user can specify whether the system creates a new console for the new process or whether it inherits the command processor’s console.

  • 控制台应用的创建方法

  • A process can create a console by using one of the following methods:

  • 使用CREATE_NEW_CONSOLE 创建控制台,否则默认会集成父亲的,此时无法保证子进程会收到输入给父进程的消息

  • A GUI or console process can use the CreateProcess function with CREATE_NEW_CONSOLE to create a console process with a new console. (By default, a console process inherits its parent’s console, and there is no guarantee that input is received by the process for which it was intended.)

    • GUI或者控制台进程创建时,使用AllocConsole 时,并不是立即附加到控制台的。
  • 如果控制台进程是通过DETACHED_PROCESS 创建的,那么就不会附加到控制台了。

  • A graphical user interface (GUI) or console process that is not currently attached to a console can use the AllocConsole function to create a new console. (GUI processes are not attached to a console when they are created. Console processes are not attached to a console if they are created using CreateProcess with DETACHED_PROCESS.)

    • 如果一个进程使用了DETACHED_PROCESS 来创建控制台,那么错误发生时,需要与用户交互。
  • 比如,GUI进程创建一个控制台,这样错误不会影响到用户使用GUI,或者控制台进程不需要与用户交互就能展示错误。
    Typically, a process uses AllocConsole to create a console when an error occurs requiring interaction with the user. For example, a GUI process can create a console when an error occurs that prevents it from using its normal graphical interface, or a console process that does not normally interact with the user can create a console to display an error.

  • 使用CreateProcess 时带上CREATE_NEW_CONSOLE 标记,就能让一个进程创建一个控制台进程了。这个方法创建的控制台是访问子进程而非父进程的。这样就可以讲控制台分离开,单独与用户交互了。
    A process can also create a console by specifying the CREATE_NEW_CONSOLE flag in a call to CreateProcess. This method creates a new console that is accessible to the child process but not to the parent process. Separate consoles enable both parent and child processes to interact with the user without conflict. If this flag is not specified when a console process is created, both processes are attached to the same console, and there is no guarantee that the correct process will receive the input intended for it. Applications can prevent confusion by creating child processes that do not inherit handles of the input buffer, or by enabling only one child process at a time to inherit an input buffer handle while preventing the parent process from reading console input until the child has finished.

  • Creating a new console results in a new console window, as well as separate I/O screen buffers. The process associated with the new console uses the GetStdHandle function to get the handles of the new console’s input and screen buffers. These handles enable the process to access the console.

  • When a process uses CreateProcess, it can specify a STARTUPINFO structure, whose members control the characteristics of the first new console (if any) created for the child process. The STARTUPINFO structure specified in the call to CreateProcess affects a console created if the CREATE_NEW_CONSOLE flag is specified. It also affects a console created if the child process subsequently uses AllocConsole. The following console characteristics can be specified:

Size of the new console window, in character cells
Location of the new console window, in screen pixel coordinates
Size of the new console’s screen buffer, in character cells
Text and background color attributes of the new console’s screen buffer
Display name for the title bar of the new console’s window
The system uses default values if the STARTUPINFO values are not specified. A child process can use the GetStartupInfo function to determine the values in its STARTUPINFO structure.

A process cannot change the location of its console window on the screen, but the following console functions are available to set or retrieve the other properties specified in the STARTUPINFO structure.

TABLE 1
Function Description
GetConsoleScreenBufferInfo Retrieves the window size, screen buffer size, and color attributes.
SetConsoleWindowInfo Changes the size of the console window.
SetConsoleScreenBufferSize Changes the size of the console screen buffer.
SetConsoleTextAttribute Sets the color attributes.
SetConsoleTitle Sets the console window title.
GetConsoleTitle Retrieves the console window title.
A process can use the FreeConsole function to detach itself from an inherited console or from a console created by AllocConsole.

Creating a Child Process with Redirected Input and Output

05/31/2018
5 minutes to read

The example in this topic demonstrates how to create a child process using the CreateProcess function from a console process. It also demonstrates a technique for using anonymous pipes to redirect the child process’s standard input and output handles. Note that named pipes can also be used to redirect process I/O.

The CreatePipe function uses the SECURITY_ATTRIBUTES structure to create inheritable handles to the read and write ends of two pipes. The read end of one pipe serves as standard input for the child process, and the write end of the other pipe is the standard output for the child process. These pipe handles are specified in the STARTUPINFO structure, which makes them the standard handles inherited by the child process.

The parent process uses the opposite ends of these two pipes to write to the child process’s input and read from the child process’s output. As specified in the STARTUPINFO structure, these handles are also inheritable. However, these handles must not be inherited. Therefore, before creating the child process, the parent process uses the SetHandleInformation function to ensure that the write handle for the child process’s standard input and the read handle for the child process’s standard output cannot be inherited. For more information, see Pipes.

The following is the code for the parent process. It takes a single command-line argument: the name of a text file.

C++

Copy
#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <strsafe.h>

#define BUFSIZE 4096

HANDLE g_hChildStd_IN_Rd = NULL;
HANDLE g_hChildStd_IN_Wr = NULL;
HANDLE g_hChildStd_OUT_Rd = NULL;
HANDLE g_hChildStd_OUT_Wr = NULL;

HANDLE g_hInputFile = NULL;

void CreateChildProcess(void);
void WriteToPipe(void);
void ReadFromPipe(void);
void ErrorExit(PTSTR);

int _tmain(int argc, TCHAR *argv[])
{
SECURITY_ATTRIBUTES saAttr;

printf("\n->Start of parent execution.\n");

// Set the bInheritHandle flag so pipe handles are inherited.

saAttr.nLength = sizeof(SECURITY_ATTRIBUTES);
saAttr.bInheritHandle = TRUE;
saAttr.lpSecurityDescriptor = NULL;

// Create a pipe for the child process’s STDOUT.

if ( ! CreatePipe(&g_hChildStd_OUT_Rd, &g_hChildStd_OUT_Wr, &saAttr, 0) )
ErrorExit(TEXT(“StdoutRd CreatePipe”));

// Ensure the read handle to the pipe for STDOUT is not inherited.

if ( ! SetHandleInformation(g_hChildStd_OUT_Rd, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT(“Stdout SetHandleInformation”));

// Create a pipe for the child process’s STDIN.

if (! CreatePipe(&g_hChildStd_IN_Rd, &g_hChildStd_IN_Wr, &saAttr, 0))
ErrorExit(TEXT(“Stdin CreatePipe”));

// Ensure the write handle to the pipe for STDIN is not inherited.

if ( ! SetHandleInformation(g_hChildStd_IN_Wr, HANDLE_FLAG_INHERIT, 0) )
ErrorExit(TEXT(“Stdin SetHandleInformation”));

// Create the child process.

CreateChildProcess();

// Get a handle to an input file for the parent.
// This example assumes a plain text file and uses string output to verify data flow.

if (argc == 1)
ErrorExit(TEXT(“Please specify an input file.\n”));

g_hInputFile = CreateFile(
argv[1],
GENERIC_READ,
0,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_READONLY,
NULL);

if ( g_hInputFile == INVALID_HANDLE_VALUE )
ErrorExit(TEXT(“CreateFile”));

// Write to the pipe that is the standard input for a child process.
// Data is written to the pipe’s buffers, so it is not necessary to wait
// until the child process is running before writing data.

WriteToPipe();
printf( “\n->Contents of %s written to child STDIN pipe.\n”, argv[1]);

// Read from pipe that is the standard output for child process.

printf( “\n->Contents of child process STDOUT:\n\n”, argv[1]);
ReadFromPipe();

printf("\n->End of parent execution.\n");

// The remaining open handles are cleaned up when this process terminates.
// To avoid resource leaks in a larger application, close handles explicitly.

return 0;
}

void CreateChildProcess()
// Create a child process that uses the previously created pipes for STDIN and STDOUT.
{
TCHAR szCmdline[]=TEXT(“child”);
PROCESS_INFORMATION piProcInfo;
STARTUPINFO siStartInfo;
BOOL bSuccess = FALSE;

// Set up members of the PROCESS_INFORMATION structure.

ZeroMemory( &piProcInfo, sizeof(PROCESS_INFORMATION) );

// Set up members of the STARTUPINFO structure.
// This structure specifies the STDIN and STDOUT handles for redirection.

ZeroMemory( &siStartInfo, sizeof(STARTUPINFO) );
siStartInfo.cb = sizeof(STARTUPINFO);
siStartInfo.hStdError = g_hChildStd_OUT_Wr;
siStartInfo.hStdOutput = g_hChildStd_OUT_Wr;
siStartInfo.hStdInput = g_hChildStd_IN_Rd;
siStartInfo.dwFlags |= STARTF_USESTDHANDLES;

// Create the child process.

bSuccess = CreateProcess(NULL,
szCmdline, // command line
NULL, // process security attributes
NULL, // primary thread security attributes
TRUE, // handles are inherited
0, // creation flags
NULL, // use parent’s environment
NULL, // use parent’s current directory
&siStartInfo, // STARTUPINFO pointer
&piProcInfo); // receives PROCESS_INFORMATION

// If an error occurs, exit the application.
if ( ! bSuccess )
ErrorExit(TEXT(“CreateProcess”));
else
{
// Close handles to the child process and its primary thread.
// Some applications might keep these handles to monitor the status
// of the child process, for example.

  CloseHandle(piProcInfo.hProcess);
  CloseHandle(piProcInfo.hThread);
  
  // Close handles to the stdin and stdout pipes no longer needed by the child process.
  // If they are not explicitly closed, there is no way to recognize that the child process has ended.
  
  CloseHandle(g_hChildStd_OUT_Wr);
  CloseHandle(g_hChildStd_IN_Rd);

}
}

void WriteToPipe(void)

// Read from a file and write its contents to the pipe for the child’s STDIN.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;

for (;😉
{
bSuccess = ReadFile(g_hInputFile, chBuf, BUFSIZE, &dwRead, NULL);
if ( ! bSuccess || dwRead == 0 ) break;

  bSuccess = WriteFile(g_hChildStd_IN_Wr, chBuf, dwRead, &dwWritten, NULL);
  if ( ! bSuccess ) break; 

}

// Close the pipe handle so the child process stops reading.

if ( ! CloseHandle(g_hChildStd_IN_Wr) )
ErrorExit(TEXT(“StdInWr CloseHandle”));
}

void ReadFromPipe(void)

// Read output from the child process’s pipe for STDOUT
// and write to the parent process’s pipe for STDOUT.
// Stop when there is no more data.
{
DWORD dwRead, dwWritten;
CHAR chBuf[BUFSIZE];
BOOL bSuccess = FALSE;
HANDLE hParentStdOut = GetStdHandle(STD_OUTPUT_HANDLE);

for (;😉
{
bSuccess = ReadFile( g_hChildStd_OUT_Rd, chBuf, BUFSIZE, &dwRead, NULL);
if( ! bSuccess || dwRead == 0 ) break;

  bSuccess = WriteFile(hParentStdOut, chBuf, 
                       dwRead, &dwWritten, NULL);
  if (! bSuccess ) break; 

}
}

void ErrorExit(PTSTR lpszFunction)

// Format a readable error message, display a message box,
// and exit from the application.
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();

FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER | 
    FORMAT_MESSAGE_FROM_SYSTEM |
    FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    dw,
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
    (LPTSTR) &lpMsgBuf,
    0, NULL );

lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT, 
    (lstrlen((LPCTSTR)lpMsgBuf)+lstrlen((LPCTSTR)lpszFunction)+40)*sizeof(TCHAR)); 
StringCchPrintf((LPTSTR)lpDisplayBuf, 
    LocalSize(lpDisplayBuf) / sizeof(TCHAR),
    TEXT("%s failed with error %d: %s"), 
    lpszFunction, dw, lpMsgBuf); 
MessageBox(NULL, (LPCTSTR)lpDisplayBuf, TEXT("Error"), MB_OK); 

LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
ExitProcess(1);

}
The following is the code for the child process. It uses the inherited handles for STDIN and STDOUT to access the pipe created by the parent. The parent process reads from its input file and writes the information to a pipe. The child receives text through the pipe using STDIN and writes to the pipe using STDOUT. The parent reads from the read end of the pipe and displays the information to its STDOUT.

C++

Copy
#include <windows.h>
#include <stdio.h>

#define BUFSIZE 4096

int main(void)
{
CHAR chBuf[BUFSIZE];
DWORD dwRead, dwWritten;
HANDLE hStdin, hStdout;
BOOL bSuccess;

hStdout = GetStdHandle(STD_OUTPUT_HANDLE);
hStdin = GetStdHandle(STD_INPUT_HANDLE);
if (
(hStdout == INVALID_HANDLE_VALUE) ||
(hStdin == INVALID_HANDLE_VALUE)
)
ExitProcess(1);

// Send something to this process’s stdout using printf.
printf("\n ** This is a message from the child process. ** \n");

// This simple algorithm uses the existence of the pipes to control execution.
// It relies on the pipe buffers to ensure that no data is lost.
// Larger applications would use more advanced process control.

for (;😉
{
// Read from standard input and stop on error or no data.
bSuccess = ReadFile(hStdin, chBuf, BUFSIZE, &dwRead, NULL);

  if (! bSuccess || dwRead == 0) 
     break; 

// Write to standard output and stop on error.
bSuccess = WriteFile(hStdout, chBuf, dwRead, &dwWritten, NULL);

  if (! bSuccess) 
     break; 

}
return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
session1 【Objective and Requirement】 Objective: Be familiar with the creation of process and thread on Windows. Requirement: Sub Task 1: Create a console application, "child", which keeps printing out "The child is talking at [system time]" (in a loop, one per 1s). Sub Task 2: Create another console application, "parent". It create a child process to execute “child”. The "parent" process keeps printing out "The parent is talking at [system time]". (one per 1s) Execute "parent" and explain the output you see. Sub Task 3: Create a "winPS" program which can output information about all the running processes. Try to output details about each process, such as PID, executable file name and path, etc. Sub Task 4: In the "mainThread" program, use "CreateThread" to create a child thread. Both the main thread and the child thread keep printing out "[ThreadID] + [System time]". session2 Objective: Create "ps" and "kill" commands on Windows. Requirement: Sub Task 1: On Linux/Unix there are a "ps" command and a "kill" command. The "ps" command can list information about all the running processes. The "kill" command can terminate processes. Based on the tasks finished in Session 1, create "ps" and "kill" commands on Windows. Tips: using "TerminateProcess" to "kill" a process. session3 Objective: Learn how to use semaphore to solve IPC problems. Requirement: Task 3.1. Sleeping barber Use semaphores to solve the problem of sleeping barber. Task 3.2. Reader & Writer Use semaphores to solve the reader and writer problem, with the readers (and writers) have higher priority. session4 Title: Upgrade Linux/Unix commands Problem: Write a program "supershell" that takes another command as an argument and executes that command. For instance, executing: “./supershell cat /usr/greg/readme" would invoke the cat command on the file /usr/greg/readme. After execution of the specified command has completed, "supershell" should display statistics that show some of the system resources the co
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值