ฉันจะพิมพ์ไปยังหน้าต่างผลลัพธ์การแก้ไขข้อบกพร่องในแอป Win32 ได้อย่างไร


99

ฉันมีโปรเจ็กต์ win32 ที่ฉันโหลดลงใน Visual Studio 2005 ฉันต้องการที่จะสามารถพิมพ์สิ่งต่าง ๆ ไปยังหน้าต่างเอาต์พุต Visual Studio แต่ฉันทำไม่ได้ตลอดชีวิตของฉัน ฉันได้ลอง 'printf' และ 'cout <<' แล้ว แต่ข้อความของฉันยังคงไม่ได้พิมพ์ออกมา

มีวิธีพิเศษในการพิมพ์ไปยังหน้าต่างเอาต์พุต Visual Studio หรือไม่?


11
โปรดทราบว่าหน้าต่างเอาต์พุต Visual Studio ไม่ใช่คอนโซล ทั้งสองเป็น "หน้าต่างที่มีข้อความอยู่" แต่เบื้องหลังต่างกัน
MSalters

หากหน้าต่างเอาต์พุต VS เริ่มต้นเพื่อแสดงเส้นทางแบบเต็มของ cpp ต้นทางก่อนแต่ละข้อความให้พิจารณาวิธีแก้ปัญหา สำหรับ __ FILE __
Laurie Stearn

คำตอบ:


138

คุณสามารถใช้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")); .. น่าจะเป็นเพราะตัวเลือกการสร้างที่เกี่ยวข้องกับ Unicode
izb

1
โปรดทราบว่าคุณจะพบว่าการมี debugview จาก sysinternals มีประโยชน์ สิ่งนี้ช่วยให้คุณเห็นเอาต์พุต ODS แม้ว่า Visual Studio จะไม่ทำงาน (หรือแม้แต่ติดตั้ง) บนกล่อง
pm100

4
@CDT: ขึ้นอยู่กับประเภทของmyStrไฟล์. มันเป็นchar*, wchar_t*หรือLPTSTR? สมมติว่าchar*คุณเรียกOutputDebugStringA(myStr)หรือใช้OutputDebugStringWกับwchar_t*และOutputDebugStringด้วยLPTSTRตามที่อธิบายไว้ในคำตอบของฉัน
Martin Liversage

1
@CDT: อะไรจะง่ายไปกว่าการเรียกใช้ฟังก์ชันที่มีพารามิเตอร์เดียวซึ่งเป็นข้อความที่คุณต้องการส่งออก เป็นความซับซ้อนของ ANSI / UNICODE หรือไม่? เพียงใช้OutputDebugStringและกำหนดสัญลักษณ์ตัวประมวลผลก่อนที่เหมาะสมเพื่อให้ตรงกับความกว้างของอักขระที่คุณใช้หรือใช้กับประเภท "T" ที่ยืดหยุ่นซึ่งช่วยให้คุณสามารถรวบรวมอักขระทั้ง 8 และ 16 บิต
Martin Liversage

1
@MonaJalal: มันไม่ชัดเจนจากความคิดเห็นของคุณว่าเป็นหน้าจอใดจึงเป็นการยากที่จะให้คำแนะนำเฉพาะแก่คุณ หากคุณดีบักกระบวนการของคุณดีบักเกอร์จะมีวิธีแสดงเอาต์พุตการดีบัก ถ้าคุณกำลังใช้ Visual Studio เป็นตัวแก้ไขข้อบกพร่องผลลัพธ์จะแสดงในหน้าต่างผลลัพธ์ หากต้องการดูผลลัพธ์จริงๆคุณต้องเลือกDebugจากShow output from dropdown หากคุณกำลังเรียกใช้กระบวนการของคุณนอกดีบักเกอร์ด้วยเหตุผลบางประการคุณสามารถใช้DebugViewเพื่อดูผลลัพธ์การดีบักจากกระบวนการทั้งหมด
Martin Liversage

29

หากโปรเจ็กต์เป็นโปรเจ็กต์ GUI จะไม่มีคอนโซลปรากฏขึ้น ในการเปลี่ยนโปรเจ็กต์เป็นคอนโซลคุณต้องไปที่แผงคุณสมบัติโปรเจ็กต์และตั้งค่า:

  • ใน " 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
@ แซค. ขอบคุณ! 4 บรรทัดที่ขึ้นต้นด้วย AllocConsole () ใช้งานได้ดี บวก 1 สำหรับสิ่งนั้น ไม่มีอะไรใช้งานได้แม้ว่าฉันจะมีคอนโซลที่จะแสดงมาก่อนในโปรเจ็กต์ Win32 ก่อนที่จะใช้มาโคร / SUBSYSTEM: CONSOLE และ / หรือ _CONSOLE มาก่อน ไม่รู้ว่าทำไมมาโครถึงไม่ทำงานในเย็นนี้ จะมีอะไรเกี่ยวข้องกับการใช้Common Language Runtime Support (/ clr)หรือไม่
riderBill

