Thoughts from Dan Miser RSS 2.0
 Saturday, February 17, 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, February 17, 2007 10:08:54 AM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Tuesday, February 06, 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, February 06, 2007 1:29:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Monday, December 18, 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, December 18, 2006 9:42:22 AM (Central Standard Time, UTC-06:00)  #    Comments [2] -
Delphi
 Sunday, December 03, 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, December 03, 2006 5:38:00 PM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Tuesday, November 14, 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, November 14, 2006 11:40:00 AM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Monday, October 23, 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, October 23, 2006 3:27:00 PM (Central Standard Time, UTC-06:00)  #    Comments [5] -
Delphi
 Wednesday, September 13, 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, September 13, 2006 10:11:00 AM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Tuesday, August 29, 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, August 29, 2006 1:27:00 PM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Monday, August 21, 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.

Monday, August 21, 2006 9:40:00 PM (Central Standard Time, UTC-06:00)  #    Comments [2] -
Delphi
 Wednesday, July 05, 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, July 05, 2006 12:47:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Wednesday, June 28, 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, June 28, 2006 8:47:00 AM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Tuesday, June 20, 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, June 20, 2006 2:06:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Tuesday, May 30, 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, May 30, 2006 11:32:00 AM (Central Standard Time, UTC-06:00)  #    Comments [4] -
Delphi
 Friday, May 12, 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, May 12, 2006 2:29:00 PM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Tuesday, February 21, 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, February 21, 2006 1:04:00 PM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Friday, February 17, 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, February 17, 2006 2:33:00 PM (Central Standard Time, UTC-06:00)  #    Comments [4] -
Delphi
 Tuesday, February 07, 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, February 07, 2006 10:47:00 AM (Central Standard Time, UTC-06:00)  #    Comments [2] -
Delphi
 Friday, February 03, 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, February 03, 2006 5:12:00 PM (Central Standard Time, UTC-06:00)  #    Comments [8] -
Delphi
 Monday, January 16, 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, January 16, 2006 9:12:00 AM (Central Standard Time, UTC-06:00)  #    Comments [5] -
Delphi
 Thursday, January 12, 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, January 12, 2006 9:48:00 AM (Central Standard Time, UTC-06: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, January 12, 2006 9:03:00 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Wednesday, January 11, 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, January 11, 2006 9:03:00 AM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Tuesday, January 10, 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, January 10, 2006 9:24:00 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Monday, January 09, 2006
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.

Monday, January 09, 2006 8:08:00 PM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Tuesday, January 03, 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, January 03, 2006 2:07:00 PM (Central Standard Time, UTC-06:00)  #    Comments [7] -
Delphi
 Friday, December 16, 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, December 16, 2005 9:18:00 AM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Wednesday, December 14, 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, December 14, 2005 3:43:00 PM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Wednesday, December 07, 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, December 07, 2005 1:18:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Friday, October 21, 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, October 21, 2005 10:16:00 AM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Friday, September 23, 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, September 23, 2005 10:58:00 AM (Central Standard Time, UTC-06:00)  #    Comments [4] -
Delphi
 Saturday, September 10, 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, September 10, 2005 1:44:00 PM (Central Standard Time, UTC-06:00)  #    Comments [3] -
Delphi
 Friday, September 02, 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, September 02, 2005 10:24:00 AM (Central Standard Time, UTC-06:00)  #    Comments [2] -
Delphi
 Thursday, July 07, 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, July 07, 2005 1:07:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Thursday, June 16, 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, June 16, 2005 2:53:00 PM (Central Standard Time, UTC-06:00)  #    Comments [2] -
Delphi
 Friday, June 03, 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, June 03, 2005 10:21:00 AM (Central Standard Time, UTC-06:00)  #    Comments [4] -
Delphi
 Thursday, June 02, 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, June 02, 2005 8:12:00 AM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Tuesday, May 24, 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, May 24, 2005 2:18:00 PM (Central Standard Time, UTC-06:00)  #    Comments [1] -
Delphi
 Thursday, April 28, 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, April 28, 2005 3:59:00 PM (Central Standard Time, UTC-06:00)  #    Comments [0] -
Delphi
 Thursday, April 21, 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.