Oct 1 09

Quick Tip: NullReferenceException, AliasToBeanTransformer, MultiQuery, and NHibernate

by Nicholas Piasecki

When the following are all true with NHibernate 2.1, you’ll receive a NullReferenceException: Object reference not set to an instance of an object thrown from the innards of NHibernate’s AliasToBeanTransformer, mentioning some nonsense about a tuple:

  • You’re writing an HQL query.
  • You’re using aliases and the AliastoBeanTransformer.
  • You’re executing the HQL query via a MultiQuery.

The problem is that, for reasons that are not clear to me, the alias names get stripped when they’re used in a MultiQuery. This results in NHibernate telling the AliasToBeanTransformer something like “here, go ahead and set the property called NULL to 42.” And so it explodes when it references the property name string that is null.

If you just use CreateQuery() instead of a multiquery, however, the aliases work fine. So that is the workaround. Alternatively, you could use a constructor-style projection in your HQL, but I think that’s a pain.

In short: AliasToBeanTransformer simply does not work in a multiquery.

Happy NHibernating.

Sep 23 09

Running Update SQL Scripts During Minor Upgrades with WiX

by Nicholas Piasecki

I’ll continue the WiX-related discussion as I build the installers that maintain the system run at Skiviez’s new venture Warehouse Fulfillment Services. WiX has been fantastic, and without it, I probably wouldn’t be using the Windows Installer, but I’ve quickly found that having these installation packages makes deploying updates to the production server and much less tense process: for the Web site, the API server, and the background task worker, installing and updating to a newer version of my software is as simple as a double-click.

I also created an installer for the database. This is new territory for me; in the past, well, the database has largely been ignored as far as configuration management was concerned. It usually had grown “organically” over time, and by organically, I mean people adding things as they needed it directly in SQL Server Management Studio. Setting up a test database, to that end, usually meant grabbing a copy of the production database and using it with sensitive data removed. Blech.

Since I was dealing with a new system, I wanted to avoid this situation from the beginning, being able to release updates to the database in a sensible, maintainable way, and ensuring that the database could be installed on a new machine as a turn-key solution should the software ever become valuable enough to be sold. What I wanted was a WiX/MSI installation package that would do the following:

  • On the first install, it should create the database and execute SQL scripts that
    • create any new users or logins required by the database;
    • create the schema;
    • create the “version 1.0″ tables, indexes, and foreign key relationships; and
    • populate the database with “required data,” such as those in lookup tables like Core.Countries or Core.AddressTypes.
  • On any subsequent upgrade, it should just run SQL patch scripts that make changes to the database.

Now, I could make this simple or I could make this complicated. To keep things simple, I assume that the database versioning progresses linearly: that is, the SQL patch scripts that I add in the future (to add new tables, add indexes, and so on) will be expected to be executed in a certain order. I’m of the opinion that this is a pretty sensible restriction to have on a database.

The way I ended up doing this in WiX was by creating a project with two components. One component contains the database and the scripts that are run to create and populate the virgin database; it only ever runs once. The second component contains the patch scripts; it runs on every install or upgrade.

Wouldn’t having all those patch scripts run every time cause problems, like trying to create tables twice and such? Well, yes, so I’ve made sure to construct the scripts according to a certain pattern. My database has a table called Core.DatabasePatches which contains a GUID and a human-readable description field. Each of my SQL patch scripts, let’s take AddIndexToVariationTypes.sql as an example, is then coded to the following pattern:

BEGIN TRANSACTION;
 
DECLARE @PatchIdentifier UNIQUEIDENTIFIER;
DECLARE @PatchDescription NVARCHAR(255);
 
SET @PatchIdentifier = '{C2929CCB-54C7-4060-8CD5-D606B0BB2C77}';
SET @PatchDescription = 'Adds an index to the Core.VariationTypes table to support bulk import operations.';
 
IF (NOT EXISTS(SELECT * FROM Core.DatabasePatches WHERE PatchIdentifier = @PatchIdentifier))
BEGIN
 CREATE INDEX IX_VariationTypes_VariationTypeName ON
	Core.VariationTypes (VariationTypeName, MerchantId);
 
	INSERT INTO Core.DatabasePatches (
		PatchIdentifier,
		Description
	) VALUES (
		@PatchIdentifier,
		@PatchDescription
	);
END
 
COMMIT TRANSACTION;

You can see that all of the work is wrapped in a big IF statement that says “check the Core.DatabasePatches table to see if this patch has already been applied, and if it has, well don’t do it again.” This way, I can let WiX just run the damn scripts every time without me resorting to storing a “database version” registry key or some other value that I could use as a <Condition> on a WiX component. As time goes on, these scripts can get consolidated into the same file and merged back into a “baseline schema” for the next major upgrade, so I’m not too worried about scaling here.

There is still the problem of the component that sets up the database initially trying to run on every install, however. The Installed property isn’t good enough here because that will return false when we are doing updates. So what I ended up doing is creating a new BASELINE_INSTALLED property that checks for the Version that I’ve used in the initial version of my installer.

    <Upgrade Id="{SOME-GUID}">
      <UpgradeVersion
        Minimum="1.0.0"
        IncludeMinimum="yes"
        OnlyDetect="yes"
        Property="BASELINE_INSTALLED" />
    </Upgrade>

