Як надрукувати у вікні виводу налагодження у програмі Win32?


97

У мене є проект win32, який я завантажив у Visual Studio 2005. Я хотів би мати можливість надрукувати речі у вікні виводу Visual Studio, але я не можу до кінця зрозуміти, як. Я спробував "printf" та "cout <<", але мої повідомлення залишаються вперто недрукованими.

Чи існує якийсь особливий спосіб друку у вікні виводу Visual Studio?


11
Зверніть увагу, що вікно виводу Visual Studio не є консоллю. Вони обидва "вікна з текстом у них", але різні за кадром.
MSalters

Відповіді:


136

Можна використовувати OutputDebugString. OutputDebugString- це макрос, який залежно від параметрів збірки перекладається на 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")); .. імовірно через якийсь варіант побудови, пов’язаний з юнікодом.
izb

1
зауважте, що вам буде корисно мати налагодження з sysinternals. Це дозволяє побачити вихід ODS, навіть якщо Visual Studio не працює (або навіть не встановлений) на коробці
pm100

4
@CDT: Це залежить від типу myStr. Це char*, wchar_t*чи LPTSTR? Якщо припустити, що це char*ви просто зателефонуйте OutputDebugStringA(myStr)або скористайтесь OutputDebugStringWіз wchar_t*і OutputDebugStringз, LPTSTRяк пояснено в моїй відповіді.
Мартін Ліверсаж

1
@CDT: Що простіше, ніж викликати функцію, що має один параметр, який є повідомленням, яке ви хочете вивести? Це складність ANSI / UNICODE? Просто використовуйте OutputDebugStringта визначайте відповідні символи препроцесора, щоб вони відповідали ширині використовуваних символів, або використовуйте гнучкі типи "T", що дозволяють компілювати як 8, так і 16 бітові символи.
Martin Liversage

1
@MonaJalal: З вашого коментаря не ясно, що це за екран , тому складно дати вам конкретні поради. Якщо ви налагодите ваш процес, налагоджувач матиме спосіб відобразити результати налагодження. Якщо ви використовуєте Visual Studio як налагоджувач, вихідні дані відображаються у вікні Вивід . Для того, щоб бачити на виході ви повинні вибрати Debug з шоу виходу з списку. Якщо ви з якихось причин запускаєте процес поза налагоджувачем, ви можете використовувати DebugView для перегляду результатів налагодження з усіх процесів.
Мартін Ліверсаж

29

Якщо проект є графічним інтерфейсом, консоль не відображатиметься. Для того, щоб змінити проект на консольний, потрібно перейти на панель властивостей проекту та встановити:

  • У " linker-> System-> SubSystem " значення " Console (/ SUBSYSTEM: CONSOLE) "
  • У " C / C ++ -> Preprocessor-> Preprocessor Definitions " додайте визначення " _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

1
@Zac. Дякую! 4 рядки, що починаються з AllocConsole (), чудово працювали. Плюс 1 за це. Більше нічого не працювало, хоча я вже отримав консолі, які раніше відображались у проектах Win32, перш ніж раніше використовував макроси / SUBSYSTEM: CONSOLE та / або _CONSOLE. Не знаю, чому макроси не працювали цього вечора. Чи може це мати щось спільне з використанням підтримки загальномовної середовища виконання (/ clr) ?
riderBill

12

Щоб надрукувати на realконсолі, потрібно зробити її видимою, використовуючи прапор компонувальника /SUBSYSTEM:CONSOLE. Додаткове вікно консолі дратує, але для цілей налагодження воно дуже цінне.

OutputDebugString друкує на виході налагоджувача під час роботи всередині налагоджувача.


6
Ви також можете виділити власну консоль за допомогою AllocConsole ()
Billy ONeal

4

Подумайте про використання макросів середовища виконання VC ++ для звітування _RPT N () та _RPTF N ()

Ви можете використовувати макроси _RPTn та _RPTFn, визначені в CRTDBG.H, щоб замінити використання операторів 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 створюють повідомлення користувача для звіту про налагодження, підставляючи аргументи [n] аргументи у рядок формату, використовуючи ті самі правила, визначені функціями printf або wprintf. Потім ці функції генерують звіт налагодження та визначають пункт призначення або пункти призначення на основі поточного режиму звіту та файлу, визначеного для 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
будьте обережні, оскільки через статичний буфер ця функція не переходить на повторний шлях і не може використовуватися з різних потоків.
Ніказо

2

Ваш проект Win32, швидше за все, є графічним інтерфейсом, а не консольним. Це спричиняє різницю у виконуваному заголовку. Як результат, ваш проект графічного інтерфейсу буде відповідальним за відкриття власного вікна. Однак це може бути вікном консолі. Зателефонуйте, AllocConsole()щоб створити його, і використовуйте функції консолі Win32, щоб писати на нього.


2

Я шукав спосіб зробити це сам і придумав просте рішення.

Я припускаю, що ви запустили проект Win32 за замовчуванням (додаток Windows) у Visual Studio, який надає функцію "WinMain". За замовчуванням Visual Studio встановлює точку входу на "ПІДСИСТЕМА: ВІКНА". Спочатку потрібно змінити це, перейшовши за адресою:

Проект -> Властивості -> Посилання -> Система -> Підсистема

І виберіть "Консоль (/ ПІДСИСТЕМА: КОНСОЛЬ)" зі спадного списку.

Тепер програма не працюватиме, оскільки замість функції "WinMain" потрібна функція "main".

Отже, тепер ви можете додати "основну" функцію, як це зазвичай робиться в C ++. Після цього, щоб запустити програму графічного інтерфейсу, ви можете викликати функцію "WinMain" зсередини функції "main".

Початкова частина вашої програми тепер повинна виглядати приблизно так:

#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;
.
.
.

Результат мого впровадження

Тепер ви можете використовувати функції для виведення на консоль у будь-якій частині вашої програми графічного інтерфейсу для налагодження чи інших цілей.


2

Ви також можете використовувати метод WriteConsole для друку на консолі.

AllocConsole();
LPSTR lpBuff = "Hello Win32 API";
DWORD dwSize = 0;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), lpBuff, lstrlen(lpBuff), &dwSize, NULL);
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.