初级代码游戏的专栏介绍与文章目录-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.dllvoid 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最简单啦)。
(这里是结束)