Why design-time components shouldn’t have install EXEs

Lots of Delphi components and .NET controls ship as EXEs that “install” the design-time component for you. I hate that, and here’s why.

  1. Revision-control unfriendly. I don’t want you sticking code under C:\Program Files. It’s code. It needs to be in source control with all my other code. That’s kind of the point of revision control. I want a ZIP that I can extract into my source tree and check in, so that later I can go back to that same point in time and rebuild my code as it was then.

    Xceed is the worst offender here. Good WinForms controls, but you can’t put them in Subversion — they have to be “installed” if you’re even going to be able to compile. You have to actually run their stupid installer on every development computer, including the automated build machines. I cannot express how deeply this offends me.

  2. Delphi-version unfriendly. If Delphi 2007 comes out and there’s no installer for FooComponentLibrary for Delphi 2007, what do you do? Sure, you can make a new package, compile it, and install. But you could do the same thing if you had a source ZIP, and you wouldn’t have to sit through an installer that might or might not work correctly if its intended version of Delphi isn’t installed.

    Of course, if it’s a Delphi component that ships without source code, then you’re screwed anyway, since DCUs aren’t compatible across Delphi versions. But no sane person would ever use a Delphi component that shipped without source code, mainly for this very reason. (Not an issue for WinForms controls, as compiled DLLs are much more version-agnostic than Delphi DCUs.)

  3. What about Turbo? The last Delphi version that I personally own is either 3 or 5, I don’t remember which. There’s no way I’ll use it anymore. I’ve been spoiled by strict private and records with methods. When I’m tinkering at home, I use Turbo Delphi Explorer, ’cause it’s free, and sort of recent. But it doesn’t support third-party design-time components. And it doesn’t have a command-line compiler. So what’s going to happen when the installer gets to the “compile and install the package” step? It wouldn’t surprise me if the installer failed and rolled back, leaving me back at square one. I try not to find out.

Component installers are probably meant to make life easier for hobbyists and beginning Delphi developers, but they make life harder for experienced developers — sometimes much harder. So component authors, please, provide a ZIP file. (And please, make it an actual, industry-standard ZIP, not a fringe format like RAR.) For Delphi components, the ZIP should contain the source code. For WinForms components, it could have either the source code, or the compiled DLLs. And in either case, of course you’re including sample code, right?

You can provide an installer too, if you must. But it should be a separate download.

(This rant was provoked by (un)DelphiX, which does provide both an installer and a source RAR (grr), but states fairly prominently that the source archive will be discontinued at some point in the future.)

Installing Subversion 1.4 as a Windows service

I just upgraded the command-line Subversion on my home laptop. The way you install Subversion as a service has changed since I wrote the Mere-Moments Guide to installing a Subversion server on Windows, so it’s time for me to blog the details (so I can find them if I need them again).

If you had an older SVN service

First, if you used to have Subversion pre-1.4 running as a service (either via the SVN 1-Click Setup installer, or by following the steps in the Mere-Moments Guide), you’ll need to uninstall it:

  • Open a command prompt.
  • Change into your Subversion bin directory (probably C:\Program Files\Subversion\bin).
  • Type svnservice -remove
  • It should report that it’s stopping (may take a while) and uninstalling the service.
  • Delete svnservice.exe.
  • Uninstall your old version of Subversion (may not be necessary, but I did).

Installing the new Subversion service

Starting with Subversion 1.4, there’s built-in support for running as a Windows service — but not for actually creating a Windows service.

I recommend downloading the Subversion installer directly from the Subversion downloads page on tigris.

Then, go read Configuring svnserve to Run as a Windows Service.

If you have Windows 2000

The above instructions should work on Windows XP and later. But if you have Windows 2000, you need to download sc.exe from Microsoft’s Resource Kit.

I did the research so you don’t have to. Here’s Microsoft’s download for sc.exe for Windows 2000.

C# 3.0: Dictionary parameters (like in Ruby)

Eilon Lipton blogged about a way to get Ruby-like dictionary parameters in C# 3.0. (Link via Scott Guthrie‘s post on URL routing.)

