RAD Studio |  |

Using the VCL/RTL
Collapse All
Topics

Name | Description
Using the VCL/RTL: Overview | There are a number of units in the component library that provide the underlying support for most of the component libraries. These units include the global routines that make up the runtime library, a number of utility classes such as those that represent streams and lists, and the classes TObject, TPersistent, and TComponent. Collectively, these units are called the VCL/RTL. The VCL/RTL does not include any of the components that appear on the Tool Palette. Rather, the classes and routines in the VCL/RTL are used by the components that do appear on the Tool Palette and... more
Adding New Measurement Types | If you want to perform conversions between measurement units not already defined in the StdConvs unit, you need to create a new conversion family to represent the measurement units (TConvType values). When two TConvType values are registered with the same conversion family, the Convert function can convert between measurements made using the units represented by those TConvType values. You first need to obtain TConvFamily values by registering a conversion family using the RegisterConversionFamily function. After you get a TConvFamily value (by registering a new conversion family or using one of the global variables in the StdConvs unit),... more
Approaches to File I/O | There are several approaches you can take when reading from and writing to files: The recommended approach for working with files is to use file streams. File streams are instances of the TFileStream class used to access information in disk files. File streams are a portable and high-level approach to file I/O. Because file streams make the file handle available, this approach can be combined with the next one. The Using file streams discusses TFileStream in detail. You can work with files using a handle-based approach. File handles are provided by the operating system when you create or open a... more
Common List Operations | Although the various list classes contain different types of items and have different ancestries, most of them share a common set of methods for adding, deleting, rearranging, and accessing the items in the list.
Converting Measurements | The ConvUtils unit declares a general-purpose Conversion Function that you can use to convert a measurement from one set of units to another. You can perform conversions between compatible units of measurement such as feet and inches or days and weeks. Units that measure the same types of things are said to be in the same conversion family. The units you're converting must be in the same conversion family. For information on doing conversions, see Performing Conversions. The StdConvs unit defines several conversion families and measurement units within each family. In addition, you can create customized... more
Copying and Clearing Custom Variants | In addition to typecasting and the implementation of operators, you must indicate how to copy and clear variants of your custom Variant type. To indicate how to copy the variant's value, implement the Copy method. Typically, this is an easy operation, although you must remember to allocate memory for any classes or structures you use to hold the variant's value:
Copying Data from One Stream to Another | When copying data from one stream to another, you do not need to explicitly read and then write the data. Instead, you can use the CopyFrom method, as illustrated in the following example. In the following example, one file is copied to another one using streams. The application includes two edit controls (EdFrom and EdTo) and a Copy File button.
Creating a Class to Enable the Custom Variant Type | Custom variants work by using a special helper class that indicates how variants of the custom type can perform standard operations. You create this helper class by writing a descendant of TCustomVariantType. This involves overriding the appropriate virtual methods of TCustomVariantType. The following topics provide details on how to implement and use a TCustomVariantType descendant: Enabling casting Implementing binary operations Implementing comparison operations Implementing unary operations Copying and clearing custom variants Loading and saving custom variant values Using the TCustomVariantType descendant
Creating a Simple Conversion Family and Adding Units | One example of when you could create a new conversion family and add new measurement types might be when performing conversions between long periods of time (such as months to centuries) where a loss of precision can occur. To explain this further, the cbTime family uses a day as its base unit. The base unit is the one that is used when performing all conversions within that family. Therefore, all conversions must be done in terms of days. An inaccuracy can occur when performing conversions using units of months or larger (months, years, decades, centuries, millennia) because there is not... more
Defining Custom Variants | One powerful built-in type of the Delphi language is the Variant type. Variants represent values whose type is not determined at compile time. Instead, the type of their value can change at runtime. Variants can mix with other variants and with integer, real, string, and boolean values in expressions and assignments; the compiler automatically performs type conversions. By default, variants can't hold values that are records, sets, static arrays, files, classes, class references, or pointers. You can, however, extend the Variant type to work with any particular example of these types. All you need to do is create... more
Enabling Casting | One of the most important features of the custom variant type for you to implement is typecasting. The flexibility of variants arises, in part, from their implicit typecasts. There are two methods for you to implement that enable the custom Variant type to perform typecasts: Cast, which converts another variant type to your custom variant, and CastTo, which converts your custom Variant type to another type of Variant. When implementing either of these methods, it is relatively easy to perform the logical conversions from the built-in variant types. You must consider, however, the possibility that the variant to or from... more
Use __fastcall When Overriding VCL Methods | Member functions for VCL classes generally use __fastcall in their declaration unless declared otherwise. This means that you must use __fastcall in the declaration of any VCL class member function that you override.
Working with ini Files and the System Registry | Many applications use ini files to store configuration information. The VCL/RTL includes two classes for working with ini files: TIniFile and TMemIniFile. Using ini files has the advantage that they can be used in cross-platform applications and they are easy to read and edit. For information on these classes, see Using TIniFile and TMemIniFile for more information. Many Windows applications replace the use of ini files with the system Registry. The Windows system Registry is a hierarchical database that acts as a centralized storage space for configuration information. The VCL includes classes for working with the... more
Using Streams | Streams are classes that let you read and write data. They provide a common interface for reading and writing to different media such as memory, strings, sockets, and BLOB fields in databases. There are several stream classes, which all descend from TStream. Each stream class is specific to one media type. For example, TMemoryStream reads from or writes to a memory image; TFileStream reads from or writes to a file. The following topics describe the methods common to all stream classes: Using streams to read or write data Copying data from one stream to another Specifying the stream position... more
Using TRegistry | If you are writing a Windows-only application and are comfortable with the structure of the system Registry, you can use TRegistry. Unlike TRegistryIniFile, which uses the same properties and methods of other ini file components, the properties and methods of TRegistry correspond more directly to the structure of the system Registry. You can specify both the root key and subkey using TRegistry, while TRegistryIniFile uses HKEY_CURRENT_USER as the root key. In addition to methods for opening, closing, saving, moving, copying, and deleting keys, TRegistry lets you specify the access level you want to... more
Implementing Binary Operations | To allow the custom variant type to work with standard binary operators (+, -, *, /, div, mod, shl, shr, and, or, xor listed in the System unit), you must override the BinaryOp method. BinaryOp has three parameters: the value of the left-hand operand, the value of the right-hand operand, and the operator. Implement this method to perform the operation and return the result using the same variable that contained the left-hand operand. For example, the following BinaryOp method comes from the TComplexVariantType defined in the VarCmplx unit:
Implementing Comparison Operations | There are two ways to enable a custom variant type to support comparison operators (=, <>, <, <=, >, >=). You can override the Compare method, or you can override the CompareOp method. The Compare method is easiest if your custom variant type supports the full range of comparison operators. Compare takes three parameters: the left-hand operand, the right-hand operand, and a var Parameter that returns the relationship between the two. For example, the TConvertVariantType object in the VarConv unit implements the following Compare method:
Implementing Unary Operations | To allow the custom variant type to work with standard unary operators ( -, not), you must override the UnaryOp method. UnaryOp has two parameters: the value of the operand and the operator. Implement this method to perform the operation and return the result using the same variable that contained the operand. For example, the following UnaryOp method comes from the TComplexVariantType defined in the VarCmplx unit:
Loading and Saving Custom Variant Values | By default, when the custom variant is assigned as the value of a published property, it is typecast to a string when that property is saved to a form file, and converted back from a string when the property is read from a form file. You can, however, provide your own mechanism for loading and saving custom variant values in a more natural representation. To do so, the TCustomVariantType descendant must implement the IVarStreamable interface from Classes.pas. IVarStreamable defines two methods, StreamIn and StreamOut, for reading a variant's value from a stream and for writing the variant's... more
Performing Conversions | You can use the Convert function to perform both simple and complex conversions. It includes a simple syntax and a second syntax for performing conversions between complex measurement types.
Persistent Lists | Persistent lists can be saved to a form file. Because of this, they are often used as the type of a published property on a component. You can add items to the list at design time, and those items are saved with the object so that they are there when the component that uses them is loaded into memory at runtime. There are two main types of persistent lists: string lists and collections. Examples of string lists include TStringList and THashedStringList. String lists, as the name implies, contain strings. They provide special support for strings of the form Name=Value,... more
Specifying the Stream Position and Size | In addition to methods for reading and writing, streams permit applications to seek to an arbitrary position in the stream or change the size of the stream. Once you seek to a specified position, the next read or write operation starts reading from or writing to the stream at that position.
Storing a Custom Variant Type's Data | Variants store their data in the TVarData record type. This type is a record that contains 16 bytes. The first word indicates the type of the variant, and the remaining 14 bytes are available to store the data. While your new Variant type can work directly with a TVarData record, it is usually easier to define a record type whose members have names that are meaningful for your new type, and cast that new type onto the TVarData record type. For example, the VarConv unit defines a custom variant type that represents a measurement. The... more
String Dependencies | Sometimes you need to convert a long string to a null-terminated string, for example, if you are using a function that takes a PChar. If you must cast a string to a PChar, be aware that you are responsible for the lifetime of the resulting PChar. Because long strings are reference counted, typecasting a string to a PChar increases the dependency on the string by one, without actually incrementing the reference count. When the reference count hits zero, the string will be destroyed, even though there is an extra dependency on it. The cast PChar will also... more
Supporting Properties and Methods in Custom Variants | Some variants have properties and methods. For example, when the value of a variant is an interface, you can use the variant to read or write the values of properties on that interface and call its methods. Even if your custom variant type does not represent an interface, you may want to give it properties and methods that an application can use in the same way.
Using a Class to Manage Conversions | You can always use conversion functions to register a conversion unit. There are times, however, when this requires you to create an unnecessarily large number of functions that all do essentially the same thing. If you can write a set of conversion functions that differ only in the value of a parameter or variable, you can create a class to handle those conversions. For example, there is a set standard techniques for converting between the various European currencies since the introduction of the Euro. Even though the conversion factors remain constant (unlike the conversion factor between, say, dollars and Euros),... more
Using a Conversion Function | For cases when the conversion is more complex, you can use a different syntax to specify a function to perform the conversion instead of using a conversion factor. For example, you can't convert temperature values using a conversion factor, because different temperature scales have a different origins. This example, which comes from the StdConvs unit, shows how to register a conversion type by providing functions to convert to and from the base units.
Using Streams to Read or Write Data | Stream classes all share several methods for reading and writing data. These methods are distinguished by whether they: Return the number of bytes read or written. Require the number of bytes to be known. Raise an exception on error.
Using the TCustomVariantType Descendant | In the initialization section of the unit that defines your TCustomVariantType descendant, create an instance of your class. When you instantiate your object, it automatically registers itself with the variant-handling system so that the new Variant type is enabled. For example, here is the initialization section of the VarCmplx unit:
Writing Utilities to Work with a Custom Variant Type | Once you have created a TCustomVariantType descendant to implement your custom variant type, it is possible to use the new Variant type in applications. However, without a few utilities, this is not as easy as it should be. It is a good idea to create a method that creates an instance of your custom variant type from an appropriate value or set of values. This function or set of functions fills out the structure you defined to store your custom variant's data. For example, the following function could be used to create a complex-valued variant:
Commonly Used Long String Routines | The string handling routines in SysUtils handle both UnicodeString and AnsiString types. Within their functional areas, some routines are used for the same purpose, the differences being whether they use a particular criterion in their calculations. The AnsiStrings unit's functions offer the same capabilities as the SysUtils unit's routines. AnsiStrings functions provide better performance for AnsiString than SysUtils functions, because SysUtils functions convert an AnsiString to a UnicodeString. AnsiStrings functions work only with AnsiString, so there is no conversion. The following tables list these routines by these functional areas: Comparison Case... more
Commonly Used Routines for Null-terminated Strings | The null-terminated string handling routines cover several functional areas. Within these areas, some are used for the same purpose, the differences being whether or not they use a particular criteria in their calculations. The following tables list these routines by these functional areas: Comparison Case conversion Modification Sub-string Copying Where appropriate, the tables also provide columns indicating whether the routine is case-sensitive, uses the current locale, and/or supports multi-byte character sets. Null-terminated string comparison routines
Compiler Directives for Strings | The following compiler directives affect character and string types. Compiler directives for strings
Copying a File | The runtime library does not provide any routines for copying a file. However, if you are writing Windows-only applications, you can directly call the Windows API CopyFile function to copy a file. Like most of the runtime library file routines, CopyFile takes a filename as a parameter, not a file handle. When copying a file, be aware that the file attributes for the existing file are copied to the new file, but the security attributes are not. CopyFile is also useful when moving files across drives because neither the RenameFile function nor the Windows API MoveFile function can rename or... more
Declaring and Initializing Strings | When you declare a string:
Deleting a File | Deleting a file erases the file from the disk and removes the entry from the disk's directory. There is no corresponding operation to restore a deleted file, so applications should generally allow users to confirm before deleting files. To delete a file, pass the name of the file to the DeleteFile function:
File Date-time Routines | The FileAge, FileGetDate, and FileSetDate routines operate on operating system date-time values. FileAge returns the date-and-time stamp of a file, or -1 if the file does not exist. FileSetDate sets the date-and-time stamp for a specified file, and returns zero on success or an error code on failure. FileGetDate returns a date-and-time stamp for the specified file or –1 if the handle is invalid. As with most of the file manipulating routines, FileAge uses a string filename. FileGetDate and FileSetDate, however, use a Handle type as a parameter. To get the file handle either: Use the FileOpen... more
Finding a File | There are three routines used for finding a file: FindFirst, FindNext, and FindClose. FindFirst searches for the first instance of a filename with a given set of attributes in a specified directory. FindNext returns the next entry matching the name and attributes specified in a previous call to FindFirst. FindClose releases memory allocated by FindFirst. You should always use FindClose to terminate a FindFirst/FindNext sequence. If you want to know if a file exists, a FileExists function returns True if the file exists, False otherwise. The three file find routines take a TSearchRec... more
Manipulating Files | Several common file operations are built into the runtime library. The routines for working with files operate at a high level. For most routines, you specify the name of the file and the routine makes the necessary calls to the operating system for you. In some cases, you use file handles instead. Warning: Although the Delphi language is not case sensitive, the Linux operating system is. Be attentive to case when working with files in cross-platform applications. The following topics describe how to use runtime library routines to perform file manipulation tasks: Deleting a file Finding a file Renaming a... more
Mixing and Converting String Types | Short, long, and wide strings can be mixed in assignments and expressions, and the compiler automatically generates code to perform the necessary string type conversions. However, when assigning a string value to a short string variable, be aware that the string value is truncated if it is longer than the declared maximum length of the short string variable. Long strings are already dynamically allocated. If you use one of the built-in pointer types, such as PAnsiString, PString, or PWideString, remember that you are introducing another level of indirection. Be sure this is what you intend.
Passing a Local Variable as a PChar | Consider the case where you have a local string variable that you need to initialize by calling a function that takes a PChar. One approach is to create a local array of char and pass it to the function, then assign that variable to the string:
Creating Drawing Spaces | The TCanvas class is defined in the Graphics unit, and encapsulates a Windows device context. This class handles all drawing for forms, visual containers (such as panels) and the printer object (see Printing). Using the canvas object, you need not worry about allocating pens, brushes, palettes, and so on—all the allocation and deallocation are handled for you. TCanvas includes a large number of primitive graphics routines to draw lines, shapes, polygons, fonts, etc. onto any control that contains a canvas. For example, here is a button event handler that draws a line from the upper left corner to... more
Printing | The VCL TPrinter object encapsulates details of Windows printers. To get a list of installed and available printers, use the Printers property. Both printer objects use a TCanvas (which is identical to the form's TCanvas) which means that anything that can be drawn on a form can be printed as well. To print an image, call the BeginDoc method followed by whatever canvas graphics you want to print (including text through the TextOut method) and send the job to the printer by calling the EndDoc method. This example uses a button and a memo on a form. When... more
Using TRegistryIniFile | Many 32-bit Windows applications store their information in the system Registry instead of ini files because the Registry is hierarchical and doesn't suffer from the size limitations of ini files. If you are accustomed to using ini files and want to move your configuration information to the Registry instead, you can use the TRegistryIniFile class. You may also want to use TRegistryIniFile in cross-platform applications if you want to use the system Registry on Windows and an ini file on Linux. You can write most of your application so that it uses the TCustomIniFile type. You... more
Renaming a File | To change a file name, use the RenameFile function:
Returning a PChar Local Variable | A common error when working with PChars is to store a local variable in a data structure, or return it as a value. When your routine ends, the PChar disappears because it is a pointer to memory, and not a reference counted copy of the string. For example:
String to PChar Conversions | Long string (AnsiString and UnicodeString) to PChar conversions are not automatic. Some of the differences between strings and PChars can make conversions problematic: Long strings are reference-counted, while PChars are not. Assigning to a string copies the data, while a PChar is a pointer to memory. Long strings are null-terminated and also contain the length of the string and other information, while PChars are simply null-terminated. Situations in which these differences can cause subtle errors are discussed in the following topics: String dependencies Returning a PChar local variable Passing a local variable as a PChar
Using TIniFile and TMemIniFile | The ini file format is still popular, many configuration files (such as the DSK Desktop settings file) are in this format. This format is especially useful in cross-platform applications, where you can't always count on a system Registry for storing configuration information. The VCL/RTL provides two classes, TIniFile and TMemIniFile, to make reading and writing ini files very easy. TIniFile works directly with the ini file on disk while TMemIniFile buffers all changes in memory and does not write them to disk until you call the UpdateFile method. When you instantiate the TIniFile or TMemIniFile object, you pass... more
Using File Streams | The TFileStream class enables applications to read from and write to a file on disk. Because TFileStream is a stream object, it shares the common stream methods. You can use these methods to read from or write to the file, copy data to or from other stream classes, and read or write components values. See Using streams for details on the capabilities that files streams inherit by being stream classes. In addition, file streams give you access to the file handle, so that you can use them with global file handling routines that require the file handle.
Accessing a Particular String | The Strings array property contains the strings in the list, referenced by a zero-based index. Because Strings is the default property for string lists, you can omit the Strings identifier when accessing the list; thus
Adding a String to a List | To add a string to the end of a string list, call the Add method, passing the new string as the parameter. To insert a string into the list, call the Insert method, passing two parameters: the string and the index of the position where you want it placed. For example, to make the string "Three" the third string in a list, you would use:
Associating Objects with a String List | In addition to the strings stored in its Strings property, a string list can maintain references to objects, which it stores in its Objects property. Like Strings, Objects is an array with a zero-based index. The most common use for Objects is to associate bitmaps with strings for owner-draw controls. Use the AddObject or InsertObject method to add a string and an associated object to the list in a single step. IndexOfObject returns the index of the first string in the list associated with a specified object. Methods like Delete, Clear, and Move operate on both... more
Copying a Complete String List | You can use the Assign method to copy strings from a source list to a destination list, overwriting the contents of the destination list. To append strings without overwriting the destination list, use AddStrings. For example,
Counting the Strings in a List | The read-only Count property returns the number of strings in the list. Since string lists use zero-based indexes, Count is one more than the index of the last string.
Creating a New String List | A string list is typically part of a component. There are times, however, when it is convenient to create independent string lists, for example to store strings for a lookup table. The way you create and manage a string list depends on whether the list is short-term (constructed, used, and destroyed in a single routine) or long-term (available until the application shuts down). Whichever type of string list you create, remember that you are responsible for freeing the list when you finish with it.
Deleting a String from a List | To delete a string from a string list, call the list's Delete method, passing the index of the string you want to delete. If you don't know the index of the string you want to delete, use the IndexOf method to locate it. To delete all the strings in a string list, use the Clear method. The following example uses IndexOf and Delete to find and delete a string:
Locating Items in a String List | To locate a string in a string list, use the IndexOf method. IndexOf returns the index of the first string in the list that matches the parameter passed to it, and returns –1 if the parameter string is not found. IndexOf finds exact matches only; if you want to match partial strings, you must iterate through the string list yourself. For example, you could use IndexOf to determine whether a given file name is found among the Items of a list box:
Iterating Through Strings in a List | To iterate through the strings in a list, use a for loop that runs from zero to Count –1. The following example converts each string in a list box to uppercase characters.
Loading and Saving String Lists | String-list objects provide SaveToFile and LoadFromFile methods that let you store a string list in a text file and load a text file into a string list. Each line in the text file corresponds to a string in the list. Using these methods, you could, for example, create a simple text editor by loading a file into a memo component, or save lists of items for combo boxes. The following example loads a copy of the MyFile.ini file into a memo field and makes a backup copy called MyFile.bak.
Manipulating Strings in a List | Operations commonly performed on string lists include: Counting the strings in a list Accessing a particular string Finding the position of a string in the list Iterating through strings in a list Adding a string to a list Moving a string within a list Deleting a string from a list Copying a complete string list
Working with Lists | The VCL/RTL includes many classes that represents lists or collections of items. They vary depending on the types of items they contain, what operations they support, and whether they are persistent. The following table lists various list classes, and indicates the types of items they contain:
Working with String Lists | One of the most commonly used types of list is a list of character strings. Examples include items in a combo box, lines in a memo, names of fonts, and names of rows and columns in a string grid. The VCL/RTL provides a common interface to any list of strings through an object called TStrings and its descendants such as TStringList and THashedStringList. TStringList implements the abstract properties and methods introduced by TStrings, and introduces properties, events, and methods to Sort the strings in the list. Prohibit duplicate strings in sorted lists. Respond to changes in the contents... more
Wide Character Routines | Wide strings contain two bytes per element and are used in a variety of situations. You may also choose to use wide strings because they simplify some of the string-handling issues in applications that have multiple target locales. Using a wide character encoding scheme has the advantage that you can make many of the usual assumptions about strings that do not work for MBCS systems, which are single byte strings. For wide strings, there is a direct relationship between the number of bytes in the string and the number of elements in the string. In a MBCS string, you have... more
Working with Files | The VCL/RTL supports several ways of working with files. In addition to using file streams, there are several runtime library routines for performing file I/O. Both file streams and the global routines for reading from and writing to files are described in Approaches to file I/O. In addition to input/output operations, you may want to manipulate files on disk. Support for operations on the files themselves rather than their contents is described in Manipulating files. Note: When writing cross-platform applications, remember that although the Delphi language is not case sensitive, the Linux operating system is. When using objects... more
Working with Strings | The runtime library provides many specialized string-handling routines specific to a string type. These are routines for strings (AnsiString and UnicodeString), wide strings (WideString), and null-terminated strings (PChars). Routines that deal with null-terminated strings use the null-termination to determine the length of the string. There are no categories of routines listed for ShortString types. However, some built-in compiler routines deal with the ShortString type. These include, for example, the Low and High standard functions. For more details about the various string types, see the Delphi Language Guide. The following topics provide an overview... more

Copyright(C) 2009 Embarcadero Technologies, Inc. All Rights Reserved.
What do you think about this topic? Send feedback!