其他语言

位置:IT落伍者 >> 其他语言 >> 浏览文章

Delphi深度探索-CodeSite应用指南


发布日期:2018年03月16日
 
Delphi深度探索-CodeSite应用指南

Delphi虽然为我们提供极其强大的调试功能查找Bug仍然是一项艰巨的工作通常我们写代码和调试代码的所消耗的时间是大致相同的甚至有可能更多为了减少无谓的时间和精力的浪费有时我们还是需要专业调试工具的帮助来提高锁定Bug的效率本文中我们将介绍着名的调试工具CodeSite Pro (它获得了年度Delphi Informant读者选择的最佳调试工具奖的第二名)它的官方网址是

CodeSite的主要功能是可以让开发者使用代码来发送运行时的详细信息到特殊的接收器以便于进一步分析更精确的说通过CodeSite实现的TCodeSite类我们可以打包并发送运行时的信息给CodeSite Dispatcher(CodeSite的消息分发器)它可以路由这些消息到一个或多个接收器来察看缺省的信息接收器是CodeSite Viewer(消息察看器)

CodeSite的效率体现在它不同于简单的显示消息的对话框或设定断点来检查变量它更类似于Delphi自带的事件日志功能(Event Log)当然毫无疑问它要比Event Log强大的多它的消息是可持续的也就是可以保存的便于回溯分析

在介绍CodeSite的具体使用前我们先来看一下它的三个组成部分

CodeSite对象

正如前面提到的从运行的应用程序中向外发送CodeSite消息是通过使用TCodeSite类(定义在CSIntf单元中)的一个实例来完成的我们只要简单的调用TCodeSite类的方法就可以 把消息发送给CodeSite Dispatcher比如可以使用对象的SendMsg方法来发送一个简单的字符串消息TCodeSite 对象实现了大量的方法来支持各种类型的信息发送而无须任何数据转换比如对象的SendObject方法有两个参数一个是消息字符串一个是对对象实例的引用这个方法会获取对象所有published的属性然后把这些属性的信息打包进CodeSite的消息中

CodeSite Dispatcher

大多数情况下CodeSite Dispatcher会安静的运行在系统的托盘区它的唯一功能是路由从各个TCodeSite对象发来的CodeSite的消息到它们的目的地缺省时CodeSite消息都会发给CodeSite Viewer我们甚至不需要启动CodeSite Dispatcher因为它会被TCodeSite等对象自动启动

TCodeSite 类定义了一个DestinationDetails属性它允许开发者设定发送的CodeSite消息是如何被CodeSite Dispatcher路由到不同目的地比如日志文件但通常没有必要修改这个属性

CodeSite Viewer

虽然CodeSite 支持发送消息到不同的目标但决大多数情况下CodeSite Viewer是主要的发送目标即使是发送到其他目标比如日志文件或另外一台机器CodeSite Viewer仍然是察看分析消息的主要工具

CodeSite Viewer由下面四个面板构成消息列表消息察看器调用堆栈和Scratch面板CodeSite Viewer的主要工作区是Message列表它用来显示发送给Viewer的全部消息或是从日志文件中加载的消息

消息察看器用来察看同消息关联的额外信息比如如果当前的消息是由SendObject方法发送的话消息察看器就会显示对象全部的publised属性当前值

调用堆栈面板会根据csmEnterMethod消息显示一个堆栈视图

Scratch面板则是用来显示非可持续的信息的当我们想跟蹤某些信息但又不想在消息日志中记录它们的时候比如当我们想察看象鼠标当前位置这类大量的并重复的消息时Scratch面板是非常有用的这时我们可以可以使用TCodeSite对象的WritePoint方法并指定Line ID参数以便指定用来容纳鼠标信息的scratch面板行数

下面就让我们用一个简单的例子来演示一下如何从程序中发送消息给CodeSite Viewer

)创建一个新的项目然后切换组件面板到CodeSite页面(CodeSite安装后会在系统中安装两个组件TCSGlobalObject和TCSObject)选择TCSGlobalObject组件然后放到窗体上TCSGlobalObject组件提供了设计时对全局TCodeSite对象的交互(全局TCodeSite是在CSInft单元中被初始化的)

)添加一个按钮然后在它的OnClick事件中写下如下代码

//CodeSite就是全局的TCodeSite对象

CodeSiteSendMsg(CodeSite的第一条消息);

)编译并运行这个简单的程序运行后点击按钮CodeSite Dispatcher和CodeSite Viewer将会运行同时在CodeSite Viewer的消息列表中将会看到程序发出的消息(注意我们没有必要在程序运行前启动CodeSite Dispatcher和CodeSite Viewer因为TCodeSite 对象在需要发送消息的时候会自动启动它们的)运行结果如下图所示

)接下来停止程序在OnClick事件处理过程中添加下面代码

