Search code examples
comwix

How can I get my WiX product script to correctly install my active x control?


The situation I have written a custom Active X control for use within another, NON-WEB, application. when I manually run "regasm /codebase MyMacTest.dll" the control is registered and it can be used within the application using a jscript interpreter. This means I need to create an ActiveXObject etc... .

So my next step is to create an install MSI using Wix toolset 3.11. According to what I have read I need to run Heat against the dll to create the output wxs content. I did that and added it to my product.wxs compiled and ran. It deployed my dll correctly, verified the registry entry however it is Not working in the application.

My c# code:

using System;
using System.Runtime.InteropServices;

namespace MyMacTest
{
    [ProgId("MyMacTest.MacroTest")]
    [ClassInterface(ClassInterfaceType.None)]
    [Guid("765563f5-55b0-41e1-9f49-311ec88f6ba2")]
    [ComVisible(true)]
    public class MacroTest
    {
        public MacroTest()
        {

        }

        public string GetMessage()
        {
            return $"This is a test for {DateTime.Now}";
        }
    }
 }

The Heat output for the compiled DLL:

<?xml version="1.0" encoding="utf-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi">
<Fragment>
    <DirectoryRef Id="TARGETDIR">
        <Directory Id="dir39B22699688E51DCD8DCBB99A47E835B" Name="Debug" />
    </DirectoryRef>
</Fragment>
<Fragment>
    <DirectoryRef Id="dir39B22699688E51DCD8DCBB99A47E835B">
        <Component Id="cmp88AF0B935698304B52D96A0CEA4F7661" Guid="PUT-GUID-HERE">
            <Class Id="{765563F5-55B0-41E1-9F49-311EC88F6BA2}" Context="InprocServer32" Description="MyMacTest.MacroTest" ThreadingModel="both" ForeignServer="mscoree.dll">
                <ProgId Id="MyMacTest.MacroTest" Description="MyMacTest.MacroTest" />
            </Class>
            <File Id="filC0F476996EBA80AC7C8F27AA886F48E0" KeyPath="yes" Source="SourceDir\Debug\MyMacTest.dll" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="Class" Value="MyMacTest.MacroTest" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="Assembly" Value="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="CodeBase" Value="file:///[#filC0F476996EBA80AC7C8F27AA886F48E0]" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="Class" Value="MyMacTest.MacroTest" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="Assembly" Value="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
            <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="CodeBase" Value="file:///[#filC0F476996EBA80AC7C8F27AA886F48E0]" Type="string" Action="write" />
        </Component>
    </DirectoryRef>
</Fragment>

My modified product with the heat fragment:

<?xml version="1.0" encoding="UTF-8"?>
<Wix xmlns="http://schemas.microsoft.com/wix/2006/wi"><?define MyMacTest_TargetDir=$(var.MyMacTest.TargetDir)?>
<Product Id="*" Name="MyMacSetup" 
       Language="1033" 
       Version="1.0.0.0" 
       Manufacturer="Mega Software Stuff" 
       UpgradeCode="b3e8921b-22da-4f05-a79f-36cafde09fce">
    <Package InstallerVersion="200" Compressed="yes" InstallScope="perMachine" />

    <MajorUpgrade DowngradeErrorMessage="A newer version of [ProductName] is already installed." />
    <MediaTemplate EmbedCab="yes"/>

    <Feature Id="ProductFeature" Title="MyMacSetup" Level="1">
        <ComponentGroupRef Id="ProductComponents" />
  <ComponentRef Id="cmp88AF0B935698304B52D96A0CEA4F7661"/>
    </Feature>
</Product>

<Fragment>
<Directory Id="TARGETDIR" Name="SourceDir">
  <Directory Id="ProgramFilesFolder">
    <Directory Id="Bp" Name="BPE">
      <Directory Id="Addin" Name="AddIns">
        <Directory Id="INSTALLFOLDER" Name="MyCustom" />
      </Directory>
    </Directory>
  </Directory>
</Directory>
</Fragment>

<Fragment>
    <ComponentGroup Id="ProductComponents" Directory="INSTALLFOLDER">
        <Component Id="MyMacTest.dll" Guid="21b3cee3-1da3-4f48-89db-7c2f6cf11431">
          <File Id="MyMacTest.dll" Name="MyMacTest.dll" Source="$(var.MyMacTest_TargetDir)MyMacTest.dll" />
        </Component>
    </ComponentGroup>
</Fragment>

