Thoughts from Dan Miser RSS 2.0
# Wednesday, 22 April 2009
Are you looking for a strong developer/architect with a serious passion for all things technical and a unique blend of experience? If so, feel free to email me. My main focus over the past couple of years has been on things ALT.NET-ish (e.g. ASP.NET MVC, NHibernate, Spring.NET, etc.) while I have delved into a variety of other technologies as well (e.g. Mindscape LightSpeed, LINQ, Dynamic Data, Delphi, etc.). If you know of an opening - contract or full-time - please keep me in mind. I'd prefer to remain in the Milwaukee area, but occasional travel wouldn't be the end of the world.

Until the next technical post here, take care and thanks in advance.

Wednesday, 22 April 2009 19:29:54 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
.NET | ALT.NET | Delphi
# Saturday, 17 February 2007

Sorry for the changing plans, but I just received word that CodeGear won't be able to make it out here for the 2/27 meeting so the DUG meeting will be cancelled. They may be able to reschedule in April, so that will give us some more lead time, but for now, things are off. Thanks for your understanding.

Saturday, 17 February 2007 16:08:54 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
# Tuesday, 06 February 2007
I just received an email from Anders Ohlsson, and he wants to schedule Milwaukee as a stop on a 10-city tour. I take that as a positive sign that Milwaukee matters enough to warrant a stop. Let's keep that perception alive by coming out and attending this meeting! The very tentative details right now are:
  • What: Delphi User Group Meeting
  • When: Tuesday, 2/27/07 from 7pm-9pm.
  • Where: Medical College of Wisconsin
  • Why: Because you want to hear about updates on CodeGear, the roadmap and Delphi itself. CodeGear would also provide door prizes and a rebate offer.
  • How: RSVP by emailing me.

If you have any other thoughts, concerns, questions, etc., send me an email. I will post final details once we have them.

Tuesday, 06 February 2007 19:29:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Monday, 18 December 2006
It looks like lots of changes are going on here. First off, I am now working in C# full-time since I took a job with SpiderLogic. I think this is going to be a great opportunity for me to expand and grow again. I doubt I will ever be out of Delphi completely, but my new focus is definitely going to be on .NET. As such, I contacted DelphiFeeds to tell them to use just my Delphi category. Someone contacted me after a VS.NET post and mentioned that it seemed strange to see that on the Delphi feed, and I agreed. I don't want to clutter a very good Delphi resource up with blog posts about a competing IDE and/or language. However, if you like what you read here, I would encourage you to subscribe to my main feed, too.

The other change is on my blog. With prompting from Steve Trefethen, I made the switch to dasBlog. I had set dasBlog up for our internal blog at my last place of employment, and it worked great. I got tired of the lack of options that .Text provided, so I felt I had to do something. dasBlog provides a couple of features that I really wanted, like searching and better spam prevention. In addition, it's actively being developed.

Converting was fairly painless. I read a post by Scott Hanselman about migrating to dasBlog, and from there I found some code to help automate the conversion. There are several dead links out there, so finding this one was a definite time saver. I changed the code slightly to get categories out of .Text and into dasBlog. I changed the main SQL statement to the one below, and then just added a line of code to add that category information to the dasBlog conversion code. This works for me because I didn't use multiple categories for a post.


SELECT 
  blog_Content.ID, blog_Content.Title, blog_Content.DateAdded, blog_Content.SourceUrl, blog_Content.PostType, 
  blog_Content.Author, blog_Content.Email, blog_Content.SourceName, blog_Content.BlogID, blog_Content.Description, 
  blog_Content.DateUpdated, blog_Content.TitleUrl, blog_Content.Text, blog_Content.ParentID, blog_Content.FeedBackCount, 
  blog_Content.PostConfig, blog_Content.EntryName, blog_LinkCategories.Title AS CategoryTitle
FROM blog_Content 
  LEFT OUTER JOIN blog_Links ON blog_Links.PostID = blog_Content.ID 
  LEFT OUTER JOIN blog_LinkCategories ON blog_Links.CategoryID = blog_LinkCategories.CategoryID

I was worried about old URLs that were used from my .Text blog, but the later versions of dasBlog already do URL rewriting to make that transition absolutely seamless. I also use w.bloggar for writing my entires, so I had to change some settings to get that to work with dasBlog. Lastly, I updated my RSS feed to use FeedBurner.

All in all, I'm quite happy with how things turned out. If you see any problems, please feel free to let me know.

Monday, 18 December 2006 15:42:22 (GMT Standard Time, UTC+00:00)  #    Comments [2] -
Delphi
# Sunday, 03 December 2006
CodeRush and Refactor! are 2 very powerful VS.NET add-ins that let you program faster. They are made by Developer Express, which is a software company that I have had nothing but incredible dealings with over the years in the Delphi area. As a matter of fact, I used CodeRush for about a year on Delphi way back in the day (maybe 1998?). Even though Mark Miller is the lead man responsible for these products, I'll still use it. (Just kidding, Mark!). In all seriousness, Mark is one of those unique developers who has the passion, vision and technical chops to back it up. I've also known Mark for years from the BorCon speaker circuit. I would highly recommend paying attention to whatever he does, because he does things right.

CodeRush is integrated into the IDE, "bringing you new ways to look at code, new ways to generate code, new ways to navigate through code, and new ways to create your own extensions to your development environment." It is very configurable. There is also a nice training window that will help remind you of what keystrokes to press to generate code. Once you have the keystrokes mastered, you can turn it off.

Refactor! Pro is just unbelievable. It provides over 50 refactorings built-in that work with C#, VB, and C++. At the heart of this product is the mindset that you should never lose workflow with modal dialogs. The result is a thing of beauty, with some of the best eye candy out there. Beauty is fine, but I really want a tool that doesn't miss refactorings, and this tool has proven incredibly reliable on that side as well. For a quick overview of Refactor, check out this 3 Minute Overview on MSDN.

Sunday, 03 December 2006 23:38:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
# Tuesday, 14 November 2006
I've been doing a lot more .NET lately, and while doing that, I've been evaluating a lot of different architectures and patterns. During my career, I've done mainly Delphi and Java, with some side jaunts into less travelled paths (e.g. MUMPS, Intermec, etc.). One thing that I have definitely learned is that the more exposure one has to other environments and languages, the better that person becomes overall. It provides a way to map new ideas into existing languages, and vice-versa.

A while back, I came across Martin Fowler's, et al. term POJO, which itself is a continuation of other "Plain Old" terms in computer science. The reason it became a mainstream acronym is that there were many frameworks in Java that were adding complexity to objects, and Fowler wanted to get developers to at least question whether architectures with simple objects were viable under certain conditions.

Therefore, my contribution to the computer science lexicon is PONO, or Plain Old (dot)Net Object. As frameworks continue to emerge on the .NET side, the problems that caused POJO to gain ground in the Java camp will become more prevalent in the .NET camp.

Tuesday, 14 November 2006 17:40:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
# Monday, 23 October 2006
David Riggs, the Editor of the Delphi Informant, has given me permission to publish my old articles and editorials. He was even kind enough to provide the type-set copies as Word documents. We'll see how good the conversion from Word to HTML works.

The articles can be found - strangely enough - on the Articles page of my web site. For some reason, we don't have the original TIniSource article, but that just gives you an excuse to purchase the Delphi Informant CD archive. :-)

Obviously, these are older articles, and some of the information may be dated, but hopefully, there will still be something worthwhile to take away. Let me know if you find anything glaring that needs correction.

Monday, 23 October 2006 22:27:00 (GMT Daylight Time, UTC+01:00)  #    Comments [5] -
Delphi
# Wednesday, 13 September 2006
It seems that COM interop isn't always a good thing. I have a problem that I don't have a solution for right now, so if you have any feedback, I'd sure appreciate hearing it.

I wrote a wrapper around Lucene.Net to provide simple API access to some very basic operations (index, search, delete) that will be used by a Delphi Win32 application. Everything was working fine, but deep in the bowels of the Lucene code, in one code path, I encountered a line like this:


      return (float) (System.Math.Log(numDocs / (double) (docFreq + 1)) + 1.0);

When there are no documents in the index, this essentially evaluates to a call to System.Math.Log(0.0), which according to the MSDN documentation, should return NegativeInfinity. In fact, when this library is called from a .NET client, it does just that. However, when called via COM interop, this results in an exception, "Attempted to divide by zero". I started to modify the Lucene source to do a check for this condition and just manually return NegativeInfinity, and that did get me past that point, but then later on in the Lucene code, I end up getting an "Arithmetic overflow/underflow" error when Lucene tries to do some math on the returned value. So rather than continue to band-aid this problem, I thought I'd try to dig to the root of the problem to see why this is happening.

You can find an extremely simple test case with a .NET Class Library, .NET test client, and Delphi test client here. Just build the .NET solution, and then you can run the createtlb.bat file in the LogClient directory to get the Delphi project ready.

Wednesday, 13 September 2006 17:11:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Tuesday, 29 August 2006
I came across a link to COMTrace from Scott Hanselman. This tool allows for interception and logging of COM traffic. I just tried it out, and it mostly works on some of my more complex MIDAS applications. Granted, it is far from perfect (e.g. I needed to start a new process multiple times before I could get it to work when tracing an app server, or it would hang when starting a client process), but I was able to eventually use it to get tracing information on all of the round-trips that the application makes, including seeing parameters being passed from client to server. From the history log, it appears that the last change was in 2002, so it's a bit dated.

A while back, I was going to develop a tool like this, and actually had some things working on the Delphi level by intercepting VarDispHandler, but that would only work within an application, and for late-bound calls. That didn't sit well with me, as I wanted all traffic intercepted, be it early-bound or late-bound, and I didn't want to necessarily instrument my applications with my tracing/hooking code. I started looking at Keith Brown's Universal Delegator, and associated tools, and was going to go down that route, but the priority changed on having this tool, so it got shelved. It's nice to see that something is out there, even if it is imperfect.

Tuesday, 29 August 2006 20:27:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Tuesday, 22 August 2006
I wrote a VS.NET 2005 class library that I wanted to use from Delphi. Using COM Interop, this should be a breeze. Simply call "regasm MyLibrary.dll /tlb" and I should get a type library that I can import in Delphi. If that's all there was to it, then I wouldn't be blogging. ;-)

The first problem I had was that when I ran this command, I got an error stating:
RegAsm error: Failed to load 'C:\MyLibrary.dll' because it is not a valid .NET assembly

This was weird, considering it was a .NET assembly. The problem was that the regasm being used was the one from the .NET 1.1 framework, so it choked on my .NET 2.0 compiled application. Simply running the VS 2005 Command Prompt and re-running regasm got the type library I wanted - or so I thought.

I marked the classes I wanted exported via COM with the [ComVisible(true)] attribute, but the only thing exported to the type library was classes with no members. In order to get the members exported, I ended up putting the [ClassInterface(ClassInterfaceType.AutoDual)] attribute on the class, too. This isn't the optimal approach, according to MSDN, so I'll end up extracting the members to an interface and exposing that instead, but at least I'm up and running for the moment.

Tuesday, 22 August 2006 04:40:00 (GMT Daylight Time, UTC+01:00)  #    Comments [2] -
Delphi
# Wednesday, 05 July 2006
Time to confess: I'm a fan of data binding. I've loved it ever since Delphi 3 made it workable by introducing TClientDataset. It's powerful, but makes application code fairly simple. Yes, it can be - and often, is - abused when used incorrectly, but overall, it's a good concept.

I recently worked on getting data binding in a .NET 2.0 application to work with a lookup combobox. The concept is pretty straight-forward (and very analogous to Delphi's TDBLookupCombobox). Databinding in .NET is pretty impressive, and a step forward from where Delphi currently is. For example, you can bind straight to business objects as opposed to being required to consume database resources/components to complete data binding. One big snag was in getting the lookup relationship to respect null values properly. For example, assume you have 2 very simple classes, Order and Supplier, that are bound via BindingSource components. Furthermore, assume you have your data binding set up properly to have a ComboBox set with DataSource=orderBindingSource, DisplayMember=Name, ValueMember=SupplierID, and nameComboBox.DataBindings.Add(new Binding("SelectedValue", this.orderBindingSource, "SupplierID", true)); (see this reference or this reference for more details). Lastly, if you populate your objects as in the example below, you will notice that the original display of the combobox is populated with the first item in the supplier list. If you scroll to the next record and then back to the first record, the combobox will display properly.


    private void btnPopulate_Click(object sender, EventArgs e)
    {
      orderBindingSource.Add(new Order(1, "Test1", null));
      orderBindingSource.Add(new Order(2, "Test2", 1));
      supplierBindingSource.Add(new Supplier(1, "Supplier X"));
      supplierBindingSource.Add(new Supplier(2, "Supplier Y"));
    }

There are a couple of ways to fix this behavior. First, you could just reverse the order and populate the supplierBindingSource first. Alternatively, you can leave the population order alone and just call ResetBindings() after you're done populating all of the data.


    private void btnPopulate_Click(object sender, EventArgs e)
    {
      orderBindingSource.Add(new Order(1, "Test1", null));
      orderBindingSource.Add(new Order(2, "Test2", 1));
      supplierBindingSource.Add(new Supplier(1, "Supplier X"));
      supplierBindingSource.Add(new Supplier(2, "Supplier Y"));
      orderBindingSource.ResetBindings(false);
    }

One other quick note. When you want to set the Order.SupplierID field to null, you cannot write code like this:


SupplierNameCombo.SelectedValue = null;

Instead, you can either set the SelectedIndex to -1, or (IMO) more elegantly, write code like this:


SupplierNameCombo.SelectedValue = DBNull.Value;
Wednesday, 05 July 2006 19:47:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Wednesday, 28 June 2006
I've been writing a lot of test cases and projects in VS.NET lately. (Ed note: No, this doesn't mean I've abandoned Delphi. I still love Delphi, and use it as my full-time tool. However, I always have - and always will - keep abreast on the entire world of development tools.) One thing that I thought would be cool to have would be a simple expert - or "addin", in VS.NET parlance - to ZIP up all of the source files automatically. I thought this would be a good opportunity to learn about the addin framework for VS.NET, but like a good developer, I thought I'd check to see if anyone else has written this already. In short order, I found an article detailing a tool named ZIPStudio. That article also provides source and a setup msi file to install the addin, but it only works for older versions of VS.NET. A few clicks later, I found the author's web site with an update of ZIPStudio that will work in VS.NET 2005.

After installing the tool, and testing it on a few solutions, it appears to be exactly what I was after. Highly recommended for those of you who want to move project source around between machines.

Wednesday, 28 June 2006 15:47:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Tuesday, 20 June 2006
OK, so it doesn't have the same ring as Eminem's song, but it is accurate. :-) I was going through some of the older sections of my hard drive looking to reorganize and cleanup unwanted files. While I was doing that, I came across a project, TerraService, that I did ages ago. To the best of my recollection, it may have been ported from a VB application that I found somewhere on MSDN, but a search on that site isn't turning up any positive hits. Sorry for any lack of proper attribution. If this was written by someone else in another language, let me know, and I'll give the proper credit.

