Sei sulla pagina 1di 42

BLAISE PASCAL

ENGLISH VERSION

ALL ABOUT DELPHI AND KYLIX, PASCAL AND RELATED LANGUAGES

Pascal

Effective Pascal For-Loops / Hacks1-Write access to read-only property Hallvard Vassbotn Resizing Bitmaps David Dirkse Making right-justified Edits Henk Schreij Delphi 2007 VCL Component Building - II Bob Swart ClientDataset filtering Martin De Bont Intraweb and AJAX Bob Swart Delphi for PHP .2 new version Herman Peeren Solving unknown variables Peter Bijlsma Obfuscator? Rob van den Bogert Restricting Edits to numbers / Euro symbol generation with the Alt key Henk Schreij Monitor aspects Jeremy North Binary Heap Julian Bucknall

Publisher: Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal) in collaboration with the Dutch Pascal User Group (Pascal Gebruikers Groep) Stichting Ondersteuning Programeertaal Pascal

Cover price Europe: 10.00 / UK 7.00 / US $ 15.00

June 2008

CONTENTS
Volume 2, ISSN 1876-0589

ALL ABOUT DELPHI AND KYLIX PASCAL AND RELATED LANGUAGES


Editor in chief Detlef D. Overbeek, Netherlands Tel.: +31 (0)30 68.76.981 / Mobile: +31 (0)6 21.23.62.68 News and Press Releases email only to editor@blaisepascal.eu Authors B Martin de Bont, Julian Bucknall, Peter Bijlsma, C Marco Cantu, D David Dirkse, Frans Doove, N Jeremy North, O Tim Opsteeg, P Herman Peeren, S Henk Schreij, Rik Smit, Bob Swart, V Hallvard VassBotn.

BLAISE PASCAL 2

Articles
Effective Pascal For-Loops Hallvard Vassbotn pag 9 Hacks1-Write access to read-only property Hallvard Vassbotn pag 11 Resizing Bitmaps David Dirkse pag 12 Making right-justified Edits Henk Schreij pag 14 Delphi 2007 VCL Component Building - II Bob Swart pag 16 ClientDataset filtering Martin De Bont pag 20 Intraweb 9.x and AJAX Bob Swart pag 25

Editors
Rob van den Bogert, W. (Wim) van Ingen Schenau, M.J. (Marco) Roessen. Corrector A.W. (Bert) Jonker, M. L. E. J.M. (Miguel) van de Laar Translations M. L. E. J.M. (Miguel) van de Laar, Kenneth Cox (Official Translator) Copyright See the notice at the bottom of this page. Trademarks All trademarks used are acknowledged as the property of their respective owners. Caveat Whilst we endeavour to ensure that what is published in the magazine is correct, we cannot accept responsibility for any errors or omissions. If you notice something which may be incorrect, please contact the Editor and we will publish a correction where relevant.

Delphi 4 PHP versie 2


Herman Peeren pag 3 Solving unknown variables in a set of linear equations Peter Bijlsma pag 28 Obfuscator? Rob van den Bogert pag 30 Restricting Edits to numbers Henk Schreij pag 32 Euro symbol generation with the Alt key Henk Schreij pag 38 Monitor aspects Jeremy North pag 33 Binary Heap Julian Bucknall pag 21

