Click here to Skip to main content
15,868,419 members
Articles / Web Development / HTML

C# COM Object for Use In JavaScript / HTML, Including Event Handling

Rate me:
Please Sign up or sign in to vote.
4.92/5 (29 votes)
22 Apr 2009CPOL6 min read 181.5K   2.5K   74   54
A complete example of how to create a C# COM object for use in JavaScript / HTML, including event handling

Introduction

I wanted to be able to use a pre-built .NET object inside a web browser. After searching the web (including CodeProject), I found that a possible solution would be to create a .NET COM component and use the ActiveXObject in JavaScript to interact with the COM object. A Google search turned up many helpful articles, but nothing that showed how to create a COM component in C#, surface .NET events through COM, and use the COM object in JavaScript to my satisfaction. In this article, I will put together all the pieces to show how to implement a complete solution, to use C# COM objects inside JavaScript.

Overview

This article describes how to create a C# COM Object (using Visual Studio .NET 2005) that can be used in JavaScript inside a web browser (only tested on IE 8 RC on Windows Vista). Complete source code for the COM object, as well as a simple web page that demonstrates how to use the COM object is provided, including how to make calls into the COM Object, and how to handle .NET events raised from the COM object.

Creating the C# COM Library

Create a new C# Class Library project called MyComComponent, and enable the setting “Register for COM interop” in the build settings for the project. If you are using Vista, you will need to run Visual Studio as an administrator to build your project after changing this setting. Exclude Class1.cs from the project.
Add a class named MyComObject.cs to the project, change the access modifier to public, and import the System.Runtime.InteropServices namespace:

C#
using System;
using System.Runtime.InteropServices;

namespace MyComComponent
{
    public class MyComObject
    {
    }
} 

Add the interfaces that define the operations and events that will be exposed to COM. Add empty interface definitions named IComObject and IComEvents to the MyComObject.cs file:

C#
using System;
using System.Runtime.InteropServices;

namespace MyComComponent
{
    public class MyComObject
    {
    }

    public interface IComObject
    {
    }

    public interface IComEvents
    {
    }
}

Expose the MyComObject class and the IComObject and IComEvents interfaces to COM. First, add the attribute [ComVisible(true)] to the class and interfaces.

C#
using System;
using System.Runtime.InteropServices;

namespace MyComComponent
{
    [ComVisible(true)]
    public class MyComObject
    {
    }

    [ComVisible(true)]
    public interface IComObject
    {
    }

    [ComVisible(true)]
    public interface IComEvents
    {
    }
}

Since we are defining our own custom COM interface, we need to tell the Type Library Exporter to not generate an interface for us. To do this, add the ClassInterface attribute to the MyComObject class with value of ClassInterfaceType.None.

C#
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class MyComObject
{
}

We need to tell the Type Library Exporter that the interface for IComEvents is IDispatch in order to get events to be raised out of our COM object. To do this, add the InterfaceType attribute to the IComEvents interface, with value of ComInterfaceType.InterfaceIsIDispatch.

C#
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IComEvents
{
}

Now we can start decorating the MyComObject with the interface definitions. IComObject is simple enough to implement, just by explicitly implementing it. The Type Library Exporter will automatically expose all events in the IComObject interface for us.

C#
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
public class MyComObject : IComObject
{
}

Adding the IComEvents interface is a little different. Use the ComSourceInterfaces attribute on the MyComObject class, with typeof(IComEvents) as the value. The ComSourceInterfaces attribute is used to define the list of interfaces that are exposed as COM event sources.

C#
[ComVisible(true)]
[ClassInterface(ClassInterfaceType.None)]
[ComSourceInterfaces(typeof(IComEvents))]
public class MyComObject : IComObject
{
}

Each type exposed from COM has a globally unique ID. Since we haven't defined the GUID’s, the Type Library Exported will do this for us. However, we want to be able to use this GUID to load the type, so we will now add the GUIDs to all three types. Use the guidgen utility to do this.

We now have the code structure in place for a COM object that explicitly implements an interface, and has an events source interface defined. The code at this point should be something like:

C#
using System;
using System.Runtime.InteropServices;

namespace MyComComponent
{
    [Guid("4794D615-BE51-4a1e-B1BA-453F6E9337C4")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IComEvents))]
    public class MyComObject : IComObject
    {
    }

    [Guid("4B3AE7D8-FB6A-4558-8A96-BF82B54F329C")]
    [ComVisible(true)]
    public interface IComObject
    {
    }

    [Guid("ECA5DD1D-096E-440c-BA6A-0118D351650B")]
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IComEvents
    {
    }
}

Now we will add some methods to the interfaces. To IComObject, add a method called MyFirstComCommand that accepts a string as an argument and returns an integer, as well as a method called Dispose that has no parameters and no return value.

C#
[Guid("4B3AE7D8-FB6A-4558-8A96-BF82B54F329C")]
[ComVisible(true)]
public interface IComObject
{ 
    [DispId(0x10000001)]
    int MyFirstComCommand(string arg);