CodeSiteSendObject(Form Form

)重新编译运行程序再点按钮一次这回你会在CodeSite Viewer中看到两条消息其中Form对应的消息包括Form的对象信息

)为了看到Form的相关联的对象信息选择CodeSite Viewer的菜单命令View|Inspector会在消息列表右侧显示一个新的面板Form的published属性都被显示在其中如下图所示

)再次停止程序然后修改OnClick过程中代码如下

CodeSiteEnterMethod(ButtonClick);

CodeSiteSendMsg(CodeSite的第一条消息);

CodeSiteSendObject(Form Form );

CodeSiteExitMethod(ButtonClick );

)这次我们再运行程序点击按钮后就会看到CodeSite的第一条消息Form的消息被缩进在ButtonClick消息之间如下图所示

通过添加EnterMethod和ExitMethod方法的调用我们可以生成一个日志来记录方法何时被调用

看过例子之后我们就会发现CodeSite的功能是非常强大的我们只要简单的在程序中添加几条语句就可以生成非常详细的信息并通过CodeSite Viewer以生动的图表表现出来接下来我们再来谈谈CodeSite的高级应用技术

发送消息到日志文件

每个程序或多或少都会有Bug不在这时发生也会在那时发生短时间内不发生很长时间就可能发作有时反复出现有时非常偶然的才能被发现如果一个人告诉你他写的程序在任何时候都没有任何问题他一定是在撒谎正是由于Bug的偶然性和隐蔽性就使得我们往往很难重复用户提交的Bug这就给我们调试程序并找到问题的原因产生了极大的障碍而CodeSite能够发送消息到日志文件的特性就使得用户报告Bug变得更容易他们只要把运行时生成的信息文件提交就可以了相应的我们调试程序的工作也会变得更轻松我们可以使用CodeSite Viewer来直观的分析错误发生的原因和位置

要想改变消息发送的目标我们可以通过设定TCodeSite 对象的DestinationDetails属性来实现这项功能要求客户的机器上必须安装了CodeSite Dispatcher它属于CodeSite中可自由分发的部分下面的要讲具体过程仍然是基于前面讲过的例子

)在窗体的OnCreate事件中添加下面代码

CodeSiteDestinationDetails = File[Path=CFirstLogcsl]

)编译并余兴程序这回我们在点击按钮后消息就不再被发送给CodeSite Viewer而是发送到C盘的FirstLogcsl文件中

)使用CodeSite Viewer加载FirstLogcsl文件这回我们就象先前一样察看被保存的CodeSite消息了

)如果我们想把消息同时发送到CodeSite Viewer和日志文件的话只修改前面的代码为

CodeSiteDestinationDetails = ViewerFile[Path=CFirstLogcsl]

发送用户定制的数据

虽然TCodeSite 类提供了大量的处理不同数据类型的方法但有时我们可能会需要发送某种自定义格式的数据信息为此TCodeSite 类定义了SendCustomData 方法它支持发送任意的数据类型并会根据一个自定义的格式器来格式化数据以便CodeSite Viewer可以正确的显示数据

首先我们需要创建一个TCSFormatter 对象的子类然后重载对象的FormatDataInspectorType和TypeName方法然后调用CodeSite对象管理器对象CSObjectManager的来注册新的TCSFormatter子类此外我们还需要调用RegisterCustomFormat方法来注册一个新的消息类型

下面是一个实际应用的例子单元CSEmployeepas中实现了一个TCSEmployeeRecord记录类型的定制格式器

unit CSEmployee;

interface

uses

Windows Graphics CSIntf;

const

csmEmployeeSummary = csmUser + ;

csmEmployeeDetails = csmUser + ;

首先在Uses部分添加对CSIntf 单元的引用第二步是为每一个格式器定义新的CodeSite消息类型常数上面我们定义了两个常数注意常数应该大于csmUser但不能大过

type

TCSEmployee = record

LastName: string;

FirstName: string;

Address: string;

City: string;

State: string;

ZipCode: string;

PhoneNumber: string;

HireDate: TDateTime;

Salary: Currency;

VacationDays: Integer;

SickDays: Integer;

Manager: Boolean;

end;

上面的记录就是我们要发送的自定义的数据类型

TCSEmployeeSummaryFormatter = class( TCSFormatter )

public

function InspectorType: TCSInspectorType; override;

procedure FormatData( var Data ); override;

function TypeName: string; override;

end;

TCSEmployeeDetailsFormatter = class( TCSFormatter )

public

