如何将文本输出到另一个已打开的控制台C ++

时间:2017-09-20 01:13:01

标签: c++ winapi stdout handle access-rights

嗨,这是我关于堆栈溢出的第一个问题 (我是一名初级程序员:P和法语也是......所以我提前为我犯的错误道歉)

我试图启动一个提升的进程以附加回父级的控制台以写入其输出

(没有崩溃没有错误只是简单的虚无)

这是我的代码:

int main(int argc, char *argv[])
{

    HANDLE consoleHandle = GetStdHandle(STD_OUTPUT_HANDLE);

    if (UAC::IsAppRunningAsAdminMode())
    {
        printf("Process Already elevated\nChecking if self invocated from unprevileged previous run...\n");
         if (argc > 1)
        {
            std::string consoleTextOutputBuffer("Elevated privileges session started...\n");
             WriteConsoleA((HANDLE)argv[2], consoleTextOutputBuffer.c_str(), consoleTextOutputBuffer.size(), NULL, NULL);
        }
    }
    else
    {
        printf("Process need elevation...\n");
        if (UAC::BeginPrivilegeElevationPrompt(consoleHandle))
        {
            printf("Elevation succesfull!\n");
        }
        else
        {
            printf("Elevation failed\n");
            system("pause>nul");
            exit(-1);
        }
    }
}

从我写的UAC课程开始:

BOOL BeginPrivilegeElevationPrompt(const HANDLE& oldConsoleHandle)
{
    wchar_t szPath[MAX_PATH];
    if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
    {
        // Launch itself as admin
        std::string oldConsoleHandleToString = std::to_string((int)oldConsoleHandle);
        std::wstring wsConsoleString(oldConsoleHandleToString.begin(), oldConsoleHandleToString.end());
        SHELLEXECUTEINFO sei = { sizeof(sei) };
        sei.lpVerb = L"runas";
        sei.lpFile = szPath;
        sei.hwnd = NULL;
        sei.lpParameters = wsConsoleString.c_str();
        sei.nShow = SW_NORMAL;
        if (!ShellExecuteEx(&sei))
        {
            DWORD dwError = GetLastError();
            if (dwError == ERROR_CANCELLED)
            {
                // The user refused to allow privileges elevation.
                printf("User did not allow elevation.\n");
                return false;
            }
            return false;
        }
        else
        {
            return true;
            _exit(1);  // Quit itself
        }
    }
    printf("Could not load module name.\n");
    return false;
};

2 个答案:

答案 0 :(得分:2)

我知道有两种方法可以直接与子进程通信。一种是使用管道..这允许您写入子进程并从中读取:https://msdn.microsoft.com/en-us/library/windows/desktop/ms682499(v=vs.85).aspx

#include <windows.h>
#include <string>
#include <fstream>
#include <thread>
#include <chrono>

PROCESS_INFORMATION CreateChildProcess(std::string CommandLine, std::string StartDirectory, DWORD WaitTime, HANDLE hInRead, HANDLE hOutWrite)
{
    STARTUPINFO SI;
    PROCESS_INFORMATION PI;
    ZeroMemory(&SI, sizeof(SI));
    ZeroMemory(&PI, sizeof(PI));

    SI.cb = sizeof(SI);
    SI.hStdInput = hInRead;
    SI.hStdError = hOutWrite;
    SI.hStdOutput = hOutWrite;
    SI.dwFlags |= STARTF_USESTDHANDLES;

    bool success = CreateProcess(0, &CommandLine[0], 0, 0, true, NORMAL_PRIORITY_CLASS, 0, StartDirectory.c_str(), &SI, &PI);

    if (success)
    {
        if (WaitTime != 0)
        {
            WaitForSingleObject(PI.hProcess, WaitTime);
            CloseHandle(PI.hProcess);
            CloseHandle(PI.hThread);
            return {0};
        }
        return PI;
    }

    return {0};
}


void RedirectInputPipe(HANDLE& hRead, HANDLE& hWrite)
{
    SECURITY_ATTRIBUTES attr;
    ZeroMemory(&attr, sizeof(attr));
    attr.nLength = sizeof(attr);
    attr.bInheritHandle = true;

    CreatePipe(&hRead, &hWrite, &attr, 0);
    SetHandleInformation(hWrite, HANDLE_FLAG_INHERIT, 0);
}


void RedirectOutputPipe(HANDLE& hRead, HANDLE& hWrite)
{
    SECURITY_ATTRIBUTES attr;
    ZeroMemory(&attr, sizeof(attr));
    attr.nLength = sizeof(attr);
    attr.bInheritHandle = true;

    CreatePipe(&hRead, &hWrite, &attr, 0);
    SetHandleInformation(hRead, HANDLE_FLAG_INHERIT, 0);
}

bool ReadPipe(HANDLE hOutput, std::string& Buffer)
{
    DWORD dwRead = 0;
    Buffer.clear();
    Buffer.resize(256);
    bool Result = ReadFile(hOutput, &Buffer[0], Buffer.size(), &dwRead, NULL);
    Buffer.resize(dwRead);
    return Result && dwRead;
}

bool WritePipe(HANDLE hInput, const char* Buffer, unsigned int BufferSize)
{
    DWORD dwWritten = 0;
    return WriteFile(hInput, Buffer, BufferSize, &dwWritten, NULL) && (dwWritten == BufferSize);
}

void HandleRead(HANDLE hOutputRead, bool &Termination)
{
    std::string Buffer;
    HANDLE ConsoleOutput = GetStdHandle(STD_OUTPUT_HANDLE);

    while(!Termination)
    {
        if (!ReadPipe(hOutputRead, Buffer))
        {
            if (GetLastError() == ERROR_BROKEN_PIPE)
                break;
        }

        WritePipe(ConsoleOutput, Buffer.c_str(), Buffer.size());
        if (output)
        {
            std::cout.write(Buffer.c_str(), Buffer.size());
        }
        Buffer.clear();
    }

    CloseHandle(ConsoleOutput);
}

int main()
{
    std::string process = "ChildProcess.exe";
    std::string startdir = "C:/Users/Brandon/Desktop/Test/bin";


    HANDLE hInputRead, hInputWrite, hOutputRead, hOutputWrite;
    RedirectInputPipe(hInputRead, hInputWrite);
    RedirectOutputPipe(hOutputRead, hOutputWrite);

    PROCESS_INFORMATION PI = CreateChildProcess(process, startdir, 0, hInputRead, hOutputWrite);

    bool Termination = false;
    std::thread(HandleRead, hOutputRead, std::ref(Termination)).detach();
    WaitForSingleObject(PI.hProcess, INFINITE);
    Termination = true;
    CloseHandle(PI.hProcess);
    CloseHandle(PI.hThread);
    CloseHandle(hInputRead);
    CloseHandle(hInputWrite);
    CloseHandle(hOutputRead);
    CloseHandle(hOutputWrite);

    return 0;
}

它的工作原理是创建一个带有输入输出句柄的子进程...然后创建一个不断轮询/读取管道的线程,并打印出它正在编写的内容。完成后清理..

下一种方法是根据WinAPI文档调用AttachConsole(ATTACH_PARENT_PROCESS) ..这会将子进程附加到父进程的控制台。完成后需要调用FreeConsole以从父进程的控制台分离:https://docs.microsoft.com/en-us/windows/console/attachconsole

答案 1 :(得分:0)

Finnaly我找到了实现我想要的方法

我通过参数将父PID传递给子进程,然后我使用了AttachConsole(PID)

瞧!

感谢大家的帮助