Then the component that creates the database has an appropriate <Condition> on it:

<Component Id="ComponentSqlDatabaseCore" Guid="{SOME-GUID}">
  <Condition><![CDATA[NOT BASELINE_INSTALLED]]></Condition>
  <CreateFolder>
    <Permission
      GenericAll="yes"
      User="SQLServerMSSQLUser$[ComputerName]$[SQLINSTANCE]" />
  </CreateFolder>
  <sql:SqlDatabase
    Id="SqlDatabaseCore"
    ConfirmOverwrite="yes"
    ContinueOnError="no"
    CreateOnInstall="yes"
    CreateOnReinstall="no"
    CreateOnUninstall="no"
    Database="Armadillo"
    DropOnInstall="no"
    DropOnReinstall="no"
    DropOnUninstall="no"
    Instance="[SQLINSTANCE]"
    Server="[SQLSERVER]">
    <sql:SqlFileSpec
      Id="SqlFileSpecCore"
      Filename="[ProgramFilesFolder]Skiviez\Armadillo\Database\Armadillo.mdf"
      Name="Armadillo" />
    <sql:SqlLogFileSpec
      Id="SqlLogFileSpecCore"
      Filename="[ProgramFilesFolder]Skiviez\Armadillo\Database\Armadillo.ldf"
      Name="ArmadilloLog" />
    <sql:SqlScript
      Id="SqlScriptCreateDatabaseUsers"
      BinaryKey="BinaryKeyCreateDatabaseUsers"
      ContinueOnError="no"
      ExecuteOnInstall="yes"
      ExecuteOnReinstall="no"
      ExecuteOnUninstall="no"
      Sequence="1" />
    <sql:SqlScript
      Id="SqlScriptCreateCoreSchema"
      BinaryKey="BinaryKeyCreateCoreSchema"
      ContinueOnError="no"
      ExecuteOnInstall="yes"
      ExecuteOnReinstall="no"
      ExecuteOnUninstall="no"
      Sequence="2" />
    <sql:SqlScript
      Id="SqlScriptInsertHarmonizedTariffSystemCodes"
      BinaryKey="BinaryKeyInsertHarmonizedTariffSystemCodes"
      ContinueOnError="no"
      ExecuteOnInstall="yes"
      ExecuteOnReinstall="no"
      ExecuteOnUninstall="no"
      Sequence="3" />
  </sql:SqlDatabase>
</Component>

For the sake of completeness, here’s what the component for the patch SQL scripts looks like:

<Component Id="ComponentSqlDatabasePatches" Guid="{SOME-GUID}">
  <CreateFolder />
  <sql:SqlScript
    Id="SqlScriptAddVariationTypeNameIndex"
    BinaryKey="BinaryKeyCreateVariationTypeNameIndex"
    ContinueOnError="no"
    ExecuteOnInstall="yes"
    ExecuteOnReinstall="yes"
    ExecuteOnUninstall="no"
    Sequence="4"
    SqlDb="SqlDatabaseCore" />
</Component>

When I have new patches to add in the future, I’ll just add them to that component, using the Sequence property make sure they run in my intended order on a fresh installation.

Now, I don’t have any scripts that un-apply any of these changes on uninstall or rollback, but since this is an installer used by one person in the world (me), I’m keeping that simple for now. Obviously something for a retail application would have that functionality, although I’ve seen recommendations that database management for those would be handled in the application itself, not in a Windows Installer. Caveat emptor!

I also don’t drop the database on uninstall because that seems terrifying to me, particularly if I have a blonde moment and do a major upgrade one day without realizing it.

This could be a totally insane approach, fair warning, but I don’t know of a better way (yet). Hope this helps someone else out there. Happy installing!

Sep 9 09

Stopping a Web Site to Remove Locked Log Files During a WiX Uninstall

by Nicholas Piasecki

So for my employer, Skiviez, I’ve created a WiX installer for the Web site. It’s pretty sweet: just double-click the MSI, and insto presto, you have a Web site installed, with the directories created, permissions set correctly, and IIS configured correctly.

The Web site uses log4net for logging, and it uses the RollingFileAppender to log information in a Logs directory for the Web site. In WiX, this behavior is specified in the usual way:

  <Directory Id="DirectoryLogs" Name="Logs">
	<Component Id="ComponentCreateFolderLogs" Guid="{DAB6113C-83BA-422d-9D77-43F112BE7EF7}">
	  <CreateFolder>
		<Permission
		  GenericRead="yes"
		  GenericWrite="no"
		  GenericExecute="no"
		  Read="yes"
		  User="Authenticated Users" />
		<Permission
		  GenericAll="yes"
		  User="NT AUTHORITY\NETWORK SERVICE" />
	  </CreateFolder>
	  <RemoveFile
		Id="RemoveFileLogsAll"
		Name="*.*"
		On="uninstall" />
	</Component>
	<Component Id="ComponentConfigWebLogs" Guid="{82B14CA7-96B1-4c92-8E6E-846C8E17ED02}">
	  <File
		Id="FileConfigWebLogs"
		Name="Web.config"
		Source="$(var.Skiviez.Armadillo.UI.Web.ProjectDir)Logs\"
		KeyPath="yes" />
	</Component>
  </Directory>

