Pascal Newsletter #36
The full source code examples of this issue are available for download.
![]() |
![]() |
Pascal Newsletter #36 - 02-JUN-2002 INDEX 1. A FEW WORDS FROM THE EDITOR 2. DELPHI'S SIGNATURE 3. USING ADO IN DELPHI 6 4. USING HTML HELP FILES IN YOUR PROGRAMS 5. INLINE ASSEMBLER IN DELPHI (I) 6. FORUMS 7. DELPHI ON THE NET - Components, Libraries and Utilities . Shareware/Commercial . Freeware - Articles, Tips and Tricks - Tutorials ________________________________________________________________________ 1. A FEW WORDS FROM THE EDITOR In the Pascal Newsletter #34 we announced the release of a Portugese version of this newsletter (the "Boletim Pascal"). Well, today I'm happy to announce the release of the Russian Edition, edited by Mike Shkolnik: * Pascal Newsletter - Russian Edition http://groups.yahoo.com/group/pascal-newsletter-ru/ Subscription: http://groups.yahoo.com/group/pascal-newsletter-ru/join pascal-newsletter-ru-subscribe@yahoogroups.com I'd like to thank the authors for the articles they contributed to this issue and I'm glad to award S.S.B. Magesh Puvananthiran a license of Greatis Print Suite, a set of components for print and preview, provided by Greatis Software: http://www.greatis.com/printsuite.htm In the next issue, one of our collaborators will receive a license of SMImport, a component suite to convert data from all popular formats, provided by Scalabium: http://www.scalabium.com/smi/index.htm We are running a competition for the first two Kylix articles we publish. The prizes will be copies of AnyShape Transpack for Kylix by MindBlast Software: http://www.mindblastsoftware.com/ As many of you probably know, Borland has made an important decision about the future of the BDE. I guess all of us saw it coming... The BDE SQL Links (for accessing data in database servers) will be shipped with Borland products until the end of this year, but will be marked as "deprecated" (no further enhancements or improvements will be made to it). The BDE, without SQL Links, will continue to ship with Borland products, but will be marked as "frozen" (meaning Borland will continue to ship, test, and support existing BDE local table support but no new enhancements will be made to the BDE, and no additional features or bug fixes are planned). You can read more in this Borland Developer Network article written by John Kaster: http://community.borland.com/article/0,1410,28688,00.html How does this decision impacts on your projects? Will you still use the BDE for local databases, or do you plan to migrate them to Interbase using IBX, or choose another solution? What about ADO? Is SQL Links technology still a choice for new projects, or do you plan to migrate to DataSnap Direct (aka dbExpress)? We'd like to hear your comments and plans for the near future. Ernesto De Spirito eds2004 @ latiumsoftware.com __________________ Collaborated in the edition of this issue: Dave Murray and José Manuel Rodríguez. Articles by José Manuel Rodríguez, S.S.B. Magesh Puvananthiran, Dave Murray and Ernesto De Spirito. ________________________________________________________________________ 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. DELPHI'S SIGNATURE By José Manuel Rodriguez (JMR) <jmr@clubdelphi.com> Intro ===== Not long ago, I saw in one of Latium Software's forums a question about how could one tell whether a certain program was compiled with Delphi. Though there were some answers recommending the use of commercial or shareware applications, such as the famous PE Explorer, there was no direct answer to this question. The fact is that there is a way, and it is quite straightforward too: since the times of Delphi 3 onwards, every file, of any kind: GUI executable, Console Application or DLL (including ActiveX Controls but not Packages) has an embedded "Delphi's signature". Windows Resources ================= Most Windows applications contain what are called Resources. Resources were introduced in an early stage of Windows's life due to its visual graphic nature: even though it's perfectly possible to generate the graphic elements needed by an application by pure code (and certainly that's the only way programs such as Resource Editors must deal with them), that's quite awkward and cumbersome. Instead, you design these graphical elements visually with the aid of special programs called Resource Editors (for example, we draw a dialog box in a graphical environment) and then we set down in the appropriate places elements such as buttons, edit windows, etc. Once we think it looks is OK, we save it as a binary resource and now it is ready to be embedded into our EXE. Here, and from now on, by EXE we mean any file that is in the PE (Portable Executable) Format supported by Win32, like EXEs, DLLs, OCXs, etc. Given the fact that some of these types of binary data are used again and again in nearly every application, a series of these binary resources were defined as standard resources: Accelerator table Bitmap Cursor Dialog box Enhanced metafile Font Icon Menu Message-table entry String-table entry Version information Besides these Standard Resources, there is another type of resources: Custom Resources or User Defined Resources. These Resources are defined as needed by the programmer and they consist sometimes of binary data that only makes sense to the programmer or the application, sometimes of well-known formats but which are not part of the standard, for example, JPEG or WAV files. Back to Delphi, one could think that every Form we create in the Delphi IDE would be rendered as the Standard Resource known as "Dialog Box", but it is not so. Instead, due to the "two way technology of the IDE", its way of working with components and storing RTTI, etc, the creators of Delphi decided to store each Form, along with all the components owned by it, as a Custom Resource. But there is more to it, besides including bitmaps, icons, etc and obviously all the Custom Resources derived from the Forms, since version 3.0 Delphi also includes two extra Custom Resources: one containing information about the packages used and another which is really the signature of the programming environment. The later is a Borland specific resource called DVCLAL (Delphi Visual Component Library Access License), and is included automatically by the Linker -with no intervention of the programmer- in every program made with Delphi. In my opinion this term is confusing as Console Apps that don't use the VCL at all have this resource embedded in them too. Resources inside a Program ========================== As we have said before, resources are binary data. In fact, it's precisely these pieces of binary data that are considered to be resources (.RES), and can be generated directly with a Resource Editor, but there are resources in plain ASCII Files too (.RC mainly, but also .DLG, etc.). These "resource source files" need to be compiled with a Resource Compiler (BRCC32) to generate the final .RES mentioned above. The fact is that, once embedded within a PE Files, resources suffer no further modification. This means that we can open any PE File and have a look directly at its resources. If you want to, you can open a PE and with the help of a Resource Editor, translate all of its strings into the language you desire, without needing to have the source code or having to recompile. Windows API has a wealth of functions that allow access to and handle these resources with little effort (some of these routines only do work in NT) and as we will see the Delphi RTL and VCL include functions that are even easier to work with. In our case, to determine whether program has been created with Delphi we have to check for the existence in the PE of a resource called DVCLAL, and if it exists, we can check that resource is indeed a proper "Delphi signature" and it's not a mere coincidence. That being the case we can be assured that the PE has been compiled by Delphi. The reason for these kind of resources and their differences, not only among the various version of Delphi, but even between the Professional and C/S (or Enterprise) releases of the same version of Delphi isn't quite clear to me. Apparently, Borland makes use of these resources, along with a set of variables and routines defined in the Unit SysUtils, not just to be able to determine whether a program has been created with Delphi but to avoid making a fraudulent use of the License terms and forbid, for example, buying the Professional version of Delphi 5 and getting the RTL/VCL libraries for Delphi 5 Enterprise, using them with the Professional version to create programs that otherwise would be only feasible by aquiring the Enterprise version. In Delphi, there are two array variables defined in the Unit SysUtils: AL1s and AL2s. And a set of procedures and functions are also defined: AL1 and AL2 (they seem to be used in the job of encrypting/decrypting), GDAL and ALR (Get Delphi Access License and Access License Resource) and the auxiliary functions RCS and RPS (probably Resources Client/Server and Resource Professional). These functions are called from certain places each time a Delphi module (DLLs and Packages included) is loaded into memory. In case you are not using the proper library with the corresponding IDE, which implies you have no License Rights, the procedure ALV (Access Licence Violation?) is invoked, which will prevent the program from executing. Thus, for instance, GDAL is called by DBTABLES.PAS (which makes it impossible to use the DBE without License Access) or by SCKTCOMP.PAS (which means that trying to make a program TCP/IP enabled with the wrong Delphi libraries will not be allowed). Let's get to work! ================== First at all, we've got to open the file. In order to do so, we are going to use the API call LoadLibraryEx. Why do we use this function instead of the more familiar LoadLibrary? Well, the function LoadLibrary is used primarily to load DLLs into the calling process address, and for this reason, in addition to just loading it, it has to handle the mapping of the DDL in the process address, finding and resolving the entry points and the like. Here, as we are only interested in opening the PE file to read it, there is no need to do those additional tasks so calling LoadLibraryEx with the parameter LOAD_AS_DATAFILE helps us prevent those extra jobs and is noticably quicker and simpler: hModule := LoadLibraryEx(PChar(cModuleName), 0, LOAD_LIBRARY_AS_DATAFILE); hModule being a variable of type THandle. Once we've got the PE file loaded in memory and a handle to reference it, the next step is to examine all the resources till we find the Custom Resources. There are identifiers defined in Windows for all types of resources, for example: RT_BITMAP, RT_STRING, RT_DIALOG, etc. Custom Resource Types are identified globally by RT_RCDATA. To examine those different kinds of resources the Windows API offers the function EnumResourceTypes. This function is an enumeration function whose way of working may sound a little odd for the common Delphi programer. The syntax of the function is: function EnumResourceTypes(hModule: HMODULE; lpEnumFunc: Pointer; lParam: Longint): BOOL; stdcall; The expected parameters are the handle we have got from the call to LoadLibraryEx and a pointer to a callback function. The third parameter is any 32 bit value that we might opt to use for our own purposes (or ignore it). The question that arises is, what does this really mean by a callback function? To put it short, it's just a function that instead of being called directly by us, that is, by our code, we pass it over to Windows and Windows itself will call it for us when appropriate. In the case of the callback of an enumeration function, that means that the enumeration function will call the callback function for each element of the enumeration. Take care that, being the callback function called by the OS, i.e., Windows and not by our code, it must follow the calling convection of Windows, not Pascal's, thus you must make sure that the function is declared as stdcall. Another question that may arise is: what is really the meaning of the second parameter, lpEnumFunc? Could it be any pointer? Well, definitively no, it can't be any type of pointer, it must be a pointer to a function of the type: function EnumResTypeProc(hModule: THandle; szType: PChar; lParam: LongInt): Boolean; stdcall; For each type of resource EnumResourceTypes finds, it will call EnumResTypeProc passing to it the Handle of the Module, the type of the resource it has found and the optional parameter. EnumResourceTypes will keep on enumerating the different resources it finds as long as there are still types of resources left to be enumerated and as long the function returns true, so in our case, we must force our function to return False as soon as the type RT_RCDATA is detected. It's very important that the second parameter mentioned earlier be a pointer to the right type of function. If we look for EnumResourceTypes in Windows SDK, what we really get is: function EnumResourceTypes(hModule: HMODULE; lpEnumFunc: ENUMRESTYPEFUNC; lParam: Longint): BOOL; stdcall; but the type ENUMRESTYPEFUNC is nothing but another name for the type TFarProc, which in turn is another name for a Pointer. As defined, the function is waiting for a pointer and this way any pointer you hand over to it will be accepted at compile-time so you must be extremely careful with what type of pointer you pass. Should it be a pointer to anything else than a pointer to the proper type of function no error will be flagged at compile-time, but, at run-time, consequences may be anything but agreeable, and most probably disastrous. In our beloved Object Pascal, we'd have defined instead the type TEnumResTypeFunc as: function(hModule: THandle; szType: PChar; lParam: LongInt): Boolean; And, any function that would have required a variable of type TEnumResTypeProc as a parameter and were passed another type would flag a clean error at run-time, thus preventing harder to debug and potentially far more dangerous runtime errors. The problem here is that EnumResourceTypes is a Windows API function and as such is written in the C language. The only way for C to support the passing of functions as parameters to another function is through the use of pointers to functions that allow no compile-time checking, contrary to Pascal which in addition to pointers to functions supports the robust functional or procedural types. So our callback function in a first approximation will be something like: function EnumTypes(hModule: THandle; szType: PChar; lParam: LongInt): Bool; stdcall; begin if szType = RT_RCDATA then ... Result := False; else Result := True; end (*EnumTypes*); As we can see, if the type of resource found isn't the expected one (RT_RCDATA) we return True so that the enumeration continues, and if it is the correct type of resource, we process it (we'll fill in the ellipsis later) and we return False in order to stop the enumeration. The call would be: hModule := LoadLibraryEx(PChar(cModuleName), 0, LOAD_LIBRARY_AS_DATAFILE); if hModule <> 0 then EnumResourceTypes(hModule, @EnumTypes, 0) Notice the use of the @ operator to pass the address of (that is, a pointer to) the callback function EnumTypes. So we have now nearly finished the skeleton of our program: open the candidate file and find out whether it has Custom Resources. If it does, then find out whether there's one whose name is DVCLAL. To do this, Windows offers an enumeration function very similar to the one we have just seen, but this time, the function enumerates all the names of the resources of a given type. The syntax of this new function is: function EnumResourceNames(hModule: HMODULE; lpType: PChar; lpEnumFunc: Pointer; lParam: Longint): BOOL; stdcall; And this time, the awaited callback function is prototyped as: function EnumResNamesProc(hModule: THandle; szType: PChar; szName: PChar; lParam: LongInt): Bool; stdcall; Its way of working is quite similar to what we have just seen. Thus, for each type of resource we pass to it, the function will call the callback with each of the names of the resources of that type till all names of that type have been enumerated or till we return False. Since it works exactly the same as we've just seen for EnumResourceTypes, we are not going to detail more in this function. So, our main program would look rather like: function EnumNames(hModule: THandle; szType: PChar; szName: PChar; lParam: LongInt): Bool; stdcall; begin if szName = 'DVCLAL' then begin Boolean(Pointer(lParam)^) := True; Result := False; exit; end (*if*); Result := True; end (*EnumNames*); function EnumTypes(hModule: THandle; szType: PChar; lParam: LongInt): Bool; stdcall; begin if szType = RT_RCDATA then Result := EnumResourceNames(hModule, szType, @EnumNames, lParam) else Result := True; end (*EnumTypes*); function IsDelphiModule(const cModuleName: String): Boolean; var hModule: THandle; begin Result := False; hModule := LoadLibraryEx(PChar(cModuleName), 0, LOAD_AS_DATAFILE); if GetLastError = 0 then EnumResourceTypes(hModule, @EnumTypes, LongInt(@Result)) else RaiseLastWin32Error; end (*IsDelphiModule*); As you can see, it consists mainly of a two-level nest of functions. For each candidate PE file, the first thing we do is to call LoadLibraryEx to obtain a handle. Next, we call EnumResourceTypes with that Handle and these functions in turn calls EnumType with whatever type of resource it finds. If the type is precisely RT_RCDATA, then EnumTypes in turn calls EnumResourceNames which in turn call EnumNames with the names of the resources of type RT_RCDATA it has found. If the name of the resource passed to EnumNames is DVCLAL we assume that our seek is done and that the file is created by Delphi so we stop the enumeration. Really, we ought to read the RAW DATA contained in the resource and make sure that is really a Delphi signature and not a coincidence. To do that we would need to know how to access a determinate resource and also the exact structure and meaning of a Delphi signature. Accessing a determinate resource will not be covered in this article but in a future one, and as for the structure of DVCLAL, I must admit I haven't guessed it yet, though a little discussion follows bellow. Here we have employed a trick (I'd call it a smart technique, but that might be because I have no grandmas to praise me). If you remember, there was an optional 32 bits wide parameter, well, given the fact that EnumResourceTypes is being called from a function that returns a Boolean, what we pass in this parameter is a pointer to this value, what would be equivalent to passing it by reference (in the C language all parameters are passed by value, so to simulate a parameter being passed by reference you have to resort to passing a pointer to it). This way as this parameter is "dragged" all along the nesting, we have a reference to the value that is going to be returned by the main function so we can set its value at will and we do so when we find a resource type of RT_RCDATA whose name is DVCLVAL. As far as the binary structure of a DVCLAL is concerned, I have little to say about it. I have determined empirically some values for the C/S or Enterprise releases of some versions of Delphi but I haven't had enough spare time to decypher it nor have I found anywhere where I could get this information from. These are the values I've found: For Delphi 3.xx Client/Server DVCLAL RCDATA { 'A2 8C DF 98 7B 3C 3A 79 26 71 3F 09 0F 2A 25 17' } For Delphi 4.xx, 5.xx & 6.xx Enterprise: DVCLAL RCDATA { '26 3D 4F 38 C2 82 37 B8 F3 24 42 03 17 9B 3A 83' } An additional problem I haven't mentioned is that at least Borland C++ Builder 4.xx and 5.xx seem to have the same signatures as their Delphi counterparts, so it's possible to identify wrongly a PE file created with C++ Builder as being created by Delphi or vice versa. Maybe the information contained in the RT_RCDATA Resource called Package Info gives additional information that can help us to solve the mess, but at the time of writing this I haven't been able to solve it either. And ... what's next? ==================== By now we have got conversant with Windows Resources and learnt how to examine any PE file and know the resources it has. To tell the truth, if all we needed was to test for the existence of a well known resource and access to its content, we could have skipped the enumerations and have gone directly to it. The Windows API (with FindResource and LoadResource) and above all, the VCL (with its class TResourceStream) can both make this task incredibly easy. However, this way we are prepared not only to check for the "Delphi signature" but to access all of the resources of a given PE file. In a future article, we'll learn how to examine all the resources of a PE File, and if it is Delphi made, even how to access the forms and show them, even though they are Custom Resources and Standard. José Manuel Rodriguez (JMR) __________________ NOTE: Any suggestions, corrections or constructive criticism would be welcome at the author's e-mail address: jmr@clubdelphi.com. All the code mentioned in this article is original by the author and is available as a demo app in the zip file bundled with this e-zine. ________________________________________________________________________ 3. USING ADO IN DELPHI 6 By S.S.B. Magesh Puvananthiran <sesbaNOSPAM@hotmail.com> This article is intended to demonstrate how can we use the ADO components available in Delphi. I have written a simple application using ADO components to retrieve the Data Source Names, table names, field names, procedure names and an option to write a query, execute it and display the result in a grid. The function of the application: When you run the application, it'll fetch all the ODBC Data Source Names from the current system and show them in a list box. If you select a Data Source Name, you will be asked to enter the user name and password. Once you enter the right user name and password, the tables and procedures available in the data source will be listed. And if you click on a table name, all the fields in the table will be listed. In the memo field you can enter an SQL query and click on the Execute button to execute the query and display the result in the grid below. Also you can save the query to a text file if you click on the Save button. If you right click the Data Source Names list box, you'll find a Refresh menu item to refresh the ODBC Data Source Names. The complete code for the application is attached. This is really a simple version of a Query Builder and we can add as many features as we want, and I just wanted to share it with you. Even though there are so many query builders available, I wanted to try writing one with Delphi's ADO components and I am going to expand this by adding more features. I welcome your ideas and suggestions for this. Thanks. Magesh. ________________________________________________________________________ 4. USING HTML HELP FILES IN YOUR PROGRAMS By Dave Murray <irongut @ vodafone.net> Do you long to move from WinHelp to HTML Help in your programs? The unit attached (dmHTMLHelp.pas) converts WinHelp calls to HTML Help enabling you to upgrade with the minimum of effort. This article is based on an article I wrote for Delphi3000.com: http://www.delphi3000.com/articles/article_2994.asp Save this unit in a directory on your Library path (Tools|Environment Options|Library|Library Path) and add it to your project uses clause. WinHelp requests will now be translated to HTML Help calls so you can use your help file just as you previously did. Specify your *.chm file in the Project Options|Application|Help file setting. Context sensitive help will work as it previously did. You can also use TApplication.HelpCommand to send help commands. Eg. Application.HelpCommand(HELP_KEY, DWORD(keyData)); NOTE: * This unit assigns its own handler to the Application.OnHelp event. DO NOT assign your own handler to Application.OnHelp. * This unit ignores any form's HelpFile property. (Delphi 4+) ________________________________________________________________________ 5. INLINE ASSEMBLER IN DELPHI (I) By Ernesto De Spirito <eds2004 @ latiumsoftware.com> This article simply intends to introduce you to the world of inline assembler in Delphi. It will barely give you an idea, and it doesn't intend to explain all the details of assembler programming, that will probably require an entire book... Why and when ============ If you take a look at the source code of the RTL and the VCL, you'll see inline assembler statements in various places. Why would Borland code parts of the RTL and the VCL in assembler? The answer is quite simple: to achieve execution speed. We know the compiler produces fast code, but a compiler can never be better than a professional assembler programmer. Now, if assembler is so good, why isn't the entire RTL and VCL coded in assembler? The answer is also simple: because a higher level programming language is easier to code, debug, read, and maintain, making it worth to sacrifice some speed for this convenience. This would help explain when assembler code should be used. To put it bluntly, apart from low-level system access, inline assembler is used when the difference in speed justifies the bother of coding in assembler. For example, in the unit Math.pas there is a lot of assembler, mainly for low-level system access (specifically, to access coprocessor features), and you'll see many assembler blocks, this time for speed, in system.pas, sysutils.pas, and classes.pas, which isn't strange since they can be considered the core units of the RTL and the VCL. In general, procedures and functions that are very likely to be called quite often in a program, should be highly optimized, but coding in assembler should be avoided to the maximum extent possible. If we want speed, before considering coding in assembler we should first improve the algorithm, and then optimize our Pascal code. If we decide to go for assembler, the optimized Pascal code would be useful for documentation purposes, and it might act as some sort of "backup code" in case in the future we have problems in maintaining the assembler source code. The CPU registers ================= CPU registers are like predefined variables, but they reside in the CPU, and sometimes they have special purposes. They don't have a type, but you can consider them as signed or unsigned 32-bit integers, or as pointers, depending on the case. Since registers are located in the CPU, it is faster to access values stored in the registers than values stored in memory, so registers are used very much to cache values. Like variables, registers have names. The names of the registers we will use most are EAX, EBX, ECX, EDX, ESI, EDI, EBP and ESP. Each register has its own particularities: - For some instructions, the CPU is optimized for using the EAX register (also known as "accumulator"), or at least the opcodes are smaller. EAX is used in multiplications and divisions (low order 32 bits of the operand and the result), string instructions, port I/O instructions, ASCII adjust and decimal adjust instructions, and in some special instructions (like CDQ, LAHF, SAHF, XLAT). - EBX is a general-purpose register, and is implicitly used by XLAT. - ECX (also known as "counter") has a special use with the LOOP instruction, bit rotating and shifting instructions, and string instructions. - EDX is used in multiplications and divisions (high order 32 bits of the result of a multiplication, or high order 32 bits of both the dividend and remainder of a division) and in some other special instructions (like CDQ). - ESI and EDI (known as the "source index" and "destination index" respectively) are like pointers used by string instructions to identify the source and destination of data, respectively. - EBP (known as the "base pointer") is usually used for addressing values in the stack (parameters and local variables). - ESP (known as the "stack pointer") is used to control the stack. It is modified automatically by instructions like PUSH, POP, CALL and RET, but can be modified by code and it can even be used as a general purpose register (as long as we preserve it). The registers EBX, ESI, EDI, EBP, and ESP should be preserved in inline assembler blocks, meaning that before using these registers we should save their values somewhere (usually the stack or another register), and when we are finished we should restore their original values. Since this saving and restoring implies instructions and therefore time, we'll only use these registers when the difference justifies it or when they are inevitably needed. Probably you noticed all register names start with the letter "E". It stands for "Extended". In the old Intel 80286, registers had 16 bits and had names like AX, BX, CX, etc. These registers still exist, and they are the least significant 16-bits (the "low words" or "low order words") of EAX, EBX, ECX, etc., respectively. By the way, the registers AX, BX, CX and DX are divided into two 8-bit registers. AL, BL, CL and DL are the low order bytes of AX, BX, CX and DX respectively, while AH, BH, CH and DH are the high order bytes of AX, BX, CX and DX respectively. For example, if the value of EAX is $7AFD503C, then the value of AX is $503C, the value of AH is $50 and the value of AL is $3C: 7A FD 50 3C AH AL /----/ AX /------------/ EAX If for example we store $99 in AH, then the value of EAX would be $7AFD993C. There is a special register, the Flags Register, which holds binary flags set by the mathematical and logical instructions, or explicitly by code, and they are used by conditional jump instructions. The Carry Flag is also used in some bit rotating instructions, and the Direction Flag is used in string instructions. This register isn't addressable by name like the other registers, but it can be saved and restored from the stack using the PUSHF and POPF instructions respectively, and it can also be partially get in the AH register using the LAHF instruction, and restored from AH using the SAHF instruction. Assembler instructions ====================== Assembler instructions are enclosed in asm..end blocks, and have the form [label:] [prexix] opcode [operand1 [, operand2 [, ...]]] The opcode is the name of the instruction, like MOV, ADD, PUSH, etc. Instructions can be separated by semicolons, line breaks or comments. By the way, comments should be in Object Pascal style, i.e. the semicolon is not the start of a comment till the end of line as it is in ordinary assembler. The following, is an example of an asm..end block mixing different kinds of comments and instruction separations: asm xchg ebx, edx; add eax, [ebx]; {semicolons separate statements} // line breaks separate statements mov ebx, p sub eax, [ebx] (*comments separate statements*) mov ebx, edx end; The convention is to use line breaks for separation, like this: asm xchg ebx, edx add eax, [ebx] mov ebx, p sub eax, [ebx] mov ebx, edx end; In the source code of the VCL you'll see that opcodes and register names are written in capital letters, and instructions are indented eight characters, but we won't use this convention here. Asm..end blocks can occur anywhere in the source code where a Pascal sentence can occur, and we can write 100% assembler procedures and functions if use "asm" instead of "begin", like this: procedure test; asm // assembler statements end; Notice that these two implementations are not the same: function f(parameters): type; begin asm // assembler statements end; end; function f(parameters): type; asm // assembler statements end; The reason is that the compiler performs certain optimizations when we implement procedures and functions completely in assembler (without using the begin..end block). Labels should be declared with a Label section, as in any other Object Pascal code, unless they are prefixed by "@@": function IsMagicNumber(x: integer): boolean; asm cmp eax, MagicNumber je @@Bingo xor eax, eax ret @@Bingo: mov eax, 1 end; Labels prefixed by "@@" are local to the inline assembler block in which they are used. This will generate a compiler error: begin .... asm .... @@destination: .... end; .... asm .... jnz @@destination // Error .... end; .... end; To correct it, we need to use a conventional label, local to the procedure or function: label destination; begin .... asm .... destination: .... end; .... asm .... jnz destination // Right .... end; .... end; Operands ======== Sometimes an operand or operands are implicit. For example, the instruction CDQ (Convert Dword to Qword) apparently takes no operands, but it works with EDX and EAX (it extends the most significant bit of EAX, the "sign" bit, into EDX, so EDX:EAX will represent the integer in EAX converted to Int64, where EAX will hold the 32 least significant bits, and EDX will hold the 32 most significant bits). For most instructions, operands can be registers. For example mov eax, ecx copies the value of ECX in EAX. Many operands can be immediate values. For example: mov eax, 5 mov eax, 2 + 3 // Constant expression, resolved at compile time mov al, 'A' // The ASCII code of 'A' is $41 (65) mov eax, 'ABC' // Equivalent to MOV EAX, $00414243 Many operands can be memory references. For example: mov [ebx], eax // EBX^ := EAX; Memory references can have many forms: mov eax, [$000FFFC] // Absolute address mov eax, [ebx] // Register mov eax, [ebp-12] // Register plus/minus constant offset mov eax, [ebp+ebx] // Register plus offset in a register mov eax, [ebp+ebx+8] // Register plus offset in a register // plus/minus constant offset mov eax, [ebp+ebx*4] // Register plus offset in a register // multiplied by a constant mov eax, [ebp+ebx*4+8] // Register plus offset in a register // multiplied by a constant, // plus/minus constant offset The use of Pascal identifiers is translated to one of the forms above: mov eax, parameter // mov eax, [ebp + constant_offset] mov eax, localvar // mov eax, [ebp - constant_offset] mov eax, globalvar // mov eax, [absolute_address] call procname // call absolute_address First example ============= We are ready to learn some opcodes with a couple of examples. We can begin with a simple function: function f(x: integer; y: integer): integer; // f(x,y) = (-x-y+5)*7 { begin Result := (-x - y + 5) * 7; end; } asm // Parameters are passed in EAX (x) and EDX (y); neg eax // EAX := -EAX; // EAX = -x sub eax, edx // EAX := EAX - EDX; // EAX = -x-y add eax, 5 // EAX := EAX + 5; // EAX = -x-y+5 imul 7 // EAX := EAX * 7; // EAX = (-x-y+5)*7 end; The first three parameters (left to right) are passed in EAX, EDX and ECX. For methods, the first parameter is Self (passed in EAX), and the first parameter explicitly declared is in fact the second parameter (passed in EDX), and the second explicit parameter is actually the third parameter (passed in ECX). The return value should be placed in EAX for 32-bit ordinal values (AX and AL should be used to return 16-bit and 8-bit ordinal values respectively). The comments explain the opcodes quite well, but for IMUL we have to add two things: * IMUL considers the operands (EAX and 7 in the example) as signed integers (we should use MUL when the operands are unsigned) * The result of the multiplication is a 64-bit integer (the most significant 32 bits of the result are placed in EDX). Multiplications are quite expensive in terms of CPU time, and sometimes it is faster to substitute them with bit shifting (when we multiply or divide by a power of two), additions and subtractions. For example: a * 7 = a * (8 - 1) = a * 8 - a = a * 2^3 - a a * 7 = a shl 3 - a Instead if IMUL 7, we can do the following: mov ecx, eax // ECX := EAX; // ECX = -x-y+5 shl eax, 3 // EAX := EAX shl 3; // EAX = (-x-y+5)*8 sub eax, ecx // EAX := EAX - ECX; // EAX = (-x-y+5)*8 - (-x-y+5) // EAX = (-x-y+5)*7 Let's see another example: function remainder(x: integer; y: integer): integer; // Returns the remainder of x divided by y { begin Result := x mod y; end; } asm // Parameters are passed in EAX (x) and EDX (y); mov ecx, edx // ECX := EDX; // EDX = y cdq // EDX:EAX := Int64(EAX); // EAX = x idiv ecx // 32-bit signed integer division: // EAX := Int64(EDX:EAX) div integer(ECX); // EDX := Int64(EDX:EAX) mod integer(ECX); mov eax, edx // Result := EDX; // remainder end; The stack ========= When a program is loaded, it gets assigned a stack, which is a memory region used as a LIFO (Last In, First Out) structure, controlled by the ESP register, which points to the stack top. ESP starts pointing to the end of the region, so every time we push a 32-bit value, the ESP register gets subtracted 4 (bytes), and the value is stored in the location pointed by ESP. | | +-----------+ | | +-----------+ | $01234567 | <- ESP +-----------+ | | PUSH $89ABCDEF // SUB ESP,4; MOV [ESP],$89ABCDEF | | +-----------+ | $89ABCDEF | <- ESP +-----------+ | $01234567 | +-----------+ | | Conversely, when we pop a 32-bit value, the value is retrieved from the location pointed by ESP, and ESP is added 4 (bytes). POP EAX // MOV EAX,[ESP]; ADD ESP,4 | | +-----------+ +-----------+ | $89ABCDEF | EAX | $89ABCDEF | +-----------+ +-----------+ | $01234567 | <- ESP +-----------+ | | The stack is used to store the return address of procedures and functions, parameters, local variables and intermediate results. In the following example we use the stack to save the value of a register for later use: function IntDiv(x: integer; y: integer; r: pinteger = NIL): integer; // Returns the integer quotient of x / y, and the remainder in r { begin Result := x div y; if r <> NIL then r^ := x mod y; end; } asm // Parameters are passed in EAX (x), EDX (y) and ECX (r) push ecx // Save ECX (r) for later use mov ecx, edx // ECX := EDX; // ECX = y cdq // EDX:EAX := Int64(EAX); // EAX = x idiv ecx // 32-bit signed integer division: // EAX := Int64(EDX:EAX) div integer(ECX); // EDX := Int64(EDX:EAX) mod integer(ECX); pop ecx // Restores ECX (ECX := r) cmp ecx, 0 // if ECX = NIL then jz @@end // goto @@end; mov [ecx], edx // ECX^ := EDX; // remainder @@end: // local label (preceded by "@@") end; Notice that for every PUSH we make we have to perform a POP, so the value of ESP is left unchanged (ESP is one of the registers we have to preserve). The CMP instruction subtract the second operand from the first (ECX-0 in this case), like the SUB instruction, but the result is not stored anywhere, although the Zero flag will be set (turned on) or cleared (turned off) depending on whether the result is zero or not, as it happens with all mathematical and logical instructions (except in certain cases). We can take advantage of this fact, and instead of writing cmp ecx, 0 we can write or ecx, ecx // ECX := ECX or ECX; The result of ECX Or ECX is ECX itself, so the value stored in ECX is the same it had before the operation, but -like we said above- the Zero flag will be set if the result is zero (i.e., if ECX was zero). We use OR instead of CMP because OR operates on two registers, just taking 2 bytes to code, while CMP operates a register with an immediate 8-bit value, taking three bytes to code, but CMP doesn't actually write to the destination (ECX in this case) like OR does, which is sometimes important when writing code optimized for Pentium. TEST ECX, ECX is usually preferred because it combines the best of both worlds (two bytes to code and doesn't write to the register: just performs a bitwise AND operation to set the flags based on the result, which is discarded). JZ (Jump if Zero), goes (jumps) to the label indicated as operand if the Zero flag is set (on), or continues with the normal execution flow it the Zero flag is cleared (off). Passing parameters on the stack ------------------------------- Let's go back to the stack. We said the first three parameters of a function were passed on EAX, EDX and ECX, but what if we have or more parameters? Additional parameters are passed on the stack, left to right, so the last parameter, will be the first on the stack. Let's suppose we have a function function Sum(a, b, c, d, e: integer): integer; begin Result := a + b + c + d + e; end; and that we want to make the call Sum(1,2,3,4,5); In assembler, it would be like this: mov eax, 1 mov edx, 2 mov ecx, 3 push 4 push 5 call Sum The CALL instruction pushes the return address on the stack and jumps to (starts executing) the function. The RET (RETurn) instruction (generated by the compiler when the end of the function is reached) pops this address from the stack and jumps to it to continue the execution from that point. Notice that we pushed the parameters on the stack, but didn't pop them. This is because, except in the CDECL calling convention, cleaning up the parameters is responsibility of the called function, not the caller. To clean up the parameters, the RET instruction is used with an operand indicating the number of bytes ESP should be incremented, 8 in this case (ESP was decremented 4 bytes per parameter when they were pushed). The compiler takes care of this, so we don't have to bother about it, but if you see the CPU debug window and you wonder what's that RET $08, now you know what it is. Upon entry to Sum, the stack would in theory like this: | | +-----------+ | Ret_Addr | <- ESP +-----------+ | $00000005 | (parameter e) +-----------+ | $00000004 | (parameter d) +-----------+ | | When a function has parameters in the stack (or local variables), the compiler generates a few instructions that are called a "stack frame". Upon entry to the function (in the "asm"), EBP is pushed on the stack (to preserve it) and ESP is assigned to it, and before leaving the function (in the "end;"), the original value of EBP is popped from the stack: function Sum(a, b, c, d, e: integer): integer; asm // push ebp; mov ebp, esp; .... end; // pop ebp; ret 8; Thus, when we enter Sum, the stack would actually look like this: | | +-----------+ | Orig. EBP | <- EBP, ESP +-----------+ | Ret_Addr | +-----------+ | $00000005 | <- EBP+8 (parameter e) +-----------+ | $00000004 | <- EBP+12 (parameter d) +-----------+ | | At [EBP] we find the original value of EBP that was pushed on the stack to preserve it when building the stack frame, at [EBP+4] we find the return address of the procedure, and at [EBP+8] we have the last parameter (the last parameter is pushed last, and therefore it's the first on the stack). The next parameter (right to left) is at [EBP+12] and so on if we had more parameters. Now let's write the function Sum in assembler: function Sum(a, b, c, d, e: integer): integer; { begin Result := a + b + c + d + e; end; } asm add eax, b add eax, c add eax, d add eax, e end; Notice that in the asm..end block we used "b", "c", "d" and "e" instead of "EDX", "ECX", "[EBP+12]" and "[EBP+8]" respectively. We can do that because the compiler will make the appropriate substitutions. Local variables on the stack ---------------------------- If our function has local variables, in complete inline assembler functions the compiler will make space for them in the stack moving the stack pointer, so the stack frame for a function with two integer local variables would look like this: push ebp mov ebp, esp sub esp, 8 // Moves ESP as if we pushed 8 bytes ... add esp, 8 // Moves ESP as if we popped 8 bytes pop ebp For the purpose of the example, here is a variant of the Sum function introduced above, but using two local variables: function SumL(a, b, c, d, e: integer): integer; var f, g: integer; { begin f := b + c; g := d + e; Result := a + f + g; end; } asm // push ebp; mov ebp, esp; sub esp, 8; add edx, ecx mov f, edx // b + c mov edx, d add edx, e mov g, edx // d + e add eax, f add eax, g end; // add esp, 8; pop ebp; ret 8 Within this function, the stack would look like this: | | +-----------+ | var. g | <- EBP-8, ESP +-----------+ | var. f | <- EBP-4 +-----------+ | Orig. EBP | <- EBP +-----------+ | Ret_Addr | +-----------+ | Param e | <- EBP+8 +-----------+ | Param d | <- EBP+12 +-----------+ | | What's next =========== In the continuation of this article we'll learn more instructions, and we'll see how to pass and return other kind of parameters, how to work with arrays, how to access record and object fields, how to call methods, and more. ________________________________________________________________________ 6. FORUMS Delphi ====== If you know a lot about Delphi but you are still far from being a guru this forum is for you. This is the only forum for intermediate-level Delphi programmers on the Web (Delphi experts are also welcome :-)). The forum now has more than 700 members and during April had traffic of just over 450 messages: 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 can also subscribe by email: http://groups.yahoo.com/group/delphi-en/join delphi-en-subscribe@yahoogroups.com Components ========== This is a forum for searching/recommending software components (VCL and CLX components, ActiveX objects, DLL libraries, shared objects, etc.), as well as utilities, tutorials, information, etc. The forum is rather new and currently has about 175 members and very low traffic: http://groups.yahoo.com/group/components I hope you join the forum to help us build a larger group. You can subscribe from the web or --more easily-- by email: http://groups.yahoo.com/group/components/join components-subscribe@yahoogroups.com Software Developers =================== This is a forum for discussions about software development and to share experience in the work, professional or commercial environments. It is not a programming forum, matters treated here are supposed to be more general or language independent. The forum is rather new and currently has about 160 members and very low traffic: http://groups.yahoo.com/group/software-developers I hope you join the forum to help us build a larger group. You can subscribe from the web or --more easily-- by email: http://groups.yahoo.com/group/software-developers/join software-developers-subscribe@yahoogroups.com ________________________________________________________________________ 7. DELPHI ON THE NET By Dave Murray <irongut @ vodafone.net> Components, Libraries and Utilities =================================== Shareware/Commercial -------------------- * llPDFLib v1.1 - by llionsoft, Shareware ($70, $280 with source) llPDFLib is pure Object Pascal library for creating PDF documents. Does not use any DLL and external third-party software to generate PDF files. Library consists of TPDFDocument component with properties and methods like Delphi's TPrinter but designed to generate a PDF file. http://www.llion.net/ * TPasScript v6.52 - Alexander Baranovsky, Shareware($80) (DELPHI/KYLIX) An interpreter of the Object Pascal language which supports all Object Pascal data types except interfaces. The TPasScript component allows you to embed the interpreter into your Delphi application, so you can extend and customize the application without having to recompile it. Using TPasScript you can: write and run scripts, call script-defined routines, call routines in a dll, import Delphi-defined classes, routines and variables. http://users.ints.net/virtlabor * MaxSpace - by Zecos Software, Shareware ($15) MaxSpace makes using Delphi and C++ Builder more comfortable It turns the IDE ToolBar and ObjectInspector into autohide toolbars, giving you unbelievable freedom for program editing and form designing. http://www.zecos.com/maxspc Freeware -------- * TRealSpinEdit v1.1 - Igor Popov, FREEWARE with source (DELPHI/KYLIX) Based on Visual C++ TSpinEdit, for all who need Value to be a float type. Now with negative value support. http://igp.dax.ru * FastReport CLX v2.46 - A Tzyganenko, FREEWARE w source (DELPHI/KYLIX) The first powerful cross-platform report generator for Delphi + Kylix. Create highly efficient cross-platform reports for with a minimum of coding. Development environment includes: Visual Report Designer, Visual Dialog Designer, Object Inspector, and Component Palette. http://www.fast-report.com * DBDateTimePicker - by Stefano Carfagna, FREEWARE with source Database aware DateTimePicker component for "DateTime" fields. http://www.data-ware.it * LIBXML libxml2 v2.4.21 - by Delphree, FREEWARE w source (DELPHI/KYLIX) Pascal units accessing the popular XML API from Daniel Veillard (http://www.xmlsoft.org). Usable from Kylix and Delphi and hopefully also from other Pascal compilers like FreePascal. http://sourceforge.net/projects/libxml2-pas/ * LIBXML libxslt v1.0.17 - by Delphree, FREEWARE w source (DELPHI/KYLIX) Pascal units accessing the popular XML API from Daniel Veillard (http://www.xmlsoft.org). Usable from Kylix and Delphi and hopefully also from other Pascal compilers like FreePascal. http://sourceforge.net/projects/libxml2-pas/ * Key Objects Library v1.27 - by Delphree, FREEWARE with source Based on experience aquired on eXtreme Class Library project. Main purpose is to create smaller programs using Delphi environment. http://bonanzas.rinet.ru/ * DARTH: C-to-Pascal header converter - by JEDI, FREEWARE with source Source compiles into a tool that parses C headers and generates Delphi source for Windows API calls. Articles, tips and tricks ========================= * The Future of the Borland Database Engine (BDE) and SQL Links The Borland RAD Team reveals plans for future database access in Delphi and C++Builder. You need to read this if you haven't already! http://community.borland.com/article/0,1410,28688,00.html * Top Picks: Reporting Tools - by Zarko Gajic Easily create complex reports that are linked directly into your EXE. Most of the tools from this list provide all the means for you to develop reports including a report engine, designer, previewer, etc. http://delphi.about.com/library/toppicks/aatpreporting.htm * GDI Graphics in Delphi - by Alistair Keys From simple lines to direct API calls: the ultimate tutorial to GDI graphics in Delphi. Look for: drawing lines and shapes, flicker-free drawings, off-screen bitmaps, GDI drawings the API way... http://delphi.about.com/library/bluc/text/uc052102a.htm * Choosing the best Help Format for your project - by Meredith Little No developer wants to work on documentation but it is a necessary evil. This article explores the concept of developing help and describes the available help formats. http://builder.com.com/article.jhtml?id=u00220020429lit01.htm * Choose the right Tool for creating Online Help - by Meredith Little Creating online help is often seen as a burden, but many tools are available to simplify the process. Here's a look at some choices. http://builder.com.com/article.jhtml?id=u00220020430lit01.htm * Functional vs. Design in documentation - by William T. Kelly Do you need a design or functional specification? It depends on who your audience is and where you are in the product development cycle. http://builder.com.com/article.jhtml?id=u00420020503WTK01.htm * How to get the text of a StatusBar? http://www.swissdelphicenter.ch/en/showcode.php?id=935 * How to make a polygon hole on a form? http://www.swissdelphicenter.ch/en/showcode.php?id=995 * How to get the associated icon of a file shortcut? http://www.swissdelphicenter.ch/en/showcode.php?id=1108 * How to list all Subdirectories of a Directory? http://www.swissdelphicenter.ch/en/showcode.php?id=1125 * How to get the Windows Version? http://www.swissdelphicenter.ch/en/showcode.php?id=1126 * How to color text in a TRichEdit? http://www.swissdelphicenter.ch/en/showcode.php?id=1129 * How to use the IP Address Control in a Form? http://www.swissdelphicenter.ch/en/showcode.php?id=1132 * How to display the exported Dll functions? http://www.swissdelphicenter.ch/en/showcode.php?id=1133 * How to display the 'Choose Computer' dialog? http://www.swissdelphicenter.ch/en/showcode.php?id=1135 * How to use steganography? http://www.swissdelphicenter.ch/en/showcode.php?id=1139 * How to resolve environment variables? http://www.swissdelphicenter.ch/en/showcode.php?id=1140 * How to determine, if a Edit field has password characters? http://www.swissdelphicenter.ch/en/showcode.php?id=1141 * How to determine the user's logon domain? http://www.swissdelphicenter.ch/en/showcode.php?id=1142 * How to highlight HTML-Tags in TRichEdit? http://www.swissdelphicenter.ch/en/showcode.php?id=1143 * How to store more than 64 KB in a TRichEdit? http://www.swissdelphicenter.ch/en/showcode.php?id=1144 * How to select a TreeView node with the right mouse button? http://www.swissdelphicenter.ch/en/showcode.php?id=1145 * How to assign an event handler to all components? http://www.swissdelphicenter.ch/en/showcode.php?id=1148 * How to crack a URL into its component parts? http://www.swissdelphicenter.ch/en/showcode.php?id=1149 * How to delete the 'Temporary Internet Files'? http://www.swissdelphicenter.ch/en/showcode.php?id=1150 * How to get the windows file type? http://www.swissdelphicenter.ch/en/showcode.php?id=1153 * How to get the grayscale from a RGB color? http://www.swissdelphicenter.ch/en/showcode.php?id=1154 * How to convert a partition/file size to a String? http://www.swissdelphicenter.ch/en/showcode.php?id=1155 * How to detect the default smtp mailserver? http://www.swissdelphicenter.ch/en/showcode.php?id=1156 * How to implement Forward/Back/Cancel buttons in TWebbrowser? http://www.swissdelphicenter.ch/en/showcode.php?id=1158 * How to format Seconds as (hh:mm:ss)? http://www.swissdelphicenter.ch/en/showcode.php?id=1163 * Catch debug information of an application - by Tommy Andersen Ever wanted to read an application's debug information? http://www.delphi3000.com/articles/article_3216.asp * Hide application in Windows 2000 - by Eber Irigoyen Hiding your application from the taskbar and "Applications". http://www.delphi3000.com/articles/article_3220.asp * Retrieve email addresses from Outlook mails - by Alain Godinas-Andrien http://www.delphi3000.com/articles/article_3222.asp * Maping / Unmaping virtual drives - by Eber Irigoyen Two functions that let you create Virtual Drives for commonly used folders (local or network folders) so they appear on "My Computer" as a new drive for quick access. Like DOS SUBST command. http://www.delphi3000.com/articles/article_3224.asp * What Custom Compiler messages can do for you - by Yoav Abrahami Create reminders that generate compiler warnings and errors. http://www.delphi3000.com/articles/article_3228.asp * How do you subclassing a versatile TList? - by Max Kleiner You can use a TList almost for everything, so an own class leads to better design and maintainability so this article shows how and why. http://www.delphi3000.com/articles/article_3229.asp * Modifying Contents of the Taskbar from Delphi - by Igor Kurilov Using ITaskbarList interface. http://www.delphi3000.com/articles/article_3230.asp * Easy Parsing an XML File - by Max Kleiner How do you get the elements from an XML file? Using MS XML DOM parser. http://www.delphi3000.com/articles/article_3231.asp * Using Autohint - by David Knight Using hints without the flybys that cover data and often annoy users. http://www.delphi3000.com/articles/article_3234.asp * Function DBGridToHtmlTable - by Zswang Wangjihu http://www.delphi3000.com/articles/article_3236.asp * Loading resources from DLL files - by Martin Strand http://www.delphi3000.com/articles/article_3238.asp * Getting System Power Information - by Magnus Flysjö Reteriving battery information for notebooks. http://www.delphi3000.com/articles/article_3241.asp * Is font "X" installed? - by Eber Irigoyen http://www.delphi3000.com/articles/article_3242.asp * Get the application associated to an extension - by Eber Irigoyen http://www.delphi3000.com/articles/article_3243.asp * DLL in Dephi to be called from Visual Basic - by Khaled Shagrouni Creating a DLL in Dephi to be called from another language like VB. http://www.delphi3000.com/articles/article_3244.asp * Get the Number of Print Jobs - by Andreas Schmidt Get the number of jobs in the windows spooler for a specific printer. http://www.delphi3000.com/articles/article_3247.asp * MySQL without the commonly required components - by Me Me MySQL the easy way without BDE or ODBC. http://www.delphi3000.com/articles/article_3248.asp * Remove horizontal and/or vertical scrollbar of TDBGrid - G Christophe Get rid of those pesky scrollbars on TDBGrid when you don't need them. http://www.delphi3000.com/articles/article_3254.asp Tutorials ========= * Kylix Tutorial Series - by Brian Long Ten part tutorial series previously published in Linux Format. http://www.blong.com/articles.htm#KylixTut * Remedial XML: Say HELLO to DOM - by Lamont Adams Get acquainted with W3C's Document Object Model (DOM) in this part of the remedial XML series. A language-neutral look at how you can put this model to work. http://builder.com.com/article.jhtml?id=u00220020522adm01.htm * A Beginner’s Guide to Delphi Programming - by Zarko Gajic FREE online Delphi programing course for beginners, now up to Part 3. http://delphi.about.com/library/weekly/aa031202a.htm Other Links =========== * VCL Scanner 2002 Sweepstakes winners - by Anders Ohlsson Thanks to all who participated in the VCL Scanner 2002 project! http://community.borland.com/article/0,1410,28665,00.html ________________________________________________________________________ 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. Don't forget we also need articles for this newsletter and there is a prize for one of the authors in each issue. All articles will be considered but we are particularly interested in articles about Kylix because there is so little available online to help Kylix developers. Send articles to <eds2004 @ latiumsoftware.com>. We are also looking for shareware authors who would like to offer their components or applications as prizes for articles in the newsletter. In return you will be promoted in this newsletter and the Latium Software web site. For more information contact Dave <irongut @ vodafone.net>. ________________________________________________________________________ If you haven't received the full source code examples for this issue, you can get them from http://www.latiumsoftware.com/download/p0036.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) 2002 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!






