如何在Win32应用程序中打印到调试输出窗口?


97

我有一个Win32项目,我已将其加载到Visual Studio 2005中。我希望能够将内容打印到Visual Studio输出窗口中,但是我一生都无法解决。我已经尝试过'printf'和'cout <<',但是我的消息仍然顽固地未打印。

是否有某种特殊的方法可以打印到Visual Studio输出窗口?


11
请注意,Visual Studio输出窗口不是控制台。它们都是“其中带有文本的窗口”,但在幕后却有所不同。
MSalters

Answers:


136

您可以使用OutputDebugStringOutputDebugString是一个宏,根据您的构建选项映射到OutputDebugStringA(char const*)还是OutputDebugStringW(wchar_t const*)。在后一种情况下,您将必须为该函数提供一个宽字符串。要创建宽字符文字,可以使用L前缀:

OutputDebugStringW(L"My output string.");

通常,您将宏版本与_T宏一起使用,如下所示:

OutputDebugString(_T("My output string."));

如果将项目配置为针对UNICODE进行构建,它将扩展为:

OutputDebugStringW(L"My output string.");

如果您不打算使用UNICODE,它将扩展为:

OutputDebugStringA("My output string.");

2
完善!谢谢。为了完整起见,事实证明我必须这样做:OutputDebugString(TEXT(“ Hello console world”)); ..大概是由于某种与unicode相关的构建选项。
izb

1
请注意,从sysinternals获得debugview会很有用。即使您没有在盒子上运行(或什至未安装)Visual Studio,也可以通过它查看ODS输出
pm100 2010年

4
@CDT:这取决于的类型myStr。是不是char*wchar_t*还是LPTSTR?假设char*您只是按照我的答案中的说明OutputDebugStringA(myStr)使用OutputDebugStringWwith wchar_t*OutputDebugStringwith进行调用或使用LPTSTR
马丁·利弗塞奇

1
@CDT:比调用具有要输出的消息的单个参数的函数更简单?是ANSI / UNICODE的复杂性吗?只需使用OutputDebugString并定义适当的预处理器符号以匹配您使用的字符的宽度,或者使用灵活的“ T”类型,即可将其编译为8位和16位字符。
马丁·利弗塞奇

1
@MonaJalal:您的评论尚不清楚屏幕是什么,因此很难为您提供具体建议。如果您调试过程,调试器将有一种方法来显示调试输出。如果将Visual Studio用作调试器,则输出将显示在“ 输出”窗口中。要实际查看输出,必须从“ 显示输出来自”下拉列表中选择“ 调试 ” 。如果出于某种原因在调试器外部运行进程,则可以使用DebugView查看所有进程的调试输出。
Martin Liversage

29

如果项目是GUI项目,则不会显示任何控制台。为了将项目更改为控制台,您需要转到项目属性面板并设置:

  • 在“链接器- >系统->子系统 ”中,值“ 控制台(/ SUBSYSTEM:CONSOLE)
  • 在“ C / C ++->预处理器->预处理器定义 ”中,添加“ _CONSOLE ”定义

仅当您具有经典的“ int main() ”入口点时,此解决方案才有效。

但是,如果像我这样(一个openGL项目),则无需编辑属性,因为这样效果更好:

AllocConsole();
freopen("CONIN$", "r",stdin);
freopen("CONOUT$", "w",stdout);
freopen("CONOUT$", "w",stderr);

printf和cout将照常工作。

如果在创建窗口之前调用AllocConsole,则控制台将显示在窗口后面,如果在之后调用,它将出现在窗口前面。

更新资料

freopen已弃用,可能不安全。使用freopen_s来代替:

FILE* fp;

AllocConsole();
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);

EDITBIN可以将子系统设置为,CONSOLE即使您使用WinMain而不是int main()
Ben Voigt 2013年

1
@扎克 谢谢!以AllocConsole()开头的4行效果很好。加1。尽管我在使用/ SUBSYSTEM:CONSOLE和/或_CONSOLE宏之前已经在Win32项目中显示过控制台,但其他任何操作都没有。不知道为什么今天晚上宏不起作用。它与使用公共语言运行时支持(/ clr)有关系吗?
riderBill 2015年

12

要打印到real控制台,您需要使用链接器标志使其可见/SUBSYSTEM:CONSOLE。额外的控制台窗口很烦人,但出于调试目的,它非常有价值。

OutputDebugString 在调试器中运行时,输出到调试器输出。


6
您也可以使用AllocConsole()分配自己的控制台
比利·奥尼尔

4