Subscriptions
1: Printed version: subscription 40.-- UK 28.-- $ 60. (including code, programs and printed magazine, 4 issues per year excl. postage. Postage fore Europe 3,-World 5,50 2: Non printed subscription 25.-- UK 22.-- $ 40. (including code, programs and download magazine) 3: Basic Subscription: 10.-- UK 9.-- $ 15. (excluding code, programs and non printable/non editable download magazine) Subscriptions can be taken out online at www.blaisepascal.eu or by written order, or by sending an email to office@blaisepascal.eu Subscriptions can start at any date. All issues published in the calendar year of the subscription will be sent as well. Cover price in Europe: 10.00 / UK 7.00 / US $ 15.00 plus postage. Subscriptions are parallel to the calender year. Subscriptions will be prolonged without notice unless the subscription department receives notice of cancellation by letter or email before 1 November. Receipt of payment will be sent by email. Invoices will be sent with the February issue. Subscription can be paid by sending the payment to: ABN AMRO Bank Account no. 44 19 60 863 Foundation for Supporting the Pascal Programming Language (Stichting Ondersteuning Programeertaal Pascal) IBAN: NL82 ABNA 0441960863 BIC ABNANL2A VAT no.: 81 42 54 147 (Stichting Programmeertaal Pascal) Subscription department Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981/Mobile: + 31 (0) 6 21.23.62.68 office@blaisepascal.eu

Columns
Books by Frans Doove pag 6

Advertisers
Components 4 Developers pag 40 DeepSea Obfuscator pag 38

The illustrations on the cover are from the book Pascal, mathematician of God. The first calculator ever, invented by Pascal with some technical details. The statistic figures were delivered by Julian Bucknall

Copyright notice All material published in Blaise Pascal is copyright SOPP Stichting Ondersteuning Programeertaal Pascal unless otherwise noted and may not be copied, distributed or republished without written permission. Authors agree that code associated with their articles will be made available to subscribers after publication by placing it on the website of the PGG for download, and that articles and code will be placed on distributable data storage media. Use of program listings by subscribers for research and study purposes is allowed, but not for commercial purposes. Commercial use of program listings and code is prohibited without the written permission of the author.

Page 2 / 1816

June 2008 BLAISE PASCAL 2

Foreword...
Already the second issue and there are more to come. It was a hefty quarter for Delphi people. A new magazine, a new company for CodeGear, a new horizon on the sky for Delphi and their other products. Hope for inspiring new developments. Embarcadero has a promising sound. We found out that we now have the start of a new subscriber group: the paperless people. About 2000 free downloads of the first issue, over night. Lots of readers let us know how pleased they are with our initiative to revive a Pascal Magazine. Of course we will. New techniques are very interesting, but older ones like the article of Julian about the Binary Heap are so fundamental, that even beginners should try to understand that matter. It really makes you a better beginner because after it you will understand some more of the fundamentals. Even though now a days it is not done to talk about pointers and all that stuff. Back to the roots. It is heard everywhere. And still inspiring. And I hope for Delphi as well. Now there needs to be stable times, a time to built the future again.
Detlef Overbeek
Downloads of code, applications etc. are available on our website: www.blaisepascal.eu

And we are very pleased that many well known authors agreed to write for us. That is a very good thing. One of the most asked questions was: will you still do articles for starters and hobby Pascallers?

Delphi for PHP 2.0

for PHP 2

by Herman Peeren
Templated forms This is not the same as the use of templates, like Smarty-templates, for designing a webpage. Which is still a very useful way of using Delphi for PHP-components in a webpage. The new Templated Form Designer is an extension of the (also new) HTML Designer. The HTML Designer provides visual capabilities to edit HTML files plus the ability to drag and drop HTML elements such as HTML Input Fields, HTML Drop Down and HTML button controls from the Tool Palette on a form. The Templated Form Designer adds the ability to work with PHP components in such an HTML-form. When working on a templated form, PHP components can be dropped inside the HTML form, and behave like any other HTML element. Templated Forms enable PHP developers to develop applications using tagging similar to ASP.NET or Cold Fusion. For instance, you can now create a templated form and add tags like the following:
<button:php id="Button1" style="z-index: 1000; width: 75px; height:25px"></button:php>

In the beginning of May the second edition of Delphi for PHP was launched. Some striking new features: Templated Forms: you can now drag and drop HTML elements as well as VCL-components on a form. You can also import HTML-pages, made with another web- design tool, like Dreamweaver. Visual guidelines, as we know them from the Delphi IDE, for easier alignment. Improvements in the code view, like: code completion, syntax checking, code folding, change tracking and sync edit. With all this, Delphi for PHP is also a good editor for non-visual PHP-files. Besides ADOdb for access to many different databases, new components were added for native database connections like for Oracle, Microsoft SQL Server and PostgreSQL. Database access can be visually accomplished by dragging tables on a form. Personally I don't like the explosion of native dbclasses; I would have preferred a wrap in one set of database access components, being able to easily switch between the several kinds with a parameter. Improved documentation and more than 20 new samples There are more enhancements. For an overview of all new features:
www.codegear.com/products/delphi/php/whats-new/

into your HTML pages. Those HTML pages can be edited in a visual designer or in a text editor. This mode of development enables the decoupling of the PHP code from the HTML code, so that developers can pass off their HTML files to external designers who can provide the styling of the HTML pages while working around the PHP tags in the HTML. PHP-components can thus be added to a page by using a kind of phpnamespace but follwing after the tag-name in stead of leading before it. Maybe someone had read a book about XML-basics upsidedown.

June 2008 BLAISE PASCAL 2

Page 3 / 1817

for PHP 2

Delphi for PHP 2.0


Something else: I've experimented a bit with importing HTMLpages that were made up outside Delphi for PHP. Of course: with all styling in a separate CSS-stylesheet. That seems to go well, but as soon as you reposition something in Delphi for PHP, an inline style is used and thereby the stylesheet is ignored. Maybe the bad habits of Visual Studio shouldn't be copied. All in all, I think, that the use of Smartytemplates in Delphi for PHP is still favorable above the use of the new templated forms. It'll have to mature first. My personal experience I like the idea: PHP is widely used, but there was no visual IDE (Integrated Development Environment) for PHP. Besides, there are a lot of open source frameworks that are used for PHP- and JavaScript-development, which could be nicely integrated. A VCL (Visual Component Library) was made, mainly based on the way the Delphi VCL is organized (with some features that were inspired by the .NETFCL). All of this was build into a Delphi-like IDE et voil: Delphi for PHP. An improvement to the VCL for PHP in comparison with the VCL for Delphi is, that there are no separate components for data-aware use. So, no TEdit plus a TDBEdit etcetera. An Edit-component in Delphi for PHP has a DataSource and a DataField property. And so have many other components. Unfortunately the VCL for PHP is, just as the classical VCL for Delphi, mainly based on inheritance of classes in stead of using interfaces for a more flexible object oriented design. An interface that can be implemented is standard object oriented PHP5, but it is not used in the VCL. Some components are still not complete.

Figure 1: Templated form designer


Never have I seen something like that, very original, but in my humble opinion: good for nothing. The idea is that a page can be designed outside of Delphi for PHP. A page produced with a templated form in Delphi for PHP can indeed be further designed with another program, like Dreamwever. But the other way around doesn't work with Delphi for PHP yet! You can import an HTML-page into Delphi for PHP, sure, but it ignores everything other than plain HTML (including their own tags like button:php). For instance, with Notepad I've put in an html-body:

<button:php id="Button1" style="z-index: 1000; width: 75px; height:25px"></button:php> <input name="button4" type="button" /> <newtag>this some nonexistent tag of my own</newtag>

Guess what Delphi for PHP made out of it this:


<input type="button" name="button4" /> <newtag>&nbsp;</newtag>&nbsp;&nbsp;this some nonexistent tag of my own

Figure 2: Visual designer guidelines Figure3: Code insight and code completion
For instance, there is a scrollbar-component, but it is (still) not possible to simply add a scrollbar to any container-component, without quite some coding what is to be scrolled and how. In ActionScript (used in Adobe's Flash and Flex) you can simply drag a scrollbar-component to anything that can contain other things. Drag-drop-ready. You could rewrite all components in Delphi for PHP stemming from the controlcomponent by taking a scrollingControl-component as base, but then you'll have to rewrite a whole tree of components; that is the inflexibility of inheritance as basis for the VCL.

So: ignoring the button:php-tag and making a mess out of an unknown tag Not very handy. When using the templated form, you cannot directly position the PHP-components anymore: you must first put position: absolute; in the component's style-code in the template-window. One of those child-diseases An improvement to version 1.0 is that you can use percentages in Delph4PHP, but only when making use of a templated form.

Page 4 / 1818

June 2008 BLAISE PASCAL 2

for PHP 2

Delphi for PHP 2.0


By the way: I added a vertical scrollbar to a panel (say: panel1) by simply adding #panel1_outer {overflow-y:auto;} to my stylesheet. There is always a way to do something, but these kinds of hacks don't give the most beautiful code and it is certainly not the way visual application development is intended. A problem with the use of several different open source frameworks in one IDE is that some of them don't cooperate very well. For instance: you can use some very nice tabbed panes (from the qooxdooframework), you can use a dbRepeater-component for displaying some database-data, but in version 1.0 you could not use those two components together The data-rows would be displayed much to far to the left. This is fixed now in version 2.0 (great!). So, although it is very nice to use all that beautiful open source code that already exists on this planet, the VCL4PHP-builders have a lot of work to integrate the different frameworks to a whole. In that way, it was an advantage of the .NET framework that it was build from scratch. Conclusion I think Delphi for PHP can become a useful tool, but we are not yet there. Of course, if your styling doesn't have to be flexible and you quickly want to make a web-application, then Delphi for PHP is a good choice. Fortunately Delphi for PHP 2.0 is also a good editor for non-visual PHP-files, so I'll certainly use it as my standard PHPeditor. Herman Peeren is addicted to computer-programming since 1977 and has worked in many different fields. His passion is the combination of creativity and technique. At the moment he is mainly focussing on web-applications and -design. He will write some articles about Object Oriented Programming and Design Patterns in Blaise Pascal Magazine.

To advertise in

BLAISE PASCAL MAGAZINE


email advert@blaisepascal.eu mob. + 31.6.21.23.62.68 or see our price list http://www.blaisepascal.eu/index.php ?actie=advertprices/priceinform

Figure 4: Code folding and new code tracking

Figure 5: Drag and drop a table from a databse on a form Version 2.0 is certainly a big improvement. Unfortunately, but understandable, it is not completely backwards compatible with version 1.0: some projects that were made with the previous version don't work in the new version. For instance: if a panel-component was put on a page in version 1.0, and that unit is opened in version 2.0, than it is impossible to set the visible-property to false. Having used the panel-visibility-property several times in a Delphi for PHP-project under version 1.0, I still have to use version 1.0 to edit my old sources But well, that's the disadvantage of an early user. I made a back-end of a CMS (Content Management System) in Delphi for PHP 1.0. It was okay for quickly making up screens for inserting and editing data, and it would have been better with version 2.0. But it is not very flexible; also because all the HTML-rendering is done in the different components themselves. Delphi for PHP was intended for making web-applications. That is certainly something else than designing websites. But: it is also something else than making desktop-applications. Web-applications are often integrated in websites (e.g. shopping cart) and are thereby viewed by different browsers on different screens etc. The lessons of today's web-design to separate design from content by using CSS should be better taken into account by Delphi for PHP.

Helpful suggestions
for using your issue of Blaise Pascal
About using this PDF file One of the most attractive features of Adobe Acrobat PDF files is that you can incorporate hyperlinks in them. We do this wherever appropriate. The articles listed on page 2 can be accessed by clicking the title or the page number. This takes you directly to start of the article you want to read. All URLs listed in code and mentioned in adverts are also clickable.

2005 Win32

2005 .NET

Recommended version Mouseover hints appear in some locacations. The page numbers on the contents page are clickable. All URLs are clickable and direct you to the site. Page 5 / 1819

June 2008 BLAISE PASCAL 2

Books

by Frans Doove
Chapter 3 describes the Together engine in considerable detail (30 pages), with extensive code examples. Chapter 4 discusses unit testing, again with extensive application examples. The final chapter discusses examples of globalisation and localisation. The discussions of the topics are clear and include many illustrative examples, as we are accustomed to see with this author. However, the reader must have a certain amount of prior knowledge and an important consideration must have the described Delphi version on his or her computer. Warmly recommended! Cant: Essential Pascal 2008, English, self-published, no ISBN number; order via Lulu (www.lulu.com). Recommended price: 20 plus shipping. Payment by credit card.beginners.

I continue to find it extremely remarkable and incomprehensible that user's guides are no longer published for new versions of Delphi not by the software producer and not by other publishers. In my opinion, a good user's guide, in addition to its educational aspects, has a not inconsiderable promotional aspect. You would think that people in this field would take a cue from Microsoft, which even offers free magazines including in Dutch. I also think that the lack of user's guides makes things unnecessarily difficult for beginners in particular and for all users in general. Another aspect is that the lack of user's guides and textbooks makes hobby programming more difficult and perhaps even practically impossible. Maybe software developers and publishers have come to regard hobbyist programmers as an extinct race? Many programmers, at least in the past, started out as hobbyists and later became professionals.
I fear that there is too little realisation that software packages, such as Delphi, are in fact rather difficult for beginners, including novice programmers. This ultimately involves at least two aspects: learning to use the development environment and mastering the programming language although we must not overlook the underlying theory, such as object-oriented programming. One of the difficulties in writing a user's guide as well as a magazine article is the risk that the description of how the programming environment works, in preparation for solving programming problems, remains a 'mental exercise' for too long and is too far removed from actual programming. Maybe this is why there are now so few good books on programming that are oriented toward beginners. Solving programming problems without adequate knowledge of the programming environment is like walking in the dark. However, knowledge of the programming environment alone does solve any programming problems. And searching the Web, of course, is even less useful. Bob Swart: Delphi 7 for Win 32 Development Essentials 2008, English, 104 pages, paper, A4 format. No ISBN number; selfpublished. Order via Lulu (www.lulu.com). Recommended price: 23 including shipping. Payment by credit card.

For many years, Cant had an e-book on his website with the title Essential Pascal, which could be downloaded free of charge. Now a revised and updated version of the text has been published by Lulu. I have a previous version of the book from 2003. The present version lacks the screenshots of the previous version, and there is no longer any colour in the text. I understand that this has to do with how it is published (and the publishing costs). The book consists of twelve chapters dedicated to Pascal as a programming language in Delphi. Naturally, the fact that the book can also be used with Free Pascal (and thus with Lazarus) and Chrome is mentioned. The book describes the conventional Pascal constructions, but not the object-oriented extensions. The intention is to publish a new version of the book with each new version of Delphi, but not to include any OOP extensions. The source code of the examples is still available free of charge from the author's website. The topics discussed in the twelve chapters are: Chapt. 1: A brief history of the Pascal language. Chapt. 2: Coding in Pascal. Chapt. 3: Types, variables and constants. Chapt. 4: User-defined data types. Chapt. 5: Statements. Chapt. 6: Procedures and functions. Chapt. 7: Handling strings. Chapt. 8: Memory. Chapt. 9: Windows programming. Chapt. 10: Variants. Chapt. 11: Programs and units. Chapt. 12: Files in the Pascal language. Reading this book is a pleasure. It is a good introduction to the Pascal language, but without any mention of Delphi. In a certain sense, this is not logical, since Delphi also provides opportunities to try out the described code, so beginners can concentrate on becoming proficient in Pascal without having to learn how to use Delphi at the same time. However, most of the sample code in the discussions of the various topics is only program components instead of complete programs. This means that you need another book to learn how to use Delphi as programming environment. Of course, the book can also be used as a reference book for aspects of the language, but I think that what the author had in mind was beginners who want to use the Pascal language and programmers who come to it from another language. Naturally, there are also other environments for working with Pascal, although Lazarus users (for example) will experience the same problems as Delphi users.

In my opinion this book is not primarily intended for beginners, but instead specifically for relatively experienced programmers who use or plan to use Delphi 7 with Windows 32. In five chapters, the author discusses five specific features of this version. Each chapter deals with a specific function of Delphi 7, in part by solving programming exercises. I find this combined approach very worthwhile. It is a suitable answer to the previously described problem. Chapter 1 is relative general and discusses (among other things) templates, designers, Vista support, Code Editor and refactoring. Chapter 2 introduces the changes and extensions to the language, with many examples.

Page 6 / 1820

June 2008 BLAISE PASCAL 2

Books
The explanations are clear, and the examples are illustrative. All significant features of the language are described. I don't know what target group the author had in mind. The explanations and examples are accessible to beginners as well as experienced programmers. However, beginners will have to learn how to use the programming environment, and with relatively large programs where an understanding of OOP is necessary they will have to consult other references. I'm not sure that beginners will know what FORTRAN, COBOL and Wordstar were (p. 12), unless they're rather old beginners. I still happen to know. Recommended for beginners and as a reference for everyone. Lex de Haan and Toon Koppelaars: Applied Mathematics for Database Professionals 2007, Apress, English, hardbound, 376 pp, ISBN 13:978-1-59059745-3, no CD. The accompanying code is available on the publisher's website. Recommended price: 45. Each chapter ends with a summary and a number of exercises. The answers to some of the exercises are given in Appendix E. Chapter 2, an introduction to set theory, is structured similarly and the topic is discussed clearly. Chapter 3 discusses logic in more detail. The topics that are discussed include algebraic properties and concepts such as commutative, associative, distributive, etc. Part 2, 'The Application' (chapters 510), devotes 130 pages to a discussion of applications. The topics include database design and the structures of possible manipulations. The terms and concepts are explained in the discussion with figures and listings. The explanations are clear and complete. Part 3, 'The Implementation' (chapters 11 & 12; 70 pp) begins with a discussion of the database design and then describes the implementation in Oracle. Chapter 12 is short (3 pages) and provides a summary of the objectives of the book. It presents a sort of rationale for the introductory mathematical chapters and their application to the design of databases. This chapter should actually be located at the front of the book as a sort of preface. Part 4 consists of Appendices AE (55 pp). Appendix A gives a formal definition of an example database. Appendix B provides an overview of the symbols. Appendix C contains a bibliography. Here it struck me that many books dating back ten years or more are listed as background references. Appendix D provides an overview of multi-valued logic, and Appendix E provides answers to some of the exercises (without explanations). It is difficult to judge whether this book is intended for students in educational programmes and/or for self-study. The authors (one of whom is now deceased) point out that anyone who does not have a basic understanding of the logic discussed in the book will find the book quite difficult. It is not clear whether this is based on experience with teaching this subject. The mathematics, which are discussed clearly, are fairly basic and in my opinion not enormously difficult, although the authors suggest otherwise. The database theory is decidedly more complex and more difficult. Aside from the question of whether this book is suitable for selfstudy, the authors say on page 23 that the target audience is IT professionals: developers, designers and architects. A good book, but probably most suitable for a specific target group, in part due to the use of the Oracle platform. Daniel Solis: Illustrated C# 2008 2008. Apress, English, paper, 694 pp, ISBN 13:978-1-59059-954, no CD; recommended price: $ 45

The news that CodeGear had been purchased by a software company that specialises in databases came in while I was preparing this text. Aside from the commercial aspects, this seems fairly logical to me. In the last while, I increasingly have the impression that Delhi programming is heading more and more in the direction of using computers for databases. A long time ago already, the French called the computer an 'ordinateur'. Maybe they were especially farsighted? In all honesty, I find this development regrettable if it is true. However, when I read the text on the Web about the origin of the 'world's largest platform-independent software provider of database and application tools', I fear that my suspicions are not entirely unfounded. On the one hand, the increasing use of computers as 'application devices' is positive and understandable, but it reduces the use of computers as tools for developing software. This despite the impression I have that there is still a lot of room for computers in and in aid of fields such as education (and probably the Dutch tax office as well). And of course, for more inventiveness in programming! This nicely published book is written by two Dutch authors and published in English by an American publisher. It consists of twelve chapters arranged in four parts. Part 1 (chapters 14) is dedicated to the underlying mathematics of databases and database design. It provides an introduction to logic (Chapt. 1), an introduction to set theory (Chapters 2 & 3), and an introduction to relations and functions (Chapt. 4). The purpose of these introductory chapters is to provide a foundation for database design. The topics discussed in these four chapters are in fact the most essential fundamental concepts in this area. These topics are always discussed in introductory courses in informatics, as well as in introductions to logic and discrete mathematics. The explanations are excellent: clear and comprehensible, with many problems and exercises. The ideas here are actually not overly difficult; they are the usual basic concepts. The text includes many examples and overviews.

A while ago we devoted some attention to C# in Blaise. At that time, Microsoft made it possible to distribute a CD with Visual Studio and C#, in order to not only advertise the capabilities of Visual Studio and .NET but also promote writing software for them in a modern programming language. As a result, many Delphi users seriously wondered whether they were witnessing the gradual demise of Delphi, perhaps abetted by the policies of Borland and its successor.

June 2008 BLAISE PASCAL 2

Page 7 / 1821

Books
And they wondered whether C# was perhaps its modern successor. Fortunately, the Delphi scene has calmed down somewhat since then, although new speculations have arisen suddenly from time to time. It's clear that there is still considerable interest in Delphi and Pascal, throughout the world. Of course, it's a good idea to keep a close eye on the problems and developments. Here I can mention two items which indicate that the problems have not been eliminated. The first item is that Microsoft distributed a CD with Visual Studio and Microsoft Visual C# via the May 2008 issue of the Dutch PC Magazine. The second item gives me even more cause for concern. On the website of a large, specialised bookseller, which provides mail-order service as well as shop sales, I once checked to see how many books were available on Pascal and Delphi and on C# at that time. There were 23 books specifically devoted to C# 2008. There were a total of 10 on Delphi. Of these, leaving aside the level, there were three on Delhi 5, two on Delphi 6, three on Delphi 7, two on Delphi 8, and one on Delphi 2005. I fear this shows that somewhat more advertising for Delphi, now that the interest in (and use of?) C# is so large, is not unwarranted. Solis's extensive book on C# 2008 is intended to appeal to beginners and moderately advanced programmers. It is a large book. The table of contents occupies 40 pages, the text fills 656 pages, and there is a 37-page index. Extensive tables of contents and indexes are always important for getting to know the structure of a book, so the book rates well in this regard. It gives the reader a good overview and insight into what it has to offer. And finally, there are 25 chapters. My first exploratory browsing through the book gave me the impression of an old-fashioned reliable textbook for learning to program in C#. The question is whether this first impression is correct. The author based his approach on the idea that many readers think visually. This means that figures, tables and charts are useful for assimilating the material. This translates into a combination of text and illustrations. In addition to the countless code examples, the discussed topics are illustrated by many charts and tables. It is impossible to mention all the topics that are discussed. After a general introduction to the C# language and .NET, as well as a general introduction to programming in C#, there is a discussion of variables, classes and methods, inheritance, expressions, operators, and statements. This is followed by exceptions, structs and arrays, events, interfaces, conversions, generics, and LINQ. This list is naturally not complete, but it gives an impression of the broad range of topics. In this regard it is a rich book, with many clear explanations and a large number of examples and illustrations. One thing I miss are chapter summaries, although the chapters are relatively short (around 18 pages on average). What I miss the most are programming exercises so readers can practice on their own and apply the discussed material to check their comprehension. Maybe this is because relatively few complete programs are generated in the book. Or because there is rather little discussion of .NET. I noticed this especially because the author is said to have experience in teaching programming courses. However, the author may have intentionally chosen to concentrate on programming in C# and omit any further digressions or discussion of applications. With 600 pages, there is naturally plenty of learning material. In summary: a good book that offers lots of study material. The explanations are clear. For some applications, including fairly essential ones, you will need to study another book. The term 'illustrated' in the title reflects the large number of tables, charts and code examples in the book. There are no screen shots. .NET is only discussed on a few pages in the first chapter. There is also no mention of which programming environment can or must be used.

How to become a subscriber


First of all you need to decide whether you want: 1 the printed version*, code included, fully printable version,lectronic version download included; including tax, excluded postage (4 issues = 20,00 euros, total 45,00) You can have your electronic version immediatly whilst the printed issue is on its way. the no paper (download) version, printing enabled and code included.(you don't have to pay postage, just pay 25,00). the basic download version, without code, without downloads and printing disabled. The second issue is not free but costs 10,00. Any first issue can be downloaded at any time. (If it is your first download.) Special limited-time offer:

2 3

subcribe
to the printed version or no paper download version

for the first year * 25.00


You can pay with PayPal or by credit card (Take Two) or by bank. Look for subscription details on page 2

Readers
Please send your questions and comments to: email office@blaisepascal.eu or to: Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981 Mobile: + 31 (0) 6 21.23.62.68

Page 8 / 1822

June 2008 BLAISE PASCAL 2

Efficient Pascal For-loops

3 Hallvard Vassbotn

2005 Win32

2007 Win32

As one of the pillars of modern programming has stated; We should forget about small efficiencies, say about 97% of the time: premature optimization is the root of all evil. C. A. R. Hoare (http://en.wikipedia.org/wiki/C._A._R._Hoare) But we should not confuse premature optimization with using an efficient programming style a style where you consistently use coding patterns that are clear, easy to read, but that still produce reasonably efficient code. Subtle changes in coding style can produce significant improvements in code size, memory usage and performance. In your day to day coding you should employ an efficient coding style. Only in specific, proven performance critical points in the code, you should go to the extra effort of optimizing the code further, making sure to perform measurements before and after the improvements. In this article we will look more closely at loops and how to write efficient code using them. Pascal has a rich set of loop statements; for-do, while-do, repeat-until and in more recent Delphis, for-in. In addition direct and indirect recursion and even the shunned goto-statement can be used to perform repetitive tasks.
Choosing the right kind of loop One trait of an efficient programmer is the ability to select the most appropriate loop for a given situation. While it is possible to use any of them for a given task, code complexity can often be reduced by using the loop-statement that best fit the requirements. For instance, I've seen programmers almost exclusively using repeat-until loops, even when looping over the contents of a list, like this:
procedure DoOperations(List : TList); var i: integer; Item: TMyObject; begin if List.Count > 0 then begin i := 0; repeat Item := List[i]; Item.Operation; i := i + 1; until i >= List.Count; end; end;

Notice that the collection being looped over here is an instance of TList. In Win32, TList holds a list of pointers. Most programmers would probably write the assignment to Item like this:
Item := TMyObject(List[i]);

This is fine and produces identical code to the line above, but strictly speaking the hard type-cast is not needed since untyped pointers are assignment compatible with all other pointers and object references. So the line above can be written:
Item := List[i];

This change does not affect the generated code, but I think it produces slightly less cluttered and easier to read code. Of course, you need to ensure that the list actually only contains object references that is (or inherits from) TMyObject. For the safety minded, you could to assign or cast to TObject first then perform a run-time checking as-cast.
Item := TObject(List[i]) as TMyObject;

One alternative is to only keep the checks in assert-enabled debug builds, like this:
Item := List[i]; Assert(TObject(Item) is TMyObject);

Another issue is that evaluating List[i] is the same as accessing the default property of TList, Items:
property Items[Index: Integer]: Pointer read Get write Put; default; Item := List.Items[i]; // Same as List[i]

Deze toegang gaat via de TList.Get methode


function TList.Get(Index : Integer): Pointer; begin if (Index < 0) or (Index >= FCount) then Error(@SListIndexError, Index); Result := FList^[Index]; end;

Notice that Get first checks that the index is in a valid range between 0 and Count-1. Inside the for-loop (provided we have coded it correctly), this test is superfluous, because we know that the index is always between 0 and Count-1. This is admittedly a minuscule performance overhead (even including the call and return instructions), but inside multiple, long running and potentially nested loops, this can add up. It also generates slightly larger code. Is there any way around this extra call and checking? It turns out there is. Helpfully, the TList class declares a public readonly property called List that gives access to a pointer to the internal array of pointers.
property List: PPointerList read FList;

For such a simple and common operation as looping over the contents of a list, this code is way to complex. It is hard to write initially, getting the border cases and conditions right, it is hard to maintain and debug and it generates sub-optimal code. There are several problems with this code. Because the condition of a repeat-until loop is only checked at the end of the loop, after the first iteration, we have to have an extra condition at the top for the special case of an empty list. In addition, the loop index variable i is initialized, incremented and checked in three separate points in the code. The chances of getting one of these statements wrong is high, and, worse, the reader and maintainer of the code has to read, understand and verify these four statements every time the code is read or modified. Performance and code-size wise, the code is inferior because of the duplication of the List.Count check and the fact that the until-expression is evaluated for every iteration of the loop (this can be more significant when the run-time cost of the expression is higher than in this case). This kind of loop, counting from 0 to a known count is a perfect fit for the for-do loop.
procedure DoOperations2(List: TList); var i: integer; Item : TMyObject; begin for i := 0 to List.Count-1 do begin Item := List[i]; // Compiles ok Item.Operation; end; end;

The clue here is that by accessing the items of the TList directly through the List property, we avoid the overhead of calling Get and checking the indx. This is part of my everyday coding style:
procedure DoOperations2(List: TList); var i: integer; Item : TMyObject; begin for i := 0 to List.Count-1 do begin Item := List.List[i]; Item.Operation; end; end;

Notice that if you compile with range-checking on, there will still be a check to see that the index is in the range 0..MaxListSize-1, but this is performed using a fast single instruction. You could disable this range-checking just for that statement:
{$R-} Item := List.List[i]; {$R+}// Direct array access

But it is starting to get ugly. In my opinion, we come to the point of what constitutes an efficient coding style for for-loops over a TList instance. Going further moves into the field of (potential premature) optimizations. But it may be interesting none-the-less to see what can be achieved. Optimizations of a for-loop There are many levels of an implementation that can be optimized. At the outermost level, optimizing at architecture level may involve using a fast in-memory database instead of a slower disk-based database. Next, optimizing by picking the most suitable algorithm

It has a number of advantages over the repeat-until loop above. It combines the initialization, increment and check into a single statement. It also only evaluates the from- and to-expressions only once before the loop.

June 2008 BLAISE PASCAL 2

Page 9 / 1823

Efficient Pascal For-loops

2005 Win32

2007 Win32

and data structures can have a profound effect, such as replacing a nave bubble-sort with a faster quick-sort algorithm. Finally, you can perform optimizations on the function and statement level. Often it does not make sense to perform micro-optimizations before the higher levels of the design have been optimized. With that warning in mind, let's see what kinds of micro-optimizations we can perform on a single for-loop that loops over the contents of a TList. Note that in most loops there will be additional optimizations that can be done, moving all calculations and work that does not change during the loop, outside the loop. We're ignoring that part here, focusing on the loop itself and accessing the TList contents. Only use local variables inside the loop Most micro-optimizations relies on knowing the code-generation patterns of the compiler and knowing what language rules inhibit the compiler from generating the most optimal code. For instance, accessing non-local variables, fields and properties inside the loop forces the compiler to call functions or to load values from memory locations. Caching frequently accessed and non-changing variables in local variables allows the compiler to keep them in fast registers instead. For instance, one trick is to cache the value of the TList List property (the raw pointer to the allocated pointer array for the collection) in a local variable, like this:
procedure DoOperations4(List: TList); // Efficient code // Only use local variables inside loops var : integer; I Item : TMyObject; LList : PPointerList; begin LList := List.List; for i := 0 to List.Count-1 do begin {$R-} Item := LList[i]; {$R+} // Direct array access Item.Operation; end; end;

Let the loop run downwards Another small detail is that the loop termination check code can be made simpler if the index variable can be checked against 0 instead of some other value. The reason for this is that the Intel processors have simplified combined instruction for checking against 0 and jumping if it is equal (or is not equal). For a for-loop this normally means that we need the loop to go towards 1 instead of 0. The reason for this is that the loop index decrement and check happens at the end of the loop. When the last valid index is 0, it will be 0 after the final decrement.
procedure DoOperations6(List: TList); // Efficient code // Let loops count down to 0 (assuming the logic is unaffected) var i: integer;Item: TMyObject; LList: PPointerList; begin LList := List.List; for i := List.Count downto 1 do begin {$R-}Item := LList[i-1]; {$R+} // Direct array access Item.Operation; end; end;

Again, looking at the loop logic for updating the index variable and the jumping back to the start of the loop, here is what D7 generates for DoOperations2 above:
// Loop prologue mov ebx,[edi+$08] // Get Count // Calc Count-1 dec ebx // test ebx,ebx // Skip loop if negative jl +$1f // EBX = Count again inc ebx // ESI = i = 0 xor esi,esi // <-- Loop body goes here // Loop update index/loop variable and jump back to the loop body // Inc(i) inc esi // Dec(EBX) dec ebx // Jump to the loop body if EBX <> 0 jnz -$1c

Actual generated code may differ slightly between versions, but DoOperations2 above (without range checking) yields this assembly code in Delphi 2007 for the two statements inside the loop..
mov eax,[edi+$04] mov eax,[eax+esi*4] call TMyObject.Operation

Notice that the compiler is still counting down and using the fast JNZ instruction, but it has to update two variables; the actual index variable (here kept in ESI) that counts up from 0 and another internal loop-control variable (in EBX) that counts down to 0. The corresponding code for DoOperations6 looks like this:
// Loop prologue mov ebx,[eax+$08] // i := Count // Compare Count with 1 cmp ebx,$01 // Skip loop if Count is less than 1 (i.e. 0) jl +$0e // <-- Loop body goes here // Loop update index variable and jump back to the loop body // Dec(i) dec ebx // test ebx,ebx // Jump to the loop body if EBX <> 0 jnz -$0e

The slightly optimized version using a local LList variable gives this assembly code:
mov eax,[esi+ebx*4] call TMyObject.Operation

It is one instruction shorter and has one less memory access. The reason the compiler cannot perform this optimization automatically, is that it cannot guarantee that the value of the List property does not change. Note that in order to use this optimization, we as programmers must make sure that this is safe. For instance, if the code inside the loop (directly or indirectly) changes the contents of the List, the optimization is not safe. The reason is that the internal list array could be reallocated when the collection grows or shrinks and this would make our local cached value invalid. So be careful about not breaking this assumption when performing this optimization. One possible improvement here would be to use an Assert to make sure the cached value has not changed inside the loop:
procedure DoOperations4b(List: TList); // Efficient code // Let loops count down to 0 (assuming the logic is unaffected) var I : integer; Item: TMyObject; LList : PPointerList; begin LList := List.List; for I := 0 to List.Count-1 do begin Assert(LList = List.List); {$R-} Item := LList[i]; {$R+} // Direct array access Item.Operation; end; end;

Here we see that the compiler only has to update a single variable (EBX) instead of two. In addition to smaller, faster code, this gives the compiler another free register to play with and use for other purposes. The Intel architecture is really hampered with too few registers (it is a little better in 64-bit mode), so any register we can free up in performance critical code will help the compiler generate more efficient code. Note that we often cannot change the direction of the loop, because it would change the logical outcome or generate the wrong external side-effects. Don't use Word or Cardinal for the index variable A common mistake when using a for-loop is to declare the loop index variable as an unsigned variable like Word or Cardinal. This can cause a subtle bug to linger in the code. You may test and see that it works fine with a list that has one or more elements. The problem arises when the list is empty. Then the expression Count-1 evaluates to -1 and the attempt to assign a negative value to an unsigned variable will result in a range check error. The bad solution to this problem is to check if Count > 0 before the loop. The best solution is to always use integer for the index variable. Conclusion The for-loop is the bread-and-butter of everyday programming, so we sure ensure that we use it correctly and produce readable and efficient code. In later installments we may be looking at other kinds of loops and efficient coding patterns.

The Assert statement would have no code generated for it when you compile the release (or performance test) version with no asserts enabled (in compiler options).

Page 10 / 1824

June 2008 BLAISE PASCAL 2

3 Hack #1: Write access to a read-only property Some time ago I was faced with the task of making our main application behave better systems with different screen resolutions (or rather pixel density, as in pixels per inch). This is the classic Large Font/Small Font problem and getting forms and dialogs to scale properly to show readable fonts and text on all displays. There are several things to keep in mind, some of them are covered here - there are more complications due to MDI windows and Form inheritance.
To make my testing easier (and possibly to let the end-user override the default scaling behavior) I decided to let the current screen density (as determined by Screen.PixelsPerInch) be controlled from a setting in the Registry. The built-in Delphi form scaling works reasonably well, and relies on the fact that the form's design-time PixelsPerInch value is different form the run-time Screen.PixelsPerInch value. Now, PixelsPerInch is a read-only, public property of the singleton TScreen class. It is initialized in the TScreen constructor to the number of vertical pixels per inch as returned by the current graphics driver:
DC := GetDC(0); FPixelsPerInch := GetDeviceCaps(DC, LOGPIXELSY);

2005 Win32

2007 Win32

First we get a pointer to the run-time type information (RTTI) of the property we are interested in using the helper function GetPropInfo from the TypInfo unit. In this call we provide our newly declared TScreenEx class, because we know it has the desired PixelsPerInch property published. By using information gleaned from the TypInfo unit about the encoding of the GetProc field, we are enable to get the offset of the backing field for the property. GetProc is encoded differently for field, normal methods and virtual methods. For fields the most significant byte is always $FF and the remaining three bytes contains the offset of the field from the start of the instance. Then we use the instance variable Screen and add the calculated offset, using a couple of well-placed type casts in the process. Now we have a pointer to the private FPixelsPerInch field inside the Screen instance, and we can change the value simply by de-referencing the pointer and assigning to it. While this second version of the code is less cryptic than the oneliner we started with, it basically hard-codes the general hack of modifying a read-only property from the specific task of modifying the Screen.PixelsPerInceh property. Let's refactor a bit and tear the two different concerns apart, giving us a reusable RTTI hack routine.
procedure SetReadOnlyIntegerProperty(Instance: TObject; RTTIClass: TClass; const PropName: string; Value: integer); var PropertyRTTI: PPropInfo; Offset: integer; Field: PInteger; begin PropertyRTTI := GetPropInfo(RTTIClass,PropName); if Assigned(PropertyRTTI) and // the property exist (PropertyRTTI.PropType^^.Kind = tkInteger) and // it is an integer property ((Integer(PropertyRTTI.GetProc) and $FF000000) = $FF000000) then // the reader is a field begin Offset := Integer(PropertyRTTI.GetProc)and $00FFFFFF; Field := PInteger(Integer(Instance) + Offset); Field^ := Value; end; end;

For my testing purposes, I wanted to set the value of the PixelsPerInch property without going to the hassle of actually changing my system setup, but to do that I would somehow have to modify the value of the read-only property. Impossible, right? Well, in software, nothing is really impossible. Software is soft, so we can change it . Changing the declaration of TScreen to make the property writeable would work, but as Allen Bauer has pointed out, making changes in the interface section of RTL and VCL units can have cascading effects, that are often undesirable. Besides, that would not really qualify as a bona-fide hack - it would have been too easy. Nah, lets do something a little more fun ;-P. PixelsPerInch is only a public property, so there is no RTTI for it. Let's declare a descendent class, that promotes the property to published:
type TScreenEx = class(TScreen) published property PixelsPerInch; end;

The aptly named SetReadOnlyIntegerProperty takes four parameters; Parameter Description Instance The object with the read-only property you want to change RTTIClass The hack-class with the re-declared published property PropName The name of the read-only property Value The integer value you want to change the property to Note that this is specific to integer properties, if you need to change properties of other types you need to write corresponding routines, replacing Integer and PInteger with appropriate types. With this handy helper routine in our belt, it is a piece of cake to reimplement our SetPixelsPerInch routine.
procedure SetPixelsPerInch(Value: integer); begin SetReadOnlyIntegerProperty(Screen,TScreenEx, 'PixelsPerInch', Value); end;

Now, since TScreen indirectly inherits from TPersistent, and TPersistent was compiled in the $M+ mode, published properties in our TScreenEx class will have RTTI generated for them. But PixelsPerInch is still a read-only property - and there is no way our TScreenEx can make it writeable, because the backing field FPixelsPerInch is private, not protected, and so is off-limits for us. The cunning thing about the RTTI generated for the TScreenEx.PixelsPerInch property, is that it includes enough information about where to find the backing field in the object instance. Open TypInfo.pas and locate the TPropInfo record that describes the RTTI for each property. Included is the GetProc pointer. For backing fields, this contains the offset off the field in the object instance (sans some flag bits). After decoding this offset and adding it to the base address of the object instance, we now can get a pointer to the backing field and thus modify it - voila write-access! Here is the short version:
procedure SetPixelsPerInch(Value: integer); begin PInteger(Integer(Screen) + (Integer(GetPropInfo(TScreenEx,PixelsPerInch').GetPrc) and $00FFFFFF))^:= Value; end;

So now you have a tool for the special occasion where you really need to modify a read-only property of a class that you don't have the luxury of modifying the source code for. Use it sparingly! )
Hallvard Vassbotn has been a professional programmer since 1985 and he is now the Head of Client Development at Infront AS (www.infront.no). You can read Hallvard's blog at hallvards.blogspot.com.

That is indeed a cryptic one-liner! Let's expand in into something slightly more readable.
procedure SetPixelsPerInch(Value: integer); var PropertyRTTI: PPropInfo; Offset: integer; PixelsPerInch: PInteger; begin PropertyRTTI := GetPropInfo(TScreenEx, PixelsPerInch'); Offset := Integer(PropertyRTTI.GetProc) and $00FFFFFF; PixelsPerInch := PInteger(Integer(Screen)+ Offset); PixelsPerInch^ := Value; end;

June 2008 BLAISE PASCAL 2

Page 11 / 1825

Resizing Bitmaps

2005 Win32

2007 Win32

by David Dirkse
This area is the product dx * dy where: dx is the horizontal edge and dy is the vertical edge of the rectangle defining the overlap. Notice, that f1 * f1 is the total size of the dbm pixel projected on sbm. (a pixel is given dimensions 1 * 1) For each pixel [i,j] in sbm , we calculate dx and dy. With f2 = f * f , variable AP = dx * dy * f2 is the weight factor for the color of pixel[i,j]. So, if AP = 0.2 , this color contributes for 20% to the final color of pixel[x,y] in dbm.

This article describes how a bitmap can be resized. For this purpose, we use a source bitmap (called sbm) to be resized to a destination bitmap (called dbm). A digital picture is made of rows and columns of dots, called pixels. A Bitmap is a flexible component to store and manipulate pictures. A Bitmap is device independent: it is only a table in memory and must be copied to a paintbox to show its contents to the user.
See figure 1, where a pixel is represented by a square. The color of a pixel is defined by a number. So, a picture is a (2) dimensional table of numbers. Several formats exist for this numbers. In this article, we use the 32 bit pixelformat. See figure 2. The number has 3 fields of 8 bits each, representing the intensity of primary colors red, green and blue. Also shown is the Windows internal color format.

Figure 1. If the multiplication factor is f , then : := sbm.width * f dbm.width := sbm.height * f. dbm.height Also we define : f1 = 1 / f f2 = f * f

Figure 3. Initially, the value of the dbm pixelcolor is set to 0. For each i and j we summarize: color = color + pixelcolor[i,j] * AP. This should be repeated 3 times of course, for red, green and blue.

Figure 2. How it works Pixels of dbm are addressed 1 by 1 from left-top to right-bottom. Each pixel of dbm is projected over sbm. This projection will (partially) cover one or more pixels of sbm. Each (partially) covered pixel of sbm will proportionally contribute to the final color of the destination pixel. See figure 3, where an 8 x 8 bitmap is reduced to 5 * 5, so f = 5 / 8 = 0,625. To illustrate the process, part of sbm is enlarged, see figure 4. The projection of the addressed dbm pixel is painted in red. In this figure we notice: sy1 sy2 sx1 sx2 = f1 * y = sy1 + f1 = f1 * x = sx1 + f1 Figure 4. Adressing pixels in a bitmap Two ways exist to address pixels in a bitmap: 1: with property pixels[..,..] of the bitmap's canvas: color := sbm.canvas.pixels[3,2] copies the color of column 3, row 2 of the picture into variable color Note, that in this case color has the internal windows format, regardless the format of the data in the bitmap. This way of working with pixels is very slow, because Windows is called.

The sx1....sy2 are single type real variables. To calculate the color of the destination pixel [x,y] , the colors of sbm pixels [i,j], [i+1,j], [i+2,j], [i,j+1]...............[i+2,j+3] must be added together, proportional to the area that is occupied on sbm.

Page 12 / 1826

June 2008 BLAISE PASCAL 2

Resizing Bitmaps

2005 Win32

2007 Win32

2: using the scanline[..] property of the bitmap if we define :


type TWA = array[0..2500] of LongInt PWA = ^TWA var py : PWA

py is a pointer to the first element of a (long) array of LongInteger values.


py := dbm.scanline[y]

followed by
py^[x] := color

copies color to row y , column x. Note, that color should have the (internal) bitmap format, as Windows is not called and no conversion takes place. The speed is much, much higher than in case 1, so this way is recommended. Calculating dx en dy Initially dx and dy are set to 1, the dimensions of a pixel. So, if a complete pixel is overlapped, these values are correct. In cases when pixel boundaries of sbm are crossed or partial pixels are covered, there are two possibilities: left or right (high or low) orientation of the rectangle dx * dy. For dx:
Right : if sx1 > i, Left : if sx2 < i +1, then dx := dx (sx1 - i) then dx := dx (i +1 sx2);

David Dirkse Born 1945 in Amsterdam, David joined Control Data Corporation in 1968, after a study in electrical engineering. As a hardware engineer, he was responsible for installation and maintenance of mainframes at scientific data centers in the Netherlands. At the decline of CDC around 1990, he started a June 2008 BLAISE PASCAL 2 a math teacher. study of mathematics and became His hobby is programming, in particular educational software, math algorithms and puzzle solving.

http://home.hccnet.nl/david.dirkse

There is also a Dutch edition of

Blaise Pascal Magazine


If you are interested in the Dutch version please send an email to office@blaisepascal.eu and we will assist you.

A similar explanation holds for dy. Manipulating colors pj is the pointer to the first element of row j in sbm.
Color := pj^[i]

copies pixel[i,j] of sbm to variable color. Note, that color now has the exact format of the data in the bitmap as mentioned before. Unpacking the primary colors into (byte) variables sB,sG en sR for blue, green and red :
sB := color and $ff sG := (color shr 8) and $ff sR := (color shr 16) and $ff

How to become a subscriber


First of all you need to decide whether you want: 1 the printed version*, code included, fully printable version,lectronic version download included; including tax, excluded postage (4 issues = 20,00 euros, total 45,00) You can have your electronic version immediatly whilst the printed issue is on its way. the no paper (download) version, printing enabled and code included.(you don't have to pay postage, just pay 25,00). the basic download version, without code, without downloads and printing disabled. The second issue is not free but costs 10,00. Any first issue can be downloaded at any time. (If it is your first download.) Special limited-time offer:

Now, we can make calculations with sB, sG and sR before packing and copying them to the destination pixel. Please look at the code listing for details. About the program The language is Delphi, version 7. Procedure resize is actually doing the work. Width and height of dbm are calculated according to the sbm dimensions and factor f. Some buttons are added to test the algorithm: picture : a picture (format *.bpm) can be loaded into sbm from a file. drawing : draws a raster with ellipses in sbm Two paintboxes, sbox en dbox, show bitmaps sbm en dbm on the screen. Procedure edge draws an edge around a paintbox. The edge is actually painted on the canvas of form1, the parent of the paintbox. This is only for esthetical reasons. Please refer to the listing for more details. Limitations The program works fine in case of size reduction. When pictures are enlarged, the results are sometimes coarse. Interpolation would probably yield a smoother result, especially in the case of photographs. Technical drawings and graphics are a different case, because they contain information on an otherwise meaningless background. So, in this case background colors need special treatment (omission). However, this may be the topic of another article.

2 3

subcribe
to the printed version or no paper download version

for the first year * 25.00


You can pay with PayPal or by credit card (Take Two) or by bank. Look for subscription details on page 2 Page 13 / 1827

Making right-justified Edits


Edits are always left-justified in Delphi, which isn't so nice with numbers. In Windows, it's possible to make Edits leftjustified, centred or right-justified at least since Windows 98. However, this property is not included in Delphi. Of course, you can create your own component and add this property to it, but it is also possible to add new properties without having to install a component. You can place the code for this in a separate library unit and then use it in any desired Form. To do this, you must include the library unit at the beginning of the program in your 'uses' section. You also have to use the FormCreate event to set the alignment of the Edit(s) to taRightJustify.
The simple way There's also a very simple way to make an entry field right-justified. You can use a Memo to imitate an Edit by setting its height to '21' (the standard Edit height), setting WantReturns and WordWrap to 'False', and setting Alignment to 'taRightJustify'. Then empty Lines, and you have a nice pseudo-Edit. I already described this in Blaise 75. Another approach is to set the BiDiMode property to 'bdRightToLeft'. This property is intended to be used with non-European languages (Arabic, Japanese, etc.) that are written and read from right to left. I don't recommend this approach, because it works on some computers but not on others, depending on which version of Windows is installed. Windows MSDN (SDK) If you want to use a real Edit instead of a Memo, you have to take a different approach. At first glance it doesn't appear easy if you look in Windows Help for Delphi, you will see: There are three styles that cause Windows to align the text in an edit control, ES_LEFT, ES_CENTER, and ES_RIGHT. These styles apply only to multiline edit controls. Here 'only multiline controls' means that it works with a Memo but not with an Edit. However, this is no longer true, as you can see from the MSDN library, which you can find on the Web at http://msdn2.microsoft.com/en-us/library. This library is so large that you can easily get lost in it, so I have illustrated the path to the Edit styles in Figure 1 (with hundreds of items omitted).

2005 Win32

2007 Win32

by Henk Schreij

This means that you cannot use right justification in Windows 95 or earlier, but you can use it in later versions. Note the second sentence, which says that the Edit style must be declared in the Create and cannot be modified afterward. This means that you must declare the alignment in the FormCreate of the Form containing the Edits. Using a library unit You can place the code where you declare the alignment, in the unit that contains the Edits. Do this only for testing, since copying the same code to every unit becomes tiresome if you use lots of units. For this reason, it is better to put the code in a separate unit that you can link into the forms that contain the Edits. Now I'll show you how to do this. Start Delphi, create a new VCL application, and place a couple of Edits on Form1. The library unit does not have to display, so it can be a formless unit. To generate this, select File / New / Unit as shown in Figure 2 (example from Delphi 2007; other versions are slightly different).

Figure 2. Adding a formless unit for the library unit You will see an empty framework with the following code:
unit Unit2; interface implementation end.

Here you have to insert the code for assigning right-justified alignment to an Edit. To do this, type the following code in the framework and save it (e.g. as 'EdtBib'):
unit EdtBib; interface uses Windows, Classes, Controls, StdCtrls; type TAlignEdit = class(TEdit) private FAlignment: TAlignment; protected procedure SetAlignment(const Value: TAlignment); virtual; procedure CreateParams(var Params: TCreateParams); override; published property Alignment: TAlignment read FAlignment write SetAlignment; end; implementation procedure TAlignEdit.CreateParams(var Params: TCreateParams); begin inherited; case FAlignment of taCenter: Params.Style:= Params.Style or ES_CENTER; taRightJustify: Params.Style:= Params.Style or ES_RIGHT; end; end; procedure TAlignEdit.SetAlignment(const Value: TAlignment); begin FAlignment:= Value; RecreateWnd; end;

Figure 1. Finding the ES_RIGHT Edit style in the MSDN library The Edit Control Styles page says:
Windows 98/ME, Windows 2000/XP/Vista: Aligns text in a single-line or multiline edit control. After the control has been created, these styles cannot be modified.

end.

A uses with StdCtrls (the unit in which the Edit is declared) is placed at the beginning of the code. The Windows, Classes and Controls units are also necessary because functions and constants that are used in the subsequent code are declared in these units.

Page 14 / 1828

June 2008 BLAISE PASCAL 2

2005 Win32

2007 Win32

Making right-justified Edits

If you have ever created a component, the rest of the code should look familiar. Otherwise, just be sure to copy everything exactly. The most important here is the 'Alignment' property. In CreateParams, inherited is used to take over the existing parameters. After this, an or is used to add taCenter with the ES_CENTER style (previously seen in the Windows MSDN). The same is done with taRightJustify. These styles tell Windows whether the text should be centred or right-justified. SetAlignment causes the FAlignment field to receive the selected value and invokes redrawing of the Edit with RecreateWnd. This pretty much covers the library unit. To right-justify a second Edit, set the alignment to 'taRightJustify' in the OnCreate of Form1. The full code (append at 1, 2 and 3) is:
unit EdtRechtsU1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, {<--(1)-} Dialogs, StdCtrls, EdtBib; type {<--(2)-} TEdit = class(TAlignEdit); TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; procedure FormCreate(Sender: TObject); private {Private declarations } public {Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Edit2.Alignment:= taRightJustify; {<--(3)-} end; end.

unit EdtBib; interface uses Windows, Classes, Controls, StdCtrls; type {<--(I)-} TEdit = class(StdCtrls.TEdit) private FAlignment: TAlignment; protected procedure SetAlignment(const Value: TAlignment); virtual; procedure CreateParams(var Params: TCreateParams); override; published property Alignment: TAlignment read FAlignment write SetAlignment; end; implementation procedure TEdit.CreateParams(var Params: TCreateParams); {<--(II)-} begin inherited; case FAlignment of taCenter: Params.Style:= Params.Style or ES_CENTER; taRightJustify: Params.Style:= Params.Style or ES_RIGHT; end; end; procedure TEdit.SetAlignment(const Value: TAlignment); {<--(III)-} begin FAlignment := Value; RecreateWnd; end; end.

The declaration of the alignment in Form1 then becomes:


unit EdtRechtsU1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, {<--(a)-} Dialogs, StdCtrls, EdtBib; type TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; procedure FormCreate(Sender: TObject); private {Private declarations } public {Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.FormCreate(Sender: TObject); begin Edit2.Alignment:= taRightJustify; {<--(b)-} end; end.

At the first arrow (1) you see that the EdtBib unit (with the AlignEdit) is included in the uses. At (2) with type, you see that the Edit originates from AlignEdit, so you can use the alignment declared in FormCreate (3). The Form at design time and run time is shown in Figure 3:

Figure 3. A right-justified Edit at design time and run time


Improving the code A further improvement is possible, which allows you to omit the type declaration at (2). To achieve this, you must put the following code in the library unit, to have an Edit directly originated from an Edit: TEdit = class(StdCtrls.Edit) You also have to 'revise' the code for the Edit in the library unit at three places, as shown in the following code:

There is one disadvantage with this approach: it is essential to place the EdtBib in your uses section after StdCtrls (a). Otherwise you will receive an error message with Edit2 (b) saying that the alignment was not recognised. This is why some programmers prefer the previously described solution with AlignEdit. Conclusion Right-justification of an Edit in order to display numbers is not implemented in Delphi. As this is actually possible with Windows 98 and later. A library unit is presented containing code where the alignment can be declared. Coding this library unit is not a lot of work if you do it only once, and it is very easy to use. The approach described here shows that you can in fact add properties to an existing component without creating a new component. You can use this approach in other situations as well.

June 2008 BLAISE PASCAL 2

Page 15 / 1829

Delphi 2007 VCL Component Building - II


Last time, we made a custom TShadingLabel component, and demonstrated how it looks and behaves on a form. However, in the Object Inspector, we noticed that the new properties Offset and LabelStyle are only shown in the Miscellaneous category, but not in any of the other categories.

2007 Win32

by Bob Swart

The third variation identifies the property using its type rather than its name. The following line of code registers a property based on its TLabelStype type. RegisterPropertyInCategory('Visual', TypeInfo(TLabelStyle)); Although it's probably safe to assume that any use of the TLabelStyle in a property will affect the visualisation of the component, and should hence be placed in the Visual category, a safer approach is the final variation, which uses both the property's type and its name to identify the property. The following line of code registers a property based on a combination of its TLabelStyle type and its LabelStyle name. RegisterPropertyInCategory('Visual', TypeInfo(TLabelStyle), 'LabelStyle'); As a result, the Register procedure contained in the eBob42.Register unit for the TEuro, TMultiLineButton, TRightEdit and TShadingLabel should be modified as follows to ensure that the Offset property in the TShadingLabel component, and the LabelStyle property of type TLabelStyle are both shown in the Visual category in the Object Inspector (note that I've also added the RegisterPropertyInCategory call that will put the MutiLine property of the TMultiLineButton component in the Visual category of the Object Inspector).

Figure 1.
That will be solved in this article, and we'll also cover the use of packages and how to create the custom component bitmap file. Property Categories Delphi offers the ability of grouping properties and events together in special categories. The Object Inspector can actually arrange the properties in two ways: by category or by name. The latter is the view we've been used to since Delphi 1, and lists all properties in an alphabetic order. Including many of the properties and events that you'd seldom use. As of Delphi 5, we can also instruct the Object Inspector to display the properties in a list of categories. As soon as you do this, the list of properties is replaced by a list of property and event categories. It really helps to open up only a single relevant category and work with the properties and events in that category, than to have to look through the entire list of (sometimes more than hundred) properties and events. As a component developer, we can not only add properties to categories, but also add new categories. The Register procedure, so far only calling RegisterComponents, is also the place to register your new custom properties in categories. We can use the overloaded procedure RegisterPropertyInCategory from the DesignIntf unit (in the DesignIDE package) to register a property in a category. RegisterPropertyInCategory There are actually four overloaded versions of RegisterPropertyInCategory. The first variation lets you identify the property by the property's name. The following line of code registers a property related to visual display of the component, identifying the property by its name Offset.
RegisterPropertyInCategory('Visual', 'Offset');

procedure Register; begin RegisterComponents('eBob42', [TEuro]); RegisterComponents('eBob42', [TMultiLineButton]); RegisterPropertyInCategory('Visual', TMultiLineButton, 'MultiLine'); RegisterComponents('eBob42', [TRightEdit]); RegisterComponents('eBob42', [TShadingLabel]); RegisterPropertyInCategory('Visual', TShadingLabel, 'Offset'); RegisterPropertyInCategory(Visual', TypeInfo(TLabelStyle), 'LabelStyle'); end;

The second variation is much like the first, except that it limits the category to only those properties of the given name that appear on components of a given type. The following line of code registers into the Visual category again a property named Offset of a component of the custom class TShadingLabel.
RegisterPropertyInCategory('Visual', TShadingLabel, 'Offset');

This is actually a safer approach, since a property called Offset might be used by other components, in which case the Visual category is not automatically the best one.

Figure 2.

Page 16 / 1830

June 2008 BLAISE PASCAL 2

2007 Win32

Delphi 2007 VCL Component Building - II


DesignIntf unit and DesignIDE package Like I also mentioned when we implemented the TMultiLineButton component, the call to the RegisterPropertyInCategory method doesn't compile just yet. We have to add the DesignIntf unit to the uses clause of the eBob42.Register.pas unit.
unit eBob42.Register; interface procedure Register; implementation uses Classes, Controls, StdCtrls, eBob42.Euro, eBob42.MultiLineButton, eBob42.RightEdit, eBob42.ShadingLabel, DesignIntf;

However, if we do that, then the compiler will complain that it cannot find the DesignIntf.dcu file. The DesignIntf unit cannot be found on your disk, but is part of the DesignIDE.dcp package (found in the C:\Program Files\Borland\BDS\3.0\lib directory for Delphi 2005 or BDS\4.0\lib directory for Delphi 2006 or RAD Studio\5.0\lib for Delphi 2007). In order to resolve the compiler error, we must add the DesignIDE.dcp package to the Requires list of the eBob42 package. Right-click on the Requires node in the Project Manager, and select Add Reference which results in a dialog where you can locate the DesignIDE.dcp package (depending on your version of Delphi):

Figure 4.
Packages There are three kinds of packages: runtime packages, design-time packages, and hybrid packages that can be used both at runtime and design-time (like our current eBob42 package). The latter should be used with care. And there are two kinds of package output files: .bpl and .dcp files. The .bpl file is the compiled package that can loaded at runtime (or design-time), while the .dcp file is a collection of all .dcu files that were required to build the package. You need the .dcp file to allow developers to link against your runtime package, unless you want to distrbute all individual .dcu files. A runtime package is used for code that implements the components like TEuro, TMultiLineButton, TRightEdit and TShadingLabel. A design-time package is used for design-time support, registering the components, registering properties into categories, etc.. Right now, our eBob42 package contains a mix of runtime and design-time code. We should fix that by creating a second package called eBob42.Design which uses the eBob42 package, but only contains the design-time support. The original eBob42 package can then be turned into a runtime only package. The reason why we haven't had to concern ourselves with this up until this point is the fact that by default a Delphi package is both a runtime and a design-time package, as we saw when we looked at the Project Options dialog for the eBob42 package. This may be handy when starting to develop components, but not good when you want to deploy them (since it would mean using design-time supporting code at runtime, which is really not needed at all).

Figure 3.
This will add the DesignIDE.dcp package to the Requires node of the eBob42 package, and will allow the compiler to find the DesignIntf unit. Although the compiler error is now gone, the solution of adding the DesignIDE package to the Requires list and the DesignIntf unit to the uses clause still mixes design-time and runtime behaviour of the VCL components in one package, which is not a good idea. If you right-click on the eBob42 package in the Project Manager and select Options, you'll get the Project Options for the eBob42.bpl package, which includes the fact that the package is set to be designtime as well as run-time: At this time, we've isolated the design-time related code as much as possible in the eBob42.Register.pas unit, but it would be a better idea to split the eBob42 package in a runtime and design-time specific part.

Design-Time Package So, let's try to clear up the issue by adding another package to the project group. Go to the Project Manager again, right-click on the ProjectGroup node, and do Add New Project to give you the Object Repository. In the Delphi Projects category, double-click on the Package icon:

Figure 5.

June 2008 BLAISE PASCAL 2

Page 17 / 1831

2007 Win32

Delphi 2007 VCL Component Building - II


This will add a new package project to the ProjectGroup, with a default name of Package1. Rename this package project to eBob42.Design.bdsproj, placing it in the same directory as the original eBob42 package. In order to let the eBob42 package compile with the DesignIntf unit in the uses clause (to support property categories), we had to add the DesignIDE package to the Requires list. Because the eBob42 package will become the runtime package, we need to remove the DesignIDE package from the Requires list again, and instead add it to the Requires list of the eBob42.Design package. Runtime Package Returning to the eBob42 package, we have to remove the eBob42.Register unit from the Contains list. This will ensure that no components are registered in this runtime only package, so it only contains runtime code. As last step, we have to mark the package as runtime only, using the Project Options dialog again.

Figure 7.

We should also add the eBob42 package to the Requires list of the eBob42.Design package, since the design-time package must use the runtime package (which contains the components that will be registered by the design-time package). Note that the Requires list contains the .dcp files. The .bpl and .dcp files for custom packages like eBob42 can be found in the Borland Studio Projects\Bpl directory under My Documents. We must also mark the eBob42.Design package as a design-time only package. Make sure it's the active project in the Project Manager, and do Project | Options to get the Project Options dialog for eBob42.Design. Check the Designtime only usage option, and feel free to enter a Description for the design-time package as well. Close the dialog when you're done.

If you compile the eBob42 runtime package again, you will get a notification message from the Delphi IDE, telling you that the package eBob42 is no longer installed (as design-time package), since it's not a design time package anymore. After that, you also will get a notification message that the registered VCL components are also no longer registered. They will be re-registered as soon as we install the eBob42.Design package again, of course. Project Manager The Project Manager should show the eBob42.bpl runtime and the eBob42.Design.bpl design-time packages with their contents. Obviously, we have to install the eBob42.Design.bpl package in order to add the four VCL components and their design-time support to the Delphi IDE again. We can right-click on eBob42.Design and select Install as a quick way to do so. This will give us a messagebox to inform us of the fact that TEuro, TMultiLineButton, TRightEdit and TShadingLabel have been installed again. Once the VCL components are installed again, we can place a TMultiLineButton ot TShadingLabel component on a VCL form to verify that the properties are now correctly categorised (for example the MultiLine, Offset or LabelStyle properties). For the LabelStyle property, we'll see the Visual category being used, and no longer the Miscellaneous category. Also note the fact that the lsLowered value is not in boldfact but in normal font, to indicate that this is the original (default) value of the property. Miscellaneous There are a few miscellaneous component building techniques that need to be covered here (see the on-line help for more details).

Figure 6.
Apart from the designide.dcp and eBob42.dcp packages, the designtime package eBob42.Design should get the eBob42.Register unit to contain the component and property category registration code.

Figure 7. Page 18 / 1832

Figure 8.

2007 Win32

Delphi 2007 VCL Component Building - II


the components that are registered in eBob42.Register.pas. For the TRightEdit component that's TRIGHTEDIT, and for the TEuro component that's TEURO, etc.. It's recommended to place all component bitmaps in one .dcr file), as long as they are all 24x24 in size using 16-colors.

First of all, we can use ComponentState to allow different behaviour of the component at design time and runtime, by checking if csDesigning is in the ComponentState set. The Loaded method is called after all properties are streamed in, to cater for the fact that properties will come in some (perhaps inappropriate) order. Consider a TTable - maybe the TableName property is read in, then the Active property. If the DatabaseName property hasn't been read in, then the SetActive property will blow up with an exception. However since it is still be read in (another ComponentState set element), the table is not opened till Loaded. This is just an example tp illustrate the point (while the actual TTable implementation may differ, the use of the Loaded method will be helpful in similar situations). Notification and FreeNotification allow a component to set a component reference to nil when that component gets destroyed, rather than leaving a dangling pointer. Consider a TDataSource's DataSet property. The underlying field would remain non-nil when the DataSet is destroyed if it weren't for Notification. Notification is automatically called for components owned by the same owner as the destroyed item. FreeNotification allows other things which aren't owned by the same owner (ie aren't on the same form or data module) to also work (linking across forms) Palette Bitmaps Now that we've designed and implemented new components, we may want to give each one of them a unique palette bitmap. Every component needs a bitmap to represent itself on the Tool Palette. If a component does not have a bitmap specified, Delphi uses the bitmap of the ancestor (back to TComponent itself, if necessary). Remember that two of our new VCL component classes (TMultiLineButton and TShadingLabel) contain existing component bitmaps inherited from their ancestor class, while the other two component classes (TEuro and TRightEdit) are using the default component bitmap from the TComponent base class. While the TShadingLabel and TMultiLineButton have the same bitmap as their parent, TLabel and TButton, these bitmaps don't represent the new feature of these two new components. And TEuro and TRightEdit only show the default bitmap. A palette bitmap for a component is easy to create: all we need to do is to make a Windows (compiled) resource file with a 24x24 bitmap. You can use any resource editor you want, including the Image Editor available in the Tools menu of the Delphi 2005 IDE (but which is no longer part of the Tools menu in Delphi 2006 or 2007 if you do not have a copy of imagedit.exe from Delphi 2005, you will have to find another resource editor instead). Do Tools | Image Editor from the Delphi 2005 IDE to start the Image Editor. Then, do File | New Component Resource File (.dcr), which will result in an Untitled1.dcr project. We then need to add a new bitmap, using Resource | New Bitmap, and specify a 24x24 pixel and 16 colour bitmap:

Figure 10.
Note that the colour in the lower left corner pixel is usually considered the invisible colour. When you're finished, save the eBob42.Register.dcr file. In order to enforce the compiler to pick it up, you can remove the eBob42.Register.pas unit from the eBob42.Design package, and readd it in order to add both the eBob42.Register.dcr and eBob42.Register.pas files to the Contains node. Warning: this will not update the component bitmap files right away. You have to restart Delphi 2005 (and sometimes even log out and back into Windows). After that, the new component bitmap are shown correctly in the Tool Palette. When everything has been done right, then the TEuro, TMultiLineButton, TRightEdit and TShadingLabel custom components can appear with their own custom bitmaps in the Tool Palette (note in the screenshot on the next page, only the TEuro and TRightEdit use custom bitmaps, the TMultiLineButton and TShadingLabel still use their inherited bitmap).

Figure 11.
Summary In this article, I've covered property categories, run-time and designtime packages, and demonstrated how to create the custom component bitmap file. We've now completed the coverage of a simple component, but there are more IDE support capabilities, including property and component editors that will be covered next time. For more information about Delphi Custom Component Development, check out my 175-page courseware manual Delphi Win32 / .NET Component Development available from Lulu.com for 30 Euro (printed) or 24 Euro (download), see http://stores.lulu.com/drbob42 for more details.

Figure 9.
This will result in a little Treeview for the Untitled1.dcr file, with Contents, Bitmap and Bitmap1 as leaf node. You need to use a name similar to the unit that contains the Register procedure, which is eBob42.Register.pas, so the .dcr file should be saved in eBob42.Register.dcr in order to allow Delphi to automatically use the bitmaps inside. You need to rename Bitmap1 and give it the same name as one of

June 2008 BLAISE PASCAL 2

Page 19 / 1833

ClientDataSet Filtering
Working with client datasets often requires some sort of filtering. This time I'm going to create a generic class to do just that on the fly. For this purpose I'm going to create a few classes (TFilterList and TFilterItem) together with a small program to demonstrate how it works. This demo is a .Net program.

2005 Win32

2007 Win32

by Martin de Bont
As you can see this class contains a few more fields and methods and two properties. For less experienced programmers let me first explain the 'overload' and 'reintroduce' keywords written behind some of these methods. The keyword 'overload' means that you are using methods (functions / procedures) with the same name but with different number and/or type of parameters. The keyword 'reintroduce' means that you are using a method that is already existing in the parent class you're inheriting from that you want to overwrite with code of your own (for consistency reasons). Using properties is a way to call methods by assigning values to that property just the way you assing values to variables. If you assign a value to a property the program will actually fire a procedure connected to this property. There are two methods belonging to a property. The Get (read) and the Set (write) method. Leaving out one of these will make the property either read-only or write-only. I expect you to let Delphi do as much work as possible. After you have keyed in the class definition, press <Shift><Control>C and Delphi will write all the stubs for you. Now you only have to write the additional code yourself. Reading through the code of which I assume you downloaded a second a go, will explains itself mostly. The property FilterCallBack is of type TFilterCallBack which is defined as: TFilterCallBack = procedure(msg: string) of object; You can assign any method to this property as long as this method has the correct number and type of parameters. In the demo-program you will see the following code snippet:

In this program I make use of a text file holding the names of the countries in the world with their associate capital cities, number of inhabitants and the region in the world where the countries reside in. I admit it is not the most challenging data but it will do for now. This file is small, not up-to-date and is named WorldCountries.csv This file will be imported after pressing a button. Before I begin jotting down the code I will give you a preview of the final result of my 'hard' work.

Figure 1 : In this case I selected all countries in Africa


with less than 500,000 inhabitants The two classes will be put in one unit (ucFilter) purely for my convenience. Good programming practice is creating a unit for every single class, then again good programmers are lazy and do as litle as possible. Having that said does not mean that I'm a good programmer. Just a lazy one. The demo will be put in unit (ufMain). I will start with the two classes. The TFilterList class is basically a list with objects and is descending from TObjectList. In this case it will hold objects of type TfilterItem, which is a simple class with only 3 fields and one constructor.

TFilterItem
TFilterItem = class : String; Key : String; Value QuickExpr : Boolean; public constructor Create(AKey: String; Value: String; QuickExpression: Boolean); end; implementation { TFilterItem } constructor TFilterItem.Create(AKey, AValue: String; QuickExpression: Boolean); begin inherited Create; := Akey; Key := AValue; Value QuickExpr := QuickExpression; end;

Figure 2. TFilterList
TFilterList = class(TObjectList) private FDataSet: TDataSet; FFilterCallBack: TFilterCallBack; procedure SetDataSet(const Value: TDataSet); procedure SetFilterCallBack(const Value: TFilterCallBack); public constructor Create; procedure ActivateFilter; procedure Add(AItem: TFilterItem); reintroduce; procedure RemoveFilter; procedure RemoveLastExpression; function GetItem(i: Integer): TFilterItem; overload; function GetItem(s: String): TFilterItem; overload; function Remove(AKey: String): Integer; reintroduce; function Text: String property DataSet: TDataSet read FDataSet write SetDataSet; property FilterCallBack: TFilterCallBack read FFilterCallBack write SetFilterCallBack; end;

The first two parameters of the constructor will hold a key and its value, the last parameter needs some extra explaining. There are two places where a filter expression can come from. It is either a value coming from a data grid cell, or it is value coming from a dialog box. If it is coming from a dialog box then QuickExpr is True, False otherwise. The purpose of QuickExpr is telling the TFilterList class how to display the filter expression (TFilterList.Text). Nothing more.

Page 20 /1834

June 2008 BLAISE PASCAL 2

ClientDataSet Filtering
2005 Win32 2007 Win32

Binary heaps
2005 Win32

Julian Bucknall

Quickly finding the largest item in a collection of items


2007 Win32

FFilter.FilterCallBack := DisplayFilter;

Here the callback method DisplayFilter is assigned to the property FilterCallBack. Every time the filter changes a call is made to this method, updating (in this case) the current filter expression. To make use of this filter class you must include the unit in the main uses section of your program. Declare a private field in the Tform1 to hold the filter list.
private FFilter: TFilterList;

When you mention heap people automatically think of memory allocation. A binary heap is not that kind of heap. It's a quite remarkable and simple data structure that has great power. It is a container of objects into which you can add objects and from which you can easily and quickly pull out the largest object, and do both operations very quickly.
It seems that a binary heap is one of the best kept secrets in computer science. Oh sure, people have heard of stacks and queues, binary trees, hash tables, and will argue about the merits of a red-black tree versus an AVL tree, but when you mention heap people automatically think of memory allocation and deallocation and where objects come from, fully formed. A binary heap is not that kind of heap. It's a quite remarkable and simple data structure that has great power. It is a container of objects into which you can add objects and from which you can easily and quickly pull out the largest object. A simple change to the implementation of the container will enable you to pull out the smallest object instead. From this rather trivial sounding data structure, we can get a simple, yet very fast, sorting algorithm, we can maintain a list of prioritized tasks, to even manage chunks of reusable memory. When talking about a data structures in the abstract, we're concerned about the operations we want to perform on that structure, how quickly we want certain operations to run (because we use them a lot), whether we're concerned about the speed of other operations, and so on. So, for example, with the stack structure, we want very fast Push and Pop operations since those are the two operations we shall be performing the most. Counting the number of items in a stack may be a "lesser" operation: maybe we're not that interested in doing it all that often. Another property of abstract data structures may be the efficiency with which it uses memory. How much overhead is there in creating such a data structure? So for example, implementing a stack as a linked list means that we have overhead for each node in the list, but implementing it in an array-like structure will only have the array's overhead. Another consideration on modern architectures is locality of reference. How close together are all the memory allocations that make up the data structure? The closer they are, the less likely we'll suffer a page fault -- that memory will be swapped out -- when using the data structure. With a binary heap, there are two operations we're really interested in: adding a new item to the heap -- call this the Insert() method - and removing the largest item from the heap -- call this RemoveMaximum(). We should optimize performance for these two operations. In doing this, we'll also make the broad statement that we'd like to use the minimum amount of memory we can. There are of course other operations we could ask for. Remove(), for example, might be a method that removes any item in the heap. If we are to have that, we should presumably have Contains() or Find() (or both) to determine whether a particular item is in the heap and optionally return it. Possibly, we might want RemoveMinimum() as well. And so on. My overall point here is that as we add more operations we're going to make the implementation more and more complex, and, for a binary heap, the standard two operations will produce a very simple implementation with good characteristics. Another question we have to answer is this: how quickly is quickly? The faster we want an operation, the more trade-offs there will be. For this particular implementation, we'll state that inserting an item and removing the maximum should be O(log(n)). That is, the time taken for each of these operations should be proportional to the logarithm of the number of items in the heap, with as low a constant of proportionality as we can muster.

Create the filter list and let it point to the client dataset. Install the callback function.

Figure 3.
// Create filterlist FFilter := TFilterList.Create; // Connect filter list to the dataset FFilter.DataSet := ClientDataSet1; // Install callback function FFilter.FilterCallBack := DisplayFilter; Next, we have to create methods to communicate with this filter list: - AddToFilter - AddInputValueToFilter - ClearFilters - RemoveLastFilter and - DisplayFilter AddToFilter will add the value of the datagrid cell under the cursor. AddInputValueToFilter will show a dialogbox where you can enter you condition for the column the cursor is in. ClearFilters will remove all filter expressions and will show all data in the dataset. RemoveLastExpression will remove the filter expression you added as last. DisplayFilter will show the complete filter expression. I have left much room for enhancements and hope it is useful for you. Feel free to experiment with these classes and have fun! Martin De Bont started programming with Delphi 1 and is ever since a Delphi adept. Having changed to .Net, C#, and PHP as professional, is still using his knowledge for the community writes many articles for us. Any correspondence or questions you can address to:

m.de.bont@hcc-pgg.nl

June 2008 BLAISE PASCAL 2

Page 21 / 1835

Binary heaps

2005 Win32

2007 Win32

Quickly finding the largest item in a collection of items

A binary heap is such a structure. A binary heap is a binary tree such that: 1. For every node in the tree, the item in the node is larger than the items in its children. 2. Every level in the tree, apart from the last, is complete. 3. All nodes in the lowest level of the tree are to the left. The last level is filled from the left to the right. This is known as leftcomplete.

Figure 1 shows such a binary heap. There are four levels, the topmost three are full, and the bottom level is left-complete. A new node that is inserted would have to occupy the left child of the b node (fairly obviously, there would be some tree rearrangements to do). Identifying the largest item in the binary heap is easy: it's the root of the tree. Removing it though in RemoveMaximum() is problematic since deleting the node gives us two halves of a tree, which is pretty useless. So we have to patch up the tree somehow so that the heap properties are satisfied again. Since we know how the structure of the heap should look if there were one fewer node, we just move the last node of the last level (the rightmost) to the root of the tree. The structure (that is, rules 2 and 3) is now valid again, but rule 1 is likely to be invalid since we're moving a small item from the bottom of the tree to the top. So let's fix rule 1 up again. We use an algorithm called trickle-down. Look at the two children of the new root. Select the larger item and if it is larger than that at the root, swap it with the item at the root. The root now obeys rule 1. Move down to the child node that participated in the swap, and perform the same operation. Select the larger child item and swap it with the item if it is greater. Continue moving down the tree until the node we reach no longer has any children or until the item in the node is larger than both its children.

Now, how quick was the RemoveMaximum() operation? The initial removal of the largest item and moving the last item to the root were simple constant time operations, they didn't depend on the number of items. (We'll see later how to identify the last node immediately.) The main time spent in the operation was undoubtedly the trickle-down process. In a worst case scenario we'll have to trickle-down all the way to the bottom level. How far is that? The number of levels in a complete binary tree such as we have with a binary heap is log(n). So the time taken by RemoveMaximum() is going to be proportional to log(n). (Note that at each level we have to do at least two comparisons and that, in general, it's the comparisons that take the time.) Let's look at Insert() now. We know how to insert the node in order to solve the structure rules: add it as the last node on the lowest level (or start a new level, if the last level is already full). But again rule 1 will probably need fixing up. This time we use an algorithm called bubble-up. Look at the parent of the newly inserted item. If it is smaller, swap it and the new item, and then move up the tree. Repeat the same process at this higher level. Continue until either you reach the root, or until the item at the parent of the current node is greater than the item we're bubbling up.

add z as new node

compare, swap k and z

remove maximum

compare, swap m and z

move last item

compare, swap x and z

compare items swap g and x

compare items swap g and n

Figure 2 shows this trickle-down process, step by step, as we remove z, the maximum, from figure 1 and replace it with the g.

Figure 3 shows this bubble-up process, step by step, as we add the z back in. And how fast is Insert()? Again, assuming that adding the actual node does not depend on the number of nodes present, all the action is with the bubble-up process. If we assume worst-case again, the item may have to bubble all the way up to the root, through log(n) levels. So we can state that Insert() will also take log(n) time. (This time we only need to do one comparison per level, so we can say that Insert() will be roughly twice as fast as RemoveMaximum().) Is there any way we can improve the speed of RemoveMaximum() so that it approaches the speed of Insert()? Actually there is a simple tweak we can do that in the general case will improve the performance of the remove operation. Consider: when we remove the largest item, we replace it with an item on the lowest level, the rightmost one on that level. Think about that for a moment. It's very likely that the item we're swapping to the root of the tree is one of the smallest in the binary heap.

Page 22 / 1836

June 2008 BLAISE PASCAL 2

Binary heaps

2005 Win32

2007 Win32

Quickly finding the largest item in a collection of items


type TBinaryHeap = class private FItems : TList; function GetParent(index : integer) : integer; function GetFirstChild(index : integer) : integer; procedure BubbleUp(fromIndex : integer); procedure TrickleDown; procedure Clear; public constructor Create; destructor Destroy; override; procedure Insert(item : TObject; priority : IPriority); function RemoveMaximum : TObject; end; constructor TBinaryHeap.Create; begin inherited Create; FItems := TList.Create; FItems.Add(nil); end; destructor TBinaryHeap.Destroy; begin if Assigned(FItems) then begin Clear; FItems.Free; end; inherited; end; procedure TBinaryHeap.Clear; var i: Integer; tuple : TTuple; begin for i := 1 to FItems.Count - 1 do begin tuple := TTuple(FItems[i]); tuple.Priority := nil; tuple.Item.Free; end; FItems.Clear; end;

That's why it's on the bottom level, after all. So, it's very likely that it will return to the bottom level, or at least close to it. The tweak we'll discuss makes use of that fact by altering the trickle-down process to something more like the bubble-up process. Since the item is likely to be small, we shall swap it with the larger of its children every time until we get to the bottom level. In other words, instead of also checking whether the larger child is also larger than the item, we'll assume that it is so and in the process avoid one of the comparisons at every position. Of course, we may overshoot in this tweaked trickle-down and bring the item lower than it should be. No problem: we'll merely bubble-up the item from where it ends up. Because it's one of the smallest items in the tree anyway, it's likely that we may not need to do much bubbling up. Certainly we shall avoid many of the comparisons using this tweaked process than with the original that does a doublecompare at every step. Now that we have an optimized pair of operations, let's think about the in-memory representation of this data structure. The first, most obvious, solution is to mimic the abstract tree: declare a node type with links to one parent and two children and a reference to an item. That's a lot of overhead: three references to other nodes. Can we do better? Consider this. Number all the nodes from the root, level by level, from left to right on each level. So the root is node 1, its children are nodes 2 and 3, their children are 4 and 5, and then 6 and 7. (Figure 1 shows these node numbers.) The next level has nodes numbered from 8 to 15, and so on. Notice that, because of the definition of a binary tree (rules 2 and 3, in particular), there are no gaps in this numbering sequence. In fact we could use an array to hold the items. There is still an issue to solve though. We need to find the parent and children of a "node". Look closely at the numbering and you'll see a pattern. The children of node x are at 2x and 2x+1, and the parent is at node x/2 (using integer division, so the remainder is discarded). Since, in most programming languages, array elements are numbered starting at 0 instead of 1 we could either modify these formulas accordingly (children at 2x+1 and at 2x+2, parent at (x-1)/2, all of which are slightly less understandable), or we could just ignore the element at position 0 (if you like, it becomes part of the overhead). We'll use the "ignore element 0" version for this implementation. Now that we have discussed both the performance and the memory storage aspects of a binary heap, we can turn to the code. First thing to realize is that the items need to be comparable, or, better that there is some extra information (call it a priority in honor of the primary use of a binary heap) that is comparable. We have a choice here: either make the priorities themselves comparable (which is the OOP way, if you will) or we can declare a procedure type that compares two priorities, and then pass an instance of that type to the binary heap. In this article, I'll take the first approach, but will use interfaces to declare the comparison method.
type IPriority = interface function GetThis : TObject; function CompareTo(p : IPriority) : integer; end;

If you notice, the Clear() method makes use of a type called TTuple. This is merely an aggregate of an item with its priority. Time for the Insert()and the BubbleUp() methods. We'll separate them since the RemoveMaximum() method will eventually make use of the latter. Note in the Insert() method that we make use of the list's Add() method to add the new item.
procedure TBinaryHeap.Insert(item: TObject; priority: IPriority); var tuple : TTuple; begin tuple := TTuple.Create(item, priority); FItems.Add(tuple); BubbleUp(FItems.Count - 1); end; procedure TBinaryHeap.BubbleUp(fromIndex: integer); var parentIndex : integer; : TTuple; item : TTuple; parent begin item := TTuple(FItems[fromIndex]); parentIndex := GetParent(fromIndex); while (parentIndex > 0) do begin parent := TTuple(FItems[parentIndex]); if (item.Priority.CompareTo(parent.Priority) > 0)then begin FItems[fromIndex] := FItems[parentIndex]; fromIndex := parentIndex; parentIndex := GetParent(fromIndex); end else parentIndex := 0; end; FItems[fromIndex] := item; end;

Now we can declare the class. It'll have an internal TList to hold the heap (this means we can use the capabilities of TList to grow the internal array, should we need to), and so the constructor and destructor will have to be declared to create and destroy the list. In the constructor, we add the dummy element 0.

Although BubbleUp() looks complicated, it's not particularly. We make a copy of the item being bubbled up, and then enter a loop to do the actual bubbling up. We don't save the item back into the list until we've found its rightful position and exited the loop. Moving along, we should now add the RemoveMaximum() method. This method takes out the item at element 1, moves the item at the end of the list to the front, and then deletes the element at the end of the list. It then calls the TrickleDown() method to move this top item down the tree.

June 2008 BLAISE PASCAL 2

Page 23 / 1837

Binary heaps

2005 Win32

2007 Win32

Quickly finding the largest item in a collection of items

function TBinaryHeap.RemoveMaximum: TObject; begin if (FItems.Count = 1) then begin Result := nil; Exit; end; Result := TTuple(FItems[1]).Item; FItems[1] := FItems.Last; FItems.Delete(FItems.Count - 1); if (FItems.Count > 1) then TrickleDown; end;

Note that there are a couple of special cases: first, there is no item to remove (we merely return nil); second, we remove the only item and so there's no need to trickle down. Finally, it's the turn of TrickleDown() method. This uses the tweaked algorithm to quickly move the item down the list, and then calls the BubbleUp method to make sure it wasn't moved down too far.
procedure TBinaryHeap.TrickleDown; var index : integer; firstChildIndex : integer; secondChildIndex : integer; item : TTuple; firstChild : TTuple; secondChild : TTuple; begin item := TTuple(FItems[1]); index := 1; firstChildIndex := GetFirstChild(index); while (firstChildIndex < FItems.Count) do begin secondChildIndex := firstChildIndex + 1; if (secondChildIndex < FItems.Count) then begin firstChild := TTuple(FItems[firstChildIndex]); secondChild := TTuple(FItems[secondChildIndex]); if (firstChild.Priority.CompareTo(secondChild.Priority)>= 0) then begin FItems[index] := FItems[firstChildIndex]; index := firstChildIndex; end else begin FItems[index] := FItems[secondChildIndex]; index := secondChildIndex; end; end else begin FItems[index] := FItems[firstChildIndex]; index := firstChildIndex; end; firstChildIndex := GetFirstChild(index); end; FItems[index] := item; BubbleUp(index); end;

This code is complicated a little bit by the fact that the current node could only have one child. We test for a child being present by checking that its index is within the list. Again, we make use of the trick from BubbleUp: make a copy of the item, and then only set it back into the list at the very end. The class is now complete and can now be used as a delegate inside something like a priority queue class. Julian M. Bucknall
has worked for companies ranging from TurboPower to Microsoft and is now Chief Technology Officer for Developer Express.

http://www.devexpress.com his own site http://www.boyet.com/


one of his books: Tomes of Delphi: algorithms and data structures is available again. In next issue of Blaise Pascal Magazine there will be a review by Frans Doove.

Page 24 / 1838

June 2008 BLAISE PASCAL 2

IntraWeb 9.x and AJAX

2005 Win32

2007 Win32

by Bob Swart

IntraWeb version 9 introduced full AJAX support to IntraWeb developers. A large number of visible IW controls now have OnAsync events that fire asynchronous and can be used to update certain parts of your page without refreshing the entire page. Note however, that the session counter is still increased! So now more than ever, the use of the Back button is prohibited!
AJAX = Asynchronous AJAX is not really a new invention, but stands for Asynchronous Javascripting And XML. Fortunately for IntraWeb developers, you really do not have to know a lot about either JavaScript or XML in order to use AJAX. What it comes down to is that an AJAX event is happening asynchronously, without the need to send a complete request or the need to refresh the entire page. This means only a partial request will be made, and a part of the page will be refreshed, resulting in a more responsive and generally more pleasant user experience. IntraWeb supports AJAX events in a number of IntraWeb components through the OnAsync events and if present the SubmitOnAscynEvent property (by default set to True). OnAsync The power of AJAX is brought to us through the ease of the OnAsync event handler. For example, the TIWButton component has the OnAsyncClick and OnAsyncDoubleClick event handlers that we can implement using normal Delphi code. As a simple example, place a TIWListbox, TIWEdit and two TIWButtons on an IntraWeb Application Form.

If we click on the AJAX button, then the contents of the IWEdit will again be added to the IWListbox, but there will be no full request to the server. As a result, the session counter in the browser will stick at 1, but at the server it's already at 2.

Figure 3. We can prove this, by clicking on the Click button again, which will send a full request to the server, and add the text to the listbox once again, but also returns with the session counter of 3 (and not 2, since it already was 2 after the AJAX event).

Figure 4. OnAsync Events Not all components from the IW Standard category support OnAsync events.
The TIWButton control supports 4 AJAX events: OnAsyncClick, OnAsyncDoubleClick, OnAsyncEnter and OnAsyncExit. The TIWCheckBox and TIWRadioButton controls support 12 AJAX events: OnAsyncChange, OnAsyncClick, OnAsyncEnter, OnAsyncExit, OnAsyncKeyDown, OnAsyncKeyPress, OnAsyncKeyUp, OnAsyncMouseDown, OnAsyncMouseMove, OnAsyncMouseOut, OnAsyncMouseOver, and OnAsyncMouseUp. TIWComboBox, TIWListBox and TIWMemo support 14 AJAX events: OnAsyncChange, OnAsyncClick, OnAsyncDoubleClick, OnAsyncEnter, OnAsyncExit, OnAsyncKeyDown, OnAsyncKeyPress, OnAsyncKeyUp, OnAsyncMouseDown, OnAsyncMouseMove, OnAsyncMouseOut, OnAsyncMouseOver, OnAsyncMouseUp and OnAsyncSelect. The TIWEdit and TIWTimeEdit controls support 13 AJAX events: OnAsyncChange, OnAsyncClick, OnAsyncDoubleClick, OnAsyncEnter, OnAsyncExit, OnAsyncKeyDown, OnAsyncKeyPress, OnAsyncKeyUp, OnAsyncMouseDown, OnAsyncMouseMove, OnAsyncMouseOut, OnAsyncMouseOver, and OnAsyncMouseUp. TIWImage, TIWImageFile and TIWImageButton support 6 AJAX events: OnAsyncClick, OnAsyncMouseDown, OnAsyncMouseMove, OnAsyncMouseOut, OnAsyncMouseOver, and OnAsyncMouseUp. The TIWLabel control supports 6 AJAX events: OnAsyncClick, OnAsyncMouseDown, OnAsyncMouseMove, OnAsyncMouseOut, OnAsyncMouseOver, and OnAsyncMouseUp. TIWLink supports only one AJAX event: OnAsyncClick. The TIWTimer control supports only one AJAX event: OnAsyncTimer. The TIWTabControl supports only one AJAX event: OnAsyncChange.

Figure 1. One button has a caption of Click, and implements the normal OnClick event, while the other is given a Caption of AJAX, and implements the OnAsyncClick event:
procedure TIWForm1.IWButton1Click(Sender:TObject); begin IWListbox1.Items.Add(IWEdit1.Text) end; procedure TIWForm1.IWButton2AsyncClick(Sender: TObject; EventParams: TStringList); begin IWListbox1.Items.Add(IWEdit1.Text) end;

If we now enter Delphi and AJAX and click on the Click button, the listbox will contain the string Delphi and AJAX and the session counter will have the value 1 (right after the EXEC part of the URL):

Figure 2.

For the components in the IW Data Controls category a similar distribution of OnAsync events exists. Note that the TIWDBNavigator and TIWDBGrid do not support any OnAsync events, however. The components in the IW Standard 3.2 or IW Data 3.2 also do not support OnAsync events (due to the lack of the XMLHttpRequest support inside the HTML 3.2 browser).

June 2008 BLAISE PASCAL 2

Page 25 / 1839

2005 Win32

2007 Win32

IntraWeb 9.x and AJAX


EventParams All OnAsync events have the following signature:
procedure TIWForm.IWControl1AsyncXXX(Sender: TObject; EventParams: TStringList); begin end;

The values of x and y are the screen coordinates where the mouse was present when the OnAsync event was triggered. The modifiers value can be ALT_MASK, CTRL_MASK, or SHIFT_MASK again. The ALT_MASK and CTRL_MASK include a comma, the SHIFT_MASK does not. Working with EventParams In order to retrieve the contents of the EventParams, I'm using the following code in my IntraWeb forms. First, I've defined a number of constants, so I don't have to retype the callback, x, y, which and modifiers strings (and make accidental typing mistakes):
const callback = 'callback'; x = 'x'; y = y'; which = 'which'; modifiers = 'modifiers';

This means that all information will be passed to the event handler inside the EventParams argument. The TStringList contains name=value pairs, including in all cases the callback (the name of the event called) and often also a x and y coordinate, a which value (the key or mouse button pressed) and optionally modifiers. The following table shows the different name=value pairs that are passed inside the EventParams for the different OnAsync events. Note that this list may change in different versions of IntraWeb. AJAX Event EventParams velden
OnAsyncChange OnAsyncClick IWCONTROLNAME= callback= callback= x= y= which= modifiers= Click callback= x= y= which= modifiers= IWCONTROLNAME= callback= IWCONTROLNAME= callback= IWCONTROLNAME= Callback= callback= AjaxRequestUniqueId=

Now, inside an OnAsync event, for example the OnAsyncClick event I can write the following code to get the X and Y coordinates:
procedure TIWForm2.IWEdit2AsyncKeyPress(Sender: TObject; EventParams: TStringList); begin IWMemo1.Text := 'Key [' +Chr(StrToIntDef(EventParams.Values [which],32))+ '] at (' + EventParams.Values[x] + , ' + EventParams.Values[y] + ')'; end;

OnAsyncDouble

OnAsyncEnter OnAsyncExit OnAsyncSelect OnAsyncTimer

AJAX Key Events


OnAsyncKeyDown

EventParams velden
IWCONTROLNAME= callback= x= y= which= modifiers= IWCONTROLNAME= callback= x= y= which= modifiers= IWCONTROLNAME= callback= x= y= which= modifiers=

OnAsyncKeyUp

This will give you a simple example how to get your hands on the fields of the EventParams stringlist. Note that you cannot combine the OnAsyncXXX with the normal OnXXX events: the OnAsyncXXX will hide the normal events (like the OnClick which will no longer fire if you've implemented an OnAsyncClick event handler). OnAsync and VisibleOnAsyncClick event, the following will not work: There are a few special circumstances you have to be aware of when using the OnAsync events of the IntraWeb controls. One of the special cases is the fact that invisible controls that are not rendered, cannot be made visible in an OnAsync event. So, if you have a TIWRegion for example, with the Visible property set to False, and you want to make it visible as the result of an OnAsyncClick event, the following will not work:
procedure TIWForm2.IWButton3AsyncClick(Sender: TObject; EventParams: TStringList); begin IWRegion1.Visible := True; end;

OnAsyncKeyPress

When I write IWCONTROLNAME= this means that the complete text value of the control, or the selected index is given as value for the name of the control (like IWEDIT1=Hello, world or IWLISTBOX1=2). This value is only present if the SubmitOnAscynEvent property it set to True. The x and y contain the coordinates on screen, and which contains the ASCII value of the key that was pressed. The value of modifiers can be CTLR_MASK, ALT_MASK, or SHIFT_MASK (the commas for CTRL and ALT are included!). .Pressing the right Alt key (with the "Alt Gr" caption) has the effect that ALT_MASK,CTRL_MASK, is given, which may not be entirely correct. AJAX Mouse Events EventParams velden
OnAsyncMouseDown callback= x= y= which= modifiers= callback= x= y= which= modifiers= callback= x= y= callback= x= y= callback= x= y=

OnAsyncMouseUp

The problem is that the TIWRegion was already invisible, and by default not rendered. You will need a real OnClick event to render the control. Once a control is rendered, it can be made invisible and visible again (that's no problem), but the problem is that you cannot show something which isn't there in the first place. The really simple solution to this problem is to set the RenderInvisibleControls property of the IntraWeb Form or the Region (container) to true. This will ensure that the control on the form or region is rendered (even when invisible), so the OnAsync event can make it visible again without having to refresh the entire page. OnAsync and Disable If you have a TIWButton which retrieves some data from a database and updates a number of IntraWeb controls asynchronously inside the OnAsyncClick event, then you may want to disable the TIWButton or at least prevent it from sending another asynchronous OnAsyncClick event to the server. This can be done, but requires two steps: first you need to disable the TIWButton right after the click, which can be done using JavaScript in the onClick event using the ScriptEvents dialog, specifying the following JavaScript:
This.disabled = true; Return true;

OnAsyncMouseMove

OnAsyncMouseOver

OnAsyncMouseOut

This will disable the button as soon as you click on it. The obvious second step involves enabling the button again. The problem is that the IntraWeb application at the server side still believes that the button is enabled (since it was a local JavaScript event that disabled the button), so just setting the Enabled property of the button to True will not have the desired effect.

Page 26 / 1840

June 2008 BLAISE PASCAL 2

2005 Win32

2007 Win32

IntraWeb 9.x and AJAX


In order to trick IntraWeb, we have to explicitly set the enabled property to False (which has no effect) and then back to True again, so the button will be enabled again. In order to simulate a big task, let's perform a Sleep(4242) and simulate the situation in the following OnAsyncClick event:

To advertise in

BLAISE PASCAL MAGAZINE


or on the website www.blaisepascal.eu email advert@blaisepascal.eu mob. + 31.6.21.23.62.68 or see our price list http://www.blaisepascal.eu/index.php ?actie=advertprices/priceinform

How to become a subscriber


First of all you need to decide whether you want: 1 Figure 5.
procedure TIWForm2.IWButton6AsyncClick(Sender: TObject; EventParams: TStringList); begin try Sleep(4242);// Proceed with what is needed finally (Sender as TIWButton).Enabled := False; (Sender as TIWButton).Enabled := True; end; end;

2 3

Summary In this article, an abstract from my Delphi Win32 VCL for the Web / IntraWeb Development courseware manual (see http://www.eBob42.com/courseware). I've discussed the way IntraWeb implements and supports AJAX asynchronous event handlers in a very elegant and straightforward way using OnAsync events with EventParams that contain more details about the event itself.

the printed version*, code included, fully printable version,lectronic version download included; including tax, excluded postage (4 issues = 20,00 euros, total 45,00) You can have your electronic version immediatly whilst the printed issue is on its way. the no paper (download) version, printing enabled and code included.(you don't have to pay postage, just pay 25,00). the basic download version, without code, without downloads and printing disabled. The second issue is not free but costs 10,00. Any first issue can be downloaded at any time. (If it is your first download.) Special limited-time offer:

Bob Swart Bob Swart (aka Dr.Bob - www.drbob42.com) is an author, trainer, developer, consultant, web master and reseller (in the BeNeLux) for his one-man company Bob Swart Training & Consultancy (eBob42) in Helmond Brandevoort in The Netherlands. Bob has spoken at Delphi and Borland Developer Conferences since 1993, gives frequent free seminars in Helmond Brandevoort, and blogs in his ASP.NET ECO-driven weblog at http://www.drbob42.com/blog. Bob is offering Delphi courseware manuals and training services at http://www.eBob42.com/courseware as well as on Lulu at http://stores.lulu.com/DrBob42 and uses these in his training and seminars. As a CodeGear reseller, Bob is the first choice for your New User or Upgrade license for Delphi, C++Builder or RAD Studio (or other CodeGear tools) in BeNeLux, based on his personal support and dedication to the environment. Bob's love for Pascal and Delphi is expressed in the names of his kids Erik Mark Pascal (1994) and Natasha Louise Delphine (1996).

subcribe
to the printed version or no paper download version

for the first year * 25.00


You can pay with PayPal or by credit card (Take Two) or by bank. Look for subscription details on page 2

Readers
Please send your questions and comments to: email office@blaisepascal.eu or to: Edelstenenbaan 21 3402 XA IJsselstein, The Netherlands Tel.: + 31 (0) 30 68.76.981 Mobile: + 31 (0) 6 21.23.62.68 Page 27 / 1841

June 2008 BLAISE PASCAL 2

Solving unknown variables in a set of linear equations


3
2005 Win32 2007 Win32

Peter Bijlsma

Sometimes we need it in one of our programs: solving one or more unknown variables from a set of equations. Most of us can manage to solve x in the equation A.x + B = 0, the unknown factor x is then B/A. But what to do when we have two or more unknown variables? What we want, is a general method to solve any number of unknown variables from a set of equations. Such a method exists, it's called Gaussian Elimination. This article describes this method and provides a Pascal Function SolveLinEq to solve an infinite number of unknown variables from a set of linear equations. And even more, for the mathematically lesser skilled amongst us, it is explained how it works.
A Function for Gaussian Elimination First, as Gauss' method works only for linear equations, we must define what linear means in this context. The unknown variables in a linear equation contain no exponents, no roots and no multiplications amongst each other, so A.x2 + B. y + C.x.y = 1 is not linear when x and y are the unknowns but linear when A, B and C are the unknowns. Furthermore, we must have as many equations as we have unknown variables and in all these equations the unknown variables must appear on the same position. As all equations must have the same number of terms, 'zero' is allowed as a term. Let us look at an example with three equations: 5P 2P -1.5P Q + 4Q + 5Q + 7R = 35 + 16R = 80 - 8R = -20

The function SolveLinEq can then be called as: Unkvar := SolveLinEq(NumEq, Term, Error); Unkvar is an array of type TUnk which gives us the calculated unknowns, NumEq is an integer of type word, containing the number of equations provided to the function, Term is an array of type TTerms in which the known variables are contained and Error is a Boolean variable which will become true when the unknowns in the set of equations cannot be solved. The complete function is as follows:
function SolveLinEq(NumEq: word; Coef: TTerms; var Error:boolean):TUnk; var i, j, k, cntr: integer; fact: double; Unknowns: TUnk; begin Error := false; j := 1; while j < NumEq do begin {re-arrange equation set to always divide by largest number} cntr := 0; fact := abs(coef[j,j]); for k:= j + 1 to NumEq do if fact < abs(coef[k, j]) then begin cntr := k; fact := abs(coef[k,j]); end; if cntr > 0 then for k := 1 to NumEq + 1 do begin fact := Coef[j,k]; Coef[j,k] := Coef[cntr,k]; Coef[cntr,k] := fact; end; {eliminate unknown variables by making their coefficients zero} for i:= j + 1 to NumEq do begin if (Coef[j,j] <> 0) and (Coef[i,j] <> 0) then begin fact := Coef[i,j]/Coef[j,j]; cntr := 0; for k := 1 to NumEq + 1 do begin Coef[i,k] := Coef[i,k] - Coef[j,k] * fact; if (k <= NumEq) and (Coef[i,k] = 0) then inc(cntr); end; if cntr = NumEq then begin Error := true; exit; end; end {<>0}; end {i}; inc(j); end {j<NumEq}; {calculate unknown variables by substitution} for i := NumEq downto 1 do begin fact := Coef[i,NumEq +1]; if i < NumEq then for j:= i +1 to NumEq do fact := fact-Coef[i,j]* Unknowns[j]; if Coef[i,i] = 0 then begin Error := true; exit; end else Unknowns[i] := fact/Coef[i,i]; end {i}; Result := Unknowns; end;

The unknowns are called P, Q an R here. I could have called them x, y and z or x1, x2 and x3, that's irrelevant. As the terms of the equations are always in the same order, we can use the matrix notation for simplicity: 5 2 -1.5 -1 4 5 7 16 - 8 35 80 -20

So this matrix contains only the known factors of the equation-terms, the coefficients. If for a certain unknown variable no term exists in an equation, a 0 must be filled in as coefficient. The coefficients can be stored in a two-dimensional array: Coef: array[1..3,1..4] of double; Of course, when we would have 9 equations with 9 unknowns, the size of the array would be [1..9,1..10]. Using each time different array-sizes is not very practical when we want to implement a function. This flexibility would normally be achieved by using Dynamic Arrays. However, when the copy of the array containing the coefficients is changed in the function SolveLinEq, the original array is also changed! This happens to be a bug in Delphi (at least in version 7, which I use, but Borlands Quality Central still advises for version 10 not to copy multidimensional dynamic arrays). So in order to take care of all possible array-sizes, we use normal arrays. Therefore we must define the following lines in the declaration part of our main program:

const MaxNumEq = 20;

//example, maximum number of equations for your application

type TTerms = array[1..MaxNumEq,1..MaxNumEq + 1] of double; //containing coef. matrix TUnk = array[1..MaxNumEq] of double; //containing unknown variables

Gauss' Method for elimination Gauss' method eliminates the unknown variables one by one by making the coefficients zero until only one equation with one unknown is left. To do this, it is necessary to multiply the terms of an equation with such a factor that one term is exactly equal to a term in another equation. By subtracting both equations from each other, a new equation is formed in which one unknown variable is eliminated. Let me explain the working of the function by looking at an example. Consider the same coefficient matrix as we saw earlier: 5 2 -1.5 -1 4 5 7 16 -8 35 80 -20

Page 28 / 1842

June 2008 BLAISE PASCAL 2

2005 Win32

2007 Win32

Solving unknown variables in a set of linear equations


To determine the multiplication factor, the function takes the coefficient at the upper left-hand corner of the matrix as a denominator. So this coefficient may not be zero. Actually, as I discovered that very large numbers can be formed when the denominator is near to zero, the function takes care that the divisor is always the largest absolute number. The first part of the function takes care of that by exchanging an equation starting with a very low coefficient with one which is more suitable. In our example there is no problem, so we continue with the elimination process. The multiplication factor is now calculated: Coef[2,1]/Coef[1,1] = 2/5 = 0.4 and we subtract the terms of the first equation directly from the equivalent terms of the second equation:
Coef[2,1] Coef[2,2] Coef[2,3] Coef[2,4] is is is is changed changed changed changed into 2 into 4 + into 16 into 80 0.4 0.4 0.4 0.4 * 5 * 1 * 7 *35 = 0 = 4.4 = 13.2 = 66

Everybody can see that the solution for this set is P = Q = R = 0. Yet the function sets the Error Boolean for this equation set. When we follow the elimination steps as described above, we see why. When this set is reduced to two equations, they read as follows:
- 5.5Q -11Q + 13.5R = 0 + 27R = 0

And these equations are equivalent! So there are more solutions than only the zero-solution and hence no unambiguous unknowns can be calculated. When you use the function SolveLinEq, never forget to verify the status of the Error Boolean! The Demonstrator To demonstrate the function, I wrote a demonstrator. See Figure 1. The source code of this program will, as always, be available on the website.

Of course, when Coef[2,1] is already zero, this step is skipped! A counter cntr determines whether the coefficients of the unknown terms are all zero. In that case the unknowns cannot be solved and the function is exited with the Error Boolean set true. The next loop through the program calculates the multiplication factor for the third equation: Coef[3,1]/Coef[1,1] = -1.5/5 = -0.3 and the coefficients are subsequently re-calculated: Coef[3,1] changes into -1.5 + 0.3 * 5 = 0, etc. The matrix now is as follows:
5 0 0 -1 4.4 4.7 7 13.2 -5.9 35 66 -9.5

When we consider the last two equations, we see that the set of equations is now reduced to two equations with two unknown variables, as the coefficients in the first terms are zero. In the same manner as above, we can now eliminate Coef[3,2], which results in the following matrix:
5 0 0 or: -1 4.4 0 -20R = 7 13.2 -20 -80 35 66 -80

The program will now execute the last part of the function. First, the unknown R is calculated: Coef[3,4]/Coef[3,3] = -80/-20 = 4. In the next loop this value is substituted in equation 2, revealing that the unknown Q = 3, so that finally in equation 1 the unknown P can be calculated: P = 2. If in this substitution process a division by 0 would occur, a solution is not possible and the function is exited with the Error Boolean set true. When there is no error, the function exits to give the calling program all unknowns in a TUnk-type array. When the Error Boolean is set, however, this array contains rubbish, so the programmer must take care of such a case in the main program. Talking about errors. Sometimes there is insufficient information to calculate the unknowns. Take for instance the following equation set:
2P 2P + 3Q = 8 + 3Q = 10

Figure 1. The SpinEdit is used to select the number of unknowns, one, two or three and the program then displays the required number of EditBoxes where the user can fill in the coefficients. By clicking the Solve button, function SolveLinEq is called and the solution is displayed. Actually, there are two different SolveLinEq functions implemented in the demonstrator, one as described above which you can use for your own programs and one for demonstration purposes. For the mathematically interested I've added a Step mode which shows the elimination and substitution process steps one by one, displaying the changing matrix as explained above. Step mode A selects the normal function, where the first part of the function takes care that always the largest (absolute) number is put in the upper left-hand side box, i.e. coefficient [1,1] in the matrix. Step mode B only takes care that on that position no zero is present. During the Step mode, the program waits for only a Step command to continue. This is done as follows: In the NextStep procedure the following lines are present:
StepClicked := false; while not StepClicked do Application.ProcessMessages;

These equations cannot both be true and are therefore inconsistent. There is no solution possible. Another problem is demonstrated by the following set:
3P 6P + 2Q = 7 + 4Q = 14

These equations are certainly consistent, but still no unambiguous solution is possible as these equations are equivalent. There is more than one possible solution. Such case is not always obvious at first glance. Consider the following equation set:
2P 2P + 3Q = 8 + 3Q = 10

Of course the Boolean StepClicked will become true each time Step mode A or Step mode B is clicked. The caption of the selected button is during the Step mode changed into Step for clarity. To avoid undesirable effects in the Step mode no other commands than StepClicked are allowed and therefore all buttons (except Step) are disabled. Also, a number of Test buttons have been provided. Test 1 selects the equation set used to explain Gauss' method earlier in this article. The change of the coefficients in the matrix as described can be monitored by selecting Step mode B. Test 2 selects a set of equations which is inconsistent. The Error Boolean is set in the elimination process. Test 3 selects a set of equations which has multiple solutions. The Error Boolean is here set in the substitution process. Test 4 demonstrates the difference in the selection criteria to exchange the position of the equations in the matrix.

June 2008 BLAISE PASCAL 2

Page 29 / 1843

Solving unknown variables in a set of linear equations


Step mode A always puts the absolutely largest number in the upper left-hand corner, Step mode B avoids only a division by zero. When this latter Step mode is selected, a division by almost zero takes place in the Test 4 set, resulting in very large numbers for the recalculated coefficients. Test 5 demonstrates that the Error Boolean is set when there are more solutions than only the zero-solution. When one coefficient in the matrix is changed, the zero-solution is given as a unique answer. Applications for the function For practical reasons, read: I'm too lazy to type a lot of code lines, the demonstrator is limited to three unknown variables. But you can be sure that the function SolveLinEq works for as many unknowns as you wish. For what reasons do you need such a function? Here are a few examples: The formula for a straight line, y = P.x + Q, can be calculated when two coordinate pairs are known. The linear equations are:
x1.P x2.P + Q + Q = y1 = y2

Obfuscator?

2005 .NET

By Rob van de Bogert

Every .NET program you write won't be compiled to machine code directly, but to CIL (Common Intermediate Language). The CLR (Common Language Runtime) then converts it to machine code. This happens at the clients' pc, when the user starts the application. The disadvantage of this system is that it is relatively easy to decompile your application and convert the CIL back to readable code. This won't be a problem for open source software, but not everybody is so happy to give away his/her code. Luckily there is software to prevent users from doing this.
Example Let's start with an example. Because the example application itself isn't very important, I kept it really simple. Figure 1 shows the interface, which consists of two input fields and some labels. When a number in one of the input fields changes, the labels below show the sum, difference, multiplication and division of the two numbers.

Don't be confused by the notation which is now slightly different. Fill in the coefficients (don't forget coefficient 1 for the Q term) and P and Q will be calculated. Likewise, the formula for a parabola (y = P.x + Q.x + R) is determined when exactly three coordinate pairs are known:
x1.P x2.P x3.P + x1.Q + x2.Q + x3.Q + R = y1 + R = y2 + R = y3

Figure 1.
The next step is to download Reflector. You can download reflector at http://www.aisto.com/roeder/dotnet/. After downloading you only need to extract the file to whatever folder. After doing this, start Reflector.exe. Using File Open you can open the dll or exe you want to look at. Let's open the example applications' executable now. In the treeview in Reflector the application will be located at the bottom. After opening some nodes in the treeview, we see the screen as displayed in figure 2.

If you have exactly 5 coordinate pairs, you might want to know which ellipse or hyperbola is represented by these figures. You'll get a 100% fit by filling in the five coordinate pairs into the following equation of degree two:
xP + xyQ + yR + xS + yT = -1

Or maybe you prefer a polynomial of the 4th degree:


x 4 P + xQ + xR + xS + T = y

Unfortunately, real life is not always that easy to obtain a formula which perfectly fits to all coordinate pairs. Normally you have many coordinate pairs and if they don't fit exactly in a formula, you want at least an approximation for that formula. Also in that case the function SolveLinEq turns out to be very useful. The coefficients must then first be determined in a process called Curve Fitting. In the next article I will discuss some aspects on the subject Curve Fitting. Peter Bijlsma is born in 1946 in the Netherlands. He studied higher electronics and followed a course Algol. In 1982 he became the proud owner of an Apple II computer and programmed in Basic and 6502 assembly language. Other languages (Pascal, Perl) he picked up via self-study. He was employed as a Technical Communicator, i.e. author of technical manuals and instructor with a manufacturer of radar and military Command and Control systems. Later on he wrote software to convert XML-based text into either Websites, CD-roms with PDF files or old fashioned handbooks. Now he is retired. peter.bijlsma@hcc-pgg.nl

Figure 2.
Say we want to see the implementation of the AddValues method. A simple double-click will do the trick. On the right side of the screen the implementation of the method will be shown. Using the dropdown at the top the language for the code can be selected. It doesn't matter in what language the application was written originally. The example application was made in C#, but I'll show the code fragments in pascal (named Delphi in reflector). When we view the code for the AddValues method, we immediately notice something.

Page 30 / 1844

June 2008 BLAISE PASCAL 2

2005 .NET

Obfuscator?

function frmMain.AddValues(value1: Decimal; value2: Decimal): string; begin CS$0$0001 := (value1 + value2); Result := CS$0$0001.ToString('0.####') end; Listing 1

The names of local variables are replaced by something meaningless. Except for that, the code is perfectly clear and understandable. It's clear that this easy reviewing of the code could be problematic for commercial software with ingenious algorithms. Obfuscator And this all leads us to the so called obfuscator. An obfuscator is an application that can manipulate your .NET software in a way that makes it more difficult to read back using Reflector. There are quite a lot of obfuscators available today. For example Dotfuscator that comes with Visual Studio. The site
http://www.howtoselectguides.com/dotnet/obfuscators/ explains

obfuscators and compares a number of them. Deepsea Obfuscator An obfuscator made in the Netherlands is DeepSea obfuscator. During installation you have the possibility to integrate it into your IDE. Unfortunately this does not work for Delphi .NET IDE's, but only for Visual Studio 2005 and 2008. The program can also be used as a standalone version. Figure 3 shows the interface.

Figure 4.
The problem of almost all (?) obfuscator software is that this renaming isn't exactly waterproof. You can make it really hard for someone to really understand your source code, but it still is possible, without having to read machine code. DeepSea Obfuscator also has the possibility to obfuscate just parts of the source code. Visit the website of DeepSea Obfuscator for detailed information about what it can do. On the website you'll also find a trial version. The url is www.deepseaobfuscator.com. The full version costs 199,Another solution Before I wrote this article my experience with obfuscator software was very limited. While I was writing it, I suddenly got another idea. In my job I had used the open source software .NETZ (http://madebits.com/netz). .NETZ is comparable to UPX for Win32 in that it compresses your executable and attaches a launcher. When the compressed executable is started, the launcher decompresses your executable in memory and launches it. If you open the compressed executable in Reflector, you can't see anything from the original source code. The only things that are visible are some functions used by the launcher. So it seems to work better than the obfuscator software, and as a bonus your executable will be smaller (unless your executable was really small, say smaller than 100 kB or so). Another advantage is that you can put dll's used by your software into the compressed executable. I do not dare to say how solid this method is, because it should be possible to detach your executable from the launcher and decompress it. My advice: use an obfuscator, compress your executable with .NETZ and then at least you made it really really hard to get to the source code.

Figure 3.
The interface is really clean and clear. The first thing to do is add an assembly using 'Add assemblies'. After you've done that a press on the green arrow (Go!) is all that's needed. After a few seconds (depending on the size if the assembly) the obfuscation is complete. When we open the obfuscated assembly in Reflector, it's easily noticed that everything is renamed to something meaningless. Take a look at figure 4 and then compare it to figure 2. Rob van der Bogert started (serious) programming in 2005 at the MCH hospital in The Hague. He started programming using Delphi 7 first. Currently he writes software using both Delphi 2006 and Visual Studio 2008 (C#). He also is interested in new technologies like WPF for example .

June 2008 BLAISE PASCAL 2

Page 31/ 1845

Restricting Edits to numbers


It's quite often necessary to have an entry field that only accepts numeric values. A possible solution is to assign the style 'Numbers' to an Edit. This will refuse any entry that is not a number. Although this solution is often used, it is not watertight as I demonstrate below. After this I describe a solution that provides complete certainty, although it is a bit more complicated.
For the most part, Delphi is not an independent application. It only provides simplified (sometimes highly simplified) access to Windows components. For instance, Edit is a Windows control with most of its properties made accessible. The properties are thus available in the Object Inspector (i.e. they are published). One of the properties you see there is CharCase. Insead of the usual ecDefault, you can set this property to ecLowercase or ecUppercase to restrict data entry to lower-case or upper-case letters. Unfortunately, there is no 'NumbersOnly' property a property that is available in Windows, but which is not included in Delphi (as I show further on). From Delphi 4 onward, there's an easy way to examine Delphi structures. For example, with an Edit you can click TEdit while pressing the Ctrl key. As you can see from Figure 1, when you do this a hand icon appears and TEdit is underlined in blue.

2005 Win32

2007 Win32

By Henk Schreij

The Delphi Help says:


An application can use a style to determine how Windows displays text that a user enters into an edit control. The ES_LOWERCASE style converts the text into lowercase characters; the ES_UPPERCASE style converts the text into uppercase characters. Some applications may need to convert the text in a Windows string (such as a filename) into a specific character set. The ES_OEMCONVERT style ensures the proper conversion of characters in these instances. When the amount of text to be displayed exceeds the size of the edit control, an application can use styles to scroll the text through the edit control. The ES_AUTOHSCROLL style automatically scrolls text horizontally. Other available styles define different aspects of an edit control. The ES_NUMBER style restricts input to the edit control to digits only.

For example, CharCase is represented here by the ES_UPPERCASE and ES_LOWERCASE styles. But one property has not been included in Delphi: the ability to restrict the entry to 'numbers only'. In Windows Help, you can see that an ES_NUMBER style does exist:
The ES_NUMBER style restricts input to the edit control to digits only.

However, this style is not included in the Edit properties. You have to do this yourself by expanding the Edit styles (preferably in the OnCreate of the Form). An example of an edit (Edit2) that allows only numeric entry is shown in the following figure, with the code underneath:

Figure 1. Using Ctrl + Click to access the source code


When you're in the source code, you can examine how the properties are implemented. For instance, with an Edit you see something like:
procedure TCustomEdit.CreateParams(var Params: TCreateParams); const Passwords: array[Boolean] of DWORD = (0, ES_PASSWORD); ReadOnlys: array[Boolean] of DWORD = (0, ES_READONLY); CharCases: array[TEditCharCase] of DWORD = (0, ES_UPPERCASE, ES_LOWERCASE); HideSelections: array[Boolean] of DWORD = (ES_NOHIDESEL, 0); OEMConverts: array[Boolean] of DWORD = (0, ES_OEMCONVERT); begin ( . . . )

Figure 4. Demo program for a numbers-only Edit


( . . . ) private procedure NumbersOnly(Edt: TEdit); public {Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.NumbersOnly(Edt: TEdit); begin SetWindowLong(Edt.Handle, GWL_STYLE, GetWindowLong(Edt.Handle, GWL_STYLE) or ES_NUMBER); end; procedure TForm1.FormCreate(Sender: TObject); begin NumbersOnly(Edit2); end;

Password, ReadOnly, CharCase, HideSelection and OEMConvert are thus included, but Numbers is not even though it does exist, as you can verify in the Windows SDK or Windows API Help (access to this varies depending on the Delphi version; Figure 2 is for Delphi 7).

Figure 2. Launching Windows Help


When you are in Windows Help, you can find the same Edit properties under Styles.
Content Index Find

Here we use GetWindowLong(Handle, GWL_STYLE) to fetch the desired style properties. Then we add the ES_NUMBER property with or. SetWindowLong in turn assigns the property to the Edit. Now you can only enter numbers in Edits that include this declaration. Place the declaration in the OnCreate of the Form. In the demo code this is only done for Edit2, but in practice you can use it for lots of other Edits. Incidentally, here the entry is actually restricted to positive integers, because commas, dots, minus signs and so on will be refused with a beep.

Figure 3. Looking up Edit styles in Windows Help Page 32 / 1846

June 2008 BLAISE PASCAL 2

Restricting Edits to numbers


3
2005 Win32 2007 Win32

Monitor Aspects by Jeremy North


2005 Win32 / 2007 Win32

The declared setting only applies to keyboard input. This means that if you add a button with the code Edit2.Text:= 'abc', the Edit will not refuse it. Of course, you can prevent this in your code, but there is an even more serious problem: you can also enter letters by using copy and paste. Unfortunately, the ES_NUMBER style won't stop this. In Blaise 80 I described a solution that uses OnKeyPress to partly solve this problem:
procedure TForm1.Edit3KeyPress(Sender: TObject; var Key: Char); begin if not (Key in ['0'..'9', #8]) then begin //(#8 = Backspace) Beep; Key:= #0; end; end;

So you have finally gotten the dual monitor setup you have always asked for. You are enjoying the extra desktop size but wish there was an easier way to move your mouse pointer from one monitor to the next. Even better would be the ability to move a window of a running application to the other monitor. This article will show you how to accomplish this task using Delphi.
We want the resulting application to be as transparent as possible to the end user. To achieve this, the following additional features will be implemented: Reside in the system tray Use system wide shortcuts System-wide hot keys have downfalls One issue with registering system-wide hot key is that the ones selected may conflict with an applications shortcut. To help avoid issues the application will allow the user to change the hot key if they find the default ones conflicting. System Tray functionality Since Delphi 2006, a component to add an entry into the system tray is provided. While the included application uses this component it isn't necessary for the running of the application if your Delphi version doesn't come with one. Moving the mouse To move the mouse to a desired desktop location you call the SetCursorPos windows API method. This method is called when you set the CursorPos property of the TMouse class. The TMouse class provides information about the mouse that is currently attached to the system. An instance of the TMouse class is created for you already in the Controls.pas unit. There is no need to create a new instance of the TMouse class, although there is also no mechanism to prevent you from doing so. To move the cursor to the point 10, 10 on your desktop (of the primary monitor), use one of the following methods. It is important to know that it is possible for a monitor to have negative coordinates. This occurs if you align a secondary display to the left or above the primary monitor. All source extracts used in this article are available in the download for the article. This includes the sample application as well as a demo
procedure TForm1.Button1Click(Sender: TObject); begin SetCursorPos(10, 10); // use API method directly Mouse.CursorPos := Point(10, 10); // use Delphi supplied method end;

This prevents copying and pasting via the keyboard (using Ctrl + V or Shift + Insert), but it does not stop you from right-clicking with the mouse in order to paste a selection, as can be seen from Figure 5:

Undo Cut Copy Paste Delete

Figure 5. Pasting with right-click Fortunately, it is easy to intercept mouse entry and empty the ClipBoard. To do this, add to the uses ClipBrd and write the following OnMouseClick code:
procedure TForm1.Edit3MouseDown(Sender: TObject; Button:TMouseButton; Shift: TShiftState; X, Y: Integer); begin // uses ClipBrd if ssRight in Shift then Clipboard.Clear; end;

This causes the Paste command to be disabled (greyed out) when the mouse is right-clicked, so you can't surreptitiously smuggle a letter into a number field. Conclusion In the above discussion, I showed you how to view the Delphi source code. This is often very handy, if only to see how good programmers write code (so you can copy them!). I also showed you how to access the Windows reference documentation. In many cases, this can also provide a lot of useful information. By comparing the two, you can see that code that 'only accepts numbers' is not implemented for Edits. I showed you how you can do this yourself with a single line of code. Unfortunately, this approach is not watertight because it is still possible to enter letters by pasting them from the clipboard. For this reason, I concluded with a bit of code that you can use to disable the clipboard.

To advertise in

Point is a function in classes.pas Monitor information The VCL includes a TMonitor class which gives specific information about a monitor, for example, its assigned number (which you can see when using the identify feature in the Display Options dialog). The TMonitor class also provides information about whether the monitor is the primary monitor and various properties about the size and bounds area of the monitor.

BLAISE PASCAL MAGAZINE


or on the website www.blaisepascal.eu email advert@blaisepascal.eu mob. + 31.6.21.23.62.68 or see our price list http://www.blaisepascal.eu/index.php ?actie=advertprices/priceinform

June 2008 BLAISE PASCAL 2

Page 33 / 1847

Monitor Aspects

2005 Win32

2007 Win32

Single monitor For a single monitor system, the BoundsRect value is simply the width and height of the monitor. The width and height values depend on the resolution you are currently running at. Multiple monitors Multiple monitors have been supported with a specific API since the Windows 98 and Windows 2000 operating systems. Delphi supports these API methods with the MultiMon unit. While we won't access any of these API methods directly for the purpose of this article, many of the mentioned VCL classes call these internally. The Screen (TScreen) is where it happens Access to multiple monitor information is provided through the TScreen class. Like the TMouse class an instance of TScreen is created for use in the Controls.pas unit. The TScreen class tells us how many monitors a system has (MonitorCount property) and also provides access to each specific monitors TMonitor class via the Monitors array property. The TScreen class also has a property that allows you to get access to the primary monitor without having to iterate over the systems' monitors checking for the value of the Primary property. Included in TScreen are three multiple monitor helper methods that are useful for the application. MonitorFromPoint - Return the TMonitor instance for the monitor that contains the point specified. MonitorFromRect - Return the TMonitor instance for the monitor that falls within the rect specified. MonitorFromWindow - Return the TMonitor instance for the monitor that the given window falls within. Implementing mouse movement In order to be able to move the mouse to a new monitor we need to know the current position of the mouse. The TMouse class provides this with the CursorPos property. To work out which monitor the mouse is currently positioned on we can use the MonitorFromPoint method of the TScreen class. The following source extract displays a message box with the current monitor number as the text.
procedure TForm1.Button2Click(Sender: TObject); var LMonitor: TMonitor; begin LMonitor := Screen.MonitorFromPoint(Mouse.CursorPos); ShowMessageFmt('Mouse in monitor: %d', [LMonitor.MonitorNum]); end;

If the Left position is the same then the monitors are vertically aligned. If the Top position is the same then the monitors are horizontally align. If both the Left and Top values are different than the user has an irregular multiple monitor setup which this application doesn't support. A revisit to this article to support irregular layouts is a future possibility. If you currently have an irregular layout, the application assumes a horizontal layout.
type TMonitorAlign = (maHorizontal, maVertical); const MonitorAlignText: array[TMonitorAlign] of string = ('Horizontal', 'Vertical'); procedure TfrmMain.bGetMonitorAlignmentClick(Sender: TObject); var LMonitor1: TMonitor; LMonitor2: TMonitor; LMonAlign: TMonitorAlign; begin // only process dual monitor setups if Screen.MonitorCount <> 2 then exit; LMonitor1 := Screen.Monitors[0]; LMonitor2 := Screen.Monitors[1]; if (LMonitor1.Top = LMonitor2.Top) then LMonAlign := maHorizontal else LMonAlign := maVertical; ShowMessage(MonitorAlignText[LMonAlign]); end; MonitorAlignText string array is a convenient way to map a string to a TMonitorAlign value.

With the knowledge of how the monitors are aligned, we now know whether the calculation requires the mouse to move to the left (X) or the mouse needs to move from the top (Y) value. If the monitors are aligned horizontally then the calculation needs to add the current monitor's width to the X value of CursorPos. If the monitors are aligned vertically, then the calculation needs to add the current monitor's height to the Y value of CursorPos. Bounds checking With these calculations it is possible that the resulting value might not actually be a valid location on either monitor. This is why an additional check is required before moving the cursor to such a location. This check is provided in the source extract below where the LNewMonitor and LCurMonitor values are tested for being equal. If the values are the same, then we need to move the mouse in the opposite direction because the calculated position does not fall within the bounds of a monitor attached to the system. The source code below includes the bounds checking and moves the

ShowMessageFormat is a function in dialogs.pas, for earlier Delphi versions use ShowMessage(Format('Mouse in monitor: %d', [LMonitor.MonitorNum]));

is dealer of DeepSea obfuscator http://www.barnsten.com/deepsea.aspx

One thing you'll notice is that the monitor number is zero based. So monitor one is at index zero of the Monitors array property of the TScreen class. To move the mouse to the next monitor for a two monitor setup, the calculation is reasonably straight forward. It is important to know whether the monitors are horizontally aligned or vertically aligned. We can check this by comparing the Left positions of each of the TMonitor instances stored in the Screen instance.

Special offer for Subscribers: Deepsea Obfuscator 237,00 10% 213,00 incl. Vat
All you need to to do is email us your order, a free trial can be downloaded at www.blaisepascal.eu

Page 34 / 1848

June 2008 BLAISE PASCAL 2

Monitor Aspects

2005 Win32

2007 Win32

procedure TfrmMain.MoveCursor; var LMonAlign : TMonitorAlign; // current cursor position LCursorPos : TPoint; LCurMonitor: TMonitor; // current monitor LNewMonitor: TMonitor; // monitor for the new position LNewCursorPos: TPoint; // only process dual monitor setups begin if Screen.MonitorCount <> 2 then exit; LMonAlign := GetMonitorAlign; // get the current cursor position LCursorPos := Mouse.CursorPos; LCurMonitor := Screen.MonitorFromPoint(LCursorPos); if LMonAlign = maHorizontal then LNewCursorPos := Point(LCursorPos.X + LCurMonitor.Width, LCursorPos.Y) else LNewCursorPos := Point(LCursorPos.X, LCursorPos.Y + LCurMonitor.Height); LNewMonitor := Screen.MonitorFromPoint(LNewCursorPos); if LNewMonitor = LCurMonitor then begin // if the monitors are the same then we need to subtract the values if LMonAlign = maHorizontal then LNewCursorPos := Point(LCursorPos.X LCurMonitor.Width, LCursorPos.Y) else LNewCursorPos := Point(LCursorPos.X, LCursorPos.Y - LCurMonitor.Height); end; Mouse.CursorPos := LNewCursorPos; end;

Figure 1.
Registering system-wide hot keys To register a system-wide hot key call the RegisterHotKey API method. This method requires a window handle, a unique identifier as well as details about the key combination to register. For this application the default hot key for moving the mouse is Ctrl+Alt+C. The following source extract shows how to register a system-wide hot key.
const HOTKEY_MOVECURSOR = 1; procedure TfrmMain.bRegisterHotKeyClick(Sender:TObject); begin if not RegisterHotKey(Handle, HOTKEY_MOVECURSOR, MOD_CONTROL or MOD_ALT, ord('C')) then begin raise Exception.CreateFmt(StrRegistrationError, [GetLastError, SysErrorMessage(GetLastError)]); end; end;

If you give the button that calls the above code an accelerator, you can successfully switch the mouse cursor position between the monitors. This is great except we really want to be able to switch the cursor position when our application isn't the active application on the desktop.. Figure 1 shows a typical dual monitor setup. The primary monitor (labelled 1) is positioned to the left of the second monitor. If the mouse is currently on the primary monitor, then we need to add the current X position to the width of the primary monitor to shift the mouse cursor to the next monitor. Once the mouse is positioned on the secondary monitor moving it back to the primary monitor requires two calculations. This is because the first calculation we do (attempting to shift the mouse to the right) is incorrect under these conditions. If the mouse is currently on the secondary monitor and you shift the X value further to the right then the mouse will end up off screen. This is why the Screen.MonitorFromPoint test is necessary. This method returns the nearest (by default) monitor to the point you pass it. If the nearest monitor to the calculated point is the same as the current monitor, then we have moved the mouse the wrong direction. Moving the mouse should result in the new monitor being different to the old monitor. There is no reason why the code attempts to add the current monitor width to the X value (height for the Y value). Even if the code subtracted the new monitor's width from the X value it would still result in the wrong calculation. While I've talked about primary and secondary monitor positions in reference to Figure 1, the code doesn't care which monitor is considered the primary one. It only cares about the bounds of each monitor and making sure that the new point exists within the bounds of the new monitor. Non uniform monitor resolutions One issue with the above code is that it doesn't take into account a setup where the monitors have a different resolution. This is solved in the included application and how this is done is that when subtracting the X or Y coordinates (remember we first try and add X or Y values to move to the next monitor) we must use the new monitors width or height, and not the current monitors width or height.

When defining the hot key you must use the uppercase version of the key. Note how ord('C') is used in the code above. Using ord('c') will not work correctly. Once you register a hotkey you also need to know when it is triggered. To do this you need to handle the WM_HOTKEY message. Add a WM_HOTKEY handler to your form as shown below.
TfrmMain = class(TForm) protected procedure WMHotKey(var Message: TWMHotKey); message WM_HOTKEY; end; procedure TfrmMain.WMHotKey(var Message: TWMHotKey); begin if Message.HotKey = HOTKEY_MOVECURSOR then MoveCursor; // private method that moves the cursor inherited; end;

Once you have registered the hot key, you should also unregister it when you have finished. You should do this in the OnDestroy event of the form.
procedure TfrmMain.bUnregisterHotKeyClick(Sender:TObject); begin if not UnregisterHotKey(Handle, HOTKEY_MOVECURSOR)then begin raise Exception.CreateFmt(StrUnregistrationError, [GetLastError, SysErrorMessage(GetLastError)]); end; end;

June 2008 BLAISE PASCAL 2

Page 35 / 1849

Monitor Aspects

2005 Win32

2007 Win32

Moving windows Before it is possible to move a window we must first identify what window is currently under the mouse cursor. The WindowFromPoint API method provides us with the handle of the window that currently falls within the point specified as the only parameter. Once we have the windows handle that the WindowFromPoint API returns, we then must make sure we get the top level window. Many controls can have a window handle and we definitely don't want to be moving windowed controls instead of actual top level windows. To get the top level window we need to call the GetParent API method in a while loop until the parent handle returned is 0. This means that we have reached the top level window that we can move. Once we have the top level window we need to call GetWindowRect to get the width and height of the window. This will be used when we move the window. The application contains a method to perform all of this for us. It accepts a parameter of TPoint which is the current mouse position and also has out parameters to return back the Handle of the top level window, the height and width of the top level window plus the window bounds. If a top level window is found, the method returns true. Instead of passing numerous parameters and requiring a number of out parameter types, a TWindowInfo record has been defined. As the record contains input and output parameters, the record is passed by reference.

Included application details The application included in the download also has methods to save and load the hot keys to and from the registry as well as the ability to minimize the application to the system tray. The menus.pas unit provides two functions to convert between a string and a hot key (shortcut) value. These methods are called TextToShortCut and ShortCutToText. While the application loads for the first time the window has an opacity of 125 (using the AlphaBlend and AlphaBlendValue properties). When you restore the application, the opacity is set back to 255. The UI allows you to change the current system-wide hot keys if you wish. Once you modify the shortcuts the Apply and Cancel buttons are enabled. Click the Apply button to save your changed shortcuts. Clicking Cancel will revert the shortcuts back to there previously saved values. The Use Defaults button will revert the shortcuts back to their default values. While the applications user interface is visible, the actual shortcuts are disabled and won't function. This is to allow you to enter the current shortcuts into the shortcut edit box. Multi-monitor features in the VCL With users having increased access to multi-monitor systems it is important that developers are aware of the implications when coding. Several new properties and methods have been added to the VCL over the past releases. I will highlight some that you may find useful for your current applications. TForm.DefaultMonitor this property allows you to select which monitor your form will appear on when started. TCustomForm.MakeFullyVisible this method makes sure that your form doesn't overlap monitors. This method accepts a TMonitor parameter. Your form will then be made visible on that monitor only. TForm.Position New position values have been added to aid positioning a form. If you want to center your form on a single monitor (determined by the DefaultMonitor property) set your forms Position property to poScreenCenter. Conclusion Hopefully this article has given you a better insight into how you can use the VCL to your advantage when working with users that have multi-monitor systems. The above source extract raises an exception because it is called from a button click handler. If you unregister your hot keys in the OnDestroy event of a form, it is advisable to not raise an exception. We have our system-wide hot keys sorted and the code to move the mouse cursor between monitors, now it is time to start moving some windows around. Jeremy North:
commenced programming with Delphi in 1997 whilst at college in the USA and since his return to Australia, he has kept pace with emerging windows technologies. More recently he has concentrated his focus on writing components and IDE Experts, as well as creating tools to help other developers work more efficiently. Some of these components and experts are released to the public on his JED Software website

type TWindowInfo = record Pt : TPoint; Hwnd : THandle; Rect : TRect; end; function GetWindowInfo(var AWindowInfo: TWindowInfo): Boolean; var LParent: THandle; LRect: TRect; begin AWindowInfo.Hwnd := WindowFromPoint(AWindowInfo.Pt); LParent := GetParent(AWindowInfo.Hwnd); while LParent <> 0 do begin AWindowInfo.Hwnd := LParent; LParent := GetParent(AWindowInfo.Hwnd); end; Windows.GetWindowRect(AWindowInfo.Hwnd, LRect); AWindowInfo.Rect := LRect; result := AWindowInfo.Hwnd > 0; end;

To move the window we call the MoveWindow API with the returned top level window handle, the updated Left and Top values (as calculated using the same method as moving the mouse cursor earlier) and with the current width and height of the window. To make sure the window is focussed we also call SetForegroundWindow with the top level window handle. In the final application the logic for moving the cursor and a window is centralised in the same method that has a single boolean parameter; which indicates if the user wants to move the window as well as the cursor.
procedure MoveCursor(const AMoveWindow: Boolean);

http://www.jed-software.com Some are free, while others incur a small fee. So lets go and visit him. (his site)

Page 36 / 1850

June 2008 BLAISE PASCAL 2

Using the website of Blaise Pascal Magazine


Free code for everyone: On the website is a special download: All the code that is listed here is available for free downloading: It can be code snippets, programs, trials, third party components etc.
The sales items: Advertising pricellist / Index of advertisers / Privacy statement All the international and national events agenda Information about the ways to subscribe

The List of available downloads:


Trials: The Deepsea-Obfuscator (the program is advertised in this issue on page 38-39) Code snippets: Opening files (Mini application for starters) Programs including code: s e m g n i a t o R (The program is ment to start learning about image handling) e l c d n s p a m t i B (is the second part to understand rotating the image) r e p l a W (is a compleet program that has an enormous wide range of code in it like rotating and moving images, opening files, moving files, creating a Client Dataset and handling it and lots of other stuff.) s t r e p x E G (is a program you cant mis, soon you will find out why: it has an enormous range of extras for Delphi Versions Delphi 5/6/7/8/2005/2006/2007. You always will find the right version) s u r z a L (The pascal editor that is made for several operating system: Windows / Linux / Mac) The version that is downloadable here is al;ways the latest stable version. If you want to know more just click on the Lazarus image

Subsribers for the free edition:


If you have subscribed before for the first (free) issue and log in you will find the followingmessage: You have already downloaded an issue before. Your first issue will always be free. Since you seem to be interested in our magazine, we hope you are willing to pay for the next issue. It is only 10,-- for the basic version for one year. (4 issues) So please Subscribe! Here it is all explained once more. How to become a subscriber Any first issue can be downloaded free at any time. (If it is your first download.) First of all you need to decide whether you want: 1 the printed version*, code included, fully printable version,lectronic version download included; including tax, excluded postage (4 issues = 20,00 euros, total 45,00) You can have your electronic version immediatly whilst the printed issue is on its way. 2 the no paper (download) version*, printing enabled and code included. (you don't have to pay postage, just pay 25,00). 3 the basic download version, without code, without extra's and printing disabled. The second issue is not free but costs 10,00. You can pay with PayPal or by credit card (Take Two) or by bank. Look for subscription details on page 2 in the magazine

For the download versions of Blaise Pascal Magazine we made all urls and emailadresses printed dierectly available by clicking on the adres. We also use the icons above to remind you on that.

June 2008 BLAISE PASCAL 2

Page 37 / 1851

Euro symbol generation with the Alt key


In most programs you can enter data using Edits, Memos, RichEdits and so on. If you are entering monetary amounts, you also need to be able to enter the euro symbol (). Here I show that with only a few lines of code, you can use Alt + 5 to display the euro symbol in all (!) forms of your program. This is standard if you configure the language and keyboard of your system to Dutch / the Netherlands, but it doesn't always work in your Delphi programs.
Depending on the language and keyboard settings selected in Windows, you can sometimes (but not always) enter the euro symbol in your Delphi programs with a keyboard shortcut. If you don't want to be dependent on Windows, you can write your own code to provide a keyboard shortcut for the euro symbol. In the Netherlands, as in most other parts of the world, the usual key combination for the euro symbol is Alt + 5. However, some countries have different combinations, such as Great Brittain (Alt + 4) and Germany (Alt + E). Consequently, the code described below must be modified slightly for these countries. ApplicationEvents component Delphi 5 and later include a component called ApplicationEvents (on the Additionals tab), which you can use to intercept events before they reach the rest of your program. This means that you only have to write some of code in your first form and it will work in your entire program, regardless of how many forms it contains. In issues of Blaise published in 2003 and 2006, I described how you can use this approach to allow the arrow buttons to be used to navigate to previous and next input screens, for converting decimal markers from full stops to commas, and for handling the mouse scroll-wheel bug in DBGrid. Figure 1 shows the ApplicationEvents component in Delphi 5 to 7 and in Delphi 2005 to 2007.

2005/6/7 Win32

By Henk Schreij

I'll explain the first line with type THackCustomEdit later on. The code begins with the declaration of variable AC, which is of type TWinControl. All components that can be given the focus are of type TWinControl, and the component that has the focus is the ActiveControl. Each form has an active control, but we want to send the euro symbol to the active form, which is the reason for Screen.ActiveForm.ActiveControl. The next line uses Msg to see which key has been pressed. The character code we need is the ASCII value of the key as calculated by Ord('5'), which is 53. VK_MENU is the virtual key code of the Alt key, which is 1 if the key is pressed. I'll explain the hack code a bit later. In the next line, the euro symbol is assigned to AC (the active control) with SelText (the blink location of the cursor). In your code, you can insert the euro symbol by holding the Alt key pressed and typing the digits 0128 on the numeric keypad. Finally, the statement Handled:= True says that no further processing of the keys is necessary.

Figure 2. Example entry locations for the euro symbol There's still one problem, which is that you can also use Alt + 5 to enter a euro symbol in an Edit, RichEdit or Memo that is set to 'read only'. You have to intercept this as well, which is the purpose of the hack. This is necessary because a WinControl does not have a ReadOnly property, but a CustomEdit, the common ancestor of Edit, RichEdit and Memo, does have this property. Unfortunately, it is protected, so it cannot be used. You can use the hack to get around this protection, since the protection is not complete: it can be read out if a derivative of the class is declared in the same unit. This is why you have to include the line type THackCustomEdit ahead of the code where you use the hack. You can place this just before the code of the hack or at the beginning of the code, so it can be used everywhere. Conclusion With just a few lines of code, you can enable Alt + 5 for entering a euro symbol in all forms. The OnShortCut event of the ApplicationEvents component is used for this purpose. However, a trick is necessary to prevent this method from being used to enter a euro symbol in a read-only field, since the common ancestor of all entry fields (CustomEdit) has ReadOnly declared as protected. The trick with the hack is simple, and you can use it in many other places to get around the restriction on using protected properties. Henk Schreij: has graduated as a technical designer. He works for his own company, as a programmer of 'custom made software' for several different companies. Henk has written articles for Blaise since Delphi 1, totalling more than 100 now, mainly aimed at starting programmers. These articles usually have something of interest, too, for those with programming experience.

Figure 1. ApplicationEvents in Delphi 7 and earlier (left) and in later versions (right) Code Place an ApplicationEvents component on Form1, find OnShortCut under Events, and double-click it. You have to use the OnShortCut event because the Alt key is a shortcut key. Shortcut keys are used for tasks such as selecting a menu item by typing an underlined letter (e.g. File). A shortcut key is always read out first, even before OnKeyDown is triggered, Type the following code in the framework shown for ApplicationEvents1ShortCut (explanation below):
type THackCustomEdit = class(TCustomEdit); procedure TForm1.ApplicationEvents1ShortCut( var Msg: TWMKey; var Handled: Boolean); var AC: TWinControl; begin AC:= Screen.ActiveForm.ActiveControl; if (Msg.CharCode = Ord('5')) and (GetKeyState(VK_MENU)<0) then begin if THackCustomEdit(AC).ReadOnly then Exit; TCustomEdit(AC).SelText:= ''; // Alt 0128 Handled:= True; end; end;

Page 38 / 1852

June 2008 BLAISE PASCAL 2

Delphi for PHP 2


Top Reasons to Buy
Accelerate PHP web development with a powerful visual Rapid Application Development (RAD) approach and framework
Delphi for PHP revolutionizes web development with a completely integrated, rapid visual development approach and component framework for PHP. Extend the VCL for PHP at any time with your own components or enhancements being offered through the open source PHP platform.

Delphis proven and familiar RAD approach means you are quickly up to speed and productive. The powerful PHP editor and debugger increase coding speed and efficiency. Connectivity with leading databases provides seamless database access, and the integrated VCL for PHP component class library help developers quickly and visually create PHP web applications and integrate PHP open source components. Delphi for PHP is the fast way to build powerful and reliable PHP web applications. Make PHP programming as simple as drag and drop with the VCL for PHP library
Delphi for PHP includes an integrated PHP 5 class library called VCL for PHP.

Delphi for PHP makes it easy to create your own components and install customized packages to use in the IDE. The VCL for PHP makes it simple for you to develop new classes, because every component is built in pure PHP. Simply place these components into forms and use them in your applications. VCL for PHP components have built-in properties, methods, and events that make web interface development a snap. Integrated development environment streamlines code management and navigation
Managing and organizing you PHP code has never been easier. The Project Manager lets you view and organize project files while the Structure Pane shows the hierarchy of components displayed on the designer.

A customizable palette of over 50 reusable components include buttons, labels, check boxes, images, DHTML menus, flash objects, grids, tree views, list boxes and more. Database components are available for accessing databases, tables, queries, and stored procedures, as well as data grids and navigation. Version 2 of the VCL for PHP adds native Oracle components, Zend Framework components as well as other updates and enhanced documentation. Build data-driven Web applications with broad database connectivity
Delphi for PHP standardizes database connectivity and includes a collection of pre-built database components that make it much simpler to build apps that connect to databases.

Delphi for PHP includes native components for MySQL, Oracle and CodeGear InterBase. The Data Explorer supports drag-and-drop database application creation with connectivity to MySQL, Microsoft SQL Server, Oracle, PostgreSQL, Sybase, SQL Anywhere, DB2 and InterBase. Support for all other PHP supported databases is available via ADOdb. Take advantage of HTML templates with embedded PHP and create AJAX-enabled Web 2.0 sites
AJAX is a popular method for building dynamic Web 2.0 sites and Delphi for PHP includes AJAX-enabled components to help you on your way to building modern looking interactive web sites.

The Code Explorer makes navigation straightforward and the Data Explorer enables you to browse database server-specific schema and objects. Version 2.0 adds many enhancements including a new file browser, multi language support, and a Data Viewer window for browsing database tables and executing queries. Powerful editing with integrated PHP debugging and profiling speed up development A sophisticated editor and debugger can make the normally frustrating process of tracking down bugs and errors in the code quick and painless. Delphi for PHP has a customizable source code editor that has color syntax highlighting, bookmarks to ease navigation through large files, and Code Insight to assist in the selection of properties and methods. The integrated debugger helps find and fix errors, view breakpoints, and local and global variables. A powerful new PHP performance profiler to help locate application bottlenecks down to a single line of PHP code so you can improve the performance of your Web apps. Delphi for PHP 2.0 includes many enhancements including faster CodeInsight and new Error Insight to help you locate errors in your source code as you type, code folding to make it easier to navigate your source, change tracking to identify changed lines of code, and much more.

Additionally, AJAX enablement is built into the core of the VCL for PHP framework, so you can add AJAX capabilities to virtually any PHP code. Delphi for PHP 2.0 also includes a new visual HTML Template editor for editing HTML and creating templated forms that include HTML with embedded PHP. Expand your capabilities by creating your own components or adding others from the open source PHP ecosystem

http://www.codegear.com/shop

June 2008 BLAISE PASCAL 2

Page 39 / 1853

[Advertorial]

DeepSea Obfuscator: .NET Obfuscation for all

Ewout Prangsma

2005 .NET

The Microsoft .NET framework is gaining popularity every day. More and more developers choose to use this framework as a foundation for their product development. There are many reasons for this. The development speed of a .NET based product is huge. This is the result of an extensive object library, the availability of many development languages all targeting the same framework and a large environment of .NET components. The popularity of the framework is also boosted by the marketing power of Microsoft itself. The use of the .NET framework compared to traditional software languages also has a down sight. Programs developed for this framework are all compiled to an intermediate language. This language (called IL) is converted to machine code during the execution of the program. This language is very rich compared to machine code. It contains a lot of information that can be used to derive the original source code. This can damage the intellectual property of the developers of the program. To solve this problem, there are various so called obfuscation tools available. These tools change the compiled programs such that its function remains the same, but it becomes much harder to derive the original source code. DeepSea Obfuscator is a new player in this market. It is much more accessible in terms of use and purchase compared to its competitors. This article will demonstrate the need for obfuscation for .NET programs, explain what differentiates DeepSea Obfuscator from its competition and explains how DeepSea Obfuscator will help you to make obfuscation a standard element of the delay business of developers. The need for obfuscation Programs developed on top of the .NET framework are compiled into assemblies. An assembly is an .exe or .dll file that looks from the surface very much like a traditional executable or dynamic link library. Internally it is very different. An assembly contains code (IL) and metadata. The metadata describes all types, functions and fields contained in the assembly. This information can be read using tools like ildasm or Reflector. Ildasm is a program that Microsoft ships with the .NET SDK. Its output is rather rough and not so easy to read. In contract to ildasm, the output of Reflect, an application developed by Lutz Roeder, gives a full visual insight in the assembly. Below is a screenshot from a simple assembly as displayed by Reflector.

Using Reflector it is also possible to look into a function itself. Doing so reveals the code of the function as shown below for the Main function. .methode private hidebysig static void Main(string[]args)cil managed { .entrypoint .maxstack 8 L-0000: nop L_0001: ldstr Hello world L_0006: call void[mscorlib]System .Console::WriteLine(string) L_000b: nop L_000c: ret } This is an example of the intermediate language (IL). Although this code is reasonable readable, it still resembles machine code. Reflector can take it a big step further than that. It supports decompilation to various software languages. The screenshot below shows the de-compiled code in Delphi. Procedure Program. Main(args: string[]); Begin Console.writeLine(Hello world) End; This has a strong resemblance with the original source code. Using the analyze features of Reflector it is rather simple to detect the structure of even large and complex programs. What do obfuscation tools do? Obfuscation tools offer a set of transformations that transform compiled assemblies into assemblies that are much harder to read. The most common transformations will be discussed below. Renaming The most common and most used transformation is renaming of types, functions and fields. By assigning a different name to a function, it is less likely to draw attention to itself. The following code sample shows very clearly where to look for the licensing model of this program.

By replacing the names of the functions with random names, the same program now looks like this.

This example shows a HelloWorld.exe assembly with 1 type, called Program in the HelloWorld namespace. This type contains a default constructor and a Main function.

By only replacing names, it is now harder (at least more work) for hackers to find the licensing model.

Page 40 / 1854

June 2008 BLAISE PASCAL 2

[Advertorial] DeepSea Obfuscator: .NET Obfuscation for all


String encryption As seen in the HelloWorld sample, strings are clearly visible in an assembly. In that sample this will not bother anyone, but there are many cases where the content of strings provides a clear insight in the meaning and purpose of a function. It is also easy to search for common strings like Invalid serial number.String encryption is a transformation that replaces readable strings in an assembly by some form of encrypted strings. Each tool has its own mechanism for this. This is what the HelloWorld sample could look like with string encryption:
Procedure O.u. Main(d: string[]); Begin Console.WriteLine(W.u($744d8230)) End; It is still clear that something is written to the console, but it is much harder to derive what is written.

2005 .NET

make obfuscation a standard and natural element or the daily work of a .NET developer. During the development of DeepSea Obfuscator, careful considerations have been given to the problems with existing tools and the responses to them by .NET developers. The following topics are results of these considerations: 1. Obfuscation has to be applicable without any need for configuration. An obfuscation tool has to recognize possible problem areas by itself and deal with them. For example, DeepSea Obfuscator will not rename serializable types and strongly typed resources are recognized and renamed correctly. If a developer does want to intervene in the obfuscation process, he can do so using standard attributes in the source code, since the source code is the workplace for the developer. Developers like to use their own development languages and tools. DeepSea Obfuscator is equipped with a wide variety of integrations with various development tools. The Code assistant, used to create configuration snippets, supports various development languages such a C#, VB.NET and Delphi. Developers do not want to wait for support for new framework versions or be slowed down due to slow development tools. DeepSea Obfuscator supported Silverlight within days after its release. It also makes optimal use of multi-core PC's. Finally the price. DeepSea Obfuscator chooses a license model the enables developers to use their license of each PC they're working on and a price that makes .NET obfuscation accessible to all.

Control flow obfuscation To avoid de-compilation by tools like ildasm or Reflector, some obfuscation tools alter the code of a function. By adding extra jumps or changing well known code structures it is attempted to make decompilation impossible. In real life this transformation is little successful. Each transformation is soon recognized by Reflector and then de-compiled after all. These kinds of transformations are also often the cause of various problems. The more complex a transformation becomes, the greater chances are that the semantics of the code are changed. In that case the programs no longer behave as the developer had intended. Other transformations Besides the transformations describes above, there are various other transformations, often surrounded with various patent pending claims. Experience leads to believe that these kinds of transformations, by their complexity and necessary configuration, tend to cause more problems that the protection they claim to provide. Common problems with obfuscation Despite the common knowledge that obfuscation is a technology that every .NET developer should use, it is still frequently unused. There are various reasons for that, but they tend to boil down to the following: 1. Obfuscation tools are traditionally expensive (usually they double the cost of a developers desktop). For this reasons they are not part of the standard equipment of a software developer. Obfuscation tools are traditionally slow. Despite the multi-core PC's that most developers use, obfuscation tools are rarely optimized for them. The developer has to wait much longer which often results in obfuscation not being a part of a standard compilation cycle. Obfuscation tools are complex. A lot of configuration is needed to make traditional obfuscation tools work properly. Since this configuration is often edited outside the source code (in a different application), the developer is forced to concern with matters outside his primary task; the development of source code.

2.

3.

4.

You can evaluate DeepSea Obfuscator right now. A free evaluation version is available on our website. There is no time limit on your evaluation.
Ewout Prangsma is General Manager TallApplications BV, supplier of DeepSea Obfuscator. www.deepseaobfuscator.com

2.

is dealer of DeepSea obfuscator http://www.barnsten.com/deepsea.aspx

3.

As a result of all these reasons, obfuscation is often only used at the very end of the development process. This often results in a very late (or lack of) discovery of typical problems such as serialization or user configuration. DeepSea Obfuscator DeepSea Obfuscator is a new player in the obfuscation market. It is made for .NET developers by .NET developers. The concept behind DeepSea Obfuscator is simple and straight forward:

Special offer for Subscribers: Deepsea Obfuscator 237,00 10% 213,00 incl. Vat
All you need to to do is email us your order and it will be send to you.

June 2008 BLAISE PASCAL 2

Page 41 / 1855

....B B B B B B B B Buzzword mania a a a a a a a a....


PDA PDS PHP PMQ PS RAIS RIA RPC SOA SOAP SPOKE SQL SSL TLS TP Vista WIN2K WINCE WINXP WS XML ESB FPC GRID IM HA HACMP HPC HTTP HUB IPC JAVA JRE JVM LB LINUX MB MoM MQ OCX ODC OOP OP ORB P2P Enterprise Service Bus Free Pascal Compiler: A compiler for Pascal Grid computing Instant Messaging High Availability High Availability Clustered Multi Processing High Performance Computing clustering HyperText Transfer Protocol Central data distributing node Inter Process Communication A randomly named programming language Java Runtime Environment Java Virtual Machine Load Balancing An open sourced popular operating system Message Broker Message oriented Middleware Message Queue OLE custom control Open Database Connectivity Object Oriented Programming Object Pascal Object Request Broker Point to Point communication Personal Digital Assitant Persistent Data Storage Hypertext Preprocessor: A programming language Persistent Message Queue Publish/Subscribe Redundant Array of Inexpensive Servers Rich Internet Application Remote Procedure Call Service Oriented Architecture Simple Object Access Protocol Data consuming or producing node connected to a hub Structured Query Language Secure Socket Layer Transport Layer Security Transaction Processing MS Vista: An operating system MS Windows 2000: A popular operating system Windows Compact Edition: Base for Windows Mobile MS Windows XP: A popular operating system Web Service eXtended Markup Language

AIR AJAX AOP AOSD AS B2B BI BIS BO BP BUS

C C++ C# CLOUD CM DA DELPHI DOTNET DS DOM EAI ECI

Adobe Integrated Runtime Asynchronous JavaScript and XML Aspect Oriented Programming Aspect Oriented Software Development Application Server Business to Business Business Intelligence Business Integration Software Business Object Business Process An abstracted media of communication between nodes A programming language An object oriented version of C C-Sharp: A programming language Cloud Computing Cluster Manager Data Access A programming language A runtime environment from Microsoft Data Storage Document Object Model Enterprise Application Integration Enterprise Content Integration

kbmMW := Succ(Buzzwords)

$kbmMW = (Buzzwords)++
Register and download kbmMW CodeGear Edition for FREE from

kbmMW = (Buzzwords)++

www.turbomiddleware.com
Read more about kbmMW Professional Edition and kbmMW Enterprise Edition at

www.components4developers.com

kbmMW is a trademark of Components4Developers. TurboMiddleware is a trademark of Components4Developers. All other trademarks are the trademarks of their respective owners

Potrebbero piacerti anche