The reason I'm making the Delphi source available is that it shows how Delphi has improved over the years. I originally wrote this application using Delphi 6. As a result, I had to manually deal with the TByteDynArray types to decode them from base64 before display. Using Delphi 2006, this is no longer necessary - the types come back decoded and in usable fashion without any extra work on your part. This sample shows several interesting concepts: stream to variant conversion, drawing tiled images, calling a webservice, dealing with GIF and JPG files, and interception of webservice traffic for debugging.

While I don't know which specific version of Delphi fixes the TByteDynArray/Base64 de/encoding, this much I know for certain: Delphi 2006 makes web services very easy to do.

Tuesday, 20 June 2006 21:06:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Tuesday, 30 May 2006
I've been writing a .NET client application that must upload a file to a IBM WebSphere (Java) server. This journey was met with mis-steps all over the place. For starters, it looked like the WebClient class was what I wanted, but it turns out you can't POST both a file and HTML form variables. That meant that I needed to go to the low-level class, HttpWebRequest, to get this done. There is code on google that got me most of the way there, but the web server always ended up rejecting the submission with errors. To make matters worse, I don't own the web server, so support on exactly what was wrong was extremely limited.

Then I stumbled across a paper, Retrieving HTTP content in .NET by Rick Strahl. In that paper, Rick ends up writing a very nice wrapper class to deal with all of the details for you. I put together a test case to try this out, and the amount of code I had to write dropped from 123 lines down to 15. The new code even works with WebSphere. What's more, the wrapper class provides a very nice model for programming applications that need to communicate via HTTP. In my opinion, this is what Microsoft should have delivered with the framework, as opposed to making people write their own wrapper or find Rick's code (which was last updated in 2002, yet still works great).

I made 2 changes to Rick's base class:

  • I changed the cMultiPartBoundary variable to look like this in order to give a unique boundary marker: string cMultiPartBoundary = "-----------------------------" + DateTime.Now.Ticks.ToString("x");
  • In the method GetUrlStream, there is a place where the end boundary marker is written to the stream. However, according to the RFC on uploading files via POST, you need to have 2 trailing dashes to mark the last end. I added those dashes like this: this.oPostData.Write(Encoding.GetEncoding(1252).GetBytes( "--" + this.cMultiPartBoundary + "--\r\n" ) );

I am one happy camper. I've done this very thing with other web applications in Delphi before, and with the Indy components, it was very easy. Now with Rick's wrapper class, it's easy to do the same kind of thing in .NET.

Tuesday, 30 May 2006 18:32:00 (GMT Daylight Time, UTC+01:00)  #    Comments [4] -
Delphi
# Friday, 12 May 2006
While ActiveX has enjoyed a (mostly) deserved reputation for being ill-tempered and hard to work with, I've used it successfully for many years as a deployment vehicle for rich-client web applications that run inside the context of IE. I'll attribute most of that success to Delphi and ActiveForms since the most complicated plumbing is taken care of for me automatically, but still gives me the ability to override what I need. Special thanks should go to Lino Tadros and Steve Teixeira, former members of the Delphi R&D team, to allow this.

Fast forward to today. My goal is relatively simple. I have a .NET application built with WinForms. I'd like the same type of ease of use in deploying this WinForm application to my users. I've done quite a bit of research on the best way to achieve this, but I haven't come up with the perfect solution yet. I'm close, but it's not buttoned up all of the way yet. For starters, I would highly recommend the following articles:

I wasn't able to find this nugget anywhere, though. If you want to call methods from your HTML page via (e.g.) JScript, you need to make your EXE assembly COM visible. The easiest way to do this is to select Properties for the EXE in the Project Manager, and press Assembly Information. There, you'll find a checkbox to make this assembly COM visible.

So far, I've been able to get a WinForm EXE hosted in IE, strong-name it and communicate from the web page to the control. I still have several items left to tackle, like wrapping everything up in a CAB file for easier deployment, and getting calls to other assemblies working. I'll comment on those as I get to them, but if you have any pointers on better ways to do any of this, I'm all ears!

Friday, 12 May 2006 21:29:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Tuesday, 21 February 2006
Someone on the newsgroups posted a linke to SourceMonitor. It is a tool that analyzes source code (Delphi, C++, C#, Java, VB, C, and HTML) and reports on various code metrics, like the number of lines of code, average statements per method, methods per class, depth of method, global routines and variables, cyclomatic complexity of a method and tons more. The charts are incredible (Kiviat graphs that are configurable with tolerance levels), and you can drill down and sort by any criteria. By doing this, you can identify methods that need to be refactored. In addition, you can View Source on the details and get buttons to take you straight to various offending code metrics (max depth, max complexity method, etc.). The detail view also shows graphs and more information about each unit. Lastly, you can take multiple snapshots so you can monitor progress of your source code's health over time.

This tool is listed as being freeware, but this is one instance where you get WAY more than you pay for.

Tuesday, 21 February 2006 19:04:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
# Friday, 17 February 2006
SmartInspect is a new logging tool published by Gurock Software. This tool works with Delphi (including BDS2006), .NET and Java applications.

By adding some simple code to your application, you can get SmartInspect to keep track of the logging in a meaningful way (by process, thread, method, session, etc.). Log events that are "children" of other events can be rolled up and grouped to allow you to focus precisely on what you're interested in. Logging can be done via file or TCP/IP, so you can coordinate log messages from multiple machines. Source code is included for the objects that SmartInspect uses, and you can extend the objects to do whatever you dream up (filter packets, automatically colorize certain events, etc.). You can even install Code Snippets/Templates into the IDE that you use. There are many more features than this, but those are the ones that really stood out for me.

I would highly recommend this product. This company has gone from nothing to having one of the most professional product experiences I have ever seen. They have a very complete, nice-looking, and easily navigable web site; incredibly great documentation (both in general and for developers); trial versions; multiple support options (forum, knowledge base, and email); a blog; walk through samples; and an extremely well-polished user interface. I wish all companies came on to the scene with such a thorough attention to detail.

In short, great job guys. I look forward to using this product more as time passes.

Friday, 17 February 2006 20:33:00 (GMT Standard Time, UTC+00:00)  #    Comments [4] -
Delphi
# Tuesday, 07 February 2006
Date: March 16th, 2006
Time: 6:30pm - 9:00pm
Cost: FREE!
Location: Marriot West in Pewaukee
Driving Directions:"Former Highway 164 North" is now Highway F North. Or, just pay attention and take Exit 295 (as they mention in the directions on the web page).
What: John Kaster will be preseting the new features of BDS 2006, which includes Delphi, C#, and C++ personalities. This release has been rock solid for us, so I imagine this will be one of those must-upgrades for everyone who uses Borland products. Last year, John raffled off 2 copies of BDS (one Architect, and one Pro) to give away because we had good numbers, so be sure to get the word out if you want a chance for that kind of give-away again. There should also be discount coupons available. There is also typically good swag to be had, and the presentations John does is second to none.

Please RSVP to dmiser@distribucon.com so we can start getting a head count to make sure the room is sized appropriately.

Scott Simonson was able to recover some of the mailing list that we used last year, but I'm sure it's not complete. Please forward this link, or the upcoming email to any interested Delphi, C#, or C++ developers and have them email me at dmiser@distribucon.com to confirm attendance.

For those that care, it's also listed in EventCentral.

Tuesday, 07 February 2006 16:47:00 (GMT Standard Time, UTC+00:00)  #    Comments [2] -
Delphi
# Friday, 03 February 2006
I keep expecting ADO.NET to work as well as Delphi 1 did 10 years ago with respect to data access, and as a result, my expectations keep getting dashed. Of course, some things (like MIDAS) only materialized in Delphi 3, so that's a scant 8 years ago. :-( It seems that all of the collective wisdom in the .NET world to remote data (via .NET Remoting or Web Services) consists of one of 3 approaches, with zero tolerance for deviation.

  1. The DataSet approach.Use 2 methods for each entity that you want to remote. For example, you find numerous posts in google and references on MSDN where you need to call myAppServer.GetCustomer() to get a DataSet and then call myAppServer.UpdateCustomer(DataSet ds) to update the customer. Repeat this over and over and over again for each entity.
  2. The built-in serialization approach. Failing #1 above, people then start to tell you to create true business objects. You just need to take all of the tables that you use, create a bunch of objects, map the objects to the DB, and you're off and running. You can also use frameworks like Rocky Lhotka's CSLA.
  3. The ORM approach. ObjectSpaces has died, but that doesn't mean ORM is dead. There are a variety of options here. To name a couple that range from free to commercial, and vary in features: NHibernate, which is an open-source port of the Java persistence framework, Hibernate; and LLBlGen Pro. Of course, this means you need to buy into the framework you use.

However, what I really wanted was a way to remote data, and not worry about the more OO-centric techniques at this point. As a result, I wrote a framework, DrTier, to do just that. I now have it working the way I want in .NET. DataSets are streamed between client and server, user code is minimal, the app servers are extensible, and I'm able to take advantage of the best things that .NET has to offer. However, ADO.NET is not among them.

For example, if you have a stateless server, you cannot guarantee the SQL statement that was used to Fill the DataSet will be the same when you get back to the app server to update the data. I ended up using the DataSet.ExtendedProperties property to cache the SQL select statement and pass it around between client and server. By doing this, I can guarantee that I'm building the appropriate INSERT/UPDATE/DELETE SQL statements (DML) when I need to update the DataSet.

Speaking of which, ADO.NET wants you to create DML statements for every table. There are countless posts and articles chastising the use of CommandBuilder (poor performance, unoptimized for MSSQL, etc.). Creating your own DML statements at run-time is no picnic, even after we've solved the above problem. If you get schema information based on your SELECT statement, you will see that the types for each field are provider-specific. That means that you would need to have some kind of mapping between provider-specific types and DbType, or find another solution to parameterize your queries (dynamic type instantiation based on the string types returned in GetSchemaTable comes to mind as one possibility).

Another good lesson learned is that when using the Data Access Block, I have found that I can't take advantage of most of the methods in that block because they aren't customizable at all. You want a DataSet loaded with schema information? Good luck. Now you're using Database.DbProviderFactory to create concrete classes like you do in straight ADO.NET. The helper methods lack extensibility, so you're forced into this pattern. Returning fully formed DbCommands that point to a shared DbConnection isn't really even supported. You need to do that manually, too.

I won't go in-depth on the other features I found lacking (unlike Delphi), like ProviderFlags, UpdateMode, TFields, extensive events during reconciliation, robust error resolving support, etc., etc., etc. It seems that ADO.NET forces you into a pattern, and if you want to deviate from that pattern, you better be prepared to work.

Yes, I have an ulterior motive. I want my app servers written in .NET, and I want them to behave like MIDAS app servers so that I can call them from existing Delphi Win32 clients. Next up, I will need to write the interop code to get things working between MIDAS and DrTier. Once all of that is done, we can get our feet wet in .NET without resorting to a complete and total rewrite on both the client and server. So far, so good, but the finish line is a long way off, and I'm afraid ADO.NET will fight me every step of the way.

Friday, 03 February 2006 23:12:00 (GMT Standard Time, UTC+00:00)  #    Comments [8] -
Delphi
# Monday, 16 January 2006
As I alluded to in an earlier post, I had troubles using the nz() function from a TADODataset in Delphi. Take the following SQL, which works just fine in Access itself:


SELECT DISTINCT ContestId, Hole, nz(Score,'MISSING')
FROM tblContestDetails

This query behaves like isnull() in MSSQL where if the Score column is null, it returns the value 'MISSING' instead. If Score isn't null, then the value of Score would be returned. To me, this syntax is very intuitive, corresponds well with other DBMS' isnull() functions, and clearly captures the intent of what you're trying to do in once concise statement. However, if you put this query in a TADODataset and try to open that dataset, you will be greeted with the following error: "Undefined function 'nz' in expression.". If you need this type of substitution in Access when executing from Delphi, I found the simplest way to get around this is to use the iif() and isnull() functions. It's more verbose, and I don't like it as much, but when you need things to work, some times you have to live with things that aren't aesthetically pleasing. The SQL above translates to this:


SELECT DISTINCT ContestId, Hole, iif(isnull(Score),'MISSING', Score)
FROM tblContestDetails
Monday, 16 January 2006 15:12:00 (GMT Standard Time, UTC+00:00)  #    Comments [5] -
Delphi
# Thursday, 12 January 2006
This link is a wiki for Live Templates that you can install in your copy of Delphi. If you come up with your own cool Live Templates, you can add them there, too.
Thursday, 12 January 2006 15:48:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
I had a need to write SQL to answer the question "How many times in a row have I scored -20 or better?" in a game of Golden Tee. I have the raw data stored in a table that has these fields (and more, but they aren't important to this exercise).


tblContestResults
ContestId    Long Integer
Score        Integer

I didn't want to use a cursor, and I wanted to stay as close to ANSI SQL as possible (so no stored procs, etc.). After googling around a bit, I came across this link that showed how to do this all in SQL using nested selects and virtual groups. The sample was written to be compatible with MSSQL. After tweaking it just a bit, I came to this solution, which works wonderfully.


SELECT Max(grpCnt) AS MaxRun
FROM (Select grp, Count(*) As GrpCnt 
  From 
  (Select A.ContestId,  A.Score, 
       Case 
           When A.Score <= -20  Then 
                    Isnull( (Select Max(B.ContestId) From tblContestResults As B 
                                Where B.ContestId < A.ContestId and B.Score>-20), 
                               (Select Min(C.ContestId) from tblContestResults AS C) ) 
      End As grp 
  From tblContestResults As A) As WithGrps 
  Where grp Is Not Null 
 Group By grp) AS WithGrpCnts;

You can take each of the nested selects out and break them apart to execute them in order to see how the query is built up. Quite an interesting technique, and one that I will definitely keep in my bag of tricks. For my purposes, I had to use MS Access, so that meant I had to change things in the SQL a bit more since there is no "CASE WHEN" in Access. The following is the code I ended up using, and it also works quite well. I didn't use the nz() function because there is a problem in Delphi's TADODataset when executing code that has that expression in it (more on that later). Using nz() does work in Access, and makes the query easier to read, IMO. But if it doesn't work in Delphi, then I can't use it.


SELECT MAX(grpCnt) AS MaxRun
FROM (SELECT grp, Count(*) as GrpCnt
  FROM
  (SELECT A.ContestID, A.Score, 
    IIf(A.Score<=-20,
      IIf(
        isnull((Select Max(B.ContestId) From tblContestResults As B Where B.ContestId < A.ContestId and B.Score>-20))
        ,(Select Min(C.ContestId) from tblContestResults AS C)
        ,(Select Max(B.ContestId) From tblContestResults As B Where B.ContestId < A.ContestId and B.Score>-20)
      )
  ,Null) AS grp
  FROM tblContestResults AS A
  ORDER BY ContestId) as WithGrps
WHERE grp is not null
GROUP BY grp) AS WithGrpCnts;
Thursday, 12 January 2006 15:03:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Wednesday, 11 January 2006
A while back, I posted a blog about Delphi having problems with running a COM server in a service under Win2003. While the work-around to make things work is fairly trivial, Borland has made things even easier in Delphi 2006. Simply set Application.DelayInitialize to true in your dpr, and all of the timing details will be taken care of for you automatically.
Wednesday, 11 January 2006 15:03:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
# Tuesday, 10 January 2006
When importing data into Excel, the data that you set will be treated as a Text cell by default. Dates in Excel are equivalent to Delphi TDateTime in that they are a julian date and time is stored as a fraction and share the same numeric equivalents after 3/1/1900 (integer value of 61). This makes life very simple when writing date values to your worksheet since you just need to write the TDateTime value to Excel and if you format the cell as a date, you get the date/time in the worksheet.


procedure TForm1.Button1Click(Sender: TObject);
var
  Excel, Workbook, Worksheet: OleVariant;
begin
  Excel := CreateOLEObject('Excel.Application');
  try
    WorkBook := Excel.WorkBooks.Open('c:\temp\ws.xls');
    WorkSheet := WorkBook.Worksheets.Item['Sheet1'];
    WorkSheet.Range['A1', 'A1'].Value := Now;
  finally
    Excel.Quit;
    Worksheet := Unassigned;
    Workbook := Unassigned;
    Excel := Unassigned;
  end;
end;
Tuesday, 10 January 2006 15:24:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
For quite some time, Delphi has allowed custom FormatSettings to be used with many date/time functions. This allows you to use custom date/time settings within an application without changing the Windows options. For example, if you used this code snippet and had a date/time format like mm/dd/yyyy, you would see the date/time reported in the new format instead:

procedure TForm3.Button1Click(Sender: TObject);
var
  MySettings: TFormatSettings;
  s: string;
  d: TDateTime;
begin
  GetLocaleFormatSettings(GetUserDefaultLCID, MySettings);
  MySettings.DateSeparator := '-';
  MySettings.TimeSeparator := ':';
  MySettings.ShortDateFormat := 'mm-dd-yyyy';
  MySettings.ShortTimeFormat := 'hh:nn:ss';

  s := DateTimeToStr(Now, MySettings);
  ShowMessage(s);
  d := StrToDateTime(s, MySettings);
  ShowMessage(DateTimeToStr(d, MySettings));
end;

However, if you used 'mmm-dd-yyyy' as the ShortDateFormat in the example above (to display Jan-09-2006), you would get an error when calling StrToDateTime on this string. The workaround is to call d := VarToDateTime(s) instead, since that function supports month names when encoding the TDateTime. I logged this bug as QC 23301, so if you'd like to see this fixed, please rate this entry and vote on it if it's important enough to you.

Tuesday, 10 January 2006 02:08:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
# Tuesday, 03 January 2006
I've been using VNC, including countless variants like UltraVNC and RealVNC, to access my home PC forever (OK, probably since around 1997 or so, so for the purposes of computer lifespans, that easily constitutes forever :-)). I even moderate a (not-so-active) open source project called DelphiVNC. However, I tried Remote Desktop recently, and I immediately switched to it. My reasons for doing this were:
  • Speed - RD seems much faster than VNC
  • Easy file copy between systems
  • Can stream remote sound to the local PC
  • Easy to setup and configure
  • Web access

Here are some of the links that I used to get things up and running:

  • Good general overview of features from MS
  • Getting access to local files in an RD session
  • RD Web Connection setup will let you connect to the remote PC from the local PC via IE.
  • How to use RD over SSH. This is a good FAQ for other RD items, too. It appears that as of XP SP2, you don't have to run in compatibility mode, at least as long as you try to connect to another port (e.g. 127.0.0.1:3390). Step 9 in this documnet means that you need to set up a SSH tunnel to forward from port 3390 to 3389. Pay close attention to the fact that you need to logout, though. If you don't do that when ending a session, you will need to reboot the remote PC to get RD working again.

If you go to a command prompt and type, "mstsc /?", you will see the different options you can pass in. I set up a few different RD profiles by selecting the Options button, filling out the details I wanted, and saving the profile. I then made shortcuts to pass in the rdp file to let me get quick and easy access to the remote PC.

Let me know how this works for you.

Tuesday, 03 January 2006 20:07:00 (GMT Standard Time, UTC+00:00)  #    Comments [7] -
Delphi
# Friday, 16 December 2005
John Kaster announced that Update 1 for BDS 2006 is now available. This update is supposed to bring the C++ personality out of preview mode, but since some of the files that changed are used by all personalities (IDE, TLE, etc.), I imagine some slight positive benefit will occur in the Delphi personalities as well.
Friday, 16 December 2005 15:18:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
# Wednesday, 14 December 2005
Version 10.0.2151.25345 of midas.dll is available on my web site at http://www.distribucon.com/midas.html.

It should be backwards compatible with older Delphi versions. If you run into any problems with this new version, I highly recommend that you enter the issue into QC. MIDAS continues to get good bug fixes due in large part to people posting their issues in QC.

Have fun!

Wednesday, 14 December 2005 21:43:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
# Wednesday, 07 December 2005
I'll leave the fun of the Big Feature Writeups (e.g. Live Templates) to others in the blogoshpere that have already started to cover some of the new features of Delphi 2006. Instead, I'll cover some of the more mundane, but very useful new features of Delphi 2006.

Method navigation is described in the "What's New in Developer Studio 2006" file like this: "You can quickly navigate between methods in your source code using a series of hotkeys". You use hotkeys like CTRL+ALT+Up-arrow and CTRL+ATL+Down-arrow to move from one method to the next in a given unit. CTRL+ALT+Home and CTRL+ALT+End take you to the first and last method - respectively - in the unit. There is also the concept of Class Locking which will only let you navigate with those hotkeys within the current class. The documentation on how to toggle this feature is incorrect, though. The correct way to do this is to position your caret in the IDE somewhere in a class, then press CTRL+Q, followed by just the letter L. After you have enabled class lock, you are constrained to method navigation only within that class.

Note: This worked for me with the IDE Classic editor setting. I'm not sure how it works with other settings.

Wednesday, 07 December 2005 19:18:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Friday, 21 October 2005
Borland is doing another 24 hours of Delphi on October 24. I'm on from 9:50am-10:20am CDT that morning (for non-US readers, check the BDN article for time conversions)I'll be talking about migration considerations for Delphi 2006, including changes to VCL, DBX, and DataSnap. I'm excited about getting a chance to share some of the things that I've learned about Delphi 2006 and pass on my very favorable experience with that product. Hopefully you can come away from my talk with a few time saving tips when migrating your applications to Delphi 2006. Looking forward to seeing you online that day!
Friday, 21 October 2005 17:16:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Friday, 23 September 2005
I was preparing a writeup on how LINQ handles IDENTITY fields, and there is some mixed news on this front. First off, the good news. The concept of how LINQ will deal with IDENTITY fields is quite solid. The goal is to have the DB return the new value for the IDENTITY field and then populate the object with that new value. Very slick. Back in Delphi 5, MIDAS introduced a property TDatasetProvider.ProviderOptions.poAutoRefresh that was supposed to do this same thing. However, it was never implemented, so it never worked. As a result, you had to use other options, like the one I outlined in my BDN article. So LINQ has a definite advantage here.

The other really cool things about LINQ IDENTITY handling is that you can build up a graph of objects (i.e. Orders and OrderDetails), add the object to a Customer object, and when you do a SubmitChanges(), the IDENTITY fields are updated properly throughout the whole object graph, in addition to linking properly in the DB.

However, the bad news is that the DLINQ implementation is throwing a System.InvalidOperationException because "There is already an open DataReader associated with this Command which must be closed first.". If you are debugging the application and pause long enough before continuing, the data will still get written to the DB. However, I have not been able to get the application to work at all when just running it (i.e. Start Without Debugging). I believe it may be due to my mixed use of retrieving a customer from the DB using LINQ, and then creating Order and OrderDetail objects locally and adding them to the Customer object. I say this because doing a simple add of a Category object all by itself doesn't yield this exception. I also had an instance one time where only the Order was inserted, and not the OrderDetail, thereby invalidating the atomicity of the transaction. I plan on cleaning up the test case and submitting it to MS. Anyone know where this kind of feedback should go?

Friday, 23 September 2005 17:58:00 (GMT Daylight Time, UTC+01:00)  #    Comments [4] -
Delphi
# Saturday, 10 September 2005
Make the time to go and watch the video of JBuilder 2006 Virtual Peer Programming. In one word: Brilliant! In many words: Absolutely drop-dead awesome, incredible, fantastic, and many other synonyms for great. :-)

In a nutshell, JB2006 will allow you to carry on IM conversations; share projects among multiple team members; cooperatively and synchronously view, navigate, edit and debug shared projects. The concept is great, and the implementation is even better. I can't wait to get my hands on this in a future version of Delphi! Imagine, no more requests like "This isn't working, can you stop over?". The possibilities are almost endless. To name a couple of the top of my head, interactive training/mentoring without taking a flight to the client's site and using this to implement XP anywhere.

For some background, I remember chatting with Pat Kerpan back when I was in the DSP group at Borland. The conversation revolved around this very idea, and how it could help with product development. As a matter of fact, I actually wrote Delphi and JBuilder plugins to integrate TeamSource DSP into the IDEs. Among the many features those plugins had, one of them was an integrated IM client (implemented via Jabber). It was actually quite handy to have in your IDE. At first, I wasn't sure how useful it would be, but I soon became very dependent on it. Others seemed to like it to, since it won the Best Team Development Tool award at the 2002 JavaOne conference. Since it beat out Oracle, IBM, and Rational, I was quite proud of my contribution.

Still, it's a strange feeling to view this video. It's not like I'm seeing my baby being born, but more like watching from afar as my best friend has a baby that I've always wanted. Yeah, I know. Quit with the metaphors while I'm behind. ;-)

Saturday, 10 September 2005 20:44:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Friday, 02 September 2005
With the loss of Project | Web Deployment Options in Delphi 2005, you will need to create your HTML files by hand to display ActiveForms. When doing this, remember that you will need to specify the GUID of the CoClass, and not the GUID of the Library.
Friday, 02 September 2005 17:24:00 (GMT Daylight Time, UTC+01:00)  #    Comments [2] -
Delphi
# Thursday, 07 July 2005
On July 13, Borland will host the 24 Hours of Delphi. While it might not be as good as the TV show "24", it does look like it will be a lot of fun.

I'll be online chatting with them in hour 7, (7am PDT, 9am CDT, check your favorite time conversion site for your local time). I'll be talking about a conversion of a huge Win32 application that we did from Delphi 6 to Delphi 2005. I'm sure there will be a mention or 2 of PopupMode in there. I've also been known to to talk about MIDAS/DataSnap, so I'm sure that will be in the mix somewhere, too! I look forward to seeing you guys online. Get your questions in early.

Thursday, 07 July 2005 20:07:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Thursday, 16 June 2005
I use Component Templates a lot. I find it to be the easiest way to start writing a test case when trying to isolate a bug. For example, one template that I have used since the invention of templates is to have a DataSource, ClientDataset, DatasetProvider, SQLQuery, SQLConnection, and DBGrid all hooked up and ready to go. When I see something I need to check out, I just drop the TDBXTemplate onto my form and I'm off and running. Unfortunately, a bug in Delphi 2005 prevents multiple components from being selected simultaneously, which means you can't create Component Templates like this.

What I've done for now is to go to the design view of the form, select the text of all the components, and add it to the Code Snippets window (select text, press Alt, and drag the text block to the Tool Palette). This means that when I want to create this "template", I have to go to the text view of the form and use the Code Snippet. I'm not thrilled about having to do this, but at least it's a workaround for now.

Thursday, 16 June 2005 21:53:00 (GMT Daylight Time, UTC+01:00)  #    Comments [2] -
Delphi
# Friday, 03 June 2005
During conversion of our application suite to Delphi 2005, I ran into a very irritating bug where a form was flashing extremely quickly during application startup. It was so fast that I couldn't get a feel for which screen it was that was flashing. Debugging the application also didn't reveal what form it was. So, what to do? I thought of using Camtasia to record the screen during startup, and then play back the recorded session at a very slow rate so that I could find out what I needed to know. I had to be sure to crank up the frames per second capture rate as high as it would go, but after a smooth install and some tweaking of options in Camtasia, the plan worked beautifully. I found out what form it was that was flashing, and then fixed it.

The gory details were that this form only got included from a DLL that had its conditional compilation directives wrong (for this build). However, this flash didn't occur in the Delphi 6 days. So I thought back to my previous experiences with PopupMode, and set PopupMode to pmAuto for this form and everything is great again.

Some days, I long for the Ctrl-S / Ctrl-Q days again. :-)

Friday, 03 June 2005 17:21:00 (GMT Daylight Time, UTC+01:00)  #    Comments [4] -
Delphi
# Thursday, 02 June 2005
I had a need to get AutoUpdate functionality into one of my applications, so that meant a perusal of Google to find out what others were using. After checking out several options, I deceided that AutoUpgrader fit the bill nicely. There were plenty of options and events to help control the AutoUpdate process. There was also a trial version that supported Delphi 2005, so that made it easier to try it out to see how it works. It requires a text file on a server somewhere, defining the newest version available. If a new version exists on the server, the component downloads the new version (all the while, providing status information about the download), and restarts the application to use the newer one.

The only downside was that the component has a VersionNumber property that is used to compare versions. I would prefer that the component would be smart enough to retrieve the version information from the file and use that, but this is easily remedied by placing this line of code in the DataModule.OnCreate where the AutoUpgrader resides:


  // GetVersionInfo is a locally defined function to retrieve version info from the current Application
  AutoUpgrader1.VersionNumber := GetVersionInfo;
Thursday, 02 June 2005 15:12:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Tuesday, 24 May 2005
I'm using Indy 9 here with Delphi 2005, and recently installed the Delphi 2005 Update 3 patch. During the patch process, several files that I deleted came back, along with several registry entries. Afterwards, I was getting an error when trying to load a DataModule that had some Indy components on them telling me that Indy90 could not be loaded because it uses IdWinsock2, which was already loaded by dclIndyCore. I imagine this happened during the install of the update by copying registry entries from HKLM to HKCU.

To work around this, delete the following registry entries:


HKCU\Software\Borland\BDS\3.0\Known Assemblies\c:\program files\borland\bds\3.0\bin\dclIndyCore.bpl
HKCU\Software\Borland\BDS\3.0\Known Assemblies\c:\program files\borland\bds\3.0\bin\dclIndyProtocols.bpl
Tuesday, 24 May 2005 21:18:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Thursday, 28 April 2005
If you use the ChildRDM architecture available in Delphi 6 in %DELPHI%\Demos\Midas\SharedConn, there is a possibility of the server hanging in memory after all clients have exited. This really only applies if you are using a COM object in another DLL and using that COM object as a ChildRDM in your main server EXE.

Debugging the problem, I found that there were still references to the external DLL, and that the CPU view was showing me that the EXE was repeatedly trying to enter a critical section, but not being able to do so. If you look at the destruction code for TDataModule, you'll see that it tries to lock access to the DataModule. Well, if you rely on Delphi to get rid of internally stored interface references when they go out of scope, it will be too late to reclaim the external DLL. The solution is as follows:

Create an overridden BeforeDestruction method in the main RDM. In that RDM, set all of your ChildRDM interface references to nil in order to explicitly release the COM objects. Then, make a call to CoFreeUnusedLibraries (found in ActiveX.pas). This will release the DLL, if the COM subsystem says it's OK to do so. However, I found that even that was enough in all cases, so add another call to CoFreeUnusedLibraries in the main RDM's finalization section. See the code snippet here:


procedure TMainRDM.BeforeDestruction;
begin
  fSharedDataRdm := nil;
  fChildRdm := nil;
  CoFreeUnusedLibraries;
  inherited;
end;

initialization
  TComponentFactory.Create(ComServer, TMainRDM,
    Class_MainRDM, ciMultiInstance, tmApartment);
finalization
  CoFreeUnusedLibraries;
end.

After doing this, I was not able to get the app server to hang in memory again.

Thursday, 28 April 2005 22:59:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Thursday, 21 April 2005
Quality Insight is Borland's re-branding of the JCL OTAPI exception, combined with an interface to submit bug reports directly to QC. In a nutshell, it takes any unhandled exceptions that occur in the Delphi 2005 IDE, report a dialog - complete with stack trace - to you, and allows you to submit the bug report to QC. It's similar in concept to Windows' online error reporting mechanism. It's a very handy tool, and I believe it will help increase the stability of the IDE, since it gives R&D more information about bugs that pop up in the IDE.

Unfortunately, it looks like it is disabled by default during install (at least it was here on all 8 machines). To turn this useful feature on, run regedit.exe and set the following key/value pair in

HKCU\Software\Borland\BDS\3.0\Known IDE Packages

$(BDS)\Bin\exceptiondiag90.bpl = (Untitled)

If the value is blank, the package will not be loaded in the IDE. Therefore, set it to some arbitrary value, like "(Untitled)", restart Delphi, and you should be greeted with the exception dialog the next time you encounter an unhandled exception in the IDE.

Note: Be careful of using other 3rd party exception tools (e.g. madExcept and Eureka). You'll have to test for sure, but these other packages may end up consuming the exception before it gets to Quality Insight. If that's the case, simply disable the IDE package of the 3rd party exception tool.

Thursday, 21 April 2005 15:27:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Wednesday, 13 April 2005
In my last entry, I discussed a simple work-around for overcoming a new Delphi 2005 bug. After further review, the Bears still suck. Sorry, that probably only makes sense if you knew the story of the Packers/Bears instant replay game. See the November 5, 1989 game on this page for more details - and yes, the replay official got it right. :-)

Back to our PageControl story... The work-around I provided works well, but the problem is that countless other things in the VCL also fire off a request to do a RecreateWnd (e.g. setting a form's Parent). This means that you would need to do all of the things that cause RecreateWnd to be done before setting ActivePage. That's not entirely possible. Here is my new list of possible work-arounds:

  • Patch the VCL to fix the new TPageControl.SetTabIndex method. Either comment out everything after inherited, or add code to take invisible tabs into consideration (a la TPageControl.GetImageIndex). This will only work if you don't use run-time packages, though.
  • Create a descendant TPageControl, override the TPageControl.SetTabIndex method, and either fix the method or restore to D6 functionality (only call inherited)
  • Wait for the fix in the VCL code
Wednesday, 13 April 2005 15:01:00 (GMT Daylight Time, UTC+01:00)  #    Comments [4] -
Delphi
# Monday, 11 April 2005
Unlike my last entry, I would classify this one as a bug.

If you use a TPageControl, and you set certain Tabsheets to invisible, and then you set the OwnerDraw property to true on the PageControl (like the code listed below), you will not get the proper page selected. This is due to a new method, TPageControl.SetTabIndex, that doesn't take visible tabs/pages into account before trying to set the PageControl.ActivePage property. In Delphi 6, this method didn't exist, and everything worked fine. However, in Delphi 2005, things are different. When you set OwnerDraw, the underlying window is recreated. During the destroy of the window, the current tab index is saved off so that it can be restored when the window is later recreated. It is at this point that the new SetTabIndex method is called, and in that method, notice that no effort is made to determine that any tabs have been set invisible, therefore, the wrong page is selected.

The simple workaround is to set OwnerDraw in the DFM, or at the very least, set OwnerDraw prior to setting any tabs to invisible.


Tabsheet2.TabVisible := false;
Pagecontrol1.ActivePage := Tabsheet4;
Pagecontrol1.OwnerDraw := true;

I also submitted this to Quality Central (QC) as issue 11978. You will find a test case in that entry, too.

Lastly, thanks to both Chris Hesik (Borland) for helping me out with some debugger issues, and Rich Werning for helping debug this to the point where we could come up with a valid test case.

Monday, 11 April 2005 23:08:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Tuesday, 05 April 2005
Here's an interesting change between Delphi 6 and Delphi 2005 in the VCL. Take the following code snippet where SQLQuery1.SQLConnection = nil:
SQLQuery1.SQL.Text := 'select * from MyTable where ID = :ID';
SQLQuery1.Params.ParamByName('ID').DataType := ftInteger;

In Delphi 6, everything works fine. In Delphi 2005, TSQLQuery.QueryChanged has been updated to use TSQLQuery.SetParamsFromSQL internally. This means that if you don't have a SQLConnection assigned when updating the query text, then the params will be cleared. This means that an exception will be thrown when trying to do the ParamByName call.

I'm not sure that this would qualify as a bug, per se, but it definitely changes your application's behavior when all of these circumstances are present.

Tuesday, 05 April 2005 21:24:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
Michael Slinn is the new Sr. Product Manager for Delphi, C++, C# and .NET at Borland (wow, that's a mouthful for a title!). I have enjoyed reading his blog posts, his replies to comments, and his posts in the newsgroups. He asked about the top 10 things that can be done to market Delphi, so I came up with this one idea that I thought was worth sharing.

Thesis: I believe the IDE and personalities should be broken out into separate SKUs.

Since the Galileo IDE can support multiple languages, it might make more sense to break up the SKUs better. With VS.NET, you buy VS.NET and get VB.NET, C#, and C++ - whether you want them all or not. My thought is that you could have the IDE be sold as the base item. It would contain things like source code integration, StarTeam integration, Caliber/RM integration, Refactoring, and maybe some more core bits. The personalities could then be sold as add-on packages. Delphi would contain Win32 and .NET. The C# personality would be separate from that, as would the C++ personality. Each personality would be sold as Pro, Enterprise, or Architect. The add-on packages would plugin to the main IDE and contain all of the bits that pertain to that personality (e.g. VCL, VCL.NET, debugger, designers, etc.).

While this would be more work initially for Borland, I think the benefits would be huge over the long-run. If someone reports a bug in the IDE that is truly an IDE bug, the IDE can be fixed and a patch released and you would get that fix for all personalities. If there is a bug in a personality, just update the personality. If the fix touches both, then both parts can be updated. This independence would be ideal for the consumer. For example, if there is a bug that is causing IDE performance bottlenecks, the consumer may be forced to wait for an entirely new release of Borland Developer Studio (BDS), since fixing those problems may require breaking changes to the interface compatibility initially released with Delphi 2005. By adopting this model, Borland could start to release more frequent patches, and also dedicate resources to the appropriate codebase based on SKU sales and profitability.

I could also see a tool used in the Borland R&D department where you would have dependancy graphs used to tell the team which files would be affected by a specific change. For example, pretend there is a unit named "CoreIDEServices.pas" that needs a patch. The developer would fix the patch, run the tool, and it would say that the only file that needs to be updated is coreide90.bpl. Perhaps a VCL for Win32 bug fix would result in only the PAS file, DCU file, and corresponding BPL file needing deployment.

People have been asking for the VCL to be more of an "a la carte" model forever. This could be a step in that direction. I'm not advocating that this be done today. Iron out some of the current problems, but keep an eye on this model. I think it would serve everyone well.

Tuesday, 05 April 2005 15:35:00 (GMT Daylight Time, UTC+01:00)  #    Comments [2] -
Delphi
# Wednesday, 30 March 2005
Somewhere along the way of installing and customizing Delphi 2005 with update 2, various 3rd party packages, and customizations, I lost the ability to view the contents of the Delphi 2005 help file. In order to get this functionality back, I used this fix:
"c:\Program Files\Borland\BDS\3.0\Help\Common\regHelp 7"

After this command executed and the help was rebuilt, everything was fine again.
Wednesday, 30 March 2005 16:27:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Thursday, 10 March 2005
Jeremy North just released version 2 of his Delphi Configuration Manager (DCM). It's a tool that helps you optimize your Delphi IDE by creating profiles that load specific packages. So, if you have one client that uses one set of packages, and another that uses a different set, you can set up 2 different profiles and only load the packages that you need depending on which client you're working on currently. The main feature that I like in this version is that run-time packages are also available to be controlled in a profile. Give it a look. Very well done.
Thursday, 10 March 2005 15:51:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Wednesday, 09 March 2005
Google Desktop Search has made it's way out of beta and is now a released product. It looks like it has become way more flexible, too. The biggest complaint I had was the lack of file extension support, which was sort of tied to the fact that there was no SDK. They re-architected this area, and now have support for plugins (including, a Trillian plugin) and allow developers to create plugins via the SDK. More information on developing plugins can be found here. They also expanded the default set of files that are indexed to include (among others) MP3 and PDF files. I also really love the DeskBar to gain easy access to searches.

My buddy, Rich Werning, and I were talking about some possible plugins that we may develop after we convert the SDK files to Delphi:

  • ZIP plugin - Scan through ZIP files to find items inside a ZIP
  • Blog/FeedDemon plugin - Search your locally downloaded blog entries, and have it take you to the original author's page when an item is found
  • Database plugin - Not entirely certain on this one, but if you provided a full connection string, maybe you could search a DB schema for fields/tables/etc. Or maybe even search a table for specific text.

I'll certainly blog anything we do on this front.

Wednesday, 09 March 2005 02:48:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Monday, 07 March 2005
I needed to create a custom TField class to handle some special processing recently. It was surprisingly easy to get this working:
1. First, create a descendant TField class and put it in a run-time package.

type
  TDrmBCDField = class(TBCDField)
    //override whatever methods you need here
  end;

2. Next, create a registration unit and place that in a design-time package. This unit has nothing more than this:

unit uDrmBCDFieldReg;

interface

procedure Register;

implementation

uses
  uDrmBCDField,
  DB;

procedure Register;
begin
  RegisterFields([TDrmBCDField, TDrmFMTBCDField]);
end;

end.

After compiling these packages, you can now get the new custom TField type to display when selecting "New Field..." in the Dataset designer. However, this doesn't address places where you don't use persistent fields, and it doesn't address the (IMO) more commonly used "Add Fields..." or "Add All Fields" context menu items. In order to always use your new custom class, you need to add code like this to your run-time package. By doing this, both the Delphi IDE and your code at run-time will pick up the proper field class type. I had to use code like this because Delphi 6 doesn't have assignable const permissions in the DB.pas unit.


var
  FieldClass: ^TFieldClass = nil;
initialization
  FieldClass := @DefaultFieldClasses[ftBCD];
  FieldClass^ := TTIPBCDField;
finalization
  if Assigned(FieldClass) then
    FieldClass^ := TBCDField;
end.

Notes:

  • There is a bug in Delphi 6 that causes occasional AVs when rebuilding packages that use RegisterFields. According to this Google post by Vitaliy Lyanchevskiy, you can use RegisterClasses during design and development to get around this problem.
  • Instead of DefaultFieldClasses, you can override TDataset.GetFieldClass if you want specific field types to be used at a TDataset-level.
Monday, 07 March 2005 22:48:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Friday, 25 February 2005
We currently use Multilizer in our product. However, we really don't use all of the features. It's use is pretty confined to "dynamically display messages" and "translate captions/labels on forms". Using Delphi 6 with Multilizer 5, things work fairly well.

We are also preparing the switch to Delphi 2005 in the next couple weeks. After contacting Multilizer, it turns out that there is no support for Multilizer 5 with Delphi 2005, and as a result, we would be required to spend many thousands of dollars to upgrade to Multilizer 6. That isn't a very attractive option. As of this writing, Multilizer will be thrown out of here, never to return. I'm investigating using Delphi's built-in Integrated Translation Environment (ITE) at the moment. I remember there were all sorts of problems with ITE early on (like in the Delphi 3/4 days). I'm hoping enough work has been done in this area to make it usable for our fairly simple needs. If not, we're writing our own tools to do this.

I believe component vendors should provide version compatible releases for newer versions of Delphi. Most component vendors actually do this quite well. I also believe that when companies try to force you into spending thousands of dollars, you should look to replace them immediately.

Friday, 25 February 2005 17:28:00 (GMT Standard Time, UTC+00:00)  #    Comments [8] -
Delphi
# Tuesday, 22 February 2005
Using Mike Scott's old Netscape plugin SDK, I've been very successful in getting applications deployed with a variety of deployment options (thin client EXE, IE ActiveX/ActiveForm, and Netscape plugin). However, the Plugin SDK has been updated several times, and I haven't gone back to keep the original Delphi port up to date. Add to this mix the "other browsers" on the market that also need to be supported, and I was left wondering which direction to go.

The result was that I found the Netscape ActiveX plugin that allows ActiveX controls to be used inside Netscape-compatible browsers. So far, the results have been mixed, but I like the idea of having single-sourced code that compiles to the same deployment option (ActiveX), and let the browsers worry about how to handle it.

Tuesday, 22 February 2005 17:56:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
# Friday, 18 February 2005
Some of you may remember my post on AQTime back in December. At the time, I commented that there were some show-stopper bugs that were preventing me from using AQTime (D6 run-time package and leaked Delphi object names not reporting correctly were the main ones).

I'm happy to report that my first reason for sticking with AQTime (Support) has proven to be a good read. Running AQTime 4.40.469.0, coupled with a custom patch acquired from their support department, I now have AQTime 4 working in my environment. I like the GUI better in AQTime 4, and the service support is tons better. For example, I couldn't profile a COM service properly in AQTime 3 (using SvCom), but it works just fine in AQTime 4.

I'm looking forward to identifying all sorts of wicked issues now. :-)

Friday, 18 February 2005 16:31:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Monday, 14 February 2005
While I'm not exactly the first one to do so, I would like to throw my congratulations out there as well. I just listened to this interview about the creation of Delphi, and it definitely brought back memories.

At the time, I was working for Med Data Systems, Inc. out of San Diego in my first gig out of college. It was a great time of learning and finding out exactly what it meant to be a professional software developer. One of my tasks was to port our DOS codebase to a Windows application. Since we used Turbo Pascal for DOS since version 5.0, it seemed like a natural fit to stay with Borland. We looked at TPW and VB, but in the end, once we saw what Delphi was like, there was no need to look any further.

My boss, Steve Belkin, and myself ended up flying up to the launch event in San Francisco (try explaining to your girlfriend that you're heading up to San Francisco over Valentine's Day and see what kind of look you get :-)). It was every bit the event that you've heard it was. If anything, the reports don't do it justice. It was truly a once-in-a-lifetime experience to witness the event first-hand, from the initial technical session where we got free software, to watching the stampede of people on the floor clamoring for the trial copy. Not to mention, Borland throws the best parties in the industry, and this one was probably their finest ever. I do miss Cadillac Margaritas...

Monday, 14 February 2005 21:16:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Wednesday, 09 February 2005
This is a quick followup on my post, HTTPSRVR with Apache. If you're using an older version of httpsrvr (e.g. from Delphi 6), then you need to add the following OnCreate/OnDestroy events for the main httpsrvr WebModule. The reason you need to do this is that each thread needs COM initialized, and Apache doesn't automatically do that initialization (whereas, IIS does).


procedure THTTPServer.WebModuleCreate(Sender: TObject);
begin
  { Each web module will be in a separate thread. We need to initialize
    the COM subsystem for each thread }
  if Assigned(ComObj.CoInitializeEx) then
    ComObj.CoInitializeEx(nil, COINIT_MULTITHREADED)
  else
    CoInitialize(nil);
end;

procedure THTTPServer.WebModuleDestroy(Sender: TObject);
begin
  CoUninitialize;
end;
Wednesday, 09 February 2005 17:20:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Monday, 07 February 2005
Ages ago, back when httpsrvr first came out, I got it working with Apache for a client. Recently, I came across a post by Sydney Delieu in the borland.public.datasnap group outlining the steps needed to get this working today. It strikes me that things are quite a bit easier today than before to get this working, but I did test these directions, and they work well.

I use Apache 2.0.52 with httpsrvr.dll. To get it working:

1. Install Apache (I will assume default directory structure for 2.0.52).
2. Copy 'httpsrvr.dll' to your /apache2/cgi-bin directory.
3. In httpd.conf, change the 'Options None' (again assuming defaults for 2.0.52) in <Directory "C:\Program Files/Apache Group/Apache2/cgi-bin"> to 'Options ExecCGI'.
4. AddHandler isapi-isa .dll to your httpd.conf.
5. Restart Apache.

Monday, 07 February 2005 21:32:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
DataSnap | Delphi
# Wednesday, 02 February 2005
I wanted a way to name threads to make it easier to debug, but since I'm still using Delphi 6 for the next month or so, I don't have access to the named thread object wizard in the Delphi IDE (as can be found in Delphi 2005). The code that is generated in Delphi 2005 when adding a named thread uses the technique found here:
MSDN article on naming threads

There are some other options, too. See this Google thread for a link to a CodeCentral entry and a technique for making setting the name more flexible.

Wednesday, 02 February 2005 17:50:00 (GMT Standard Time, UTC+00:00)  #    Comments [2] -
Delphi
# Monday, 17 January 2005
I just put the Delphi 2005 version of MIDAS.DLL up on my web site. I kept both the D7 and D2005 versions up on that page. If you have any problems using this newer version, please post a report to QC.
Monday, 17 January 2005 16:32:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
# Friday, 14 January 2005
2 interviews on BDNradio interviews with Ramesh Theivendran are now available. If you use Borland tools to access databases, you should definitely listen to these interviews.

Friday, 14 January 2005 20:46:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Tuesday, 28 December 2004
Atmapuri posted this message in borland.public.delphi.non-technical about his obsersavtions on memory leaks in Delphi 2005. He also posted some steps to disable Together to make the memory leaks go away, and also some information on what could be affected by doing this. Obviously, do this at your own peril.

The 10,000 foot view highlights from his post:


The severity of the memory leaks is proportional
to size of the source file being edited.

To completely resolve them:
Remove or rename the following packages
to disable Together:

"tgide90.bpl"
"borland.studio.together.dll"

in the \Bin directory.

It is not sufficient to disable Together from the Project
menu!!! The memory leaks due to Together will
happen as soon as you use the code editor and while
the Delphi is starting.

And here is the list of things he thinks get affected when you do this:


It affects ECO II modeling in Delphi.NET and C#. ECO II will still
work, but modeling will not. If you use W32 only
I think you dont loose anything.

Check the original post out for more detailed techincal information. Great stuff, Atmapuri!

Tuesday, 28 December 2004 15:53:00 (GMT Standard Time, UTC+00:00)  #    Comments [4] -
Delphi
# Thursday, 23 December 2004
Wow, Borland has been busy. They released a patch for Delphi 8 and a patch for Delphi 2005 in the past couple of days. I love seeing this kind of effort to get the tools as stable as possible. Thank you to the whole Delphi team for getting these to us!

As a quick bit of background, the Delphi 8 Update 3 patch fixes the problem with .NET 1.1 SP1 and is binary incompatible with previous Delphi 8 versions. The Delphi 2005 Update 1 patch solves the most pressing issues with the RTM version of Delphi 2005 (most notably, memory leaks!).

Thursday, 23 December 2004 19:34:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
I was reviewing some multi-threaded code for a friend last night. The code was causing problems, especially on a multi-CPU machine. The code was using global variables and no synchronization to protect access. So I did some searching on google and found some very good primers on threading in Delphi.

Thursday, 23 December 2004 16:46:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
# Wednesday, 15 December 2004
Here are the steps I use to debug an NT service that was created with Delphi 6. While this works for me, there may be room for optimization.

  1. Our applications differentiate between a service and server version by conditional define. If you do the same thing, be sure to remember to compile the server with your "SERVICE" directive in Project | Options
  2. In Project Options | Linker, check the options for "Inculde TD32 debug info" and "Include Remote Debug Symbols"
  3. Do a full build on the server
  4. Register and install the service (e.g. "mysrv /regserver" and "mysrv /install") . The regserver part is needed if your serivce is a COM service.
  5. Set breakpoints where you want them in the server code
  6. Open up the Services Control Panel applet
  7. Select the server in the applet, and press Start
  8. *VERY QUICKLY*, go back to Delphi and select Run | Attach Process, select the service executable (e.g. MySrv) and press Attach
  9. You may get a CPU view in the Delphi IDE at this point. If so, press F9 again.
  10. You should be able to run a client now and have the breakpoints be respected. You may be presented with the CPU view when the breakpoint is tripped. Just go to the source window, and everything should work as normal.

NOTE: The reason for the CPU view is due to a MS problem in ntdll.dll. For some reason, they shipped Win2k and above with a hard breakpoint in the file. I used this Delphi expert, and things worked much better. You may need this file in order to get debugging working. Then again, you might not. ;)
Wednesday, 15 December 2004 15:44:00 (GMT Standard Time, UTC+00:00)  #    Comments [2] -
Delphi
# Tuesday, 14 December 2004
I've had limited experience using unit testing, but I have read a considerable amount on the subject - especially as it pertains to Java. Most of the time, I want to add unit testing, but it's always a hard sell to management. It's an even tougher sell to management when you come in to a project that has been selling. After all, "The product works fine. Sure, it may have some bugs, but we'll fix those in due time.". I come from a more proactive school of thought where I'd rather we find ways to prevent bugs in the first place, and have the code be maintainable enough to fix it when a bug does creep in.

That's where unit testing comes in. If you can find a way to automatically identify bugs before they get into your codebase, you're much better off. Additionally, writing unit tests typically results in writing more maintainable code. After all, I would hope that you wouldn't want to write one test case to exercise a 3,000 line function. :-(

All of the above is just an introduction to John Kaster's BDNTV episode on unit testing in Delphi 2005. Check it out. It's short, but still very informative.

Tuesday, 14 December 2004 18:48:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Friday, 10 December 2004
I've been playing with AQTime for a little over 1 week now. All in all, I love it. My company bought 2 licenses because we saw the potential in the product.

Here are the things I like so far:

  • Support. The people on their newsgroups are fantastic.
  • The GUI. They make liberal use of DevExpress components throughout, and do so quite capably.
  • The new Assistant window that helps guide you through the profiling process.
  • The samples and documentation.

The things I don't like so far are greater - not necessarily in number, but in quality.
  • Leaked Delphi objects are reported as "VCL native memory.N", where N is the number of VCL objects that have been created so far. AQTime 3 reported the actual object name here.
  • If you use run-time packages (at least with Delphi 6), AQTime 4 won't show you any leak information. AQTime 3 showed me leak information without fail. According to Atanas, the next version of AQTime 4 should fix this problem.
  • In AQTime 3, they used to have a profiler called Reference Count Profiler. This would help you identify bad AddRef/Release lifetimes. The claim is that AQTime 4 can get by without this, but I'm not sold yet - especially given the first 2 points.
  • I have a support issue open trying to get DCOM Server support done properly in AQTime 3.

Now, I realize that I'm just coming up to speed with this product, and this may very well be a case of RTFM and/or learning curve. I reserve the right to move things from the "like" to "don't like" category as I learn more about this very powerful product. But the bottom line is that, for most of our profiling right now, we are using AQTime 3, as it seems a bit more capable than AQTime 4.

During my perusal of their newsgroup, I noticed a little tidbit that said something to the effect of: AQTime 4 is a new version of AQTime that merges together the old Unmanaged profilers and the newer .NET profilers. During the effort to get AQTime 4 produced, they understandably looked for ways to improve the product. They probably shared new codebases, consolidated the list of profilers, etc. The combination of all these things means that I'm waiting for patches to AQTime 4 before I can use it to it's fullest extent. I know of Atanas' well-deseverd and excellent reputation in the Delphi community, so I'm not worried that I'll never get what I want. I just hope it's sooner rather than later. ;-)

Friday, 10 December 2004 20:48:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Thursday, 09 December 2004
Everything is set. Here is the information on the Delphi 2005 launch event in Milwaukee.

John Kaster will be presenting a free session on Delphi 2005. It will run from 6:00-8:00 pm on 12/21/04 at the Marriott West in Pewaukee. That page should get you there with only slight problems. For some unknown reason, the civil engineers in the area decided to rename a bunch of highways out here, so pay close attention to the orange signs under the exit signs. "Former Highway 164 North" is now Highway F North, I believe. Or, just pay attention and take Exit 295 (as they mention in the directions on the web page).

In addition to getting some great information, John will be bringing Christmas spirit with him, and act as Santa Claus for the evening. There will be free T-shirts and other goodies to give away, flyers and marketing materials on Delphi 2005, feedback forms to let Borland know what we think, rebate coupons good for 25% off Delphi 2005 purchases (must be present to receive one; they are time limited (no immediate details - John thought they might be good through the end of this month or next, and John also thought it covered up to 4 copies - read the form when you get there for the final word ;-)), a drawing to give away a Delphi 2005 Architect, and depending on attendance, a drawing for 1 or more Delphi 2005 Pros. How's that for a run-on sentence?!? :-)

I will be emailing everyone on my list with this same information. Feel free to email me if you need to change your RSVP, or if you know of anyone else who wants to show up. It looks like we're going to have a great turnout. Thanks everyone, and I look forward to seeing you all there!

Thursday, 09 December 2004 15:28:00 (GMT Standard Time, UTC+00:00)  #    Comments [6] -
Delphi
# Saturday, 04 December 2004
Using DBX, we came across a bug where the values for the fields in the first record were not available in the OnCalcFields event. After doing some digging, I found that this was already reported as QC 1328. I noticed that the problem was marked as Fixed in Delphi 2005, and we are still using Delphi 6, so that would explain it.

I then went and modified my own test case, and the one reported in QC and noticed that both of these test cases still exhibited the problem in D2005. I talked to a couple of friends in Scotts Valley, and they confirmed the behavior as still being present in Win32 in D2005. So, it turns out that the fix only made it to the VCL.NET side of things. At least that source is similar enough to figure out what's going on.

The problem exists in TDataset.GetCalcFields. Take a look at the VCL.NET implementation in D2005 for the complete code. However, I still needed to get the fix into our codebase, and we won't be upgrading everything to D2005 for a little bit yet. We also can't just patch the VCL in the DB.pas unit since we are using runtime packages on our app servers in order to pass VCL components around.

With all of these requirements in place, I set out to create a descendant of TSQLQuery and override GetCalcFields. This turned out to be problematic, however, due to the fixed code's need to set the FEOF private variable in TDataset. I realized that the EOF property is read-only and that the backing field is private, so I used Hallvard's Hack #1. This works absolutely flawlessly, and I now have the fix isolated to one place so that when we upgrade to D2005, I can simply remove this new component in the handful of places that it gets used.

Here is the implementation code that I finally came up with:


type
  TDatasetEx = class(TDataset)
  published
    property EOF;
  end;

procedure TDRMSQLQuery.SetEOF(Value: boolean);
begin
  PBoolean(Integer(Self) + (Integer(GetPropInfo(TDatasetEx, 'EOF').GetProc) and $00FFFFFF))^ := Value;
end;

{$WARNINGS OFF}
// We are guaranteed that the only time we both set and use eofBak is when
// State = dsInactive. Therefore, we don't care about the warning.
procedure TDRMSQLQuery.GetCalcFields(Buffer: PChar);
var
  eofBak: Boolean;
begin
  if (State = dsInactive) then
  begin
    eofBak := EOF;
    SetEOF(False);
  end;

  try
    inherited GetCalcFields(Buffer);
  finally
    if (State = dsInactive) then
      SetEOF(eofBak);
  end;
end;

Notes:

  • I would fully expect that Borland will fix this fully in an upcoming D2005 patch.
  • Thanks to Joerg and John (from Borland) for helping out with this one.
  • The problem does not exist in the BDE, just DBX. I haven't checked other data access layers (ADO, IBX, DOA, etc.), but if they have the same problem, then you can use something like this technique to fix it for now. Since the true fix is in TDataset, all other implementations will be fixed automatically when that fix is rolled out.
Saturday, 04 December 2004 19:41:00 (GMT Standard Time, UTC+00:00)  #    Comments [2] -
Delphi
# Friday, 03 December 2004
I couldn't help myself. There I was, looking at the iPods, and the next thing you know it's purchased and at my house. There must be some kind of mind control device on those things! Still, it is one incredible mp3 player. I wrote a couple of small utilities to interface with iTunes via the COM SDK. Of course, I used Delphi to write the code. :-)

I just came across this blog entry, talking about managed access to iTunes. If you follow the links from that page, there are some more code samples.

Friday, 03 December 2004 21:07:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
# Thursday, 02 December 2004
Thanks to Steve Trefethen, who pointed me to this link:
Delphi 2005 Architect Packs A Wallop

All in all, a very positive review of Delphi 2005. I've been pretty happy with how things have been working here, too. Although, to be fair, I'm not using it as my primary IDE right now. Basically, I've upgraded custom components, compiled smaller utilities, etc. in preparation for the migration of the entire team to go to Delphi 2005. We can't even begin to think about that, though, until DevExpress provides D2005 versions of their libraries.

Thursday, 02 December 2004 19:34:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
# Thursday, 04 November 2004
One of the big dilemmas programmers face every day is "Should I write this code as quickly as possible or should I write it to be as robust as possible?". Deadline pressures make some pick the quick option too often. :-( That's why I was pleasantly surprised to find CodeSmith. CodeSmith is a template-based code generator that can output code that can then be used in any ASCII based language (e.g. Delphi).

During development, there are plenty of times when you need to create a collection of objects. For example, you have a TCustomer object and need a TCustomerList object to store a list of those objects. You have a lot of options on how to represent this. Here are some of those options:

  • Place the TCustomer objects in a TList. You need to typecast the code, and you can put any object in the list. Plus, you have to worry about object lifetimes.
  • Use a TObjectList instead of a TList. This allows you to not worry about object lifetimes, but the rest of the problems using a TList still exist.
  • Use a TStrings object (e.g. TStringList), and add your object in to the Objects property
  • Create a custom list class, where you don't have to worry about type safety or object lifetime

Unfortunately, the last option is rarely chosen due to time constraints. It doesn't take all that long to do, but if you have to stop what you're doing to create the custom class, it doesn't get done more times than not.

To solve that, I created a CodeSmith template to generate strongly typed collections. Download my template, install CodeSmith, play with it, and let me know what you think. I can think of some nice improvements, like adding comments (along with an option to include those comments or not). After I get some feedback on this, I plan to upload it to the CodeSmith File Share forum.

Thursday, 04 November 2004 23:08:00 (GMT Standard Time, UTC+00:00)  #    Comments [4] -
Delphi
# Tuesday, 02 November 2004
On December 21, 2004 from 6:00-8:00 pm, John Kaster will be coming to Milwaukee, WI to show off Delphi 2005. Location is still to be determined, but most likely will be in the Pewaukee/Waukesha/Brookfield area. The event is free, and if you're using Borland tools (or want to know more about life outside the MS camp), you should really find a way to get there.

Please send me an email if you are interested in attending. Response has been fantastic so far. Let's welcome a transplanted Wisconsinite back home in style!

Tuesday, 02 November 2004 15:38:00 (GMT Standard Time, UTC+00:00)  #    Comments [3] -
Delphi
# Monday, 01 November 2004
While I was thinking about how best to provide a write-up on getting COM Interop working with my pluggable COM architecture, I came across this Delphi 8 paper and this Delphi 2005 paper by Brian Long (of Falafel) in one of his blog entries. It's rather complete from a Delphi perspective, so all that I would need to cover would be how to link a Delphi Win32 COM host with a VS.NET C# client. Somehow, I don't think that's nearly as exciting, since Brian did such an excellent job detailing COM Interop.

Hopefully this will give me a chance to talk about my custom COM audting/profiling solution instead.

Edited on 11/2/04 to update the links based on feedback from Brian Long. Thanks, Brian!

Monday, 01 November 2004 19:12:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Wednesday, 27 October 2004
This is not YAGDSP (Yet Another Google Desktop Search Post). Well, not completely. ;-)

I like Google Desktop Search. It's lightning fast, and I like the idea and implementation. I think it will really get better once they release an API. For some reason, I never really latched on to IndexServer. I'm not sure why, it just seems too cumbersome or something. I like the concept of IndexServer, but I always end up using grep instead. Maybe it's just that Old Habits Die Hard.

But with GDS, I find myself using it more for some reason. However, as a Delphi or C# developer, you probably aren't deriving much benefit from this version of the GDS beta since pas and cs files are not indexed. Lord CRC (sorry, don't know his actual name) solved that problem by hacking the EXE to change the file extensions that are indexed. He also puts in some information on what you can do to fix this yourself if you're uncomfortable with using his hacked EXE. You can download the hack here.

Wednesday, 27 October 2004 18:07:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Wednesday, 20 October 2004
We have several DataSnap (ugh, it still pains me to say that instead of MIDAS :-)) servers in our application suite. We can run them either as regular processes or services. Recently, one of our customers went to Windows 2003 and our applications quit working. It turns out that Microsoft made some changes in Win2003 that causes a "Server Execution Failed" error when running a COM server as a service.

While searching Google, I found one post from Anders Evensen, who had contacted Microsoft directly about the issue. According to him, "The problem is that Microsoft requires StartServiceCtrlDispatcher to be called before CoRegisterClassObject. However, this requirement has not been implemented in earlier versions of Windows than 2003. Windows 2003 checks that the service has been started before it allows CoRegisterClassObject to be called."

Delphi does this backwards, since StartServiceCtrlDispatcher is called in TService.Execute, and CoRegisterClassObject is called during the call to Application.Initialize. I ended up with the following solution, and things now work wonderfully on all Windows versions.


function IsRunningInInstallMode : Boolean;

  function FindSwitch(const Switch: string): Boolean;
  begin
    Result := FindCmdLineSwitch(Switch, ['-', '/'], True);
  end;

begin
  Result := FindSwitch('REGSERVER') or FindSwitch('UNREGSERVER')
    or FindSwitch('INSTALL') or FindSwitch('UNINSTALL');
end;

procedure TService1.ServiceExecute(Sender: TService);
begin
  { Windows 2003 Server support. The problem is that you should always call
    StartServiceCtrlDispatcher (done in VCL) before calling CoRegisterClassObject
    (done from Application.Initialize). Win2003 now enforces this explicitly, but
    earlier versions did nothing if this was done improperly. }
  if not IsRunningInInstallMode then
    SvcMgr.Application.Initialize;

  while not Terminated do
  begin
    ServiceThread.ProcessRequests(false);
    Sleep(500);
  end;
end;
Wednesday, 20 October 2004 22:51:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Friday, 15 October 2004
If you've ever developed an ActiveForm, then you probably have run into the scenario where you go to a standard control in your form (e.g. an Edit control), select the text, and then press Ctrl+C to copy the text. However, I'm willing to bet that your next reaction was stunned amazement when you realized that the text was not actually copied to the clipboard. To make matters even more maddening, if you use Ctrl+X or Ctrl+V in that very same edit control, things work as you would expect.

I had to fix this problem recently, and it is awfully strange. Pressing Ctrl+X and Ctrl+V makes the TActiveFormControl.TranslateAccelerator method return S_FALSE. Pressing Ctrl+C, the return is S_OK. So in order to fix this, I want to return S_FALSE when Ctrl+C is pressed. I do this by creating a new TActiveXControlClass that overrides the TranslateAccelerator method and returns S_FALSE when needed. The only other thing that needs to be done is to use the new class in your TActiveFormFactory.Create call so that we can get calls to our TranslateAccelerator method and do our own custom stuff.

One caveat: Things are working quite well in IE6. I'm not sure what will happen in other ActiveX containers. Use at your own risk, or at least test in whatever container you're deploying to first!

Here's the code. Please let me know what you think. If you see any improvements that can be made, I'd love to hear about those, too. But at least, for now, my customers are happy again.


type
 TTranslateAcceleratorFormControl = class(TActiveFormControl, IOleInPlaceActiveObject)
  function TranslateAccelerator(var msg: TMsg): HResult; stdcall;
 end;

{ TTranslateAcceleratorFormControl }
{ 
When pressing Ctrl+C, the inherited method returns S_OK meaning that the container thinks
it did what it needed to, and therefore, the message is consumed. When pressing Ctrl+X or
Ctrl+V, the return from the inherited method is S_FALSE.

Therefore, we will always call the inherited method to allow for default processing. If the
return is S_OK, and the message is WM_KEYDOWN, and we have pressed Ctrl+C, we override
the result and return S_FALSE instead.
}
function TTranslateAcceleratorFormControl.TranslateAccelerator(var msg: TMsg): HResult;
begin
  Result := inherited TranslateAccelerator(msg);

  if Result = S_OK then  
  begin
    if (msg.message = WM_KEYDOWN) and (GetKeyState(VK_CONTROL) < 0) and (msg.wParam = 67) then
      Result := S_FALSE;
  end;
