Pascal Newsletter #26
The full source code examples of this issue are available for download.
![]() |
![]() |
Pascal Newsletter #26 - 05-SEP-2001 INDEX 1. A FEW WORDS FROM THE EDITOR - Delphi-en forum - Help wanted 2. DIVING INTO DELPHI (and II) - The story of an instance that was not yet an instance - Mechanism of calls to static methods - Mechanism of calls to virtual methods - Some final words - Bibliographical references - Glossary 3. BUILDING A BUSINESS OBJECT 4. DELPHI 6 PERSONAL EDITION FAQ - Why did they call it "Personal"? - Who is it meant for? - Is it free? - If it's free, why is Borland selling it? - Is it open source? - Do I have to distribute my applications under the GNU GPL? - Which components does the Personal Edition come with? - Is there a Delphi 6 Standard Edition? - How do I install Delphi 6 Personal? 5. VCL COMPONENTS - SpeedParser Component - Rapid Expression Parser - PCX Image w/palette support 6. TIPS & TRICKS - Making an application a TCP/IP client - Sharing my experience... · Connecting to a TCP/IP Server from a Delphi client · The Solution I found is... - Aligning text in a StringGrid - Making an application "modal" - Preventing the user from launching other applications 7. DELPHI ON THE NET ________________________________________________________________________ 1. A FEW WORDS FROM THE EDITOR In this issue I'm pleased to introduce the second (and final) part of the article "Diving into Delphi" by Víctor Lorenzo Prado. I'm also glad to welcome new authors to this newsletter like Max Kleiner, Mattias Andersson, Tommy Andersen and S.S.B. Magesh Puvananthiran. Delphi-en forum =============== The Delphi-en forum at Yahoo! Groups is still in formation. While it doesn't have much movement yet, it has been steadily growing over time and now reaches more than 190 members. Please join to increase the number of members and help the group reach the "critical mass" to make the forum alive. http://groups.yahoo.com/group/delphi-en Subscription: http://groups.yahoo.com/group/delphi-en/join delphi-en-subscribe@yahoogroups.com Help wanted =========== For more than a year, each issue of the newsletter has been the work of only one individual. Since the last issue, I'm thankful to have the collaboration of Matthew J. Brock, who revised the English text of the newsletter. This has been a good experience, and now I'd like to make a call for other positions: Delphi Versions Compatibility: This will be a team of Delphi programmers using different versions of the compiler. Their job would be to test the code samples of the articles to see if they work in their version, and if not, to port them if possible. Spanish-to-English translators: His/her job would be to translate the text of articles originally written in Spanish. Links Editors: This will be a small team of web surfers whose mission would be to select the best Delphi articles published each week on the Internet. Editor: Yep, this is my place, not vacant yet, but perhaps in the near future. The job of the editor is to handle part of the relations with the authors, put the pieces together and write the editor's note. Although these jobs don't mean much more than a couple of hours of work per week, the requirement for all positions is a high degree of availability (like half an hour per day or more) because, as you might guess, the newsletter is made in "chains" of small parts... This is all volunteer work: there's no compensation for it, except for the satisfaction of helping spread the knowledge about Delphi and being credited as part of the staff of the newsletter. If you are interested, please don't hesitate to contact me and don't forget to mention the position/s you are applying for and why you are applying for them. Regards, Ernesto De Spirito eds2004 @ latiumsoftware.com __________________ English-language editor: Matthew J. Brock <mjb@delphi-programmer.com> ________________________________________________________________________ JfControls Library. Multi-language. Multi-appearance. Skins. Privileges. More than 40 integrated and customizable components. Impressive GUI. Centralized resources administration. Multiple programming problems solved. For Delphi 3-7 and C++ Builder 3-6. http://www.jfactivesoft.com/ ________________________________________________________________________ 2. DIVING INTO DELPHI (Part II) By Víctor Lorenzo Prado <vlorz@yahoo.es> The story of an instance that was not yet an instance ===================================================== In the previous section we introduced some basic concepts relating to the theory of the programming which is very useful for the professional software developer. It was deemed necessary to abstract their implementation from the internal structure of the classes. In this section, however, we propose to "abstract from abstractions" in order to better understand the operation of classes in Delphi. Let's begin by analyzing the origins of a very common error when programming in Delphi: making calls to methods of an instance, or attempting to access its properties when it has not yet has been initialized and that will help us learn some of the inner workings of the compiler. Let's take as a study case the declaration and implementation of the class "TSampleClass" which can be seen in Listing 1: Listing 1. TSampleClass class. ------------------------------ Interface Type TSampleClass = Class Protected { Protected declarations } { Private fields (data) } FProperty1 : integer; FProperty2 : integer; { Property-access methods } procedure SetProperty2(const Value: integer); function GetProperty2 : integer; Public { Public declarations } { Public methods } function VirtualFunction1 : integer; virtual; function StaticFunction1 : integer; { Public properties } Property Prop1 : integer read FProperty1 write FProperty1; Property Prop2 : integer read GetProp2 write SetProp2; End; { TSampleClass } Implementation { TSampleClass } procedure TSampleClass.SetProperty2(const Value: integer); begin { Assign the value to the property } FProperty2 := Value; end; function TSampleClass.GetProperty2: integer; begin { Return the value of the property } Result := FProperty2; end; function TSampleClass.StaticFunction1: integer; begin { Simply return 0 } Result := 0; end; function TSampleClass.VirtualFunction1: integer; begin { Simply return 0 } Result := 0; end; (Note: The rest of the code that forms the unit has been ommited for the sake of simplicity) (-------- End of Listing 1 --------) Analyzing the listing, we see that a class is being declared which has two properties, "Prop1" and "Prop2", both of the type "32-bit signed integer" type (integer): { Public properties } Property Prop1 : integer read FProperty1 write FProperty1; Property Prop2 : integer read GetProp2 write SetProp2; We see the access to the content of the property "Prop1" is done directly through reading and writing the field "FProperty1." The property "Prop2" is accessed through the property-access methods "GetProp2" (for reading) and "SetProp2" (for writing). In addition to these two properties, the class also exports two methods: { Public methods } function VirtualFunction1 : integer; virtual; function StaticFunction1 : integer; In their implementation, both methods use identical functions and return a null value (zero). However, something curious happens with them; they don't behave equally when they are called for an instance that still "hasn't been created" or instantiated, as it is usually said. Let's see the experiment shown in the program code of Listing 2: Listing 2. Calls to methods without instantiating the class. ------------------------------------------------------------ function TDivingIntoDelphi_II.TestMethodsCalls_1 : integer; var SampleClass : TSampleClass; i : integer; begin { Note that the calls are made without initializing the instance } i := SampleClass.StaticFunction1 + 5; Result := i + SampleClass.VirtualFunction1; end; (-------- End of Listing 2 --------) Taking a glance at the code, it seems this function returns as a result the integer value 5 ("SampleClass.StaticFunction1" should return 0, that added to 5 gives 5 as the result, that added to the result of "SampleClass.VirtualFunction1", also zero, must then result in a value of 5). But it doesn't happen that way! If this function is executed step by step, we see that, in effect, "SampleClass.StaticFunction1" returns 0, but the function "SampleClass.VirtualFunction1" doesn't! Instead of returning 0 this function causes an Access Violation fatal error: Access violation at address 00402DD4 in module 'T4C.exe'. Read of address C08BC300. Why does this happen if the two functions are "exactly equal?" Ah... because they aren't "exactly equal." The method "VirtualFunction1" is declared virtual, whereas the method "StaticFunction1" is static. This makes the call mechanisms,(use of the microprocessor CALL instruction) implemented by Delphi to have access to them totally different. Mechanism of calls to static methods ==================================== Let's begin by taking a look at the operation of the calls to static methods by observing what happens when compiling the code fragment given in Listing 3: Listing 3. Calls to the same method in different instances. ----------------------------------------------------------- procedure TDivingIntoDelphi_II.TestMethodsCalls_2; var { Local-variables declarations } SampleClass1 : TSampleClass; SampleClass2 : TSampleClass; i : integer; begin try { Creation of the instances of the class } SampleClass1 := TSampleClass.Create; SampleClass2 := TSampleClass.Create; { Calls to the methods } i := SampleClass1.GetProperty2; i := SampleClass2.GetProperty2; Result := i; finally { Destruction of the instances } SampleClass1.Free; SampleClass2.Free; end; end; (-------- End of Listing 3 --------) Observe the sections this code fragment has: first, declaration of the local variables (two for the instances of the TSampleClass class and an integer number); a second one where the class instances are created (this particular subject will be discussed below); a third one where for two different instances of the same class the method "GetProperty2" is invoked; and a fourth one for liberating the memory taken by the instances. What's interesting about this code is observing the implementation of the method "GetProperty2" of the class "TSampleClass" given in Listing 1, and how Listing 3 invokes it. If no parameter is passed to the method, then how can program code "know" where are the data whereupon it must operate? Starting off from what was brought up in the first part about the encapsulation in a same entity of data and methods to process it, one might think that for each instance of the class that is created, along with the data space is another copy of the code that must process them. But doing it that way would be a mistake: a total waste of resources. At first this seems to be an "enigma", but the solution used to solve this technical problem is extremely simple: sending as a first parameter a pointer to the zone that contains the data of the instance. Doesn't this parameter appear to NOT be declared in the interface of the method? It's true, it doesn't appear! This is precisely where it is applying the paradigm of the abstraction of the implementation of the mechanisms that make the classes work. But this mechanism isn't totally hidden from the programmer even though it doesn't appear in the declaration of the interface (brilliant, less code to write!), but it can be accessed by means of the parameter "Self". The parameter "Self" is nothing else but a pointer to the instance of the class! Let's see how this works at the level of the assembler code generated by the compiler for the section where the methods are invoked: { Sentence } i := SampleClass1.GetProperty2; { Generated code } MOV EAX,[EBP-$04] CALL TSampleClass.GetProperty2 MOV [EBP-$0C],EAX { Sentence } i := SampleClass2.GetProperty2; { Generated code } MOV EAX,[EBP-$08] CALL TSampleClass.GetProperty2 MOV [EBP-$0C],EAX The code is self-explanatory, even for those who are not used to 32-bit assembly language. Let's dissect this code: * Sentence written in Delphi: i := SampleClass1.GetProperty2; * Prepare the first parameter with the address where the data of the instance are located: MOV EAX,[EBP-$04] * Call to the program code corresponding to the method: CALL TSampleClass.GetProperty2 * Store the returned result: MOV [EBP-$0C],EAX Since the variables that have been declared are local to a procedure, the space for them is reserved on the STACK. That's why EBP-$04 contains the pointer (address) to the data of the instance "SampleClass1"; EBP-$04, to those of "SampleClass2"; and EBP-$0C, the value of "i". First wee began with a static method that doesn't take parameters for the sake of simplicity. But even if the method took parameters, the mechanism would be the same, the first parameter would be a pointer to the data of the instance and then the rest of the parameters. Seeing this, it appears at first glance as the answer to why when the method "StaticFunction1" was called in Listing 2, no error happened, right? There were no errors because no field of the instance was accessed, but if there had been an attempt to access any of the fields there would have been errors. Try, for example, executing step by step the following line of code, observing the instructions in assembly language generated by the compiler: i := TSampleClass(nil).GetProperty2; In this case, the method "GetProperty2" will generate an Access Violation error when trying to access the field "FProperty2". Now note an important detail in the code generated by the compiler for the call to a static method: the kind of instruction used is direct: CALL TSampleClass.GetProperty2 "TSampleClass.GetProperty2" represents the address where the code that implements the method "GetProperty2" is located. This is possible because, in the case of static methods, knowing the class to which the instance belongs is knowing exactly where the address of the code that implements it is located. *** FOR STATIC METHODS, THE TYPE (CLASS) DECLARED FOR THE VARIABLE THAT WILL CONTAIN THE INSTANCE, NOT THE TYPE OF THE CLASS THAT HAS BEEN INSTANTIATED, IS THE ONE THAT DETERMINES WHICH WILL BE THE CALLED METHODS *** Mechanism of calls to virtual methods ===================================== Before explaining the mechanism of a call to virtual methods of an instance, it is important to remember a concept: OOP is not solely ENCAPSULATION of data and methods of processing it in the same entity, but also it is POLYMORPHISM, and the virtual methods are indeed those that allow their existence. By using virtual methods we can create different classes with equal interfaces and behaviors, where the behavior of the instance of the class is determined by the class that has been instantiated and not by the type declared for the variable the instance is assigned to [1]. The mechanism used by Delphi to implement the calls to virtual methods uses the same method to pass the information about the instance for which the method is invoked, but it differs in the way it makes the call. Since the method to call depends on the class that has been instantiated (possibly not known at compile time) and not on the type declared for the variable that stores the instance, the call method cannot be of a direct form. The solution found for this problem is also relatively simple: the use of a Virtual Methods Table (VMT), a table that stores the addresses of the virtual methods associated with the class of which the instance has been created. Is it necessary for each instance to have its own copy of the virtual methods table? No, it's enough that a single virtual methods table exists that is associated with the class and that the instance "knows" where to locate it. The simplest way would be, when creating the instance, to place the address of the virtual methods table as a pointer in one of its fields. Let's take again as study material the code fragment given in Listing 4: Listing 4. Calls to the same method in different instances. function TDivingIntoDelphi_II.TestMethodsCalls_3 : integer; var { Declaration of a local variable } SampleClass1 : TSampleClass; begin try { Creation of an instance of the class } SampleClass1 := TSampleClass.Create; { Calls to the method of an initialized class } Result := SampleClass1.VirtualFunction1; finally { Destruction of the created instance } SampleClass1.Free; end; end; (-------- End of Listing 4 --------) Let's see then the code generated by Delphi to have access to a virtual method: { Sentence } Result := SampleClass1.VirtualFunction1; { Code generated } MOV EAX,[EBP-$08] MOV EDX,[EAX] CALL DWORD PTR [EDX] MOV [EBP-$04],EAX Again, the code is self-explanatory, but let's dissect it: * Sentence written in Delphi: Result := SampleClass1.VirtualFunction1; * Preparation of the first parameter with the address where the data of the instance are located: MOV EAX,[EBP-$08] * Get the address of the virtual methods table: (pointer stored in the first data field of the instance) MOV EDX,[EAX] * Call to the program code corresponding to the method: (first entry in the virtual methods table) CALL DWORD PTR [EDX] * Store the result in a temporary variable on the STACK: MOV [EBP-$04],EAX And now, after seeing this code it is possible to reach another important conclusion: *** TO INVOKE A VIRTUAL METHOD OF AN INSTANCE, IT IS NECESSARY TO HAVE ACCESS TO ONE OF ITS FIELDS, THE VMT, AND THEREFORE VIRTUAL METHODS OF AN INSTANCE CANNOT BE INVOKED IF THE INSTANCE HAS NOT YET BEEN CREATED *** This is the cause of the error that pops up when we run the sample code in Listing 2. To learn more details about the inner workings of the virtual methods table and its structure you can consult the material that Borland offers in [2]. Some final words ================ Do you realize now how to create methods that "generally" do not "explode?" I imagine you do, but just in case, I will give you a hint... Have you noticed that the "Free" of the descendants of "TObject" almost "never" explode? Look to its code, which is as interesting as it is simple. Moral of the story: What is the difference between an instance of a class and a football ball? We can imagine we have a ball rolling in front of us and we can pretend that we kick it and make a goal, and even listen to the cheering of the fans. It doesn't matter how hard we kick it, nobody will be injured nor will there be a broken window. We can simulate that we play with it if we want. But in the case of the instance we cannot play too much with the imagination, because if it doesn't exist, yes, we'll be able to "break some crystal window" and instead of applause we'll get as payment a nasty Windows poster saying "Access Violation... blah blah blah." Bibliographical references ========================== [1] - Borland Software Corporation, "Virtual and Dynamic Methods", Object Pascal Reference (included with the on-line help), 1999. [2] - Borland Software Corporation, "Virtual Method Table", Object Pascal Reference (included with the on-line help), 1999. Glossary ======== CALL - Microprocessor instruction. Call to a subroutine. It allows to execute blocks of code that make tasks that are common to several parts of the program without having to repeat them. ACCESS VIOLATION FATAL ERROR - Error that happens when an application tries to have access to a resource reserved for another application or the operating system itself. INSTANTIATE - To create an instance of a class. To dynamically reserve the memory space necessary to store its fields and the rest of the data that will allow its operation by means of a call to the constructor of the class. STACK - Data structure used by the microprocessors to momentarily save the content of the registers and later be able to restore them after finished working with them. It works according to a logic "Last-In First-Out" (the last in entering is the first in leaving). The STACK also is used by the programs to pass parameters to subroutines and to create local variables. VMT - Virtual Method Table. Table of pointers where the addresses associated to the virtual methods of an instance of a class are stored. ________________________________________________________________________ 3. BUILDING A BUSINESS OBJECT - By Max Kleiner http://max.kleiner.com "UML mit Delphi" ca.370 S., Prize: 40.85 EUR, ISBN-3935042000 What is a business object? -------------------------- To provide business services, a business object works in collaboration with data storage objects and interface objects. A business object transforms data into information with queries or calculations. The data may be acquired from data services or file services. Business objects are sometimes referred to as conceptual objects because they provide services which meet business requirements, regardless of technology. The idea of this article is dedicated to the development of such a business object using an example with InterBase. In a data modules' unit file, you can write methods, including event handlers for the components in the module, as well as global routines that encapsulate business rules. For example, you might write a procedure to perform a fee calculation in a bank and you could call such a procedure from an event handler for a component in the module or from any form that uses the module. In a simple business object (without fields in the class), you do have at least 4 tasks to fulfill: 1. The Business-Class inherits from a Data-Provider 2. The query is part of the class 3. A calculation or business-rule has to be done 4. The object is independent from the GUI: the GUI calls the object 1. We build the class, Superclass can be a TQuery or a TDataModule: type TBusinessObj = class(TQuery) private function open_QueryFee(qryID: integer):boolean; function calcFee(fee: double):double; public procedure changeFee(amount: double); procedure changeLimit(amount: double); end; 2. We define a parameterized query function TBusinessObj.open_QueryFee(qryID: integer):boolean; begin Result := False; try SQL.Clear; SQL.Add('SELECT * from ACCOUNT'); SQL.Add('WHERE acc_no = :pClient_acc'); Params[0].Name := 'pClient_acc'; Params[0].DataType := ftInteger; Params[0].AsInteger := qryID; Open; Result := True; finally // garbage or something end; end; 3. Before we save the amount, we calculate something procedure TBusinessObj.changeFee(amount: double); begin Edit; FieldByName('FEE').AsFloat := calcFee(amount); Post; end; // like a business rule function TBusinessObj.calcFee(fee: double): double; begin result:= (2 * fee) / BANK_FACTOR // just a calc end; 4. Let's go to the client, which has a business object as his member: private ... tblAccount: TBusinessObj; end; procedure TForm1.btnFeeClick(Sender: TObject); begin with tblAccount do begin if open_QueryFee(StrToInt(edtAccount.Text)) then changeFee(strToFloat(edtFee.text)); end; end; As long as we don't have fields in the business object in order to map a relational database in a OO-manner, we can simple typecast the instance, without a constructor. procedure TForm1.FormCreate(Sender: TObject); begin tblAccount := TBusinessObj(Query1); // typecast end; Outlook: -------- When the time comes, you want to map the attributes from a database to the fields of the corresponding class. That means all SQL-statements are encapsulated so you get real OO-access which I will describe in a second part. Let's see an example of a highlight of object database programming, which is similar to Boldsoft with BOLD or gs-soft with MetaBASE: Person := TPerson.Create; oAddressList := TAddressList.Create; try oPerson.Person_ID := 30; oAddressList := TAddressList.DBReadAllRelatedToObject(oPerson); for i := 0 to oAddressList.Count - 1 do S_MBox(oPerson.LastName + ' ' + oAddressList.Adresses[i].Town); finally oPerson.Free; oAddressList.Free; end; ________________________________________________________________________ 4. DELPHI 6 PERSONAL EDITION FAQ Why did they call it "Personal"? ================================ Because its license limits you to use it only for noncommercial use. Any commercial, business, governmental or institutional purposes of any kind are expressly prohibited. They called it "Personal" because it's really "personal". :) You can distribute your works created with Delphi 6 Personal Edition, but may not receive any direct or indirect compensation. This means for example that you can't sell licenses for your software, charge for a tailor-made application, ask for royalties, distribute "adware" applications, etc., etc., etc. "Direct or indirect compensation" is quite comprehensive!!! Who is it meant for? ==================== You can draw your own conclusions. I guess it is meant for hobbyists or people who just want to learn some programming... Surely it is not meant for programmers who expect to get paid from their work, for businesses, or for institutions of any kind (including the government). Is it free? =========== Yes, it's free (gratis), but you have to register. You can download it from the Borland site: http://www.borland.com/delphi/personal/index.html If it's free, why is Borland selling it? ======================================= The download is 140 MB, too much for the Internet connection many people have. Besides, the CD-ROM version that Borland is selling for $99.95 includes a printed manual and installation support. Is it open source? ================== No, it isn't open source, although the source code of some units is available (like windows.pas). Do I have to distribute my applications under the GNU GPL? ========================================================== No, that applies to Kylix Open Edition. With Delphi 6 Personal, if you distribute your applications, you have to distribute them free of charge, but you are not forced to distribute the source code if you don't want to, although you cannot charge for it if you decide to distribute it. Which components does the Personal Edition come with? ===================================================== Delphi 6 Personal Edition comes with 85 components. No DB components, FastNet, QReports, etc. It is very much like Delphi 5 Standard Edition (with some new things, of course). Is there a Delphi 6 Standard Edition? ===================================== Since version 6, there's no Standard Edition of Delphi. If you are interested in developing commercial applications, consider purchasing Delphi 5 Standard Edition. How do I install Delphi 6 Personal? =================================== 1) Download Delphi 6 Personal from the Borland site: http://www.borland.com/delphi/personal/index.html In the third step of the download process don't forget to click the link "Send me a serial number and activation key via email" to receive by email the serial number and activation key that is required to be able to install the product. If you are a member of the Community don't forget first to check that your name, email address and other data in your community account are correct. If you are not a member of the community, ensure you are typing your name, email address, and other data correctly. 2) Run the downloaded file BorlandDelphiPersonalEdition.exe. This will unpack the installer to a folder of your choice (by default it will be C:\Program files\Borland Delphi Personal Installer) 3) Run the installer (INSTALL.EXE) and click on the "Delphi 6" button in the Setup Launcher to start the installation. 4) You'll be prompted for the serial number and authorization key that should have been supplied to you by email. 5) Read the license very carefully. If you agree to the terms of the license, click the corresponding radio button and then click the "Next" button to go on. 6) Proceed with the rest of the installation as usual... 7) At the end you may need to reboot your system. 8) After the installation, you'll find the group "Borland Delphi 6" in your Windows Start menu. Go there and run Delphi 6. 9) You'll be prompted for registration. You'll need an Internet connection to register on-line (the registration process is quite easy for Borland Community members). You can also register by phone or web browser if you have an activation key. I wasn't sent an activation key, so I guess it's available for purchasers of the CD-ROM version. 10) Enjoy! :) ________________________________________________________________________ 5. VCL COMPONENTS SpeedParser Component - Rapid Expression Parser =============================================== - By Mattias Andersson <mattias@centaurix.com> This expression-parser component will rapidly evaluate math formulas and allow you to define your own variables. When evaluating math expressions, speed is quite an issue. Hence, I made an attempt to make an expression parser which would recompile the expression into lists/arrays of instructions and variables. Use the ParseString property to set your expression and then simply call the Parse method to perform the evaluation. If you wish to define your own variables use the AddVar and SetVar methods. Predefined variables are A..Z and Pi. The included demo application uses SpeedParser components to set the RGB values of every pixel in an image depending on expressions that evaluate the X and Y coordinates. The TImage component is not very fast when setting pixels; if you are looking for a fast graphics library you should take a look at Graphics32 by Alex Denisov: http://www.g32.org Mattias Andersson PCX Image w/palette support =========================== - By Tommy Andersen <tommy.andersen@easyware.org> I read the article at http://213.208.2.22/articles/article_2565.asp by Maarten de Haan, and I thought I'd publish my own PCX graphic component which supports palette handling etc. This component does not support saving of PCX images as the one mentioned above, but combine these two, and you'll get a great component! :) Tommy Andersen ________________________________________________________________________ 6. TIPS & TRICKS Making an application a TCP/IP client - Sharing my experience... ============================================================== - By S.S.B. Magesh Puvananthiran <sesbaNOSPAM@hotmail.com> Connecting to a TCP/IP Server from a Delphi client -------------------------------------------------- As every Delphi developer knows, in order to make a Delphi application a TCP/IP client, we can use the TClientSocket component. I faced a problem when I tried to connect to a TCP/IP server (another computer) and send data to that machine and get data back. In the form's OnShow event, I set the Address and Port properties of the TClientSocket component to the TCP/IP server's IP Address and Port number, and set Active to True. Then I tried to send the data in the same event and I was unable to. I checked that the Active property was True (meaning connected). I thought the problem could be with the form's OnShow event, so I put the same code (setting the IP Address and Port number and Active to True) in the FormCreate and OnClick events, but that still didn't solve the problem. The problem was that I tried to connect to the TCP/IP server and send the data at the same time. This doesn't seem to work properly. After that I read the Delphi Help carefully and found the solution. The Solution I found is: ------------------------ First we need to set the IP Address/Port number of the TCP/IP server in the TClientSocket component properties in the main form's OnCreate event and set Active to True. After that we can use the Open and Close methods of the TClientSocket component to connect to and disconnect from the TCP/IP server respectively. If we wish to send data to a TCP/IP server from different forms in a project, we can use a DataModule and put the TClientSocket component there and use it everywhere throughout the project by including that DataModule in all the unit files where we want to use it. Also, in the ClientSocketRead event we need to add a time delay while reading data back from the TCP/IP server. This time delay could be just milliseconds and depends on the network traffic since we may not read all the data sent from the TCP/IP server at a time even if you keep a large buffer. So you may need to wait for several milliseconds between reads. I used the ClientType of the TClientSocket as ctNonBlocking. We can also use ctBlocking as ClientType, but in that case the TCP/IP server should be a threaded one. Even though it seems to be simple stuff, I just wanted to share this with all our friends in Delphi. Thanx. Magesh. Aligning text in a StringGrid ============================= To align text in a string grid you have to assign the OnDrawCell event, calculate the position of the text, and then draw it on the Canvas. The following example centers the text of the first row of a TStringGrid and right-aligns the text of the first column: procedure TForm1.StringGrid1DrawCell(Sender: TObject; ACol, ARow: Integer; Rect: TRect; State: TGridDrawState); var CellText: string; begin with StringGrid1 do if ARow = 0 then begin CellText := Cells[ACol, ARow]; Canvas.TextRect(Rect, Rect.Left + (Rect.Right - Rect.Left - Canvas.TextWidth(CellText) + 1) div 2, Rect.Top + 2, CellText); end else if ACol = 0 then begin CellText := Cells[ACol, ARow]; Canvas.TextRect(Rect, Rect.Right - Canvas.TextWidth(CellText) - 2, Rect.Top + 2, CellText); end; end; Preventing the user for launching other applications ==================================================== If you don't want the users of your application to be able to launch other applications while yours is running, you can use the following function to "lock"/"unlock" your system: procedure ModalApp(Activate: LongBool); var h: cardinal; begin SystemParametersInfo(SPI_SCREENSAVERRUNNING, Cardinal(Activate), @h, 0); h := FindWindow('Shell_TrayWnd', nil); if h <> 0 then if Activate then ShowWindow(h, SW_HIDE) else ShowWindow(h, SW_SHOWNOACTIVATE); h := FindWindow('Progman', 'Program Manager'); if h <> 0 then EnableWindow(h, not Activate); end; Assuming Activate is True, the call to SystemParametersInfo with SPI_SCREENSAVERRUNNING is to "fool" the operating system and make it believe the screensaver is running. This will have the desired side- effect of disabling the ALT+TAB, CTRL+ESC and CTRL+ALT+DEL key shortcuts. The purpose of this is to prevent the user from forcefully closing the application with the Task Manager (the closing of the application is supposedly password protected). Then we find the handle of the taskbar to hide it (we could have disabled it instead) and finally we find the handle of the desktop to disable it. Call ModalApp passing False as parameter to deactivate the effect and return to normal. NOTE: This code probably won't work on Windows NT/2000. ________________________________________________________________________ 7. DELPHI ON THE NET * Delphi Database Programming Course - By Zarko Gajic Free online database programming course for beginner Delphi developers focused on ADO techniques. Two new chapters have been added in the last weeks (Chapter 14 "Charting with Databases" and Chapter 15 "Lookup!"). http://delphi.about.com/library/weekly/aa010101a.htm * Searching for Delphi - By Zarko Gajic Where and how to search for Delphi and Object Pascal programming related materials on the Net. http://delphi.about.com/library/weekly/aa121900a.htm * Delphi Coding Standards and Conventions - By Zarko Gajic Recommendations of standards and conventions for designing, coding, and commenting software projects written in Delphi and Kylix. http://delphi.about.com/cs/standards/index.htm * Component writing, part 2 - By Peter Morris This second part cover how to write advanced properties, how to write custom streaming for those properties, and sub-properties http://www.howtodothings.com/showarticle.asp?article=320 * Screen Shuffling with Delphi - By Zarko Gajic Delphi code that divides the current desktop screen into blocks and then swaps the blocks. It includes an option that lets you adjust the shuffling speed, and the size of the blocks. Great intro to sliding puzzle game or to screen saver development. http://delphi.about.com/library/weekly/aa082801a.htm * Listening to the Clipboard - By Zarko Gajic Extending the clipboard's flexibility and functionality from Delphi. Taking control over the Clipboard with custom formats. Coding Delphi to receive clipboard change notifications. http://delphi.about.com/library/weekly/aa110700a.htm * Delphi Object Hierarchy - By Borland Colorful hierarchy of all objects in all versions of Delphi 5. http://delphi.about.com/library/doh.pdf * Building a stand-alone Web service with Indy - By Dave Nottage This article shows how to build a Web service using Indy and Delphi 6. http://community.borland.com/article/0,1410,27513,00.html * Pente: An introduction to programming strategy games - By Armando de la Torre This article describes the creation of a game known as Pente or Go, a simple strategy game which has similar rules to tic-tac-toe. http://community.borland.com/article/0,1410,27512,00.html * Other people's windows This article demonstrates how to subclass non-Delphi windows from within a Delphi application. http://community.borland.com/article/0,1410,27295,00.html * WebSnap and Web Services hand-in-hand - By Daniel Polistchuck Here's a guide to combining WebSnap and Web Services to create SOAP client web pages. http://community.borland.com/article/0,1410,27691,00.html * Programmers' pets - By Pintér Gábor Every programmer has a favorite application. http://community.borland.com/article/0,1410,26428,00.html * Managing a birthday calendar with InterBase - By Marco Hemmes User-defined functions are the key to remembering everyone's natal anniversary. http://community.borland.com/interbase/0,1419,7,00.html * Untapped resources in Windows - By Lubomir Rosenstein Scanning and creating shortcuts using COM and Delphi. http://community.borland.com/article/0,1410,26174,00.html * Generic handling for any Edit menu - By Steven C. Gudmundson Get out of the repetitive-coding rate rat race with this flexible, reusable code. http://community.borland.com/article/0,1410,26842,00.html * A Simple example of Artificial Intelligence using Delphi Array - By Raghunath Dhungel A Simple example of Artificial Intelligence using Delphi Array. Computer simulates learning process of human, learning by correcting mistakes! http://www.delphi3000.com/articles/article_2551.asp * How to deal with OLE DB directly, using its interfaces - By Alex Wijoyo An example for how to deal with OLE DB directly, using its interfaces. http://www.delphi3000.com/articles/article_2604.asp * Writing a simple ISAPI Filter for IIS - By Daniel Wischnewski This article shows you the basics of creating a simple ISAPI filter and how to use one to map sub-domains into specific folders. http://www.delphi3000.com/articles/article_2646.asp * How to change properties of objects just by name? - By Jürgen Sommer How can I access properties of classes that are not implemented via the uses-clause, just knowing their names (by string)? http://www.delphi3000.com/articles/article_2664.asp * How to create a DataBaseName Property? - By Geers Christophe The following example shows how to create a component with a DataBaseName property like the DataBaseName property of TQuery. The purpose of this property is to list all the available databases so that the user of the component can just select the required database. http://www.delphi3000.com/articles/article_2692.asp ________________________________________________________________________ YOU CAN HELP US We need your help to keep this newsletter going and growing. You can help by referring the newsletter to your colleagues: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php Or you can help by voting for us in some or all of these rankings to give more visibility to our web site and thus increase the number of subscriptions to this newsletter: http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium http://news.optimax.com/delphi/links/links.exe/click?id=70C517ECAE6E http://www.programmingpages.com/?r=latiumsoftwarecomenpascal http://www.top219.org/cgi-bin/vote.cgi?delphi&83 http://top100borland.com/in.php?who=20 http://top200.jazarsoft.com/delphi/rank.php3?id=latium http://213.65.224.200/cgi-bin/toplist.cgi/hits?Id=80 It's just a few seconds for you that REALLY mean a lot to us. ________________________________________________________________________ If you haven't received the full source code examples for this issue, you can get them from http://www.latiumsoftware.com/download/p0026.zip ________________________________________________________________________ This newsletter is provided "AS IS" without warranty of any kind. Its use implies the acceptance of our licensing terms and disclaimer of warranty you can read at http://www.latiumsoftware.com/en/legal.php where you will also find a note about legal trademarks. Articles are copyright of their respective authors and they are reproduced here with their permission. You can redistribute this newsletter as long as you do it in full (including copyright notices), without changes, and gratis. ________________________________________________________________________ Main page: http://www.latiumsoftware.com/en/pascal/delphi-newsletter.php Group home page: http://groups.yahoo.com/group/pascal-newsletter/ Subscribe/join: pascal-newsletter-subscribe@yahoogroups.com Unsubscribe/leave: pascal-newsletter-unsubscribe@yahoogroups.com Problems with your subscription? eds2004 @ latiumsoftware.com ________________________________________________________________________ Latium Software http://www.latiumsoftware.com/en/index.php Copyright (c) 2001 by Ernesto De Spirito. All rights reserved. ________________________________________________________________________ |
The full source code examples of this issue are available for download.
![]() |
Errors? Omissions? Comments? Please contact us!






