Search code examples
wixwindows-installercom-interopgac

How to architect Wix installer upgrade to move GAC components into regular folders?


I have an existing installation that includes .NET assemblies which are registered for COM Interop and installed in the GAC. These DLLs are used only by an unmanaged EXE that we also include in the installation.

A simple sample component (obfuscated from the real thing) looks like this:

  <Component Id="MyApp.ClientInterop.dll" Guid="{9A9C2B62-531C-4E60-ABD2-EDD447643C4F}">
    <Class Id="{963057C7-83B4-4602-87B4-B0AB9D3D149A}" Context="InprocServer32" Description="MyApp.ClientInterop.ClientAppUpdate" ThreadingModel="both" ForeignServer="mscoree.dll">
      <ProgId Id="MyApp_Interop.ClientAppUpdate" Description="MyApp.ClientInterop.ClientAppUpdate" />
    </Class>
    <File Id="MyApp.ClientInterop.dll" Name="MyApp.ClientInterop.dll" KeyPath="yes" Assembly=".net">
      <TypeLib Id="{08963623-70A8-4DD8-A8DA-EAF209919797}" Description="MyApp_ClientInterop" Language="0" MajorVersion="1" MinorVersion="0">
        <Interface Id="{207BB6C4-3054-4853-BA20-40D99EA6ACFE}" Name="IClientAppUpdate" ProxyStubClassId="{00020424-0000-0000-C000-000000000046}" ProxyStubClassId32="{00020424-0000-0000-C000-000000000046}" />
      </TypeLib>
    </File>
    <RegistryValue Root="HKCR" Key="CLSID\{963057C7-83B4-4602-87B4-B0AB9D3D149A}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{963057C7-83B4-4602-87B4-B0AB9D3D149A}\InprocServer32\1.0.0.0" Name="Class" Value="MyApp.ClientInterop.ClientAppUpdate" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{963057C7-83B4-4602-87B4-B0AB9D3D149A}\InprocServer32\1.0.0.0" Name="Assembly" Value="MyApp.ClientInterop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf47a01d50a7b0f5" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{963057C7-83B4-4602-87B4-B0AB9D3D149A}\InprocServer32\1.0.0.0" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{963057C7-83B4-4602-87B4-B0AB9D3D149A}\InprocServer32" Name="Class" Value="MyApp.ClientInterop.ClientAppUpdate" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{963057C7-83B4-4602-87B4-B0AB9D3D149A}\InprocServer32" Name="Assembly" Value="MyApp.ClientInterop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=cf47a01d50a7b0f5" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{963057C7-83B4-4602-87B4-B0AB9D3D149A}\InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
  </Component>

For several reasons I won't get into here, we would like to re-architect the application to have the DLLs live in the folder with the executable, rather than in the GAC.

Can this be accomplished with a major upgrade, without requiring the user to completely uninstall the prior version. Our installer is sequenced so the old version is removed during finalization because most upgrades change only a small percentage of the components.

I was initially planning on the following changes to each GAC component:

  1. Remove Assembly=".Net" attribute
  2. Generate new Guid on the Component element

This should work with regard to the file system. However, I'm not sure what will happen to the, , , , and elements. If I do not change those, I suspect they will be removed when the old version uninstalls at the end, leaving a broken installation.

Is this an accurate interpretation? Does this mean I would need to force new Interface/Typelib/CLSID values for all of my Interop classes? I have over 1000 classes exposed through Interop on this project.

Could I change the sequencing so the old product is installed first? I am concerned with doing that because it means I have to stay with that sequence until I can be sure all of my customers are beyond that release. It will also significantly increase the installer time (that has been previously tested).

Perhaps there is another option that I am not aware of?


Solution

  • Doing the major upgrade with RemoveExistingProducts at the beginning would be safer, a clean slate so to speak. However I think it would work with REP at the end if:

    1. Your assemblies get a new component guid so that the older product assemblies are removed from the GAC.

    2. Registry entries are not versioned so they are always overwritten. You should be able to keep the same WiX component IDs, but you'll need to a add CodeBase location anyway so it should be easy to see if it has all worked. Obviously after the upgrade you need no assemblies in the GAC, assemblies in the normal file system, and CodeBase registry entries that point to them, and clients that can call them ok :)