简体   繁体   中英

Registering .net assembly for COM succeeds with regasm but fails using RegistrationServices.RegisterAssembly

This is one of the strangest issue I have encountered.

There is a .net assembly, which is exposed to COM.
If you register it with regasm /codebase my.dll - it is sucessfully registered, and can be used.
However, if you register it from code using RegistrationServices.RegisterAssembly() :

[...]
    RegistrationServices regSvcs = new RegistrationServices();
    Assembly assembly = Assembly.LoadFrom(path);

    // must call this before overriding registry hives to prevent binding failures on exported types during RegisterAssembly
    assembly.GetExportedTypes();

    using (RegistryHarvester registryHarvester = new RegistryHarvester(true))
    {
        // ******** this throws *********
        regSvcs.RegisterAssembly(assembly, AssemblyRegistrationFlags.SetCodeBase);
    }

Then it throws exception:

Could not load file or assembly 'Infragistics2.Win.UltraWinTree.v9.2, Version=9.2.20092.2083,
Culture=neutral, PublicKeyToken=7dd5c3163f2cd0cb' or one of its dependencies. 

Provider type not defined. (Exception from HRESULT: 0x80090017)

This error has very little resource on the.net, and looks like related to some security(?) cryptography(?) feature.

After long-long hours, I figured out what causes this (but don't know why):

If there is a public class with a public constructor in the assembly with a parameter UltraTree (from the referenced assembly 'Infragistics2.Win.UltraWinTree.v9.2'), then you cannot register from code, but with regasm only.
When I changed the have a public function Init(UltraTree tree) , then it works, I can register from code. So:

// regasm: OK / RegistrationServices.RegisterAssembly(): exception
public class Foo
{
   public Foo(UltraWinTree tree) { .. }
}
Foo foo = new Foo(_tree);

-------------- vs --------------

// regasm: OK / RegistrationServices.RegisterAssembly(): OK
public class Foo
{
   public Foo() {}
   public void Init(UltraWinTree tree) { .. }
}
Foo foo = new Foo();
foo.Init(_tree);

So I could workaround by passing UltraWinTree in a new Init() function instead of constructor, but this is not nice, and I want to know the reason, what the heck is going on?

Anyone has any idea? Thanks.

PS:
Okay, but why we want to register from code? As we use Wix to create installer, which uses heat.exe to harvest registry entries (which are added during asm registration), so heat.exe does assembly registration from code.

I've been dealing with this for years so this is the only answer you need to read:

Heat calls regasm /regfile. So does InstallShield when you tell it to. If you read this page:

https://learn.microsoft.com/en-us/do.net/framework/tools/regasm-exe-assembly-registration-tool

There's a very important caveat in the remarks section.

You can use the /regfile option to generate a.reg file that contains the registry entries instead of making the changes directly to the registry. You can update the registry on a computer by importing the.reg file with the Registry Editor tool (Regedit.exe). The.reg file does not contain any registry updates that can be made by user-defined register functions. The /regfile option only emits registry entries for managed classes. This option does not emit entries for TypeLibIDs or InterfaceIDs.

So what to do? Use Heat to generate most of the metadata. Then on a clean machine, (snapshot VM best) us a registry snapshot and compare tool such as InCntrl3 or InstallWatch Pro and sniff out what additional meta regasm writes to the registry. Finally massage that into your Wxs code.

Then on a cleam machine test the install. The result should work and not require any custom actions in the install.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM