Delphi in-depth exploration-CodeSite application guide
Although Delphi provides us with extremely powerful debugging functions, finding bugs is still an arduous task. Usually the time we spend writing code and debugging code is roughly the same, and it may even be more. In order to reduce unnecessary waste of time and energy, sometimes we still need the help of professional debugging tools to improve the efficiency of locking bugs. In this article we will introduce the famous debugging tool CodeSite PRo 2.0 (it won the second place in the 2000 Delphi Informant Readers' Choice Award for Best Debugging Tool). Its official website is www.raize.com. The main function of CodeSite is to allow developers to use code to send runtime details to special receivers for further analysis. More precisely through the TCodeSite class implemented by CodeSite, we can package and send runtime information to the CodeSite Dispatcher (CodeSite's message distributor), which can route these messages to one or more receivers for viewing. The default message receiver is CodeSite Viewer. The efficiency of CodeSite is reflected in the fact that it is different from a simple dialog box that displays messages or setting breakpoints to check variables. It is more similar to the event log function (Event Log) that comes with Delphi. Of course, it is undoubtedly more powerful than Event Log. There are many, and its messages are sustainable, that is, they can be saved, which facilitates retrospective analysis. Before introducing the specific use of CodeSite, let's first take a look at its three components. CodeSite Object As mentioned earlier, sending CodeSite messages out from a running application is accomplished by using an instance of the TCodeSite class (defined in the CSIntf unit). We can simply call the method of the TCodeSite class. The message is sent to the CodeSite Dispatcher. For example, you can use the object's SendMsg method to send a simple string message. The TCodeSite object implements a large number of methods to support various types of information sending without any data conversion. For example, the SendObject method of the object has two parameters: one is the message string, and the other is a reference to the object instance. This method will obtain the object. All published attributes, and then package the information of these attributes into CodeSite messages. CodeSite Dispatcher In most cases, CodeSite Dispatcher will run quietly in the system tray area. Its only function is to route CodeSite messages sent from various TCodeSite objects to their destinations. By default, CodeSite messages are sent to CodeSite Viewer. We don't even need to start the CodeSite Dispatcher because it will be started automatically by objects such as TCodeSite. The TCodeSite class defines a DestinationDetails property that allows developers to configure how sent CodeSite messages are routed by the CodeSite Dispatcher to different destinations, such as log files. But there is usually no need to modify this property. CodeSite Viewer Although CodeSite supports sending messages to different targets, in most cases CodeSite Viewer is the primary sending target. Even when sent to other destinations, such as log files or another machine, CodeSite Viewer is still the primary tool for viewing and analyzing messages. CodeSite Viewer consists of the following four panels: message list, message viewer, call stack and Scratch panel. The main workspace of CodeSite Viewer is the Message list, which is used to display all messages sent to the Viewer or messages loaded from log files. Message viewers are used to view additional information associated with messages. For example, if the current message is sent by the SendObject method, the message viewer will display the current values of all published properties of the object. The call stack panel displays a stack view based on csmEnterMethod messages. Scratch panels are used to display non-sustainable information. The Scratch panel is very useful when we want to track certain information but do not want to record it in the message log, such as when we want to view a large number of repeated messages such as the current position of the mouse. At this time we can use the WritePoint method of the TCodeSite object and specify the Line ID parameter to specify the number of scratch panel lines used to accommodate mouse information. Let us use a simple example to demonstrate how to send a message to CodeSite Viewer from the program: (1) Create a new project, and then switch the component panel to the CodeSite page (CodeSite will install two components in the system after installation TCSGlobalObject and TCSObject). Select the TCSGlobalObject component and place it on the form. The TCSGlobalObject component provides design-time interaction with the global TCodeSite object (the global TCodeSite is initialized in the CSInft unit). (2) Add a button, and then write the following code in its OnClick event: //CodeSite is the global TCodeSite object CodeSite.SendMsg('CodeSite's first message'); (3) Compile and run this simple program. Click the button after running, and CodeSite Dispatcher and CodeSite Viewer will run. At the same time, you will see the messages sent by the program in the message list of CodeSite Viewer (note: we do not need to start CodeSite Dispatcher and CodeSite Viewer before running the program, because the TCodeSite object will automatically start them when it needs to send messages). The running results are shown in Figure 4.38 below:
(4) Next, stop the program and add the following code in the OnClick event handling process: CodeSite.SendObject('Form1', Form1); (5) Recompile and run the program, click the button again, this time you will be in the CodeSite Viewer I saw two messages. The message corresponding to Form1 includes the object information of Form1. (6) In order to see the associated object information of Form1, select the CodeSite Viewer menu command View|Inspector to display a new panel on the right side of the message list, in which the published attributes of Form1 are displayed, as shown in Figure 4.39 below. :
(7) Stop the program again, and then modify the code in the OnClick process as follows: CodeSite.EnterMethod('Button1Click'); CodeSite.SendMsg('CodeSite's first message'); CodeSite.SendObject('Form1', Form1 ); CodeSite .ExitMethod('Button1Click' ); (8) This time when we run the program and click the button, we will see that the "CodeSite's first message" and "Form1" messages are indented between the "Button1Click" messages, as shown in Figure 4.40 below:
By adding calls to the EnterMethod and ExitMethod methods, we can generate a log to record when the methods are called. After looking at the examples, we will find that the function of CodeSite is very powerful. We can generate very detailed information by simply adding a few statements to the program and display it in vivid charts through the CodeSite Viewer. Next, let’s talk about CodeSite’s advanced application technology. Sending messages to log files Every program will have more or less bugs. If it doesn't happen at this time, it will happen at that time. If it doesn't happen in a short time, it may happen after a long time. Sometimes it happens repeatedly, and sometimes it happens very accidentally. was discovered. If a person tells you that the programs he writes run without any problems at any time, he is lying. It is precisely because of the accidental and hidden nature of bugs that it is often difficult for us to repeat bugs submitted by users, which creates a huge obstacle for us to debug the program and find the cause of the problem, and CodeSite can send messages to log files. This makes it easier for users to report bugs, as they only need to submit the information file generated during runtime. Correspondingly, our work of debugging the program will become easier. We can use CodeSite Viewer to intuitively analyze the cause and location of the error. To change the destination of message sending, we can do this by setting the DestinationDetails property of the TCodeSite object. This feature requires CodeSite Dispatcher to be installed on the customer's machine, which is a freely distributable part of CodeSite. The specific process below is still based on the example mentioned before: (1) Add the following code to the OnCreate event of the form: CodeSite.DestinationDetails := 'File[Path=C:/FirstLog.csl]'; (2 ) compile and execute the program. This time after we click the button, the message is no longer sent to the CodeSite Viewer but to the FirstLog.csl file on the C drive. (3) Use CodeSite Viewer to load the FirstLog.csl file. This time we view the saved CodeSite messages as before. (4) If we want to send messages to CodeSite Viewer and log files at the same time, just modify the previous code to: CodeSite.DestinationDetails := 'Viewer,File[Path=C:/FirstLog.csl]'; Send user-customized Data Although the TCodeSite class provides a large number of methods for processing different data types, sometimes we may need to send data information in a certain custom format. To this end, the TCodeSite class defines the SendCustomData method, which supports sending any data type and formats the data according to a custom formatter so that the CodeSite Viewer can display the data correctly. First we need to create a subclass of the TCSFormatter object, and then overload the object's FormatData, InspectorType and TypeName methods. Then call the CodeSite object manager object CSObjectManager to register the new TCSFormatter subclass. In addition, we also need to call the RegisterCustomFormat method to register a new message type. The following is an example of practical application. A customized formatter of the TCSEmployeeRecord record type is implemented in unit CSEmployee.pas: unit CSEmployee; interface uses Windows, Graphics, CSIntf; const csmEmployeeSummary = csmUser + 1; csmEmployeeDetails = csmUser + 2; First, in Uses section adds a reference to the CSIntf unit. The second step is to define new CodeSite message type constants for each formatter. Above we defined two constants. Note that the constants should be larger than csmUser, but not larger than 32,000. 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; The above record is the custom data type we want to send. TCSEmployeeSummaryFormatter = class( TCSFormatter ) public function InspectorType: TCSInspectorType; override; procedure FormatData( var Data ); override; function TypeName: string; override; end; Data ); override; function TypeName: string; override; end; Above are the definitions of two custom formatter classes. The first formatter will format the TCSEmployee record into a text format, and the second formatter will format the TCSEmployee record into a grid style. implementation uses SysUtils; {==========================================} {== TCSEmployeeSummaryFormatter Methods ==} {==========================================} function TCSEmployeeSummaryFormatter .InspectorType: TCSInspectorType; begin Result := itStockStringList; end; The first step in implementing a custom formatter is to determine which type of built-in viewer will be used to view the formatted data. In this case, a string list viewer is used. The viewer type will be used by the FormatData method. procedure TCSEmployeeSummaryFormatter.FormatData( var Data ); var EmpRec: TCSEmployee; begin EmpRec := TCSEmployee( Data ); AddLine( EmpRec.FirstName + ' ' + EmpRec.LastName ); AddLine( EmpRec.Address ); AddLine( EmpRec.City + ', ' + EmpRec.State + ' ' + EmpRec.ZipCode ); AddLine( '' ); AddLine( 'Phone: ' + EmpRec.PhoneNumber ); AddLine( 'Hire Date: ' + DateToStr( EmpRec.HireDate ) ); AddLine( 'Salary: ' + Format( '% m', [ EmpRec.Salary ] ) ); AddLine( '' ); AddLine( 'Vacation Days: ' + IntToStr( EmpRec.VacationDays ) ); AddLine( 'Sick Days: ' + IntToStr( EmpRec.SickDays ) ); if EmpRec.Manager then AddLine( 'Manager: Yes' ) else AddLine( 'Manager: No' ); end ; The FormatData method is the core part. Note that the Data parameter passed to the FormatData method is an untyped variable parameter. This means that this parameter can be of any data type. Through the format registration process, we can ensure that the forced type is mapped to a custom data record without conversion errors. After converting the data type, we can format the data. Here we use the AddLine method of the TCSFormatter base class to add dividing lines between strings for formatting. function TCSEmployeeSummaryFormatter.TypeName: string; begin Result := 'TCSEmployee'; end; The overload of the TypeName method is optional, but usually we can use it to return the string that appears in the message list. {=========================================} {== TCSEmployeeDetailsFormatter Methods == } {=========================================} function TCSEmployeeDetailsFormatter.InspectorType: TCSInspectorType ; begin Result := itStockGrid; end; For the employeesdetails formatter, the named grid viewer will be used to view the data: procedure TCSEmployeeDetailsFormatter.FormatData( var Data ); var EmpRec: TCSEmployee; begin EmpRec := TCSEmployee( Data ); AddNameValuePair( 'LastName', EmpRec .LastName ); AddNameValuePair( 'FirstName', EmpRec.FirstName ); AddNameValuePair( 'Address', EmpRec.Address ); AddNameValuePair( 'City', EmpRec.City ); AddNameValuePair( 'State', EmpRec.State ); AddNameValuePair( 'ZipCode', EmpRec.ZipCode ); AddNameValuePair( 'PhoneNumber', EmpRec.PhoneNumber ); AddNameValuePair( 'HireDate', EmpRec.HireDate ); AddNameValuePair( 'Salary', Format( '%m', [ EmpRec.Salary ] ) ); AddNameValuePair( 'VacationDays', EmpRec.VacationDays ); AddNameValuePair( 'SickDays', EmpRec.SickDays ); AddNameValuePair( 'Manager', EmpRec.Manager ); end; Here in order to format the data in the grid viewer, we use the AddNameValuePair method. function TCSEmployeeDetailsFormatter.TypeName: string; begin Result := 'TCSEmployee'; end; The following two procedures are used to encapsulate calls to the SendCustomData method. Here, the global TCodeSite object instance CodeSite is called: {===== ===============} {== Support Methods ==} {======================} procedure CSSendEmployeeSummary ( const Msg: string; EmpRec: TCSEmployee ); begin CodeSite.SendCustomData( csmEmployeeSummary, Msg, EmpRec ); end; procedure CSSendEmployeeDetails( const Msg: string; EmpRec: TCSEmployee ); begin CodeSite.SendCustomData( csmEmployeeDetails, Msg, EmpRec ); end; Finally, don't forget to call the CSObjectManager.RegisterCustomFormatter method to register the formatter to the CodeSite Object Manager. initialization CSObjectManager.RegisterCustomFormatter( csmEmployeeSummary, TCSEmployeeSummaryFormatter ); CSObjectManager.RegisterCustomFormatter( csmEmployeeDetails, TCSEmployeeDetailsFormatter ); end.