羽夏闲谈——调试控制台
前言
??在Windows
上使用C++
写项目需要控制台显示一些信息,尤其一些GUI
程序。虽然自带输出和相关调试函数,但总是不太方便,来回切挺麻烦的,这里分享一下我封装好的带有格式化输出的调试控制台供大家使用。如下是头文件:
#pragma once
// 作者:WingSummer(寂静的羽夏)
// 协议:MIT
// 作用:对调试控制台的封装,仅限于 Windows 平台
#include
class CDebugConsole
{
public:
///
/// 初始化控制台,使用前必须执行
///
/// 成功返回 true,失败返回 false
bool static InitConsole();
///
/// 关闭控制台并释放使用的资源
///
///
bool static CloseConsole();
///
/// 向控制台输出字符串信息
///
/// 想要输出的 ASCII 字符串
/// 成功返回 true,失败返回 false
bool static Write(char* info);
///
/// 向控制台输出字符串信息
///
/// 想要输出的宽字符字符串
/// 成功返回 true,失败返回 false
bool static Write(wchar_t* info);
///
/// 向控制台输出格式化字符串信息
///
/// 待格式化的 ASCII 字符串
/// 格式化参数
/// 成功返回 true,失败返回 false
bool static WritePrintf(char* format, ...);
///
/// 向控制台输出格式化字符串信息
///
/// 待格式化的宽字符字符串
/// 格式化参数
/// 成功返回 true,失败返回 false
bool static WritePrintf(wchar_t* format, ...);
///
/// 向控制台输出一行字符串信息
///
/// 想要输出的 ASCII 字符串
/// 成功返回 true,失败返回 false
bool static WriteLine(char* info);
///
/// 向控制台输出一行字符串信息
///
/// 想要输出的宽字符字符串
/// 成功返回 true,失败返回 false
bool static WriteLine(wchar_t* info = L"");
///
/// 向控制台输出一行格式化字符串信息
///
/// 待格式化的 ASCII 字符串
/// 成功返回 true,失败返回 false
///
bool static WritePrintfLine(char* format, ...);
///
/// 向控制台输出一行格式化字符串信息
///
/// 待格式化的宽字符字符串
/// 格式化参数
/// 成功返回 true,失败返回 false
bool static WritePrintfLine(wchar_t* format, ...);
///
/// 设置控制台置顶,只在初始化成功控制台有效
///
/// true 则为设置置顶,反之取消
void static SetTopMost(bool topmost);
};
??如下是函数实现:
// 作者:WingSummer(寂静的羽夏)
// 协议:MIT
// 作用:对调试控制台的封装,仅限于 Windows 平台
#include "pch.h" //这个是预编译头,如果代码项目没有就删掉
#include "CDebugConsole.h"
#include
#define CharBufferSize 4096
#define WCharBufferSize 2048
#pragma warning(disable : 4267)
static HANDLE handle;
static void* buffer = NULL;
HWND console;
bool CDebugConsole::InitConsole()
{
if (handle)
return false;
bool status = AllocConsole();
handle = GetStdHandle(STD_OUTPUT_HANDLE);
console = GetConsoleWindow();
SetWindowTextW(console, L"调试输出控制台");
HMENU menu = GetSystemMenu(console, NULL);
RemoveMenu(menu, SC_CLOSE, NULL);
//申请分配一个物理页,我不信你的字符串会长于2047个
buffer = VirtualAlloc(NULL, CharBufferSize, MEM_COMMIT, PAGE_READWRITE);
return status;
}
bool CDebugConsole::CloseConsole()
{
if (buffer) VirtualFree(buffer, NULL, MEM_FREE);
bool status = FreeConsole();
handle = NULL;
console = NULL;
return status;
}
bool CDebugConsole::Write(char* info)
{
if (!handle)
return false;
return WriteConsoleA(handle, info, strlen(info), NULL, NULL);
}
bool CDebugConsole::Write(wchar_t* info)
{
if (!handle)
return false;
return WriteConsoleW(handle, info, wcslen(info), NULL, NULL);
}
bool CDebugConsole::WritePrintf(char* format, ...)
{
va_list ap;
va_start(ap, format);
int outcount = _vsnprintf_s((char*)buffer, CharBufferSize, CharBufferSize, format, ap);
if (outcount < 0 || outcount > CharBufferSize)
return false;
bool status = WriteConsoleA(handle, buffer, outcount, NULL, NULL);
va_end(ap);
return status;
}
bool CDebugConsole::WritePrintf(wchar_t* format, ...)
{
va_list ap;
va_start(ap, format);
int outcount = _vsnwprintf_s((wchar_t*)buffer, WCharBufferSize, WCharBufferSize, format, ap);
if (outcount < 0 || outcount > WCharBufferSize)
return false;
bool status = WriteConsoleW(handle, buffer, outcount, NULL, NULL);
va_end(ap);
return status;
}
bool CDebugConsole::WriteLine(char* info)
{
size_t len = strlen(info);
if (len + 1 > CharBufferSize)
{
return false;
}
memcpy_s(buffer, CharBufferSize, info, len);
char* p = (char*)buffer;
p[len] = '\n';
return WriteConsoleA(handle, buffer, len + 1, NULL, NULL);
}
bool CDebugConsole::WriteLine(wchar_t* info)
{
size_t len = wcslen(info);
if (len + 1 > WCharBufferSize)
{
return false;
}
memcpy_s(buffer, CharBufferSize, info, len * 2);
wchar_t* p = (wchar_t*)buffer;
p[len] = '\n';
return WriteConsoleW(handle, buffer, len + 1, NULL, NULL);
}
bool CDebugConsole::WritePrintfLine(char* format, ...)
{
va_list ap;
va_start(ap, format);
int outcount = _vsnprintf_s((char*)buffer, CharBufferSize, CharBufferSize, format, ap);
if (outcount < 0 || outcount > CharBufferSize - 1)
return false;
char* p = (char*)buffer;
p[outcount] = '\n';
bool status = WriteConsoleA(handle, buffer, outcount + 1, NULL, NULL);
va_end(ap);
return status;
}
bool CDebugConsole::WritePrintfLine(wchar_t* format, ...)
{
va_list ap;
va_start(ap, format);
int outcount = _vsnwprintf_s((wchar_t*)buffer, WCharBufferSize, WCharBufferSize, format, ap);
if (outcount < 0 || outcount > WCharBufferSize - 1)
return false;
wchar_t* p = (wchar_t*)buffer;
p[outcount] = '\n';
bool status = WriteConsoleW(handle, buffer, outcount + 1, NULL, NULL);
va_end(ap);
return status;
}
void CDebugConsole::SetTopMost(bool topmost)
{
if (!console)
return;
SetWindowPos(console, topmost ? HWND_TOPMOST : HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE);
}
??注意,由于里面使用了WinAPI
进行封装,故 只能在Windows
上 使用。使用上述代码时,请保留我的个人信息。
后记
??这个实现的原理并不难,代码也通俗易懂,使用函数接口注释比较详尽。对于C#
版本本人暂时无法写,由于PInvoke
无法调用WriteConsole
函数,故本人无法封装。如果仍想使用,可以只封装我代码中的初始化控制台和关闭控制台以及Write
函数即可,生成Dll
,再PInvoke
,因为C#
的字符串处理和格式化十分的方便,没必要写。希望以上代码对你有帮助。如下是效果: