Sunday, September 22, 2013

Registration-Free COM with ActiveX Controls

In my current project, I have beside other things, a form with an ActiveX control on it. Additionally, I am using registration free COM, meaning that the COM information is stored in a manifest file instead of the registry.

Everything worked fine, until I tried to create the form a second time. Also this worked without problems, but not in the Visual Studio debugger. Here I got the strange exception:

System.NotSupportedException: Unable to get the window handle for the 'xxx' control. Windowless ActiveX controls are not supported.
at System.Windows.Forms.AxHost.EnsureWindowPresent()
at System.Windows.Forms.AxHost.InPlaceActivate()
at System.Windows.Forms.AxHost.TransitionUpTo(Int32 state)
at System.Windows.Forms.AxHost.CreateHandle()
at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
at System.Windows.Forms.Control.CreateControl(Boolean fIgnoreVisible)
at System.Windows.Forms.AxHost.EndInit()

After hours of thinking, debugging, code stripping and so on (to be honest, mainly from a colleague of me), we found the solution: the used manifest was not complete. The manifest was created using mt.exe with the typelib of the control. This manifest looked like

<file name="..." hashalg="SHA1">
  <comClass clsid="..." tlbid="..." description="..." />
  <typelib tlbid="..." version="..." helpdir="" />

When we compared this with the entries in the registry, we saw in the registry much more things. And also in the assembly manifest documentation are more attributes mentioned. Therefore we tried to add as much attributes to the manifest as possible (even if we did not understand every bit completely). And viola, now the error was gone!

In total, we added 4 attributes (in our case):

<file name="..." hashalg="SHA1">
  <comClass clsid="..." tlbid="..." description="..." threadingModel="..." progid="..." miscStatus="..." />
  <typelib tlbid="..." version="..." helpdir="" flags="..." />

I hope this could help, if you have a similar issue.

Wednesday, September 18, 2013

AppDomains and user.config

Previously I had a problem with an application using different AppDomains. In one AppDomain I wrote some settings to an user.config file. And then I got in another AppDomain the following exception:
System.Configuration.ConfigurationErrorsException: Configuration system failed to initialize
---> System.Configuration.ConfigurationErrorsException: Unrecognized configuration section userSettings. (C:\Users\uuuuuuuu\AppData\Local\cccccc\aaaaaaaaaaaaaa_Url_4v0elz3yo0gytsdhg5vusobffefqs0so\\user.config line 3)

This was very strange since in this AppDomain I even didn’t use any user.config. After some hours of investigation, I found the problem. The user.config file will be written to

<Profile Directory>\<Company Name>\<App Domain>_<Evidence Type>_<Evidence Hash>\<Version>\user.config
  • <Profile Directory>: %APPDATA% or %LOCALAPPDATA%
  • <Company Name>: value of AssemblyCompanyAttribute, trimmed to 25 characters, invalid characters replaced by '_'
  • <App Domain>: friendly name of the current AppDomain, trimmed to 25 characters, invalid characters replaced by '_'
  • <Evidence Type> and <Evidence Hash>: some magic from AppDomain’s evidence
  • <Version>: value of AssemblyVersionAttribute
In my case, all these properties had the same value. Therefore the whole stuff got mixed up: 
  • company name: ok this is by purpose the same
  • version: I generated the version from the build number for all assemblies, but also this is not so uncommon
  • evidence: I created the new AppDomains with
    AppDomain.CreateDomain(appDomainName, null, appDomainSetup);
    The second parameter is the evidence for the new AppDomain. If it is null, the evidence from the current AppDomain will be taken. Also understandable.
  • App domain: this was the sticky point. I used for the AppDomain’s name the full name of the main assembly. Since I used a pattern like Company.Application.Subsystem..., this name was simply too long. The first 25 characters were all the same...
So the solution was quite easy: I just had to change the AppDomain’s name. But the trail to the solution took its time. Maybe this blog can accelerate your search a little bit.

BTW: Finally I checked the source code of System.Configuration.ClientConfigPaths.cs. This helped me a lot to understand the problem.