多控制台窗口实现

         对于控制台或命令行程序的进程,按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()中,当然你需要在主函数里解析命令行,并根据参数,进入到该入口。

这里只实现了单向的一对多通信。


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值