13

หากต้องการพิมพ์ไปยังคอนโซลคุณต้องทำให้มันมองเห็นได้โดยใช้ธงลิงเกอร์real /SUBSYSTEM:CONSOLEหน้าต่างคอนโซลพิเศษนั้นน่ารำคาญ แต่สำหรับวัตถุประสงค์ในการดีบักมันมีค่ามาก

OutputDebugString พิมพ์ไปยังเอาต์พุตดีบักเกอร์เมื่อรันภายในดีบักเกอร์


6
คุณยังสามารถจัดสรรคอนโซลของคุณเองโดยใช้ AllocConsole ()
Billy ONeal

6

หากคุณต้องการพิมพ์ตัวแปรทศนิยม:

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

%uสำหรับที่ไม่ได้ลงชื่อ%fสำหรับลอยตามการอ้างอิง
Laurie Stearn

5

พิจารณาใช้แมโครรันไทม์ 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 เมื่อรายงานถูกส่งไปยังหน้าต่างข้อความดีบักชื่อไฟล์ lineNumber และ moduleName จะรวมอยู่ในข้อมูลที่แสดงในหน้าต่าง


ควรเพิ่มคำตอบหรือข้อสังเกตที่_RPTF0สามารถใช้ได้โดยที่ไม่คาดว่าจะมีการส่งผ่านตัวแปรหลังจากสตริงรูปแบบ ในทางกลับกัน_RPTFNมาโครต้องการอาร์กิวเมนต์อย่างน้อยหนึ่งรายการตามสตริงรูปแบบ
61

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
ระวังเนื่องจากบัฟเฟอร์แบบคงที่ฟังก์ชันนี้จะไม่ reentrant และไม่สามารถใช้จากเธรดอื่น
Nikazo

2

โครงการ Win32 ของคุณน่าจะเป็นโครงการ GUI ไม่ใช่โครงการคอนโซล สิ่งนี้ทำให้เกิดความแตกต่างในส่วนหัวที่เรียกใช้งานได้ ดังนั้นโครงการ GUI ของคุณจะต้องรับผิดชอบในการเปิดหน้าต่างของตัวเอง นั่นอาจเป็นหน้าต่างคอนโซล เรียกAllocConsole()เพื่อสร้างและใช้ฟังก์ชันคอนโซล Win32 เพื่อเขียนลงไป


2

ฉันกำลังมองหาวิธีที่จะทำสิ่งนี้ด้วยตัวเองและคิดหาวิธีง่ายๆ

ฉันสมมติว่าคุณเริ่ม Win32 Project (แอปพลิเคชัน Windows) เริ่มต้นใน Visual Studio ซึ่งมีฟังก์ชัน "WinMain" ตามค่าเริ่มต้น Visual Studio จะตั้งค่าจุดเข้าเป็น "SUBSYSTEM: WINDOWS" ก่อนอื่นคุณต้องเปลี่ยนแปลงโดยไปที่:

โครงการ -> คุณสมบัติ -> Linker -> ระบบ -> ระบบย่อย

และเลือก "คอนโซล (/ SUBSYSTEM: CONSOLE)" จากรายการแบบเลื่อนลง

ตอนนี้โปรแกรมจะไม่ทำงานเนื่องจากจำเป็นต้องใช้ฟังก์ชัน "main" แทนฟังก์ชัน "WinMain"

ตอนนี้คุณสามารถเพิ่มฟังก์ชัน "หลัก" ได้เหมือนปกติใน C ++ หลังจากนี้ในการเริ่มโปรแกรม GUI คุณสามารถเรียกใช้ฟังก์ชัน "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;
.
.
.

ผลการใช้งานของฉัน

ตอนนี้คุณสามารถใช้ฟังก์ชันเพื่อส่งออกไปยังคอนโซลในส่วนใดก็ได้ของโปรแกรม GUI ของคุณเพื่อการดีบักหรือเพื่อวัตถุประสงค์อื่น ๆ


2

คุณยังสามารถใช้เมธอดWriteConsoleเพื่อพิมพ์บนคอนโซล

AllocConsole();
LPSTR lpBuff = "Hello Win32 API";
DWORD dwSize = 0;
WriteConsole(GetStdHandle(STD_OUTPUT_HANDLE), lpBuff, lstrlen(lpBuff), &dwSize, NULL);
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.