There is nothing particularly exotic going on here. I tell Windows Installer to create the Logs folder, set some permissions on it so that NETWORK SERVICE (which the Web site runs as) can read and write to the folder and so that I (as an Authenticated User can easily read it), copy in a Web.config that locks down the folder from being served by IIS, and tell Windows Installer to remove all of the log files during uninstall. I’ve written this a bazillion times before and it works fine for most programs.

But when I tried to uninstall while the Web site was still running, the uninstallation would fail because the log file was still locked by the Web site. This is because WiX’s custom action that finagles with IIS runs after the standard RemoveFiles action in Windows Installer. Some poor soul seemed to be having my exact same problem, but no answers there.

Since this is the only Web site running on the server, an easy solution is to use another built-in WiX custom action to stop IIS during uninstallation and then start it back up again afterward. This has the advantage of spinning down the ASP.NET worker process so that the lock on the file is released, RemoveFiles completes successfully, the WiX IIS custom action removes the Web site, and finally IIS is gracefully started back up again.

I’m not sure why it wasn’t obvious to me; took me a good 45 minutes to figure this out.

Just drop this in the same Component that your Web site is defined in:

<ServiceControl
  Id="ServiceControlWebSiteMain"
  Name="W3SVC"
  Start="both"
  Stop="both"
  Wait="yes" />

(Side note: Windows Installer’s architecture is completely insane. I reckon I’ll save that rant for another post. Perhaps the most amusing bit is that when uninstallation fails for some reason, such as the situation described in this post, Windows Installer will happily re-install everything back to the way it was! Ha!)

Happy uninstalling!

Aug 20 09

QuickTip: When Your App Crashes in Release Mode But Runs Fine Under the Debugger

by Nicholas Piasecki

Let’s say that you’ve written a super neat-o C# application that uses P/Invoke into a third-party native DLL. Let’s also say that, oh, 6 times out of 10, your application crashes hard with an “attempted to write to protected memory” error when run in release mode. And let’s finally say that when you run the application under the debugger, the crash doesn’t happen.

You might think that the third-party, native DLL that’s loaded into your process is, well, crashing your process.

And you’d be right. It is crashing there, but it’s not its fault. Because you’re causing it to walk off the end of an array.

Not that I did this or anything, spending a good 45 minutes to figure out what was going wrong. In my case, when I was P/Invoking to ZP4 (which, by the way, is awesome, for various reasons that I’ll have to enumerate in another post), I declared the extern to take a StringBuilder parameter (the underlying C DLL was expecting a char*). This is all very well and good and documented as “the right type” in MSDN.

Of course, I forgot to make sure that the Capacity of the StringBuilder was big enough for what the C DLL was going to try and put in the character array that interop was building under the hood. Unlike in .NET land where a StringBuilder can just make itself bigger when it runs out of room, in P/Invoke, the interop basically does this:

  • Interop looks at the Capacity of the StringBuilder and allocates a char array that many chars wide. After all, it can’t read minds to know how much the function it is about to call is going to shove in there.
  • Interop passes a pointer to that unmanaged char array by value to the C function.
  • (The C function does whatever it does, referencing that pointer.)
  • Keeping the initial Capacity in mind, Interop looks at the unmanaged char array and marshals it back into the StringBuilder. Then it frees the unmanaged memory it had allocated for the array since it’s no longer needed.

Because I didn’t set the Capacity before all this went down, I ended up with the default Capacity of 16 characters. It just so happens that the C function I was calling expected the character to array to be 928 characters. Oops.

Since the debugger adds a lot of record-keeping information to the stack, generally padding out the size and layout of my program in memory, I was “getting lucky” in debug mode by scribbling over 912 bytes of memory that weren’t very important. Without the debugger, though, I was scribbling on top of rather important things, eventually walking outside of my own memory space, causing Interop to delete memory it didn’t own. Most of the time.

Aug 12 09

“The data area passed to a system call is too small” and other idiocies when installing a Zebra LP2844 printer driver

by Nicholas Piasecki

For reasons that would bore most to tears, I had to swap out a workstation at work today. This workstation had one Zebra LP2844 laser printer attached to it, and along with the “new” (well, new-to-that-particular-desk, not new-to-the-world) workstation, it was getting another.

Simple! The “new” workstation already had the Zebra LP2844 printer drivers installed, so I’ll just Start > Printers > Add Printer > Yep, LPT1: > Mmhmm, ZEBRA EPL > (scrolling … scrolling … Why is this dialog box so small? … scrolling) ah, LP2844 > Continue Anyway (Why can’t a million dollar company get their drivers signed?) > Finish, insto presto, and….

Boom. “Printer Driver was not installed. The operation could not be completed.”

Hum.

I’ll try it again. This is an indication of insanity–expecting a different result from the same operation–but it’s 7:30 a.m. and I can’t be experiencing problems already. Come on.

Boom. “Printer Driver was not installed. The operation could not be completed.”

Okay, I’ll just download Zebra’s driver setup utility and install the printer that way. Next > Next > Add a printer > Next > Next, and….

Boom. “The data area passed to a system call is too small.”

Hum. That’s a new one. But different!

I know this printer works, why is this not working?

The Solution

After about 30 minutes of head-scratching, I stumbled upon the solution. These types of errors are apparently the result of corrupted or just plain buggy printer drivers already installed on the machine. They could be unrelated to the printer that you’re trying to install; it doesn’t matter. They’ll cause the error when the “add a printer” mechanism enumerates through the list of printer drivers already installed on the machine.

To fix it, it’s time to blow away some printer drivers. Go to Printers under the Control Panel. Click File > Server Properties and switch to the Drivers property sheet. Remove any suspicious-looking drivers and try to add your printer again. (For me, the problematic driver was a custom LP2844 driver that came on a UPS WorldShip disk.)

Jul 16 09

Wake up, USPS

by Nicholas Piasecki

Our latest shipping integration at work is near nirvana: you place a package on a scale, scan the barcode (the shipping program itself doesn’t even need to be focused!), choose the box type, and press enter. Then a label comes shooting out of the label printer, and maybe some customs forms documents, too.

Behind that magic <ENTER> press, though, a lot of magic is happening behind the scenes:

  • I call our payment processing service to cleanse the address into a format suitable for mailing.
  • I call the FedEx Web service to determine if the address is residential or commercial.
  • If standard shipping, I call the USPS XML API, FedEx SOAP API, and UPS XML APIs simultaneously to find the cheapest rate and best transit time.
  • I call the FedEx or UPS API directly to print a shipping label immediately OR invoke a Stamps.com COM object to pop up an extra dialog, which the user then presses enter to spit out the label interminably slowly.

It’s the last part that has been the bane of my existence. USPS does not offer any online mechanism to print postage-paid shipping labels in an automated fashion. You get to choose between Endicia and Stamps.com. While Endicia has an XML API that functions nearly identically to those offered by UPS and FedEx, it’s ridiculously expensive, and Stamps.com has no such API. Instead, I have to interact with a COM object that is popping up dialogs and forcing the warehouse worker to worry about buying postage when it runs low.

To add insult to injury, Stamps.com doesn’t understand EPL2 and instead sends its labels as gigantic GDI documents to the label printer, giving us print times of nearly 15 seconds for a 4″ x 6″ label when going over parallel. Compare to less than 2 seconds for a label sent as an EPL2 document. But Stamps.com is cheap.

And there are no alternatives.

Sure, FedEx has SmartPost and FIMS and UPS has Mail Innovations. These are services where we would put our non-postage paid parcels into a trash bag that FedEx or UPS would pick up, sort, route through their system to a post office near the parcel’s final destination, and affix postage there and let it enter the USPS system by mailing it for us at the “last mile post office.” But Mail Innovations is worthless (a statistically significant number of our packages shipped through Mail Innovations to Canada were tampered with), FIMS is mind-boggingly slow (up to 60 days to send a package to Finland, and FedEx reps seem to have no idea that it is actually taking that long for their service to actually work), and SmartPost is expensive unless you’re talking enterprise-level volumes, which I am not. Plus, if a package disappears, there’s plenty of finger pointing, trust me.

And sure, there’s Click-N-Ship at the USPS’s Web site. But Click-N-Ship provides zero integration capabilities and as such is useless when you’re talking more than, say, 3 packages per day. It prints lots of instructions that you just end up throwing away, uses PDF for label output (so you can’t really automate printing unless you want to rig up a harebrained routing-through-Ghostscript scheme), and is really intended for home users.

There’s permit imprint, which would work if we were mailing 200 identically weighted parcel post packages, but that is never going to happen. Some widgets are bigger than others.

And sure, there’s that moronic new commercial where they show the USPS employees touting the simplicity of the new priority mail flat-rate boxes for businesses. You still have to stamp them–that’s half my problem. And using a flat rate box means that you’re throwing money away a good 50% of the time. We’re not stupid. It does not cost $9.85 to send a 12″ x 9″ x 6″ box from Virginia to North Carolina. And where’s my delivery confirmation barcode? I’ll have to affix that too and record the number–how is this saving me any time? I want to throw my remote at the TV every time this commercial comes on.

So, I don’t understand why the post office doesn’t offer a postage Web service like UPS’s or FedEx’s XML shipping services, unless it’s just too much competition with their own licensees. The Visa business model does not make sense for everyone.

I don’t understand why the post office doesn’t sell laser printer versions of its CN-22 and CP-72 customs forms. We had to have our own specially made, and use the USPS’s API to generate the actual form. (More on this later.)

I don’t understand why the USPS has an address verification API available through their Web tools but doesn’t let anybody use it. (By anybody, I mean that only non-profits can use it. Not even local governments can use it.) Shouldn’t it be an obvious service to help customers verify that they’re shipping to a valid address? Well, perhaps I do understand, because there’s lots of money to be made selling CASS certification to vendors who then can charge ridiculously high fees for what is, at its core, just a giant database table that gets updated once a month. By giant, I mean it fits on a CD-ROM. CASS is beyond stupid.

I don’t understand why the USPS doesn’t get its act together and offer business accounts for its Priority mail and higher services. With the USPS, indicia is like currency: a misprinted label means you lose money, whereas with FedEx and UPS, a misprinted label means you throw it in the trash. But far, far more importantly, this means that businesses can’t leverage the “float” when using USPS: the shipping cost is incurred immediately instead of on net 30 terms.

I don’t understand why the USPS doesn’t allow scheduling a daily pickup for businesses. Sure, there’s the insane carrier request API, which we use, and have to remember to update every few months until we max out the pickup requests again. But it’s particularly insane that it requests the number of packages that will be picked up and that mail carriers treat this as some sort of contractual obligation. I am setting a date in the future–I can give you an estimated average, but please don’t complain when I have 90 packages instead of 50. I can’t see the future to know how many orders we will have to process that morning.

I don’t understand why the ICCC is staffed by weasels. When an API is not on your test server but you require us to test against the requested API on the test server before getting access to the production server, then something is really wrong, and someone at the USPS needs to be fired. Right now.

If anyone at the post office is listening, one of the greatest barriers to using the post office directly is a lack of integration points. Quit moaning about the number of parcels dropping and make it easier to actually ship packages, and maybe we’ll be more apt to use it.

Update 11/11/2009: See my comment below for more detailed information since this post was written.

Jul 6 09

When in doubt: OpenID is a bad idea for your Web site

by Nicholas Piasecki

Well, that’s a bit of an inflammatory title. But this screenshot speaks volumes:

I can't log in!

I can't log in!

What has happened here is that my OpenID provider, Verisign Labs, is down for some reason while StackOverflow, the site that I’m trying to log on to, is still up.

A single point of failure

While this isn’t a big deal for a site that I use recreationally, it is kind of a big deal in principle: all sites that use OpenID are inaccessible to me, since I only have this single OpenID provider. It is a single point of failure.

Sure, I could have multiple OpenID accounts, such as a “backup” OpenID account at another provider, but this defeats the purpose of OpenID, of having a central point of authority regarding my online identity. If I have to create a second OpenID provider, I might as well just make a native username and password at the site that I’m trying to log into. I could also be my own OpenID provider, but I shouldn’t have to do that, and the average OpenID consumer is not going to know how to do that, anyway. It’s faster just to create a “native account” at the Web site that I’m trying to access and be done with it.

An unnecessary third-party dependency

For any e-commerce business, it’s usually unwise to take on any third-party dependency. Speaking from experience, that third-party will go down for some reason at some point and you will be the one fielding angry customer support calls for something that isn’t your problem. Even worse, if something as critical as authentication through OpenID goes down, then you’re losing sales, and that means that you’re not doing your job.

If you’re going to add OpenID to an e-commerce Web site or a “mission-critical” Web site, then you should implement it as a red-headed step-child, “convenience only” implementation; this way, your users can fall back onto their “native credentials” (those stored in your database) when their preferred OpenID provider explodes. In this way, OpenID is a shortcut, not a single authoritative source, much how like customers use PayPal’s Express Checkout to save time when entering payment and addressing information during checkout. If PayPal’s Web Service API goes down (and believe you me, it’s gone down several times this year alone), customers can still begrudgingly use the “native checkout” to complete their purchase. Some lost conversions due to the inconvenience–and for incorrectly faulting the integrity of your Web site for the third party’s failure–but at least the sale is still actually possible. If OpenID is your only authentication mechanism, and a user’s OpenID provider goes down, then that user is simply screwed. The onus should not be on the consumer to provide a backup authentication mechanism to your Web site.

It’s just complicated

OpenID is still just too complicated. I’m not the smartest software developer in the world by any means, but if I can barely wrap my mind around it enough just to get my first account, then I am 100% certain that our customers would have problems with it:

  • Why do I have to go to some third-party provider to sign in? That doesn’t make any sense.
  • Why should I trust that third-party provider? I’m not even sure that I trust you!
  • I still don’t know which third-party provider to use! Oh well, I guess I’ll use Verisign, at least I’ve heard of them.
  • What happens to my credentials when that third-party provider goes under?
  • Can I call your business to reset my password when I forget it? I will anyway!

Even with our “native registration” of e-mail addresses and passwords, we had to use an Amazon.com style login form because users kept filling out the “new customer” form even when they already had an account:

Login screen. Amazon’s sign-in screen remains a model to be emulated, minimizing the common problem of new customers who try to log in without having registered. Amazon presents two questions in linear order: (1) “What is your email address?” and (2) “Do you have an Amazon.com password?” For the second question, users can select one of two radio buttons: “No, I am a new customer,” or “Yes, I have a password.” Many other sites present the new- and established-user sections side-by-side, and thereby divert new users to the established-user section through the magnetic attraction of type-in fields. — Jakob Niesen, useit.com

When our users have trouble navigating two fields and a radio button, you can imagine the hilarity that ensues when they are presented with multiple authentication mechanisms. Especially one that doesn’t ask for a password.

A leaky abstraction

While the idea of consolidating your online identity to a single source sounds like a good idea, it simply does not work in practice. Not even the United States government has a consistent picture of my identity: my identity is stored with the IRS, the Social Security Administration, the United States Postal service, the Virginia Department of Taxation, the Virginia Department of Motor Vehicles, and the City of Richmond’s Voter Registrar. These are all independent identity stores that, in general, agree that I am who I say that I am, but each identity is stored and updated independently of one another. Consider OpenID the RealID act of the Internet: what happens when your centralized identity gets screwed up? It’s an existentialist crisis and you just have to deal with it: identity is what other people say you are; it’s not what you say you are. Better to have multiple voices confirming your identity than just one.

Conclusions and Delusions

If you’re implementing a social-oriented Web site that is designed to integrate with Facebook or some of the Web-2.0-savvy consumers, then OpenID might make sense. Those nerds may have heard of OpenID and know how to use it, foibles and all. Until the dust settles on OpenID, though, I wouldn’t add it to a commercial site even if you paid for it: nobody has asked for it; it will cause problems.

My general recommendation would be to have the usual in house username and password mechanism that can be supplemented by OpenID. But realize that each alternative identification mechanism that you add runs the risk of customer confusion, bugs, security holes, and increased customer support. When in doubt, leave it out.

Jun 26 09

SerializationException can’t be caught when object provides a deserialization constructor

by Nicholas Piasecki

