习惯了单片机上面的C语言,使用printf进行输出调试,上位机虽然可以使用日志进行调试,但是很多时候做通信,需要实时显示16进制HEX编码,很不方便,因此自己写了一个使用richTextBox显示的自定义控件,用于刷新格式化的数据到界面,这个地方千万不用使用textBox,会非常卡的,长时间运行各种不稳定,基本的原理就是自定义一个printf函数,将格式化的字符串写入到FIFO中,使用一个异步线程将char转换为string,再使用异步委托显示到richTextBox中,这样可以高效的显示数据,并且耗用极低的CPU,但是内存占用会增加10-40MB,因为string特别占用内存,效果如下:
显示的调试信息效果
下面看使用方法,需要以下4个文件
1.将自定义控件添加到一个容器中。
DebugPrintfControl ^mDebugPrintfControl; //调试
//调试界面初始化
this->mDebugPrintfControl = gcnew DebugPrintfControl(2*1024*1024);
this->mDebugPrintfControl->Dock = System::Windows::Forms::DockStyle::Fill; //填充
this->panel7->Controls->Add(this->mDebugPrintfControl);
this->mDebugPrintfControl->SetDisplay(false); //默认关闭显示打印
2.使用方法,使用有2种,一种是专用的数据包打印,一种就是通用的printf打印
USER_DEBUG.Printf("程序启动\r\n");
sprintf_s(buff, 8 * 1024, "\r\n->收到数据(%d字节)\r\n", Data->DataLen);
USER_DEBUG.PrintfDataPack(buff, Data->DataBuff, Data->DataLen);
3.代码如下
DebugPrintf.c
#include "StdAfx.h"
#include "DebugPrintf.h"
#include <stdio.h>
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <MyFIFO.h>
#include <stdlib.h>
#include <stdarg.h> //定义成一个可变参数列表的指针
#include "crtdbg.h"
//2017-11-29 修复C++异常处理
//2017-12-23 增加缓冲区为5K,并且增加数据包打印输出
DebugPrintf USER_DEBUG; //调试输出
DebugPrintf::DebugPrintf()
{
this->pDebugFifo = new MyFIFO(128, 5*1024 + 32);
}
DebugPrintf::~DebugPrintf()
{
delete(this->pDebugFifo);
}
//调试信息输出-注意单次长度不要超过5K
DWORD DebugPrintf::Printf(const char * format, ...)
{
char *buff = new char[1024*5 + 32];
DWORD len;
va_list ap;
try
{
va_start(ap, format);
len = vsnprintf(buff, 1024*5+32, format, ap);
va_end(ap);
this->pDebugFifo->FIFO_Write((BYTE *)buff, len);
}
catch (...)
{
len = 0;
}
delete []buff;
return len;
}
//数据包打印输出
DWORD DebugPrintf::PrintfDataPack(char *pInfo, BYTE *pDataPack, WORD DataLen)
{
char *buff = new char[1024 * 5 + 32];
DWORD len = 0;
if (pDataPack == nullptr) return 0;
try
{
if (pInfo != nullptr)
{
len = strlen(pInfo);
if (len > 400)
{
len = 400;
pInfo[400] = 0; //限制长度
}
if (len > 0)
{
len = sprintf_s(buff, 5 * 1024, pInfo); //先添加信息头
}
}
else len = 0;
//循环打印报文
if (DataLen > 1500) DataLen = 1500; //限制长度
for (int i = 0; i < DataLen; i++)
{
len += sprintf_s(&buff[len], 5 * 1024 - len, "%02X ", pDataPack[i]);
}
this->pDebugFifo->FIFO_Write((BYTE *)buff, len); //写入到FIFO中
}
catch (...)
{
len = 0;
}
delete []buff;
return len;
}
DebugPrintf.h
#pragma once
#ifndef _DEBUG_PRINTF_
#define _DEBUG_PRINTF_
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <MyFIFO.h>
class DebugPrintf
{
private:MyFIFO *pDebugFifo;
public:
DebugPrintf();
~DebugPrintf();
DWORD Printf(const char * format, ...); //调试输出
DWORD PrintfDataPack(char *pInfo, BYTE *pDataPack, WORD DataLen);//数据包打印输出
MyFIFO *GetFifo() //获取调试数据FIFO
{
return this->pDebugFifo;
}
};
extern DebugPrintf USER_DEBUG; //调试输出
#endif //_DEBUG_PRINTF_
自定义控件
DebugPrintfControl.c
#include "StdAfx.h"
#include "DebugPrintfControl.h"
#include "DebugPrintf.h"
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include <MyFIFO.h>
#include <userlib.h>
#include <SystemLog.h>
#define CLASS_NAME WindowsFormsApplication_c01::DebugPrintfControl //类名称定义
//异步线程数据打印处理
void CLASS_NAME::BackgroundWorker1_DoWork(void)
{
MyFIFO *pFifo = USER_DEBUG.GetFifo(); //获取调试FIFO缓冲区
BYTE *p;
DWORD len;
DWORD cnt;
DWORD i;
while (1)
{
try
{
cnt = pFifo->FIFO_GetDataNumber(); //获取缓冲区中数据数量
if (cnt > 30)
{
cnt = 30;
}
if (cnt > 0)
{
this->mStringBuilder->Clear(); //清空
for (i = 0; i < cnt; i++) //将数据集中到一起,一次打印
{
if (pFifo->FIFO_ReadNotCopy(&p, &len) == true)
{
try
{
if (this->isShow == true) //开启了显示才显示数据
{
p[len] = 0;
this->mStringBuilder->Append(CharToString(p));
}
}
catch (Exception^ e)
{
SYS_LOG.Write(__FILE__ + __LINE__ + e->Message);
Sleep(10);
}
pFifo->FIFO_ReduceOne();
}
}
this->DebugPrintString(this->mStringBuilder->ToString()); //一次打印
}
Sleep(300); //降低刷新率,尽量一次刷新,保证系统的稳定性
}
catch (Exception^ e)
{
SYS_LOG.Write(__FILE__ + __LINE__ + e->Message);
Sleep(100);
}
}
}
//存储数据为txt
void CLASS_NAME::SaveDataToText(void)
{
if (this->richTextBox1->Text->Length == 0)
{
System::Windows::Forms::MessageBox::Show("数据为空,无法进行存储!", "错误", System::Windows::Forms::MessageBoxButtons::OK,
System::Windows::Forms::MessageBoxIcon::Error);
return;
}
//进行数据存储
//弹出保存对话框
this->saveFileDialog1->Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*"; //支持的扩展名
this->saveFileDialog1->FileName = "调试日志记录文件.txt";
//主设置默认文件extension(可以不设置)
this->saveFileDialog1->DefaultExt = "txt";
System::Windows::Forms::DialogResult result = this->saveFileDialog1->ShowDialog(); //显示保存对话框
if (result == System::Windows::Forms::DialogResult::OK) //点击了保存
{
array<BYTE> ^data;
System::Text::Encoding^ unicode = System::Text::Encoding::UTF8;
try
{
System::IO::FileStream ^fs = System::IO::File::Create(this->saveFileDialog1->FileName->ToString());
String ^str = this->richTextBox1->Text->Replace("\n", "\r\n"); //替换换行符
data = unicode->GetBytes(str);
if (data == nullptr || data->Length == 0)
{
System::Windows::Forms::MessageBox::Show("数据为空,存储失败!", "错误", System::Windows::Forms::MessageBoxButtons::OK,
System::Windows::Forms::MessageBoxIcon::Error);
return;
}
fs->Write(data, 0, data->Length); //写入数据
fs->Close(); //关闭文件
if (System::Windows::Forms::MessageBox::Show("保存数据成功,是否打开文件!", "存储成功", System::Windows::Forms::MessageBoxButtons::YesNo,
System::Windows::Forms::MessageBoxIcon::Question) == System::Windows::Forms::DialogResult::Yes)
{
//打开文件
//检查文件是否存在
if (!System::IO::File::Exists(this->saveFileDialog1->FileName->ToString()))
{
System::Windows::Forms::MessageBox::Show("打开文件失败,文件不存在!", "错误", System::Windows::Forms::MessageBoxButtons::OK,
System::Windows::Forms::MessageBoxIcon::Error);
return;
}
//调用默认程序打开文件
System::Diagnostics::Process::Start(this->saveFileDialog1->FileName->ToString());
}
}
catch (System::IO::IOException^ e)
{
System::Windows::Forms::MessageBox::Show("文件被占用,请关闭文件或修改导出文件名!", "错误", System::Windows::Forms::MessageBoxButtons::OK,
System::Windows::Forms::MessageBoxIcon::Error);
return;
}
}
}
//显示开关设置
void CLASS_NAME::SetDisplay(bool isShow)
{
this->isShow = isShow;
}
DebugPrintfControl.h
#pragma once
#define WIN32_LEAN_AND_MEAN
#include "windows.h"
#include "DebugPrintf.h"
#include "SystemLog.h"
//2017-12-23 增加删除超出部分文本功能,增加显示开关,增加自动滚动设置
//2018-01-10 优化TextBox占用率
using namespace System;
using namespace System::ComponentModel;
using namespace System::Collections;
using namespace System::Windows::Forms;
using namespace System::Data;
using namespace System::Text;
using namespace System::Drawing;
namespace WindowsFormsApplication_c01 {
/// <summary>
/// DebugPrintfControl 摘要
/// </summary>
public ref class DebugPrintfControl : public System::Windows::Forms::UserControl
{
String ^NormalInfo; //正常打印信息
String ^ErrorInfo; //故障信息
private: System::Windows::Forms::ToolStripMenuItem^ 显示开ToolStripMenuItem;
delegate void Delegate_DebugString(String ^pString);//UI托管
Delegate_DebugString ^mDataDebug; //数据包调试
private: System::Windows::Forms::Timer^ timer1;
private: System::Windows::Forms::TextBox^ textBox2;
bool isShow; //是否显示
private: System::Windows::Forms::ToolStripMenuItem^ 自动滚动开ToolStripMenuItem;
bool isLogAutoScroll; //是否自动滚动
StringBuilder ^mStringBuilder;
private: System::Windows::Forms::RichTextBox^ richTextBox1;
DWORD ShowStrSize; //显示的字符串最大长度
public:
DebugPrintfControl(DWORD ShowStrSize)
{
this->ShowStrSize = ShowStrSize;
if (this->ShowStrSize < 200) this->ShowStrSize = 200;
if (this->ShowStrSize > 100 * 1024 * 1024) this->ShowStrSize = 100 * 1024 * 1024;
InitializeComponent();
//
//TODO: 在此处添加构造函数代码
//
this->isShow = true; //默认开启显示
this->isLogAutoScroll = true; //是否自动滚动
this->mStringBuilder = gcnew StringBuilder(1024*1024);
mDataDebug = gcnew Delegate_DebugString(this, &WindowsFormsApplication_c01::DebugPrintfControl::DebugToString); //数据包调试
this->backgroundWorker1->RunWorkerAsync(); //执行异步线程
}
protected:
/// <summary>
/// 清理所有正在使用的资源。
/// </summary>
~DebugPrintfControl()
{
if (components)
{
delete components;
}
}
//任务
void BackgroundWorker1_DoWork(void);
//存储数据为txt
void SaveDataToText(void);
//打印debug信息,打印string
void DebugToString(String ^pStr)
{
int temp;
if (pStr == nullptr) return;
try
{
temp = this->richTextBox1->TextLength + pStr->Length; //计算总长度
if (temp > (this->ShowStrSize - 100))
{
temp -= (this->ShowStrSize - 100);
//删掉最前面的
this->richTextBox1->Select(0, temp); //选择超出部分
this->richTextBox1->SelectedText = ""; //设置超出部分为空,相当于删掉超出部分的数据
}
//使用 AppendText 后会自动滑动到最后面,不受控制
this->richTextBox1->AppendText(pStr); //可以极大的降低CPU占用率
}
catch (Exception^ e)
{
SYS_LOG.Write(__FILE__ + __LINE__ + e->Message);
Sleep(100); //延时5秒,防止不停崩溃消耗电脑资源
}
//限制长度
/*temp = this->textBox1->TextLength + pStr->Length; //计算总长度
if (temp > (1024*1000 - 100))
{
temp -= (1024*1000 - 100);
//删掉最前面的
this->textBox1->Select(0, temp); //选择超出部分
this->textBox1->SelectedText = ""; //设置超出部分为空,相当于删掉超出部分的数据
}
this->textBox1->Text += pStr;
//下面两行代码可以让数据一直显示在最下面
if (this->isLogAutoScroll == true) //自动滚动开启了
{
this->textBox1->SelectionStart = this->textBox1->Text->Length;
this->textBox1->ScrollToCaret();
}*/
}
//调用托管打印数据
void DebugPrintString(String ^pStr)
{
try
{
this->BeginInvoke(mDataDebug, pStr); //打印实时日志
}
catch (Exception^ e)
{
SYS_LOG.Write(__FILE__ + __LINE__ + e->Message);
Sleep(100); //延时5秒,防止不停崩溃消耗电脑资源
}
}
public:
void SetDisplay(bool isShow); //显示开关设置
protected:
private: System::Windows::Forms::ContextMenuStrip^ contextMenuStrip1;
private: System::Windows::Forms::ToolStripMenuItem^ 另存为ToolStripMenuItem;
private: System::Windows::Forms::ToolStripMenuItem^ 清空显示ToolStripMenuItem;
private: System::Windows::Forms::SaveFileDialog^ saveFileDialog1;
private: System::ComponentModel::BackgroundWorker^ backgroundWorker1;
private: System::ComponentModel::IContainer^ components;
private:
/// <summary>
/// 必需的设计器变量。
/// </summary>
#pragma region Windows Form Designer generated code
/// <summary>
/// 设计器支持所需的方法 - 不要
/// 使用代码编辑器修改此方法的内容。
/// </summary>
void InitializeComponent(void)
{
this->components = (gcnew System::ComponentModel::Container());
this->contextMenuStrip1 = (gcnew System::Windows::Forms::ContextMenuStrip(this->components));
this->另存为ToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
this->清空显示ToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
this->显示开ToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
this->自动滚动开ToolStripMenuItem = (gcnew System::Windows::Forms::ToolStripMenuItem());
this->saveFileDialog1 = (gcnew System::Windows::Forms::SaveFileDialog());
this->backgroundWorker1 = (gcnew System::ComponentModel::BackgroundWorker());
this->timer1 = (gcnew System::Windows::Forms::Timer(this->components));
this->textBox2 = (gcnew System::Windows::Forms::TextBox());
this->richTextBox1 = (gcnew System::Windows::Forms::RichTextBox());
this->contextMenuStrip1->SuspendLayout();
this->SuspendLayout();
//
// contextMenuStrip1
//
this->contextMenuStrip1->Items->AddRange(gcnew cli::array< System::Windows::Forms::ToolStripItem^ >(4) {
this->另存为ToolStripMenuItem,
this->清空显示ToolStripMenuItem, this->显示开ToolStripMenuItem, this->自动滚动开ToolStripMenuItem
});
this->contextMenuStrip1->Name = L"contextMenuStrip1";
this->contextMenuStrip1->Size = System::Drawing::Size(149, 92);
this->contextMenuStrip1->Opening += gcnew System::ComponentModel::CancelEventHandler(this, &DebugPrintfControl::contextMenuStrip1_Opening);
//
// 另存为ToolStripMenuItem
//
this->另存为ToolStripMenuItem->Name = L"另存为ToolStripMenuItem";
this->另存为ToolStripMenuItem->Size = System::Drawing::Size(148, 22);
this->另存为ToolStripMenuItem->Text = L"另存为(.txt)";
this->另存为ToolStripMenuItem->Click += gcnew System::EventHandler(this, &DebugPrintfControl::另存为ToolStripMenuItem_Click);
//
// 清空显示ToolStripMenuItem
//
this->清空显示ToolStripMenuItem->Name = L"清空显示ToolStripMenuItem";
this->清空显示ToolStripMenuItem->Size = System::Drawing::Size(148, 22);
this->清空显示ToolStripMenuItem->Text = L"清空显示";
this->清空显示ToolStripMenuItem->Click += gcnew System::EventHandler(this, &DebugPrintfControl::清空显示ToolStripMenuItem_Click);
//
// 显示开ToolStripMenuItem
//
this->显示开ToolStripMenuItem->Name = L"显示开ToolStripMenuItem";
this->显示开ToolStripMenuItem->Size = System::Drawing::Size(148, 22);
this->显示开ToolStripMenuItem->Text = L"显示:开";
this->显示开ToolStripMenuItem->Click += gcnew System::EventHandler(this, &DebugPrintfControl::显示开ToolStripMenuItem_Click);
//
// 自动滚动开ToolStripMenuItem
//
this->自动滚动开ToolStripMenuItem->Name = L"自动滚动开ToolStripMenuItem";
this->自动滚动开ToolStripMenuItem->Size = System::Drawing::Size(148, 22);
this->自动滚动开ToolStripMenuItem->Text = L"自动滚动:开";
this->自动滚动开ToolStripMenuItem->Visible = false;
this->自动滚动开ToolStripMenuItem->Click += gcnew System::EventHandler(this, &DebugPrintfControl::自动滚动开ToolStripMenuItem_Click);
//
// backgroundWorker1
//
this->backgroundWorker1->WorkerReportsProgress = true;
this->backgroundWorker1->WorkerSupportsCancellation = true;
this->backgroundWorker1->DoWork += gcnew System::ComponentModel::DoWorkEventHandler(this, &DebugPrintfControl::backgroundWorker1_DoWork);
this->backgroundWorker1->ProgressChanged += gcnew System::ComponentModel::ProgressChangedEventHandler(this, &DebugPrintfControl::backgroundWorker1_ProgressChanged);
this->backgroundWorker1->RunWorkerCompleted += gcnew System::ComponentModel::RunWorkerCompletedEventHandler(this, &DebugPrintfControl::backgroundWorker1_RunWorkerCompleted);
//
// timer1
//
this->timer1->Enabled = true;
this->timer1->Interval = 1000;
this->timer1->Tick += gcnew System::EventHandler(this, &DebugPrintfControl::timer1_Tick);
//
// textBox2
//
this->textBox2->Anchor = static_cast<System::Windows::Forms::AnchorStyles>((System::Windows::Forms::AnchorStyles::Top | System::Windows::Forms::AnchorStyles::Right));
this->textBox2->BackColor = System::Drawing::SystemColors::ScrollBar;
this->textBox2->BorderStyle = System::Windows::Forms::BorderStyle::None;
this->textBox2->Font = (gcnew System::Drawing::Font(L"微软雅黑", 11.25F, System::Drawing::FontStyle::Regular, System::Drawing::GraphicsUnit::Point,
static_cast<System::Byte>(134)));
this->textBox2->ForeColor = System::Drawing::Color::FromArgb(static_cast<System::Int32>(static_cast<System::Byte>(192)), static_cast<System::Int32>(static_cast<System::Byte>(0)),
static_cast<System::Int32>(static_cast<System::Byte>(0)));
this->textBox2->Location = System::Drawing::Point(787, 8);
this->textBox2->Name = L"textBox2";
this->textBox2->Size = System::Drawing::Size(68, 20);
this->textBox2->TabIndex = 1;
this->textBox2->Text = L"显示关闭";
this->textBox2->TextAlign = System::Windows::Forms::HorizontalAlignment::Center;
this->textBox2->Visible = false;
//
// richTextBox1
//
this->richTextBox1->ContextMenuStrip = this->contextMenuStrip1;
this->richTextBox1->DetectUrls = false;
this->richTextBox1->Dock = System::Windows::Forms::DockStyle::Fill;
this->richTextBox1->HideSelection = false;
this->richTextBox1->Location = System::Drawing::Point(0, 0);
this->richTextBox1->Name = L"richTextBox1";
this->richTextBox1->ReadOnly = true;
this->richTextBox1->ScrollBars = System::Windows::Forms::RichTextBoxScrollBars::ForcedVertical;
this->richTextBox1->Size = System::Drawing::Size(882, 426);
this->richTextBox1->TabIndex = 2;
this->richTextBox1->Text = L"";
//
// DebugPrintfControl
//
this->AutoScaleDimensions = System::Drawing::SizeF(6, 12);
this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font;
this->Controls->Add(this->textBox2);
this->Controls->Add(this->richTextBox1);
this->Name = L"DebugPrintfControl";
this->Size = System::Drawing::Size(882, 426);
this->contextMenuStrip1->ResumeLayout(false);
this->ResumeLayout(false);
this->PerformLayout();
}
#pragma endregion
private: System::Void 清空显示ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
this->richTextBox1->Clear();
}
private: System::Void 另存为ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
this->SaveDataToText();
}
//异步线程
private: System::Void backgroundWorker1_DoWork(System::Object^ sender, System::ComponentModel::DoWorkEventArgs^ e) {
this->BackgroundWorker1_DoWork();
}
//状态改变,刷新UI
private: System::Void backgroundWorker1_ProgressChanged(System::Object^ sender, System::ComponentModel::ProgressChangedEventArgs^ e) {
}
//结束
private: System::Void backgroundWorker1_RunWorkerCompleted(System::Object^ sender, System::ComponentModel::RunWorkerCompletedEventArgs^ e) {
}
//显示开关切换
private: System::Void 显示开ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
if (this->isShow == true) //开启了显示
{
this->isShow = false;
this->显示开ToolStripMenuItem->Text = "显示:关";
}
else
{
this->isShow = true;
this->显示开ToolStripMenuItem->Text = "显示:开";
}
}
//弹出时更新菜单显示开关名称
private: System::Void contextMenuStrip1_Opening(System::Object^ sender, System::ComponentModel::CancelEventArgs^ e) {
if (this->isShow == true) //开启了显示
{
this->显示开ToolStripMenuItem->Text = "显示:开";
}
else
{
this->显示开ToolStripMenuItem->Text = "显示:关";
}
}
//1秒定时器,如果显示关闭后进行提示
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) {
if (isShow == false) //显示关闭了,闪烁提示
{
if (this->textBox2->Visible == false)
{
this->textBox2->Visible = true;
}
else
{
this->textBox2->Visible = false;
}
}
else //影藏提示
{
this->textBox2->Visible = false;
}
}
//自动滚动设置
private: System::Void 自动滚动开ToolStripMenuItem_Click(System::Object^ sender, System::EventArgs^ e) {
if (this->isLogAutoScroll == true) //自动滚动开启了
{
this->isLogAutoScroll = false;
this->自动滚动开ToolStripMenuItem->Text = "自动滚动:关";
}
else
{
this->isLogAutoScroll = true;
this->自动滚动开ToolStripMenuItem->Text = "自动滚动:开";
}
}
};
}