Click here to Skip to main content
Click here to Skip to main content

Building COM Objects in C#

By , 2 Aug 2004
 

Introduction

The topics covered in this article are:

  • Creating a simple COM object in C# (use the COM Interop property).
  • Accessing the COM from a VC++ client. (I have tested it with VC++6.0 and VC++ .NET). Client uses the TypeLibrary (.TLB file).

For the sake of simplicity and ease of use for the developers, testing this code, I have used the Northwind database built into with the default installation of SQL Server database.

  • Modify the name of the SQL Server in the COM object to connect to your SQL Server.
  • Also, I have created a default user ID and password of scott / tiger to connect to the database. Either create this, or use an existing ID / password.

Part I: Creating a simple COM object in C#

COM objects are of the type ClassLibrary. The COM object generates a DLL file. To create a simple COM object from the VS Development Environment, select ....

New->Project->Visual C# Projects ->Class Library

Create a project named Database_COMObject.

Remember: Exposing the VC# objects to the COM world requires the following ...

  • The class must be public.
  • Properties, methods, and events must be public.
  • Properties and methods must be declared on the class interface.
  • Events must be declared in the event interface.

Other public members in the class that are not declared in these interfaces will not be visible to COM, but they will be visible to other .NET Framework objects. To expose properties and methods to COM, you must declare them on the class interface and mark them with a DispId attribute, and implement them in the class. The order the members are declared in the interface is the order used for the COM vtable. To expose events from your class, you must declare them on the events interface and mark them with a DispId attribute. The class should not implement this interface. The class implements the class interface (it can implement more than one interface, but the first implementation will be the default class interface). Implement the methods and properties exposed to COM here. They must be marked public and must match the declarations in the class interface. Also, declare the events raised by the class here. They must be marked public and must match the declarations in the events interface.

Every interface needs a GUID property set before the interface name. To generate the unique GUID, use the guidgen.exe utility and select the Registry Format.

Here is how the interface class looks like ...

    [Guid("694C1820-04B6-4988-928F-FD858B95C880")]
    public interface DBCOM_Interface
    {
        [DispId(1)]
        void Init(string userid , string password);
        [DispId(2)]
        bool ExecuteSelectCommand(string selCommand);
        [DispId(3)]
        bool NextRow();
        [DispId(4)]
        void ExecuteNonSelectCommand(string insCommand);
        [DispId(5)]
        string GetColumnData(int pos);
    }

For COM events ..

    // // Events interface Database_COMObjectEvents 
    [Guid("47C976E0-C208-4740-AC42-41212D3C34F0"), 
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface DBCOM_Events 
    {
    }

For the actual class declaration:

[Guid("9E5E5FB2-219D-4ee7-AB27-E4DBED8E123E"),
    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(DBCOM_Events))]
    public class DBCOM_Class : DBCOM_Interface
    {

Note the following property set before the class:

    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(DBCOM_Events))]

The ClassInterfaceType.None indicates that no class interface is generated for the class. If no interfaces are implemented explicitly, the class will only provide late bound access through IDispatch. Users are expected to expose functionality through interfaces that are explicitly implemented by the class. This is the recommended setting for ClassInterfaceAttribute.

The ComSourceInterfaces(typeof(DBCOM_Events))] identifies a list of interfaces that are exposed as COM event sources for the attributed class. For our example, we do not have any events exposed.

Here is the complete COM object source ..

using System;
using System.Runtime.InteropServices;
using System.IO;
using System.Text;
using System.Data.SqlClient;
using System.Windows.Forms ;

namespace Database_COMObject
{
    [Guid("694C1820-04B6-4988-928F-FD858B95C880")]
    public interface DBCOM_Interface
    {
        [DispId(1)]
        void Init(string userid , string password);
        [DispId(2)]
        bool ExecuteSelectCommand(string selCommand);
        [DispId(3)]
        bool NextRow();
        [DispId(4)]
        void ExecuteNonSelectCommand(string insCommand);
        [DispId(5)]
        string GetColumnData(int pos);
    }

