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
eds2008 @ 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-2006 & 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.orgk
Mattias Andersson
PCX Image w/palette support
===========================
- By Tommy Andersen <tommy.andersen@easyware.org>
I read the article at http://www.delphi3000.com/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://dn.codegear.com/article/27203
* 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.programmingpages.com/?r=latiumsoftwarecomenpascal
http://top100borland.com/in.php?who=20
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/en/file.php?id=p26
________________________________________________________________________
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? eds2008 @ latiumsoftware.com
________________________________________________________________________
Latium Software http://www.latiumsoftware.com/en/index.php
Copyright (c) 2001 by Ernesto De Spirito. All rights reserved.
________________________________________________________________________
|