# 15.4 错误报告
有时候你需要在控制台或者一个对话框中显示一条消息以帮助调试或者用来提示那些不能被你的代码正常处理的行为.wxWidgets提供了很多用于记录错误的函数,这些函数工作方式各不相同,你可以使用它们来进行运行情况的报告和记录.比如,当你正在分配一个很大的图片时,可能由于这个图片太大了,系统无法分配足够的资源,系统将使用wxLogError函数显示一个对话框来报告这个错误(如下图所示).又或者说,你想将某个参数的值打印在调试窗口中以便于调试,你可以使用wxLogDebug函数.究竟这些错误信息或者调试信息是显示在终端上,对话框中还是别的什么地方,取决于你所使用的函数,以及当前激活的wxLog目标对象,我们将在稍后的部分描述相关内容.
![](img/mht4639%281%29.tmp)
所有的这种记录函数都拥有类似于printf或vprintf的语法,也就是:第一个参数是格式化文本参数,后面是不定类型的变量或者一组指向变量的指针,如下所示:
```
wxString name(wxT("Calculation"));
int nGoes = 3;
wxLogError(wxT("%s does not compute! You have %d more goes."),
name.c_str(), nGoes);
```
下面我们逐个描述一下这些函数:
wxLogError函数用来显示那些必须显示给用户的错误消息.其默认行为是弹出一个对话框来通知用户相应的错误.那为什么不直接使用 wxMessageBox呢?原因是,首先wxLogError提示的错误消息是可以通过创建一个wxLogNull的Log目标来屏蔽让其不显示出来的,而且这些消息也是排在系统队列中,在系统空闲的时候显示的.因此如果有一系列的错误同时出现,它们将显示在同一个对话框中,而如果使用 wxMessageBox,你的用户可能不得不不停的点击OK按钮.
wxLogFatalError和wxLogError类似,不过除了显示错误消息,它还使用标准的系统调用abort,以错误码3结束整个程序的运行.和其它类似的函数不同,这个函数显示的消息不能通过设置空的打印目标的方法来屏蔽.
wxLogWarning也和wxLogError类似,不过显示的信息将作为警告而不是错误.
wxLogMessage则用来显示所有正常的,信息类型的消息,默认也是显示在对话框中.
wxLogVerbose则用来显示那些冗长的详细信息.通常情况下,这种信息是不显示的,但是如果用户想显示它以便了解程序运行的更详细的情况,可以通过使用wxLog::SetVerbose函数改变这种默认的行为.
wxLogStatus则用来显示状态条消息,如果当前的frame窗口拥有一个状态条,那么这个消息将显示在那里.
wxLogSysError通常主要被wxWidgets自己使用,它用来报告那些系统错误,同时会显示由errno或者GetLastError(依平台的不同)指示的错误码和错误消息.它的另外一种形式允许你的第一个参数的位置显式的指示系统错误码.
wxLogDebug用来显式调试信息.这些信息只在调试版本中(定义了__WXDEBUG__宏)才会出现,在正式版本中将被移除.在 windows平台上,只有当程序在一个调试器中运行或者使用第三方工具比如来自http://www.sysinternals.com的 DebugView工具运行的时候才会显示出来.
wxLogTrace和wxLogDebug的功能几乎完全一样,也是只在调试模式才会显示信息.之所以有这个函数,是为了提供一个和普通的调试模式不同的级别以便区分普通的调试信息和用于跟踪的调试信息.它的另外一种形式允许你指定一个掩码,通过wxLog:: AddTraceMask函数设置了掩码以后,只有掩码符合的跟踪消息才会被显示出来以实现跟踪消息的过滤.比如在wxWidgets内部使用了 mousecapture掩码.如果你设置了这个掩码,在鼠标移动的时候你将看到跟踪信息.
```
void wxWindowBase::CaptureMouse()
{
wxLogTrace(wxT("mousecapture"), wxT("CaptureMouse(%p) "), this);
...
}
void MyApp::OnInit()
{
// Add mousecapture to the list of trace masks
wxLog::AddTraceMask(wxT("mousecapture"));
...
}
```
你可能会疑惑,为什么不直接使用C的标准输入输出函数或者C++的流呢?简短的回答是,它们都是很不错的机制,但是并不一定适用于wxWidgets.wxLog机制主要有以下三个优点:
首先,wxLog是可移植的.常用的printf语句或者C++的cout流和cerr流在unix系统下工作是没有问题的,但是在 windows系统中,对于图形化界面的应用程序,这些函数或流可能不能正常显示需要的内容.因此,你可以使用wxLogMessage作为printf 的一个简单的替代品.
你也可以通过下面的方法将所有的log信息转向标准的cout流:
```
wxLog *logger=new wxLogStream(&cout);
wxLog::SetActiveTarget(logger);
```
另外,将发送往cout的输出重定向到一个wxTextCtrl控件也是可行的,这需要使用到wxStreamToTextRedirector类.
其次:wxLog更灵活.使用wxLog机制的输出可以被分情况重定向或者隐藏,比如只显示错误消息和告警消息,忽略所有正常的信息.而如果使用标准的函数或流,这是不可能的或者说是很难作到的.
最后:wxLog机制也是更完善的机制.通常,当有错误发生的时候,应该给用户显示一些信息.让我们来举一个简单的例子,假如你正在进行写文件操作,这时候发生了磁盘空间不足的情况,这种错误是被wxWidgets内部(wxFile::Write)处理的,因此,调用这个函数只能知道写动作发生了异常,至于是什么类型的异常则很难得到,如果在这种情况下使用wxLogError函数,正确的错误码和相应的错误信息都将显示给用户.
现在我们来描述以下wxWidgets的Log机制是怎样工作的,以便你处理那些默认没有提供的行为.
wxWidgets有一个log目标的概念:它其实就是一个wxLog的派生类.它需要实现wxLog定义的那些虚函数,这些函数将在相应的Log函数被调用的时候使用.任何时候都只有一个log目标是活动的.log目标通常的使用方法就是调用wxLog:: SetActiveTarget来安装这个目标,安装以后的目标将在相应的log函数被调用的时候自动使用.
要创建一个自定义的log目标,你只需要创建一个wxLog的派生类,并实现其虚函数DoLogString和(或)DoLog.如果你对wxWidgets默认的增加时间戳和信息类型的格式化方法感到满意,只是想更改信息的目的地,那么实现DoLogString函数就足够了,而重载 DoLog函数则使得你可以任意的定制输出信息的格式,不过同时你也需要自己区分信息的各种类型.你可以参考src/common/log.cpp文件看看wxWidgets是怎么作到这一点的.
wxWidgets自己实现了几个wxLog的派生类,你也可以读一读它们的代码,这对你创建自定义的log目标也是有好处的.这些预定义的log目标包括:
wxLogStderr将所有的信息输出到FILE*作为参数的文件中,如果FILE*为空,则输出到标准错误输出.
wxLogStream和wxLogStderr功能相同,不过它使用标准C++的ostream类和cerr流来代替FILE*和stderr.
wxLogGui则是wxWidgets所有wxWidgets程序默认使用的log目标,依平台的不同它实现了不同的输出处理.
wxLogWindow则提供了一个类似"跟踪终端"之类的窗口,这个窗口将显示所有的输出信息,同时这些信息也将显示在之前的log目标上.这个跟踪终端窗口提供了清除信息,关闭窗口以及将所有信息保存到文件中的功能.
wxLogNull则被用来临时阻止某些错误信息的输出,比如你打开不存在的文件的时候将显示一个错误信息,有时候你不希望显示这个信息,可以在栈上创建一个wxLogNull变量,在这个变量的作用域范围内,没有任何错误信息将被显示,而离开了其作用域,则所有的信息又可以正常显示了.
```
wxFile file;
// wxFile.Open()在打开一个不存在的文件时通常会显示错误信息,但是在这里我们不想看到这个信息.
{
wxLogNull logNo;
if ( !file.Open("bar") )
... process error ourselves ...
} // wxLogNull的析构函数被调用,旧的log目标被恢复.
wxLogMessage("..."); // 可以被显示
```
有时候你也许希望将信息输出到多个地方,比如,你可以希望所有的信息在正常显示的同时被保存在某个文件中,这时候你可以使用wxLogChain和wxLogPassThrough,如下所示:
```
// 这将隐式的设置当前log目标
wxLogChain *logChain = new wxLogChain(new wxLogStderr);
// 所有的输出将被同时显示在stderr和通常的地方
// 不要直接删除logChain指针,这会导致当前活动log目标为一个不确定的指针.
// 应该使用SetActiveTarget.
delete wxLog::SetActiveTarget(new wxLogGui);
```
wxMessageOutput VS wxLog
有时候,使用wxLog不太合适,这主要是因为wxLog对输出的信息作了过多的处理,并且会等待空闲的时候才会显示这些信息.而 wxMessageOutput和它的派生类则可以作为你的底层printf的替代品,来在GUI和命令行程序中使用.你可以象使用printf函数那样使用wxMessageOutput::Printf函数,比如,如果你想把信息打印在标准错误输入:
```
#include "wx/msgout.h"
wxMessageOutputStderr err;
err.Printf(wxT("Error in app %s.\n"), appName.c_str());
```
wxMessageOutputDebug将信息显示在调试器终端或者是标准错误输出中,这主要看程序是以什么方式运行的,和 wxLogDebug不同,wxMessageOutputDebug输出的信息在正式版本中将不会被移除.GUI应用程序还可以使用 wxMessageOutputMessageBox来即时显示消息,而不比象wxLog那样需要搜集(其它Log信息)和等待(系统空闲),同样的还存在一个wxMessageOutputLog类,它将消息输出到wxLogMessage.
和wxLog类似,wxMessageOutput也有一个当前的目标,这个目标可以通过wxMessageOutput::Set设置,通过wxMessageOutput::Get获取.默认的目标是系统初始化的时候由wxWidgets设置的,在命令行程序中使用的是 wxMessageOutputStderr,在GUI程序中使用的是wxMessageOutputMessageBox.wxWidgets内部经常使用这个对象,比如在wxCmdLineParser类中,使用了下面的代码:
```
wxMessageOutput* msgOut = wxMessageOutput::Get();
if ( msgOut )
{
wxString usage = GetUsageString();
msgOut->Printf( wxT("%s%s"), usage.c_str(), errorMsg.c_str() );
}
else
{
wxFAIL_MSG( _T("no wxMessageOutput object?") );
}
```
- 第一章 介绍
- 1.1 为什么要使用wxWidgets?
- 1.2 wxWidgets的历史
- 1.3 wxWidgets社区
- 1.4 wxWidgets和面向对象编程
- 1.5 wxWidgets的体系结构
- 1.6 许可协议
- 第一章小结
- 第二章 开始使用
- 2.1 一个小例子
- 2.2 应用程序类
- 2.3 Frame窗口类
- 2.4 事件处理函数
- 2.5 Frame窗口的构造函数
- 2.6 完整的例子
- 2.7 wxWidgets程序一般执行过程
- 2.8 编译和运行程序
- 第二章小结
- 第三章 事件处理
- 3.1 事件驱动编程
- 3.2 事件表和事件处理过程
- 3.3 过滤某个事件
- 3.4 挂载事件表
- 3.5 动态事件处理方法
- 3.6 窗口标识符
- 3.7 自定义事件
- 第三章小结
- 第四章 窗口的基础知识
- 4.1 窗口解析
- 4.2 窗口类概览
- 4.3 基础窗口类
- 4.4 顶层窗口
- 4.5 容器窗口
- 4.6 非静态控件
- 4.7 静态控件
- 4.8 菜单
- 4.9 控制条
- 第四章小结
- 第五章绘画和打印
- 5.1 理解设备上下文
- 5.2 绘画工具
- 5.3 设备上下文中的绘画函数
- 5.4 使用打印框架
- 5.5 使用wxGLCanvas绘制三维图形
- 第五章小节
- 第六章处理用户输入
- 6.1 鼠标输入
- 6.2 处理键盘事件
- 6.3 处理游戏手柄事件
- 第六章小结
- 第七章使用布局控件进行窗口布局
- 7.1 窗口布局基础
- 7.2 窗口布局控件
- 7.3 使用布局控件进行编程
- 7.4 更多关于布局的话题
- 第七章小结
- 第八章使用标准对话框
- 8.1信息对话框
- 8.2 文件和目录对话框
- 8.3 选择和选项对话框
- 8.4 输入对话框
- 8.5 打印对话框
- 第八章小结
- 第九章创建定制的对话框
- 9.1 创建定制对话框的步骤
- 9.2 一个例子:PersonalRecordDialog
- 9.3 在小型设备上调整你的对话框
- 9.4 一些更深入的话题
- 9.5 使用wxWidgets资源文件
- 第九章小结
- 第十章使用图像编程
- 10.1 wxWidgets中图片相关的类
- 10.2 使用wxBitmap编程
- 10.3 使用wxIcon编程
- 10.4 使用wxCursor编程
- 10.5 使用wxImage编程
- 10.6 图片列表和图标集
- 10.7 自定义wxWidgets提供的小图片
- 第十章小结
- 第十一章剪贴板和拖放操作
- 11.1 数据对象
- 11.2 使用剪贴板
- 11.3 实现拖放操作
- 第十一章小结
- 第十二章高级窗口控件
- 12.1 wxTreeCtrl
- 12.2 wxListCtrl
- 12.3 wxWizard
- 12.4 wxHtmlWindow
- 12.5 wxGrid
- 12.6 wxTaskBarIcon
- 12.7 编写自定义的控件
- 第十二章小结
- 第十三章数据结构类
- 13.1 为什么没有使用STL?
- 13.2 字符串类型
- 13.3 wxArray
- 13.4 wxList和wxNode
- 13.5 wxHashMap
- 13.6 存储和使用日期和时间
- 13.7 其它常用的数据类型
- 第十三章小结
- 第十四章文件和流操作
- 14.1 文件类和函数
- 14.2 流操作相关类
- 第十四章小结
- 第十五章内存管理,调试和错误处理
- 15.1 内存管理基础
- 15.2 检测内存泄漏和其它错误
- 15.3 构建自防御的程序
- 15.4 错误报告
- 15.5 提供运行期类型信息
- 15.6 使用wxModule
- 15.7 加载动态链接库
- 15.8 异常处理
- 15.9 调试提示
- 第十五章小结
- 第十六章编写国际化程序
- 16.1 国际化介绍
- 16.2 从翻译说起
- 16.3 字符编码和Unicode
- 16.4 数字和日期
- 16.5 其它媒介
- 16.6 一个小例子
- 第十六章小结
- 第十七章编写多线程程序
- 17.1 什么时候使用多线程,什么时候不要使用
- 17.2 使用wxThread
- 17.3 用于线程同步的对象
- 17.4 多线程的替代方案
- 第十七章小结
- 第十八章使用wxSocket编程
- 18.1 Socket类和功能概览
- 18.2 Socket及其基本处理介绍
- 18.3 Socket标记
- 18.4 使用Socket流
- 18.5 替代wxSocket
- 第十八章小结
- 第十九章使用文档/视图框架
- 19.1 文档/视图基础
- 19.2 文档/视图框架的其它能力
- 19.3 实现Undo/Redo的策略
- 第十九章小结
- 第二十章完善你的应用程序
- 20.1 单个实例和多个实例
- 20.2 更改事件处理机制
- 20.3 降低闪烁
- 20.4 实现联机帮助
- 20.5 解析命令行参数
- 20.6 存储应用程序资源
- 20.7 调用别的应用程序
- 20.8 管理应用程序设置
- 20.9 应用程序安装
- 20.10 遵循用户界面设计规范
- 20.11 全书小结