^(코딩캣)^ = @"코딩"하는 고양이;

[Windows API] OutputDebugString을 printf처럼 서식(포맷) 적용하여 사용하기

Microsoft Windows/단편 Windows API 예제
2020. 6. 5. 15:03

Windows API


본 시리즈에서는 Windows API 개별 함수에 대해 간략한 사용법 및 응용에 대해 정리한다.

이전 게시글: 크리티컬 섹션을 사용하여 스레드간 변수 공유

 

OutputDebugString을 printf처럼 서식(포맷) 적용하여 사용하기


OutputDebugString은 Windows 응용 프로프램이 실행되는 동안 디버그 출력 채널을 통하여 테스트에 필요한 문자열을 출력할 있는 함수이다. MSDN에 따르면 그 원형은 다음과 같이 선언되어 있다.

VOID OutputDebugString(
	LPCTSTR lpOutputString // string to be displayed
);

위와 같이 OutputDebugString은 이미 완성된 문자열만을 출력할 뿐, printf처럼 서식과 가변인수를 전달하는 기능이 없다. 때문에 변수의 값을 출력하고자 할 때 별도의 버퍼를 마련해야 하고, 유니코드 또는 MBCS 매크로에 따른 TCHAR 처리 등 매번 출력할 때마다 다소 불편한 면이 있다. 물론 MFC로 가면 TRACE 매크로가 있어서 디버그 문자열 출력 시 변수 출력과 서식 지정이 용이하지만, Windows API만을 사용하여 코딩할경우 이상하게도 마땅한 공식적인 함수가 없다.

기존 함수의 불편을 덜고, 디버그 문자열 출력 시 변수의 값 출력을 용이하게 하기 위하여 새 함수를 다음과 같이 정의할 수 있다.

#define BUFFER_CONST 8192 // 내부 버퍼의 크기 (필요한만큼 조정 가능)

VOID OutputFormattedDebugString(LPCTSTR format, ...) {
	static TCHAR buffer[BUFFER_CONST] = { TEXT('\0'), }; // TCHAR형 문자 버퍼

	va_list arg; // 가변인수 벡터

	va_start(arg, format); // 가변인수 벡터 리셋
	_vstprintf(buffer, format, arg);
	va_end(arg);

	OutputDebugString(buffer);
	ZeroMemory(buffer, sizeof(buffer)); // 사용 후 버퍼 내용 지우기
}

이 함수의 작동은 비교적 간단한 편이다. 임시 버퍼에 서식대로 문자열을 출력하여 완성된 문자열을 얻고, 이것을 다시 OutputDebugString에 전달하는 것이다.

Windows API는 문자형으로 TCHAR를 사용한다. 임시 버퍼로 문자열을 출력할 때 이를 고려하여 함수를 선택할 필요가 있다.

printf는 지정된 서식을 따라 표준 출력에 문자열을 출력하는 함수이다. 이 함수의 와이드 문자 버전은 wprintf이다. printfwprintfTCHAR형으로 일반화한 명칭이 _tprintf이다.

/* stdio.h */
int printf(const char *format [, argument]... );
/* wchar.h */
int wprintf(const wchar_t *format [, argument]... );
/* tchar.h */
#ifdef _UNICODE
#define _tprintf wprintf
#else
#define _tprintf printf
#endif

sprintf는 지정된 서식을 따라 특정한 버퍼에 문자열을 출력하는 함수이다. 이 함수의 와이드 문자 버전은 swprintf이다. sprintfswprintfTCHAR형으로 일반화한 명칭이 _stprintf이다.

/* stdio.h */
int sprintf( char *buffer, const char *format [, argument] ... );
/* wchar.h */
int swprintf( wchar_t *buffer, const wchar_t *format [, argument] ... );
/* tchar.h */
#ifdef _UNICODE 
#define _stprintf swprintf
#else
#define _stprintf sprintf
#endif

printf, sprintf는 가변인수를 하드코딩으로 받는 함수이다. 서식과 가변인수가 몇 개가 있든, 동적으로 문자열을 출력하기 위하여 가변인수 벡터로 가변인수를 받는 함수가 vprintfvsprintf이다. 각각 표준 출력과 특정한 버퍼에 완성된 문자열을 출력한다.

vprintf의 와이드 문자 버전은 vwprintf이고, vprintfvwprintfTCHAR형으로 일반화한 명칭이 _vtprintf이다. 마찬가지로 특정 버퍼로 문자열을 출력하는 함수는 vsprintf, vswprintf이고 이를 일반화한 명칭이 _vstprintf이다. 본 함수에서 _vstprintf를 사용한 이유는 이와 같다.


/* stdio.h */
int vprintf( const char *format, va_list argptr );
int vsprintf( char *buffer, const char *format, va_list argptr ); 
/* wchar.h */
int vwprintf( const wchar_t *format, va_list argptr );
int vswprintf( wchar_t *buffer, const wchar_t *format, va_list argptr );
/* tchar.h */
#ifdef _UNICODE
#define _vtprintf vwprintf
#define _vstprintf vswprintf
#else
#define _vtprintf vprintf
#define _vstprintf vsprintf
#endif

사용 방법은 printf와 다르지 않다. 서식을 첫 번째 인수에 넣고, 다음 인수부터는 0개 이상의 변수들을 전달하면 된다. 다음은 그 예이다.

VOID OutputFormattedDebugString(LPCTSTR format, ...); // 새롭게 정의한 함수 원형

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) {
	OutputFormattedDebugString(TEXT("Hello, World!\n")); // 일반 문자열 출력 테스트
	OutputFormattedDebugString(TEXT("%d + %d = %d\n"), 1, 2, 1 + 2); // 가변인수를 통한 값 출력 테스트
	OutputFormattedDebugString(TEXT("string: \"%s\"\n"), TEXT("DebugString")); // 문자열 서식 테스트
	OutputFormattedDebugString(TEXT("character: '%c'\n"), TEXT('A')); // 문자 서식 테스트
	return 0;
}

소스 코드를 디버그하였을 때 출력 창에 의도대로 출력된 것을 확인할 수 있다.

새롭게 정의한 OutputFormattedDebugString의 작동