    [DispId(0x10000002)]
    void Dispose();
}

To IComEvents, add a single method called MyFirstEvent that accepts a string as an argument and has no return value. Note the absence of the event keyword.

C#
[Guid("ECA5DD1D-096E-440c-BA6A-0118D351650B")]
[ComVisible(true)]
[InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
public interface IComEvents
{
    [DispId(0x00000001)]
    void MyFirstEvent(string args);
}

All that’s left to do is to implement the methods and events in the MyComObject class. The implementation of MyFirstCommand just raises the MyFirstEvent event and returns a random number. The Dispose method displays a message box. Add the following code to the MyComComponent class, and add a reference to System.Windows.Forms.dll to the project:

C#
[ComVisible(false)]
public delegate void MyFirstEventHandler(string args);

public event MyFirstEventHandler MyFirstEvent;

public int MyFirstComCommand(string arg)
{
    if (MyFirstEvent != null)
        MyFirstEvent(arg);
    return (int)DateTime.Now.Ticks;
}

public void Dispose()
{
    System.Windows.Forms.MessageBox.Show("MyComComponent is now disposed");
}

Now we have a fully functional COM object, written in C#. The code for the COM object should look like this:

C#
using System;
using System.Runtime.InteropServices;

namespace MyComComponent
{
    [Guid("4794D615-BE51-4a1e-B1BA-453F6E9337C4")]
    [ComVisible(true)]
    [ClassInterface(ClassInterfaceType.None)]
    [ComSourceInterfaces(typeof(IComEvents))]
    public class MyComObject : IComObject
    {
        [ComVisible(false)]
        public delegate void MyFirstEventHandler(string args);

        public event MyFirstEventHandler MyFirstEvent;

        public int MyFirstComCommand(string arg)
        {
            if (MyFirstEvent != null)
                MyFirstEvent(arg);
            return (int)DateTime.Now.Ticks;
        }

        public void Dispose()
        {
            System.Windows.Forms.MessageBox.Show("MyComComponent is now disposed");
        }
    }

    [Guid("4B3AE7D8-FB6A-4558-8A96-BF82B54F329C")]
    [ComVisible(true)]
    public interface IComObject
    {
        [DispId(0x10000001)]
        int MyFirstComCommand(string arg);

        [DispId(0x10000002)]
        void Dispose();
    }

    [Guid("ECA5DD1D-096E-440c-BA6A-0118D351650B")]
    [ComVisible(true)]
    [InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface IComEvents
    {
        [DispId(0x00000001)]
        void MyFirstEvent(string args);
    }
}

Creating an Instance of the COM Component

To use the COM component in a web page, embed an <object /> tag in the head of the page

ASP.NET
<object name="myComComponent" id="myComComponent" 
	classid="clsid:4794D615-BE51-4a1e-B1BA-453F6E9337C4" />

Hooking Up Events to the COM Component

Hooking up the events from the COM component is a little tricky. What you need to do is embed a <script/> tag in the body of the web page as follows:

JavaScript
<script for="myComComponent" event="MyFirstEvent(args)" language="javascript">
function myComComponent::MyFirstEvent(args) {
}

The "for" attribute should be set to the name of instance of your COM component. The "event" attribute should be set to the JavaScript function signature for the event. The name of the JavaScript function is the instance name of your COM component, followed by double colons, followed by the JavaScript signature of the event.

Executing Commands on the COM Component

Here's the JavaScript code to execute MyFirstComCommand:

JavaScript
var returnCode = myComComponent.MyFirstComCommand("Hello World!");

Disposing of the COM Component

In Internet Explorer 8, the COM component is not destroyed until the web browser is fully closed - closing of the tab that contained the COM component does not dispose of the object! If the user continuously hits the refresh button, new instances of the COM component will be created, but the old instances won't be released until the web browser is closed. You can use the onunload event to detect when the page is unloaded, then dispose of your COM component.

In our example, the Dispose method we added could be used for this purpose. Just change the implementation of the Dispose method to clean up any resources used by the COM component. The implementation in this example just shows a Message Box, so we know when the Dispose method is getting called.

Putting it all Together

Below is the source code to a web page that creates an instance of the MyComComponent COM object, executes methods on the COM component, and listens for events from the COM component.

ASP.NET
<html>
    <head>
        <title>My Com Component</title>
        <object id="myComComponent" name="myComComponent" 
		classid="clsid:4794D615-BE51-4a1e-B1BA-453F6E9337C4"></object>
        <script language="javascript" type="text/javascript">
            function myButton_click() {
                var returnCode = myComComponent.MyFirstComCommand(myArgs.value);
                var msg = "myComComponent.MyFirstComCommand returned " + returnCode;
                appendText(msg);
                appendText("\r\n");
            }
            
            function setText(txt) {
                myTextBox.value = txt;
            }
            
            function appendText(txt) {
                myTextBox.value = myTextBox.value + txt;
            }
            
            function MyComComponent_onload() {
                setText("");
                myComComponent.MyFirstComCommand("Hi");
            }
            
            function MyComComponent_onunload() {
                myComComponent.Dispose();
            }
        </script>
    </head>
    <body onload="MyComComponent_onload();" onunload="MyComComponent_onunload();">
        <h1>My Com Component</h1>
        <table>
            <tr>
                <td>
                    <button id="myButton" onclick="myButton_click();">Com Method</button>
                    <label>Args</label>
                    <textarea id="myArgs" rows="1" cols="16">Hello World!</textarea>
                </td>
            </tr>
            <tr>
                <td>
                    <textarea id="myTextBox" rows="10" cols="80"></textarea>
                </td>
            </tr>
        </table>
        
        <script for="myComComponent" event="MyFirstEvent(args)" language="javascript">
        function myComComponent::MyFirstEvent(args) {
            appendText("myComComponent raised MyFirstEvent. args: ");
            appendText(args);
            appendText("\r\n");
        }
        </script>
    </body>
</html>

Using the Code

To use the sample web page, you first need to compile the MyComComponent solution file. The generated assembly will automatically be registered for COM Interop when you build the solution, so you don't need to put the assembly in the same folder as the web page.

After building the solution, open the web page MyComComponent.htm in your browser (make sure ActiveX and JavaScript are enabled).

Links

History

  • 22 April 2009 - Original Post

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer Desire2Learn
Canada Canada
Professional software developer in St. John's, Newfoundland, Canada

http://www.jerometerry.com

Comments and Discussions

 
QuestionNot working in Windows 10 Chrome Pin
Ehsan Sajjad3-Feb-19 0:06
professionalEhsan Sajjad3-Feb-19 0:06 
AnswerRe: Not working in Windows 10 Chrome Pin
Thang_Duong5-Sep-19 16:28
Thang_Duong5-Sep-19 16:28 
Questionif many event ?? Pin
Member 121179198-Nov-15 22:10
Member 121179198-Nov-15 22:10 
QuestionUsing return in event IE10 Pin
mrosalesdiaz4-Jun-15 7:54
mrosalesdiaz4-Jun-15 7:54 
QuestionGreat Article - Thanks Plus Question Pin
Member 796953920-Dec-13 4:51
Member 796953920-Dec-13 4:51 
GeneralMy vote of 5 Pin
Domingo López30-Oct-13 9:21
Domingo López30-Oct-13 9:21 
SuggestionMemory leak with event reception Pin
Domingo López30-Oct-13 9:18
Domingo López30-Oct-13 9:18 
GeneralMy vote of 5 Pin
Eugene Sadovoi12-Apr-13 12:04
Eugene Sadovoi12-Apr-13 12:04 
Questionmy vote of 5 Pin
mohamad yousef28-May-12 0:53
mohamad yousef28-May-12 0:53 
QuestionThanks Pin
Hemen.r5-May-12 21:49
Hemen.r5-May-12 21:49 
GeneralMy vote of 5 Pin
Grant van Staden1-May-12 17:57
Grant van Staden1-May-12 17:57 
GeneralMy vote of 5 Pin
Manoj Kumar Choubey28-Feb-12 18:13
professionalManoj Kumar Choubey28-Feb-12 18:13 
Nice
Questionthe code is not working in IE8 but works fine in IE7 Pin
harishladhani16-Feb-12 1:39
harishladhani16-Feb-12 1:39 
AnswerRe: the code is not working in IE8 but works fine in IE7 Pin
Jerome Terry16-Feb-12 2:24
Jerome Terry16-Feb-12 2:24 
GeneralMy vote of 4 Pin
seyersoft28-Dec-11 10:41
seyersoft28-Dec-11 10:41 
QuestionRegistering this dll on another machine Pin
seyersoft28-Dec-11 9:17
seyersoft28-Dec-11 9:17 
AnswerRe: Registering this dll on another machine Pin
Jerome Terry28-Dec-11 11:23
Jerome Terry28-Dec-11 11:23 
AnswerRe: Registering this dll on another machine Pin
Member 831849929-Nov-12 18:13
Member 831849929-Nov-12 18:13 
GeneralMy vote of 5 Pin
joshi1816-Dec-11 1:44
joshi1816-Dec-11 1:44 
GeneralMy vote of 5 Pin
Victor DS12-Jan-11 20:17
Victor DS12-Jan-11 20:17 
GeneralTargetException Pin
Wjousts12-Nov-10 7:49
Wjousts12-Nov-10 7:49 
GeneralRe: TargetException Pin
Wjousts12-Nov-10 8:29
Wjousts12-Nov-10 8:29 
GeneralRe: TargetException Pin
aaaaaad26-Jul-11 0:38
aaaaaad26-Jul-11 0:38 
GeneralC# COM Object in VBA or VB6 error on event Pin
Member 255672622-Oct-10 12:50
Member 255672622-Oct-10 12:50 
GeneralRe: C# COM Object in VBA or VB6 error on event Pin
bigkeys11-Nov-10 1:45
bigkeys11-Nov-10 1:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.