Pascal Newsletter #39
The full source code examples of this issue are available for download.
![]() |
![]() |
Pascal Newsletter #39 - 13-SEP-2002 Contents 1. A few words from the editor 2. In the news - Delphi 7 is here! - Delphi for .NET compiler preview - Kylix 3 gets 5 stars from LinuxPlanet - Upcoming Borland Events - Delphi Fireworks Component Contest 3. Understanding VisualCLX - What are hook-objects in a Qt application? 4. Creating High Performance Middleware Applications with Indy 5. Inline Assembler in Delphi (III) - Static Arrays 6. Forums / mailing lists 7. Delphi on the Net - Components, Libraries and Utilities . Freeware - Articles, Tips and Tricks - Tutorials - Other links ________________________________________________________________________ 1. A few words from the editor First of all, I'd like to apologize for the delay in publishing this issue, but I've been buried in work these past weeks. I hope #40 gets published in three weeks. In this issue, I'd like to thank Max Kleiner for contributing another article to the newsletter, and I'm glad to award him a license of AnyShape Transpack v2.0 for Kylix, the cross-platform component, that eases the creation of transparent, weirdly shaped windows with WYSIWYG editing, design-time preview, automatic dragging, REAL stay-on-top forms, and the ability to combine regions, provided by MindBlast Software: http://www.mindblastsoftware.com/?page=transpack&ref=PascalNL By the way, the author of the next Kylix article we publish will also receive a license of AnyShape Transpack v2.0 for Kylix. I'd also like to thank Romeo Lefter for his article about Indy, and I'm happy to award him a Delphi Information Library CD (DIL CD), a great information resource with articles, tips, tricks, components, javascripts, images, update packs, and much more, provided by the UK Borland User Group: http://www.richplum.co.uk/html/dil.asp For the next issue, we have available the following prizes for our contributors: * 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/ * Greatis Form Designer v3.4 - by Greatis Software, Shareware ($49.95) It's a runtime form designer that allows you to move and resize any control on your form. You don't need to prepare your form to use Form Designer. Just drop TFormDesigner component onto any form, set Active property to True and enjoy! For Delphi 4-7 and BCB 3-6. http://www.greatis.com/formdes.htm * Developer Information Library (DIL) CD - by UK Borland User Group Over 17,000 Tips, Tricks, FAQs and Technical Articles ˇ Patches and Updates for Borland Tools ˇ Over 4000 Components & Tools ˇ Over 4000 Bitmaps ready to use with another 20000 zipped ˇ Over 350 ready to use JavaScripts ˇ Complete Set of Linux How-tos ˇ and much much more... http://www.richplum.co.uk/html/dil.asp Ernesto De Spirito eds2004 @ latiumsoftware.com __________________ Collaborated in this issue: Dave Murray ________________________________________________________________________ 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. In the news Delphi 7 is here! ================= Delphi 7 is among us, called Delphi 7 Studio, and it comes in four editions: * Delphi 7 Studio Architect ($3,499 - Upgrade from Enterprise: $2,399) http://www.borland.com/delphi/architect/index.html * Delphi 7 Studio Enterprise ($2,999 - Upgrade: $1,899) http://www.borland.com/delphi/delphi_enterprise/index.html * Delphi 7 Studio Professional ($999 - Upgrade: $399) http://www.borland.com/delphi/delphi_professional/index.html * Delphi 7 Studio Personal ($99 - available for free download) http://www.borland.com/delphi/delphi_personal/index.html The new Architect edition is very much like the Enterprise edition, but it comes with additional tools, including the new Bold for Delphi from BoldSoft, which allows developers to maintain less code with true MDA, plus it comes with UML technology, integrated support for Rational Rose, ModelMaker, import/export model information from/to Bold Model Editor, and automatic database schema generation using SQL. DataSnap (formerly known as MIDAS) is royalty free in both the Enterprise and the Architect edition, and is also included in the Professional Edition. Except for the Personal edition, all Delphi 7 Studio editions include Kylix 3 for Delphi environment (allowing developers to use one code base to leverage the power of cross-platform development for Linux) and the Delphi 7 Studio Migration Kit (to migrate applications to Microsoft .NET). Delphi 7 links: * Main page http://www.borland.com/delphi/architect/index.html * System requirements http://www.borland.com/delphi/pdf/del7_sysreqs.pdf * FAQ http://www.borland.com/delphi/pdf/del7_faq.pdf * Feature Matrix http://www.borland.com/delphi/pdf/del7_feamatrix.pdf * Delphi 7/.NET User Group Tour 2002 - US / Canada Borland is taking Delphi 7 and the Delphi Preview for Microsoft .Net on the road in the US and Canada. http://bdn.borland.com/article/0,1410,29089,00.html Delphi for .NET compiler preview ================================ Included with Delphi 7, you can find a preview of the Delphi for .NET compiler, a command-line utility named "dccil.exe", which produces Common Intermediate Language (CIL) applications that can run anywhere the .NET run-time is available as fully managed applications. This means that Delphi applications can now move beyond their traditional Windows/Intel platform to any other platform that has a .NET runtime, such as the .NET compact framework available for tablet PCs, phones, and PDAs. * Delphi for .NET compiler preview - By John Kaster A first look at the Delphi for .NET compiler features and Delphi's new language syntax http://bdn.borland.com/article/0,1410,28972,00.html * Delphi for .NET Preview: Samples This web site provides sample applications for the Delphi for .NET preview compiler. These sample applications are produced by members of the Borland community, and Borland staff. http://dotnet.borland.com * Using Delphi as a script language for ASP.NET - by John Kaster A preview of Delphi for .NET support inside ASP.NET http://bdn.borland.com/article/0,1410,28974,00.html Kylix 3 gets 5 stars from LinuxPlanet ===================================== * Kylix 3: Borland's Linux Delphi and C++ RAD is a Winner - By Steven J. Vaughan-Nichols Now, however, Kylix answers the needs of most Linux programmers by fully supporting C++ with the same development environment. The result is a RAD that should quickly become Linux's most popular integrated development environment (IDE). http://www.linuxplanet.com/linuxplanet/reviews/4427/1/ * Taking Kylix 3 for a test drive - By Joe Barr In short, Kylix 3 has allowed me to develop this simple C++ GUI app for X in just a few days. Given how little C++ and X development experience I have, and the fact that I've never worked with pipes except from the command line, that's pretty good time. Kylix has demonstrated to me it deserves its RAD label. http://www.idg.net/go.cgi?id=738304 Upcoming Borland Events ======================= * Entwickler Konferenz (EKON6), Morfeldfen/Frankfurt Germany, September 22-27, 2002 Deutsch - http://www.entwicklerkonferenz.de/ English - http://www.entwicklerkonferenz.com/ * BorCon Europe, London UK, October 28-29, 2002 http://www.borconeurope2002.com/ * BorCon Japan, Tokyo Japan, November 19-20, 2002 http://www.borland.co.jp/ * BorCon France, Paris France, November 21-22, 2002 http://info.borland.fr/conference/2002/ Delphi Fireworks Component Contest ================================== The webmaster of http://chuckr.freeshell.org/index.php has started a Fireworks Component Contest. The prize? Awe and recognition from your peers, just for entering. Also it makes a great resume entry. Here are the rules: - You must create a Delphi component which shows fireworks. - The component must be a visual component, installable on the palette, and contained in one .PAS file. - You must submit all source and the EXE file to the contest. - The component must use only standard Delphi objects. No DirectX. OpenGL will be allowed since quite a few graphics cards and Windows versions support it. You may use graphics accelleration libraries. - Submissions must be received by Oct 5, 2002. Judging will then begin. - Submissions will be judged on elegance of implementation, appearance, features (see next), and speed. - Some suggested, but optional features: user can change number of explosions on screen at the same time, user can change color of fireworks, user can select different explosion shapes, different explosion types (some real fireworks have a second stage explosion), etc. - Sound is optional. Entries should be submitted to <chuckr69 @ fastmail.fm>. ________________________________________________________________________ IBAdmin 3.2 - Complete Interbase SQL tool - A powerful DBA/Development tool for managing Interbase servers and databases. IBAdmin provides many capabilities to help with your DB design and management. You can use the Database Designer to visually design the database structure, the Grant Manager to manage users, or the SQL Debugger which can be used to debug stored procedures and triggers. Comfortable SQL code editing with Code- Insight and Code Completion. >>>>>>>>> http://www.sqlly.com/ibadmin2.htm ________________________________________________________________________ 3. Understanding VisualCLX - What are hook-objects in a Qt application? By Max Kleiner <max @ kleiner.com> Kleiner Kommunikation http://max.kleiner.com/ VisualCLX is the part of CLX that represents the Visual Components that would normally reside in the TWinControl hierarchy in the VCL. VisualCLX framework is a set of classes that represent Visual Controls but have to work (if possible) on both MS Windows and X in Linux. The controls represented by the VisualCLX components are implemented by a C++ class library called Qt and widgets, from the Norwegian development company called Trolltech. Qt is also available on Windows. - The VCL TWinControl class is called TWidgetControl Qt is a C++ class library, cause of differences in C++ and OP (Object Pascal) details, an OP program cannot directly manipulate Qt widgets. Instead, VisualCLX makes use of an additional library, called the Qt interface library (written in C++ as libqtintf.so) which exports all the Qt functionality in a manner that is accessible to OP code. The import unit for this interface library is called Qt.pas but this means that rather than being declared as classes, the Qt widget methods are all imported as flat methods or strictly speaking functions. We define a flat method as a method of a class that is declared as a standalone subroutine or function. However, since at the C++ side they are indeed classes, almost every flat method takes one extra parameter, which is the reference to the Qt widget. You think this might slow applications but most of the time you won't measure any difference in run-time behavior. So what's the difference in a architectural manner? In a known OP application, you call methods via object references, e.g.: myButton.setBounds(15, 15, 65, 35); Turning the method into a flat method, the object reference is passed as the first parameter so the method code knows which instance it should be invoking. Here is a "it goes almost like this" example flat method, which is equivalent to the method just used: QButton_SetBounds(myButton, 15, 15, 65, 35); or in a Kylix Qt-manipulation: uses Qt, QTypes; var Btn: QButtonH; Btn := QButton_create(Handle, PChar('Btn')); QButton_setGeometry(Btn, 15, 15, 65, 35); Of course you would normally have no need to write code like this as a QButton does it all for you, but it serves as a simplified example of how CLX components do their thing by using the CLXDisplayAPI. What's the CLXDisplayAPI ------------------------ The CLXDisplay API is the official name for the Qt.pas unit that ships with Kylix and also with Delphi6 or later. It acts as an import unit for the Qt widget library used by VisualCLX. So things in life are a bit more complicated than this. Qt is a C++ class library, and OP cannot direct manipulate C++ classes. Because of this, Borland wrote an additional library to lay between a CLX application and the Qt library. This extra library is called libqtintf.so (the Qt interface library), and Qt.pas is actually the import unit for this interface library. TWidgetControl--->Qt.pas->libintf.so--->Qt_Widget_Classes Understanding Signal/Slot mechanism ----------------------------------- A hook object is a simple C++ object that exists in the Qt interface library as an intermediary. So you want to customize the reaction of a widget like in windows with event handlers, signal/slot play this role: - A signal (event) from a widget - A slot (event handler) responds to a signal So we've learnt, it's not possible to have the slot written directly in OP, means the Qt interface library defines a hook class for each widget class. The hook class implements a simple slot for each available widget signal, whose sole job is to call some code in our Kylix application. More on signal / slot and the Way Kylix does events --------------------------------------------------- So it seems that messages (callback functions) are not the CLX way of doing things, it doesn't mean that CLX provides no support of messages, but it's not the Kylix way of doing things. We suggest, e.g. mouse movements, that you let CLX respond to the mouse and simply override the methods that CLX uses for those events. Creating a component and need to catch the mouse messages, you can use the following method: procedure MouseMove(shift: TShiftState; X, Y: integer); override; The way we have to think is that in Qt, developers do not respond directly to messages. Instead they work with a signal / slot mechanism and the connect function like: QObject::connect(timer, SIGNAL(timeout)), SLOT(timerSlot())); timer -> start(1000); or another example to get accustomed to: QObject::connect(myslider, SIGNAL(sliderMoved(in)), mylcdNumber, SLOT(display(in))); There is nothing special about the SliderMoved and Display methods. Just ordinary C++ methods that are marked as signals and slots, just as some Kylix methods are marked as being virtual. QObject is the base class in Qt, just as TObject is the base class in OP (Object Pascal). QObject has a class or static method named connect. An OP class method is the same thing as a C++ or Java static method. In particular, you can call a class or static method without first creating an instance of the object to which it belongs. And where's the event-loop in Kylix? Here is the event loop that lies at the center of CLX applications: procedure TApplication.HandleMessage; Hooks again and Overview ------------------------ Fact: So you learned that Qt uses a signal and slot mechanism, and CLX uses an event mechanism. It's not so important how the two are connected, it might be valuable some time later, but here is an overview: Qt has a signal and slot mechanism. CLX has an event mechanism. To translate Qt signals and slots into CLX events, the Kylix team created a mechanism known as hooks. Each CLX object type has a hook object. This hook object converts the signals and slot events associated with a particular object into CLX events. It then sends these events to the appropriate CLX control. In particular, there is a CLX method of TWidgetControl named EventFilter that receives the majority of these events. You can find more on this topic on the Kylix2 CompanionTool CD: sams_publishing/kdgch07.pdf chapter 7 "CLX architecture & Visual Development" or Borland Code Central Entry ID #1679. Here an impressive extract: If you feel the urge to go beyond the usual CLX API, then here is one of the methods that you want to override: function TWidgetControl.EventFilter(Sender: QObjectH; Event: QEventH): Boolean; This one is the big Kahuna. EventFilter gets most of the events that Qt and the OS throws at it. Just opening up QControls and looking at the 500+ lines that form the implementation of this method is enough to send any sane programmer running for the safety of the standard CLX APIs. However, some people like to live on the edge. They claim that the air is thinner but cleaner out there. function TWidgetControl.MainEventFilter(Sender: QObjectH; Event: QEventH): Boolean; cdecl; var Form: TCustmForm; begin try if csDesigning in ComponentState then begin Form := GetParentForm(Self); if (Form <> nil) and (Form.DesignerHook <> nil) and Form.DesignerHook.IsDesignEvent(Self, Sender, Event) then begin Result := True; Exit; end; end; Result := EventFilter(Sender, Event); except Application.HandleException(Self); Result := False; end; end; __________________ Reference: http://www.delphi3000.com/article.asp?ID=3311 ________________________________________________________________________ When was the last time you voted for the Pascal Newsletter? Please support this initiative voting for us in The Programming Top 100! http://www.sandbrooksoftware.com/cgi-bin/TopSite2/rankem.cgi?id=latium ________________________________________________________________________ 4. Creating High Performance Middleware Applications with Indy By Romeo Lefter <rombest @ hotmail.com> Rombest Software http://www.vreau.com Middleware is one of the coolest technologies that are now on the market. Unfortunately, ready to use tools for this technology costs much. This technology is associated with databases because it is intensely used in this field, but it is not limited with databases. Using this technology, you can create a plethora of applications that use "thin" clients. Typically, a middleware framework looks like below: +-------+ +----------+ +--------+ |Clients| <<-Connection1->> |Middleware| <<-Connection2->> |Database| | | | server | | server | +-------+ +----------+ +--------+ In the image, the [Clients] represents your thin clients, the [Middleware server] is your application server and [Database Server] is the database. Conforming to this model, clients, the middleware server and the database server runs on different machines(the database server and middleware server can run on the same machine). Connection1 and Connection2 are the connections between our parts. The middleware architecture is the best way to make serious economies. In example, the MS SQL Server need suplimentary license for each client. Also, it needs something named "Client Access License" (I think). All of this costs money. With a single client license (+ Client Access License) you can build a middleware server and then work with many clients, without paying additional licenses. And this solution works fine! Related to middleware I want to say here that I don't like Midas, COM, DCOM, COM+ etc. There are many middleware solutions on the market. A little part of them represents cool solutions, but expensive. There are solutions that are easy to use but, unfortunately, slower. The problem that I will discuss here is about how to create a free middleware application. This is possible and, believe me, superior in performance than other competitors. First, let's review the tools we need for this job. 1. Indy http://www.nevrona.com/indy You need Indy. Because it is simple to use and very fast. There are other commercial packages on the market that are faster than Indy, but working with them is not so easy. For me, Indy is one of the best network packages on the market. 2. KbmMemTable: http://www.onelist.com/community/memtable This is the best Memory Table that is now on the market. It is thread safe, it's content (records and structure) can be save on to disk or stream, supports transactions, compressed blobs and much more. Best of all, it is free with source code. => A short description of TKbmMemTableFeatures As I have said, the kbmMemTable threading model is one of the best. You have to set up just few properties and your table can safely work in a multithreading environment. First property is AttachedMaxCount. It is an integer property and it stores the maximum number of memtables that can be attached to this table. Attaching process is cool. When you attach a memory table (let's name it A) to another table (B) all the data contained in B table are available for the A table. More than simply view, the A table can add or update records and the results are reflected in the B table. In a multi threaded environment, this is the best model. Another characteristic of KbmMemTable is it's possibility to save it's data to streams or files. This is very good for what we want to do. In the last version, TKbmMemTable has two helper components: kbmBinaryStreamFormat and TkbmCsvStreamFormat that help us to establish a common "stream language" for the server and for the client. As you will see, SavetoStreamviaFormat and LoadFromStreamviaFormat are the most used methods in our environment. It's time now to discuss the protocol. First, for security reasons, you need a authentication part. So, we have to implements 2 commands: Login and Logoff. The login command is sent to the server with 2 parameters: the user name and the password, like below: login user pass If the pair (user pass) corresponds to the data that are stored on the server, the user is able to work, else it is disconnected. When the user wants to terminate it's session, it sends to the server the Logoff command, without any parameter. In this moment, the server will disconnect the client. In order to make this example simple and portable, I will work here with paradox tables, that will emulate our database server. Also, for easy portability, I will use queries. In this application I will use the country.db table, that is in your DBDEMOS alias. The clients that will connect to our middleware server will be able to: - add records to this table - get entire table For adding records to table, we will implement an command (Add) with five parameters: Name, Capital, Continent, Area, Population. So, the command will look like bellow: Add p1 p2 p3 p4 p5 For getting the table we need just a command without parameters (Get). It's work time, so let's start. Open a new project, put a Memory table and a TIdTcpServer on it. The memory table will be used for the login process, so you have to create 2 string fields for it:user and password. You have to implement some procedures for adding, deleting and updating users. Also, on the FormCreate and FormClose events you have to load/save persistent data for this table(Using LoadFromBinaryFile/ LoadFromCsvFile and SaveToBinaryFile/SaveToCsvFile methods). I have decided to use a memory table for handling user authentication because it is the best in speed and because it's thread-safe mechanism works perfectly. I have decided to have the following architecture: On the MainForm: usrs: A KbmMemTable used for authentication (users table) Server: A TIdTcpServer component, our server MThread: A TIdThreadMgrPool component (I'll use for this example a pool threaded with 100 pool size) For easy understand, I'll use CommandHandlers Enabled in this projects, so we have to define 4 commands: -Login -Logoff -Get -Add Because our pool size is 100, the AttachedMaxCount property of the AuthTable has to be 100. Also, for easy understanding we have to put all "database interface" components in a DataModule. So, create a datamodule and put some components on it, like below: Query1: A TQuery component that will interface with the database, in our case database is DBDEMOS Session1: A TSession component used for safe transactions logintable: A memTable that will be attached to AuthTable in order to verify if the pair (user,password) is correct buffertable: another memTable used for data exchange Turn the AutoCreateForm to off for datamodule (in Project|Option). Now we will look in depth on the server model. As I have said, the user has to login on the server. When the user send the LOGIN command the server will do the following steps: 1. Creates the datamodule; 2. Attach the logintable (from the datasource) to AuthTable (that is on the MainForm); 3. Verify if the pair (user,password) is valid; 4. Sends back to the user the authentication Result: - If the number of parameters is lower than 2 (i.e. the command looks like login myName), the server sends ('101 - Wrong number of params! Good bye!') and disconnects the user; - If the pair (user, password) is valid, the server sends a message like ('201 - Ok, you are now in the system, man!') and the client will remain connected; - If the pair (user, password) is not valid, the server sends a message like ('102 - Sorry, invalid user or password. Good bye!') and disconnect the user. A little code will be more explicit if you haven't understand what I have said. So, look below: procedure TForm1.serverCommandHandlers0Command(ASender: TIdCommand); var ClientDataModule:TDatas; loginFlag:boolean; begin // Login command // Format login <<user>> <<password>> if ASender.Params.Count<2 then begin ASender.Thread.Connection.WriteLn( '101 - Wrong number of params! Good bye!'); ASender.Thread.Connection.Disconnect; end; // Create the dataModule.. Its owner is the actual Connection! ClientDataModule:=TDatas.Create(ASender.Thread.Connection); // Assign for the session component an unique name ClientDataModule.Session1.SessionName := 'ClientSession' + Inttostr(ASender.Thread.ThreadID); ClientDataModule.logintable.AttachedTo := usrs; ClientDataModule.logintable.Active := True; ClientDataModule.Query1.SessionName := ClientDataModule.Session1.SessionName; // Ok, now we will verify if the (user,password) is valid if not ClientDataModule.logintable.Locate('User', ASender.Params[0],[]) then loginflag:=false else if ClientDataModule.logintable.FieldByName('Password').AsString = Asender.Params[1] then loginFlag:=true else loginFlag:=false; if loginFlag then Asender.Thread.Connection.WriteLn( '201 - Ok, you are now in the system, man!') else begin Asender.Thread.Connection.WriteLn( '102 - Sorry, invalid user or password. Good bye!'); ASender.Thread.Connection.Disconnect; end; end; At first view, it is little "strange" for you the mode we have created the DataModule (in my project it's name is Datas). As you have observed, I have created it using a local variable. The problem is "how we will access it in other procedures". And here is my little innovation. Using a global variable is really hard because we don't know how many connections we have in a moment. Don't forget that the owner of the DataModule is the Connection. For each active connection we have a DataModule created. Of course, it is created only if the user is logged on. If the user is not logged on, the DataModule does not exist. For easy understanding look at the below scenario: The client connects to the server, using the connect method of TIdTcpClient. In this moment, the client can send any command to the server. We need to know that it is logged on when it sends to the server a command like Get or Add. Using the model I have described, the user verification is really easy, because if for a client connection a DataModule exists, that means that the client is logged on, if not, the client is not logged on. It's time now to implement a "finder" function. This function is useful to find if a datamodule exists for a connection. If the DataModule exists, the function will return a reference to the DataModule. Else, it will return nil. function TForm1.FindModule(connection:TIdTCPServerConnection):TDatas; var i: integer; begin Result := nil; for i := 0 to Connection.ComponentCount-1 do if Connection.Components[i] is TDatas then Result := (Connection.Components[i] as TDatas); end; As I have said, we are using a "pool thread model" for our server. That means that the thread is not destroyed when the user disconnects from the server. So, we have to manually destroy the DataModule, each time when a user is disconnected from our server: procedure TForm1.serverDisconnect(AThread: TIdPeerThread); var AData:TDatas; begin AData := FindModule(AThread.Connection); if AData <> nil then AData.Free; end; That means that for Logoff command we have to do something like below: procedure TForm1.serverCommandHandlers1Command(ASender: TIdCommand); begin ASender.Thread.Connection.Disconnect; end; We are now in the moment of the real implementation. We have to exchange data between our client and server, so we will implement first the Get command. In order to make this example easy to understand this command will not have any parameters. But, you can add parameters that can be used for filtering or for any other action. Bellow, you have the command implementation: procedure TmainFrm.serverTIdCommandHandler2Command(ASender: TIdCommand); var AData:TDatas; AStream:TStream; begin AData:=FindModule(ASender.Thread.Connection); if Adata=nil then begin ASender.Thread.Connection.WriteLn( '103 - You are not logged in! Good bye!'); Asender.Thread.Connection.Disconnect; end; // Create the stream AStream:=TMemoryStream.Create; // Start the interrogation AData.Query1.sql.Clear; AData.Query1.sql.Add('select * from country'); AData.Query1.Active:=true; // Load the Query's data to buffertable Adata.buffertable.LoadFromDataSet(Adata.Query1,[mtcpostructure]); // Save BufferTable to Astream AData.buffertable.SaveToStreamViaFormat(Astream, adata.kbmBinaryStreamFormat1); // Move Stream to first position Astream.Seek(0, soFromBeginning); ASender.Thread.Connection.WriteLn( '202 - Ok! The stream is comming!'); // Send the Stream to client Application ASender.Thread.Connection.WriteStream(AStream,true,true); // Free the stream AStream.Free; end; For this command, the client will receive a stream containing all table data. This stream can be loaded into a TKbmMemTable and the entire table on the server will be visible on the client. Without BDE or any other third party "connectivity". Because the Add command is the same in implementation (you have to read the params and create a query that inserts params into the table) I will not elaborate further on this aspect. Also, because the client implementation is really easy I will not describe it. This is the entire "technology". It is simple to implement, superior in performance and it costs nothing. There are, of course many other aspects you have to think about. For example you can use compressed streams in order to reduce the traffic between server and clients. There are some things you have to know before you start in your next "big middleware server" creation. Before you start, you have to build your protocol and this, in my opinion, is the most important part of the project. A good protocol will help you in the development process. Let's see the above code fragment. First, you have to see that before each string that is sent to the client is a number. In the example protocol I have used a numeric protocol combined to a string protocol. Before a text that is send to the client, I have a numeric code: 1XX represents the error actions and 2XX represents the success actions. This will help you in developing client applications. You can create, on the client side, a helper function that extracts this numeric code and, for each string that is received by your client you will know if an error has occurred or "all is OK". Another interesting part is the synchronization. When a server sends a stream, you have to read a stream on the client side. The same with strings. If you wait for a string and a stream is sent, your client application will be blocked. This is why I have inserted the message '202 - Ok! The stream is comming!'. At first view, it is unnecessary, but just look at the beginning of the procedure. If the client is not logged, the server sends the '103 - You are not logged in! Good bye!' message. When our client sends the get command to the server it first reads a string because it has to know if the server returns an error. Another interesting part is about how streams are sent. The WriteStream procedure has 3 parameters. First is the stream that will be sent. The second parameter is a boolean. If is true, the stream will be sent from the start, else the stream will be sent from the current position. The third parameter is also a boolean. If is true, the size of the stream will be sent to the client, if it is false, this value will not be sent. If this last parameter is true, the size is sent as a integer value, before the stream. So the below fragments of code are the same: 1. => WriteStream with last parameter false .... i := MyStream.Size; ASender.Thread.Connection.WriteInteger(i); ASender.Thread.connection.WriteStream(MyStream, true, false); .... is the same with 2. => WriteStream with last parameter true .... ASender.Thread.Connection.WriteStream(MyStream, true, true); .... A demo project is attached to this article. It contains a full client and a server implementation. Enjoy it! __________________ Reference: http://www.delphi3000.com/member.asp?ID=806 ________________________________________________________________________ 5. Inline Assembler in Delphi (III) - Static Arrays By Ernesto De Spirito <eds2004 @ latiumsoftware.com> Passing static arrays as parameters =================================== Static arrays parameters are passed as pointers to the first element of the array, independently of whether the parameter is passed by value or by reference (either as "var" or as "const"). Given the following declarations... const ARRAY_MAX = 5; type TArrayOfInt = packed array [0..ARRAY_MAX] of longint; var a, b: TArrayOfInt; procedure InitializeArray(var a: TArrayOfInt); var i: integer; begin for i := 0 to ARRAY_MAX do a[i] := i; end; ...the call to the procedure InitializeArray in assembler would be like this: // In Object Pascal: // InitializeArray(a); // In Inline Assembler: asm mov eax, offset a // EAX := @a; call InitializeArray // InitializeArray; end; OFFSET is an assembler unitary operator that returns the address of a symbol. OFFSET is not applicable to local symbols. You should use the LEA opcode (see below), which is more "universal". Static arrays passed by value ----------------------------- If the array is passed by value, it is responsibility of the called function to preserve the array. When a function needs to change the values of one or more elements of an array passed by value, normally it creates a local copy and works on the copy. The compiler creates a copy for us in the "begin" of Pascal procedures and functions, but in full assembler procedures and functions we have to do it by ourselves. One way of doing it is like this: procedure OperateOnArrayPassedByValue(a: TArrayOfInt); var _a: TArrayOfInt; asm // Copy the elements of "a" (parameter) in "_a" (local copy) push esi // Saves ESI on the stack push edi // Saves EDI on the stack mov esi, eax // ESI := EAX; // @a lea edi, _a // EDI := @_a; mov eax, edi // EAX := EDI; // @_a mov ecx, type TArrayOfInt // ECX := sizeof(TArrayOfInt); rep movsb // Move(ESI^, EDI^, ECX); pop edi // Restores EDI from the stack pop esi // Restores ESI from the stack // Here goes the rest of the function. We'll work on "_a" (the // local copy), whose first element is now pointed by EAX. end; The new things here are the LEA and MOVSB opcodes, the REP prefix, and the TYPE operator, described below: LEA (Load Effective Address) ----------------------------- Moves to the first operand the address of the second. Here we compare LEA with MOV: Instruction Translated as Effect ------------------------------------------------------------------- lea eax, localvar lea eax, [ebp-$04] EAX := @localvar; EAX := EBP - $04; mov eax, localvar mov eax, [ebp-$04] EAX := localvar; EAX := (EBP - $04)^; MOVSB (MOVe String Byte) ------------------------ Copies the byte pointed by ESI to the location pointed by EDI, and increments ESI and EDI so they point to the next byte. The work of MOVSB can be described as follows: ESI^ := EDI^; // Assume ESI and EDI are of type PChar Inc(ESI); Inc(EDI); Notes: * MOVSW and MOVSD are the Word (16-bit) and DWord (32-bit) versions respectively (ESI and EDI are incremented by 2 and 4 respectively). * The registers are decremented if the Direction Flag is set. REP --- The REP prefix is used in string operations to repeat the operation decrementing ECX until ECX is zero. The work of REP could be described as follows: // rep string_instruction @@rep: string_instruction loop @@rep Notes: * REP is not a shorthand for a code like the above. It works a lot faster. * The value of ECX is not checked at the beginning of the loop (if ECX is zero, the instruction would be repeated 2^32 times, but will generate an AV long before that, as soon as ESI or EDI point to an invalid memory location). TYPE ---- The TYPE operator is a unary operator evaluated at compile time, and it returns the size in bytes of the operand, which must be a data type. For example, TYPE WORD will return 2 and TYPE INTEGER will return 4. Accessing the elements of an array ================================== To access an element a[i] we need the values "@a[0]" and "i" in registers (like EDX and ECX, for example), and then we can use memory addressing as follows: lea edx, a // EDX := @a; mov ecx, i // ECX := i; mov ax, [edx+ecx*type integer] // AX := EDX[ECX]; // a[i]; // PWord(EDX + ECX * SizeOf(integer))^ In the example, we assumed that the elements have 2 bytes (we moved the value of a[i] to AX, a 16-bit register), that the array is not a packed one (each element actually occupies 4 bytes, the size of an integer, so this value was used to compute the position of the element), and that the array is zero-based. For example: var a: array [0..N] of word = (1, 2, 3, 6, ...); +------ EDX = @a | v +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-- | 1 | 0 | | | 2 | 0 | | | 3 | 0 | | | 6 | 0 | | | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+-- a[0] a[1] a[2] a[3] [edx] [edx+04] [edx+08] [edx+12] If the array is not zero-based, we have to adjust the value of the index to make it zero-based before addressing the element. Examples: // a[1..100] : mov ecx, i // ECX := i; dec ecx // Dec(ECX); // Adjust ECX : // a[-10..10] : mov ecx, i // ECX := i; add ecx, 10 // Inc(ECX, 10); // Adjust ECX : The procedure InitializeArray (introduced above) can be implemented in assembler like this: procedure InitializeArray(var a: TArrayOfInt); asm // EAX = PByte(@a[0]); xor ecx, ecx // ECX := 0; @@loop: mov [eax+ecx*type integer], ecx // PInteger(EAX+ECX*4)^ := ECX; // ...or EAX[ECX] := ECX; inc ecx // ECX := ECX + 1; cmp ecx, ARRAY_MAX // if ECX <= ARRAY_MAX then jle @@loop // goto @@loop; end; Or like this: procedure InitializeArray(var a: TArrayOfInt); asm // EAX = @a[0]; xor ecx, ecx // ECX := 0; @@loop: mov [eax], ecx // EAX^ := ECX; inc ecx // Inc(ECX); add eax, type integer // Inc(EAX); // Point to the next element cmp ecx, ARRAY_MAX // if ECX <= ARRAY_MAX then jle @@loop // goto @@loop; end; Returning array values ====================== Functions returning arrays receive an additional last parameter which is the pointer to the memory location where they should place their return value (memory is allocated and freed if necessary by the caller). For example, let's consider the following function: function ReverseArray(const a: TArrayOfInt): TArrayOfInt; var i: integer; begin for i := 0 to ARRAY_MAX do Result[i] := a[ARRAY_MAX-i]; end; The function receives two parameters: 1) EAX = the address of the first element of the array "a" 2) EDX = the address of the first element of Result The function can be rewritten in assembler as follows: function ReverseArray(const a: TArrayOfInt): TArrayOfInt; asm // EAX = @a[0]; EDX = @Result[0]; push ebx // Save EBX mov ebx, eax // EBX := EAX; xor ecx, ecx // ECX := 0; @@loop: mov eax, ARRAY_MAX sub eax, ecx // EAX := ARRAY_MAX-ECX; mov eax, [ebx+eax*type integer] // EAX := EBX[EAX]; mov [edx+ecx*type integer], eax // EDX[ECX] := EAX; inc ecx // ECX := ECX + 1; cmp ecx, ARRAY_MAX // if ECX <= ARRAY_MAX then jle @@loop // goto @@loop; pop ebx // Restore EBX end; Well, this is it for now. In the next issue we'll see how to work with records. ________________________________________________________________________ 6. Forums / mailing lists To join any of our forums, the best way is to subscribe from the web, since that way you'll be able to access the features available at the web site (like changing your subscription options, viewing the past messages, accessing the files section, etc.). A Yahoo! ID is required for that, 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 (you'll only have email access). * 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 :-) http://groups.yahoo.com/group/delphi-en/ Subscription: http://groups.yahoo.com/group/delphi-en/join delphi-en-subscribe@yahoogroups.com * Kylix: Kylix programming. http://groups.yahoo.com/group/KylixGroup/ Subscription: http://groups.yahoo.com/group/KylixGroup/join KylixGroup-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. http://groups.yahoo.com/group/components/ Subscription: 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. http://groups.yahoo.com/group/software-developers/ Subscription: 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 =================================== Freeware -------- * Kylix 3 Open Edition available for download It's a whopping 300+ MB download, but worth every nibble! http://community.borland.com/article/0,1410,29010,00.html * Explorer Drop v1.1, FREEWARE with source - by Simon Grossenbacher TExplorerDrop component enables Drag&Drop with the Windows Explorer for all controls inherited from TWinControl. http://www.torry.net/vcl/system/draganddrop/swissexplorerdrop.zip * KACDO Proffesional v1.0, FREEWARE - by Kiril Antonov Delphi implementation of Microsoft's CDO for Win2k -a set of functions for composing and sending mail. Works only on Win2k/XP machines. Set of 3 powerful components: Message - Message, SMTP, NNTP, Encoding + Decoding Component; Manager - manages IIS SMTP Folders; TreeView - displays + modifies structure of complex messages. http://www.torry.net/vcl/internet/email/kacdopro.zip * TAdvFTP, FREEWARE with source - by Vadim Winebrand (KYLIX) An advanced FTP client component which supports resumes and gives the download rate every few seconds. Includes socks server support. http://www.torry.net/kylix/clxinternet/itools.zip Articles, tips and tricks ========================= * TJpegImage lets you transform BMPs to JPEGs - by Bob Swart Dr Bob shares a trick for transforming BMPs to JPEGs and vice versa. Thanks to a hidden component, it's not as hard as you might think. http://builder.com.com/article.jhtml?id=u00220020913swa01.htm * Using Delphi objects to store config information - by Sebastián Mayorá This article explains how to use objects as a substitute for INI files (and other similar techniques) to store configuration information. http://delphi.about.com/library/bluc/text/uc090302a.htm * Back to School with more Delphi knowledge - by Zarko Gajic Whether you're parent, student or teacher here are the topics you need to enhance your knowledge of Delphi programming. Go back to school in style with the right tutorials, code samples and Delphi quizzes. http://delphi.about.com/library/weekly/aa082702a.htm * ModelMaker tutorials - by Anders Ohlsson ModelMaker is included in D7 Enterprise & Architect - here are some tutorials to get you started. http://bdn.borland.com/article/0,1410,29006,00.html * How to install and uninstall fonts www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=169 * How to check if a string is a number www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=170 * How to get second title bar color www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=171 * BDE error list www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=173 * How to get the CPU speed www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=174 * How to write a correct date in SQL www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=175 * How to get around TQuery.Refresh if it doesn't work - by m3Rlin Sometimes TQuery.Refresh will not refresh as it's supposed to... www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=176 * How to read the serial number of an Audio CD - by m3Rlin Audio CDs, like almost every computer drive/media have a serial number too. Some programs use this number to recognize CDs. www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=177 * How to check is a character is a letter - by m3Rlin www.delphifaq.net/modules.php?op=modload&name=FAQ&op=view&id=178 * How to export a StringGrid to an Excel-File? http://www.swissdelphicenter.ch/en/showcode.php?id=379 * How to turn on/off Caps/Num/Scroll Lock? http://www.swissdelphicenter.ch/en/showcode.php?id=926 * How to synchronize DBGrid title alignments with field alignments? http://www.swissdelphicenter.ch/en/showcode.php?id=1074 * How to save memory with duplicate strings? http://www.swissdelphicenter.ch/en/showcode.php?id=1110 * How to draw a gradient on a canvas with an arbitrary number of colors? http://www.swissdelphicenter.ch/en/showcode.php?id=1162 * How to retrieve information about the TWebBrowser control? http://www.swissdelphicenter.ch/en/showcode.php?id=1191 * How to retrieve the network card addresses? http://www.swissdelphicenter.ch/en/showcode.php?id=1206 * How to convert C Types to Object Pascal Types? http://www.swissdelphicenter.ch/en/showcode.php?id=1217 * How to get an inverse color value to a color? http://www.swissdelphicenter.ch/en/showcode.php?id=1222 * How to show values in a hexadecimal representation? http://www.swissdelphicenter.ch/en/showcode.php?id=1312 * How to show values in binary representation? http://www.swissdelphicenter.ch/en/showcode.php?id=1313 * How to get the start command for a installed Mail-Client? http://www.swissdelphicenter.ch/en/showcode.php?id=1320 * How to execute a document and wait for it to finish? http://www.swissdelphicenter.ch/en/showcode.php?id=1333 * How to do bit-wise manipulation? http://www.swissdelphicenter.ch/en/showcode.php?id=1341 * How to Draw on a Form's Caption bar? http://www.swissdelphicenter.ch/en/showcode.php?id=1345 * How to access Paradox tables on CD or read-only drives? http://www.swissdelphicenter.ch/en/showcode.php?id=1351 * How to define BDE aliases in code? http://www.swissdelphicenter.ch/en/showcode.php?id=1353 * How to multiply big integer values? http://www.swissdelphicenter.ch/en/showcode.php?id=1363 * How to calculate the logarithm for a variable base? http://www.swissdelphicenter.ch/en/showcode.php?id=1371 * How to auto hide IDE windows when coding/designing? http://www.swissdelphicenter.ch/en/showcode.php?id=1382 * How to get the length of wav file (in second)? http://www.swissdelphicenter.ch/en/showcode.php?id=1383 * How to Clone the Controls Properties? http://www.swissdelphicenter.ch/en/showcode.php?id=1392 * How to set system evrionment variable? http://www.swissdelphicenter.ch/en/showcode.php?id=1394 * How to Set a new Index to TToolButton of a TToolbar? http://www.swissdelphicenter.ch/en/showcode.php?id=1395 * How to include the mouse-cursor in a screen shot? http://www.swissdelphicenter.ch/en/showcode.php?id=1396 * How to send data to another program by auto-drag&drop? http://www.swissdelphicenter.ch/en/showcode.php?id=1398 * How to empty all StringGrid cells? http://www.swissdelphicenter.ch/en/showcode.php?id=1399 * How to get the width and height of a Gif-File? http://www.swissdelphicenter.ch/en/showcode.php?id=1400 * How to transition the system to the standby/ hibernate state? http://www.swissdelphicenter.ch/en/showcode.php?id=1401 * How to know if the form is modal? http://www.swissdelphicenter.ch/en/showcode.php?id=1402 * How to prevent copy/paste/cut in TEdit? http://www.swissdelphicenter.ch/en/showcode.php?id=1403 * How to use regular expressions in Delphi? http://www.swissdelphicenter.ch/en/showcode.php?id=1406 * How to save a QuickReport to stream? http://www.swissdelphicenter.ch/en/showcode.php?id=1410 * How to build a Multi Screen Emulator? http://www.swissdelphicenter.ch/en/showcode.php?id=1418 * How to use the DrawAnimatedRects API? http://www.swissdelphicenter.ch/en/showcode.php?id=1419 * How to get the caret-position systemwide? http://www.swissdelphicenter.ch/en/showcode.php?id=1420 * How to invoke the 'find' dialog in a TWebBrowser? http://www.swissdelphicenter.ch/en/showcode.php?id=1421 * How to determine if the current session is remotely controlled? http://www.swissdelphicenter.ch/en/showcode.php?id=1424 * How to translate a virtual-key to ASCII code? http://www.swissdelphicenter.ch/en/showcode.php?id=1425 * How to convert a bitmap to RTF code? http://www.swissdelphicenter.ch/en/showcode.php?id=1426 * How to export a TDBGrid to excel without OLE? http://www.swissdelphicenter.ch/en/showcode.php?id=1427 * How to read a REG_MULTI_SZ value From the Registry? http://www.swissdelphicenter.ch/en/showcode.php?id=1431 * How to restore the default positions of the IDE Toolbars? http://www.swissdelphicenter.ch/en/showcode.php?id=1432 * How to retrieve all database tables with ADO? http://www.swissdelphicenter.ch/en/showcode.php?id=1433 * How to add items to the Application's Windows System Menu? http://www.swissdelphicenter.ch/en/showcode.php?id=1435 * How to get the number of Files in the Recycle Bin + their total size? http://www.swissdelphicenter.ch/en/showcode.php?id=1436 * How to change Background Color in TRichEdit for selected characters? http://www.swissdelphicenter.ch/en/showcode.php?id=1438 * How to show/hide the ActiveDesktop? http://www.swissdelphicenter.ch/en/showcode.php?id=1439 * How to copy formated Rtf-Text from one TRichedit to an other? http://www.swissdelphicenter.ch/en/showcode.php?id=1440 * How to use different underline styles for Text in TRichEdit? http://www.swissdelphicenter.ch/en/showcode.php?id=1441 * How to put the TWebbrowser into Edit Mode? http://www.swissdelphicenter.ch/en/showcode.php?id=1442 * How to set the paragraph line spacing in a TRichedit? http://www.swissdelphicenter.ch/en/showcode.php?id=1444 * Speed up connection to Oracle 8i - by Mark Halter http://www.delphi3000.com/articles/article_3344.asp * How do we store Graphics/Shapes like an Object? - max kleiner Designing a diagram editor or a graphic-tool raises the problem of storing all the painted shapes in a file without get lost in too much overhead. http://www.delphi3000.com/articles/article_3348.asp * Simulate a Web Form POST Request - by Clever Components Discusses the automation of the upload process via HTTP protocol by the POST method using the components from Clever Internet Suite. http://www.delphi3000.com/articles/article_3351.asp * Threaded Brute Forcing Class - by Stewart Moss http://www.delphi3000.com/articles/article_3352.asp * Simple useful Irc routines - by Stewart Moss http://www.delphi3000.com/articles/article_3353.asp * Generic File Importer Base Class - by Stewart Moss Here is a useful base class to create derived classes to import data from any flat file format you can think of. http://www.delphi3000.com/articles/article_3354.asp * ADO Dataset -> CSV file How to export a ADO Dataset to a Comma Separated Values file with a few lines of code. http://www.delphi3000.com/articles/article_3355.asp * Send E-Mails with Indy Components (Easy) http://www.delphi3000.com/articles/article_3356.asp * Show message in OnEnter event Do you ever needed to display a message using the OnEnter event? This example shows how to correctly display a message by using the ShowMessage (or an equivalent function) in OnEnter event. http://www.delphi3000.com/articles/article_3357.asp * EventLog change notification in real-time I needed a way to be notified in real-time when someone acceded my computer inside an intranet. After doing some research, the solution would pass by using the Security event log that is used when you activate any audit option. http://www.delphi3000.com/articles/article_3358.asp * SQL-DMO part 1: The SQL-DMO API This is the first part of a serie of articles about the SQL Distributed Management Objects known as SQL-DMO API. In this first article I'll talk about SQL-DMO, whats the purpose of it and what you'll gain if you use it. I will also show how to install it, so you can use it within our Delphi projects http://www.delphi3000.com/articles/article_3359.asp * Multi Column ListBox with Column Sorting and Resizing This is a VCL that allows multiple columns in a list box. The columns may be sorted (if the AllowSorting property is set to true) by clicking on the column header title. The column headers are set up in the Sections property. They are of type THeaderSections from the THeader component and thus may also display images from an associated image list. The items in the ListBox are semi-colon delimited fields. The fields are lined up in accordance to the Section headers and may be resized by the user at run-time. http://www.delphi3000.com/articles/article_3360.asp * Capture Output of a Console Application - Revised How do you start a DOS or Console Application and capture the output while it is running ? For example, how do you capture the output of the FileCompare (FC) Command ? http://www.delphi3000.com/articles/article_3361.asp * Retrieve Multiple Recordsets from ORACLE 8i Stored Procedure A faster and more resource efficient way to get data from the Database server (in this case - Oracle) http://www.delphi3000.com/articles/article_3363.asp Tutorials ========= * Defining a ClientDataSet's Structure Using TFields - by Cary Jensen This article demonstrates how to define a ClientDataSet's structure at both design-time and runtime using TFields. How to create virtual and nested dataset fields is also demonstrated. http://community.borland.com/article/0,1410,29001,00.html * Understanding ClientDataSet Indexes - by Cary Jensen A ClientDataSet does not obtain its indexes from the data it loads. Indexes, if you want them, must be explicitly defined. This article shows you how to do this at design-time or runtime. http://community.borland.com/article/0,1410,29056,00.html * Improve application design with Prototyping, Modeling, + Storyboarding - by Ronald Anthony Lewis Meeting client expectations is the number-one goal of application development. Here's how one developer uses three approaches to handle different aspects of this critical objective. http://builder.com.com/article.jhtml?id=u00320020909RAL01.htm * An introduction to XML grammar - by Philip Page Document Type Definitions (DTDs) are an optional but useful part of XML. This article shows you how to declare a grammar in a DTD. http://builder.com.com/article.jhtml?id=u00320020906ppg01.htm * Unified Modeling Language simplifies software design - by Shelley Doll UML is the industry standard for modeling software architecture. It combines best practices, platform independence, and extensibility into a common language for describing solutions. This overview of UML describes why it's useful and outlines the major concepts. http://builder.com.com/article.jhtml?id=u00420020903dol01.htm * To be or not to be normal: That is the database question - Eric Roland There are advantages + disadvantages to normalizing database schemas. This article provides specific examples of when and why you should normalize or denormalize your final database design based on performance requirements. http://builder.com.com/article.jhtml?id=u00320020819ero01.htm * Transition from Logical to Physical Data Model - S Harkins + A Fuller Moving from a logical to physical design is not as straightforward as it appears. Use these tips to make the process as smooth as possible. http://builder.com.com/article.jhtml?id=u00320020826gcn01.htm Other Links =========== * Delphi Fireworks Component Contest Basically, you have to make a self-contained Delphi visual component. There are no prizes, but you will receive awe and recognition from your peers, just for entering. Also it makes a great resume entry. http://chuckr.freeshell.org/index.php * ModelMaker is available for Delphi 7 Professional - by John Kaster Borland provides a license to ModelMaker for Delphi 7 Professional. http://community.borland.com/article/0,1410,29090,00.html * Borland to wield tools against Microsoft - by Wylie Wong After nearly being knocked out for good by Microsoft, software maker Borland is back on its feet and eager for a rematch. http://news.com.com/2100-1001-954958.html * Borland has a bead on Visual Studio - By Mark Driver (from Gartner) Borland plans to offer an alternative to Microsoft's Visual Studio .NET development environment. Such a product could suit application developers that want to leverage .NET and the best applications from many vendors. techupdate.zdnet.com/techupdate/stories/main/0,14179,2879601,00.html * Now all we need are celebrity endorsements - by Lamont Adams Humourous article about the possibility of celebrity endorsements in programming. Use the official IDE of the World Cup! http://builder.com.com/article.jhtml?id=u00220020904adm01.htm * SharpDevelop lets you jump into C# Programming for FREE! - Tony Patton You're dying to start programming in C#, but Visual Studio .NET is just too expensive. Help is on the way with this free .NET IDE. http://builder.com.com/article.jhtml?id=u00220020821ton01.htm ________________________________________________________________________ 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/p0039.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. ________________________________________________________________________ Group home page: http://groups.yahoo.com/group/pascal-newsletter/ Subscribe/join: pascal-newsletter-subscribe@yahoogroups.com Unsubscribe/leave: pascal-newsletter-unsubscribe@yahoogroups.com Report spam/abuse: abuse@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!






