windows - SetStdHandle 对 cout/printf 没有影响

标签 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);

结果是 Hello, world!由于调用 cout 而被写入控制台和 printf , 和 Hello, world!也被写入文件 Foo.txt调用 WriteFile 的结果.我的假设是,当一切都在一开始就被初始化时,HANDLEGetStdHandle 返回缓存并重用于 coutprintf .这是完全合理的,而且正是我想的那样 GetStdHandle需要调用操作系统(可能很长!)。问题是我想覆盖该行为,并在可能的情况下将 cout 和 printf 与应用程序的标准句柄“同步”。

在提出任何替代方案之前,让我准确描述一下我正在尝试做的事情(是的,我知道可以使用 freopen 来达到这个目的)。我需要做的是在更改之前将当前标准输出句柄“保存”到类似堆栈的数据结构中,以便能够恢复以前的输出句柄。对于这种情况,任何不足都是 Not Acceptable (即我无法恢复到 CONOUT$ 等)。这个需要有递归的能力。 IE。以下内容应该会像您期望的那样工作:

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;

如果有一种“重新同步”的方法,这将非常容易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();
}

以上代码的正确性可以使用WriteFile来验证调用 GetStdHandle(STD_OUTPUT_HANDLE)代替 cout .我理想需要的是相当于 freopen适用于 HANDLE秒。这样我就可以使用 DuplicateHandleHANDLE 上由 GetStdHandle 返回然后这个MyReopenHandle为该 HANDLE 设置基础文件的函数到我喜欢的文件。我相信这会起作用,因为我假设这两个 printfcout有一个HANDLE保存在深处的某个地方。我试图通过复制标准输出句柄、关闭该句柄然后调用 CreateFile 来“伪装”它希望它能给我同样的 HANDLE值,但这充其量只是偶尔有效。如果您有兴趣,这是我的代码:

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;
}

上面的断言大约有一半的时间失败(我并不是真的期望或指望它会起作用......我只是感兴趣)。这就是我对这个问题的了解。如果有人有任何建议,请告诉我:)

最佳答案

哇,在寻找手动设置 FILEHANDLE 的方法后,我终于发现有一种相当简单的方法可以使用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;
}

这还将为您调用 SetStdHandle!

关于windows - SetStdHandle 对 cout/printf 没有影响,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21374548/

相关文章:

python - 与 bsddb 有关的许可证问题

c# - 一旦我在 Active Directory 中获得用户组,如何获得组的 SID?

c++ - 在没有 MFC 的 win32 中保存/打开通用对话框

windows - 如何最好地将 CString 转换为 BSTR 以将其作为 "in"参数传递给 COM 方法?

windows - 为什么 Windows 上的程序通常不将自己添加到 PATH 变量中?

winapi - ExitWindowsEx windows 7 关机不起作用

c++ - 没有 mutex.h 的 VC++ 中的自旋锁同步

c++ - 分配大量内存会出错?为什么?

c++ - 你能举一个C++中堆栈溢出的例子吗?

windows - 通过 USB 端口发送和接收数据