考虑使用VC ++运行时宏来报告_RPT N()和_RPTF N()

您可以使用CRTDBG.H中定义的_RPTn和_RPTFn宏来替换使用printf语句进行调试。当未定义_DEBUG时,这些宏会在您的发行版本中自动消失,因此无需将它们包含在#ifdefs中。

例...

if (someVar > MAX_SOMEVAR) {
    _RPTF2(_CRT_WARN, "In NameOfThisFunc( )," 
         " someVar= %d, otherVar= %d\n", someVar, otherVar );
}

或者,您可以直接使用VC ++运行时函数_CrtDbgReport,_CrtDbgReportW

_CrtDbgReport和_CrtDbgReportW可以将调试报告发送到三个不同的目的地:调试报告文件,调试监视器(Visual Studio调试器)或调试消息窗口。

_CrtDbgReport和_CrtDbgReportW使用printf或wprintf函数定义的相同规则,通过将arguments [n]参数替换为格式字符串来创建调试报告的用户消息。然后,这些函数将基于当前报告模式和为reportType定义的文件来生成调试报告并确定一个或多个目标。当报告发送到调试消息窗口时,文件名,行号和模块名将包含在窗口中显示的信息中。


值得一提的是,_RPTF0可以在格式字符串之后不传递任何变量的情况下使用或添加答案。_RPTFN另一方面,该宏在格式字符串之后至少需要一个参数。
AMN

4

如果要打印十进制变量:

wchar_t text_buffer[20] = { 0 }; //temporary buffer
swprintf(text_buffer, _countof(text_buffer), L"%d", your.variable); // convert
OutputDebugString(text_buffer); // print

4

如果需要查看广泛使用printf而不更改代码(或进行最小更改)的现有程序的输出,则可以按以下方式重新定义printf并将其添加到公共头文件(stdafx.h)中。

int print_log(const char* format, ...)
{
    static char s_printf_buf[1024];
    va_list args;
    va_start(args, format);
    _vsnprintf(s_printf_buf, sizeof(s_printf_buf), format, args);
    va_end(args);
    OutputDebugStringA(s_printf_buf);
    return 0;
}

#define printf(format, ...) \
        print_log(format, __VA_ARGS__)

1
由于使用了静态缓冲区,因此请小心,此函数不可重入,不能在不同线程中使用。
Nikazo

2

您的Win32项目可能是GUI项目,而不是控制台项目。这导致可执行标头中的差异。因此,您的GUI项目将负责打开其自己的窗口。不过,那可能是一个控制台窗口。调用AllocConsole()以创建它,并使用Win32控制台函数对其进行写入。


2

我一直在寻找一种自己做的方法,并想出了一个简单的解决方案。

我假设您在Visual Studio中启动了默认的Win32项目(Windows应用程序),该项目提供了“ WinMain”功能。默认情况下,Visual Studio将入口点设置为“ SUBSYSTEM:WINDOWS”。您需要先通过以下方法对此进行更改:

项目->属性->链接器->系统->子系统

然后从下拉列表中选择“控制台(/ SUBSYSTEM:CONSOLE)”。

现在,该程序将无法运行,因为需要“ main”功能而不是“ WinMain”功能。

因此,现在您可以像在C ++中一样添加“ main”函数。此后,要启动GUI程序,可以从“ main”函数内部调用“ WinMain”函数。

现在,程序的开始部分应如下所示:

#include <iostream>

using namespace std;

// Main function for the console
int main(){

    // Calling the wWinMain function to start the GUI program
    // Parameters:
    // GetModuleHandle(NULL) - To get a handle to the current instance
    // NULL - Previous instance is not needed
    // NULL - Command line parameters are not needed
    // 1 - To show the window normally
    wWinMain(GetModuleHandle(NULL), NULL,NULL, 1); 

    system("pause");
    return 0;
}

// Function for entry into GUI program
int APIENTRY wWinMain(_In_ HINSTANCE hInstance,
                     _In_opt_ HINSTANCE hPrevInstance,
                     _In_ LPWSTR    lpCmdLine,
                     _In_ int       nCmdShow)
{
    // This will display "Hello World" in the console as soon as the GUI begins.
    cout << "Hello World" << endl;
.
.
.

我执行的结果

现在,您可以使用函数在GUI程序的任何部分中输出到控制台,以进行调试或其他目的。


2

您还可以使用WriteConsole方法在控制台上进行打印。

AllocConsole();
LPSTR lpBuff = "Hello Win32 API";
DWORD dwSize = 0;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), lpBuff, lstrlen(lpBuff), &dwSize, NULL);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.