    // Events interface Database_COMObjectEvents 
    [Guid("47C976E0-C208-4740-AC42-41212D3C34F0"), 
    InterfaceType(ComInterfaceType.InterfaceIsIDispatch)]
    public interface DBCOM_Events 
    {
    }


    [Guid("9E5E5FB2-219D-4ee7-AB27-E4DBED8E123E"),
    ClassInterface(ClassInterfaceType.None),
    ComSourceInterfaces(typeof(DBCOM_Events))]
    public class DBCOM_Class : DBCOM_Interface
    {
        private SqlConnection myConnection = null ; 
        SqlDataReader myReader = null ;

        public DBCOM_Class()
        {
        }

        public void Init(string userid , string password)
        {
            try
            {
                string myConnectString = "user id="+userid+";password="+password+
                    ";Database=NorthWind;Server=SKYWALKER;Connect Timeout=30";
                myConnection = new SqlConnection(myConnectString);
                myConnection.Open();
                //MessageBox.Show("CONNECTED");
            }
            catch(Exception e)
            {
                MessageBox.Show(e.Message);
            }
        }

        public bool ExecuteSelectCommand(string selCommand)
        {
            if ( myReader != null ) 
                myReader.Close() ;

            SqlCommand myCommand = new SqlCommand(selCommand);
            myCommand.Connection = myConnection;
            myCommand.ExecuteNonQuery();
            myReader = myCommand.ExecuteReader();
            return true ;
        }
        
        public bool NextRow()
        {
            if ( ! myReader.Read() )
            {
                myReader.Close();
                return false ;
            }
            return true ;
        }

        public string GetColumnData(int pos)
        {
            Object obj = myReader.GetValue(pos);
            if ( obj == null ) return "" ;
            return obj.ToString() ;
        }

        public void ExecuteNonSelectCommand(string insCommand)
        {
            SqlCommand myCommand = new SqlCommand(insCommand , myConnection);
            int retRows = myCommand.ExecuteNonQuery();
        }

    }
}

Before you build the COM object, we have to register the object for COM Interop. To do this, right click the project name in the Solution Explorer. Click Properties. Click Configuration ->Build. Expand the output section. Set the Register for COM Interop to true.

Indicate that your managed application will expose a COM object (a COM-callable wrapper) that allows a COM object to interact with your managed application.

In order for the COM object to be exposed, your class library assembly must also have a strong name. To create a strong name, use the utility SN.EXE.

sn -k Database_COM_Key.snk

Open the AssemblyInfo.cs and modify the line:

[assembly: AssemblyKeyFile("Database_COM_Key.snk")]

Build the object. The build also results into a type library that can be imported into your managed or unmanaged code.

Part II: Creating a Client using Visual C++ to access this COM Object

I have tested the COM object with the VC++ 6.0 and VC++ .NET environment. Create a simple project using the VC++ development environment. Import the type library using the #import directive. Create a Smart Pointer to the Interface.Execute, the exposed functions from the interface. Make sure to add the CoInitialize() call when the application loads.

    CoInitialize(NULL);

    Database_COMObject::DBCOM_InterfacePtr 
       p(__uuidof(Database_COMObject::DBCOM_Class));
    db_com_ptr = p ;
    db_com_ptr->Init("scott" , "tiger");

