您的位置:首页 > 编程语言 > C语言/C++

VC++ 用于调试的printf显示控件

2018-01-11 13:43 381 查看
习惯了单片机上面的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 = "自动滚动:开";
}
}
};
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: