Part 1: Building an ActiveX Control in C# (with CAB file via CabArc)

I will show you how to write MyActiveX.cs, turn it into a DLL, place it on an HTML page and distribute it with a CAB file. But I will start in reverse order.

I will start with the HTML code and the CAB file. If you are going to build an ActiveX control in C#, then you need to be able to send it around (by the way, ActiveX controls only work in IE, not Chrome or Firefox, so you will only send it around to IE browsers).

When IE tries to access an ActiveX file (and it does not already have it on the hard drive), then the HTML code tells it to download a CAB and install it. For the sake of simplicity, let’s say your index.html file points to your ActiveX control this way:

<OBJECT id="MyActiveX" name="MyActiveX" 
classid="clsid:3026b51e-a3ff-4587-9ed2-36d7d527bbe6" VIEWASTEXT 
codebase="MyActiveX.cab#Version=1,0,2,0">

MyActiveX.cab is in the same folder as index.htm, and if the MyActiveX.dll is missing, then the CAB file is downloaded and installed. But you have to make that CAB file. To build the CAB file (which is like a ZIP file), you need to use the free CABARC program. You will pass it the name of the ActiveX, and a few support tools that will get stuffed in there with it. I will explain all the files in time, but here is the command line to create a CAB:

cabarc -s 6144 n MyActiveX.cab MyActiveX.dll RegNetX.exe setup.cmd MyActiveX.inf

(You can Download Microsoft’s cabarc.exe here).

This creates the CAB file, MyActiveX.cab, and inside it is the other four files. The final file, MyActiveX.inf, tells the CAB file what to do and what is inside it. It tells us that the ActiveX DLL will go to DestDir=11 (which is a secret Microsoft code meaning windows\sytem32). DestDir=11. Write it down. Use it. The -s command means to add on an extra 6144 bytes so that we can sign this CAB file when all is said and done (I will talk about signing CAB files in Part 2 or Part 3 — do not underestimate how important it is to sign your CAB file and ActiveX file if you want clients to be able to install and use your software).

Let’s look first at MyActiveX.inf (which tells the CAB file how to install on the client):


[Setup Hooks]
hook1=hook1

[hook1]
run=%EXTRACT_DIR%\setup.cmd

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

[Add.Code]
MyActiveX.dll=MyActiveX.dll
MyActiveX.inf=MyActiveX.inf
setup.cmd=setup.cmd
RegNetX.exe=RegNetX.exe

[MyActiveX.dll]
file-win32-x86=thiscab
clsid={3026b51e-a3ff-4587-9ed2-36d7d527bbe6}
RegisterServer=no
FileVersion=1,0,2,0
DestDir=11

[MyActiveX.inf]
file=thiscab

[setup.cmd]
file=thiscab

[RegNetX.exe]
file=thiscab

If you inspect this file, you see that the CAB file will run setup.cmd as the install script when it arrives at a target machine. There is a good article on CAB files and hooks on the Microsoft web site, but that article fails to give you the extra steps required to release a .NET based ActiveX control. Still, go read it and get familiar with the subject, or keep moving along here and you’ll get the gist.

The reason you need all this specialty code for a .NET project, is because a CAB file uses RegSvr32 to register an ActiveX, and that won’t work for a .NET DLL. You need to use Microsoft’s RegAsm to register a .NET ActiveX, but CAB files don’t know about RegAsm. The install location of RegAasm is in your .NET directory. So Setup.cmd is called as the install program, and it handles the registration.

That’s the trick! Setup.cmd is the trick. And here is Setup.cmd


@echo off
set MyACTIVEX=%windir%\system32\MyActiveX.dll
copy MyActiveX.dll %MyACTIVEX%
RegNetX

You may be wondering: What is RegNetX?

It is a little install program I wrote. All it does is install my one ActiveX program. I compile the install program from the command line using csc (which comes free with .NET and is in your Framework directory):

csc RegNetX.cs

Here is the code:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Diagnostics;

namespace RegNetX
{
    class Program
    {
        static void Main(string[] args)
        {

	    string DLL = Environment.SystemDirectory + "\\MyActiveX.dll";

 	    Console.WriteLine("Usage: RegNetX [-u]");
 	    Console.WriteLine("    Will register " + DLL);
 	    Console.WriteLine("   -u unregisters it");

            // This is the location of the .Net Framework Registry Key
            string framworkRegPath = @"Software\Microsoft\.NetFramework";

            // Get a non-writable key from the registry
            RegistryKey netFramework = Registry.LocalMachine.OpenSubKey(framworkRegPath, false);

            // Retrieve the install root path for the framework
            string installRoot = netFramework.GetValue("InstallRoot").ToString();

            // Retrieve the version of the framework executing this program
            string version = string.Format(@"v{0}.{1}.{2}\",
              Environment.Version.Major,
              Environment.Version.Minor,
              Environment.Version.Build);

            // Return the path of the framework
            string path = System.IO.Path.Combine(installRoot, version);

            if (path == null)
                path = installRoot + version;

            try
            {
                Process regAsm = new Process();
                regAsm.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                regAsm.StartInfo.CreateNoWindow = true;
                regAsm.StartInfo.WorkingDirectory = path;
                regAsm.StartInfo.FileName = "regasm.exe";
                if (args.Length == 0)
                  regAsm.StartInfo.Arguments = "/silent /codebase " + DLL;
                else
                  regAsm.StartInfo.Arguments = "/unregister " + DLL;
 		
		Console.WriteLine(path + "regasm.exe " + regAsm.StartInfo.Arguments );
                regAsm.Start();
            }
            catch
            {
                Console.WriteLine("Error running regasm.exe " + args[0]);
            }
        }
    }
}

RegNetX will find the .NET install and call RegAsm. And note well: I hard coded everything to install the ActiveX DLL into the windows\system32 directory. It makes life so much easier if you know that %windir% is a real environment variable that will exist on your target client. When your code is actually installing, it is in a free fall, and your scripts are operating mostly blind, as it were, so you need to try to land in some known fixed location. For that reason, I coded setup.cmd with a fixed install location with an environment variable that will most likely exist. And how nice that DestDir in the INF file is also pointing to %windir%\system32.

After writing this article, I realized that the CAB building tool, cabarc, is old. It is no longer distributed (though it was on my Windows 7 machine). There is another CAB building tool from Microsoft called “MakeCab”. In Part 2 (if I write Part 2), I will go deeper into all this and show the actual ActiveX control. If there is a Part 3, it may be about MakeCab. Given the positive feedback I have received so far, I may just write that second article.

This entry was posted in .NET, Programming. Bookmark the permalink.

5 Responses to Part 1: Building an ActiveX Control in C# (with CAB file via CabArc)

  1. Roger Coates says:

    This saved be from a very long weekend today, it was good to eventually solve this mystery on a Friday afternoon, when I’d been trying most of the week!

    Thanks!!

  2. Pingback: Lots to learn « Roger's Stuff

  3. srives says:

    Roger, it is nice to know that some pain was averted. I went back and re-read the article after your comments. When I wrote the article, I changed all the variable names to generic values, “MyActiveX”, for example. And in that, the setup.cmd had a little mistake in it so I fixed it.

    I also added a paragraph at the end hammering home the importance of using %windir% and the corresponding DestDir in the INF file.

  4. Sheik says:

    Wonderful article for me. Thanks for this post. I will check if that works for me. Eagerly looking forward to Part 2 and Part 3. Can I contact you directly somehow? Thanks.

  5. srives says:

    Sure Trenton. Email me at steve.rives@gmail.com. It may take a bit of time to reply as I won’t be around my computer this weekend. But feel free to ask, and I’ll give you whatever code I’ve written that relates.