This code executes a SQL Command against the Customers table and returns the customer information for a given customer ID.

    char cmd[1024];
    sprintf(cmd , "SELECT COMPANYNAME , CONTACTNAME ,
        CONTACTTITLE , ADDRESS  FROM CUSTOMERS WHERE CUSTOMERID = '%s'" , m_id );
    const char *p ;

    bool ret = db_com_ptr->ExecuteSelectCommand(cmd);

    if ( ! db_com_ptr->NextRow() ) return ;

    _bstr_t mData = db_com_ptr->GetColumnData(3);
    p = mData ;
    m_address    =    (CString)p ;

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here

About the Author

mubbsher
Web Developer
Germany Germany
No Biography provided

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
Hint: For improved responsiveness ensure Javascript is enabled and choose 'Normal' from the Layout dropdown and hit 'Update'.
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionRasing COM Event in C# and capture/handle them in VC++ native clientmemberMember 737491222-Mar-13 3:54 
GeneralMy vote of 5membervesal28-Feb-13 14:07 
GeneralMy vote of 5memberDavid Chiclana21-Jan-13 22:28 
QuestionWhy is the GUID different?memberTony Reynolds17-Jul-12 22:52 
Questionmy vote of 5membermuhamad yousef28-May-12 1:18 
GeneralMy vote of 5memberHarry von Borstel26-Apr-12 3:28 
Generalcan't find out the entry point DllRegiserServermembermaonan8-Feb-10 22:00 
QuestionRegsiter COM objectmemberdidialaoui30-Dec-09 10:45 
GeneralCOM - .NET Querymemberjaygaba15-Oct-08 2:41 
Generalerror LNK2019: unresolved external symbol "void __cdecl _com_issue_error(long)memberioengineer2-Jul-08 3:38 
GeneralCan't see registred COM objectmemberZilo(svk)11-Apr-07 4:07 
GeneralRe: Can't see registred COM objectmemberZilo(svk)11-Apr-07 4:29 
GeneralRe: Can't see registred COM objectmemberbeat catelloni7-Sep-09 22:17 
QuestionComments for the methods and properties ????memberJerry Walter10-Dec-06 3:14 
QuestionHow to make the test clientmembertime_error7-Dec-06 3:49 
QuestionEvent Reciever ExamplememberMayurGadhave3-Dec-06 20:09 
GeneralAccessing com+ in LanmemberDearSoul12-Oct-06 21:02 
QuestionExposing Eventsmembertechno_adi2-Aug-06 0:04 
GeneralTwo QuestionsmemberAndré Voigt13-Dec-05 3:43 
Generalexe com (out of process server)membersalnaemi27-Oct-05 3:54 
Hi..
Good job, but this is a dll com (in process server). Is there any way to change it to an exe com (out of process server). Cause for me I did the same thing and now I found a problem (freezing windows)when I load and run my com from a vc++ and I think it is a matter of sharing resources and memory at the same time.
Thanks in advance.
Regards,

GeneralAcees COM in C#memberketan p bhaid5-Aug-05 3:34 
QuestionHow Can I use C# DLL in XLS?sussAnonymous3-Aug-05 6:00 
AnswerRe: How Can I use C# DLL in XLS?sussKewal Mehra5-Aug-05 4:24 
GeneralThank youmemberhirstmg31-Jul-05 18:41 
QuestionCOM Singleton?memberTim Green JIT21-Apr-05 7:05 
GeneralTHANKSmemberAFleischmann7-Mar-05 5:50 
GeneralHandling Events in VC++memberyofnik18-Feb-05 5:47 
GeneralRe: Handling Events in VC++memberAnt Htoo Naing29-Mar-06 22:27 
GeneralRe: Handling Events in VC++ [modified]memberJason C Daniels18-Jul-06 11:51 
GeneralYet Another MSDN PlagiaristmemberPaul Lyons3-Aug-04 14:15 
GeneralRe: Yet Another MSDN PlagiaristmemberWillemM10-Aug-04 20:34 
GeneralRe: Yet Another MSDN Plagiaristsussbluelizard12-Aug-04 5:56 
GeneralRe: Yet Another MSDN PlagiaristmemberPaper#10-Jan-05 9:55 
QuestionHow can I add a property to the InterfacememberEric Engler29-Sep-04 10:42 
AnswerRe: How can I add a property to the InterfacememberRichard Everett26-Nov-04 13:41 

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

Permalink | Advertise | Privacy | Mobile
Web03 | 2.6.130617.1 | Last Updated 3 Aug 2004
Article Copyright 2004 by mubbsher
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid