<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'><id>tag:blogger.com,1999:blog-29331675</id><updated>2008-05-04T10:02:06.003+02:00</updated><title type='text'>The Delphi Geek</title><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/blogger.html'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default?start-index=26&amp;max-results=25'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml'/><author><name>gabr</name></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>92</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-29331675.post-3731342398341995199</id><published>2008-05-01T15:48:00.001+02:00</published><updated>2008-05-01T15:48:41.359+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #8: A Synchronisation Toolkit</title><content type='html'>&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;Every programmer accumulates reusable code over the years and I am no exception. Sometimes, parts of that code seem generic enough to be worth documenting and presenting it to the public.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;Today, I'd like to put on display four classes belonging to the inter-process synchronisation part of my personal toolbox.&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- A Synchronisation Toolkit, The Delphi Magazine 86, October 2002&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;In October 2002 I presented my &lt;a title="GpSync current" href="http://gp.17slon.com/gp/gpsync.htm" target="_blank"&gt;GpSync&lt;/a&gt; unit, which is still very actively updated and developed. At the time of writing, there were four reusable classes in this unit - a token, a group, a counted group and single-write-multiple-readers reimplementation copied (and translated from C to Delphi) from Advanced Windows by Jeffrey Richter. &lt;p&gt;Since that first public occurrence, GpSync was extended with a flag, a counter, a message queue (shared-memory based, can be used to send messages from an interactive application to a service and back), and a circular buffer. To this day, it represents most common building blocks I'm using in multithreaded applications. &lt;p&gt;&amp;nbsp; &lt;p&gt;Links: &lt;a href="http://www.17slon.com/blogs/gabr/TDM/tdm86-gp.pdf" target="_blank"&gt;article&lt;/a&gt; (PDF, 148 KB), &lt;a href="http://www.17slon.com/blogs/gabr/TDM/tdm86-gp.zip" target="_blank"&gt;source code&lt;/a&gt; (ZIP, 196 KB), &lt;a title="GpSync current" href="http://gp.17slon.com/gp/gpsync.htm" target="_blank"&gt;current GpSync unit&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/05/tdm-rerun-8-synchronisation-toolkit.html' title='TDM Rerun #8: A Synchronisation Toolkit'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=3731342398341995199' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3731342398341995199'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3731342398341995199'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-5243911239324581927</id><published>2008-04-21T22:10:00.001+02:00</published><updated>2008-04-21T22:10:57.021+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='enumerators'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Enumerating XML nodes</title><content type='html'>&lt;p&gt;I just submitted an update to OmniXmlUtils.pas - a helper library for the Delphi implementation of the XML DOM model, the &lt;a title="OmniXML" href="http://www.omnixml.com/" target="_blank"&gt;OmniXML&lt;/a&gt;. It allows you to walk over child nodes with a simple enumerator (but you already saw that coming, huh?).&lt;/p&gt; &lt;p&gt;In OmniXML (and in MS XML on which the OmniXML interface is based), you access child nodes via IXMLNodeList interface. Actually, when you access any subset of nodes (for example, when you call SelectNodes and pass it an XPath expression), you are using IXMLNodeList. More and more I was using it, more I hated its stupid interface. &lt;pre class="pas-source"&gt;  IXMLCustomList = &lt;span class="pas-kwd"&gt;interface&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetLength: Integer;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetItem(&lt;span class="pas-kwd"&gt;const&lt;/span&gt; &lt;span class="pas-kwd"&gt;Index&lt;/span&gt;: Integer): IXMLNode;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Item[&lt;span class="pas-kwd"&gt;const&lt;/span&gt; &lt;span class="pas-kwd"&gt;Index&lt;/span&gt;: Integer]: IXMLNode &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetItem;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Length: Integer &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetLength;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;  IXMLNodeList = &lt;span class="pas-kwd"&gt;interface&lt;/span&gt;(IXMLCustomList)&lt;br /&gt;    &lt;span class="pas-kwd"&gt;procedure&lt;/span&gt; Reset;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; NextNode: IXMLNode;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;[I removed declarations of all functions that are not used when walking over the list.]&lt;br /&gt;&lt;p&gt;&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Basically, I have two options with the current design. I can use a standard for loop using Length and Item[] or I can use NextNode and while loop.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Or, of course, I can write an enumerator - and that's what I did. XMLEnumNodes takes an IXMLNodeList and wraps it in an enumerator. Even better - it takes an xml node or document and an XPath expression and runs SelectNodes automatically for me so I can write code like this: &lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; nodePassword &lt;span class="pas-kwd"&gt;in&lt;/span&gt; XMLEnumNodes(xmlConfig, &lt;span class="pas-str"&gt;'//*/PasswordHash'&lt;/span&gt;) &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  SetTextChild(nodePassword, &lt;span class="pas-str"&gt;'(removed)'&lt;/span&gt;);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Now that's what I call a nice code!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;If you can't wait for tomorrow update to the &lt;a title="OmniXML daily snapshot" href="http://www.omnixml.com/download.html" target="_blank"&gt;OmniXML daily snapshot&lt;/a&gt;, you can download OmniXMLUtils 1.25 &lt;a title="OmniXMLUtils 1.25" href="http://17slon.com/blogs/gabr/files/OmniXMLUtils125.zip" target="_blank"&gt;here&lt;/a&gt;.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/04/enumerating-xml-nodes.html' title='Enumerating XML nodes'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=5243911239324581927' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/5243911239324581927'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/5243911239324581927'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-3173887829579927797</id><published>2008-04-01T13:06:00.002+02:00</published><updated>2008-05-01T15:17:33.331+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #7: File Sharing on Linux</title><content type='html'>&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;In the Windows world we are used to file locking. It is something natural, something that permeates the operating system, something that we had to learn when we stopped using DOS. But for all practical purposes, Linux is Unix, and Unix was designed with different things in mind, one of which was openness and sharing. From the very old days, Unix has supported different kinds of file systems, including those that span a network. Remember, that was well before Windows, and file locking was something that was simply too expensive (in terms of time and network traffic) to be implemented. And that is why there is no locking on Linux.&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- File Sharing on Linux, The Delphi Magazine 84, August 2002&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;The year was 2002, Kylix was still alive and I was planning to write some software for the new platform. Sadly, that never happened - other (Windows) projects were more important and before I came back to Kylix, it was more or less dead.&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/TDMRerun7FileSharingonLinux_B3FE/image.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="181" alt="GpLinuxFileSync tester" src="http://17slon.com/blogs/gabr/files/TDMRerun7FileSharingonLinux_B3FE/image_thumb.png" width="244" align="right" border="0"&gt;&lt;/a&gt;Still, this short excursion was responsible for my only Linux article. I took my&amp;nbsp; GpFileSync unit from &lt;a title="Let's Cooperate" href="http://17slon.com/blogs/gabr/2008/02/tdm-rerun-4-let-cooperate.html" target="_blank"&gt;TDB 68: Let's Cooperate&lt;/a&gt; and tried to adapt it to Linux. As it turned out, this was nearly an impossible mission. The ideas behind Unix file systems almost totally prevent its abuse in such direction. To make myself clear - it is certainly possible to use file system for locking, but there are big problems where you want to a) make the lock disappear if the program terminates abnormally, b) put the lock file on a networked location (NFS) or c) all of the above.&lt;/p&gt; &lt;p&gt;I ended with three less than perfect solutions. First used a simple lock file, second was an adaptation of the first that worked with the NFS 2 volums (both were not able to auto-delete the file-mutex on program termination) and third used fcntl() call (auto-delete worked well, but NFS was not fully supported). As far as I know, none of them was ever tested in a real-world application.&lt;/p&gt; &lt;p&gt;Links: &lt;a title="TDM 84: File Sharing on Linux [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm84-gp.pdf" target="_blank"&gt;article&lt;/a&gt; (PDF, 125 KB), &lt;a title="TDM 84: File Sharing on Linux [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm84-gp.zip" target="_blank"&gt;source code&lt;/a&gt; (ZIP, 16 KB), &lt;a title="GpFileSync 2.06" href="http://www.17slon.com/blogs/gabr/TDM/GpFileSync-current.zip" target="_blank"&gt;current GpFileSync unit&lt;/a&gt;&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/04/tdm-rerun-7-file-sharing-on-linux.html' title='TDM Rerun #7: File Sharing on Linux'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=3173887829579927797' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3173887829579927797'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3173887829579927797'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-6217109632923188383</id><published>2008-03-27T08:43:00.001+01:00</published><updated>2008-03-27T08:44:35.814+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>GpLists emergency update</title><content type='html'>&lt;p&gt;TGpInt64List.Move was totally broken. Great thanks go to 'Intra' for noticing it.&lt;/p&gt; &lt;p&gt;&lt;a title="GpLists 1.38a" href="http://gp.17slon.com/gp/gplists.htm"&gt;Fixed now.&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/gplists-emergency-update.html' title='GpLists emergency update'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=6217109632923188383' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6217109632923188383'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6217109632923188383'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-9016749953005659742</id><published>2008-03-21T11:31:00.001+01:00</published><updated>2008-03-21T12:02:50.957+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='enumerators'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>Walking the key-value container</title><content type='html'>&lt;p&gt;The recent discussion in comments to my latest articles (&lt;a title="Fun with enumerators - Walking the (integer) list" href="http://17slon.com/blogs/gabr/2008/03/fun-with-enumerators-walking-integer.html" target="_blank"&gt;Fun with enumerators - Walking the (integer) list&lt;/a&gt;, &lt;a title="On bad examples and smelly code" href="http://17slon.com/blogs/gabr/2008/03/on-bad-examples-and-smelly-code.html" target="_blank"&gt;On bad examples and smelly code&lt;/a&gt;) caused a shift in my perspective. I was always treating my TGpIntegerObjectList and TGpInt64ObjectList as lists with some baggage attached, but in practice I'm almost never using them that way. Most of the time I treat them as a background storage for key-value pairs.&lt;/p&gt; &lt;p&gt;What's the difference? Most of the time, I don't care about item indices. When I use my lists as containers, I never need to know where in the list some (Key, Value) pair is stored. OK, almost never. When I'm deleting from the list, I sometimes use the index, just for the performance purposes. And when I access the Value part, I have to find the index with IndexOf and then use it to reference the Objects property. There are probably other cases too - but that's something that you just have to take into account if you want to use a list as a storage media.&lt;/p&gt; &lt;p&gt;From time to time I'm extending lists in my &lt;a title="GpLists" href="http://gp.17slon.com/gp/gplists.htm" target="_blank"&gt;GpLists&lt;/a&gt; unit with small wrappers that help me not to use list indices in some specific situation. Today, I used the new Walk enumerator in some code and asked myself: "Why does it have to return list index? Why couldn't it return a (Key, Value) pair?"&lt;/p&gt; &lt;p&gt;Good question. Why couldn't it? It turns out that it could.&lt;/p&gt; &lt;h2&gt;A little enumerator that could&lt;/h2&gt; &lt;p&gt;Let's set up some assumptions first. Assume that I have this pointless list.&lt;pre class="pas-source"&gt;il := TGpIntegerObjectList.Create;&lt;br /&gt;il.AddObject(&lt;span class="pas-num"&gt;1&lt;/span&gt;, TGpString.Create(&lt;span class="pas-str"&gt;'one'&lt;/span&gt;));&lt;br /&gt;il.AddObject(&lt;span class="pas-num"&gt;2&lt;/span&gt;, TGpString.Create(&lt;span class="pas-str"&gt;'two'&lt;/span&gt;));&lt;br /&gt;il.AddObject(&lt;span class="pas-num"&gt;3&lt;/span&gt;, TGpString.Create(&lt;span class="pas-str"&gt;'three'&lt;/span&gt;));&lt;/pre&gt;Further assume that I want to walk over it and display both number and text for each item. I can do this with a standard loop.&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx := &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;to&lt;/span&gt; il.Count - &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  Log(&lt;span class="pas-str"&gt;'%d: %s'&lt;/span&gt;, [il[idx], TGpString(il.Objects[idx]).Value]);&lt;/pre&gt;Or with my index-returning walker.&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.Walk &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  Log(&lt;span class="pas-str"&gt;'%d: %s'&lt;/span&gt;, [il[idx], TGpString(il.Objects[idx]).Value]);&lt;/pre&gt;But what I'd really like to do is.&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; kv &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.WalkKV &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  Log(&lt;span class="pas-str"&gt;'%d: %s'&lt;/span&gt;, [kv.Key, TGpString(kv.Value).Value]);&lt;/pre&gt;Or even better.&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; kv &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.WalkKV &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  Log(&lt;span class="pas-str"&gt;'%d: %s'&lt;/span&gt;, [kv.Key, kv.StrValue]);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;In other words, I want to return not a single item, but a pair of items from the enumerator. Of course, Delphi expressions can only return a single result and not a &lt;a title="tuple" href="http://en.wikipedia.org/wiki/Tuple" target="_blank"&gt;tuple&lt;/a&gt; so we have to provide a wrapper for enumerated (Key, Value) pairs. We have to pack them in a record, class or interface.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Getting all self-destructive&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;My first idea was to return an interface to a key-value object from the enumerator. &lt;/p&gt;&lt;pre class="pas-source"&gt;  IGpKeyValue = &lt;span class="pas-kwd"&gt;interface&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetKey: int64;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetValue: TObject;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Key: int64 &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetKey;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Value: TObject &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetValue;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ IGpKeyValue }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  TGpKeyValue = &lt;span class="pas-kwd"&gt;class&lt;/span&gt;(TInterfacedObject, IGpKeyValue)&lt;br /&gt;  &lt;span class="pas-kwd"&gt;private&lt;/span&gt;&lt;br /&gt;    kvKey: int64;&lt;br /&gt;    kvValue: TObject;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;protected&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt;  GetKey: int64;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt;  GetValue: TObject;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; Create(key: int64; value: TObject);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Key: int64 &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetKey;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Value: TObject &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetValue;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpKeyValue }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  TGpIntegerObjectListWalkKVEnumerator = &lt;span class="pas-kwd"&gt;class&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-comment"&gt;//...&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt;  GetCurrent: IGpKeyValue;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Current: IGpKeyValue &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetCurrent;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpIntegerObjectListWalkKVEnumerator }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TGpIntegerObjectListWalkKVEnumerator.GetCurrent: IGpKeyValue;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  idx: integer;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  idx := wkeListEnumerator.GetCurrent;&lt;br /&gt;  Result := TGpKeyValue.Create(wkeListEnumerator.List[idx],&lt;br /&gt;    TGpIntegerObjectList(wkeListEnumerator.List).Objects[idx]);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpIntegerObjectListWalkKVEnumerator.GetCurrent }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;That surely works fine, but guess what - it's incredibly slow. I wouldn't expect anything else - after all an object is allocated for every enumerated value, plus all that complications with interface reference counting...&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I did some testing, of course. Thousand iterations over a list with 10.000 elements. Results are quite interesting. 5 milliseconds for a standard for loop. 50 milliseconds for my Walk enumerator. 5 &lt;strong&gt;seconds&lt;/strong&gt; for interface-based WalkKV. Ouch! Let's return to the drawing board...&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;One allocation is enough&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;My next idea was to return not an interface, but an object. When you return an object, you actually return a pointer to the object data, which is quite fast. It would not help much if I would recreate this object every time the GetCurrent is called, but luckily there is no real reason to do that. I can create the object when enumerator is created and destroy it when enumerator is destroyed.&lt;/p&gt;&lt;pre class="pas-source"&gt;  TGpKeyValue = &lt;span class="pas-kwd"&gt;class&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;private&lt;/span&gt;&lt;br /&gt;    kvKey  : int64;&lt;br /&gt;    kvValue: TObject;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Key: int64 &lt;span class="pas-kwd"&gt;read&lt;/span&gt; kvKey &lt;span class="pas-kwd"&gt;write&lt;/span&gt; kvKey;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Value: TObject &lt;span class="pas-kwd"&gt;read&lt;/span&gt; kvValue &lt;span class="pas-kwd"&gt;write&lt;/span&gt; kvValue;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpKeyValue }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  TGpIntegerObjectListWalkKVEnumerator = &lt;span class="pas-kwd"&gt;class&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;private&lt;/span&gt;&lt;br /&gt;    wkeCurrentKV: TGpKeyValue;&lt;br /&gt;    wkeListEnumerator: TGpIntegerListWalkEnumerator;&lt;br&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; Create(aList: TGpIntegerObjectList; idxFrom, idxTo: integer);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;destructor&lt;/span&gt;  Destroy; &lt;span class="pas-kwd"&gt;override&lt;/span&gt;;&lt;br /&gt;    &lt;span class="pas-comment"&gt;//...&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt;  GetCurrent: TGpKeyValue;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Current: TGpKeyValue &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetCurrent;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpIntegerObjectListWalkKVEnumerator }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; TGpIntegerObjectListWalkKVEnumerator.Create(aList: TGpIntegerObjectList;&lt;br /&gt;  idxFrom, idxTo: integer);&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;inherited&lt;/span&gt; Create;&lt;br /&gt;  wkeListEnumerator := TGpIntegerListWalkEnumerator.Create(aList, idxFrom, idxTo);&lt;br /&gt;  wkeCurrentKV := TGpKeyValue.Create;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpIntegerObjectListWalkKVEnumerator.Create }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;destructor&lt;/span&gt; TGpIntegerObjectListWalkKVEnumerator.Destroy;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  FreeAndNil(wkeCurrentKV);&lt;br /&gt;  FreeAndNil(wkeListEnumerator);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;inherited&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpIntegerObjectListWalkKVEnumerator.Destroy }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TGpIntegerObjectListWalkKVEnumerator.GetCurrent: TGpKeyValue;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  idx: integer;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  idx := wkeListEnumerator.GetCurrent;&lt;br /&gt;  wkeCurrentKV.Key := wkeListEnumerator.List[idx];&lt;br /&gt;  wkeCurrentKV.Value := TGpIntegerObjectList(wkeListEnumerator.List).Objects[idx];&lt;br /&gt;  Result := wkeCurrentKV;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpIntegerObjectListWalkKVEnumerator.GetCurrent }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;BTW, you can see another trick in this implementation - enumeration by delegation. I'm reusing my Walk enumerator to do the actual walking.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;That works &lt;strong&gt;much&lt;/strong&gt; faster than the interface-based version - 300 ms for my test case. It's still six times slower than the Walk enumerator, though, and it is not really obvious why the difference is so big. Leave that be, for a moment.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The third approach would be to use a record to store the current (Key, Value) pair. Then there is no allocation/deallocation at all, but resulting code is &lt;strong&gt;not&lt;/strong&gt; faster. Record-based enumerator needs about 500 ms to run the test case.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;This slowdown occurs because record is not returned as a pointer, but as a full copy. In the class-based scenario, GetCurrent returns a pointer to the TGpKeyValue object and that pointer is passed in a register. In the record version, GetCurrent returns not a pointer to the record, but the record itself - it copies full record to the stack so the caller can use this copy - and that is waaaay slower.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;The speed difference&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Let's return to that major speed difference between Walk and WalkKV. I looked at the code, but couldn't find any good reason. Then I turned to the CPU view and it was evident. The problem lied not in the enumerator, but in my poorly designed benchmarking code :(&lt;/p&gt;&lt;br /&gt;&lt;p&gt;You see, I was timing multiple repetitions of these three loops:&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx := &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;to&lt;/span&gt; &lt;span class="pas-num"&gt;10000&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;br /&gt;  ;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.Walk &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  ;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; kv &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.WalkKV &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  ;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Three empty loops, so I'm timing just the enumeration, yes? No!&lt;/p&gt;&lt;br /&gt;&lt;p&gt;First loop just runs from 1 to 10000. Trivial job and compiler will generate great code.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Second loop does the same, but with more overhead.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Third loop does much more than that. It also accesses il[] and il.Objects[] (inside the GetCurrent).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;In reality, code inside the for statement in the first two cases would have to access il[] and il.Objects[] by itself. The code inside the third for statement has no need for that - it gets data neatly prepared in kv.Key and kv.Value.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I changed the loops to:&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx := &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;to&lt;/span&gt; il.Count - &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  il[idx];&lt;br /&gt;  obj := il.Objects[idx];&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.Walk &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  il[idx];&lt;br /&gt;  obj := il.Objects[idx];&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; kv &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.WalkKV &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  kv.Key;&lt;br /&gt;  obj := kv.Value;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I'm just accessing and throwing the Items[]/Key information away and then I copy the Objects[]/Value information into the local variable. All loops are now running comparable jobs. &lt;/p&gt;&lt;br /&gt;&lt;p&gt;Results are now significantly different. Simple for loop needs 300 ms to run the test, Walk version needs 310 ms (really small difference) and WalkKV version needs 470 ms. It is still slower, but by less than the factor of two. If there would be a real code inside the for loop, the difference would be unnoticeable.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Morale? You should benchmark, but you should also check that you're benchmarking the right thing!&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Syntactic sugar&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;The generic version of WalkKV only supports this kind of code:&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; kv &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.WalkKV &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  Log(&lt;span class="pas-str"&gt;'%d: %s'&lt;/span&gt;, [kv.Key, TGpString(kv.Value).Value]);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;But there's a really neat trick to change this into&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; kv &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.WalkKV &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  Log(&lt;span class="pas-str"&gt;'%d: %s'&lt;/span&gt;, [kv.Key, kv.StrValue]);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;without modifying the WalkKV itself. Can you guess it? Class helpers, of course.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;You just have to declare a simple helper in the WalkKV consumer (no need to change the WalkKV itself) and you can use the &lt;em&gt;StrValue&lt;/em&gt; instead of &lt;em&gt;TGpString(kv.Value).Value&lt;/em&gt;.&lt;/p&gt;&lt;pre class="pas-source"&gt;  TGpKeyStrValue = &lt;span class="pas-kwd"&gt;class&lt;/span&gt; helper &lt;span class="pas-kwd"&gt;for&lt;/span&gt; TGpKeyValue&lt;br /&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; StrValue: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;; &lt;span class="pas-kwd"&gt;inline&lt;/span&gt;;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TGpKeyStrValue }&lt;/span&gt; &lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TGpKeyStrValue.StrValue: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := TGpString(Self.Value).Value;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Conclusion: class helpers are great. Key-value walker is useful. Enumerator-induced performance loss is negligible. Enumerators are fun.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;---&lt;/p&gt;&lt;br /&gt;&lt;p&gt;I've tagged this and all previous enumerator-related articles with the &lt;em&gt;enumerators &lt;/em&gt;label so you can now access them all at once via this simple &lt;a title="Enumerator articles @ The Delphi Geek" href="http://17slon.com/blogs/gabr/labels/enumerators.html" target="_blank"&gt;link&lt;/a&gt;.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/walking-key-value-container.html' title='Walking the key-value container'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=9016749953005659742' title='3 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/9016749953005659742'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/9016749953005659742'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-2424482803911191273</id><published>2008-03-16T23:00:00.001+01:00</published><updated>2008-03-16T23:00:56.194+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>On bad examples and smelly code</title><content type='html'>&lt;p&gt;I've received lots of criticism on my &lt;a title="latest article on enumerators" href="http://17slon.com/blogs/gabr/2008/03/fun-with-enumerators-walking-integer.html" target="_blank"&gt;latest article on enumerators&lt;/a&gt;. For the convenience of my readers, here are some excerpts.&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;&lt;em&gt;Removal loops should be iterated backwards to prevent index shuffling.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;... while I can certainly see the usefulness in your Slice- and Walk-enumerators ... I don't think they're the best solution to the two examples you quoted.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;Simplicity does not justify an example that fails to work correctly. A bad example is still a bad example, no matter how simple.&lt;/em&gt;&lt;/p&gt; &lt;p&gt;&lt;em&gt;I'd also like to echo the other comments w.r.t your initial example. Reversing the direction of the iteration is the simplest wasy to simplify the exemplar task, not invoking enumerators and the such like.&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;They can be summarized in one sentence: "Your examples suck." The main rationale for that reasoning seems to be the accepted way to delete stuff from a TList/TObjectList/TAnyList - start at the end and proceed backwards.&lt;/p&gt; &lt;p&gt;Well, I have two things to say. [I apologize for responding in a new post, but I feel that this is an interesting topic by itself.]&lt;/p&gt; &lt;p&gt;1. Yes, my examples are simplified. Terribly so. Maybe they are even simplistic. At least some part of my readers seems to think so. But that's just the way I like them.&lt;/p&gt; &lt;p&gt;I'm not trying to enforce my ideas on my readers. I'm only displaying them to the public. That's why I tend to minimize all my examples. I'll show you the code, I'll show you the usage, but you should think about the solution and how it applies to your problems. Maybe you'll see that it can be useful and you'll adopt it. Maybe not. Maybe you'll remember it a year from now and suddenly see the light. Maybe you'll use it for a month or two and then decide that I'm a twit and that your previous ways were better. [In this case, please refer to the last paragraph in this post.] I don't really care. I'm giving my ideas out, I'm glad of every feedback and I'm happy if somebody else is using my code. I've received a lot from the Delphi community in past ten years and I like to give back as much as I can.&lt;/p&gt; &lt;p&gt;2. I don't like the 'traverse the list backwards and all will be well' approach to list deletion. I have a really strong aversion to it. It looks fine, it works fine but it smells really bad.&lt;/p&gt; &lt;p&gt;I was thinking a lot about why I dislike it so much and I think that the main reason is that it relies too much on the list internals. Admittedly, this is a very well documented behavior, which will never change in the future. Let's try again - it relies too much on the internals of the underlying structure. If at some point in the future you change the structure, this assumption may fail. OK, it is hard to imagine an underlying structure that would behave almost the same as the TList but would behave differently on Delete, but what if you just forgot to adapt this part of the code to the new structure? Unlikely, I agree.&lt;/p&gt; &lt;p&gt;People will complain that I can't give a strong reason for my fears and they will be correct. I don't have any good cause against this kind of list cleanup.&lt;/p&gt; &lt;p&gt;You see, if I learned something in my programming career, it's that some code should never be used. When you program, some things work out and some not. Some code works fine from the beginning and some causes problem after a problem and bug report after a bug report. A good programmer recognizes that and stops using the code/fragments/patterns that are likely to cause problems in the future. Somehow I have classified the 'downto deletion' in the same category. I don't remember why. That's why I'm not saying it is bad, only that it smells bad.&lt;/p&gt; &lt;p&gt;The other reason is the code readability. If find the code that explicitly states its intention more readable and more maintainable. Your point may, of course, differ.&lt;/p&gt; &lt;p&gt;---&lt;/p&gt; &lt;p&gt;May my ramblings not stop you from commenting. I'm happy when I receive comments that point to my errors, however justifiable they may be. Even if I don't agree with them, they make me rethink my strategies so at the end I'm even more sure in my approach.&lt;/p&gt; &lt;p&gt;Flame on!&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/on-bad-examples-and-smelly-code.html' title='On bad examples and smelly code'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=2424482803911191273' title='10 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/2424482803911191273'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/2424482803911191273'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-6810470796300386442</id><published>2008-03-14T12:25:00.001+01:00</published><updated>2008-03-20T17:19:07.709+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='enumerators'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>Fun with enumerators - Walking the (integer) list</title><content type='html'>&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;Do you hate such code?&lt;/em&gt;&lt;/p&gt;&lt;pre class="pas-source"&gt;    idx := &lt;span class="pas-num"&gt;0&lt;/span&gt;;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;while&lt;/span&gt; idx &amp;lt; il.Count &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;if&lt;/span&gt; ShouldBeRemoved(il[idx]) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;        il.Delete(idx)&lt;br /&gt;      &lt;span class="pas-kwd"&gt;else&lt;/span&gt;&lt;br /&gt;        Inc(idx);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;&lt;em&gt;If you do, read on!&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;br /&gt;&lt;p&gt;TGpIntegerList &lt;em&gt;[For the purpose of this blog entry, TGpIntegerList token is redefined to implicitly expands to "TGpIntegerList, TGpInt64List, and all classes derived from them."]&amp;nbsp; &lt;/em&gt;included enumeration support since Delphi 2005 times. You could, for example, write&lt;/p&gt;&lt;pre class="pas-source"&gt;  il := TGpIntegerList.Create([&lt;span class="pas-num"&gt;1&lt;/span&gt;, &lt;span class="pas-num"&gt;3&lt;/span&gt;, &lt;span class="pas-num"&gt;5&lt;/span&gt;, &lt;span class="pas-num"&gt;7&lt;/span&gt;]);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;for&lt;/span&gt; i &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;      Log(&lt;span class="pas-str"&gt;'%d'&lt;/span&gt;, [i]);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;finally&lt;/span&gt; FreeAndNil(il); &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;and get numbers 1, 3, 5, and 7 in the log. &lt;/p&gt;&lt;br /&gt;&lt;h2&gt;Slicing ...&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;Recently, I've extended TGpIntegerList with two additional enumerators, both (of course) springing from practice. I've noticed that I still write lots of 'old school' for loops because I want to do something special with the first element. For example, take this simple code fragment that finds a maximum element in the list.&lt;/p&gt;&lt;pre class="pas-source"&gt;    max := il[&lt;span class="pas-num"&gt;0&lt;/span&gt;];&lt;br /&gt;    &lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx := &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;to&lt;/span&gt; il.Count - &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;if&lt;/span&gt; il[idx] &amp;gt; max &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;        max := il[idx];&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;To do such thing with for-each, you have to introduce a boolean flag.&lt;/p&gt;&lt;pre class="pas-source"&gt;    gotMax := false;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;for&lt;/span&gt; i &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;if&lt;/span&gt; &lt;span class="pas-kwd"&gt;not&lt;/span&gt; gotMax &lt;span class="pas-kwd"&gt;then&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;        max := i;&lt;br /&gt;        gotMax := true;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;end&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;else&lt;/span&gt; &lt;span class="pas-kwd"&gt;if&lt;/span&gt; i &amp;gt; max &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;        max := i;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Ugly.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Much nicer implementation can be written with the new Slice() enumerator.&lt;/p&gt;&lt;pre class="pas-source"&gt;    max := il[&lt;span class="pas-num"&gt;0&lt;/span&gt;];&lt;br /&gt;    &lt;span class="pas-kwd"&gt;for&lt;/span&gt; i &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.Slice(&lt;span class="pas-num"&gt;1&lt;/span&gt;) &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;if&lt;/span&gt; i &amp;gt; max &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;        max := i;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Slice takes two parameters representing starting and ending index in the list and returns all values between them (indices are inclusive). Ending index can be omitted in which case it will default to Count-1 (i.e. to the end of the list).&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Slice enumerator is implemented in a traditional manner - with the enumerator factory. For more details see &lt;a title="TWinControl.Controls Enumerator, Revisited" href="http://17slon.com/blogs/gabr/2008/02/twincontrolcontrols-enumerator_06.html" target="_blank"&gt;my older articles&lt;/a&gt; and &lt;a title="GpLists" href="http://gp.17slon.com/gp/gplists.htm" target="_blank"&gt;GpLists&lt;/a&gt; source code.&lt;/p&gt;&lt;br /&gt;&lt;h2&gt;... And Walking&lt;/h2&gt;&lt;br /&gt;&lt;p&gt;The second and much more interesting enumerator solves the problem shown in the introduction to this article. I noticed that I write lots of code that iterates over some list and removes some entries while keeping others intact. Typical scenario: removing timed-out clients from the server's list.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Something like that is impossible to do with the TGpIntegerList default enumerator. Firstly because this enumerator returns list elements and not list indices (and we typically have to access the object stored in the .Objects[] part of the list during the process) and secondly because the enumerator does not work correctly if enumerated list is modified behind its back. The second issue also prevents the use of standard for loop.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;To solve both problems at once, I've introduced the Walk enumerator. It returns list indices instead of list elements and functions correctly if Add, Insert or Delete are used on the list while enumerator is active. (The second part is implemented by using TGpIntegerList internal notification mechanism, introduced just for this purpose.)&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Now I can write:&lt;/p&gt;&lt;pre class="pas-source"&gt;    &lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx &lt;span class="pas-kwd"&gt;in&lt;/span&gt; il.Walk &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;      &lt;span class="pas-kwd"&gt;if&lt;/span&gt; ShouldBeRemoved(il[idx]) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;        il.Delete(idx);&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Implementation? Check the source, Luke. It's not complicated.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Now if only I could add such enumerator to the TObjectList ...&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/fun-with-enumerators-walking-integer.html' title='Fun with enumerators - Walking the (integer) list'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=6810470796300386442' title='10 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6810470796300386442'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6810470796300386442'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-977649665546812071</id><published>2008-03-14T10:32:00.001+01:00</published><updated>2008-03-14T10:39:34.711+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>Gp* update</title><content type='html'>&lt;h2&gt;GpHugeFile 5.05&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Delphi 2007 changed the implementation of CreateFmtHelp so that it clears the Win32 'last error'. Therefore, it was impossible to detect windows error in detection handler when EGpHugeFile help context was hcHFWindowsError. To circumvent the problem, all EGpHugeFile help contexts were changed to a negative value. HcHFWindowsError constant was removed. All Win32 API errors are passed in the HelpContext unchanged. IOW, if HelpContext &amp;gt; 0, it contains an Win32 API error code, otherwise it contains one of hcHF constants.  &lt;li&gt;Added method TGpHugeFile.GetTime and property TGpHugeFile.FileTime.&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;GpStreamS 1.22&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Added AppendToFile helper functions (two overloads).  &lt;li&gt;Added ReadTag and WriteTag support for int64 and WideString data.  &lt;li&gt;Added two overloaded SafeCreateFileStream versions returning exception message.  &lt;li&gt;Added Append stream helper.  &lt;li&gt;Added AutoDestroyWrappedStream property to the TGpStreamWindow class.  &lt;li&gt;Added TGpBufferedStream class. At the moment, only reading is buffered while writing is implemented as a pass-through operation.  &lt;li&gt;Made 'count' parameter to CopyStream optional, the same way as TStream.CopyFrom is implemented.  &lt;li&gt;Check for &amp;lt; 0 position in TGpStreamWindow.Seek.  &lt;li&gt;Fixed reading/writing of zero bytes in TGpStreamWindow.  &lt;li&gt;Added bunch of 'inline' directives.&lt;/li&gt;&lt;/ul&gt; &lt;h2&gt;GpLists 1.35&lt;/h2&gt; &lt;ul&gt; &lt;li&gt;Implemented TGpClassList, a TStringList-based list of classes. Class names must be unique. Useful when you need to implement a class registry to generate objects from class names.  &lt;li&gt;Added Walk enumerator to the TGpIntegerList and TGpInt64List. This enumerator allows list modifications (Add, Insert, Delete) from the enumeration consumer. IOW, you can do this:&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; idx &lt;span class="pas-kwd"&gt;in&lt;/span&gt; list.Walk &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;if&lt;/span&gt; SomeCondition(list[idx]) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;    list.Delete(idx);&lt;/pre&gt;&lt;br /&gt;&lt;li&gt;Modified TGpCountedInt64List to store int64 counters. &lt;br /&gt;&lt;li&gt;Added property ItemCounter[] to TGpCountedIntegerList and TGpCountedInt64List. &lt;br /&gt;&lt;li&gt;Added Slice(from, to) enumerator to TGpIntegerList and TGpInt64List. &lt;br /&gt;&lt;li&gt;Add TGpObjectRingBuffer and TGpDoublyLinkedList enumerators. Both lock access to the list during the enumeration process if multithreaded mode is enabled. &lt;br /&gt;&lt;li&gt;Added method Contains to TGpIntegerList, TGpInt64List, TGpCountedStringList, TGpTMethodList. &lt;br /&gt;&lt;li&gt;When TGpIntegerObjectList or TGpInt64ObjectList was Sorted and with Duplicates set to dupIgnore, calling AddObject with item that was already in the list caused internal exception. &lt;br /&gt;&lt;li&gt;Enumerators changed to records with GetCurrent inlined, as suggested in &lt;a title="Hallvard Vassbotn: More fun with Enumerators" href="http://hallvards.blogspot.com/2007/10/more-fun-with-enumerators.html" target="_blank"&gt;More fun with Enumerators&lt;/a&gt;.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;GpTimezone 1.22&lt;/h2&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Implemented DateLT, DateLE, DateGT, DateGE.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h2&gt;DSiWin32 1.36a&lt;/h2&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Added procedures DSiCenterRectInRect and DSiMakeRectFullyVisibleOnRect. &lt;br /&gt;&lt;li&gt;Added DSiTerminateProcessById procedure. &lt;br /&gt;&lt;li&gt;Added three performance counter helpers DSiPerfCounterToMS, DSiPerfCounterToUS, and DSiQueryPerfCounterAsUS. &lt;br /&gt;&lt;li&gt;Added function DSiTimeGetTime64. &lt;br /&gt;&lt;li&gt;Added function DSiGetProcessFileName. &lt;br /&gt;&lt;li&gt;Added function DSiEditShortcut. &lt;br /&gt;&lt;li&gt;Added function DSiInitFontToSystemDefault. &lt;br /&gt;&lt;li&gt;Added many SHGetSpecialFolderLocation folder constants. &lt;br /&gt;&lt;li&gt;Added ShGetSpecialFolderLocation flags CSIDL_FLAG_DONT_UNEXPAND and CSIDL_FLAG_DONT_VERIFY. &lt;br /&gt;&lt;li&gt;Added dynamically loaded API forwarder DSiSetSuspendState. &lt;br /&gt;&lt;li&gt;Added dynamically loaded API forwarders DSiEnumProcessModules, DSiGetModuleFileNameEx, and DSiGetProcessImageFileName. &lt;br /&gt;&lt;li&gt;Changed DSiIsAdmin to use big enough buffer for token data. &lt;br /&gt;&lt;li&gt;Changed DSiIsAdmin to ignore SE_GROUP_ENABLED attribute because function was sometimes incorrectly returning False. &lt;br /&gt;&lt;li&gt;Added parameter 'parameters' to DSiCreateShortcut and DSiGetShortcutInfo. &lt;br /&gt;&lt;li&gt;More stringent checking in DSiGetProcessWindow.&lt;/li&gt;&lt;/ul&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/gp-update.html' title='Gp* update'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=977649665546812071' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/977649665546812071'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/977649665546812071'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-4524797457175774056</id><published>2008-03-07T08:44:00.001+01:00</published><updated>2008-03-07T08:44:32.386+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='reviews'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #6: GExperts 1.0</title><content type='html'>&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;The greatest (in my view), and one of the oldest of the Delphi IDE expert suites is GExperts. It was created by Gerals Nunn (in 1997, if I am not mistaken), who also developed it for almost two years. Gerald has moved to other projects, but he kindly donated the GExperts source code to the online community.&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- GExperts 1.0, The Delphi Magazine 72, August 2001&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p align="left"&gt;I still stand by those words. GExperts is a great suite of experts and I still use it every day. I also wrote few experts that are included in the GExperts pack and few that are included only in my private build.&lt;/p&gt; &lt;p align="left"&gt;Links: &lt;a title="TDM 72: GExperts 1.0 [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm72-gp.pdf" target="_blank"&gt;article&lt;/a&gt; (PDF, 95 KB), &lt;a title="GExperts" href="http://www.gexperts.org" target="_blank"&gt;GExperts&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/tdm-rerun-6-gexperts-10.html' title='TDM Rerun #6: GExperts 1.0'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=4524797457175774056' title='5 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/4524797457175774056'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/4524797457175774056'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-2429694435562662050</id><published>2008-03-03T11:26:00.001+01:00</published><updated>2008-03-03T11:26:31.542+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Debugging With Lazy Breakpoints</title><content type='html'>&lt;p&gt;Sometimes you're hunting down a problem that occurs very rarely. When it does, an exception is triggered. If you're debugging, Delphi should pop up (it does) and allow you to look at the stack and inspect local variables (it doesn't - happens a lot to me). What can you do?&lt;/p&gt; &lt;p&gt;You could set a conditional breakpoint just before the problem occurs and test for the problem causing condition here. That would work on a short run, but if you're hunting the problem for many weeks, the breakpoint may move around or even get disabled. Delphi's breakpoint storage mechanism is somewhat unstable if you edit the code around the breakpoint a lot (and even more if you edit the code on multiple computers).&lt;/p&gt; &lt;p&gt;More stable solution is to do everything in code, from testing if debugger is running to pausing the program and breaking into the debugger. Following fragment illustrates the approach:&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt; &lt;p&gt;&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;if&lt;/span&gt; DebugHook &amp;lt;&amp;gt; &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt; &lt;span class="pas-comment"&gt;// test if debugger is running&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;if&lt;/span&gt; problem = true &lt;span class="pas-kwd"&gt;then&lt;/span&gt; &lt;span class="pas-comment"&gt;//test the condition&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;asm&lt;/span&gt; &lt;span class="pas-asm"&gt;int&lt;/span&gt; &lt;span class="pas-asm"&gt;3&lt;/span&gt; &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;//break into debugger&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Not very obvious and full of arcane knowledge, but it works.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/debugging-with-lazy-breakpoints.html' title='Debugging With Lazy Breakpoints'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=2429694435562662050' title='16 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/2429694435562662050'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/2429694435562662050'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-3673470061621919621</id><published>2008-03-01T21:03:00.001+01:00</published><updated>2008-03-01T21:03:58.363+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='best practices'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><title type='text'>Paint It Black (And Red)</title><content type='html'>&lt;p&gt;Just in case you're not following Julian's blog, I'd like to bring your attention to his new &lt;a title="Red-Black Trees" href="http://www.boyet.com/Articles/RedBlack1.html" target="_blank"&gt;series on red-black trees&lt;/a&gt;. Red-black trees are great data structure, but are notoriously hard to code correctly as the algorithm is full of weird edge cases. &lt;/p&gt; &lt;p&gt;I've always been a follower of his 'algorithms and data structures' articles in The Delphi Magazine and I'm sure his new series will be presented at the equally high standard. He's using C#, VB and .NET but I'm sure the series will be interesting for us Delphi users, too. [And he already gave us RB tree implementation in the &lt;a title="Easy Data Structures Library for Delphi" href="http://www.boyet.com/FixedArticles/EZDSL.html" target="_blank"&gt;EZDSL&lt;/a&gt; library - thanks, Julian!]&lt;/p&gt; &lt;p&gt;Of course, if you own his &lt;a title="Tomes of Delphi: Algorithms and Data Structures" href="http://www.boyet.com/FixedArticles/DADSBook.html" target="_blank"&gt;DADS&lt;/a&gt; book, you can read all about RB trees there. &lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/03/paint-it-black-and-red.html' title='Paint It Black (And Red)'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=3673470061621919621' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3673470061621919621'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3673470061621919621'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-4560013638947847455</id><published>2008-02-26T14:24:00.001+01:00</published><updated>2008-02-26T14:24:33.084+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='reviews'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #5: SvCom 4.0</title><content type='html'>&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;When you get into really serious Windows development, you may well need to create NT service applications. Borland's input is half-hearted: Delphi includes support for NT service application writing, but as you work with this framework, you'll soon discover its less-polished parts!&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- SvCom 4.0, The Delphi Magazine 69, May 2001&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p align="left"&gt;In the TDM #69 I reviewed SvCom, "an integrated package of classes and tools for development of service applications and use of NT Security framework." (as the current SvCom web page states). Good stuff, I'm still using it for service development.&lt;/p&gt; &lt;p align="left"&gt;Links: &lt;a href="http://www.17slon.com/blogs/gabr/TDM/tdm69-gp.pdf" target="_blank"&gt;article&lt;/a&gt; (PDF, 67 KB), &lt;a title="SvCom" href="http://www.aldyn-software.com/svcom.html" target="_blank"&gt;SvCom&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/02/tdm-rerun-5-svcom-40.html' title='TDM Rerun #5: SvCom 4.0'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=4560013638947847455' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/4560013638947847455'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/4560013638947847455'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-2401735444603453272</id><published>2008-02-11T14:06:00.001+01:00</published><updated>2008-02-11T14:06:59.861+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #4: Let's Cooperate</title><content type='html'>&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;Every serious Windows programmer who has developed at least one multi-threaded application knows about thread synchronisation. You know what I mean: critical sections, mutexes, semaphores, signals and so on. ... But to write truly distributed applications, running on different computers, we need new tools.&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- Let's Cooperate, The Delphi Magazine #68, April 2001&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/TDMRerun4LetsCooperate_C010/figure5.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="271" alt="Figure 5: Receiving a message" src="http://17slon.com/blogs/gabr/files/TDMRerun4LetsCooperate_C010/figure5_thumb.png" width="204" align="right" border="0"&gt;&lt;/a&gt;I don't remember what was the exact reason for this article, but obviously I was working on some multi-process multi-computer solution and I needed a way to synchronise those computers. Of course, once I started writing the article, it got completely out of hand and I finished with file system based mutex, critical section, group, event, and even with a message queue and single-writer-multiple-readers synchronisation primitive. There were also many state diagrams like the one of the right dispersed through the article. Poor readers. &amp;gt;:-(&lt;/p&gt; &lt;p&gt;The plan was to make most of them work on Linux as well, but as I discovered in &lt;em&gt;File Sharing on Linux&lt;/em&gt; (not yet published on my blog), but as I found later, this was nearly impossible to do as Linux implements file locking in completely different way.&lt;/p&gt; &lt;p&gt;I never used those primitives much as TCP/IP communication became much more prominent in later years. Still, they can be very useful if you want to quickly put together a multi-computer demo or quick and dirty solution.&lt;/p&gt; &lt;p&gt;The GpFileSync unit never made it to my web so I'm linking to the current version at the bottom of this article.&lt;/p&gt; &lt;p&gt;Links: &lt;a title="TDM 68: Let's Cooperate [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm68-gp.pdf" target="_blank"&gt;article&lt;/a&gt; (PDF, 117 KB), &lt;a title="TDM 68: Let's Cooperate [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm68-gp.zip" target="_blank"&gt;source code&lt;/a&gt; (ZIP, 47 KB), &lt;a href="http://www.17slon.com/blogs/gabr/TDM/GpFileSync-current.zip" target="_blank"&gt;current GpFileSync unit&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/02/tdm-rerun-4-let-cooperate.html' title='TDM Rerun #4: Let&amp;#39;s Cooperate'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=2401735444603453272' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/2401735444603453272'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/2401735444603453272'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-8056059332151750472</id><published>2008-02-06T11:54:00.001+01:00</published><updated>2008-02-06T11:54:30.388+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #3: Time Is The Simplest Thing</title><content type='html'>&lt;blockquote&gt; &lt;p&gt;&lt;em&gt;'Time, I'll tell you about time. Time is the simplest thing.'&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- Time Is The Simplest Thing, The Delphi Magazine #65, January 2001&lt;br&gt;[quote from Clifford D. Simak's The Fisherman]&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Now this is one of my favorite articles. It deals with time and more specifically with time zones. As most of my articles this one also builds on real-world problems I encounter during my programming practice. In this case, it was motivated by a big coding flop which taught me an important lesson: "Never store local time in a database." [No, I still don't want to recall all the details. It was related to daylight savings time transitions, that's all I'm willing to tell.]&lt;/p&gt; &lt;p&gt;Anyway, I fixed the problems in the abovementioned code and during that process I found out that there is almost no support for time zones built in Delphi and that Windows APIs on that area are really not that simple to use. So I built a time zone manipulation library that handles time zone information access and time zone conversions with daylight savings time support. This &lt;a href="http://gp.17slon.com/gp/gptimezone.htm"&gt;GpTimezone&lt;/a&gt; unit is still alive and I'm using it regularly - mostly the NowUTC function.&lt;/p&gt; &lt;p&gt;The article also dealt with the (now happily forgotten) Swatch Time.&lt;/p&gt; &lt;p&gt;Also included was a neat utility (if I'm allowed to say so) for timezone conversions, which I'm still using today without any changes to the original code.&lt;/p&gt;&lt;img height="406" alt="GpTimezone demo application" src="http://gp.17slon.com/gp/images/testutc.gif" width="791"&gt;  &lt;p&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;Links: &lt;a title="TDM 65: Time Is The Simplest Thing [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm65-gp.pdf"&gt;article&lt;/a&gt; (PDF, 134 KB), &lt;a title="TDM 65: Time Is The Simplest Thing [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm65-gp.zip"&gt;source code&lt;/a&gt; (ZIP, 162 KB), &lt;a title="GpTimezone" href="http://gp.17slon.com/gp/gptimezone.htm"&gt;current GpTimezone unit&lt;/a&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/02/tdm-rerun-3-time-is-simplest-thing.html' title='TDM Rerun #3: Time Is The Simplest Thing'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=8056059332151750472' title='1 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/8056059332151750472'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/8056059332151750472'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-8946589024738477850</id><published>2008-02-06T11:07:00.001+01:00</published><updated>2008-03-20T17:20:03.364+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='enumerators'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>TWinControl.Controls Enumerator, Revisited</title><content type='html'>&lt;p&gt;&lt;/p&gt; &lt;p&gt;Fredrik Loftheim made an interesting observation to my previous article on &lt;a title="TWinControl.Controls enumerator" href="http://17slon.com/blogs/gabr/2008/02/twincontrolcontrols-enumerator.html"&gt;TWinControl.Controls enumerator&lt;/a&gt; - namely that the enumerator factory doesn't have to be interface based. It can be replaced by a record. Source is almost the same as before, but we can skip the interface declaration. Generated code is simpler as the enumerator factory is simply allocated from the stack and automatically destroyed when it goes out of scope. No interface support and no reference counting is required. Simpler and faster, that's the way to go.&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;interface&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;type&lt;/span&gt;&lt;br /&gt;  TControlEnumerator = &lt;span class="pas-kwd"&gt;record&lt;/span&gt;&lt;br /&gt;  strict &lt;span class="pas-kwd"&gt;private&lt;/span&gt;&lt;br /&gt;    ceClass : TClass;&lt;br /&gt;    ceIndex : integer;&lt;br /&gt;    ceParent: TWinControl;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetCurrent: TControl;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; MoveNext: boolean;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Current: TControl &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetCurrent;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  TControlEnumeratorFactory = &lt;span class="pas-kwd"&gt;record&lt;/span&gt;&lt;br /&gt;  strict &lt;span class="pas-kwd"&gt;private&lt;/span&gt;&lt;br /&gt;    cefClass : TClass;&lt;br /&gt;    cefParent: TWinControl;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt;  GetEnumerator: TControlEnumerator;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumeratorFactory }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt;  EnumControls(parent: TWinControl; matchClass: TClass = &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;): TControlEnumeratorFactory;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;implementation&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; EnumControls(parent: TWinControl; matchClass: TClass = &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;): TControlEnumeratorFactory;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := TControlEnumeratorFactory.Create(parent, matchClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ EnumControls }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;{ TControlEnumerator }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; TControlEnumerator.Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  ceParent := parent;&lt;br /&gt;  ceClass := matchClass;&lt;br /&gt;  ceIndex := -&lt;span class="pas-num"&gt;1&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator.Create }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TControlEnumerator.GetCurrent: TControl;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := ceParent.Controls[ceIndex];&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator.GetCurrent }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TControlEnumerator.MoveNext: boolean;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := false;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;while&lt;/span&gt; ceIndex &amp;lt; (ceParent.ControlCount - &lt;span class="pas-num"&gt;1&lt;/span&gt;) &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;    Inc(ceIndex);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;if&lt;/span&gt; (ceClass = &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;) &lt;span class="pas-kwd"&gt;or&lt;/span&gt; (ceParent.Controls[ceIndex].InheritsFrom(ceClass)) &lt;span class="pas-kwd"&gt;then&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;      Result := true;&lt;br /&gt;      break; &lt;span class="pas-comment"&gt;//while&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;//while&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator.MoveNext }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-comment"&gt;{ TControlEnumeratorFactory }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; TControlEnumeratorFactory.Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  cefParent := parent;&lt;br /&gt;  cefClass := matchClass;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumeratorFactory.Create }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TControlEnumeratorFactory.GetEnumerator: TControlEnumerator;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := TControlEnumerator.Create(cefParent, cefClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumeratorFactory.GetEnumerator }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;The new version of the TWinControl.Controls enumerator plus some other stuff is available at &lt;a title="GpVCL" href="http://gp.17slon.com/gp/files/gpvcl.zip"&gt;http://gp.17slon.com/gp/files/gpvcl.zip&lt;/a&gt;.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/02/twincontrolcontrols-enumerator_06.html' title='TWinControl.Controls Enumerator, Revisited'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=8946589024738477850' title='4 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/8946589024738477850'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/8946589024738477850'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-3337553389785709423</id><published>2008-02-01T15:42:00.001+01:00</published><updated>2008-03-20T17:20:43.413+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='enumerators'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>TWinControl.Controls Enumerator</title><content type='html'>&lt;p&gt;I'm still having &lt;a href="http://17slon.com/blogs/gabr/2007/03/fun-with-enumerators.html" target="_blank"&gt;fun with enumerators&lt;/a&gt;. The latest I wrote is a filtering enumerator for TWinControl.Controls[] that allows me to write code like this: &lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  control: TControl;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; control &lt;span class="pas-kwd"&gt;in&lt;/span&gt; EnumControls(pnlAttributes, TSpeedButton) &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  TSpeedButton(control).Enabled := false;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;I found it interesting that Borland built a Components[] enumerator in the VCL, but not a Controls[] enumerator.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;The EnumControls interface is simple. It takes a starting point for enumeration and an optional class filter. By specifying the latter, you tell the enumerator that you're only interested in child controls of a specified class.&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; EnumControls(parent: TWinControl; &lt;br&gt;  matchClass: TClass = &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;): IControlEnumeratorFactory;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;As it is used in a for..in loop, EnumControl must return a class or interface that implements GetEnumerator function and that is exactly what it does. GetEnumerator, on the other hand, creates an instance of the TControlEnumerator class, which implements the actual enumeration.&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;type&lt;/span&gt;&lt;br /&gt;  TControlEnumerator = &lt;span class="pas-kwd"&gt;class&lt;/span&gt;&lt;br /&gt;  strict &lt;span class="pas-kwd"&gt;private&lt;/span&gt;&lt;br /&gt;    ceClass : TClass;&lt;br /&gt;    ceIndex : integer;&lt;br /&gt;    ceParent: TWinControl;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetCurrent: TControl;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; MoveNext: boolean;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;property&lt;/span&gt; Current: TControl &lt;span class="pas-kwd"&gt;read&lt;/span&gt; GetCurrent;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;  IControlEnumeratorFactory = &lt;span class="pas-kwd"&gt;interface&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt; GetEnumerator: TControlEnumerator;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ IControlEnumeratorFactory }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;On the implementation side, EnumControls creates an instance of the TControlEnumeratorFactory class, which implements the IControlEnumeratorFactory interface.&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; EnumControls(parent: TWinControl; matchClass: TClass = &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;): IControlEnumeratorFactory;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := TControlEnumeratorFactory.Create(parent, matchClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ EnumControls }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;type&lt;/span&gt;&lt;br /&gt;  TControlEnumeratorFactory = &lt;span class="pas-kwd"&gt;class&lt;/span&gt;(TInterfacedObject, IControlEnumeratorFactory)&lt;br /&gt;  strict &lt;span class="pas-kwd"&gt;private&lt;/span&gt;&lt;br /&gt;    cefClass: TClass;&lt;br /&gt;    cefParent: TWinControl;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;public&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;function&lt;/span&gt;  GetEnumerator: TControlEnumerator;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ GetEnumerator }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;TControlEnumeratorFactory just stores &lt;em&gt;parent&lt;/em&gt; and &lt;em&gt;matchClass &lt;/em&gt;parameters for later use in the GetEnumerator function.&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; TControlEnumeratorFactory.Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  cefParent := parent;&lt;br /&gt;  cefClass := matchClass;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumeratorFactory.Create }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TControlEnumeratorFactory.GetEnumerator: TControlEnumerator;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := TControlEnumerator.Create(cefParent, cefClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumeratorFactory.GetEnumerator }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;GetEnumerator creates an instance of the TControlEnumerator class, which implements the actual enumeration in a pretty standard manner. Only the MoveNext method is slightly more complicated than usual because it must optionally check the &lt;em&gt;matchClass&lt;/em&gt; parameter.&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;constructor&lt;/span&gt; TControlEnumerator.Create(parent: TWinControl; matchClass: TClass);&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;inherited&lt;/span&gt; Create;&lt;br /&gt;  ceParent := parent;&lt;br /&gt;  ceClass := matchClass;&lt;br /&gt;  ceIndex := -&lt;span class="pas-num"&gt;1&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator.Create }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TControlEnumerator.GetCurrent: TControl;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := ceParent.Controls[ceIndex];&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator.GetCurrent }&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;function&lt;/span&gt; TControlEnumerator.MoveNext: boolean;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  Result := false;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;while&lt;/span&gt; ceIndex &amp;lt; (ceParent.ControlCount - &lt;span class="pas-num"&gt;1&lt;/span&gt;) &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;    Inc(ceIndex);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;if&lt;/span&gt; (ceClass = &lt;span class="pas-kwd"&gt;nil&lt;/span&gt;) &lt;span class="pas-kwd"&gt;or&lt;/span&gt; (ceParent.Controls[ceIndex].InheritsFrom(ceClass)) &lt;span class="pas-kwd"&gt;then&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;      Result := true;&lt;br /&gt;      break; &lt;span class="pas-comment"&gt;//while&lt;/span&gt;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;//while&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;; &lt;span class="pas-comment"&gt;{ TControlEnumerator.MoveNext }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;It's instructive to see how all those parts play together when the code fragment from the beginning of this blog entry is executed. &lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; control &lt;span class="pas-kwd"&gt;in&lt;/span&gt; EnumControls(pnlAttributes, TSpeedButton) &lt;span class="pas-kwd"&gt;do&lt;/span&gt;&lt;br /&gt;  TSpeedButton(control).Enabled := false;&lt;/pre&gt;&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;EnumControls is called. It creates an instance of the IControlEnumeratorFactory. TControlEnumeratorFactory constructor stores &lt;em&gt;parent&lt;/em&gt; and &lt;em&gt;matchClass&lt;/em&gt; parameters into internal fields. &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;TControlEnumeratorFactory.GetEnumerator is called. It creates an instance of the TControlEnumerator and passes it internal copies of &lt;em&gt;parent&lt;/em&gt; and &lt;em&gt;matchClass&lt;/em&gt; parameters. &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;TControlEnumerator's MoveNext and Current are used to enumerate over the &lt;em&gt;parent&lt;/em&gt;'s child controls.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;For..in loop terminates and compiler automatically destroys the object, created by the GetEnumerator method.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;li&gt;Sometime later, the method containing this for..in loop terminates and compiler automatically frees the IControlEnumeratorFactory instance created in first step.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;p&gt;The TWinControl.Controls enumerator plus some other stuff is available at &lt;a title="GpVCL" href="http://gp.17slon.com/gp/files/gpvcl.zip"&gt;http://gp.17slon.com/gp/files/gpvcl.zip&lt;/a&gt;.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/02/twincontrolcontrols-enumerator.html' title='TWinControl.Controls Enumerator'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=3337553389785709423' title='4 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3337553389785709423'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3337553389785709423'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-6658608254664941167</id><published>2008-01-29T18:39:00.001+01:00</published><updated>2008-01-29T18:40:08.894+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #2: A Better Build Process</title><content type='html'>&lt;blockquote&gt; &lt;p align="justify"&gt;&lt;em&gt;I had to live with the problem. Until the day when it occurred to me that I could very probably find the solution to my problem on the internet. So I connected to Deja News and searched.&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- A Better Build Process, The Delphi Magazine #49, September 1999&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;A life without Google? Was that really possible? I can hardly remember those times.&lt;/p&gt; &lt;p&gt;The article was written in Delphi 4 times and deals with building Delphi applications from a command line. More specifically, it solves the problem of dynamically recreating &lt;em&gt;project&lt;/em&gt;.res file, which is something that you have to do if you want to increment build number in the version info resource during the build process. (A utility to increment build number was included, too.)&lt;/p&gt; &lt;p&gt;Also on display was a batch file that built my trusty GpProfile profiler and a "very complicated" tail utility.&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-preproc"&gt;{$APPTYPE Console}&lt;/span&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;program&lt;/span&gt; tail;&lt;br /&gt;&lt;span class="pas-kwd"&gt;uses&lt;/span&gt;&lt;br /&gt;  SysUtils, Classes;&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  str  : TStringList;&lt;br /&gt;  i    : integer;&lt;br /&gt;  first: integer;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  str := TStringList.Create;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;try&lt;/span&gt;&lt;br /&gt;    str.LoadFromFile(ParamStr(&lt;span class="pas-num"&gt;1&lt;/span&gt;));&lt;br /&gt;    first := str.Count-StrToIntDef(ParamStr(&lt;span class="pas-num"&gt;2&lt;/span&gt;),&lt;span class="pas-num"&gt;20&lt;/span&gt;);&lt;br /&gt;    &lt;span class="pas-kwd"&gt;if&lt;/span&gt; first &amp;lt; &lt;span class="pas-num"&gt;0&lt;/span&gt; &lt;span class="pas-kwd"&gt;then&lt;/span&gt; first := &lt;span class="pas-num"&gt;0&lt;/span&gt;;&lt;br /&gt;    &lt;span class="pas-kwd"&gt;for&lt;/span&gt; i := first &lt;span class="pas-kwd"&gt;to&lt;/span&gt; str.Count-&lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt; Writeln(str[i]);&lt;br /&gt;  &lt;span class="pas-kwd"&gt;finally&lt;/span&gt;&lt;br /&gt;    str.Free;&lt;br /&gt;  &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;.&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Not much has changed in the Delphi world - I would code it almost identical today.&lt;/p&gt;&lt;br /&gt;&lt;p&gt;Links: &lt;a title="TDM 49: A Better Build Process [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm49-gp.pdf" target="_blank"&gt;article&lt;/a&gt; (PDF, 70 KB), &lt;a title="TDM 49: A Better Build Process [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm49-gp.zip" target="_blank"&gt;source code&lt;/a&gt; (ZIP, 185 KB)&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/01/tdm-rerun-2-better-build-process.html' title='TDM Rerun #2: A Better Build Process'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=6658608254664941167' title='2 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6658608254664941167'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6658608254664941167'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-202725796893292991</id><published>2008-01-28T13:25:00.001+01:00</published><updated>2008-01-28T13:25:16.249+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>TDM Rerun #1: Safer Sockets</title><content type='html'>&lt;blockquote&gt;&lt;font size="2"&gt; &lt;p align="justify"&gt;&lt;em&gt;Instead of fixing the ScktComp unit it is probably better to create a derived class, override the Read method, paste code from ScktComp and make this small modification. Choose your way but beware: CancelIO is not available in Windows 95, only in 98 and NT 4.&lt;/em&gt;&lt;/p&gt; &lt;p align="right"&gt;&lt;em&gt;- Safer Sockets, The Delphi Magazine #44, April 1999&lt;/em&gt;&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;Those were the times ... Most of computers were still running Windows 95, the actual Delphi version was 4 and we were using TWinSocketStream for data transfer over Windows sockets. Oh, happy days. Not!  &lt;p&gt;I won't comment this article much. It describes how to fix a problem with an obsolete component that should never be used in modern software. I still remember how happy I was switching to &lt;a title="ICS" href="http://www.overbyte.be/frame_index.html" target="_blank"&gt;ICS&lt;/a&gt; ... Still, I promised all my articles and I have to start with this one.  &lt;p&gt;This article represents another important milestone in my 'journalist' career - it was my first article written in English language and a start of very happy long-term relationship with &lt;a title="The Delphi Magazine" href="http://www.thedelphimagazine.com" target="_blank"&gt;The Delphi Magazine&lt;/a&gt;.  &lt;p&gt;Links: &lt;a title="TDM 44: Safer Sockets [article]" href="http://www.17slon.com/blogs/gabr/TDM/tdm44-gp.pdf" target="_blank"&gt;article&lt;/a&gt; (PDF, 49KB), &lt;a title="TDM 44: Safer Sockets [source code]" href="http://www.17slon.com/blogs/gabr/TDM/tdm44-gp.zip" target="_blank"&gt;source code&lt;/a&gt; (ZIP, 1 KB)&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/01/tdm-rerun-1-safer-sockets.html' title='TDM Rerun #1: Safer Sockets'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=202725796893292991' title='0 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/202725796893292991'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/202725796893292991'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-976795972673452063</id><published>2008-01-24T19:01:00.001+01:00</published><updated>2008-01-24T21:56:16.298+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>Make a better Delphi</title><content type='html'>&lt;p&gt;Delphi is a good language and Delphi IDE is a good development environment and together they make a good pair, but admit it - Delphi language is missing some modern language constructs (parameterized types, for example) and Delphi IDE can be rough at the edges. We can't do much about the former, but Delphi IDE can be greatly enhanced by installing third party add-ons (and great thanks to Borland/CodeGear for enabling us to do so).&lt;/p&gt; &lt;p&gt;Recently I've noticed that I can't really function well on a bare-bones IDE. I'm heavily dependent on three IDE extensions - &lt;a title="GExperts" href="http://www.gexperts.org" target="_blank"&gt;GExperts&lt;/a&gt;, &lt;a title="DDevExtensions" href="http://andy.jgknet.de/dspeedup/index.php?page=DDevExtensions" target="_blank"&gt;DDevExtensions&lt;/a&gt;, and &lt;a title="ModelMaker Code Explorer" href="http://www.modelmakertools.com/code-explorer/index.html" target="_blank"&gt;ModelMaker Code Explorer&lt;/a&gt;. I decided to dedicate some blog space to them and describe what they do and why I like them.&lt;/p&gt; &lt;p&gt;It would take too much space to describe every aspect of those three add-ons, so I decided to focus on a navigation in a broad sense - moving inside unit, between units, between code and design view etc. Even more, I won't cover every navigational helper in those three, I'll just focus on the features I'm using regularly.&lt;/p&gt; &lt;h2&gt;Delphi IDE&lt;/h2&gt; &lt;p&gt;Delphi IDE has some neat navigation tools built in. First, there are &lt;strong&gt;markers&lt;/strong&gt;. You can drop ten markers by pressing &amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&lt;em&gt;number&lt;/em&gt;, where &lt;em&gt;number&lt;/em&gt; is a numeric key from 0 to 9 (you don't have to use numeric keyboard keys, standard keyboard numbers will work just fine). By pressing &amp;lt;Ctrl&amp;gt;&lt;em&gt;number&lt;/em&gt; you'll be transferred to the line containing the marker. To delete a marker you just press &amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&lt;em&gt;number&lt;/em&gt; while the caret is positioned in the line containing the marker.&lt;/p&gt; &lt;p&gt;Another neat feature of the Delphi IDE is the ability to &lt;strong&gt;jump from method implementation to its declaration &lt;/strong&gt;and back. The shortcut for this operation is &amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&amp;lt;UpArrow&amp;gt; or &amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&amp;lt;DownArrow&amp;gt; (both work the same).&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="134" alt="Delphi: find references" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb.png" width="240" align="right" border="0"&gt;&lt;/a&gt;When you want to find where some identifier is used (in current unit and in the rest of the project), you can use &lt;strong&gt;find references&lt;/strong&gt; (&amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&amp;lt;Enter&amp;gt;) command. I'm mentioning it here because you can also use it to jump to the place where the identifier is used.&lt;/p&gt; &lt;p&gt;I'm also using the &lt;strong&gt;unit list&lt;/strong&gt; (&amp;lt;Ctrl&amp;gt;&amp;lt;F12&amp;gt;) and &lt;strong&gt;form list&lt;/strong&gt; (&amp;lt;Shift&amp;gt;&amp;lt;F12&amp;gt;) regularly. First shows the list of units in the project and second the list of forms. Just double-click the unit/form or select it and press &amp;lt;Enter&amp;gt; and it will open in the IDE.&lt;/p&gt; &lt;h2&gt;GExperts&lt;/h2&gt; &lt;p&gt;GExperts is a set of freeware experts that enhance Delphi IDE in various ways. Only small subset is connected to the navigation.&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="205" alt="GExperts: open file" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb_3.png" width="240" align="right" border="0"&gt;&lt;/a&gt;One of the most useful helpers is &lt;strong&gt;open file&lt;/strong&gt; expert. Press the key (I have it configured to &amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&amp;lt;Alt&amp;gt;O) and up pops a list of all files that are accessible from the current project. You can select the unit and press &amp;lt;Enter&amp;gt; to open it (alternatively you can double-click that unit) but before that you can - and that's even more important - start typing in the filter box and the expert will remove all non-matching units.&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_3.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="174" alt="GExperts: procedure list" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb_4.png" width="240" align="left" border="0"&gt;&lt;/a&gt; &lt;/p&gt; &lt;p&gt;Another useful tool is &lt;strong&gt;procedure list&lt;/strong&gt; expert. As the name suggests, it presents a list of methods in the current unit, which you can, of course, filter by typing. Double-click or &amp;lt;Enter&amp;gt; will beam you to the selected method.&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_4.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="240" alt="GExperts: form designer menu" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb_5.png" width="149" align="right" border="0"&gt;&lt;/a&gt;GExperts also offers bunch of helpful editor experts. &lt;strong&gt;Locate matching delimiter&lt;/strong&gt; (&amp;lt;Ctrl&amp;gt;&amp;lt;Alt&amp;gt;&amp;lt;LeftArrow&amp;gt;/&amp;lt;RightArrow&amp;gt;) jumps to a matching delimiter - from left to right bracket, from &lt;em&gt;begin&lt;/em&gt; to &lt;em&gt;end&lt;/em&gt;, from &lt;em&gt;implementation&lt;/em&gt; to &lt;em&gt;interface&lt;/em&gt; and so on. &lt;strong&gt;Locate previous/next identifier&lt;/strong&gt;&lt;em&gt; &lt;/em&gt;(&amp;lt;Ctrl&amp;gt;&amp;lt;Alt&amp;gt;&amp;lt;UpArrow&amp;gt;/&amp;lt;DownArrow&amp;gt;) locates previous/next occurrence of the identifier under the caret.&lt;/p&gt; &lt;p&gt; The last navigational help in the GExperts is hidden in the form designer pop-up menu. It is extended with the &lt;strong&gt;find component reference&lt;/strong&gt; command. Right-click on a component or control and select this option and GExperts will bring up the editor, focused on the part where this component/control is declared. Or used for the first time in the code. Sometimes former and sometimes latter and I can never guess which one it will be.&lt;/p&gt; &lt;h2&gt;DDevExtensions&lt;/h2&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_5.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="131" alt="DDevExtensions: unit list" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb_6.png" width="240" align="right" border="0"&gt;&lt;/a&gt;DDevExtensions is developed by Andreas Hausladen, author of well-known &lt;a title="DelphiSpeedUp" href="http://andy.jgknet.de/dspeedup/index.php" target="_blank"&gt;DelphiSpeedUp&lt;/a&gt;. I'm mostly using it because it reconfigures the &amp;lt;Tab&amp;gt; key to indent a block of code and &amp;lt;Shift&amp;gt;&amp;lt;Tab&amp;gt; to unindent it, but that's not a navigational extension so you can pretend you didn't hear that from me. It also implements &lt;strong&gt;smart home &lt;/strong&gt;functionality. First time you press the &amp;lt;Home&amp;gt; key, caret will jump to the first column in the row. If you press the &amp;lt;Home&amp;gt; button again, caret will jump to the first non-blank character in that line. Useful.&lt;/p&gt; &lt;p&gt;DDevExtensions also redefines &lt;strong&gt;unit list&lt;/strong&gt; and &lt;strong&gt;form list&lt;/strong&gt; keyboard shortcuts so that display a list that can be filtered - similar to the GExeperts' &lt;strong&gt;open file&lt;/strong&gt; expert.&lt;/p&gt; &lt;h2&gt;ModelMaker Code Explorer&lt;/h2&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_6.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="240" alt="MMX: Explorer" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb_7.png" width="90" align="right" border="0"&gt;&lt;/a&gt;ModelMaker Code Explorer (or MMX as it is usually called between loving users) is one of the largest Delphi IDE add-ons on the market. Its most important functionality is refactoring - creating classes, fields, properties, and methods becomes lightingly fast with MMX - but it also offers some navigational functionality. MMX is not free like GExperts and DDevExtensions but I can assure you that it's worth every cent.&lt;/p&gt; &lt;p&gt;An important part of MMX interface is the &lt;strong&gt;Explorer window&lt;/strong&gt;. It lists every class and method in the current unit, has movements synchronized with the editor and is filterable.&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_7.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="151" alt="MMX: indexer" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb_8.png" width="240" align="left" border="0"&gt;&lt;/a&gt;Similar to Delphi's &lt;strong&gt;find reference&lt;/strong&gt; is &lt;strong&gt;find in module&lt;/strong&gt; command (&amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&amp;lt;Alt&amp;gt;F). It locates all occurrences of&amp;nbsp; the identifier in the current module and uses tabbed interface to store old searches. Great advantage of MMX over Delphi is that it works in older IDEs and that it works on a non-compilable code.&lt;/p&gt; &lt;p&gt;MMX also offers &lt;strong&gt;extended up/down navigation&lt;/strong&gt;. Delphi's shortcuts &amp;lt;Ctrl&amp;gt;&amp;lt;Shift&amp;gt;&amp;lt;UpArrow&amp;gt;/&amp;lt;DownArrow&amp;gt; are redefined to jump (in addition to the default behaviour) between propery declaration, its getter and setter, between constructors and destructors and between overloaded methods. Highly useful.&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_8.png" target="_blank"&gt;&lt;img style="border-right: 0px; border-top: 0px; border-left: 0px; border-bottom: 0px" height="108" alt="MMX: global history" src="http://17slon.com/blogs/gabr/files/MakeabetterDelphi_11E76/image_thumb_9.png" width="240" align="right" border="0"&gt;&lt;/a&gt;The last enhancement on my today's list is &lt;strong&gt;global history&lt;/strong&gt;. MMX will automatically remember any method where you spent more than a few seconds. When you press the keyboard shortcut (I have it set to &amp;lt;Ctrl&amp;gt;` - the latter being the key left to the '0' key), MMX displays pop-up menu with most recently edited methods. Click one and you're instantly transferred there.&lt;/p&gt; &lt;h2&gt;Other enhancements&lt;/h2&gt; &lt;p&gt;That's all by me, I've exhausted my knowledge. Here's where you, dear reader, can help. If you know a trick that I didn't mention or a helpful expert that I don't have installed, do tell me about it in the comments. You'll help me and other Delphi programmers.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/01/make-better-delphi.html' title='Make a better Delphi'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=976795972673452063' title='14 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/976795972673452063'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/976795972673452063'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-6152095051295124630</id><published>2008-01-23T19:59:00.001+01:00</published><updated>2008-01-28T13:40:45.800+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='The Delphi Magazine'/><title type='text'>Reposting TDM Articles</title><content type='html'>&lt;p&gt;Like some other authors (&lt;a title="Hallvard Vassbotn: Republishing my old The Delphi Magazine articles" href="http://hallvards.blogspot.com/2008/01/republishing-my-old-delphi-magazine.html" target="_blank"&gt;Hallvard Vassbotn&lt;/a&gt;, &lt;a title="Dr. Bob: Republishing Articles from The Delphi Magazine" href="http://www.bobswart.nl/Weblog/Blog.aspx?RootId=5:1822" target="_blank"&gt;Bob Swart&lt;/a&gt;) I'll be republishing articles I wrote for The Delphi Magazine. [Chris, thanks for the permission!] &lt;/p&gt; &lt;p&gt;Unlike Dr. Bob and Hallvard I won't have problems with choosing from my TDM articles - I published much less than them so I'll simply republish everything :) &lt;/p&gt; &lt;p&gt;In the next months you can expect online versions of my articles:&lt;/p&gt; &lt;ul&gt; &lt;li&gt;&lt;strong&gt;Safer Sockets&lt;/strong&gt; [TDM #44]&lt;br&gt;&lt;em&gt;Describes an issue with TWinSocketStream and a presents a workaround.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;A Better Build Process&lt;/strong&gt; [TDM #49]&lt;br&gt;&lt;em&gt;How to build Delphi programs from a command line; introduces some helper tools.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Time Is The Simplest Thing&lt;/strong&gt; [TDM #55]&lt;br&gt;&lt;em&gt;The art of timezones. Introduces &lt;a title="GpTimezone" href="http://gp.17slon.com/gp/gptimezone.htm" target="_blank"&gt;GpTimezone&lt;/a&gt; unit, which is still alive and regularly updated.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Let's Cooperate&lt;/strong&gt; [TDM #68]&lt;br&gt;&lt;em&gt;My first take on steampunk process synchronisation - not via OS primitives but by using file system.&lt;/em&gt; Introducing GpFileSync unit, which somehow never made it to &lt;a title="Gp's home" href="http://gp.17slon.com/" target="_blank"&gt;my web site&lt;/a&gt;.  &lt;li&gt;&lt;strong&gt;SvCom 4.0&lt;/strong&gt; [TDM #69] &lt;br&gt;&lt;em&gt;A review of &lt;a title="SvCom" href="http://www.aldyn-software.com/svcom.html" target="_blank"&gt;SvCom&lt;/a&gt; service development package. Still alive and well, currently at version 7.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;GExperts 1.0&lt;/strong&gt; [TDM #72] &lt;br&gt;&lt;em&gt;A review of &lt;a title="GExperts" href="http://www.gexperts.org/" target="_blank"&gt;Delphi experts&lt;/a&gt; we all love.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;File Sharing On Linux&lt;/strong&gt; [TDM #84] &lt;br&gt;&lt;em&gt;My only article on Kylix continued the &lt;/em&gt;Let's Cooperate&lt;em&gt; theme.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;A Synchronisation Toolkit&lt;/strong&gt; [TDM #86] &lt;br&gt;&lt;em&gt;Simple multi-process synchronisation and communication primitives. Introduces the &lt;a title="GpSync" href="http://gp.17slon.com/gp/gpsync.htm" target="_blank"&gt;GpSync&lt;/a&gt; unit, which is sill alive and regularly updated.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;My Data Is Your Data&lt;/strong&gt; [TDM #88] &lt;br&gt;&lt;em&gt;Describes an easy to use wrapper around Windows' shared memory. Introduces &lt;a title="GpSharedMemory" href="http://gp.17slon.com/gp/gpsharedmemory.htm" target="_blank"&gt;GpSharedMemory&lt;/a&gt; unit.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Synchronisation Toolkit Revisited&lt;/strong&gt; [TDM #91] &lt;br&gt;&lt;em&gt;An upgrade of the &lt;a title="GpSync" href="http://gp.17slon.com/gp/gpsync.htm" target="_blank"&gt;GpSync&lt;/a&gt; unit, introducing message queue.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Shared Pools&lt;/strong&gt; [TDM #95] &lt;br&gt;&lt;em&gt;Shared memory pools. I never developed this concept any further.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Shared Events Part 1: Rationale, Design, Implementation&lt;/strong&gt; [TDM #97] &lt;br&gt;&lt;em&gt;Cross-process event dispatch using shared memory. Introduces &lt;a title="GpSharedEvents" href="http://gp.17slon.com/gp/gpsharedevents.htm" target="_blank"&gt;GpSharedEvents&lt;/a&gt; unit.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Shared Events Part 2: Redesign&lt;/strong&gt; [TDM #102] &lt;br&gt;&lt;em&gt;Implementation details and a redesign.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;A Portable XML&lt;/strong&gt; [TDM #105] &lt;br&gt;&lt;em&gt;A review of the &lt;a title="OmniXML" href="http://www.omnixml.com/" target="_blank"&gt;OmniXML&lt;/a&gt; library. Still alive and regularly updated.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Many Faces Of An Application&lt;/strong&gt; [TDM #107] &lt;br&gt;&lt;em&gt;How to write an application that can be a service, Windows GUI and system tray icon monitor.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Thread Pooling, The Practical Way&lt;/strong&gt; [TDM #112] &lt;br&gt;&lt;em&gt;Describes Windows thread pool, various problems with it and some workarounds.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;Put It In A Tree&lt;/strong&gt; [TDM #118] &lt;br&gt;&lt;em&gt;Discusses various approaches to creating a tree structure from a list of mail messages.&lt;/em&gt;  &lt;li&gt;&lt;strong&gt;To Manage Memory&lt;/strong&gt; [TDM #126]&lt;br&gt;&lt;em&gt;An introduction to memory management algorithms and structures plus a short overview of the (at that time very young) FastMM memory manager.&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt; &lt;p&gt;And that's all folks. I'll probably attack this list in chronological manner. If you have any preferences I may make an exception and change priorities so don't be afraid to ask for article you want to read first.&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/01/reposting-tdm-articles.html' title='Reposting TDM Articles'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=6152095051295124630' title='3 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6152095051295124630'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/6152095051295124630'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-4123926426063847169</id><published>2008-01-14T22:10:00.001+01:00</published><updated>2008-01-14T22:13:06.836+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><title type='text'>Disappearing Welcome Page</title><content type='html'>&lt;p&gt;One of the most annoying bugs in Delphi 2007 must be the disappearing Welcome Page. Sometimes, when you do Close All, IDE decides that Welcome Page contains highly sensitive material that is not intended for your eyes, and deletes the contents. Like this:&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/DisappearingWelcomePage_134CB/wsproblem.png" target="_blank" atomicselection="true"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="480" alt="Empty Welcome Page" src="http://17slon.com/blogs/gabr/files/DisappearingWelcomePage_134CB/wsproblem_thumb.png" width="629" border="0"&gt;&lt;/a&gt;&amp;nbsp;&lt;/p&gt; &lt;p&gt;By trial and error (and much luck) I have found a way to restore it.&lt;/p&gt; &lt;p&gt;Step 1: Click into the edit box next to the 'house' icon. Text (bds:/default.htm) will be selected.&lt;/p&gt; &lt;p&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="48" alt="Step 1" src="http://17slon.com/blogs/gabr/files/DisappearingWelcomePage_134CB/wsstep1.png" width="262" border="0"&gt; &lt;/p&gt; &lt;p&gt;Step 2: Press &amp;lt;Backspace&amp;gt; to delete the text.&lt;/p&gt; &lt;p&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="48" alt="Step 2" src="http://17slon.com/blogs/gabr/files/DisappearingWelcomePage_134CB/wsstep2.png" width="262" border="0"&gt; &lt;/p&gt; &lt;p&gt;Step 3: Press &amp;lt;Enter&amp;gt;. Nothing will happen, but the trick won't work without this.&lt;/p&gt; &lt;p&gt;Step 4: Click the 'house' icon. Welcome Page should reload.&lt;/p&gt; &lt;p&gt;&lt;a href="http://17slon.com/blogs/gabr/files/DisappearingWelcomePage_134CB/wsstep3.png" target="_blank" atomicselection="true"&gt;&lt;img style="border-top-width: 0px; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="480" alt="Working Welcome Page" src="http://17slon.com/blogs/gabr/files/DisappearingWelcomePage_134CB/wsstep3_thumb.png" width="629" border="0"&gt;&lt;/a&gt;&lt;/p&gt; &lt;p&gt;If that&amp;nbsp;doesn't work, you can always close&amp;nbsp;the welcome page and reopen it via View, Welcome Page menu. Is is, however, faster to try click-backspace-enter-click trick first.&lt;/p&gt;</content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/01/disappearing-welcome-page.html' title='Disappearing Welcome Page'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=4123926426063847169' title='12 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/4123926426063847169'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/4123926426063847169'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-1513516955960127851</id><published>2008-01-12T21:37:00.001+01:00</published><updated>2008-03-20T17:21:26.291+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='enumerators'/><title type='text'>TDataset Enumerator</title><content type='html'>&lt;p&gt;You already know that I love &lt;a title="Fun With Enumerators" href="http://17slon.com/blogs/gabr/2007/03/fun-with-enumerators.html" target="_blank"&gt;all things enumeratorish&lt;/a&gt;. Just recently I found a great example that combines three kinds of Delphi magic to allow the programmer to write very simple TDataset enumeration loops. &lt;s&gt;[And for the sake of my life I can't remember where I first saw the link to this code. Apologies to the original author.]&lt;/s&gt; [Updated 2008-01-14: Jim McKeeth found my source. Apparently &lt;a title="Random Thoughts on the Passing Scene #48" href="http://blogs.codegear.com/nickhodges/2008/01/02/39000" target="_blank"&gt;Nick Hodges&lt;/a&gt; was the first to blog about this enumerator. Thanks, Nick!]&lt;/p&gt; &lt;p&gt;This TDataset enumerator was written by Uwe Raabe and is freely available in the CodeGear's Code Central as item &lt;a title="for-in-loop with TDataSet" href="http://cc.codegear.com/Item/25386" target="_blank"&gt;#25386&lt;/a&gt;.&lt;/p&gt; &lt;p&gt;The three trick he uses are:&lt;/p&gt; &lt;ol&gt; &lt;li&gt;Data helper is used to push enumerator support into TDataset.  &lt;li&gt;Standard enumerator support to enumerate over dataset and return special descendant of the Variant type for each record.  &lt;li&gt;Custom variant type to automagically access record fields by by adding .RecordFieldName to the variant variable. [I must admit I didn't even know this was possible to do in Delphi.]&lt;/li&gt;&lt;/ol&gt; &lt;p&gt;All together those tricks allow you to write very simple TDataset iteration loops (code taken from the example in the DN #25386):&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt;  Employee: Variant;&lt;br /&gt;  S: &lt;span class="pas-kwd"&gt;string&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt;&lt;span class="pas-kwd"&gt;for&lt;/span&gt; Employee &lt;span class="pas-kwd"&gt;in&lt;/span&gt; QuEmployee &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;  S := Trim(Format(&lt;span class="pas-str"&gt;'%s %s'&lt;/span&gt;, [Employee.First_Name, Employee.Last_Name]));&lt;br /&gt;  &lt;span class="pas-kwd"&gt;if&lt;/span&gt; Employee.Hire_Date &amp;lt; EncodeDate(&lt;span class="pas-num"&gt;1991&lt;/span&gt;, &lt;span class="pas-num"&gt;1&lt;/span&gt;, &lt;span class="pas-num"&gt;1&lt;/span&gt;) &lt;span class="pas-kwd"&gt;then&lt;/span&gt;&lt;br /&gt;    S := &lt;span class="pas-str"&gt;'*'&lt;/span&gt; + S;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;&lt;br /&gt;&lt;p&gt;Excellent job!&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/01/tdataset-enumerator.html' title='TDataset Enumerator'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=1513516955960127851' title='5 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/1513516955960127851'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/1513516955960127851'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-3108258341482351660</id><published>2008-01-10T00:05:00.001+01:00</published><updated>2008-01-10T00:05:13.901+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='Win32'/><category scheme='http://www.blogger.com/atom/ns#' term='Unicode'/><title type='text'>Unicode Delphi</title><content type='html'>&lt;p&gt;The Oracle at Delphi (aka Allen Bauer) has posted some information on &lt;a href="http://blogs.codegear.com/abauer/2008/01/09/38845" target="_blank"&gt;Unicode support in the next Delphi&lt;/a&gt; (codenamed Tiburon). &lt;/p&gt; &lt;p&gt;Firstly, let me say that I really appreciate that we'll finally get VCL Unicode and reference-counted wide string support in Delphi. I really really really appreciate that.&lt;/p&gt; &lt;p&gt;Secondly, I must say, and I must say this very loud, that &lt;strong&gt;I DON'T LIKE HOW IT WILL BE IMPLEMENTED!&lt;/strong&gt; (And I apologize for yelling in public.)&lt;/p&gt; &lt;p&gt;Allen wrote&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;This new data type will be mapped to string which is currently an AnsiString underlying type depending on how it is declared.&amp;nbsp; Any string declared using the [] operator (string[64]) will still be a ShortString which is a length prefixed AnsiChar array.&amp;nbsp; The UnicodeString payload will match that of the OS, which is UTF16.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;And&lt;/p&gt; &lt;blockquote&gt; &lt;p&gt;If your code must still operate on Ansi string data, then simply be more explicit about it.&amp;nbsp; Change the declaration to AnsiString, AnsiChar, and PAnsiChar.&amp;nbsp; This can be done today and will recompile unchanged in Tiburon.&lt;/p&gt;&lt;/blockquote&gt; &lt;p&gt;I wrote my response in comments to the Allen's post and I'll restate it here.&lt;/p&gt; &lt;p&gt;This is very very bad. This will not work for our company and I presume for many of us working with legacy applications with millions of source lines. We have applications using 3rd party components and libraries (all with source code, thankfully). We have applications using libraries that can be compiled for Win32 and for DOS (with BP7 and plenty of IFDEFs, of course). And we have no illusions that they will continue to work if &lt;em&gt;string&lt;/em&gt; suddenly becomes an UTF-16-holding type.&lt;/p&gt; &lt;p&gt;We would like to move to Unicode Delphi, but we would like to do it gradually, when starting work on new applications. That way, we can test all units, components and libraries as they are introduced in the new program and we can fix all problems that will occur.&lt;/p&gt; &lt;p&gt;So please, CodeGear, give as an 'Unicode application' option. It should be disabled for old applications and enabled for new ones. That would be a much better plan.&lt;/p&gt; &lt;p&gt;&lt;em&gt;[If you agree with me, make sure to post a comment in Allen's blog stating your opinion. If you don't agree with me, make sure to post a comment in Allen's blog stating your opinion. He needs as much feedback as possible to make decitions that will be good for us.]&lt;/em&gt;&lt;/p&gt;  </content><link rel='alternate' type='text/html' href='http://17slon.com/blogs/gabr/2008/01/unicode-delphi.html' title='Unicode Delphi'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=29331675&amp;postID=3108258341482351660' title='5 Comments'/><link rel='replies' type='application/atom+xml' href='http://17slon.com/blogs/gabr/atom.xml' title='Post Comments'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3108258341482351660'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/29331675/posts/default/3108258341482351660'/><author><name>gabr</name></author></entry><entry><id>tag:blogger.com,1999:blog-29331675.post-6502173771328882811</id><published>2007-11-13T11:42:00.000+01:00</published><updated>2007-11-13T22:30:10.933+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='Delphi'/><category scheme='http://www.blogger.com/atom/ns#' term='Win32'/><category scheme='http://www.blogger.com/atom/ns#' term='source code'/><category scheme='http://www.blogger.com/atom/ns#' term='programming'/><category scheme='http://www.blogger.com/atom/ns#' term='utilities'/><title type='text'>Calculating accurate 'Now'</title><content type='html'>&lt;p&gt;You may be aware of the fact that Windows functions that return current time with millisecond &lt;a title="Wikipedia:Precision" href="http://en.wikipedia.org/wiki/Precision" target="_blank"&gt;precision&lt;/a&gt; are not &lt;a title="Wikipedia:Accuracy" href="http://en.wikipedia.org/wiki/Accuracy" target="_blank"&gt;accurate&lt;/a&gt; to a millisecond. Then, again, you may be not. In any case, I'll show you how to calculate accurate time. Well, maybe not really accurate, but at least it will be miles better than Windows' functions.&lt;/p&gt; &lt;p&gt;Let's start with the system time. If you take a look at the &lt;a title="MSDN:GetSystemTime" href="http://msdn2.microsoft.com/en-us/library/ms724390.aspx" target="_blank"&gt;GetSystemTime&lt;/a&gt; function, you'll see that it returns a structure containing years, months, hours ... and so on down to milliseconds. The problem is that it is not incremented in millisecond steps. If you fetch the time, wait for two milliseconds and fetch the time again, chances are that both structures would be completely the same. Raymond Chen states in &lt;a title="Precision is not the same as accuracy" href="http://blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx" target="_blank"&gt;Precision is not the same as accuracy&lt;/a&gt; that the accuracy of typical Windows clock is somewhere from 10 to 55 ms. On most computers I've tested, system time accuracy is approximately 15 ms.&lt;/p&gt; &lt;p&gt;To demonstrate this, I've written this 'complicated' code fragment:&lt;/p&gt;&lt;pre class="pas-source"&gt;&lt;span class="pas-kwd"&gt;procedure&lt;/span&gt; TForm6.btnGetSystemTimeClick(Sender: TObject);&lt;br /&gt;&lt;span class="pas-kwd"&gt;var&lt;/span&gt;&lt;br /&gt; i: integer;&lt;br /&gt; st: TSystemTime;&lt;br /&gt;&lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt; &lt;span class="pas-kwd"&gt;for&lt;/span&gt; i := &lt;span class="pas-num"&gt;1&lt;/span&gt; &lt;span class="pas-kwd"&gt;to&lt;/span&gt; &lt;span class="pas-num"&gt;15&lt;/span&gt; &lt;span class="pas-kwd"&gt;do&lt;/span&gt; &lt;span class="pas-kwd"&gt;begin&lt;/span&gt;&lt;br /&gt;   Windows.GetSystemTime(st);&lt;br /&gt;   outLog.Lines.Add(IntToStr(st.wMilliseconds));&lt;br /&gt;   Sleep(&lt;span class="pas-num"&gt;1&lt;/span&gt;);&lt;br /&gt; &lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;br /&gt;&lt;span class="pas-kwd"&gt;end&lt;/span&gt;;&lt;/pre&gt;&lt;br /