function InspectorType: TCSInspectorType; override;

procedure FormatData( var Data ); override;

function TypeName: string; override;

end;

上面是两个定制的格式器类的定义第一个格式器将把TCSEmployee 记录格式化为一个文本格式第二个格式化器将把TCSEmployee 记录格式化为网格样式

实现一个自定义的格式化器的第一步是确定哪种类型的内置察看器将被用来察看格式化后的数据这里使用的是字符串列表察看器察看器类型将被FormatData方法所使用

procedure TCSEmployeeSummaryFormatterFormatData( var Data );

var

EmpRec: TCSEmployee;

begin

EmpRec := TCSEmployee( Data );

AddLine( EmpRecFirstName + + EmpRecLastName );

AddLine( EmpRecAddress );

AddLine( EmpRecCity + + EmpRecState + + EmpRecZipCode );

AddLine( );

AddLine( Phone: + EmpRecPhoneNumber );

AddLine( Hire Date: + DateToStr( EmpRecHireDate ) );

AddLine( Salary: + Format( %m [ EmpRecSalary ] ) );

AddLine( );

AddLine( Vacation Days: + IntToStr( EmpRecVacationDays ) );

AddLine( Sick Days: + IntToStr( EmpRecSickDays ) );

if EmpRecManager then

AddLine( Manager: Yes )

else

AddLine( Manager: No );

end;

FormatData 方法是核心部分注意传递给FormatData方法的Data参数是一个无类型的可变参数这就意味着这个参数可以是任何数据类型的通过格式注册过程我们可以确保强制类型映射为自定义的数据记录而不会发生转换错误

转换数据类型后我们就可以对数据进行格式化了这里使用TCSFormatter 基类的 AddLine方法在字符串间添加分割线来进行格式化

function TCSEmployeeSummaryFormatterTypeName: string;

begin

Result := TCSEmployee;

end;

TypeName方法的重载是可任选的但通常我们可以用它来返回显示在消息列表中的字符串

{=========================================}

{== TCSEmployeeDetailsFormatter Methods ==}

{=========================================}

function TCSEmployeeDetailsFormatterInspectorType: TCSInspectorType;

begin

Result := itStockGrid;

end;

对于employeedetails格式器来说命名网格察看器将被用来察看数据信息

procedure TCSEmployeeDetailsFormatterFormatData( var Data );

var

EmpRec: TCSEmployee;

begin

EmpRec := TCSEmployee( Data );

AddNameValuePair( LastName EmpRecLastName );

AddNameValuePair( FirstName EmpRecFirstName );

AddNameValuePair( Address EmpRecAddress );

AddNameValuePair( City EmpRecCity );

AddNameValuePair( State EmpRecState );

AddNameValuePair( ZipCode EmpRecZipCode );

AddNameValuePair( PhoneNumber EmpRecPhoneNumber );

AddNameValuePair( HireDate EmpRecHireDate );

AddNameValuePair( Salary Format( %m [ EmpRecSalary ] ) );

AddNameValuePair( VacationDays EmpRecVacationDays );

AddNameValuePair( SickDays EmpRecSickDays );

AddNameValuePair( Manager EmpRecManager );

end;

这里为了在网格察看器中格式化数据我们使用AddNameValuePair方法来实现

function TCSEmployeeDetailsFormatterTypeName: string;

begin

Result := TCSEmployee;

end;

下面两个过程是用来封装对SendCustomData方法的调用的这里对全局的TCodeSite对象实例CodeSite进行了调用

{=====================}

{== Support Methods ==}

{=====================}

procedure CSSendEmployeeSummary( const Msg: string; EmpRec: TCSEmployee );

begin

CodeSiteSendCustomData( csmEmployeeSummary Msg EmpRec );

end;

procedure CSSendEmployeeDetails( const Msg: string; EmpRec: TCSEmployee );

begin

CodeSiteSendCustomData( csmEmployeeDetails Msg EmpRec );

end;

最后不要忘了调用CSObjectManagerRegisterCustomFormatter方法把格式器注册到CodeSite对象管理器中

initialization

CSObjectManagerRegisterCustomFormatter( csmEmployeeSummary

TCSEmployeeSummaryFormatter );

CSObjectManagerRegisterCustomFormatter( csmEmployeeDetails

TCSEmployeeDetailsFormatter );

end

除了上面谈到的特性外CodeSite还支持远程调试也就是可以把消息路由到一台远程机器上的日志文件或Code Viewer上由于通常时候我们很少会用到这一特性这里就不进行详细的讨论了

               

上一篇:Delphi中把bmp图片转换成jpg图片

下一篇:分布式数据库中的关键问题Delphi