Ruby lets you pass a dictionary as the last parameter(s) to a method. I won’t go into details, but the syntax looks like this:

get_html_link("Click me", "Key1" => "value1", "Key2" => "value2", "Key3" => "value3")

(Yes, the idiomatic usage would be to use :key1 instead of a string literal, but the strings are probably less confusing for the non-Ruby-adept.)

With Eilon’s code, you can get some very similar syntax in C#:

GetHtmlLink("Click me", new { Key1 = "value1", Key2 = "value2", Key3 = "value3" });

This code passes an anonymous type (that’s the new {...}), and then uses Reflection to read its properties. Reflection is not something you want to do in tight inner loops, but when readability is the main concern (as it is 95+% of the time), this technique is worth considering.

The place this is useful is when you’re really passing a dictionary of arbitrary values — when the function doesn’t know how many items to expect, or what they’ll be called. If, on the other hand, the function knows it’s expecting three properties called Key1, Key2, and Key3, you’d be better off defining a type with three properties, rather than using an anonymous type. Then the refactoring tools would be on your side, and you wouldn’t have reflection overhead. (The syntax wouldn’t change much, though — you’d just add the type name after new.)

Using anonymous types this way requires some work on the part of the function being called, to do the needed bits of Reflection. Eilon’s article includes sample code, although it leaves something to be desired; for example, he makes a new type for the key-value pairs, when KeyValuePair<> is already out there waiting to be used. And at that, why return a list of key-value pairs, when you could just return a dictionary?

Here’s my contribution. This version has the added advantage that you can pass in either a type with public properties (e.g. an anonymous type), or an IDictionary (generic or non-), and it does the Right Thing™. It returns a dictionary with case-insensitive key lookup, so you could use dictionary["key1"] just as well as dictionary["Key1"] (change it if that’s not what you want). Unit tests are left as an exercise for the reader.

public static IDictionary<string, TValue> ObjectToDictionary<TValue>(object o)
{
var result = new Dictionary<string, TValue>(StringComparer.InvariantCultureIgnoreCase);
if (o == null)
return result;
if (o is IDictionary)
{
foreach (DictionaryEntry entry in (IDictionary) o)
result.Add(entry.Key.ToString(), (TValue) entry.Value);
}
else
{
foreach (PropertyDescriptor prop in TypeDescriptor.GetProperties(o))
result.Add(prop.Name, (TValue) prop.GetValue(o));
}
return result;
}

The above code snippet is public domain; use it how you like.

Using the Microsoft Ergonomic Keyboard’s zoom wheel to scroll

Just ran across instructions on using the Microsoft Natural Ergonomic Keyboard 4000’s “zoom slider” as a scroll wheel instead.

The instructions involve compiling a JScript.NET application and running an XSL transform on the keyboard’s config file. But it looks to me like it’d be a whole lot easier to just open the config file in a text editor and make a couple of manual edits. I’ll have to try it when I get over this danged cold and get back to the office.

Parallel processing made easy

Microsoft Research and the CLR team are working on something called the Task Parallel Library, which looks like it will make .NET-based parallel processing a whole lot easier than it is today. The library is supposed to be released as a CTP sometime yet this year.

The article in MSDN Magazine has all the details, and is a great read. The parallel “for” loop looks extremely useful, and accordingly gets a lot of column inches in the article — including some tips on how much parallelism is too much, and things to think about when you tune its performance. They’ve also got a convenient way to execute two (or more) methods in parallel. The higher-level stuff is all implemented in terms of task and future classes that you can use on their own.

One of the best things about this library is that it handles exceptions nicely. If you run a loop in parallel, and any of the parallel threads throws an exception, all the other threads are canceled, and the exception is re-thrown in the place you originally started the loop — just like it would be if you’d used an ordinary “for” loop. This exception handling is baked into the base Task class, so it’s there for everything in the library.

