Error writing large files using OpenXML


We recently encountered a problem writing large files using OpenXML from an Office add-in. When run as an execuable the same code doesn’t fail. The relevant part of the error message is “Unable to determine the identity of domain” which is from an IsolatedStorageException even though, at the time of the error, the code in question was writing to a MemoryStream. So what’s going on?

Under the skin, OpenXML uses the System.IO.Packaging classes to read and write Office documents. After some investigation we discovered that when any part exceeds 8MB the Packaging API begins to use IsolatedStorage using a user/domain store as a scratch pad area. That’s great when code is running as an executable. However when running as an add-in the managed code is started by COM infrastructure. Unfortunately, when COM creates the AppDomain instance it doesn’t provide any evidence so the call to create an IsolatedStorage stream fails. The error message “Unable to determine the identity of domain” means exactly that: the AppDomain has no identity.

So what’s the solution? Well if you are entirely reliant on COM to use your assembly, for example because you want to allow users to use your code in a script, then you may be out of luck. For those developing using VSTO there is, apparently, the option to install the add-in using ClickOnce which is supposed to address the issue in some way.

Our solution has been to fix the AppDomain. Once an assembly has been registered for COM interop any classes can be created directly by COM. However Microsoft used to recommend that assembly classes are instantiated indirectly using a COM shim. Fortunately for us this is our pattern so we’re able to provide valid evidence when creating the AppDomain.

Information and Links

Join the fray by commenting, tracking what others have to say, or linking to it from your blog.


Other Posts

Write a Comment

Take a moment to comment and tell us what you think. Some basic HTML is allowed for formatting.

Reader Comments

Thank you very much for the post. Can you be more specific in how you went about fixing the AppDomain? I’m having the exact same problem when processing large documents and I’m not too familiar with the AppDomain concept. Thanks again.

An AppDomain is the .NET Framework’s unit of isolation. By default an application has one AppDomain. You will access details of one AppDomain using the static method “System.AppDomain.CurrentDomain”. But doesn’t have to be that way. An application can create additional AppDomains using the static method “System.AppDomain.CreateDomain”.

Suppose you have an application and some operations need to be isolated from others then you can create an additional AppDomain. Once created,interacting with the additional domain is like dealing with a COM object in the sense you can only deal with public classes/methods/properties and information passing between two AppDomains must be marshalled (usually classes must be serializable) just as is the case with COM interop.

The garbage collector works by AppDomain so another use for AppDomains is to run an operation in an alternative AppDomain knowing that when you terminate the sub-domain that the garbage collector will reclaim all memory whether you explicitly released/disposed it or not.

Creating an AppDomain is straigh-forward though there are lots of properties you can set. One of those properties is the evidence. Here’s an example of creating a simple AppDomain and providing evidence:

<br />
AppDomainSetup setupInfo = new AppDomainSetup();<br />
setupInfo.ApplicationBase = "<some path>";<br />
setupInfo.PrivateBinPath = @"</some><some legal place to look for assemblies>";<br />
setupInfo.ConfigurationFile = "my.config";<br />
var appDomain = AppDomain.CreateDomain("MySubDomain", this.GetType().Assembly.Evidence, setupInfo);<br />
</some>

You can’t create new evidence from within one AppDomain. But this pattern works well enough for us to be able to use System.IO.Packaging.

If you want to create new evidence you’d need to create your own .NET loader (which you can do though it must be done from, say, C++).