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();
                regAsm.WaitForExit(); // based on comboFusion's comment below (in the comment section of this blog)
            }
            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.

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

  1. 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. 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.

  3. 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.

  4. Thanks so much for posting this…had the hardest time figuring out how to get the dll registered properly. Using 64 bit windows, the dll was being copied to the “C:\Windows\SysWOW64″ directory… just something to be mindful of.

  5. For a hat trick, we’re trying to raise an event inside of a C# Net assembly in CAB as you’ve described above so that it’s caught by a javascript for=”myidtag” event=”myevent” language=”javascript” function in the browser.

  6. A few questions…

    For MyActiveX.dll you used “RegisterServer=no”.

    1. Why “no” instead of “yes”? Is it because you specifically register MyActiveX.dll within the “RegNetX” program?
    2. What if I use “RegisterServer=yes” in the scenario you describe in the article?

    Thank you so much in advance and again thank you for this great article!!!

  7. Steve, besides your article above, I also found this great article about ActiveX and deploy from 2006: http://www.simple-talk.com/dotnet/visual-studio/build-and-deploy-a-.net-com-assembly/

    Peter, about JS Callback and .NET events… here are just a few articles that succesfully helped me creating what you also need(ed). I am sure if you still did not get to the bottom of it (I am sure that by now you did, but maybe someone else didn’t), you certainly will by using the knowledge collected in the links below:

    - http://www.codeproject.com/KB/cs/cs_com_obj_for_javascript.aspx?display=Print
    - http://stackoverflow.com/questions/26536/active-x-control-javascript
    - http://www.codeproject.com/KB/dotnet/extend_events.aspx?display=Print
    - http://stackoverflow.com/questions/150814/how-to-handle-an-activex-event-in-javascript

  8. Useful FIX suggestion for RegNetX program: right after regAsm.Start(), consider using regAsm.WaitForExit() method.

    Why? Because this small line of code can save you a lot of time and nerves… so that when deploy occurs (extraction of files from .cab and ActiveX components registration) the web page DOESN’T have the chance finish loading BEFORE the ActiveX component is succesfully registered.

    The thing is that if the web page is loaded before regAsm process manages to register the ActiveX component, you will be forced to reload the page again in order for the ActiveXObject to be successfully initalized.

    It took me approximately 8 hours to figure out why my ActiveXObject wasn’t succesfully initialized when the web page was loaded, since there can be so many things that can go wrong during the deploy af an ActiveX component, so here I am sharing this useful tip with you, to give back to the community. :) Hopefully I managed to save someone from unnecessary time loss (and nerves).

    Oh, and BTW, I tested Steve’s example in Win7 32 and WinXP 32 and it works well.

    The only thing I would like to ask: is it really necessary to extract dll files in System32? Can deploy be done without “poluting” the client computer?

  9. ComboFusion,

    I updated the code above in the blog article to reflect your change — thanks for the 8-hour time saving tip. That’s a day of work saved by one line of code. The next person that will need this code will be very thankful.

    Regarding extracting to System32, I did this based on the variables available to the old CabArc.exe program — though if a person uses the newer cab creation tools, they may find that such is not necessary. I agree with you, why clog up the users system32 directory? Very bad form. I must admit, it was not my first choice.

    Steve

  10. Very bad form. I must admit, it was not my first choice.

    Yes, but it works well! :) Until I find a better solution, your solution was far the best so far!

  11. I’m facing an issue wherein IE tries to install every time I navigate to the page even though the control is installed and available. Any ideas?

  12. Hi,
    Your RegNetX work form me, Thanks a lot
    but, Its not working on 64 bit Windows7

    Hoping for your help

  13. The best I can do now is approve your comment and let’s see if someone here will see it and contact you. I’d have to recompile it and test it on my own Windows 7 box. Shoot, who knows what it’ll do now that 8 is out.

  14. Wow. Thanks for this post.
    This work on my WebSite.
    But Just 1 Problem, When dll version updated.
    First of all, my english is very very bad. Sorry ^^;;

    1) Rebuild dll file
    2) Modify inf file(FileVersion : 1,0,0,1 -> 1,0,0,2)
    3) Remake cab file & upload to server
    4) Modify object version

    5) IE Browser Refresh => Old Version(1,0,0,1) executed.. Not updated.

    How Can I AutoUpdate DLL.
    Please Help me.

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>