Well, this one was new to me, and I spent about ten minutes chasing my tail. Consider the following code (C#):

if (buffer != null)
{
	using (MemoryStream ms = new MemoryStream(buffer))
	using (GZipStream gs = new GZipStream(ms, CompressionMode.Decompress))
	{
		try
		{
			userProfile = new BinaryFormatter().Deserialize(gs) as UserProfile;
		}
		catch (SerializationException)
		{
			this.Clear();
		}
		catch (InvalidDataException)
		{
			this.Clear();
		}
	}
}

Looks pretty sane and cautious, right? This is part of one of my HTTP cookie classes, and it just takes a previously serialized object and attempts to deserialize it. If for some reason deserialization doesn’t work, big whoop, just clear the cookie and make the user log in again, because that probably means I just updated the software on the server anyway.

However, one of the constructors for the UserProfile class looks like this:

/// <summary>
/// Initializes a new instance of the UserProfile class.
/// </summary>
/// <param name="info">The <see cref="T:System.Runtime.Serialization.SerializationInfo"/>
/// to populate with data.</param>
/// <param name="context">The destination
/// <see cref="T:System.Runtime.Serialization.StreamingContext"/>) for this serialization.</param>
protected UserProfile(SerializationInfo info, StreamingContext context)
{
	string mailAddress;
	string merchantCode;
 
	this.Identity = info.GetValue("Identity", typeof(ArmadilloIdentity)) as ArmadilloIdentity;
	this.Culture = new CultureInfo(info.GetInt32("Culture"));
	this.Currency = Currency.GetCurrency(info.GetString("Currency"));
	this.SetTimeZone(info.GetString("TimeZone"));
 
	mailAddress = info.GetString("MailAddress");
	if (!string.IsNullOrEmpty(mailAddress))
	{
		this.SetMailAddress(mailAddress);
	}
 
	merchantCode = info.GetString("MerchantCode");
	if (!string.IsNullOrEmpty(merchantCode))
	{
		this.merchant = new MerchantLookup(merchantCode);
	}
 
	this.revision = (byte[])info.GetValue("Revision", typeof(byte[]));
}

This type of constructor allows me to work around the fact that some of the types that the UserProfile class is using aren’t marked as serializable, so I come up with my own way of serializing and deserializing them.

So what happens in my first code snippet when serialization fails? Well, the SerializationException doesn’t get caught. Why the hell not?

Well, the BinaryFormatter is obviously using reflection to new up an instance of my UserProfile. Typically, this is a non-issue because the constructor does not do anything interesting. But here, it does. So when my constructor bombs on a GetValue() call because I’ve changed the object since it was last deserialized, it tosses a SerializationException as we expect–but since the BinaryFormatter was using reflection to new up the object, that exception gets wrapped in a TargetInvocationException.

In other words, to be able to catch the exception, I need to

try
{
	userProfile = new BinaryFormatter().Deserialize(gs) as UserProfile;
}
catch (TargetInvocationException)
{
	this.Clear();
}
catch (SerializationException)
{
	this.Clear();
}

It’s the TargetInvocationException that gets caught. (I left the SerializationException in there because I’m paranoid.)

Jun 13 09

Drawing on an in-memory metafile in C#

by Nicholas Piasecki

So metafiles in the GDI world of Windows are a vector format that is essentially a sequence of GDI drawing commands (we know it as System.Drawing.Graphics in the .NET world) serialized to disk. Because it’s a vector format, we can play back the metafile to different device contexts, all with different DPIs, and get more or less the same high-fidelity result on each device. But working with an in-memory metafile in C# isn’t as intuitive as one might think.

A problem that you will encounter is that while the metafile calculates its DPI according to the resolution of the reference device, the .NET Graphics instance that you get to draw on seems to always be set to 96 DPI. That simply will not work: the result, in fact, is a lot of cussing when your carefully measured measurements simply don’t line up when you play back the metafile later. Here’s a link to a guy who had the same exact problem. It gets really confusing because the amount of error will vary depending on the machine you run it on, since the reference device is the physical resolution (i.e., the desktop you’re looking at).

The secret is to apply a scale transform to your Graphics instance to fix the DPI mismatch.

Here’s some sample code:

/// <summary>
/// Adds a new metafile page, selects it as the current metafile, and
/// returns its graphics object. The caller is responsible for disposing
/// the returned Graphics instance. The unit of measurement in the
/// returned Graphics instance is GraphicsUnit.Inch.
/// </summary>
/// <param name="pageSize">The size of the page, in inches.</param>
/// <returns>a Graphics instance to render the page onto; the caller is
/// responsible for disposing of this Graphics instance</returns>
protected Graphics GetNextPage(SizeF pageSize)
{
    IntPtr deviceContextHandle;
    Graphics offScreenBufferGraphics;
    Graphics metafileGraphics;
    MetafileHeader metafileHeader;
    KeyValuePair<Metafile, MemoryStream> pair;
 
    this.currentStream = new MemoryStream();
    using (offScreenBufferGraphics = Graphics.FromHwndInternal(IntPtr.Zero))
    {
        deviceContextHandle = offScreenBufferGraphics.GetHdc();
        this.currentMetafile = new Metafile(
            this.currentStream,
            deviceContextHandle,
            new RectangleF(0, 0, pageSize.Width, pageSize.Height),
            MetafileFrameUnit.Inch,
            EmfType.EmfOnly);
 
        pair = new KeyValuePair<Metafile, MemoryStream>(
            this.currentMetafile,
            this.currentStream);
        this.metafiles.Add(pair);
 
        offScreenBufferGraphics.ReleaseHdc();
 
        metafileGraphics = Graphics.FromImage(this.currentMetafile);
        metafileHeader = this.currentMetafile.GetMetafileHeader();
        metafileGraphics.ScaleTransform(
            metafileHeader.DpiX / metafileGraphics.DpiX,
            metafileHeader.DpiY / metafileGraphics.DpiY);
 
        metafileGraphics.PageUnit = GraphicsUnit.Inch;
        metafileGraphics.SetClip(new RectangleF(0, 0, pageSize.Width, pageSize.Height));
    }
 
    return metafileGraphics;
}

Most of this is mumbo-jumbo related to my particular PrintDocumentBase implementation, but the real gem is the ScaleTransform() call: that’s where I account for the mismatch between the resolution of the metafile itself and Graphics instance that I’ll be doodling on. (My implementation also expects the page size to set so additional clipping information can be set, but that’s not important to this discussion.)

I can now doodle on the returned graphics object with abandon, resting assured that when I say “draw a line that is one inch long” I will get a line in my metafile that is one inch long. (My instance is storing the Metafile and MemoryStream pair in a dictionary. When I want to save the metafile to disk, I just have to call ToArray() on the MemoryStream.)

Bonus: Printing a serialized Metafile

Now let’s say that I have a byte[] array that corresponds to a serialized Metafile. How do I actually print that to my printer? We’ll have to do some P/Invokes. I have my own class called PrinterDocumentBase that basically returns a byte[] array on each call to GetPage(int pageNo). I assume that what I get back from that call in the function below is a serialized Metafile. (I have other PrinterBase inheritors that assume that byte[] array represents something different, like EPL2 data or ZPL data, and sends it to the printer appropriately.)

/// <summary>
/// Prints the given document.
/// </summary>
/// <param name="document">the document, which is guaranteed not to be
/// null in and in a language that the printer supports</param>
/// <param name="copies">the number of copies, which is guaranteed to be
/// a positive number</param>
protected override void DoPrint(PrinterDocumentBase document, int copies)
{
    IntPtr deviceContext;
    NativeMethods.DOCINFO documentInfo;
    int hardMarginLeft;
    int hardMarginTop;
    int pagesCount;
 
    documentInfo = new NativeMethods.DOCINFO();
    documentInfo.fwType = 0;
    documentInfo.lpszDatatype = null;
    documentInfo.lpszDocName = document.Name;
    documentInfo.lpszOutput = null;
    documentInfo.cbSize = Marshal.SizeOf(documentInfo);
 
    pagesCount = document.Prepare();
 
    deviceContext = NativeMethods.CreateDC(
        "WINSPOOL",
        this.Name.Normalize(),
        null,
        IntPtr.Zero);
 
    if (deviceContext != IntPtr.Zero)
    {
        hardMarginLeft = NativeMethods.GetDeviceCaps(
            deviceContext, 
            NativeMethods.DeviceCap.PHYSICALOFFSETX);
        hardMarginTop = NativeMethods.GetDeviceCaps(
            deviceContext, 
            NativeMethods.DeviceCap.PHYSICALOFFSETY);
 
        hardMarginLeft = (int)(hardMarginLeft * 100F / NativeMethods.GetDeviceCaps(deviceContext, NativeMethods.DeviceCap.LOGPIXELSX));
        hardMarginTop = (int)(hardMarginTop * 100F / NativeMethods.GetDeviceCaps(deviceContext, NativeMethods.DeviceCap.LOGPIXELSY));
 
        for (int copyIdx = 0; copyIdx < copies; ++copyIdx)
        {
            if (NativeMethods.StartDoc(deviceContext, documentInfo) > 0)
            {
                for (int i = 0; i < pagesCount; ++i)
                {
                    byte[] data;
 
                    data = document.GetPage(i);
 
                    using (var ms = new MemoryStream(data))
                    using (var metafile = Metafile.FromStream(ms))
                    {
                        using (var g = Graphics.FromHdcInternal(deviceContext))
                        {
                            g.TranslateTransform(-hardMarginLeft, -hardMarginTop);
 
                            if (NativeMethods.StartPage(deviceContext) > 0)
                            {
                                g.DrawImage(metafile, 0, 0);
                                g.Flush();
 
                                if (NativeMethods.EndPage(deviceContext) <= 0)
                                {
                                    throw new Win32Exception();
                                }
                            }
                            else
                            {
                                throw new Win32Exception();
                            }
                        }
                    }
                }
 
                if (NativeMethods.EndDoc(deviceContext) <= 0)
                {
                    throw new Win32Exception();
                }
            }
        }
    }
    else
    {
        throw new Win32Exception();
    }
 
    if (deviceContext != IntPtr.Zero)
    {
        NativeMethods.DeleteDC(deviceContext);
    }
}

The StartDoc and such functions are documented in MSDN and are pretty boring for this discussion.

Because when I specify I want text to print at a certain measurement in my metafile, I do have to do some dancing to shift back the hard margins of the printer–I’ll assume that I was smart enough to account for this when I was doodling on my metafile.

Keep in mind that when you do set GraphicsUnit.Inch as I have, your pen widths will be in whole inches, which can result in some pretty wide strokes! Took me a while to figure that one out, too, though it was blindingly obvious.

Hope that helps someone get on the right path!

Jun 13 09

Getting WINS-like computer name resolution over VPN in SBS 2008

by Nicholas Piasecki

So this week concluded several sleepless nights and much heartburn as I migrated Skiviez’s SBS 2003 machine (running as our domain controller and our mail server) to SBS 2008. As far as things go, it went relatively smoothly, and the remainder of the week was dealing with lots of small niceties that I had forgotten that I had set up on the 2003 server that I now needed to set up once again.

One of these was something that I used for my convenience over a VPN connection from home. You see, the internal order processing application that I wrote uses some shared folders to store some temporary data, such as e-mails that are generated but not yet released to Exchange, or a local copy of images that are available on the Web site. This software–and our users–are used to referring to Windows file shares as \\COMPUTER-NAME\SHARE-NAME; for example, \\CYRUS\Pickup Holding, because for some reason some of the older servers are named after my boss’s dead cats.

When connecting through VPN to SBS 2008, however, that “suffix-less” name resolution was not working. So when \\CYRUS\Pickup Holding failed to resolve to anything, \\cyrus.skiviez.com\Pickup Holding would work fine. This was super annoying.

The reason this worked previously with our SBS 2003 installation is that it was acting as a WINS server, which provided this type of computer name resolution for us. SBS 2008 finally retires this ancient technology by default, however, so I had two choices: I could either install the WINS server role on SBS 2008, or I could just figure out how to get the 015 DNS Domain Name option from DHCP to relay through the VPN connection.

I chose the latter option, since it’s certainly less confusing to be able to say to someone in the future “we don’t use WINS, DNS does everything.” So here’s how to do it:

  1. On the SBS 2008 server, click Start > Administrative Tools > Routing and Remote Access.
  2. In the tree view, drill down past the server name to IPV4 > General. Right-click the General option and choose “New Routing Protocol” and choose DHCP Relay Agent.
  3. Now right-click the newly appended “DHCP Relay Agent” node and choose Properties. Add the IP address of your DHCP server (which is probably your SBS server itself), and click OK. Then click it again and choose “New Interface” and add the “Internal” interface.
  4. Now if you connect through VPN, an ipconfig /all should show your domain name as a “Connection-specific DNS suffix” and pinging machines by their suffix-less computer names should work. (If it doesn’t, make sure your DHCP server is using that 015 DNS Domain Name option, which the SBS 2008 wizards set up by default.)

Happy file sharing!