🧼C, C++/자체제작 프로그램및 배포

윈도우 콘솔디버깅 ( C++ / 20 )

Mawile 2021. 7. 4.
728x90

 

개발환경 >> Visual Studio 2022 Preview

언어 >> C++20에서 정상작동확인

운영체제 >> Windows10 home

 


안녕하세요!!

혹시 윈도우 애플리케이션 개발해보신분들중에서,

도중에 변수값을 확인하고싶은데 확인하는부분을 따로 만들기 귀찮으신분계신가요??

윈도우 애플리케이션개발을 하다보면,

콘솔과는다르게 문자열값이 아닌값을 확인하려면 귀찮게 그부분도 따로 구현했어야됩니다..

이번에는 윈도우 애플리케이션환경에서 콘솔환경에서의 디버깅을 쉽게 하도록하는 라이브러리를 만들었습니다.

솔직히 디버깅이라는거는 그냥 부주제로 써본것이고, 윈도우애플레케이션환경에서도 콘솔에다가 c++ 스탠다드 라이브러리인 iostream에 포함된 std::cout을 마음껏 사용할수있는 라이브러리를 만들어왔습니다.

 

근데, 실제로 앞으로 알려드릴 "ConsoleDebug.h"헤더파일에서는 실제 std::cout을 사용하지않았습니다.

왜냐하면, winapi로 할당된 콘솔에는 std::cout이 안되기때문이죠.

따라서 WriteFile함수를 이용해서 하나하나 타입에따라 오버로딩했습니다.

 

샘플용 소스코드
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
#include "ConsoleDebug.h"

LRESULT CALLBACK WindowProcedure(HWND hwnd, UINT msg, WPARAM wp, LPARAM lp) {
	switch (msg) {
	case WM_DESTROY:
		PostQuitMessage(0);
		break;
	}
	return DefWindowProcW(hwnd, msg, wp, lp);
}

WPARAM InitWindow(HINSTANCE hIn) {
	WNDCLASSW w = { 0 };
	MSG msg = { 0 };

	w.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	w.hCursor = LoadCursorW(hIn, IDC_ARROW);
	w.hIcon = LoadIconW(hIn, IDI_APPLICATION);
	w.hInstance = hIn;
	w.lpfnWndProc = WindowProcedure;
	w.lpszClassName = L"DP";

	if (!RegisterClassW(&w)) return -1;
	if (!CreateWindowExW(0, L"DP", L"TITLE", WS_VISIBLE | WS_OVERLAPPEDWINDOW, 100, 100, 600, 600, 0, 0, 0, 0)) return -1;

	while (GetMessageW(&msg, 0, 0, 0)) {
		TranslateMessage(&msg);
		DispatchMessageW(&msg);
	}

	return msg.wParam;
}

int _stdcall WinMain(HINSTANCE hInst, HINSTANCE hPrev, LPSTR lp, int cmd) {
//생성자의 첫번째인자는 디버깅용 콘솔할당여부
//생성자의 두번째인자는 와이드문자열 인코딩방식
	ConsoleDebug* cd = new ConsoleDebug(true, CP_ACP);

	for (int i = 0; i < 10; ++i) {
		*cd << L"test : " << i << '\n'; // <<연산자로 출력가능합니다.
	}
	try {
		//cd->Start(); //해당부분 주석제외시, 예외사항발생, 이유는 이미 시작된 디버깅이시작된 객체이다.
		//cd->Close(); cd->Close(); //해당부분 주석제외시, 예외사항발생, 종료된 객체를 한번더 종료했기때문이다.
	}
	catch (std::exception const& e) {
		MessageBoxA(0, e.what(), "", MB_OK);
		delete cd;
		return -1;
	}

	WPARAM wp = InitWindow(hInst);

	delete cd;
	return wp;
}

실행결과

와우!!

분명 윈도우애플리케이션인데,

엄청 간편하게 콘솔창을 할당하고, std::cout 처럼 간단하게 출력하었습니다.

 

그외 부수적인 멤버함수도 몇가지 만들어놨습니다.

void ConsoleDebug::Start() : 콘솔창 할당, 이미 할당된 객체라면 예외발생

