SetStdHandle对cout/printf没有影响

Dun*_*can 6 windows winapi visual-c++

标题说明了一切.当我运行以下代码时:

HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);
HANDLE hFile = CreateFile(TEXT("Foo.txt"), GENERIC_WRITE, FILE_READ_ACCESS | FILE_WRITE_ACCESS,
    NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

SetStdHandle(STD_OUTPUT_HANDLE, hFile);
std::cout << "Hello, ";
printf("world!\n");
WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), "Hello, world!\n", 13, NULL, NULL);

SetStdHandle(STD_OUTPUT_HANDLE, hOut);
CloseHandle(hFile);
Run Code Online (Sandbox Code Playgroud)

其结果是,Hello, world!被写入到控制台呼叫的一个结果coutprintf,和Hello, world!也被写入文件Foo.txt的调用的结果WriteFile.我的假设是,当一切都在一开始就被初始化时,HANDLE返回的GetStdHandle被缓存并重用于coutprintf.这是完全合理的,正如我所想的那样,我GetStdHandle需要调用操作系统(可能很长!).麻烦的是我想要覆盖这种行为并尽可能地将cout和printf与应用程序的标准句柄"同步".

在建议任何替代方案之前,让我准确描述一下我正在尝试做什么(是的,我知道可以freopen用于此目的).我需要做的是在更改之前将当前标准输出句柄"保存"在类似堆栈的数据结构上,以便我能够恢复以前的输出句柄.任何不足之处都是这种情况所不能接受的(即我无法恢复CONOUT$等).这需要具有递归的能力.即以下应该按照您的预期工作:

std::cout << "A1" << std::endl;

StartStdOutRedirection(TEXT("Foo.txt"));
std::cout << "B1" << std::endl;

StartStdOutRedirection(TEXT("Bar.txt"));
std::cout << "C1" << std::endl;
EndStdOutRedirection();

std::cout << "B2" << std::endl;
EndStdOutRedirection();

std::cout << "A2" << std::endl;
Run Code Online (Sandbox Code Playgroud)

如果有一种"重新同步"的方法,这将是非常容易的,stdout因为以下代码应该做到这一点:

std::vector<HANDLE> vStdOutHandles;
void StartStdOutRedirection(_In_ LPCTSTR lpFile)
{
    vStdOutHandles.push_back(GetStdHandle(STD_OUTPUT_HANDLE));
    SetStdHandle(STD_OUTPUT_HANDLE, CreateFile(lpFile, GENERIC_WRITE,
        FILE_WRITE_ACCESS | FILE_READ_ACCESS, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL));
}

void EndStdOutRedirection(void)
{
    CloseHandle(GetStdHandle(STD_INPUT_HANDLE));
    SetStdHandle(STD_OUTPUT_HANDLE, vStdOutHandles.back());
    vStdOutHandles.pop_back();
}
Run Code Online (Sandbox Code Playgroud)

上述代码的正确性可以通过使用代替的WriteFile调用来验证.我非常需要的是等效的作品上秒.这样,我可以使用在由归国,然后这个功能对于底层文件中设置合我心意的文件.我相信这会起作用,因为我假设两者并在内心深处保存.我试图通过复制标准输出句柄,关闭该句柄,然后调用希望它会给我相同的值来"伪造它" ,但这种情况最多只能偶尔起作用.如果您有兴趣,这是我的代码:GetStdHandle(STD_OUTPUT_HANDLE)coutfreopenHANDLEDuplicateHandleHANDLEGetStdHandleMyReopenHandleHANDLEprintfcoutHANDLECreateFileHANDLE

std::vector<HANDLE> vStdOutHandles;
bool StartStdOutRedirection(_In_ LPCTSTR lpFile)
{
    bool fResult = false;
    HANDLE hProc = GetCurrentProcess();
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

    if (hOut != INVALID_HANDLE_VALUE)
    {
        HANDLE hDup;
        if (DuplicateHandle(hProc, hOut, hProc, &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
        {
            // Need to close the current handle before we open the new one
            CloseHandle(hOut);
            HANDLE hFile = CreateFile(lpFile, GENERIC_WRITE, FILE_WRITE_ACCESS | FILE_READ_ACCESS,
                NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

            if (hFile != INVALID_HANDLE_VALUE)
            {
                // Should be same HANDLE; else we're screwed...
                assert(hFile == hOut);
                SetStdHandle(STD_OUTPUT_HANDLE, hFile);

                vStdOutHandles.push_back(hDup);
                fResult = true;
            }
            else
            {
                // Otherwise, reopen the previous output HANDLE on failure
                DuplicateHandle(hProc, hDup, hProc, &hFile, 0, FALSE, DUPLICATE_SAME_ACCESS);

                assert(hFile == hOut);
                CloseHandle(hDup);
            }
        }
    }

    return fResult;
}

bool EndStdOutRedirection(void)
{
    bool fResult = false;
    HANDLE hProc = GetCurrentProcess();
    HANDLE hOut = GetStdHandle(STD_OUTPUT_HANDLE);

    if (hOut != INVALID_HANDLE_VALUE && vStdOutHandles.size() != 0)
    {
        HANDLE hDup;
        HANDLE hNext = vStdOutHandles.back();

        // Close current handle and re-open previous one
        CloseHandle(hOut);
        if (DuplicateHandle(hProc, hNext, hProc, &hDup, 0, FALSE, DUPLICATE_SAME_ACCESS))
        {
            // Again, we're screwed if these are not the same
            assert(hOut == hDup);
            SetStdHandle(STD_OUTPUT_HANDLE, hDup);

            vStdOutHandles.pop_back();
            fResult = true;
        }
    }

    return fResult;
}
Run Code Online (Sandbox Code Playgroud)

上述断言失败了大约一半的时间(我并没有真正期待或依靠它工作......我只是感兴趣).就这个问题而言,这就是我所知道的.如果有人有任何建议,请告诉我:)

Dun*_*can 5

HANDLE哇,在寻找手动设置a 的方法一段时间后FILE,我终于发现有一种相当简单的方法可以使用 C 运行时库来执行此操作:

std::vector<int> vfdStdOut;
void StartStdOutRedirection(_In_ LPCSTR lpFile)
{
    // Duplicate stdout and give it a new file descriptor
    int fdDup = _dup(_fileno(stdout));
    vfdStdOut.push_back(fdDup);

    // Re-open stdout to the new file
    freopen(lpFile, "w", stdout);
}

bool EndStdOutRedirection(void)
{
    if (vfdStdOut.size() != 0)
    {
        // Get last saved file descriptor and restore it
        int fdNext = vfdStdOut.back();
        _dup2(fdNext, _fileno(stdout));

        // Need to close the file associated with the saved file descriptor
        _close(fdNext);

        vfdStdOut.pop_back();
        return true;
    }

    return false;
}
Run Code Online (Sandbox Code Playgroud)

这也会照顾SetStdHandle你的呼唤!

  • 请小心“_fileno”,文档说它可以返回负数,并且如果标准句柄已经关闭(父进程已调用 CreateProcess,没有标准句柄继承),则它会返回负数。您必须使用直接数字,例如“STDOUT_FILENO”而不是“_fileno(stdout)”,否则所有顺序调用都将失败,这意味着“_close(fdNext)”将关闭句柄而不重复! (2认同)