Monday, 15 December 2008

Unable to determine the identity of domain

Don't you just hate it when you have some code that works fine in your development environment but not when you hand it to testers?

Loyal readers might recall that I have been migrating a bunch of legacy Win32 code to .Net. In the case of our GUI editor, that people use to map out their business processes, I have been writing a C# COM component that gets called from the legacy Delphi.Win32 code. Gradually more and more functionality is migrating to C#.

Along the way we have been storing the user's business process map in an OpenXML package. This is basically a ZIP file containing images, the model XML and bits of XML specifying how things are connected. Then it's a case of using System.IO.Packaging to get at it all.

Testing discovered that some larger client models were failing to upgrade with the mysterious error message "Unable to determine the identity of domain". This turns out to have nothing to do with Windows domains or user permissions.

As with all things .Net, someone somewhere has no doubt hit this problem before. Kevin Rohrbaugh had a similar scenario
http://www.coderoni.com/2008/04/04/server-side-office-document-generation-bug/
. His workaround was to ensure that they were running under an account that did not have a profile on the local machine (more below). That's not an option for us.

I'll spare you a gruesome recap of the full debugging which involves much "spelunking" with Reflector as Kevin so aptly calls it. Here are the broad strokes:

When you get a stream on a part in an OpenXML package (so that you can get at the uncompressed bits), you think you are getting something in-memory. However, if the package part is too big (more than 1.3Mb compressed), the framework decides to unzip the entire package part to disk and to give you a handle on that instead. This has unforeseen performance consequences, but we'll ignore them for now.

Where does it unzip them to? Isolated Storage. Exactly what kind of isolated storage depends on the user account you are running under. If you are running under a user account that doesn't have a profile on the local machine, the framework uses a machine-scoped location. If you are running under an account that does have a profile on the local machine (as we will be -- it'll be running under the account of the user of the GUI tool) it uses a user-scoped account.

If I add a reference to the assembly containing the code that accesses the package, and run it in the debugger all is well. But under COM all is far from well.

Running reflector on the Isolated Package class shows that when using user-scoped isolated storage, the framework examines the "evidence" of the AppDomain (where an AppDomain is a lightweight process). Under COM, we are running in a DefaultDomain that doesn't have any evidence.

You can't set the evidence for an AppDomain once it has been started and it's not possible to specify that the COM DLL should run with certain evidence or in a special AppDomain. Running the "Microsoft .NET Framework 2.0 Configuration" tool and granting Full Trust to our assembly doesn't solve the problem because there is still no "evidence" for the Framework code to examine. So there are two options:

1) In the Win32 code, host the CLR, create an AppDomain with the appropriate evidence and load the assembly. Use reflection to get at the methods.

2) (What I did in the interests of expediency). In the COM component, create a new AppDomain with the appropriate evidence. and execute the code in that. This works fine. There is a performance hit because we are now marshaling across AppDomains as well as marshaling across COM. We will see if the performance is acceptable. If not we will have to go with (1).

Doing (2) is similar to what you have to do for Office add-ins. For Office add-ins, the recommended strategy to satisfy the security model is to have an unmanaged shim. You sign the shim to make Office happy. Office talks to the shim, the shim acts as a proxy passing everything to your managed code.

In our case, the COM interface now loads up the AppDomain and proxies calls to an instance of our class running in that AppDomain.

The crucial (necessary and sufficient) piece of evidence is that we require the code to be running in the MyComputer zone.

First we need a simple AppDomainSetup:

AppDomainSetup setup = new AppDomainSetup();
setup.ApplicationBase = AppDomain.CurrentDomain.BaseDirectory.ToString();


Then we need our evidence

Evidence evidence = new Evidence();
evidence.AddHost(new Zone(SecurityZone.MyComputer));


Now we can fire up an AppDomain running with that evidence.

AppDomain hostedAppDomain = AppDomain.CreateDomain("Demo", evidence, setup);


Now we get a handle on an instance of our class running in that AppDomain

ObjectHandle handle = hostedAppDomain.CreateInstance("MyStuff.Demo, Version=1.0.0.0, Culture=neutral, PublicKeyToken=d0e8b069449d61a1", "MyStuff.Demo.DemoComponent");


