Jak drukować do okna wyjściowego debugowania w aplikacji Win32?

99

Mam projekt win32, który załadowałem do programu Visual Studio 2005. Chciałbym móc drukować rzeczy do okna wyjściowego programu Visual Studio, ale do końca życia nie mogę się tego dowiedzieć. Próbowałem „printf” i „cout <<”, ale moje wiadomości uparcie pozostają niewydrukowane.

Czy istnieje jakiś specjalny sposób drukowania w oknie wyjściowym programu Visual Studio?

izb
źródło
11
Zwróć uwagę, że okno wyjściowe programu Visual Studio nie jest konsolą. Oba są „oknami z tekstem”, ale za kulisami różnią się od siebie.
MSalters
Jeśli okno wyjściowe VS domyślnie pokazuje pełną ścieżkę źródłowego CPP przed każdym komunikatem, rozważ obejście dla __ PLIK __.
Laurie Stearn

Odpowiedzi:

138

Możesz użyć OutputDebugString. OutputDebugStringto makro, które w zależności od opcji kompilacji jest mapowane na OutputDebugStringA(char const*)lub OutputDebugStringW(wchar_t const*). W późniejszym przypadku będziesz musiał dostarczyć do funkcji szeroki ciąg znaków. Aby utworzyć literał szerokiego znaku, możesz użyć Lprzedrostka:

OutputDebugStringW(L"My output string.");

Zwykle będziesz używać wersji makra razem z _Tmakrem w następujący sposób:

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

Jeśli projekt jest skonfigurowany do kompilowania dla UNICODE, rozwinie się do:

OutputDebugStringW(L"My output string.");

Jeśli nie tworzysz dla UNICODE, rozwinie się do:

OutputDebugStringA("My output string.");
Martin Liversage
źródło
2
Idealny! Dzięki. Jednak dla kompletności okazało się, że muszę to zrobić: OutputDebugString (TEXT ("Hello console world")); .. prawdopodobnie z powodu jakiejś opcji kompilacji związanej z Unicode.
izb
1
zauważ, że przydaje się opcja debugview z sysinternals. Pozwala to zobaczyć dane wyjściowe ODS, nawet jeśli program Visual Studio nie jest uruchomiony (ani nawet zainstalowany) na pudełku
pm100
4
@CDT: To zależy od typu myStr. Jest to char*, wchar_t*czy LPTSTR? Zakładając, że jest to char*po prostu zadzwonić OutputDebugStringA(myStr)lub skorzystać OutputDebugStringWz wchar_t*i OutputDebugStringz LPTSTRjak wyjaśniono w mojej odpowiedzi.
Martin Liversage
1
@CDT: Co jest prostsze niż wywołanie funkcji mającej pojedynczy parametr, którym jest wiadomość, którą chcesz wyprowadzić? Czy jest to złożoność ANSI / UNICODE? Po prostu użyj OutputDebugStringi albo zdefiniuj odpowiednie symbole preprocesora, aby pasowały do ​​szerokości używanych znaków, albo wybierz elastyczne typy „T”, które pozwalają na kompilację do znaków 8- i 16-bitowych.
Martin Liversage
1
@MonaJalal: Z twojego komentarza nie wynika jasno, który ekran jest, więc trochę trudno jest udzielić ci konkretnej porady. Jeśli debugujesz swój proces, debugger będzie miał sposób na wyświetlenie wyniku debugowania. Jeśli używasz programu Visual Studio jako debugera, dane wyjściowe są wyświetlane w oknie danych wyjściowych . Aby faktycznie zobaczyć dane wyjściowe, musisz wybrać Debuguj z listy rozwijanej Pokaż wyjście z . Jeśli z jakiegoś powodu uruchamiasz proces poza debuggerem, możesz użyć DebugView, aby wyświetlić dane wyjściowe debugowania ze wszystkich procesów.
Martin Liversage
29

Jeśli projekt jest projektem GUI, konsola nie pojawi się. Aby zamienić projekt na konsolowy należy wejść do panelu właściwości projektu i ustawić:

  • W „ linker-> System-> SubSystem ” wartość „ Console (/ SUBSYSTEM: CONSOLE)
  • W „ C / C ++ -> Preprocessor-> Preprocessor Definitions ” dodaj definicję_CONSOLE

To rozwiązanie działa tylko wtedy, gdy masz klasyczną metodęint main () punkt wejścia ”.

Ale jeśli jesteś taki jak w moim przypadku (projekt openGL), nie musisz edytować właściwości, ponieważ działa to lepiej:

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

printf i cout będą działać jak zwykle.

Jeśli wywołasz AllocConsole przed utworzeniem okna, konsola pojawi się za oknem, jeśli wywołasz ją później, pojawi się przed.

Aktualizacja

freopenjest przestarzały i może być niebezpieczny. Użyj freopen_szamiast tego:

FILE* fp;

AllocConsole();
freopen_s(&fp, "CONIN$", "r", stdin);
freopen_s(&fp, "CONOUT$", "w", stdout);
freopen_s(&fp, "CONOUT$", "w", stderr);
Zac
źródło
EDITBINmożna ustawić podsystem na, CONSOLEnawet jeśli używasz WinMainzamiast int main().
Ben Voigt,
1
@Zac. Dzięki! 4 wiersze zaczynające się od AllocConsole () działały świetnie. Plus 1 za to. Nic innego nie działało, chociaż konsole pojawiały się wcześniej w projektach Win32 przed użyciem makr / SUBSYSTEM: CONSOLE i / lub _CONSOLE. Nie wiem, dlaczego makra nie działały tego wieczoru. Czy może to mieć coś wspólnego z używaniem obsługi Common Language Runtime Support (/ clr) ?
riderBill
12

Aby drukować na realkonsolę, musisz uczynić ją widoczną za pomocą flagi konsolidatora /SUBSYSTEM:CONSOLE. Dodatkowe okno konsoli jest denerwujące, ale do celów debugowania jest bardzo cenne.

OutputDebugString drukuje na wyjściu debugera podczas uruchamiania wewnątrz debugera.

Dzwonienie
źródło
6
Możesz także przydzielić własną konsolę za pomocą AllocConsole ()
Billy ONeal
5

Rozważ użycie makr środowiska wykonawczego VC ++ do raportowania _RPT N () i _RPTF N ()

Możesz użyć makr _RPTn i _RPTFn, zdefiniowanych w CRTDBG.H, aby zastąpić użycie instrukcji printf do debugowania. Te makra automatycznie znikają w kompilacji wydania, gdy nie zdefiniowano parametru _DEBUG, więc nie ma potrzeby umieszczania ich w #ifdefs.

Przykład...

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

Lub możesz bezpośrednio użyć funkcji wykonawczych VC ++ _CrtDbgReport, _CrtDbgReportW .

_CrtDbgReport i _CrtDbgReportW mogą wysyłać raport debugowania do trzech różnych miejsc docelowych: plik raportu debugowania, monitor debugowania (debuger programu Visual Studio) lub okno komunikatu debugowania.

_CrtDbgReport i _CrtDbgReportW tworzą komunikat użytkownika dla raportu debugowania, zastępując argumenty [n] argumentów w ciągu formatu, używając tych samych reguł zdefiniowanych przez funkcje printf lub wprintf. Funkcje te następnie generują raport debugowania i określają miejsce docelowe lub miejsca docelowe w oparciu o bieżące tryby raportów i plik zdefiniowany dla reportType. Gdy raport jest wysyłany do okna komunikatu debugowania, nazwa pliku, numer linii i nazwa modułu są zawarte w informacjach wyświetlanych w oknie.

Samouk
źródło
Warto dodać do odpowiedzi lub zanotować, że _RPTF0można jej użyć, gdy nie oczekuje się przekazywania zmiennych po ciągu formatu. Z _RPTFNdrugiej strony makro wymaga co najmniej jednego argumentu po ciągu formatu.
amn
5

Jeśli chcesz wydrukować zmienne dziesiętne:

wchar_t text_buffer[20] = { 0 }; //temporary buffer
swprintf(text_buffer, _countof(text_buffer), L"%d", your.variable); // convert
OutputDebugString(text_buffer); // print
svensito
źródło
%udla unsigned, %fdla float zgodnie z odniesieniem .
Laurie Stearn
4

Jeśli chcesz zobaczyć wyjście istniejącego programu, który szeroko używał printf bez zmiany kodu (lub z minimalnymi zmianami), możesz przedefiniować printf w następujący sposób i dodać go do wspólnego nagłówka (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__)
vlad
źródło
1
uważaj ze względu na bufor statyczny, ta funkcja nie jest ponownie wprowadzana i nie może być używana z różnych wątków.
Nikazo
2

Twój projekt Win32 jest prawdopodobnie projektem GUI, a nie projektem konsoli. Powoduje to różnicę w nagłówku pliku wykonywalnego. W rezultacie projekt GUI będzie odpowiedzialny za otwarcie własnego okna. Może to być jednak okno konsoli. Wywołaj, AllocConsole()aby go utworzyć, i użyj funkcji konsoli Win32, aby go zapisać.

MSalters
źródło
2

Sam szukałem sposobu, aby to zrobić i znalazłem proste rozwiązanie.

Zakładam, że uruchomiłeś domyślny projekt Win32 (aplikacja Windows) w programie Visual Studio, który udostępnia funkcję „WinMain”. Domyślnie program Visual Studio ustawia punkt wejścia na „PODSYSTEM: WINDOWS”. Najpierw musisz to zmienić, przechodząc do:

Projekt -> Właściwości -> Konsolidator -> System -> Podsystem

I wybierz „Konsola (/ SUBSYSTEM: CONSOLE)” z listy rozwijanej.

Teraz program nie będzie działać, ponieważ zamiast funkcji „WinMain” potrzebna jest funkcja „główna”.

Teraz możesz dodać funkcję „główną”, tak jak zwykle w C ++. Następnie, aby uruchomić program GUI, możesz wywołać funkcję „WinMain” z wnętrza funkcji „main”.

Początkowa część twojego programu powinna teraz wyglądać mniej więcej tak:

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

Wynik mojej realizacji

Teraz możesz używać funkcji do wyświetlania na konsoli w dowolnej części programu GUI w celu debugowania lub do innych celów.

wyskakuje
źródło
2

Możesz także użyć metody WriteConsole do drukowania na konsoli.

AllocConsole();
LPSTR lpBuff = "Hello Win32 API";
DWORD dwSize = 0;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), lpBuff, lstrlen(lpBuff), &dwSize, NULL);
HaseeB Mir
źródło