<!-- This segment gotten from heat: "c:\Program Files (x86)\WiX Toolset v3.11\bin\heat" file MyMacTest.dll -out mymactest.wxs  -->
<Fragment>
<DirectoryRef Id="INSTALLFOLDER">
  <Component Id="cmp88AF0B935698304B52D96A0CEA4F7661" Guid="b33671b4-2f75-44bc-8435-f16c5f9b7268">
    <Class Id="{765563F5-55B0-41E1-9F49-311EC88F6BA2}" Context="InprocServer32" Description="MyMacTest.MacroTest" ThreadingModel="both" ForeignServer="mscoree.dll">
      <ProgId Id="MyMacTest.MacroTest" Description="MyMacTest.MacroTest" />
    </Class>
    <!--<File Id="filC0F476996EBA80AC7C8F27AA886F48E0" KeyPath="yes" Source="$(var.MyMacTest_TargetDir)MyMacTest.dll" />-->
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}" Value="" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="Class" Value="MyMacTest.MacroTest" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="Assembly" Value="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0" Name="CodeBase" Value="file:///[#MyMacTest.dll]" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="Class" Value="MyMacTest.MacroTest" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="Assembly" Value="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="RuntimeVersion" Value="v4.0.30319" Type="string" Action="write" />
    <RegistryValue Root="HKCR" Key="CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32" Name="CodeBase" Value="file:///[#MyMacTest.dll]" Type="string" Action="write" />
  </Component>
</DirectoryRef>
</Fragment>
</Wix>

My question on all of this:

After spending a couple of days I have found that there seem to be a lot of messages on this, but nothing that gives me a step by step example or explanation on how to create a wix script that will perform the "regasm /codebase" that I need to do. Kind of confusing. This is my first wix/MSI project.

What am I missing and/or what do I need to know to make this work? Is there a good step by step somewhere, that i haven't found yet, that I can be pointed to?

Update with diffs

manual registration with regasm /codebase

[HKEY_CLASSES_ROOT\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}]
@="MyMacTest.MacroTest"

[HKEY_CLASSES_ROOT\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\Implemented Categories]

[HKEY_CLASSES_ROOT\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]

[HKEY_CLASSES_ROOT\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32]
@="mscoree.dll"
"ThreadingModel"="Both"
"Class"="MyMacTest.MacroTest"
"Assembly"="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///C:/Users/Kevin/OneDrive/Documents/MyMacTest.DLL"

[HKEY_CLASSES_ROOT\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0]
"Class"="MyMacTest.MacroTest"
"Assembly"="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///C:/Users/Kevin/OneDrive/Documents/MyMacTest.DLL"

[HKEY_CLASSES_ROOT\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\ProgId]
@="MyMacTest.MacroTest"

===========================================

msi install

[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}]
 @="MyMacTest.MacroTest"

[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\Implemented Categories]

[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\Implemented Categories\{62C8FE65-4EBB-45e7-B440-6E39B2CDBF29}]
@=""

[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32]
@="mscoree.dll"
"ThreadingModel"="Both"
"Class"="MyMacTest.MacroTest"
"Assembly"="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///C:\\Program Files (x86)\\BPE\\AddIns\\MyCustom\\MyMacTest.dll"

[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\InprocServer32\1.0.0.0]
"Class"="MyMacTest.MacroTest"
"Assembly"="MyMacTest, Version=1.0.0.0, Culture=neutral, PublicKeyToken=583df5a1a2a0a8da"
"RuntimeVersion"="v4.0.30319"
"CodeBase"="file:///C:\\Program Files (x86)\\BPE\\AddIns\\MyCustom\\MyMacTest.dll"

[HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{765563F5-55B0-41E1-9F49-311EC88F6BA2}\ProgID]
@="MyMacTest.MacroTest"

Not sure why the MSI install added WOW6432Node to the paths... but these are the differences.

Thanks!


Solution

  • UPDATE: Be aware that COM is bitness aware. In your example you are building a 32bit MSI that will install the COM server in the 32bit registry. You may be calling it from a 64bit process and that would not work.

    The way you are doing generally works. To troubleshoot you will want to:

    1) Create a VM and snapshot it

    2) Install your MSI

    3) Run a registry diff tool such as Install Watch Pro or InControl. Take a snapshot of the registry.

    4) Manually run your regasm /codebase command from an elevated command prompt.

    5) Take another snapshot of the registry and run a diff report.

    6) Examine the differences for meaningful relevant differences and author them into your WXS source.

    7) Rebuild the MSI

    8) Apply the VM Snapshot to get back to a clean state.

    9) Install the MSI and retest functionality.

    If you are wondering why heat didn't work by itself the answer has to do with COM. Sometimes the sniffing techniques just don't work. In the case of .NET COM Interop / COM Visible assemblies sometimes the answer lies in this caveat. In your case this might have to do with your use of the ClassInterface attribute.

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

    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). Note that the .reg file does not contain any registry updates that can be made by user-defined register functions. Note that the /regfile option only emits registry entries for managed classes. This option does not emit entries for TypeLibIDs or InterfaceIDs.

    If you are unable to resolve this, I have 25 years experience working through these types of issues. If you are able to share the DLL and give me a code snippet to smoke test with I could create a WiX snippet that properly registers the assembly.

    PS- I would merge those two components. The COM/Registry elements should belong to the same component as the file they are for.