To pull this off, DemoComponent has to inherit from MarshalByRefObject so now we have a little .Net remoting magic to do. We have to get a lease on the object and extend its lease if we are not done with it. A trivial class LicenseRenewer that implements ISponsor does the trick

lease = (ILease)handle.GetLifetimeService();
lease.Register(leaseRenewer);


Finally we can get a usable instance of the class. We access this locally and it transparently proxies calls to the other AppDomain.

demoComponent = (IDemoComponent)handle.Unwrap();


Now calls to our COM interface can just explicitly proxy to demoComponent e.g.

public bool Demo()
{
return demoComponent.Demo();
}


Then our COM interface just proxies things to demoComponent. Any types you want to marshall across AppDomains have to marked [Serializable()] of course.

Tuesday, 30 September 2008

Open XML

I am working on a report that a few customers are very keen on. We take the description of roles and business processes and produce a role/task matrix to show which roles can perform which operations in the business.

The HTML version of the output is a thing of beauty if I say so myself. It has lots of merged rows so that we can represent a process consisting of several stages then in each cell in the matrix we have a pretty icon that shows the effective permissions.

Now the task is to produce the same thing in Excel. We'll be targeting Excel 2007, which means using the Open XML package format. No problem there. As mentioned in earlier postings, I have been converting our file formats to that format anyway. But now it's time to grapple with the issue of images in Excel. When you think of a tabular representation of data like this in HTML, you think of cells containing (references to) images. In Excel, however, the cells contain data and somewhere else altogether is some DrawingML that specifies what the icons look like and where they go.

I have started doing some investigation of the DrawingML but quotes like this make me think I might have a steep learning curve:

http://openxmldeveloper.org/articles/2327.aspx
"The attribute “editAs” specifies how the drawing object is moved/resized when the rows and columns are resized. For the possible values and their details, refer to page 4765 of the ECMA Open XML specification document."

Ah yes, the infamous 6,000 page document submitted for standards approval. I sense some work ahead of me.

Thursday, 28 August 2008

Start 'em young

My 6.5 year old son was off school sick yesterday. I decided it's time to introduce him to the joys of coding. What better language than Logo to get someone started. I know, I know, it's really a symbolic processing language with a noble Lisp pedigree and so much more than turtle graphics, but turtle graphics seemed like a good place to start.

We used a browser-based Logo implementation at http://www.calormen.com/Logo. It's a site that won't win any beauty contests, but it seemed fairly functional. I explained the underlying metaphor of the turtle with a pen that can be raised or lowered and showed him how to draw a few simple things by a sequence of commands e.g. fd 100 rt 90 fd 100 rt 90 fd 100 rt 90 fd 100. That seemed to make sense once I explained how 90 degrees meant going from vertical to horizontal. This is the kid who asked me when he was four if two dots could ever make a curve, so I figured he'd be good.

He immediately wanted to try a few things, like moving the turtle forward 1,000. I just let him try and see. Whoa! The turtle goes out of the visible region. We cleared the screen (cs) and tried some more things. What happens if you go forward a really large number like 100000000000000. I was expecting an error but no it just went out of the visible region. What happens if you turn right one degree and draw?

Interestingly he said he was "asking" the turtle to do things although after listening to me referring to "commands" he started to say he was "commanding" it to do things. I left him to experiment on his own for a while.

Perhaps next time we'll do some control structures. Perhaps something simple like repeat 4 [ rt 90 fd 100].

Friday, 1 August 2008

F5 / F9

I am slowly but surely going mad. I am currently grafting some lovely shiny C# 3.5 code on to the side of our legacy Delphi.Win32 code. This means working in Visual Studio and the Delphi IDE simultaneously.

I doubt that I will ever love the Delphi IDE. OK, I am still annoyed that it was crashing 20 times per day (literally -- I took to obsessively recording them) until I applied the service pack, but at least it's stable now. The IDE lacks the most basic functionality: no moving the cursor back to a line before this one, no clicking on a point in the call stack and having it set the frame so you can inspect variables at that point, no examining the values of variables if an exception gets thrown. Still, I don't have to love it. Delphi is obviously circling the drain as a development environment. Their latest schedule of coming features makes it obvious they are a few years behind the curve. Coming real soon, Unicode strings! 64 bit will have to wait until the time of our children's children.