end;

initialization
  TActiveFormFactory.Create(
    ComServer,
    //TActiveFormControl,             // standard Delphi-generated class
    TTranslateAcceleratorFormControl, // our replacement class
    TAFActionTest,
    Class_AFActionTest,
    1,
    '',
    OLEMISC_SIMPLEFRAME or OLEMISC_ACTSLIKELABEL,
    tmApartment);
end.
Friday, 15 October 2004 19:56:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Wednesday, 13 October 2004
I've written some about this interface, and how it allows you to grab data from an HTML page and use that data inside an ActiveX control. In a nutshell, your HTML will have an OBJECT tag for the ActiveX control and - optionally - some PARAM tags inside the OBJECT tag. One thing to keep in mind is that the IPersistPropertyBag methods will only fire if you actually define at least one PARAM tag. Without a PARAM tag, the code will not execute.

The moral of the story is: if you have some kind of initialization code in your IPersistPropertyBag methods, be doubly sure that you define a PARAM tag!

Which reminds me, now that Delphi Informant is no longer in business, I'll see what I can do to get my articles published with them posted on my web site. I'll post an entry here when I can do that.

Wednesday, 13 October 2004 17:49:00 (GMT Daylight Time, UTC+01:00)  #    Comments [2] -
Delphi
In SysUtils, there is a handy function (CheckWin32Version) to check that you are running at least a certain version of Windows. Unfortunately, it is wrong. For example, let's say you want to make sure you are running Win2000 or greater. The following code should do just that:

  if CheckWin32Version(5) then
    CallMyWin2000OrGreaterFunctionHere;
When running on XP, the major version will be 5, and the minor version will be 1. These are stored in writable constants in SysUtils. Inside CheckWin32Version, the check for AMinor is backwards, so your results will be wrong.

I am now using this function instead and everything works fine:


function FixedCheckWin32Version(AMajor: Integer; AMinor: Integer = 0): Boolean;
begin
  Result := (AMajor < Win32MajorVersion) or
            ((AMajor = Win32MajorVersion) and
             (AMinor <= Win32MinorVersion));
end;

Just a side-note: This was fixed in Delphi 7 and above. Of course, if you're using D6, you'll need to use this.

Wednesday, 13 October 2004 17:38:00 (GMT Daylight Time, UTC+01:00)  #    Comments [4] -
Delphi
# Friday, 24 September 2004
I'm a little partial to Borland DB technologies, and especially BDP. ;-) However, I think this demo shows some of the power that BDP has in the upcoming version of Delphi. Check out the BDNtv episode presented by Jason Vokes and see what you think.

I saw many other properties and methods in this demo that weren't touched on. All in all, BDP looks incredibly flexible in Diamondback - especially when either doing multi-tier apps, or using data from heterogenous sources.

Friday, 24 September 2004 15:51:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Thursday, 23 September 2004
By default, COM Servers register themselves in HKEY_CLASSES_ROOT (HKCR). The actual registry entries will get written to HKEY_LOCAL_MACHINE (HKLM) in most cases. HKCR is an alias to HKEY_LOCAL_MACHINE in older versions of Windows, but starting in Windows 2000 and later, HKCR is a merged view of HKLM and HKEY_CURRENT_USER (HKCU). I needed a way to register my COM server into HKCU so that locked down machines that don't have access to HKLM could still run properly. For some background information on HKCR, check out MSDN.

I came across the function RegOverridePredefKey, and it looked like it would do the job by allowing me to hook into a root HKEY and write to a completely different HKEY. So I started looking in the Delphi source code, and noticed that this function was not imported. The following code snippet shows the procedure that I ended up writing to have anything written to HKCR be written to HKCU instead.


function RegOverridePredefKey(hKey: HKEY; hNewKey: HKEY): Longint; stdcall; 
  external advapi32 name 'RegOverridePredefKey';

procedure OverrideRegistryKey(Register: boolean);
var
  HKCU: HKEY;
  ret: integer;
begin
  if Register then
  begin
    RegOpenKeyEx(HKEY_CURRENT_USER, 'Software\Classes', 0, HEY_ALL_ACCESS, HKCU);
    try
      ret := RegOverridePredefKey(HKEY_CLASSES_ROOT, HKCU);
      if ret <> ERROR_SUCCESS  then
        ShowMessage('Error overriding HKCU:' + #13#10 + SysErrorMessage(ret));
    finally
      RegCloseKey(HKCU);
    end;
  end
  else
    RegOverridePredefKey(HKEY_CLASSES_ROOT, 0);
end;
So that left me with the question "Where do I call this?". My first thought was to hook into the UpdateRegistry method and call my OverrideRegistry procedure there. Unfortunately, by the time UpdateRegistry is called in my Remote DataModule (RDM), several other registration calls have already finished. This left me with some things in HKLM and some in HKCU. Then I remembered that the COM registration occurs when Application.Initialize is called in the dpr file. The following is the code I use in my dpr to write to the right place in the registry.

  OverrideRegistryKey(true);
  try
    Application.Initialize;
  finally
    OverrideRegistryKey(false);
  end;
After doing this, I checked in regedit and verified that everything was registered in HKCU, and it was there. Since HKCR shows the merge of HKLM and HKCU, it was also present in HKCR. This means that COM calls will find the app server. Running a quick test showed me that everything worked as I expected.

In my production code, I'll also put in a quick check to make sure that we're running on Windows 2000 or greater. Since we run both as a Server and a Service, I'll put this code in a conditional define, since there is no concept of HKCU in a service.

Note: I also failed to find the header for RegOpenUserClassesRoot in Delphi. If you find that you need this function, you'll need to import the function yourself.

Thursday, 23 September 2004 22:48:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Wednesday, 15 September 2004
Take a look at this sneak peek of DiamondBack presented by John Kaster. Major things I noticed just in this 14 minute demo were:
  • Language bundling announement (C#, Delphi, Win32)
  • for..in syntax (similar to C# foreach syntax)
  • A new start page (to replace DelphiDirect, most likely)
  • Refactoring support (available in all languages)
  • Error Insight
  • Help Insight
  • History view (including smart diff engine. Similar to JBuilder's feature of the same name - very nice)
  • Tight StarTeam integration
  • Free floating form designer (a la Delphi 7 and below)
  • Delphi Win32 was enhanced to support many extensions created in Delphi .NET
Wow!! It seems pretty full-featured, and I didn't even see all of the features first-hand at BorCon. I can't wait to get my hands on it. Well done, John and Borland.
Wednesday, 15 September 2004 20:05:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Tuesday, 14 September 2004
Yup, it's time to talk about ActiveForms again. :-)

If you're using ActiveForms, then you need to know about an ugly little secret. If you use MyDelphiForm.ActiveControl := Edit1, then you will get an exception stating 'Cannot focus a disabled or invisible window'. The reason for this is due to the implementation of TCustomForm.SetActiveControl. This setter method is called when you try to set ActiveControl on a form. The first thing it does is this:


    if not ((Control = nil) or (Control <> Self) and
      (GetParentForm(Control) = Self) and ((csLoading in ComponentState) or
        Control.CanFocus)) then
      raise EInvalidOperation.Create(SCannotFocus);
Note the part where it is trying to assert that the root ParentForm for the Control you are trying to set focus to is the same as the current form. This works fine in regular windows applications, but unfortunately in ActiveForms, the root ParentForm is the actual ActiveForm. Most people use that form as nothing more than a container to then host their forms inside. I wrote about this technique in a previous Delphi Informant, and it has been posted plenty of places on the Internet as well (e.g. Conrad Hermann had the first mention of this that I can remember).

The workaround is to use code like this to set the ActiveControl:


var
  ParentForm: TCustomForm;
begin
  ParentForm := GetParentForm(Self);
  ParentForm.ActiveControl := Edit1;
Tuesday, 14 September 2004 16:37:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Thursday, 09 September 2004
An ActiveForm will not receive a WM_ACTIVATE message when initializting since WM_ACTIVATE only goes to the top level windows, or in this case, the IE browser window. By sending our own WM_ACTIVATE, we get initial focus set to the ActiveForm, but more importantly, the WMActivate method in TCustomForm calls TCustomForm.SetActive. This in turn, sets the Active property which means that the form has focus. This is important later on, e.g. in TCustomForm.SetActiveControl, the focus will never be set to the ActiveControl.

For example, the code below will do nothing in the default case. It will work fine after you apply the work-around mentioned later on.


var
  ParentForm: TCustomForm;
begin
  ParentForm := GetParentForm(Self);
  ParentForm.ActiveControl := Edit1;
end;

The bad behavior is also apparent when using TPageControl, since TPageControl.ChangeActivePage tries to set the ActiveControl when changing pages.

The simple work-around for this is to call the following in your ActiveForm code: PostMessage(Handle, WM_ACTIVATE, WA_ACTIVE, 0);

Since I use PARAM tags in my OBJECT tag to pass parameters, I am doing this in IPersistPropertyBagLoad method after creating my Delphi form. After doing this, everything is working great.

Thanks to Steve Trefethen for listening to my original vent. :-)

Thursday, 09 September 2004 16:01:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Sunday, 13 June 2004
Even though this is a .NET post, the concept absolutely transfers to classic Delphi as well. The other day, I needed to solve a problem in C#. Given a pseudo-code architecture like this:

class Base {}

class Concrete : Base {}

class Normal
{
  public Concrete ConcreteProp { get; set; }
}
and usage like this:

Base prop = new Concrete();
Normal obj = new Normal();
obj.ConcreteProp = prop;
I would receive (rightfully) a compiler error. Now, understand a few points before firing off comments.
  • The architecture must remain as-is. The classes above are actually FCL classes which I obviously can't change.
  • The usage needs to remain similar to the one shown above. I want to be able to store a reference to any class that descends from Base and use it in Normal.ConcreteProp later on.
  • Obviously, I could just throw a typecast in here to make things work, but this is the end-result of the test case. My requirements also dictated usage of Activator.CreateInstance and Reflection. I can't add references and typecasts to things I don't know about, so this must be truly dynamic.
.NET (and Delphi) don't have any concept of dynamic typecasting. So in order to solve the problem, I ended up taking a different approach. By using an extra Activator.CreateInstance to create a new Concrete obejct, I can then use that new object to assign over with no problems. After creating the object, I then copy over all of the base properties to the newly created object. So far, this has worked pretty well. However, it is not without it's downsides. The main drawbacks I see to this approach are:
  • I am not using the same object reference I stored, which means more memory usage
  • I cannot easily assign all properties from the cached Concrete object to the newly created one. In order to do this fully, I'll have to use Reflection to walk through all of the properties and set them all (if possible). For now, I know which properties absolutely must be assigned, so I just assign those.

Edited to add: OK, so I butchered the problem description in my haste. Mea culpa. I think I just stumbled into one of my own pet peeves. :-) Here's the actual code that doesn't work in case you're interested:


System.Data.Common.DbDataAdapter adapter = sqlDataAdapter1; 
System.Data.SqlClient.SqlCommandBuilder cb = new System.Data.SqlClient.SqlCommandBuilder(); 
cb.DataAdapter = adapter; 

 

Sunday, 13 June 2004 22:49:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Thursday, 10 June 2004
This may be old news to some of you, but I just came across a paper titled Simple Application Framework for VCL by DevExpress. DevExpress has some killer third-party products, including the QuantumGrid. They offer support on a variety of platforms and languages, including Delphi, Kylix, ActiveX, and .NET. Their components work very well, are well-designed, and add pizazz to your application. This paper covers some best practices for Delphi application development. However, if you're a .NET guy, they have a version of the paper for .NET, too.
Thursday, 10 June 2004 06:02:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Monday, 24 May 2004
Hallvard Vassbotn has started blogging. Long overdue, IMO. Delphi programmers would be insane not to keep up with his blog as just about every single thing he posts to the newsgroups is pure gold.
Monday, 24 May 2004 15:47:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Thursday, 20 May 2004
If you were affected by the "persistent field size mismatch exception" introduced in Delphi 7.1, then take a look at the new public beta that Borland released.
Thursday, 20 May 2004 16:13:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Wednesday, 19 May 2004
I fully believe that having a way to automate your builds is the only way to guarantee good results when building your product. The more complex your project becomes, the more complex your build becomes. This is a bad thing for several reasons, the 2 foremost being that the build becomes time-consuming and error-prone due to manual interaction. Of course, the corollary is that when you get to that stage, there is typically only one (maybe 2) person(s) that understand how to do a full build of your product. Using an automated build tool can help the entire team and the quality of your product immensely.

I have spear-headed the effort to automate builds several times now. Sometimes, it is met with resistance. Sometimes, it is encouraged. But in every case, when the build is automated the end-result blows people away. Comments like "Wow, doing a build is that easy?" are frequently heard. :-) After getting an automated build into your organization, people start seeing many other opportunities to fine-tune the build. "Oooh, wouldn't it be nice if we could schedule this to run at midnight every night?" or "Let's have the build run the test scripts automatically for us" or "Hey, we can add this other utility into the build process to ensure that datasets are closed at design-time!". The results are always well worth the effort. At one company, I took a completely manual build from 4 hours down to double-clicking and 45 minutes. The added benefit of not forgetting steps also made the build more reliable the first time. I imagine results like these are more common than not.

There are several tools out there, and here's my simple list of tools that I find useful. All of them are extensible in the sense that you can create your own actions and have those actions execute whatever you want during the build. In my opinion, that is the absolute best part of creating the build script!

ProductLanguageNotes
FinalBuilderMultiple (Delphi, BCB, VS.NET)Commercial, but absolutely fantastic
AntJavaOpen Source
NAnt.NETOpen Source. Knock-off of Ant
Even if you end up crufting your own automated make together with BAT files and make, it's worth it. So quit standing on the sidelines and get cracking! If you know of other build tools that you find useful, please leave a comment and I'll update the main page.
Wednesday, 19 May 2004 20:24:00 (GMT Daylight Time, UTC+01:00)  #    Comments [6] -
Delphi
# Wednesday, 12 May 2004
The new Delphi 7.1 update contains a new version of MIDAS.DLL (version 7.1.1692.666). Dave Rowntree did some digging and came up with this list of fixes in MIDAS in Delphi 7.1. (Thanks, Dave!!!) Note that he cross-referenced the fixes to the QualityCentral entries. So I would say this is (yet) more proof that QC is working. You can download the newest version of the dll from my web site. It should be backwards compatible with Delphi 5 and 6. I will update this entry with any fixes that were missed, or any new issues that got introduced.

Fixed in Midas.dll
1712 - TFieldAggregate returns incorrect value
2019 - nested CDS Edit/Post twice, master ApplyUpdates, Delta incorrect
2027 - Edit/Post on nested CDS InternalCalc field causes ChangeCount to increase
2333 - Incorrect Delta after ApplyUpdates(0) with poPropogateChanges
2529 - Opening .XML files using D7 Midas.dll that were created using D6 Midas.dll fails
2626 - Inability to use ftLargeInt as an index with a tClientDataset
2717 - Incorrect ChangeCount with TWideStringField
3777 - InternalCalc fields are reset to null by ApplyUpdates when using poPropogateChanges
4301 - CancelRange on nested ClientDataset breaks link with masterrecord
4508 - ChangeCount being incremented when data has not changed.
4676 - Size of Blobfields not adjusted when using RefreshRecord
5319 - Client Dataset Locate method on a Word field always returns false
5509 - MIDAS allocates values against nested dataset AutoInc fields.
5646 - Nested TClientDataSet deletion fails if child has CloneCursor has been invoked
6591 - TBlobfield looses value when LogChanges = False
6849 - ClientDataSet insert duplicate's details record's with NestedDataSet

Fixed in Midas.dll + VCL
2011 - 'key violation' - InternalCalc field in nested CDS/poFetchDetailsOnDemand
3786 - ClientDataSet.FindKey on cloned cursor fails
3496 - ClientDataSet Filter on a LargeIntField - Design / Run Time Errors.
7543 - MIDAS corrupts indices

Wednesday, 12 May 2004 15:56:00 (GMT Daylight Time, UTC+01:00)  #    Comments [14] -
Delphi
# Tuesday, 11 May 2004
Now that you have the new Delphi 7.1 update, you may be wondering what fixes were done to the ActionBands. Steve Trefethen - the Borland R&D engineer responsible for these components - updated the Unofficial ActionBand Fixes to work with Delphi 7.1 and explained why they were included this way. While it's not exactly great news that the fixes weren't included inside Delphi 7.1, it is good to see that Steve took the effort to make sure that we could still get the fixes with the new version. Thanks much for your efforts, Steve!
Tuesday, 11 May 2004 15:36:00 (GMT Daylight Time, UTC+01:00)  #    Comments [4] -
Delphi
# Saturday, 08 May 2004
My article, Replacing TabSheets with Frames is now up on the BDN website. This article summarizes issues that you will likely find when designing an application using TPageControl components. The article presents some background information and solutions to problems by using TFrames, interfaces and inheritance. Give it a look, and leave any comments here (or on the BDN site). Be sure to rate the article, too!

Now might be a good time to subscribe to the Delphi BDN RSS feed, too.

Saturday, 08 May 2004 17:45:00 (GMT Daylight Time, UTC+01:00)  #    Comments [11] -
Delphi
# Friday, 07 May 2004
Are you a hard-core Delphi programmer that doesn't speak a lick of C#? If so, John Kaster, from Borland, wrote this article to talk about BabelCode (along with a nice sample of creating web services in Delphi 8). BabelCode is a web service that converts C# code to Delphi code using the CodeDOMs from C#Builder and Delphi 8 for .NET. I don't think any programmer who wants to write .NET code should overlook learning C#, but until you do, you can use the web service in the article to translate the massive amount of information out there to the language of your liking. The other benefit of using BabelCode is that since the CodeDOM is central to the IDE, if you find a bug in the translation of code, you most likely have also found a bug that will manifest itself in the Delphi IDE. So be sure to be a good citizen, and report any bugs you find when using BabelCode. Feel free to read my article on why you should report bugs if you'd like.
Friday, 07 May 2004 22:37:00 (GMT Daylight Time, UTC+01:00)  #    Comments [2] -
Delphi
# Wednesday, 05 May 2004
After a year off from BorCon, I'll be back this year again. I'll be presenting a couple of topics: "Win32 WebServices" and "Multi-tier Applications in .NET for Delphi Programmers". The "Win32 WebServices" talk will focus on using and writing web services with Delphi 7. The Multi-tier talk will cover how to create a multi-tier application in .NET. There will be some analogies between DataSnap and ADO.NET, with a dash of Remoting how-to.
Please feel free to email me or leave comments if there are aspects of these topics that you would like to see covered. Hope to see you there!
Wednesday, 05 May 2004 17:25:00 (GMT Daylight Time, UTC+01:00)  #    Comments [3] -
Delphi
# Monday, 03 May 2004
Some people emailed me to let me know that they were having problems downloading the files. Not sure how the problem came up, but it is fixed. The released projects have been updated on the DelphiVNC project. Since it's a SourceForge project, you can always get the latest files from CVS. Instructions for this specific project can be found here.
Monday, 03 May 2004 17:29:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Thursday, 29 April 2004
This is a nice example of using Delphi 8's new class operators. It converts temps between units easily.
Thursday, 29 April 2004 17:37:00 (GMT Daylight Time, UTC+01:00)  #    Comments [2] -
Delphi
# Wednesday, 28 April 2004
Borland has posted an updated license for Delphi 8. This new license explicitly allows the Borland.Delphi.dll assembly to be redistributed, which is especially important to component developers.
Wednesday, 28 April 2004 15:14:00 (GMT Daylight Time, UTC+01:00)  #    Comments [0] -
Delphi
# Monday, 26 April 2004
Marc Rohloff posted a pretty nice list copmaring and contrasting Delphi and C#. Feel free to add comments on other things here. I'll keep a running list going here.
Features C# has that Delphi doesn't
===================================
1) foreach
2) operators using, lock, checked and unchecked
3) block scoped variables
4) case statements with strings
5) assembly internal classes
6) namespaces can span code units
7) implicit array dimensioning: int[] a = new int{1,2,3};
8) ternary operator ( ? : )
9) can use classes from namespaces without importing the whole namespace
10) circular references are possible
11) try..catch..finally
12) assignment operators (+=, ++, -=, etc)
13) multi-file assemblies
14) You don't need to distribute Borland.Delphi.dll
15) Compiler warnings can be emitted if you omit XML documentation

Features Delphi has that C# doesn't
===================================
1) sub-range types
2) enums and sets are first-class types
3) class type support
4) virtual constructors
5) virtual class methods
6) nested procedures
7) non-default index properties
8) can defines constant arrays and records
9) resourcestring s
10) default parameters
11) variants
12) arrays with non-integral subscripts
13) sets with more than 64 elements
14) message handlers
15) unions (variant records)
16) untyped parameters
17) const parameters
18) class helpers 
19) smart linker 
20) named constructors 
21) array properties 
22) unmanaged exports 
23) super fast single pass compiler, compiling any project faster than C#, using less memory at the same time.

Edited to bring the comments up to the main page. Thanks for the input everyone!
Monday, 26 April 2004 21:10:00 (GMT Daylight Time, UTC+01:00)  #    Comments [53] -
Delphi
# Tuesday, 13 April 2004
I've seen some people bemoaning the "lack of innovation" from Borland. That got me thinking a bit, and that's never a good thing. :-)

The main argument people seem to make when arguing "lack of innovation" is something along the lines of "Delphi 7 isn't anything more than Delphi 6 with a bunch of 3rd party software packed with it" or "Even the MDA work Borland is doing was done via acquisition". While this is provably false, even if it were true, I don't see the problem. If a company looks at the develop vs. acquire decision (a logical extension to the build vs. buy decision) and comes to the conclusion that it is more cost-effective to acquire, then it is the smart thing to do. By recognizing that you are finite, and allocating your resources accordingly, you make wise business choices. To develop technology yourself despite a cost-effective alternative is nothing more than pride to the point of hubris.

Secondly, if one company - be it Borland of Microsoft - continually adds features to their product that are addressed by existing third parties, then they will cause a collapse in the third party market. After all, why should a third party innovate when they know that a company with deeper pockets will just come along and implement the same thing in their core product (more on this later). In addition, the same people that argue that Borland has just been acquiring technology argue that Borland should reinvent that exact same technology and bundle it into the core product. So they are effectively advocating that one company should address all needs for all developers. I would rather have choice and competition in the market to spur the industry to greater heights. Besides, making both of the first two arguments in the same post is illogical. Either you think a company should innovate everything, or it should innovate nothing. If you argue in between, then you are just arguing with which things got bundled in.

Lastly, innovation is only innovation in the short-term. In the long-term, the first product to market rarely gets a stranglehold on the market no matter how good it is. For examples look at Macintosh, IBM DOS, MIDAS, and countless other technologies. They were ahead of their time, but were not immediately accepted. Instead, they served as the thing that others wanted to "emulate". After the innovation had time to mature, another company came along and "borrowed" from that innovation. Typically, people who pish-poshed a technology (e.g. Delphi or MIDAS), come around and extoll the virtues of the new technology (e.g. .NET FCL or ADO.NET). So it isn't as much about innovation as it is about marketing or brand allegiance.

Tuesday, 13 April 2004 15:07:00 (GMT Daylight Time, UTC+01:00)  #    Comments [5] -
Delphi
# Monday, 29 March 2004
Steve Trefethen just posted this little tidbit on how to have Windows Search find your Delphi files. While I seem to recall seeing this behavior some time ago, I didn't dig into it and kept using my trusty grep to find text in files. Maybe this is what I needed to start using a GUI instead!
Monday, 29 March 2004 21:28:00 (GMT Daylight Time, UTC+01:00)  #    Comments [1] -
Delphi
# Wednesday, 17 March 2004
I've been playing with ObjectSpaces (OS) lately. I like the idea and the implementation is pretty good. The main limitations I see are:
  • For the foreseeable future, it will only talk to MSSQL (and it's various flavors). If they're writing an Object Persistence Framework (OPF), shouldn't someone have mentioned that they should probably abstract out the DB-specific stuff? I understand this is 1.0, but please, that is so limiting that it almost makes OS unusable. Do I really want to have DB lock-in to MSSQL? I don't think so.
  • It is tied to Whidbey. With the delay shipping Whidbey, this means that we won't see a production-capable version of ObjectSpaces until Q1 of 2005! That is a long time to wait to get OPF. Microsoft should find a way to unbundle certain pieces of the PDC preview - such as ASP.NET 2.0 and OS - and deliver them on a schedule that is independent of an IDE release.

At any rate, here is some good material to help get up to speed on OS:

I plan on writing some articles detailing my experiences (good and bad!) with OS, discuss alternative OPFs, including ECO (available in C#Builder and Delphi 8), and flesh out some best practices when using this kind of technology.

Wednesday, 17 March 2004 20:16:00 (GMT Standard Time, UTC+00:00)  #    Comments [0] -
Delphi
# Friday, 12 March 2004
Here is another open source project worth checking out. If you want to do some VNC development using Delphi, check this project out.
Friday, 12 March 2004 16:38:00 (GMT Standard Time, UTC+00:00)  #    Comments [5] -
Delphi
# Friday, 05 March 2004
I hate being required to do anything. Being required to be connected to the Internet to have an application work is especially ugly to me. To that end, I downloaded w.bloggar. It is a freeware application that allows you to enter your blogs in a Windows application and publish the entries to your blog server. It has worked rather well so far. However, in order to get this to work with .Text, you need to take a look at this. I had to make sure that the content handlers were working properly (I used the wrong web.config file) and that I could log in properly (I had the wrong value for the blog_config.Flag column). After that, the directions in the link are all you should need to get going. Be sure to add the Categories that you want in the .Text web interface too, as I couldn't find a way to add categories from w.bloggar.

I also took a brief look at BlogJet. It appears to be poised to be better than w.bloggar in many ways. The GUI is crisper. It supports .Text out of the box. However, I'm a bit concerned about the pre-beta status and having it time expire on March 15, 2004. They're also going to make it shareware as opposed to freeware. I'm OK with buying shareware, but it better add value over freeware apps then. w.bloggar will suffice for now.

Lastly, I did some digging on RSS aggregators. In my mind, FeedDemon stood head and shoulders above the rest. Plus, Nick Bradbury is a Delphi guy who writes amazing software (see HomeSite).

Friday, 05 March 2004 16:43:00 (GMT Standard Time, UTC+00:00)  #    Comments [1] -
Delphi
Navigation
Archive
<2017 June>
SunMonTueWedThuFriSat
28293031123
45678910
11121314151617
18192021222324
2526272829301
2345678
Blogroll
About the author/Disclaimer

Disclaimer
The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.

© Copyright 2017
Dan Miser
Sign In
Statistics
Total Posts: 388
This Year: 0
This Month: 0
This Week: 0
Comments: 630
Themes
Pick a theme:
All Content © 2017, Dan Miser
DasBlog theme 'Business' created by Christoph De Baene (delarou)