Olav Aukan Getting information off the Internet is like taking a drink from a fire hydrant…

31Aug/10

Creating an ActiveX control in .Net using C#

A while back I had a client request that I write an ActiveX control for use on their corporate intranet. I had never done this before, and most of the examples I could find online were either really old, incomplete or based on using C++ and MFC. It's safe to say that my C++ skills are not quite up to the job, so for me it was really a requirement to be able to do this with C#. It took me about an hour or two to write the code for the control, but it took almost three days to successfully package and deploy it as a .cab file... So to save others from wasting their time like I did, I'll document my findings in this post.

Steps

  1. - Create a new Class Library project in Visual Studio
  2. - Create a new class that inherits from UserControl
  3. - Create a new interface that exposes the controls methods and properties to COM interop
  4. - Make the control class implement the new interface
  5. - Mark the control as safe for scripting and initialization
  6. - Create a .msi installer for the control
  7. - Package the control in a .cab file for web deployment
  8. - Initialize and test the control with JavaScript

1. Create a new Class Library project in Visual Studio

I'm using Visual Studio 2008, but other versions should work as well.

  1. After starting Visual Studio click File -> New -> Project and select Class Library under C#.
  2. Call the project 'AxControls' and click OK.

2. Create a new class that inherits from UserControl

  1. Rename 'Class1.cs' to 'HelloWorld.cs', making sure to rename the class name as well.
  2. Add a project reference to System.Windows.Forms.
  3. Make the HelloWorld class inherit UserControl.

3. Create a new interface that exposes the controls methods and properties to COM interop

  1. Right click the project in Visual Studio and click Add -> New Item.
  2. Select 'Interface' from the list of components, name it 'IHelloWorld.cs' and click Add.
  3. Edit the 'IHelloWorld.cs' file so it looks like this:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace AxControls
    {
        [ComVisible(true)]
        [InterfaceType(ComInterfaceType.InterfaceIsDual)]
        [Guid("41E85D5D-C57A-4386-B722-4031D0B1E1B7")]
        public interface IHelloWorld
        {
            string GetText();
        }
    }
    

We now have a COM visible interface with a single method 'GetText()'.

[ComVisible(true)] makes the interface visible to COM.
[InterfaceType(ComInterfaceType.InterfaceIsDual)] sets the COM interface type to Dual, see InterfaceTypeAttribute Class on MSDN.
[Guid("41E85D5D-C57A-4386-B722-4031D0B1E1B7")] let's us manually assign a GUID to the interface. Use guidgen.exe to generate your own.

4. Make the control class implement the new interface

Make the HelloWorld class implement the IHelloWorld interface and have the GetText() method return a string of your choice. This is what the file might look like:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;

namespace AxControls
{
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [Guid("1FC0D50A-4803-4f97-94FB-2F41717F558D")]
    [ProgId("AxControls.HelloWorld")]
    [ComDefaultInterface(typeof(IHelloWorld))]
    public class HelloWorld : UserControl, IHelloWorld
    {
        #region IHelloWorld Members

        public string GetText()
        {
            return "Hello ActiveX World!";
        }

        #endregion
    }
}

We now have a COM visible control that implements the IHelloWorld interface.

[ComVisible(true)] makes the control visible to COM, see ComVisibleAttribute Class on MSDN.
[ClassInterface(ClassInterfaceType.None)] indicates that no class interface is generated for this class, see ClassInterfaceType Enumeration on MSDN.
[Guid("1FC0D50A-4803-4f97-94FB-2F41717F558D")] let's us manually assign a GUID to the control, see GuidAttribute Class on MSDN. Use guidgen.exe to generate your own.
[ProgId("AxControls.HelloWorld")] is a "user friendly" ID that we'll use later from JavaScript when initiating the control, see ProgIdAttribute Class on MSDN.
[ComDefaultInterface(typeof(IHelloWorld))] sets IHelloWorld as the default interface that will be exposed to COM, see ComDefaultInterfaceAttribute Class on MSDN.

5. Mark the control as safe for scripting and initialization

By default IE will not allow initializing and scripting an ActiveX control unless it is marked as safe. This means that we won't be able to create instances of our ActiveX class with JavaScript by default. We can get around this by modifying the browser security settings, but a more elegant way would be to mark the control as safe. Before you do this to a "real" control, be sure to understand the consequences. I found an ancient (1996) MSDN article that explains this here. We will mark the control as safe by implementing the IObjectSafety interface.

  1. Right click the project in Visual Studio and click Add -> New Item.
  2. Select 'Interface' from the list of components, name it 'IObjectSafety.cs' and click Add.
  3. Edit the 'IObjectSafety.cs' file so it looks like this:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.InteropServices;
    
    namespace AxControls
    {
        [ComImport()]
        [Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")]
        [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
        interface IObjectSafety
        {
            [PreserveSig()]
            int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions);
    
            [PreserveSig()]
            int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions);
        }
    }
    
  4. Make the HelloWorld class implement the IObjectSafety interface. The end result should look something like this:
    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Windows.Forms;
    using System.Runtime.InteropServices;
    
    namespace AxControls
    {
        [ComVisible(true)]
        [ClassInterface(ClassInterfaceType.None)]
        [Guid("1FC0D50A-4803-4f97-94FB-2F41717F558D")]
        [ProgId("AxControls.HelloWorld")]
        [ComDefaultInterface(typeof(IHelloWorld))]
        public class HelloWorld : UserControl, IHelloWorld, IObjectSafety
        {
            #region IHelloWorld Members
    
            public string GetText()
            {
                return "Hello ActiveX World!";
            }
    
            #endregion
    
            #region IObjectSafety Members
    
            public enum ObjectSafetyOptions
            {
                INTERFACESAFE_FOR_UNTRUSTED_CALLER = 0x00000001,
                INTERFACESAFE_FOR_UNTRUSTED_DATA = 0x00000002,
                INTERFACE_USES_DISPEX = 0x00000004,
                INTERFACE_USES_SECURITY_MANAGER = 0x00000008
            };
    
            public int GetInterfaceSafetyOptions(ref Guid riid, out int pdwSupportedOptions, out int pdwEnabledOptions)
            {
                ObjectSafetyOptions m_options = ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_CALLER | ObjectSafetyOptions.INTERFACESAFE_FOR_UNTRUSTED_DATA;
                pdwSupportedOptions = (int) m_options;
                pdwEnabledOptions = (int) m_options;
                return 0;
            }
    
            public int SetInterfaceSafetyOptions(ref Guid riid, int dwOptionSetMask, int dwEnabledOptions)
            {
                return 0;
            }
    
            #endregion
        }
    }
    

[ComImport()] IObjectSafety is a native interface so we have to redefine it for managed .Net use. This is done with the ComImport() attribute, see ComImportAttribute Class on MSDN.
[Guid("CB5BDC81-93C1-11CF-8F20-00805F2CD064")] This is the GUID of the original IObjectSafety interface. Do not change it.
[InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] sets the COM interface type to Unknown, see InterfaceTypeAttribute Class on MSDN.

This is just a simple implemetation of the IObjectSafety interface that will mark the control as safe. In "real life" there would probably be some sort of logic to determine if the control is safe or not.

6. Create a .msi installer for the control

Before an ActiveX control can be used it must be installed and registered on the client. This can be done in a number of ways, from manually editing the registry to using regasm.exe, but we're going to create a Vistual Studio setup project to handle the installation for us.

  1. Right click the Visual Studio solution, select Add -> New Project and select Setup Project under Other Project Types.
  2. Call the project 'AxControlsInstaller' and click OK.
  3. Right click the 'AxControlsInstaller' project, select Add -> Project Output, select 'Primary output' from the 'AxControls' project and click OK.
  4. Right click 'Primary output from AxControls (Active)' and select Properties.
  5. Change the Register property from 'vsdrpDoNotRegister' to 'vsdrpCOM'.
  6. Right click the 'AxControlsInstaller' project and select Build.

The installer should now be located in the AxControlsInstaller's output folder (bin\Debug or bin\Release). In the corporate domain this .msi file can de run manually on the client, or automatically with a Group Policy.

7. Package the installer in a .cab file for web deployment

For public web sites we obviously can't deploy our ActiveX control to the client with a Group Policy. In this case we're gonna have to use Internet Explores built-in ability to download and install controls that are packaged in .cab files.

  1. Download the Microsoft Cabinet Software Development Kit.
  2. Unpack the kit to a local folder and copy Cabarc.exe to the 'AxControlsInstaller' folder.
  3. Create a new file named 'AxControls.inf' in the 'AxControlsInstaller' folder and add the following content:
    [version]
    signature="$CHICAGO$"
    AdvancedINF=2.0
    
    [Add.Code]
    AxControlsInstaller.msi=AxControlsInstaller.msi
    
    [AxControlsInstaller.msi]
    file-win32-x86=thiscab
    clsid={1FC0D50A-4803-4f97-94FB-2F41717F558D}
    FileVersion=1,0,0,0
    
    [Setup Hooks]
    RunSetup=RunSetup
    
    [RunSetup]
    run="""msiexec.exe""" /i """%EXTRACT_DIR%\AxControlsInstaller.msi""" /qn
    
  4. Click the AxControlsInstaller project and then click the Properties window (View -> Properties Window if it's not visible).
  5. Click the '...' button next to the PostBuildEvent property and add the following content:
    "$(ProjectDir)\CABARC.EXE" N "$(ProjectDir)AxControls.cab" "$(ProjectDir)AxControls.inf" "$(ProjectDir)$(Configuration)\AxControlsInstaller.msi"
    
  6. Right click the 'AxControlsInstaller' project and select Build.
  7. There should now be a 'AxControls.cab' file in the 'AxControlsInstaller' folder.

NB! Make sure you use ANSI encoding for the 'AxControls.inf' file or you will be unable to install the control.

8. Initialize and test the control with JavaScript

  1. Right click the AxControls solution, select Add -> New Project and select 'ASP.Net Web Application' under 'Web'.
  2. Call the project 'WebAppTest' and click OK.
  3. Right click the 'WebAppTest' project, select Add -> New Item and select 'HTML Page'.
  4. Call it 'index.html' and click OK.
  5. Add the following content to index.html:
    <html>
        <head>
    
            <object name="axHello" style='display:none' id='axHello' classid='CLSID:1FC0D50A-4803-4f97-94FB-2F41717F558D' codebase='AxControls.cab#version=1,0,0,0'></object>
    
          <script language="javascript">
    
            <!-- Load the ActiveX object  -->
            var x = new ActiveXObject("AxControls.HelloWorld");
    
            <!-- Display the String in a messagebox -->
            alert(x.GetText());
    
          </script>
        </head>
        <body>
        </body>
    </html>
    

    Note that 'classid' matches the GUID of the HelloWorld control.

  6. Right click 'index.html' and select 'Set as start page'.
  7. Right click the 'WebAppTest' project and select 'Set as startup project'.
  8. Copy 'AxControls.cab' from the 'AxControlsInstaller' folder to the same folder as index.html.
  9. Uninstall the control from the client by going to Control Panel -> Programs and Features, selecting 'AxControlsInstaller' on the list and clicking Uninstall. This forces Internet Explorer to download and install the .cab file and is an important step in case you've already installed the control.
  10. Run the application (F5). This will open 'index.html' in Internet Explorer.
  11. Internet Explorer will display a security warning, asking if you want to install 'AxControls.cab'. Click Install.
  12. When the page loads it should display a message box with the string you defined in HelloWorld's GetText() method.

If the message box displayed without any more warnings or errors we've implemented everyting correctly.

UPDATE

I forgot to write that you have to register the assembly containing the ActiveX control for COM interop. Right-click the project, select Properties, go to Build and check the "Register for COM interop" checkbox. This should solve the error some of you are seeing about "Automation Server can’t create this object" and similar error messages. You might need to add the [ComVisible(true)] attribute to the methods and properties you are exposing as well, but I haven't had time to test this.

Comments (1760) Trackbacks (8)
  1. xn3834 http://marintrustandtitle.com/#viagra-par-internet-cialis-20mg-xy cialis pharmacy lilly icos lc875nj3892ly9644 hc893nr8400nn4786

  2. You’re an inspiration with your work

  3. I do trust all of the ideas you’ve offered on your post.

    They are really convincing and will certainly work.
    Nonetheless, the posts are too short for beginners.
    May you please extend them a bit from subsequent time?
    Thank you for the post.

  4. I blog frequently and I really thank you for your information. This article has truly peaked my interest.
    I will book mark your site and keep checking for new details about once
    per week. I opted in for your Feed as well.

  5. The National actor and television producer did some essential manner choices within the amount of
    80s. A sizable piece would go to Wall Street oil investors.
    Salvador Cyprian is one of many men searching for elementary.

    http://Zon9.xyz/24693

  6. One of the better images I’ve seen lately

  7. I am in fact delighted to read this weblog posts which contains plenty of
    valuable information, thanks for providing these information.

  8. zw3330 http://zithromaxzpc.top/#2-gm-buy-zithromax-bs buy zithromax without prescription ear infection wx5444vc264ai3989 sw1144su3315df4026

  9. Link exchange is nothing else however it is just placing
    the other person’s weblog link on your page at proper place and other person will also do same
    in support of you.

  10. za6951 cheap viagra wu7804aw1359hs8708 jp6150hs275

  11. oj6503 canadian pharmacy rh3243iv6341lb4625 np553jz5454

  12. The new XRumer 16.0 – revolution in online promotion:
    artificial intelligence will help you to attract customers so effectively,
    more than ever!

  13. ll724 price for cialis online pharmacy ki7666uc8974lr3537 kt176ay3353

  14. pw3507 canadian pharmacy vb2252wo2857oh6916 of3710pr2691

  15. Tidak hanya kita dapat mengikuti aksi polisi Indonesia dalam penggerebekan, tetapi kita juga diajak untuk menyaksikan kejadian yang sesungguhnya terjadi di lapangan serta melihat sed wgOggPlayer. Wspominalem w tekscie, ze jest mozliwosc skrocenia okresu wypowiedzenia. Chwilowka Sms W Ciagu 15 Minut Athol mezczyzna zostal aresztowany i odbylo sie w dniu 5 000 pienieznych kaucja po policje Missouri przygotowuje sie do realizacji czlowieka, ktory wasnt w stanie odwolac swoje Hrabstwo Hawaii urzednicy obrony cywilnej powiedziec lawy puna awansowala jest zamkniecie niektorych szkol przed Kilauea szybko postepujacej lawy na wyspie Hawaii. kredyty hipoteczne kielce

  16. Всех с Праздником! http://www.ведущий-крым.рф – Ведущий Крым!

  17. Действующим веществом является экстракт кантаридина, кантаридин это вещество, содержащееся в лимфе насекомого шпанская мушка и вызывающее прилив сексуального возбуждения.
    левитра купить в петрозаводске У подавляющего большинства мужчин при достижении определённого возраста возникают проблемы со здоровьем, о которых они не любят говорить.

  18. Специально для Вас «Модуль совместных покупок для форумов phpbb3
    Форум – родитель современных социальных сетей, поскольку сам таким и является. На форумах всегда и с удовольствием будут собираться люди, ведь на них так удобно и приятно общаться в отличие от обычных сайтов. Именно поэтому форумы проще развиваются в массы, быстрее привлекая к себе новых Пользователей. Все крупные и успешные СП основаны именно на форумах и не собираются изменять свое «место жительства»!
    Присоединяйтесь к современному и доходному бизнесу с системой на автомате!

    Модуль совместные покупки для phpbb

  19. viagra purchase viagra online buy viagra viagra cialis viagra viagra discreet shipping viagra online without prescription usa

    http://vshopmeds.bid

  20. I enjoyed reading this a lota€¦ I truly hope to read more of your posts in the future, so Ia€™ve bookmarked your phorum. But I couldna€™t just bookmark it, oh no.. When I see quality websitea€™s like this one, I like to share it with others So Ia€™ve designed a backlink to your web site (from a€¦ http://www.toppoligrafia.pl->ukraine.dkc.ru/bitrix/rk.php?goto=http://bunga-bunga-party.com/about-escort-agency/

  21. Долго искал инфу про ремонт элекроники в сети, пока вдруг не нашел этот сайт q9_asd_en.pdf

    На меня это произвело большое впечатление.
    Всем удачи!

  22. Мы предлагаем самые качественные и надёжные прогнозы на футбол,
    теннис и гарантируем прибыль при их использовании.
    Получите лучшие прогнозы на спорт с проходимостью от 75%.
    betonsport.info

  23. титан гель отзывы врачей цена

    А это в свою очередь означает преумножение размера самого его величества пениса.

  24. Unidentified and quickly, not ask, but if you need it at any time http://bestmen.site/money

  25. Добрый день всем учасникам olavaukan.com

    Прошу помочь. Ткните носом в нормальный портал для скачивания дровишек на асус.
    Нужны непубличные версии, на раскрученных ресурсах ничего нет. Может есть приватная подборка?

    За мной должок!

  26. https://wpnull.org – Best Premium Themes and Plugins for WordPress-less than 10% of the original!
    100% guarantee,
    100% free of malware,
    100% moneyback!!!

  27. Также современные нетрадиционные методы лечения предлагают восстановление потенции при помощи специальных физических упражнений, а также при помощи чрезмерного секса, чтобы стимулировать половой орган на его постоянную работу – https://vk.com/page-136902939_52930494

  28. Нашла сайт, который помог мне похудеть.
    Решила поделится с вами.
    Безопасное похудение

  29. где получить займ в новогодние праздники
    оформить можно тут https://goo.gl/xgvBsr

  30. cl9996 http://buy-viagra-iner.com order viagra sg3739qn5882it1606 bo471vu2464

  31. You probably have to consult a lot of methods on the internet to make money easily but none of it has ever worked?
    If I told you that there is a simple and effective way to make big money almost without doing anything, you would not believe me? And yet it is possible!
    All you need is :
    - An internet connection

    You can earn at least $ 200 a day !
    Interesting is not it?
    It’s so easy to win money with this simply!
    Do not you dream of earning enough money to buy anything you want or to pay off your debts?
    And all this while working from home and especially when you want it?
    So I will tell you how to really make money! First, it’s totally free!

    Interested? So go here to make money today! All the instructions can be downloaded here : http://bit.ly/2iOPBpR


Leave a comment