I particularly liked some of the ways they squeeze the best performance out of the library. For example, if you create a future, and later you ask for its return value, but it hasn’t yet started to run, it’ll just be run immediately on the calling thread. No task switching. That’s kinda cool! They also optimize for single-core (e.g., Parallel.For just runs as a simple loop if you only have a single-core processor), and they scale up the number of worker threads if there’s a lot of blocking going on.

All in all, this looks like it’ll be a terrific library when it ships. Of course, I found this article after I’d put experimental multithreading support into DGrok! I’ll probably rip out my code and use theirs when they ship — they’re much more expert at multithreading than I am, so not only are they a lot more likely to get all the fringe cases right than I am, but they should be faster than mine as well.

Thanks to Allen Bauer for the link to the MSDN article.

Non-trivial example of unmanaged exports

Chris Bensen just posted an example of passing non-trivial types — strings and interfaces — between Win32 and .NET using unmanaged exports. If you’ve ever wondered how to use unmanaged exports to pass anything more complicated than integers, this post is for you.

(And in case anyone was wondering, I’m the one who sent the question he’s responding to.)

Now I just need to find some spare time to tinker with this, because it’s prompting a number of questions in my mind. Can I put some attributes on the .NET interface to get rid of the IDispatch requirement? Do you have to use a var parameter to return an interface, or can you use an out parameter or a function? What happens when you throw an exception in .NET and catch it in Win32, and vice versa? How easy is it to pass callbacks (delegates) back and forth?

Of course, I don’t know if I’ll get that tinkering done very soon, for two reasons: (1) I’ll have to play with it on a work PC (my home PC only has Turbo Delphi for Win32), and (2) next month is NaNoWriMo.

DGrok 0.8: Faster and more configurable

DGrok 0.8 is now available.

You can now configure compiler options (IFDEFs, IFOPT, etc.) from the GUI. Your settings (compiler options, search paths) are saved from session to session. And the parser is probably around 20-25% faster (thanks to Brian for profiling and finding the hot spots).

Here’s the list of new GUI features:

  • FIX: .dproj-file exclusion got broken in 0.7. Made it work again.
  • CHG: Rules are now shown in a treeview instead of as hyperlinks. And the “Parse” button is back (along with its Alt+P accelerator).
  • ENH: Much-requested: Added “Compiler Options” page, where you can specify what to do with IFDEFs and IFOPTs.
  • ENH: Much-requested: Save settings (including search paths and compiler options) on exit, and reload on program startup.
  • ENH: Sped up parsing.
  • ENH: Show elapsed time after parsing.
  • ENH: When running an action, show a summary node with the action name and the number of hits.
  • ENH: Give a better error on duplicate filenames (it now shows these errors in the tree, instead of popping up an “unhandled exception” dialog).
  • ENH: Added Find (Ctrl+F) and Find Next (F3) to source-edit boxes.

New “find stuff” actions:

  • ENH: Added FindAsmBlocks action.
  • ENH: Added FindVariantRecords action.

Things you’ll see if you’re using the parser in your own code:

  • FIX: ToCode() was throwing exceptions on nodes with a trailing, empty ListNode (commonly seen with “procedure of object”). Fixed.
  • CHG: All node-type properties now end with the word Node.
  • ENH: Add a Projects collection to CodeBase.
  • ENH: Added ParentNode property to AstNode.
  • ENH: Added ChildNodes property to AstNode. When you need to iterate a node’s children, this is easier than using Properties. It also skips over nulls.
  • ENH: Added ParentNodeOfType<> method to AstNode.

DGrok 0.7 released: putting the parser to work

DGrok 0.7 is now available.

