标题说明了一切。当我运行下面的代码:SetStdHandle对cout/printf没有影响
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
。我的假设是,当一切都在开始初始化时,由GetStdHandle
返回的HANDLE
被缓存并重新用于cout
和printf
。这是完全合理的,正是我想要的,因为我假设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;
这将是过于容易,如果有办法“重新同步” 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
代替cout
调用GetStdHandle(STD_OUTPUT_HANDLE)
来验证。我理想需要的是相当于freopen
,适用于HANDLE
s。通过这种方式,我可以在GetStdHandle
返回的HANDLE
上使用DuplicateHandle
,然后使用MyReopenHandle
函数将该HANDLE
的底层文件设置为我喜欢的文件。我相信这会工作,因为我认为printf
和cout
有一个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;
}
上述断言失败半个月左右的时间(我是不是真的希望或者在计算工作......我只是感兴趣)。就我所知,这个问题就是这样。如果有人有任何建议,请让我知道:)
这具有负面影响,即任何依赖'WriteFile'到'GetStdHandle'返回的'HANDLE'的任何东西仍然会写入原始设备。看到解决方案,我能够做到这一点不hacky,并与OS – Duncan
同意!你的解决方案好得多!这是没有黑客,像我一样... – xMRi
我不相信,因为我相信用标准输出(或任何标准设备)调用freopen是未定义的行为?至少在我的研究中出现了这种情况;至少在使用'cout'方面没有任何保证。恰巧,Windows上的实现(我相信大多数其他主要平台也是如此)会实现你所期望的。 – Duncan