void ConsoleDebug::Close() : 콘솔창 할당해제, 이미 할당이 해제된 객체라면 예외발생

std::string ConsoleDebug::Buffer() : 현재 출력스트림의 내용을 반환한다. 예외없음

void ConsoleDebug::Clear() : 현재 출력스트림의 내용을 제거한다. 예외없음

void ConsoleDebug::WideCharEncode(UINT) : 와이드문자열 출력시, 인코딩방식을 결정한다. 예외없음

정의된 인코딩방식 매크로

<<연산자는 int, float, double, char, char*, wchar_t*, std::string타입이 오버로딩 되어있습니다. 예외없음

 

 

ConsoleDebug.h
#pragma once
#include <iostream>
#include <Windows.h>


class ConsoleDebug {
public:
	std::string Buffer() { return buffer; }
	void Clear() { buffer = ""; }

	void Start() {
		std::exception e("Exception: Already an console is allocated.");

		if (!Allocation) {
			Clear();
			Allocation = true;
			AllocConsole();
		}
		else throw e;
	}

	void Close() {
		std::exception e("Exception: Already an console is free.");

		if (Allocation) {
			Clear();
			Allocation = false;
			FreeConsole();
		}
		else throw e;
	}

	void WideCharEncode(UINT _CodePage) { Encode = _CodePage; }

	friend ConsoleDebug& operator<<(ConsoleDebug& cd, int arg) noexcept {
		if (!cd.Allocation) return cd;
		char buf[30];
		sprintf_s(buf, "%d", arg);
		WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, std::strlen(buf), 0, 0);
		cd.buffer += buf;
		return cd;
	}

	friend ConsoleDebug& operator<<(ConsoleDebug& cd, float arg) noexcept {
		if (!cd.Allocation) return cd;
		char buf[30];
		sprintf_s(buf, "%f", arg);
		WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, std::strlen(buf), 0, 0);
		cd.buffer += buf;
		return cd;
	}

	friend ConsoleDebug& operator<<(ConsoleDebug& cd, double arg) noexcept {
		if (!cd.Allocation) return cd;
		char buf[30];
		sprintf_s(buf, "%lf", arg);
		WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), buf, std::strlen(buf), 0, 0);
		cd.buffer += buf;
		return cd;
	}

	friend ConsoleDebug& operator<<(ConsoleDebug& cd, char arg) noexcept {
		if (!cd.Allocation) return cd;
		WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), &arg, 1, 0, 0);
		cd.buffer += arg;
		return cd;
	}

	friend ConsoleDebug& operator<<(ConsoleDebug& cd, const char* arg) noexcept {
		if (!cd.Allocation) return cd;
		WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), arg, std::strlen(arg), 0, 0);
		cd.buffer += arg;
		return cd;
	}

	friend ConsoleDebug& operator<<(ConsoleDebug& cd, const wchar_t* arg) noexcept {
		if (!cd.Allocation) return cd;
		const int len = std::wcslen(arg) + 1;
		char* mul = new char[len];
		memset(mul, 0x00, len);

		WideCharToMultiByte(cd.Encode, 0, arg, len, mul, len, 0, 0);
		WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), mul, std::strlen(mul), 0, 0);
		cd.buffer += mul;

		delete[] mul;
		return cd;
	}

	friend ConsoleDebug& operator<<(ConsoleDebug& cd, std::string const& arg) noexcept {
		if (!cd.Allocation) return cd;
		WriteFile(GetStdHandle(STD_OUTPUT_HANDLE), arg.c_str(), arg.size(), 0, 0);
		cd.buffer += arg;
		return cd;
	}

	ConsoleDebug() noexcept { }
	explicit(true) ConsoleDebug(bool _Begin = false, UINT _CodePage = CP_ACP) noexcept {
		Encode = _CodePage;
		if (_Begin) Start();
	}
	~ConsoleDebug() noexcept {
		Encode = CP_ACP;
		if (Allocation) {
			Clear();
			Allocation = false;
			FreeConsole();
		}
	}

private:
	std::string buffer;
	bool Allocation = false;
	UINT Encode = CP_ACP;
};

 

728x90

댓글