But that's not what's driving me mad. It's the F5/F9 thing. In Visual Studio F5 is run, F9 is set breakpoint. In Delphi it's exactly the opposite. For the past twenty odd years, I have used IDEs where F5 means run, so it's now burned into my brain. Having F5 mean "insert breakpoint" just seems profoundly wrong.

There doesn't seem to be a way to remap the keys in Delphi beyond choosing a Visual Studio emulation mode (it doesn't specify what version of VS). I could remap the keys in Visual Studio, but VS has things the way I like them.

In the interests of fair and balanced whining, I should point out that even Microsoft can't decide about the whole F5/F9 thing. I usually have a folder open on the Global Assembly Cache. I have to hit F5 to refresh it. In Outlook, refreshing (checking for new messages) is F9.

Saturday, 12 July 2008

It's the little things

Yesterday afternoon I was walking up the hill from work, headed to Starbucks to buy some coffee filters.

The area where I work is full of little boutiques that survive on a steady flow of tourists offloaded from package tour buses. One boutique that sells (as near as I can determine from glancing in the window) very expensive letter paper and body parts for Pinocchio dolls of various sizes had a sign in the window saying that it was open and "Back in 5 minutes". The door was ajar. The proprietor was nowhere to be seen. I was greatly amused that they had left the door open so customers could come in from the cold and look around.

Back to the coffee filters. At home we make American-style drip coffee, i.e. what they call "coffee" (with no modifiers) in the US. Coffee filters are something of a specialty item in New Zealand and surprisingly expensive in the supermarkets. At Starbucks the filters are about half the price of the supermarkets.

I picked two boxes of 100 filters and took them to the counter. The cashiers looked at the boxes as if they had never seen them before, turning them around hunting for the price sticker. They rang them up then asked if I wanted them to remove the stickers -- i.e. they thought these novelty items must be gifts. I just chuckled. I declined the special little carry bag too.

I guess we're not in Seattle any more.

Tuesday, 8 July 2008

Anticipatory medicine

My 6.5 year old son was asking why doctors can't cure the common cold. I explained that one problem is that the various viruses keep mutating, so you can't just have one medicine to solve the problem. He suggested that the scientists should anticipate the way that the viruses will mutate and make the medicine for that scenario. Then when the viruses mutate the medicine will get them.

OpenXML Packages

I am taking the various files that users refer to when documenting the process flow in their organization and putting them all in an OpenXML package (using System.IO.Packaging).

Office provides an additional layer around these packages to make it a bit easier to produce Office documents (which are just packages with a particular internal structure). See here for the SDK.

