对于控制台或命令行程序的进程,按MSDN所说,只能有一个那黑区区的窗口。如果你要想拿一个专门用于响应用户交互,另一个专门用于输出信息,或再一个输出输出其它信息。这时,是无法满足需求的。Windows的限制了一个进程,一个控制台窗口。
既然如此不行,那就创建多个进程吧。一个主进程对应多个子进程,父子进程序采用管道通信。在创建一个控制台子进程时,会把读句柄传入。了进程在一个循环中,反复读取来自该句柄的内容,并把它输出到控制台屏幕。
代码如下:(你拿去要修改,我是从项目拿来出的)
#ifndef NETBUS_APP_CONSOLE_H_
#define NETBUS_APP_CONSOLE_H_
#include <windows.h>
#include <atlstr.h>
#include <base/basictypes.h>
#include <base/win/scoped_any.h>
#include <base/memory/scoped_ptr.h>
#include <base/win/synchronized.h>
namespace leopard{
// IMPROVE: Make console as double-direct
// communication between any two console.
class Console{
public:
~Console();
static Console& Current();
static HRESULT Create(Console** console);
HRESULT Read(const TCHAR** buffer, DWORD* read_count);
HRESULT Write(const CString& message);
bool IsChild() const { return is_child_; }
bool IsRoot() const { return !IsChild(); }
static void Main();
HRESULT Shutdown();
HRESULT GetConsoleScreenBufferInfo(PCONSOLE_SCREEN_BUFFER_INFO info);
HRESULT SetConsoleCursorPosition(COORD pos);
HRESULT FillConsoleOutputCharacter(CHAR cCharacter, DWORD nLength,
COORD dwWriteCoord, LPDWORD lpNumberOfCharsWritten);
private:
Console(bool is_child);
void DoMain();
HRESULT Close();
HRESULT DoCreate();
HRESULT Init();
PROCESS_INFORMATION proc_info_;
scoped_pipe pipe_write; // for parent, used to write data to child
scoped_pipe pipe_input; // for parent, creature of parent; for child to read data from
scoped_handle console_out_; // for parent and child. display text on
scoped_handle console_in_; // for parent, used to read input from user.
bool is_child_;
static const uint32 kBufferSize = 512;
scoped_array<TCHAR> buffer_;
base::win::LLock lock_;
scoped_ptr<base::win::Reactor> reactor_;
static Console* current_;
DISALLOW_EVIL_CONSTRUCTORS(Console);
};
}
#endif //
#include "common/console.h"
#include <windows.h>
#include <atlstr.h>
#include <base/win/app_util.h>
#include <base/win/debug.h>
#include <base/win/error.h>
#include <base/win/logging.h>
#include <base/win/path.h>
#include <base/win/scoped_any.h>
#include <base/win/system.h>
#include "common/constants.h"
namespace leopard{
using namespace base::win;
Console* Console::current_ = NULL;
Console::Console(bool is_child):is_child_(is_child){
}
Console::~Console(){
reactor_.reset(NULL);
Close();
}
HRESULT Console::Init(){
buffer_.reset(new TCHAR[kBufferSize]);
reset(pipe_write, GetStdHandle(STD_OUTPUT_HANDLE));
reset(pipe_input, GetStdHandle(STD_INPUT_HANDLE));
HRESULT hr = S_OK;
if(!is_child_){
if(::AllocConsole()){
reset(console_out_, GetStdHandle(STD_OUTPUT_HANDLE));
reset(console_in_, GetStdHandle(STD_INPUT_HANDLE));
LOG_INFO(_T("Console::Init out 0x%x in 0x%x]"), get(console_out_), get(console_in_));
hr = S_OK;
}else{
hr = HRESULTFromLastError();
LOG_ERROR(_T("Console::Init failed][0x%x]"), hr);
}
}
return hr;
}
Console& Console::Current(){
if(!current_){
current_ = new Console(false);
VERIFY1(SUCCEEDED(current_->Init()));
}
return *current_;
}
HRESULT Console::Create(Console** console){
ASSERT1(console);
scoped_ptr<Console> child(new Console(true));
VERIFY1(SUCCEEDED(child->Init()));
HRESULT hr = child->DoCreate();
if(SUCCEEDED(hr)){
*console = child.get();
child.release();
}
return hr;
}
HRESULT Console::SetConsoleCursorPosition(COORD pos){
if(!::SetConsoleCursorPosition(get(console_out_), pos)){
return HRESULTFromLastError();
}
return S_OK;
}
HRESULT Console::GetConsoleScreenBufferInfo(PCONSOLE_SCREEN_BUFFER_INFO info){
if(!::GetConsoleScreenBufferInfo(get(console_out_), info)){
return HRESULTFromLastError();
}
return S_OK;
}
HRESULT Console::DoCreate(){
SECURITY_ATTRIBUTES sec_att;
sec_att.bInheritHandle = TRUE;
sec_att.lpSecurityDescriptor = NULL;
sec_att.nLength = sizeof(SECURITY_ATTRIBUTES);
release(pipe_input);
release(pipe_write);
if (!CreatePipe(address(pipe_input), address(pipe_write), &sec_att, 0)) {
return HRESULTFromLastError();
}
STARTUPINFO strinfo;
ZeroMemory(&strinfo, sizeof(STARTUPINFO));
strinfo.cb = sizeof(STARTUPINFO);
ZeroMemory(&proc_info_, sizeof(PROCESS_INFORMATION));
CString command_line(app_util::GetCurrentModulePath());
EnclosePath(&command_line);
command_line.Append(_T(" /"));
command_line.Append(kWithConsole);
command_line.Append(_T(" /"));
command_line.Append(kInputHandle);
CString handle_str;
handle_str.Format(_T(" %u"), get(pipe_input));
command_line.Append(handle_str);
BOOL success = ::CreateProcess(NULL, // Module name
CStrBuf(command_line), // Command line
NULL, // Process handle not inheritable
NULL, // Thread handle not inheritable
TRUE, // Set handle inheritance to TRUE
CREATE_NEW_CONSOLE, // No creation flags
NULL, // Use parent's environment block
NULL, // Use parent's starting directory
&strinfo, // Pointer to STARTUPINFO structure
&proc_info_); // Pointer to PROCESS_INFORMATION structure
if(!success){
return HRESULTFromLastError();
}
return S_OK;
}
HRESULT Console::Read(const TCHAR** buffer, DWORD* read_count){
ASSERT1(buffer);
ASSERT1(buffer_.get());
__mutexScope(lock_);
*buffer = buffer_.get();
if(::ReadConsole(get(console_in_),
buffer_.get(),
kBufferSize,
read_count,
NULL)){
return S_OK;
}else{
return HRESULTFromLastError();
}
}
HRESULT Console::Write(const CString& message){
HRESULT hr = S_OK;
DWORD length = _tcslen(message);
if(!is_child_){
if(!::WriteConsole(get(console_out_), message, length, NULL, NULL)){
hr = HRESULTFromLastError();
}
}else{
DWORD wrote = 0;
length *= sizeof(TCHAR);
if(!::WriteFile(get(pipe_write), &length, sizeof(length), &wrote, NULL) ||
!::WriteFile(get(pipe_write), message, length, &wrote, NULL)){
hr = HRESULTFromLastError();
}
}
return hr;
}
void Console::Main(){
Console& current = Current();
CommandLineParser parser;
CString handle_str;
parser.ParseFromString(::GetCommandLine());
parser.GetSwitchArgumentValue(kInputHandle, 0, &handle_str);
reset(current.pipe_input);
_sntscanf_s(handle_str, 10, _T("%u"), address(current.pipe_input));
if(valid(current.pipe_input)){
for(;;){
current.DoMain();
}
}else{
LOG_ERROR(_T("[Console::Main][Invlaid pipe handle 0x%x]"), get(current.pipe_input));
}
_exit(0);
}
void Console::DoMain(){
LOG_INFO(_T("[Console::DoMain][read pipe handle 0x%x]"), get(pipe_input));
if(!valid(pipe_input)){
LOG_ERROR(_T("[Input handle is null.]"));
return;
}
DWORD length = 0;
DWORD readn = 0;
if(!::ReadFile(get(pipe_input), &length, sizeof(length), &readn, NULL)){
LOG_ERROR(_T("[ReadFile failed][0x%x]"), HRESULTFromLastError());
return;
}
bool ret = true;
std::vector<TCHAR> bytes(length);
std::vector<TCHAR>::iterator pos = bytes.begin();
*pos = _T('\0');
while(length && ret){
if(ret = !!::ReadFile(get(pipe_input),
&(*pos),
length,
&readn,
NULL)){
length -= readn;
pos += readn;
}
}
if(!ret){
LOG_ERROR(_T("[ReadFile failed.][0x%x"), HRESULTFromLastError());
}
CString message(static_cast<TCHAR*>(&(*bytes.begin())));
Write(message);
LOG_INFO(_T("[Console::DoMain done]"));
}
HRESULT Console::Close(){
if(is_child_){
::TerminateProcess(proc_info_.hProcess, 0);
::CloseHandle(proc_info_.hProcess);
::CloseHandle(proc_info_.hThread);
ZeroMemory(&proc_info_, sizeof(proc_info_));;
}else{
::FreeConsole();
}
return S_OK;
}
HRESULT Console::Shutdown(){
Close();
return S_OK;
}
} // namespace leopard
子控制台进程的入口就在 Console::Main()中,当然你需要在主函数里解析命令行,并根据参数,进入到该入口。
这里只实现了单向的一对多通信。