初级代码游戏的专栏介绍与文章目录-CSDN博客
我的github:codetoys,所有代码都将会位于ctfc库中。已经放入库中我会指出在库中的位置。
这些代码大部分以Linux为目标但部分代码是纯C++的,可以在任何平台上使用。
如何设计日志类请看:C++类设计:设计一个日志类(源码)_初级代码游戏的博客-CSDN博客
这个版本有些不同,支持输出到编辑框、文件和VS的输出窗口,基本结构完全相同。
目录
一、效果
二、完整源码
三、详解
3.1 输出到VS输出窗口
3.2 输出到编辑框
3.3 输出到文件
一、效果
VS2017,MFC,UNICODE字符集。
输出到编辑框:
自动向下滚动,编辑框必须是多行的。
输出到VS输出窗口(调试模式):
二、完整源码
头文件:
//common.h
#pragma once
#include <sstream>
#include <map>
using namespace std;
extern CEdit* pGlobalLogWnd;
class CStringConvert
{
public:
static string WStringToString(wstring const& wstr)
{
size_t buflen = wstr.size() * sizeof(wchar_t) + 1;
char* buf = new char[buflen];
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, buf, (int)buflen, NULL, 0);
string ret = buf;
delete[] buf;
return ret;
}
static wstring StringToWString(string const& str)
{
//返回接受字符串所需缓冲区的大小,已经包含字符结尾符'\0'
int iSize = MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, NULL, 0);
wchar_t* buf = new wchar_t[iSize * sizeof(wchar_t)];
MultiByteToWideChar(CP_ACP, 0, str.c_str(), -1, buf, iSize);
wstring ret = buf;
delete[] buf;
return ret;
}
};
class CLog
{
private:
HANDLE hFile = 0;//输出文件
stringstream m_buf;//一条信息
CString m_file;//产生日志的文件名
int m_line;//产生日志的行号
int m_limit_text = UINT_MAX;//最大长度,编辑框的默认值大概是几十K,需要设置得更大
void end(char const* logtype)
{
CString str;
//格式化信息
str += logtype;
str += ":";
str += m_buf.str().c_str();
if (m_file.GetLength() > 0)
{
CString line;
line.Format(TEXT("%d"), m_line);
str += TEXT(" [From ") + m_file + TEXT(":") + line + TEXT("]");
}
str += "\r\n";
//输出到VS信息窗口
OutputDebugString(str);
//输出到文件
if (hFile)
{
DWORD dwCount;
string mbcs_string = CStringConvert::WStringToString(str.GetString());
WriteFile(hFile, mbcs_string.c_str(), (DWORD)mbcs_string.size(), &dwCount, 0);
}
//输出到编辑框
if (pGlobalLogWnd && ::IsWindow(pGlobalLogWnd->m_hWnd))
{
pGlobalLogWnd->SetLimitText(m_limit_text);
while (pGlobalLogWnd->GetWindowTextLength() >= m_limit_text)
{
//pGlobalLogWnd->SetSel(0, mag_size.begin()->second + 2);
//pGlobalLogWnd->ReplaceSel(TEXT(""));
//mag_size.erase(mag_size.begin());
pGlobalLogWnd->SetWindowText(TEXT(""));
}
int nLength = pGlobalLogWnd->GetWindowTextLength();
pGlobalLogWnd->SetSel(nLength, nLength);
pGlobalLogWnd->ReplaceSel(str);
pGlobalLogWnd->LineScroll(pGlobalLogWnd->GetLineCount());
}
else
{
OutputDebugString(TEXT("日志窗口失效\r\n"));
}
//清除缓存的信息
m_buf.str("");
m_file = "";
m_line = 0;
}
public:
CLog()
{
wchar_t pszPathName[2048];
GetModuleFileName(NULL, pszPathName, 2048);
StrCat(pszPathName, TEXT(".log"));
hFile = CreateFile(pszPathName, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, NULL
, OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
if (INVALID_HANDLE_VALUE == hFile)
{
MessageBox(NULL,TEXT("创建或打开日志文件失败"),TEXT("出错"),0);
}
}
~CLog()
{
if (hFile)CloseHandle(hFile);
}
void set_LimitText(int n) { m_limit_text = n; }
struct CLog_ende
{};
struct CLog_endi
{};
struct CLog_endd
{};
CLog& SetPos(char const * file, int line)
{
m_file = file;
m_line = line;
return *this;
}
template<typename T>
CLog& operator<<(T const& data)
{
m_buf << data;
return *this;
}
CLog& operator<<(CLog_ende const& data)
{
end("错误");
return *this;
}
CLog& operator<<(CLog_endi const& data)
{
end("信息");
return *this;
}
CLog& operator<<(CLog_endd const& data)
{
end("调试");
return *this;
}
CLog& operator<<(CString const& data)
{
return operator<<(data.GetString());
}
CLog& operator<<(wchar_t const* data)
{
return operator<<(CStringConvert::WStringToString(wstring(data)));
}
CLog& operator<<(RECT const& data)
{
return *this << "{" << data.left << "," << data.top << "," << data.right << "," << data.bottom << "}";
}
};
extern bool G_IS_DEBUG;
extern CLog gloablLog;
#define thelog (gloablLog.SetPos( __FILE__ , __LINE__))
#define debuglog if(G_IS_DEBUG)gloablLog.SetPos( __FILE__ , __LINE__)
extern CLog::CLog_ende ende;
extern CLog::CLog_endi endi;
extern CLog::CLog_endd endd;
源文件:
#include "pch.h"
#include "common.h"
CEdit* pGlobalLogWnd = nullptr;
bool G_IS_DEBUG = false;
CLog gloablLog;
CLog::CLog_ende ende;
CLog::CLog_endi endi;
CLog::CLog_endd endd;
使用:
//设置编辑框最大长度
gloablLog.set_LimitText(1024 * 1024);
//设置编辑框指针,不设置则不输出到编辑框
pGlobalLogWnd = &this->m_LogWnd;
//打开调试开关,否则debuglog不输出
G_IS_DEBUG = true;
//输入出日志:
thelog<<a<<b<<c<<endi;
thelog<<a<<b<<c<<ende;
thelog<<a<<b<<c<<endd;
debuglog<<a<<b<<c<<endi;
debuglog<<a<<b<<c<<ende;
debuglog<<a<<b<<c<<endd;
三、详解
3.1 输出到VS输出窗口
在调试模式下可以直接输出到VS的输出窗口,使用如下函数即可:
debugapi.h
Kernel32.lib/Kernel32.dll
void OutputDebugStringA(
[in, optional] LPCSTR lpOutputString
);
不过如果不是调试模式就没用了。
3.2 输出到编辑框
图形界面程序完全可以自带日志窗口,这里用了一个编辑框CEdit。用作日志的编辑框需要做一些设置:
- 多行
- 滚动条
- 最大字符数大一些
- 自动滚动
前两个在窗体设计器设置即可,后两个靠代码:
//输出到编辑框
if (pGlobalLogWnd && ::IsWindow(pGlobalLogWnd->m_hWnd))
{
pGlobalLogWnd->SetLimitText(m_limit_text);
while (pGlobalLogWnd->GetWindowTextLength() >= m_limit_text)
{
//pGlobalLogWnd->SetSel(0, mag_size.begin()->second + 2);
//pGlobalLogWnd->ReplaceSel(TEXT(""));
//mag_size.erase(mag_size.begin());
pGlobalLogWnd->SetWindowText(TEXT(""));
}
int nLength = pGlobalLogWnd->GetWindowTextLength();
pGlobalLogWnd->SetSel(nLength, nLength);
pGlobalLogWnd->ReplaceSel(str);
pGlobalLogWnd->LineScroll(pGlobalLogWnd->GetLineCount());
}
编辑框的修改要用ReplaceSel,不能用SetWindowText,会严重闪烁。
3.3 输出到文件
输出到文件没什么好解释了,要注意的就是输出文件的编码,尽量用utf-8,兼容性比较好。如果输出宽字符,用记事本打开可能乱码(或许是没有添加BOM的原因,总之utf-8最简单啦)。
(这里是结束)