The Office SDK makes some things a little easier, although some of the names had me a bit confused at first (CustomXmlPart for example has nothing XML-ish about it, it's just a nice way to put things in the place in a .docx file where Word expects the custom XML to go). But with that extra layer you lose some of the functionality of the underlying System.IO.Packaging. For example, the Office SDK provides some nice functions to add images but you don't get to specify the compression. One JPG file ended up going from 276,216 bytes to 403,084 bytes, i.e. actually getting bigger. Using the underlying System.IO.Packaging, I am able to specify no compression for JPGs -- they have such low entropy all you do is add the overhead of the ZIP housekeeping bits.

Curiously, with both the Office SDK and the underlying classes you have to specify the image type. I couldn't find anything in the DotNet foundation classes to tell me the image type given a stream or even given a file name, so I had to bake something trivial. Fortunately, a lot of the time I know exactly what the image type is anyway.

DotNet Stream to IStream

Lately I have been grafting some new cloth on to our legacy code. In this case, that means writing C# COM servers that get called from legacy Delphi.Win32 code.

I have been taking the various files that together comprise a user's model of the process flow in their organization and bundling them together using System.IO.Packaging (a nice wrapper on a ZIP file).

In all the places in the legacy Delphi code where it might try to get a TFileStream (Delphi people like to start their class names with the letter T) it will instead be getting a TOleStream on a C# object that implements IStream.

Here's the kicker. C# Stream objects don't implement IStream so you need a wrapper. Blech. Luckily for me, Oliver Sturm has already posted a partial solution (it implements Read but not Write) here so I was spared too much grief. Then it was just a matter of regenerating the Delphi wrappers for the assembly and now we're as good as gold, as they say in this country.

Tuesday, 27 May 2008

Visual Studio templates

Visual Studio 2008 sticks a bunch of using statements at the top of any new C# source file. They include System.Collections.Generic and System.Linq. I tend to not use these for my current projects, but instead to use System.Collections.ObjectModel (where Collection<> lives) and System.Xml.Linq (where XElement etc live).

After a little poking around, here's what you do to change the template:

In %ProgramFiles%\Microsoft Visual Studio 9.0\Common7\IDE\ItemTemplates\CSharp\Code\1033\Class.zip

is a template called Class.cs. Edit it to say something like this:


using System;
using System.Collections.ObjectModel;
$if$ ($targetframeworkversion$ == 3.5)using System.Xml.Linq;
$endif$

namespace $rootnamespace$
{
  class $safeitemrootname$
  {
  }
}


Save it back into the zip file and run


devenv.exe /setup


Then restart Visual Studio and all is good.

One caveat: I am not sure exactly what features might get reset by running with the /setup option. It didn't trash my custom font settings at least.

Land of expensive books

Books are expensive in New Zealand. Always have been. Now that we are living here, we have become avid users of the public library, which is probably a Good Thing (R) (TM) anyway.

Over the weekend I decided to spend a gift card at Borders (the same chain as in the US). I had read a good review of Steve Skiena's "The algorithm design manual" -- $75.50 USD on BetterWorld including shipping. That's $100 NZD at the current exchange rate. Shipping from the US usually takes 7-10 days -- USPS doesn't ship by sea any more.

Borders didn't have it but they could special order it for the princely sum of $215 NZD. BetterWorld it is then.

This is completely nuts -- more than double the price of buying it from a US retail source. For items that might involve after sales support (e.g. electronics goods) I could understand paying a small premium. Labor costs in NZ are high after all. But this is a book we're talking about.

Tuesday, 20 May 2008

XLinq in C#

I have been converting some rather inelegant bits of C# code (stuff I had written, no reflection on anyone else) from the DotNet 2.0 XPathNavigable way of doing things to the DotNet 3.0 Linq way of doing things.

Using XLinq (Linq on XML) is fun. It's a shiny new hammer that you can use for banging in all sorts of nails, but you can get a bit carried away.

In my case, I have been extracting the externalNames (think, fully qualified names) of some XSOL variables and stuffing them in a Collection called variables. MSDN has trivial examples that are more or less like this
IEnumerable externalNames =
from externalName in root.Descendants("externalName")
select (string)externalName;

foreach (string name in externalNames)
{
  variables.Add(name);
}

That all seems simple enough: extract the contents of the descendant elements.

Of course, it can be collapsed to make things a little more elegant without making them more obscure:

foreach (string name in
from externalName in root.Descendants("externalName") select (string)externalName)
{
  variables.Add(name);
}

But then I get to thinking, why not do it all in one go? The result of all that Linq stuff is an IEnumerable. I can't implicitly convert it to a Collection but there is an overload for the constructor for Collection<> that will do the trick:

variables = new Collection(
(from externalName in root.Descendants("externalName") select (string)externalName)
.ToList());


So there you go. You can do it using the new shiny hammer but things are starting to look more complex than is necessary. Thinking about it some more, it really is sufficient to skip all that Linq and say
foreach (XElement externalName in root.Descendants("externalName"))
{
  variables.Add(externalName.Value);
}


This is not to say that XLinq is not a good thing. Clearly it would be wonderful for more complex queries.

Friday, 16 May 2008

Registry on 64 bit Windows

The world is slowly but surely moving to 64 bit but along the way there are a few wrinkles to work out.

At XSOL we develop tools that people use to model their business processes. They can do that as an end in itself e.g. for internal documentation or they can press a button and we produce a custom line of business application for them with a SQL back end etc. We use Python for application extensibiity.

A customer was having trouble getting our installer to run on their 64 bit machine. The installer checks for the presence of Python (which they have to install separately for licensing reasons) by looking in the registry. It was failing to detect that they had Python installed.

It transpires that our code that checked for the presence of Python was in a 32 bit DLL. Windows 2003 64 bit (and Vista 64 bit) does a bit of magic when 32 bit apps look at the registry, like the magic it does about the location of the "documents and settings" folder etc. Long story short, we had to create an entry in the right place just to fool our installer.

On a 32 bit OS we look at

HKEY_LOCAL_MACHINE\SOFTWARE\ActiveState \ActivePython \2.5.1.1

On a 64 bit OS running our 32 bit detection code we think we are looking there but we are actually looking at

HKEY_LOCAL_MACHINE\SOFTWARE\WOW6432Node \ActiveState \ ActivePython\2.5.1.1

There is of course a KB entry about this: http://support.microsoft.com/kb/896459

Monday, 5 May 2008

ASP.Net and localhost

Ah the Monday morning software patch routine. Time for a Windows update that wants a restart. After the restart my dual monitor configuration is toast yet again. By now the process for fixing dual mon is quite familiar: click some options, don't panic about the displays being upside down, restart the machine, then change the orientation for the monitor that wasn't working.

Then on to the next issue: ASP.Net debugging using Visual Studio's Cassini server on Vista. Issue #1, IPv6 problems -- edit C:\Windows\System32\drivers\etc\hosts, add an extra : for the localhost mapping i.e. it should say

127.0.0.1 localhost
:::1 localhost

Then on to the next issue: when I attempt to run the website in VS2008, I see that it has fired up Cassini on some random port but the port that IE thinks it is using is two less, e.g. is Cassini is using 49756, IE thinks we are using port 49754. It's consistently off by two. If I manually correct the port in the address bar in IE it's fine. The solution involves some tweaking of settings in Eset NOD antivirus:

http://forums.asp.net/p/1235447/2267983.aspx

There are some nice screen shots and detailed instructions that I won't bother to reproduce here. Suffice to say that you should pay close attention to putting a cross not a check mark beside devenv.exe.

This fix doesn't require a restart of your machine, but you will of course have to stop any current Cassini session.

Saturday, 3 May 2008

Modern ASCII art

I must confess that for the last few years I haven't been paying much attention to the mechanics of web page design. Lately I have been pottering in ASP.Net which has been a lot of fun. I was dimly aware that laying out my page using tables probably wasn't au courant so I have been doing a crash update course on the use of the div tag. No magic there and of course CSS is extremely straight forward.

Sometimes though you come across something done using the current technology that you never would have imagined was even possible. Check out this: a rendering of Homer Simpson done entirely with div tags. If you inspect the underlying source you can see that it's actually ASCII art.

Wednesday, 16 April 2008

Hotel California

Hello dedicated readers. I'm back after an extended period of radio silence.

The last few weeks I have been trying to move my cell phone from "on account" to "prepay". Prepay is quite popular in New Zealand, and has nice options not available to people on account, e.g. paying a few dollars per month for unlimited calls to a designated phone number. Since I mostly call my wife, that seemed best.

When I call customer service, the telco (who shall remain nameless) answers right away. Every time I call (five times until I started using their online feedback form) I have to explain why I want to go off account. I was on hold for more than 1.5 hours over various calls. The customer service representative assured me that they would call back within 48 hours (later revised to 48 business hours, later revised to five working days). It was starting to feel like the Hotel California -- "you can check out any time you like, but you can never leave". Speaking of golden oldies, the on-hold music is a raucous piece that loops for a while... then switches to Dionne Warwick:

I know I'll never love this way again,
so I keep holdin' on before the good is gone.
I know I'll never love this way again,
hold on, hold on.

I decided to use their web form. I suspected the worst and copied the text of my complaint to Notepad before hitting submit. The web form required me to create an online customer account to lodge my complaint. Sure enough, when I finally hit submit the submission failed because my credentials didn't match. Presumably they mean that I had created the ticket as an anonymous user but submitted as an authenticated user. I logged on and did it again from scratch.

When last I called, they had offered me three months free on our current phones. I thought this was because they valued me as a customer but it seems to have something to do with number portability. If I moved to their competitor I could keep the same number but they don't seem to have the software in place to let me move from account to prepay while retaining the same number. They offered me an additional two months free in the hope that they will have the software installed by the end of May. That's fine by me.

At $0 I can't complain about the cell phone plan. Cell phone plans are one of those things that are mysteriously more expensive in New Zealand than they should be. For $50 per month I get 120 minutes of calls within their network. Calls to landlines are 49 cents per minute. For the same price in Seattle I was getting 450 minutes shared between two phones with unused minutes rolling over and no distinction between calling a cell phone or a land line.