What’s new:

  • FIX: Initialized variables and typed constants now accept the empty list, ‘()’. The grammar doc already indicated this, but the code didn’t do it. It does now.
  • CHG: In package files, allow assembly attributes after the ‘requires’ and ‘contains’ clauses, rather than before. (I know they can come after, and that’s what I currently need to parse. If they can come other places, I’ll have to extend this later.)
  • ENH: Added support for undocumented {$SOPREFIX}, {$SOSUFFIX}, and {$SOVERSION} compiler directives.
  • ENH: The “search paths” box now allows a semicolon-delimited list of directories. As a side effect, there’s no longer a Browse button.
  • ENH: The “search paths” box now lets you put “\**” at the end of a search path. This means “include subdirectories”. Without that, it only does one specific directory. In a semicolon-delimited list, this gives you the ability to recurse into some directories and not others.
  • ENH: Added features to find all “with” statements, nested methods, and global variables in the code.

You’ll notice that there’s no longer a “Parse” button; instead it’s a hyperlink. This means that it no longer has a keyboard accelerator. Sorry — I miss it too. I’ll probably add a shortcut at some point.

There are five hyperlinks in this version:

  • Parse All: parses all the source files in the search path(s). You have to do this before any of the other links will enable.
  • Show Parse Results: resets the display to show the results of Parse All. This is only useful after you’ve clicked one of the later links, and want to get back to the thing that shows which files did and didn’t parse successfully.
  • FindGlobalVariables: Gives you a list of all global variables.
  • FindNestedMethods: Gives you a list of all nested methods (procedure inside a procedure, etc.).
  • FindWithStatements: Gives you a list of all the “with” statements.

Those last three are the bare beginnings of DxCop, a tool I’m eventually planning to write on top of the DGrok parser. DxCop (named after the .NET Framework’s FxCop) will analyze your code, looking for places where you aren’t following best practices. FxCop looks at your compiled code, while DxCop will look at the source, but it’s the same idea.

For those interested in looking at the code, all three “find” features will show you how to put the Visitor pattern to use. They’re only the beginning. Feel free to suggest other such features to add (or to send me code) — but keep in mind that DGrok doesn’t yet have symbol-table support, so anything involving “find references” isn’t possible just yet.

DGrok 0.6.5 released: fixed $IFNDEF

DGrok 0.6.5 is available. It’s a minor bugfix release, hence the half-version.

What’s new:

  • FIX: {$IFNDEF UnrecognizedSymbol} was ignoring its contents. That’s the right thing for {$IFDEF} but not for {$IFNDEF}. Fixed.
  • ENH: Allow attributes in package files.
  • ENH: Add support for {$INLINE} and {$STACKCHECKS} undocumented compiler directives.

Ordinarily I would have gotten more done in an evening. It’s all Sam’s fault for distracting me with his Erlang talk last night.

DGrok 0.6 released: enter Visitor

DGrok 0.6 is now available for download.

New in this version:

  • All documented compiler directives are supported (plus one undocumented one, {$VARSTRINGCHECKS})
  • Added support for hard casts to the file keyword, e.g., file(AUntypedVarParameter)
  • Changed PointerType to allow any type, not just QualifiedIdent. This allows things like ^string.
  • Fixed runtime cast error when a program or unit has a beginend block or asm block for its initialization section. For simplicity’s sake, all init sections are now represented by InitSectionNode.
  • Ignore .dproj files when you search for *.dpr.
  • Ignore {$DEFINE} and {$UNDEF} when they occur inside a false {$IF...} block. Oops.
  • Ignore unrecognized compiler directives if they’re inside a false {$IF...} block. This lets me ignore, say, FreePascal compiler directives (since they usually occur inside {$IFDEF FPC} blocks).
  • Allow dotted names for packages (in the “Package Name” line).
  • Contains clauses now support “in filename” specifiers. (Actually, contains clauses are now handled by the same rule as uses clauses.)
  • Added support for [assembly: Attribute(...)] syntax. Yes, mostly I’ve been focusing on parsing Win32, but I have some need to deal with .NET code as well.
  • Changed it so that not-yet-defined symbols are treated as false by {$IFDEF} rather than explicitly raising an error. The error served its purpose earlier, but I reached the pain point and shut it off.
  • Added a Visitor class.

Whew. Not bad for one evening’s work.

I think Visitor will really be the way you put the parser to work. I was hoping to get some examples into today’s build, but I ran out of time. Play around if you like.