65.9K
CodeProject 正在变化。 阅读更多。
Home

创建基于控制台的调试窗口

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.70/5 (13投票s)

2019年4月21日

CPOL

3分钟阅读

viewsIcon

11468

downloadIcon

338

本文将展示如何有选择地为Win32甚至MFC程序添加控制台窗口,用于调试目的。

构建块

一个合适的日志记录函数应该具有灵活性,能够接受任意数量的各种类型的参数,例如日期、数字和字符串。首先,我将介绍可以在每个日志条目中包含的信息类型。

调用函数

可以始终知道调用每个日志条目的函数的名称。为此,我们在 WriteLogFile() 函数中添加一个参数,并通过一个 MACRO 调用它,该 MACRO 自动添加以下 预处理器指令 作为第一个参数:__FUNCTION__ (参见 这篇文章)。

一般信息

我们通常希望日志条目包含一般信息,例如

  • 当前日期和时间
  • 应用程序启动的日期和时间
  • (可选)应用程序创建的日期和时间

计算我们的周期

这不是每个应用程序都需要的东西,但有些应用程序确实需要。如果您的应用程序有一个主事件循环,您通常会从一些初始化开始,然后启动此循环,直到应用程序终止。您可能想计算此循环发生了多少次。

设置文本和背景颜色

以下函数可用于设置文本的文本和背景颜色。该函数应由一个基于设置的条件包装器调用,以使其更易于在运行时查看日志。例如,区分信息日志条目和错误/警告,并为不同类型的紧急情况设置不同的颜色。

inline void setcolor(int textcol, int backcol)
{
    if ((textcol % 16) == (backcol % 16))textcol++;
    textcol %= 16; backcol %= 16;
    unsigned short wAttributes = ((unsigned)backcol << 4) | (unsigned)textcol;
    HANDLE hStdOut = GetStdHandle(STD_OUTPUT_HANDLE);
    SetConsoleTextAttribute(hStdOut, wAttributes);
}

定义日志级别

使用以下代码,我们定义了不同的日志级别,允许稍后根据选定的“日志级别”显示日志条目。

// Logging Level
    typedef enum
    {
        minimal = 1,           // messages that must be displayed 
        normal = 2,            // messages that would normally be displayed
        verbose = 3            // messages that don't necessarily need to be displayed
    } LoggingLevel;

然后,我们可以使用 MACRO 选择所需的日志级别作为 WriteLogFile() 的参数。

#define LOG_VERBOSE utils::LoggingLevel::verbose
#define LOG_NORMAL utils::LoggingLevel::normal
#define LOG_MINIMAL utils::LoggingLevel::minimal

但是,我们需要定义两组不同的日志级别

日志消息级别:假设一个函数有一个调用 WriteLogFile,我们认为在大多数情况下不应该显示。然后,我们希望将此特定消息定义为“冗余”。

应用程序级别:现在,我们希望动态地定义我们现在处于“冗余”、“调试”还是“最小”模式。我们使用这些 MACRO 来实现。

#define LOGGINGTYPE_NONE 0
#define LOGGINGTYPE_SIMPLE 1
#define LOGGINGTYPE_DEBUG 2

如果我们处于“SIMPLE”模式,我们将仅显示被分类为“最小”的日志条目。如果我们处于“DEBUG”模式,我们还将包括被分类为“冗余”的条目,等等...

显示我们程序的创建日期

我们将获取我们程序的创建日期,但首先,我们定义一个“友好”的正式格式来显示它

#define FRIENDLY_DATEFORMAT L"%d-%m-%Y, %H:%M:%S"

这意味着:日-月-年 时:分:秒。例如

04-04-2019 10:00:00

我们使用以下代码获取我们程序的创建日期

    CTime GetFileDateTime(LPCWSTR FileName)
    {
        FILETIME ftCreate, ftAccess, ftWrite;
        HANDLE hFile;
        CTime result = NULL;
        CTime FileTime;

        hFile = CreateFile(FileName, GENERIC_READ, 
                           FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
        if (hFile == INVALID_HANDLE_VALUE)
        {
            return result;
        }

        // Retrieve the file times for the file.
        if (!GetFileTime(hFile, &ftCreate, &ftAccess, &ftWrite))
            return result;
        FileTime = ftWrite;

        CloseHandle(hFile);
        result = (CTime)FileTime;
        return result;
    }

    // GetSelfDateTime- returns the creation date and time of the running program
    CTime GetSelfDateTime(void)
    {
        WCHAR szExeFileName[MAX_PATH];
        GetModuleFileName(NULL, szExeFileName, MAX_PATH);
        return GetFileDateTime(szExeFileName);
    }

正如您所看到的,GetFileDateTime() 有 2 种变体。当它被调用时没有参数时,将找出当前运行程序的完整路径,然后使用它来调用另一个变体,并将其传递给函数。返回值是一个 CTime

显示处理器类型

我们可能还想显示处理器类型,很可能是 32 位 (x86) 或 64 位 (x64)。我们使用以下函数来实现

bool Isx64OS()
{
    SYSTEM_INFO systemInfo;
    GetNativeSystemInfo(&systemInfo);
    return (systemInfo.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64);
}.

Using the Code

现在是时候看看整个函数以及我们如何调用它了。

我们使用 WriteLogFile(),如下面的代码片段所示。

注意:当设置了 _LOG 预处理器指令时,我们使用日志记录,否则我们不使用,并且此代码的组织方式确保了未打开 _LOG 的版本不会在二进制可执行文件中包含所有日志消息,因为有时我们不希望发布这些消息,而只是在内部使用它们。

int myIntVar = 999;
wchar_t *myStringVar = L"Test";
#if _LOG
     WriteLogFile(__FUNCTION__, LOG_COLOR_WHITE, LOG_VERBOSE, 
     L"myStringVar = %s myIntVar = %d", myStringVar, myIntVar);
#endif

结果

正如您所看到的,当运行代码时,您应该会看到控制台窗口,如以下屏幕截图所示

历史

  • 2019年4月21日:初始版本
© . All rights reserved.