SetStdHandle 对 cout/printf 没有影响 [英] SetStdHandle has no effect on cout/printf

查看:44
本文介绍了SetStdHandle 对 cout/printf 没有影响的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The title says it all. When I run the following code:

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! 作为对 coutprintf 的调用的结果被写入控制台,并且 Hello, world! 也被写入文件 Foo.txt 作为对 WriteFile 的调用的结果.我的假设是,当一切都在一开始被初始化时,GetStdHandle 返回的 HANDLE 被缓存并重用于 cout打印.这是完全合理的,正是我想要的,因为我假设 GetStdHandle 需要调用操作系统(这可能很长!).问题是我想覆盖该行为并尽可能将 cout 和 printf 与应用程序的标准句柄同步".

The result is that Hello, world! gets written to the console as a result of the calls to cout and printf, and Hello, world! also gets written to the file Foo.txt as a result of the call to WriteFile. My assumption is that when everything gets initialized at the very beginning, the HANDLE returned by GetStdHandle is cached and reused for both cout and printf. That's perfectly reasonable and exactly what I would want as I assume GetStdHandle requires a call to the operating system (which can be long!). The trouble is that I want to override that behavior and "sync" both cout and printf with the application's standard handles if possible.

在提出任何替代方案之前,让我确切地描述一下我想要做什么(是的,我知道可以为此目的使用 freopen).我需要能够做的是在更改它之前将当前标准输出句柄保存"在类似堆栈的数据结构上,以便我能够恢复以前的输出句柄.对于这种情况,任何不足都是不可接受的(即我无法恢复到 CONOUT$ 等).这需要具有递归的能力.IE.以下应该像您期望的那样工作:

Before suggesting any alternatives, let me describe exactly what it is I am trying to do (yes, I know that it is possible to use freopen for this purpose). What I need to be able to do is to "save" the current standard output handle on a stack-like data structure before I change it so that I can be able to restore the previous output handle. Anything short of that is unacceptable for this situation (i.e. I can't restore to CONOUT$, etc.). This needs to have the ability to be recursive. I.e. the following should work as you would expect it to:

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,这将非常容易,因为以下代码应该可以解决问题:

This would be excessively easy if there was a way to "re-sync" stdout as the following code should do the trick:

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 来验证.理想情况下,我需要的是在 HANDLE 上工作的 freopen 等价物.这样我就可以在 GetStdHandle 返回的 HANDLE 上使用 DuplicateHandle 然后这个 MyReopenHandle 函数来设置底层文件将 HANDLE 放到我喜欢的文件中.我相信这会起作用,因为我假设 printfcout 都有一个 HANDLE 保存在深处的某个地方.我试图通过复制标准输出句柄,关闭该句柄,然后调用 CreateFile 来伪造"它,希望它会给我相同的 HANDLE 值,但是这充其量只是偶尔工作.如果您有兴趣,这是我的代码:

The correctness of the above code can be verified by using WriteFile with a call to GetStdHandle(STD_OUTPUT_HANDLE) in place of cout. What I ideally need is an equivalent of freopen that works on HANDLEs. That way I could use DuplicateHandle on the HANDLE returned by GetStdHandle and then this MyReopenHandle function to set the underlying file for that HANDLE to the file of my liking. I believe that would work as I assume both printf and cout have a HANDLE saved somewhere deep down. I tried to "fake it" by duplicating the standard output handle, closing that handle, and then calling CreateFile in hopes that it'll give me the same HANDLE value, but that works sporadically at best. Here's my code for that if you are interested:

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

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

The above assert fails about half the time (I wasn't really expecting or counting on that to work... I was just interested). That is about as far as I have gotten in terms of this problem. If anyone has any suggestions, please let me know :)

推荐答案

哇,找了一段时间手动设置FILEHANDLE,我终于发现使用 C 运行时库有一种相当简单的方法可以做到这一点:

Wow, after a while of searching for a way to manually set the HANDLE of a FILE, I finally discovered that there is a fairly straightforward way to do this using the C Run-Time Library:

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

这篇关于SetStdHandle 对 cout/printf 没有影响的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