0

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.

Eugene Astafiev
  • 47,483
  • 3
  • 24
  • 45
Zoli
  • 841
  • 8
  • 31
  • It's useless to register a COM object that you cannot instantiate, co-creatable COM objects *must* have a public parameterless constructor, they must be creatable w/o any parameters. So, it's probably because the registration services reads/queries on the public constructor for some reason, and fails trying to load the Infragistics dependency for some other reason. – Simon Mourier Jan 09 '23 at 10:21
  • Thanks. I thought this too, but there IS a public parameterless constructor, I just did not add to the source sample above for simplicity. – Zoli Jan 09 '23 at 10:53
  • You can just register COM objects using the registry, there's nothing mysterious about COM registration. https://stackoverflow.com/a/35789844/403671 – Simon Mourier Jan 09 '23 at 11:08
  • @SimonMourier as I wrote, the wix/heat.exe which registers from code (so that is not our source code). – Zoli Jan 09 '23 at 12:30
  • @Jackdaw I was registering the same dll at the same location with both `regasm` and `RegistrationServices`. And (if I remember properly) even from the same command prompt. Note that, due to error `Provider type not defined. (Exception from HRESULT: 0x80090017)` I suspect this is something else then a general dll not found error. Not to mention `Assembly.LoadFrom(path)` succeeds before registration. – Zoli Jan 09 '23 at 12:33
  • you don't have to harvest anything, as I wrote there's nothing mysterious about COM registration, you can just declare the dll not as a COM one and add the required keys manually or use regasm from wix. Or use Procmon from sysinternals https://learn.microsoft.com/en-us/sysinternals/downloads/procmon filter on file & registry access and try to determine what causes your issue. – Simon Mourier Jan 09 '23 at 12:57
  • @SimonMourier, yes that is also possible. But that is also a workaround just as mine "solution". Note that wix/heat works with the other 22 assemblies, so I don't want to change the installer creation pipeline due to one mysterious case. So in fact the case is "solved", but I want to know what/why happened? – Zoli Jan 09 '23 at 13:26
  • @Jackdaw ,yes, _dll not found_ I meant dll or dependencies. I tried again, output: [link](https://i.ibb.co/fncXpX9/heat-reg-error.png) . Same cmd prompt, regasm succeeds code reg fails for the same dll (full file/path names are blurred). **Note** you should have not deleted your comments, as those were good hints, could have been helpful for others, also now my comments make no sense.. – Zoli Jan 09 '23 at 13:44
  • You are only loading the assembly but that assembly has dependencies and the error is likely about those not being loaded/present. Maybe try hooking the AppDomain's AssemblyResolve event and if it's looking for an assembly that is not loaded then you also load that. – AndrewS Jan 11 '23 at 14:18
  • Calling Regasm from WiX is a form of self registration and all self registration is considered an antipattern. You should harvest the data and have WiX/MSI do the work for you. Please see my answer below. – Christopher Painter Jan 11 '23 at 15:59

1 Answers1

0

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/dotnet/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.

Christopher Painter
  • 54,556
  • 6
  • 63
  • 100
  • Thanks. I have to say, this sounds strange. 1) We have dozens of native and managed com classes, and all are harvested perfectly by heat, except the mentioned one. However, after the change (workaround) in this class, this assembly is also harvested properly. 2) I have the heat source front of me, I don't see it calls regasm. (although not the latest code). 3) The code sample in the question above `...regSvcs.RegisterAssembly()...` is from the heat code. I think it does similar the regasm /regfile + snapshot, but internally using its `RegistryHarvester ` class. I have not looked deep into it. – Zoli Jan 12 '23 at 15:49
  • well ya, they call the api function comparble to regasm regfile. I've worked on some very large .net productlines with thousands of assemblies and yes the vast majority will be fine. However you occassionaly come across something that needs what I describe above. As your title says, regasm worked. So therefore the approach that I mention will work. – Christopher Painter Jan 12 '23 at 17:53
  • Yes, indeed, but my question is about why the `RegistrationServices.RegisterAssembly()` fails and `regasm` succeeds? What's causing this difference? – Zoli Jan 13 '23 at 09:55
  • 1
    You tagged the question with wix so I was assuming what your actual question was is how do I get my installer to work so I can deploy this app. :) For me, COM is a thing of the past so I don't do much beyond the occassional [ComVisble(true)]. My focus is that of a WiX consultant and this is my bag of tricks for when a customer presents something like this and they don't know and don't want to "fix" anything in their code. Just make the installer work. – Christopher Painter Jan 13 '23 at 13:49
  • BTW if you figure it out be sure to mention me. Always interested in more trivia to add to my awareness. – Christopher Painter Jan 13 '23 at 13:50
  • Yeah sure. It's just a mistery for me, and bugging me. So maybe I will debug when have some time. If I figure out something, will share here. – Zoli Jan 13 '23 at 20:30