Pascal Newsletter #15
The full source code examples of this issue are available for download.
![]() |
![]() |
Pascal Newsletter #15 - 31-JAN-2001 INDEX 1. A FEW WORDS FROM THE EDITOR 2. JFCONTROLS LIBRARY - DOWNLOAD - INSTALLATION - RUNNING THE DEMO - MY FIRST EXAMPLE - CENTRALIZED ADMINISTRATION OF RESOURCES - FEATURES OF JFCONTROLS LIBRARY - COST AND PAYMENT METHODS 3. ERROR HANDLING IN DELPHI 5 (II) - DEFAULT EXCEPTION HANDLER - API ERRORS - API ERRORS AS EXCPETIONS - CUSTOM EXCEPTIONS - OTHER ERRORS 4. WINDOWSE - ADVANCED WINDOWS ANALYSER! - GREATIS SOFTWARE - WINDOWSE - LICENSE 5. A DBGRID WITH A CHECKBOX 6. BORLAND HAS JUST ANNOUNCED AVAILABILITY OF KYLIX ________________________________________________________________________ 1. A FEW WORDS FROM THE EDITOR In this issue I'm glad to present JfControls Library, a components package that will help you build applications with a very professional look and functionality. --------------------------------------------- I M P O R T A N T A N N O U N C E M E N T --------------------------------------------- Before writing this article, I contacted the makers of this library and convinced them to do a drawing ("raffle") with one license of their library (including three months of free updates), and as a special courtesy, the subscribers of this newsletter that participate in the drawing will be given two numbers and can get more numbers by referring other participants. To participate, or get more information: http://www.latiumsoftware.com/jfcontrols/index.php?lang=en Regards, Ernesto De Spirito eds2004 @ latiumsoftware.com ________________________________________________________________________ JfControls Library. Multi-language. Multi-appearance. Skins. Privileges. More than 40 integrated and customizable components. Impressive GUI. Centralized resources administration. Multiple programming problems solved. For Delphi 3-7 and C++ Builder 3-6. http://www.jfactivesoft.com/ ________________________________________________________________________ 2. JFCONTROLS LIBRARY Looking for Delphi links to publish in the past issue I came across JfActiveSoft: http://www.jfactivesoft.com/ My first thought was "just another components package", but I started reading the features and they looked interesting, so I decided to download it and give it a try... Well, I can anticipate you that it's not just another components package! It really impressed me so much that I decided to write about these controls, but I can't tell you in words what they look like. You'll have to see them by yourself... DOWNLOAD ======== http://www.jfactivesoft.com/endownload.htm Download the following files: * General program for installation (JfSetup) * JfControls library Trial (available for D3, D4 and D5) * Demo program of the JfControls library This is enough to see the demo, but now that you are there, also download the following files because quite probably you'd like to have them after seeing the demo: * JfControls Library Help (available for D3, D4 and D5) * Tutorial in PDF format of the JfControls library Both files are available in English and Spanish INSTALLATION ============ The installation is really easy. First of all you have to install JfSetup (the general program for installations). This will create a group named JfActiveSoft in your Start menu where you'll find JfSetup. Then you have to decompress the rest of archives in a temporary folder and run JfSetup to install them in your system (use the 'Add' button). And this is it. You don't have to configure Delphi, edit .inc files, compile units, install packages or anything... RUNNING THE DEMO ================ Now that you have the components installed, run Delphi and open the the demo project, which is usually located at: C:\Program files\JfActiveSoft\JfControls_Demo\JfDemoSt.dpr Press F9 and start exploring the demo... I recommend you to click on "Components for menus" and then select "TJfMainMenu". Use the mouse to navigate thru ALL the options in the menus and submenus because there are some interesting things to see... You can also play a bit with the options in the dialog box... Click "Configuration" in the top menu, select "Colors configuration (Themes)", choose a color scheme and click "OK". Click "Configuration" again and this time select "Language selection" and then choose for example "Spanish" and click "OK". Now click on "Configuración" and choose "Selección de idioma", choose "Ingles" and click on "Aceptar" to restore the English configuration. MY FIRST EXAMPLE ================ How difficult would be to make something like that demo with these components? Start a new project and see it by yourself... In the Components Palette you'll see three new tabs: JfStandard, JfAdditional and JfDataControls that hold about 40 components in total, but in this example we are only going to use a few components of the JfStandard tab. First of all, drop a TJfCApplication and TJfCForm component to your form. The main form of your application should have a TJfCApplication component and every form should have a TJfCForm component. It's not completely necessary actually, but some features depend on this. Now we are going to add a background to the form: * Click the JfCForm1 component you've just dropped * In the Object Inspector look for the SetBackground property and click its ellipsis (...) button. You'll see the "SetsBackGround" dialog. * Click "Add". You'll see the "Add BackGround" dialog. * If you want to can provide a name and description for the background (for example "Standard Background 1" and "Simple gradient" respectively). * In the Style combo choose "bgsHorzOut" * Select the Start and End colors (you can click on the color-picker buttons to select the colors). In the "Result Background" area you'll see a preview of your background. * Click "OK" to add the new background. You'll see it in the list. * Click "OK" to select this background for the form. The new background will be visible when you run the application, so press F9 to see it. Stop the application and now drop a TJfCLabel to your form. Now we are going to set the font for the label. It might seem a bit complicated at first, but you'll realize the advantage later: * Click the JfCLabel1 component you've just dropped * In the Object Inspector look for the Text property and click the "plus" (+) button. * Find the SetFont property and click its ellipsis (...) button. You'll see the "SetsFont" dialog. * If you want to can provide a name and description for the font (for example "Font1" and "Times New Roman 14 Bold" respectively). * Click on the little button next to the Font to open the dialog * Select "Times New Roman", "Bold" and "14" and click OK. * Click "OK" to add the new font. You'll see it in the list. * Click "OK" to select this font for the label. Now set the background for the label following a similar procedure like you did with the JfCForm1 component, adding a new background to the list. Now set the shape of the label: * Set AutoSize to False. * Open the Shape object property (clicking the "plus" (+) button). * Set the Visible property of the shape to True. * Set the Shape property of the shape to shaArrow. * Open the Brush object property of the shape. * Click its ellipsis (...) button of the SetBackground property of the Brush. You'll see the "SetsBackground" dialog. * Select the background you first gave to the form and click "OK". * Resize the label so that the caption text fits inside the arrow. Drop a TJfBitBtn to your form and set its font: * Click the JfBitBtn1 component you've just dropped * In the Object Inspector look for the Text property and if necessary click the "plus" (+) button to open the options. * Find the SetFont property and click its ellipsis (...) button. You'll see the "SetsFont" dialog. * You can add a new font as you've done before, but this time simply choose the one we set for the label and click OK. Now set the background of button with the SetBackground property, but this time selecting an existing one (the one of the label, or the one of the form). CENTRALIZED ADMINISTRATION OF RESOURCES ======================================= As you can see, there's a centralized list of backgrounds and also a list of fonts, that act as a repository for those kind of resources. There are also repositories for images, colors and string constants. These repositories are called "packages" (don't mistake them with Delphi packages). It takes some steps to add a new resource, but once you've done it, they are easy to use it, speeding up the design of your application. This model has an important side-effect, which is one of the reasons why JfControls were designed this way: if you modify a resource, all components that share it will automatically be "updated". For example, click the SetFont property of the button, click the "Modify" button and then change the font to Arial and click "OK" to apply the changes and "OK" to select the font. As you can see, the change applied also to the label, because they both share the same font resource. In the case of string constants, there's also a repository for them, and for example this allows translation. There's another important side-effect, which is perhaps the main reason for the existence of JfControls: in the case of the image repository, each image is loaded only once and used many times, being friendly with system resources. FEATURES OF JFCONTROLS LIBRARY ============================== * Packages administration for configuration. It's possible to store all type of configurations for the components in "packages". These "warehouses" can be saved as files apart from the application or in the DFM files of Delphi that later on are integrated in the own executable one. When the packages are kept in independent files of the executable, they have the advantage of being able to be selected at run time, and this way an application can change its language or appearance. * Multilanguage in run and design time. The packages technology give you the possibility to develop applications with support for several languages. Easily you will be able to build applications that changes in run and design time of one to other language. The library has incorporated special properties editors to facilitate the translation. * Multiconfiguration in run and design time for backgrounds, colors, fonts and images. An application made with the JfControls library has the capacity of change its appearance in run and design time * Centralized configuration of colors, fonts, backgrounds, images, strings, messages and texts. The packages technology allows to make groups of common elements. There are packages for configuration of colors, fonts, backgrounds and images. This way a color, font or image can be used in different places of application, windows, controls, etc. * Transparencies administration in all the components. All the controls of JfControls library, panels, edition fields, RadioGroups, selection list, check box, etc., allow to make transparencies with the background of the container in which they are. * Container package of images that admits simultaneously graphic formats of any type. The images package is able to contain all type of graphic formats that are or can be installed in Delphi. The JfControls library by default supports the ICO, BMP, WMF, JPG formats, but the installation of another freeware or shareware packages will make the library support additional formats. There are some special functions to carry out their registration inside the library. * Variables package for manual and automatic configurations of application. There's a special type of package that is able to save variables of all types in a nested structure, like in the Windows registry, and it is used to automatically save the positions and sizes of windows and others significant window elements, and at same time it's good as a container for the variables that the user wish to save in it. It has like advantage over the Windows registry that it's independent of the operating system, it's managed in memory, it's transparent to the developer and it's easily portable. * Regional configuration imbued in the library. The regional configuration is independent of the operating system, so that -if you wish- the application is not subject to Windows formats of dates, times, numbers, etc. All these is saved in the language packages and it's independent of the language that you select. * Label associated to all the controls. All controls of the JfControls library have an associated label, which is completely customizable and can be placed anywhere you like it. * Multiple procedures and functions that simplify the development of applications. There are several procedures and functions that facilitate the manage of windows and configuration packages of the library. The JfControls library has been developed thinking in voluminous applications replete of windows, for which one should use the auto-creation characteristic of Delphi windows only in few occasions and it's better if you don't use it, because the application will run most quickly and will use better Windows resources. The procedures and functions that are included in the library improve the administration of windows creation and destruction, and they alleviate the load on system resources, also allowing dynamic changes of style. There also are functions to manage strings, dates, numbers, messages, exceptions and more. * Multiple traditional problems resolved. The JfControls library is not limited to incorporating a group of components and controls for application development, but also have fixed all the traditional problems that take place during the elaboration of a project, problems relative to graphics, as the administration of diverse graphic formats from the databases and their visualization in the TJfDBImage component, positioning of MDI-child windows, automatization of the parent-MDI window's buttons, administration of backgrounds in main MDI windows, colors and fonts in administration of messages and in administration of errors, correction of flicks in the repainted of forms and controls, etc. * New mask administration with multi-location. The JfControls library incorporates a new masks system, much more easy to use, because the controls have the possibility to know the data type that they will manage through their DataType property, but masks administration is not limited to a local control in the component, but rather it can be based in the definitions that you have made at the level of all the application, at the level of the window in which the control is or in a local mode. These capacities enable an application to change at a global level the definition of masks for all the windows or for some in short, making more easy to work, for example, with several different periods of date, with different decimal types for money, etc., inside of one application. * Data type administration in all the controls. All the controls have a DataType property that indicate them the data types they will manage. This, together with their Value property of Variant type, give them possibilities of format and use much more easy. The data types at the moment supported are: Integer, Currency, Double, DateTime, String, Binary and Memo. * Special optimization system of the operating system resources. The JfControls library executes a series of special procedures in the graphics administration, so that the library gets a liberation of system resources, enabling applications to use a lot of quantity of graphics without draining the resources of the machine and giving the possibility to carry out application much bigger. * 30 components completely customizable. The components of JfControls library share the properties that allow them to use the configurations defined in packages, so they can be completely customizable. All the components have common properties to define the text, associate label, masks, transparency, resize, position, privileges and visibility. * 11 components linked to data. The data components are linked to data sources that have been implemented using the standard data links of the VCL, so they are compatible with any dataset developed starting from the version 3 of Delphi. This way the data components can be used with the BDE, IBX, ADO Express or any other group of components derived of the standard Delphi dataset. * Menu bar completely customizable. The menu bars have been totally reprocessed to achieve a distinction touch and functionality that you won't be found in traditional applications. Their capacity and easiness of configuration will give you the possibility to create applications that will be distinguished very easily from others. Several menu bars can simultaneously exist in the same window, with different alignments and different scroll possibilities. The menus of JfControls library allow banners at any level, including the main bar of a menu. They have alignment capacity and they can have backgrounds with gradients, images and solid colors. The texts allow angles, different styles and colors. The submenu borders can be defined and they can have images in the different options. The menu bar controls automatically the buttons in MDI child windows, they can be changed of shape, background, etc. The same capacity of configuration has been implemented in the popups. * More than 40 shape types for labels and buttons. Most of the library's controls allow to take different shapes or to incorporate different shapes in their interior. This way, for example, the TJfSpeedButton, TJfLabel or TJfCheckBox components can take more than 40 types shapes and the TJfListBox component when working in CheckBox mode allows each mark to have a certain shape. The shapes can have image backgrounds, gradient or solid colors, and they can incorporate lines of different sizes, styles and colors. * Embedded forms. The TJfScrollBox component has special procedures to manage other windows in its interior. These procedures automate the creation and destruction of the forms and negotiate the location of window inside the component. * Control for visualization of images that resists any graphic format type. The TJfImage component has been transformed and improved to make it able to manage any graphic format type installed in Delphi, it can even work in format-autodetection mode. The database version of this component has the same functionality and it's able to save in the same table field any graphic format simultaneously. * TJfPageControl component with exclusive customization possibilities. The TJfPageControl component allows a complete customization for its tabs, they can take the buttons style with different shapes and backgrounds, or a more traditional style. The tabs can be in any position, to be multiline or in scroll mode. * StatusBar completely customizable and with defaulted information types. The TJfStatusBar component is completely customizable, it allows backgrounds and styles for any internal panel, letter types, multiline text, defaulted information (Time, Bloq.num, Num.Caps, etc.), embedded progress bar, images and much more. * TJfLabel component with shapes, text with angle, images and multiline. The TJfLabel component is a highly sophisticated label control, simultaneously it's able to manage embedded shapes, accessory labels, text with angle, multiline text, shade, backgrounds with images, gradient, water marks, solid colors, shines, annexed images, privileges, masks, data types and defaulted information. Most controls have associated a label that can be placed in any position and has the same functionality, almost as complete as the TJfLabel component. * Edition field with incorporated buttons. The TJfEdit component is a edition field with transparency, masks administration, data type to manage, backgrounds with images, gradient, solid colors, shade, enclosed image, associated label, 6 enclosed buttons (Calendar, calculator, maintenance, relationship, customizable 1, customizable 2) completely customizable, privilege of centered access, different styles for borders, special property for the execution of data, when is pressed the ENTER key, without necessity of passing the focus to the following control, capacity to specify a period of time so that the OnChange event takes place (synchronizations with fields of a database). * Buttons bar of administration and navigation for the components of images, texts and databases. In JfControls library there are several button bars that are good to manage the characteristics of load, recording and erasing of TJfMemo, TJfRichEdit and TJfImage controls. There is also the traditional navigation bar for the registries administration in a data source. In all of them you can configure the shape of the buttons, the background and their visibility. These bars connect with the referenced controls. * Enlarged administration of rich texts. The TJfRichEdit component is for multiline rich text edition and allows solid color for background, shade, totally customizable associated label, privilege of centered access, inclusion of images next to the text, insertion of OLE objects, dialog box with a complete environment to edit rich text and traditional mode of handling. * Progress bar completely customizable. The TJfProgressBar component is a progress bar that has been completely rewrite and allows backgrounds with images, gradient, solid colors, transparencies, shades, associated label, privilege of centered access, orientation of the bar and blocks movement, different block size, backgrounds completely customizable for blocks and different border types. * Privileges administration. The TJfCApplication component allows a centered administration of access and visualization privileges of all the library controls. You can create definitions of permits and connect several controls to any of them, this way the handling of that permit automatically produces a status change in all the controls linked to it. One of the most widespread uses in the privileges administration is the capacity to give some permits or others, according to the user that uses the application, facilitating the work of granting and removing the visibility or the handling of controls. * Customizable calculator and calendar incorporated. There is a customizable calculator and calendar that can be executed from the buttons of edition fields or from the TJfCApplication component. The calendar allows resizing so you can see more or less months. Both components are coupled automatically to the characteristics of the regional configuration indicated in the TJfCApplication component. * Administration of image-shaped forms. There are new components to create image-shaped forms. The windows generated this way can take any shape that you can imagine. You'll surely impress your users. The shapes of the windows can be changed at run time. Buttons can manage different images and backgrounds for each one of their status. Mouse detection can be carried out within regions or within the total area of button. The TJfBitBtn component can even take shaped forms. * Visual manual. There's a complete tutorial very visual which facilitates the adaptation to the components handling, while introducing you, step by step, in the development of your applications. * Complete context help from Delphi. The context help is complete, so that you will be able to see the explanations of all classes, properties, methods, events, procedures and functions of JfControls library. * Compatibility. The library is compatible with Delphi 3 to 5, and Windows 95, 98, Me, NT and 2000. COST AND PAYMENT METHODS ======================== The JfControls Library with 3 months of free updates costs Euros 195.33 / $ 180.23 USD, and the annual updates subscription costs Euros 126.21 / $ 116.45 USD. Library+Sources are now available for sale and they cost Euros 390.66 / $ 362.34 USD. You can pay online if you have Visa, Mastercard, 4B, American Express or Virtual Cash credit card. If you have another credit card or don't wish to pay online, there's a printable form at http://www.jfactivesoft.com/ensales.htm that you have to complete and send by fax to +34 96 6655275. You can also pay them by a bank transfer: Banesto Plaza de Crevillente, 3 - Elche (Alicante) - Spain Entity 0030 Branch 3125 Account : 33-0387300273. Include Euros 16.83 / $ 15.05. for bank wire fee. You have to send the transfer voucher by fax to +34 96 6655275. ________________________________________________________________________ 3. ERROR HANDLING IN DELPHI 5 (II) The first part of this article was published in the last issue: http://www.latiumsoftware.com/en/pascal/0014.php DEFAULT EXCEPTION HANDLER ========================= Appart from trapping an exception to display the corresponding error message, a try..except..end block allows applications to perform some clean-up or finalization code, habitually to free allocated resources (close open files, release memory, etc.). For example, in the following procedure we free the string list whether an exception occurred or not: procedure TForm1.Button1Click(Sender: TObject); var StringList: TStringList; begin StringList := nil; try StringList := TStringList.Create; StringList.LoadFromFile(''); ShowMessage(StringList.Text); except on e: exception do HandleError(Sender, e); end; StringList.Free; // Finalization code. end; For procedures that doesn't need to free resources, for example like this one: procedure TForm1.Button2Click(Sender: TObject); var i: integer; begin i := 0; ShowMessage(IntToStr(10 div i)); end; ...you don't need to use a try block, and you can still trap exceptions. The trick is done by assigning an OnException event handler for the Application object. To simplify this task for the programmer I wrote a procedure HandleApplicationExceptions that assigns to OnException a procedure that shows the corresponding error message and saves it in the errors log. To make use of it, you simply have to call this procedure for example when your program starts: program Project1; uses Forms, Unit1 in 'Unit1.pas' {Form1}, ErrHndlr in 'ErrHndlr.pas'; {$R *.RES} begin Application.Initialize; HandleApplicationExceptions; // Activate the error handler Application.CreateForm(TForm1, Form1); Application.Run; end. For those of you who might be interested in the implementation details, in the ErrHndlr unit I defined HandleApplicationExceptions as follows: procedure HandleApplicationExceptions; begin Application.OnException := AppExceptionHandler.Handler; end; It simply assigns the OnException property of the Application object to the address of the AppExceptionHandler.Handler method. I had to declare a little class to provide this procedure of type TExceptionEvent (the type of the OnException property), declared in the Forms unit: type TExceptionEvent = procedure(Sender: TObject; E: Exception) of object; I declared this class in the implementation section of ErrHndlr as follows: type TAppExceptionHandler = class(TObject) public procedure Handler(Sender: TObject; E: Exception); end; var AppExceptionHandler: TAppExceptionHandler; procedure TAppExceptionHandler.Handler(Sender: TObject; E: Exception); begin HandleError(Sender, E, nil); end; The method just calls the HandleError(Sender,E,Address) procedure presented in the past issue. NOTE: I didn't create the instance AppExceptionHandler because we don't need it since we never access properties of this object, so it doesn't have to exist. Using this event handler, the line appended to the errors log for the error produced in TForm1.Button2Click would look like the following Mon 08-Jan-2001 15:39:28 - EDivByZero: Division by zero [Form1] @ 44748F API ERRORS ========== Windows API functions don't use Delphi exceptions. Rather they report error conditions in their return value. Most of them return 0 (or False) if unsuccessful and you can get the error code by calling GetLastError immediately aftwards (before calling any other API function). To obtain the corresponding error message you can pass this code to the function SysErrorMessage. You can use the errors and raise exceptions, like I'll show you below, but I also decided to write a couple of overloaded functions to handle api errors directly: procedure HandleError(ApiError: integer); overload; var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(nil, ApiError, Address); end; procedure HandleError(Sender: TObject; ApiError: integer); overload; var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(Sender, ApiError, Address); end; Both are wrappers that provide the Address parameter for the following procedure: procedure HandleError(Sender: TObject; ApiError: Integer; Address: Pointer); overload; var ErrMessage: string; begin ErrMessage := SysErrorMessage(ApiError); ShowErrorBox(ErrMessage); SaveError(Sender, 'Error WinAPI #' + IntToStr(ApiError) + ': ' + ErrMessage, Address); end; This procedure is similar to the HandleError(Sender,E,Address) procedure that I presented in the last issue. Here is an example of use of HandleError with API errors: procedure TForm1.Button3Click(Sender: TObject); var Handle: THandle; Buffer: array [0..511] of char; BytesRead: longword; begin Handle := CreateFile('project1.dpe', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if Handle = INVALID_HANDLE_VALUE then HandleError(Sender, GetLastError) else begin if not ReadFile(Handle, Buffer, 511, BytesRead, nil) then HandleError(Sender, GetLastError) else begin Buffer[BytesRead] := #0; ShowMessage(Buffer); end; CloseHandle(Handle); end; end; The line appended to the errors log will look like this: Mon 08-Jan-2001 15:41:06 - Error WinAPI #2: The system can't find the specified file [Form1.Button3] @ 445C31 API ERRORS AS EXCPETIONS ======================== Another way of handling API erros is by raising an exception when they occur so we can use the exception handling mechanism we've seen before. For example: procedure TForm1.Button4Click(Sender: TObject); var Handle: THandle; Buffer: array [0..511] of char; BytesRead: longword; begin Handle := INVALID_HANDLE_VALUE; try Handle := CreateFile('project1.dpe', GENERIC_READ, FILE_SHARE_READ, nil, OPEN_EXISTING, 0, 0); if Handle = INVALID_HANDLE_VALUE then raise Exception.Create(SysErrorMessage(GetLastError)); if not ReadFile(Handle, Buffer, 511, BytesRead, nil) then raise Exception.Create(SysErrorMessage(GetLastError)); Buffer[BytesRead] := #0; ShowMessage(Buffer); except on e: Exception do HandleError(Sender, e); end; CloseHandle(Handle); end; The line appended to the errors log will look like the following: Mon 08-Jan-2001 15:42:17 - Exception: The system can't find the specified file [Form1.Button4] @ 445DEA CUSTOM EXCEPTIONS ================= Raising exceptions this way is a bit expensive in code size. We can define a procedure to simplify things a little bit: procedure RaiseApiError(ApiError: integer); begin raise Exception.Create(SysErrorMessage(ApiError)) end; Then you can use it this way: procedure TForm1.Button4Click(Sender: TObject); ... if Handle = INVALID_HANDLE_VALUE then RaiseApiError(GetLastError); if not ReadFile(Handle, Buffer, 511, BytesRead, nil) then RaiseApiError(GetLastError); ... end; The problem with converting API errors to exceptions is that we lose the error code information because the Exception class doesn't define a field with an error number. For this reason I defined a base class for custom exceptions that provides two more fields: one for an error number and another one for the caller address: type ECustomError = class(Exception) public Number: Integer; Address: Pointer; constructor Create(const ErrMessage: string; Address: Pointer); overload; constructor Create(const ErrMessage: string; Number: Integer = 0); overload; constructor Create(const ErrMessage: string; Number: Integer; Address: Pointer); overload; end; procedure RaiseCustomError(const ErrMessage: string; Number: Integer = 0); implementation constructor ECustomError.Create(const ErrMessage: string; Address: Pointer); begin Create(ErrMessage, 0, Address); end; constructor ECustomError.Create(const ErrMessage: string; Number: Integer; Address: Pointer); begin inherited Create(ErrMessage); Self.Number := Number; Self.Address := Address; end; {W+} constructor ECustomError.Create(const ErrMessage: string; Number: Integer); var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; Create(ErrMessage, Number, Address); end; procedure RaiseCustomError(const ErrMessage: string; Number: Integer); var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; raise ECustomError.Create(ErrMessage, Number, Address); end; We can now derive an EApiError class from ECustomError: type EApiError = class(ECustomError) public constructor Create(ApiError: Integer); overload; constructor Create(ApiError: Integer; Address: Pointer); overload; end; procedure RaiseApiError(ApiError: integer); implementation constructor EApiError.Create(ApiError: Integer; Address: Pointer); begin inherited Create(SysErrorMessage(ApiError), ApiError, Address); end; constructor EApiError.Create(ApiError: Integer); var Address: Pointer; begin asm // Address := %ReturnAddress%; mov edx, [ebp+4]; // EDX := (EBP+4)^; mov Address, edx; // Address := EDX; end; Create(ApiError, Address); end; {W+} procedure RaiseApiError(ApiError: integer); var Address: Pointer; begin asm // Address := %ReturnAddress%; mov edx, [ebp+4]; // EDX := (EBP+4)^; mov Address, edx; // Address := EDX; end; raise EApiError.Create(ApiError, Address); end; Now RaiseApiError raises an EApiError exception that includes the error code and the address where the exception was risen, so we just have to make a little modification to our old HandleError(Sender,E,Address) procedure: procedure HandleError(Sender: TObject; E: Exception; Address: Pointer); overload; begin ShowErrorBox(E.Message); if E is ECustomError then with ECustomError(E) do SaveError(Sender, E.ClassName + ' #' + IntToStr(Number) + ': ' + Message, Address) else SaveError(Sender, E.ClassName + ': ' + E.Message, Address); end; Now the line appended to the errors log when an API error occurs would look like this: Mon 08-Jan-2001 15:42:17 - EApiError #2: The system can't find the specified file [Form1.Button4] @ 445C31 OTHER ERRORS ============ There are other errors that might occur in an application and that one would like to save in an errors log. Mainly these are internal errors, programming errors, logic errors, or whatever you call them. You can use RaiseCustomError to raise them as an exception, like for example: procedure TForm1.Button5Click(Sender: TObject); begin if (GetTickCount And 1) <> 0 then RaiseCustomError('An error condition occurred'); end; Because we didn't use a try block, the exception will be captured by the OnException event handler of the Application object, and the line appended to the errors log might look like this: Mon 08-Jan-2001 15:42:17 - ECustomError #0: An error condition occurred [Form1] @ 445EE1 Like in the case of API errors, I also wrote a couple of procedures to handle these errors without making use of exceptions: procedure HandleError(const ErrMessage: string); overload; var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(nil, ErrMessage, Address); end; procedure HandleError(Sender: TObject; const ErrMessage: string); overload; var Address: Pointer; begin asm // Address := %ReturnAddress%; mov ecx, [ebp+4]; // ECX := (EBP+4)^; mov Address, ecx; // Address := ECX; end; HandleError(Sender, ErrMessage, Address); end; They both get the address parameter to call HandleError: procedure HandleError(Sender: TObject; const ErrMessage: string; Address: Pointer); overload; begin ShowErrorBox(ErrMessage); SaveError(Sender, ErrMessage, Address); end; You can use of these procedures as shown here: procedure TForm1.Button5Click(Sender: TObject); begin ShowMessage('Initialization code'); if (GetTickCount And 1) <> 0 then HandleError(Sender, 'An error condition occurred'); ShowMessage('Finalization code'); end; The line appended to the errors log might look like this: Mon 08-Jan-2001 15:42:17 - An error condition occurred [Form1] @ 445EE1 - - - - - - - - - - - - - - - - - - - - - I hope you've found this article useful. If you have doubts or questions you can subscribe to our low-traffic free mailing list. This is the only forum for intermediate-level Delphi programmers on the Web (Delphi hackers are also welcome :-)). If you know much of Delphi but you are still far from being a guru this forum is for you: http://groups.yahoo.com/group/delphi-en If you want to join the group, the best way is to subscribe from the web since you can access the special features available at the web site (a Yahoo! ID is required and you can get yours free by registering as a Yahoo! user), but if you don't want to register or if you don't have full Internet access you also can subscribe by email: http://groups.yahoo.com/group/delphi-en/join delphi-en-subscribe@yahoogroups.com ________________________________________________________________________ 4. WINDOWSE - ADVANCED WINDOWS ANALYSER! GREATIS SOFTWARE ================ Greatis Software is a rapidly growing company based in Yaroslavl, Russia, that specializes in system software development and special software for developers. Their web page is http://www.greatis.com and there you can find a lot of freeware application and components, some of them with source code. WINDOWSE ======== One of the applications you can find there is WinDowse, an extremely convenient tool for obtaining necessary technical information about any window (remember that a component is itself a window). When you place the mouse cursor on a window, WinDowse will show you all parameters of the window, window class, parents and children including: * "Window" tab - Text or caption - Process ID - Exe filename - Application instance - Window handle - Parent window handle - Window function address - Window menu handle - Coordinates in parent - Coordinates in screen - Window size - Window client area size - Window style and extended style * "Class" tab - Class name - Class function address - Icon and small icons handles - Cursor handle - Background brush handle - Module handle - Class style. * "Parents" tab - List of parent hierarchy * "Children" tab - List of children of current window * "Graphics" tab - Absolute (screen) cursor position - Relative cursor position on window coordinates - Relative cursor position on window client area coordinates - Color of the pixel under mouse cursor - Screen capture tools: display, zoom, copy, save All parameters can be shown in hexadecimal, decimal or binary formats. Upon activation, WinDowse displays a continuos readout as the user moves the mouse about the screen - switching back and forth between separate or nested windows. At any time the continuous readout can be frozen by a click of the mouse, and the data for that window studied in detail. WinDowse also allows results to be copied directly to the clipboard. Each field of the analysis is supplied with context-sensitive help explaining "what is". LICENSE ======= WinDowse 4.1 is FREE. Full source code for Delphi costs $19.95(US). To order the sources visit http://www.greatis.com/windowsebuy.htm For more information, contact Greatis Software: b-team@greatis.com ________________________________________________________________________ 5. A DBGRID WITH A CHECKBOX In the last issue I promised to show how to place checkboxes for boolean fields in a DBGrid. Although the code is usable, of course it would be better to use some professional component like one of those you can surely find in the net. The purpose of this article is simply to present an example of the use of some properties of the DBGrid, Column and Field objects. The first thing we have to take care of is displaying the checkbox. For this purpose we should trap the OnDrawColumnCell of the DBGrid: procedure TForm1.DBGrid1DrawColumnCell(Sender: TObject; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); begin ChkDBGridDrawColumnCell(Sender as TDBGrid, Rect, DataCol, Column, State); end; ChkDBGridDrawColumnCell is a procedure I declared in a separate unit: procedure ChkDBGridDrawColumnCell(DBGrid: TDBGrid; const Rect: TRect; DataCol: Integer; Column: TColumn; State: TGridDrawState); var X, Y, Index: integer; Field: TField; begin Field := Column.Field; if (Field <> nil) and (Field.DataType = ftBoolean) then begin // Fill the cell with the background color DBGrid.Canvas.FillRect(Rect); // Determine the position of the graphic inside the cell case Column.Alignment of taRightJustify: X := Rect.Right - 2 - 16; taCenter: X := (Rect.Right - Rect.Left - 16) div 2 + Rect.Left; else // taLeftJustify: X := Rect.Left + 2; end; Y := (Rect.Bottom - Rect.Top - 16) div 2 + Rect.Top; // Determine the image to be used if Field.AsBoolean then Index := 1 else Index := 0; // Draw the graphic Form1.ImageList1.Draw(DBGrid.Canvas, X, Y, Index); end else // Default drawing DBGrid.DefaultDrawColumnCell(Rect, DataCol, Column, State); end; What this procedure does is first checking if the cell corresponds to a boolean field and -if so- it draws a checked or unchecked checkbox using the corresponding image taken from an image list. The code is similar to the one I presented in the last issue in the article DRAWING CELLS IN A DBGRID. For other columns it simply calls DefaultDrawColumnCell. Now we have to face a problem: whenever the user presses a key or clicks in a selected cell, or presses F2, the grid goes into "editor mode". This is so because normally the grid has the option dgEditing activated (see the Options property). To override this behavior we have to turn off the dgEditing option when the user selects a cell that corresponds to a boolean field, and then turn it on again when the user leaves the cell. For this purpose we have to trap the OnColEnter and OnColExit events: procedure TForm1.DBGrid1ColEnter(Sender: TObject); begin ChkDBGridColEnter(Sender as TDBGrid); end; procedure TForm1.DBGrid1ColExit(Sender: TObject); begin ChkDBGridColExit(Sender as TDBGrid); end; Again I called procedures defined in a separate unit: procedure ChkDBGridColEnter(DBGrid: TDBGrid); var Field: TField; begin Field := DBGrid.SelectedField; if (Field <> nil) and (Field.DataType = ftBoolean) then DBGrid.Options := DBGrid.Options - [dgEditing]; end; procedure ChkDBGridColExit(DBGrid: TDBGrid); var Field: TField; begin Field := DBGrid.SelectedField; if (Field <> nil) and (Field.DataType = ftBoolean) then DBGrid.Options := DBGrid.Options + [dgEditing]; end; You only have to use these procedures if your DBGrid uses the dgEditing option. Finally, we have to provide a way for the user to toggle the value of the field, for example clicking the cell with the mouse or pressing the space bar. We can do it trapping the OnCellClick and OnKeyPress events: procedure TForm1.DBGrid1CellClick(Column: TColumn); begin ChkDBGridCellClick(Column); end; procedure TForm1.DBGrid1KeyPress(Sender: TObject; var Key: Char); begin ChkDBGridKeyPress(Sender as TDBGrid, Key); end; The procedures are defined as follows: procedure ChkDBGridCellClick(Column: TColumn); var Field: TField; begin Field := Column.Field; if (Field <> nil) and (Field.DataType = ftBoolean) and Field.CanModify and not Column.ReadOnly then with Field.Dataset do begin if State = dsBrowse then Edit; Field.AsBoolean := not Field.AsBoolean; end; end; procedure ChkDBGridKeyPress(DBGrid: TDBGrid; var Key: Char); var Field: TField; begin Field := DBGrid.SelectedField; if (Field <> nil) and (Field.DataType = ftBoolean) then if (Key = ' ') and Field.CanModify and not DBGrid.Columns[DBGrid.SelectedIndex].ReadOnly then with Field.Dataset do begin if State = dsBrowse then Edit; Field.AsBoolean := not Field.AsBoolean; end; end; ________________________________________________________________________ 6. BORLAND HAS JUST ANNOUNCED AVAILABILITY OF KYLIX Kylix will come in three versions: * Kylix Desktop Developer (for Linux GUI development) U$S 999. * Kylix Server Developer (for web application development) U$S 1,999. * Open Edition (for open source and free software (GPL) developers). The first two will be will be generally available before the end of the first quarter 2001 (Borland has announced shipments for February 22) and they are available for shopping right now at the Borland site: http://shop.borland.com/Category/0,1257,3-15-1080,00.html The Open Edition version will be available by mid-2001 for free download (or for purchase at $99 with hardcopy documentation and CD). What is the difference between Kylix Desktop Developer and Kylix Server Developer? Basically, - The Server edition comes with NetCLX (Internet components, including Apache support) that aren't available in the Desktop edition. - The Server edition will support IBM DB2, Oracle 8i, InterBase and MySQL, while the Desktop edition will only support the latter two. What do they have in common? - Both editions will come with CLX source code - System Requirements: * Intel Pentium 200 MHz (P2 400 MHz recommended) * 64 MB RAM (128 MB recommended) * CD-ROM drive * 175 MB hard disk space or 155 MB full install * VGA or higher resolution monitor * Mouse or other pointing device - Supported Linux Distributions: * Red Hat 6.2 or higher * Mandrake 7.2 or higher * SuSE 7.0 or higher More information: http://www.borland.com/kylix/ ________________________________________________________________________ YOU CAN HELP US We need your help to keep this newsletter going and growing. You can help by referring the newsletter to your colleagues: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php Or you can help by voting for us in some or all of these rankings to give more visibility to our web site and thus increase the number of subscriptions to this newsletter: http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://www.top219.org/cgi-bin/vote.cgi?delphi&83 http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80 It's just a few seconds for you that REALLY mean a lot to us. ________________________________________________________________________ If you haven't received the full source code examples for this issue, you can get them from http://www.latiumsoftware.com/download/p0015.zip ________________________________________________________________________ This newsletter is provided "AS IS" without warranty of any kind. Its use implies the acceptance of our licensing terms and disclaimer of warranty you can read at http://www.latiumsoftware.com/en/legal.php where you will also find a note about legal trademarks. Articles are copyright of their respective authors and they are reproduced here with their permission. You can redistribute this newsletter as long as you do it in full (including copyright notices), without changes, and gratis. ________________________________________________________________________ Main page: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php Group home page: http://groups.yahoo.com/group/pascal-newsletter/ Subscribe/join: pascal-newsletter-subscribe@yahoogroups.com Unsubscribe/leave: pascal-newsletter-unsubscribe@yahoogroups.com Problems with your subscription? eds2004 @ latiumsoftware.com ________________________________________________________________________ Latium Software http://www.latiumsoftware.com/en/index.php Copyright (c) 2001 by Ernesto De Spirito. All rights reserved. ________________________________________________________________________ |
The full source code examples of this issue are available for download.
![]() |
Errors? Omissions? Comments? Please contact us!






