ActiveX, installation doesn't work

2019-01-08 01:43发布

问题:

I want to create and deploy an ActiveX plugin, somethign really small. I just want to display a message box.

I've created a library project for it and compile it to a DLL. The plugin works when I register the dll via command line (regasm) on one machine.

So I now need to create an installer, put it inside a CAB file and sign it.

I've installed Visual Studio 2008 to use Setup Project project (but I can use the InstallShield from 2010+ if anyone can explain how to do it). I add my dll to the Application Folder and mark it as Register = vsdrpCOM and I build it and I get a .msi and .exe.

I think I only need the msi file and not the exe. I created a setup.inf file with the fallowing content :

[version]
signature="$CHICAGO$"
AdvancedINF=2.0

[Setup Hooks]
hook1=hook1

[hook1]
run=msiexec.exe /i "%EXTRACT_DIR%\ActiveInstaller.msi" /qn

To build the .cab file I use the command makecab /f build.ddf. Here is my .ddf file :

.Set DiskDirectoryTemplate=;
.Set CabinetNameTemplate=ActiveInstaller.cab
../ActiveInstaller.msi
../setup.inf

This generated the cab file. Now I need to sign it. At the moment I used a self signed certificate that I generated and installed on my pc (when I check the sign cab file, windows says it's safe). I use another command line which is signtool signwizard (I also try to sign it manually from here). Then the file is signed and I upload it to my website, I launch the website, I'm prompted to install the plugin and I install it.

But then, it does not work, I have no idea why. I've tried so much stuff, with different installer, different options, different inf file, different signing method, etc.

The only tutorial that I found are at least three years old, I don't know if anything as changed since they were wrote. Here are the main link that I used : http://blogs.msdn.com/b/asiatech/archive/2011/12/05/how-to-develop-and-deploy-activex-control-in-c.aspx and another one http://www.codeproject.com/Articles/24089/Create-ActiveX-in-NET-Step-by-Step

SOLUTION :

So, everything Pepo said is true so I marked his answer as accepted. I also found someone who the whole source code (including how to create the .exe which runs the msi) here.

回答1:

The problem might be that you try to run msiexec.exe and this exe is not in the cab file. See this question (be sure to scroll down to the incredibly helpful sample code posted by Roey 5 Aug 2009). Try to either create a setup.exe that will run process msiexec.exe and install your msi or make an installer with a bootstrap setup.exe file and include both in the cab.

Also, you might want to read about non-admin activex installations.

Your activex dll has to be signed and your activeX should implement this interface

/// <summary>
/// Options supported for the IObjectSafety interface 
/// </summary>
[Serializable]
[ComVisible(true)]
public enum ObjectSafetyOptions
{
    /// <summary>
    /// Indicates that the caller of the interface identified by riid might be untrusted.
    /// </summary>
    INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
    /// <summary>
    /// Indicates that the data passed into the interface identified by riid might be untrusted.
    /// </summary>
    INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
    /// <summary>
    /// Indicates that the caller of the interface identified by riid knows to use IDispatchEx.
    /// </summary>
    INTERFACE_USES_DISPEX = 0x00000004,
    /// <summary>
    /// Indicates that the data passed into the interface identified by riid knows to use IInternetHostSecurityManager.
    /// </summary>
    INTERFACE_USES_SECURITY_MANAGER = 0x00000008
};

/// <summary>
/// Provides methods to get and set safety options.
/// The IObjectSafety interface should be implemented by objects that have interfaces which support "untrusted" clients, such as scripts.
/// It allows the owner of the object to specify which interfaces must be protected from "untrusted" use.
/// </summary>
[ComImport()]
[Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
public interface IObjectSafety
{
    /// <summary>
    /// Gets the safety options supported by an object and the safety options that are currently set for that object.
    /// </summary>
    /// <param name="iid">An interface identifier for a given object</param>
    /// <param name="pdwSupportedOptions">Receives the address of a DWORD representing all the options supported for the interface identified by riid.</param>
    /// <param name="pdwEnabledOptions">Receives the address of a DWORD representing all the options currently enabled for the interface identified by riid.</param>
    /// <returns>Returns one of the following values:
    /// S_OK - the object is safe for loading
    /// E_NOINTERFACE - the riid parameter specifies an interface that is unknown to the object</returns>
    [PreserveSig]
    long GetInterfaceSafetyOptions(ref Guid iid, out int pdwSupportedOptions, out int pdwEnabledOptions);

    /// <summary>
    /// Returns whether an object is safe for initialization or scripting, as specified.
    /// </summary>
    /// <param name="iid">An iInterface identifier for the object to be made safe.</param>
    /// <param name="dwOptionSetMask">A mask representing the options to be validated.</param>
    /// <param name="dwEnabledOptions">A DWORD representing all the options currently enabled for the interface identified by riid. </param>
    /// <returns>Returns one of the following values:
    /// S_OK - the object is safe for loading
    /// E_NOINTERFACE - the riid parameter specifies an interface that is unknown to the object
    /// E_FAIL - the dwOptionSetMask parameter specifies an option that is not supported by the object</returns>
    [PreserveSig]
    long SetInterfaceSafetyOptions(ref Guid iid, int dwOptionSetMask, int dwEnabledOptions);
};

I used advanced installer to create msi installer and I set dll registration. Advanced installer then generated all necessary registry keys.

In my test the final inf file was

[version]
signature="$CHICAGO$"
AdvancedINF=2.0
[Deployment]
InstallScope=user|machine
[Setup Hooks]
install=install
[install]
run="""%EXTRACT_DIR%\runmsi.exe""" """%EXTRACT_DIR%\simpleactivex.msi"""

Please note the triple quotes. They are important.

I used this ddl

.Set DiskDirectoryTemplate=.
.Set CabinetNameTemplate=simpleactivex.cab
runmsi.exe
simpleactivex.msi
simpleactivex.inf

And I build cab using this commands

"c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe" sign /sha1 9A15DC8F51773C557BA2F75CF155F8CBD367A8E1 /tr http://tsa/tsa /d SimpleActiveX /du "http://yourcompany.com" /v runmsi.exe simpleactivex.msi

makecab /V3 /F make.ddl

"c:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\signtool.exe" sign /sha1 9A15DC8F51773C557BA2F75CF155F8CBD367A8E1 /tr http://tsa/tsa /d simpleactivex /du "http://yourcompany.com" /v simpleactivex.cab

runmsi.exe is a dummy exe file that runs msiexec with given parameters. Alternatively you could use exe installer or bootstrap exe and msi installer. The important part to note is that IE will not allow to run anything outside the cab file. Therefore you have to do this hack.

When debugging I used this dummy html page

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN" > 

<html>
  <head>
    <title>WebForm1</title>
  </head>
  <body style="margin-top: 0px; margin-left: 0px;">
  <OBJECT id="SimpleActiveXCtrl" classid="clsid:C0082E22-8A19-4600-8332-D31C4055291A" codebase="SimpleActiveX.CAB"></OBJECT>    

<script language="javascript">
    // da sa volat z JS metody ActiveXu alebo nastavit property
function OpenActiveX()
{
    try
    {
        alert(document.SimpleActiveXCtrl.HelloWorld("hello"));
    }
    catch(Err)
    {
        alert(Err.description);
    }
}   


</script>

  </body>
</html>