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

Calling Managed .NET C# COM Objects from Unmanaged C++ Code

By , 11 Jan 2006
 

Preface

COM Interoperability is the feature of Microsoft .NET that allows managed .NET code to interact with unmanaged code using Microsoft's Component Object Model semantics.

This article is geared towards C# programmers who are familiar with developing COM components and familiar with the concept of an interface. I'll review some background on COM, explain how C# interacts with COM, and then show how to design .NET components to smoothly interact with COM.

For those die-hard COM experts, there will be some things in this article that are oversimplified, but the concepts, as presented, are the important points to know for those developers supplementing their COM code with .NET components.

Introduction

.NET Interfaces and Classes

The basis for accessing .NET objects either from other .NET code or from unmanaged code is the Class. A .NET class represents the encapsulation of the functionality (methods and properties) that the programmer wants to expose to other code. A .NET interface is the abstract declaration of the methods and properties that classes which implement the interface are expected to provide in their implementations. Declaring a .NET interface doesn't generate any code, and a .NET interface is not callable directly. But any class which implements ("inherits") the interface must provide the code that implements each of the methods and properties declared in the interface definition.

Microsoft realized that the very first version of .NET needed a way to work with the existing Windows technology used to develop applications over the past 8+ years: COM. With that in mind, Microsoft added support in the .NET runtime for interoperating with COM - simply called "COM Interop". The support goes both ways: .NET code can call COM components, and COM code can call .NET components.

Using the code

Steps to create a Managed .NET C# COM Object:

  1. Open VS.NET2003->New Project->Visual C# Projects->Class Library.
  2. Project name: MyInterop.
  3. Create MyDoNetClass.cs file, and add the following lines of code:
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
  4. Create an Interface IMyDotNetInterface.
  5. Create a class MyDoNetClass.
  6. Add the following line for MyDotNetClass:
    [ClassInterface(ClassInterfaceType.None)]

Although a .NET class is not directly invokable from unmanaged code, Microsoft has provided the capability of wrapping a .NET interface in an unmanaged layer of code that exposes the methods and properties of the .NET class as if the class were a COM object. There are two requirements for making a .NET class visible to unmanaged code as a COM object:

Requirement 1:

You have to add GUIDs - Globally Unique Identifiers - into your code for the interface and the class separately, through a GUID tool.

  1. Now, create a GUID for the Interface, and add the following line for the interface:
    [Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")]
  2. Now, create a GUID for the class, and add the following line for the class:
    [Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
  3. Your code will look like:
    using System;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace MyInterop
    {
        [Guid("03AD5D2D-2AFD-439f-8713-A4EC0705B4D9")]
        interface IMyDotNetInterface
        {
            void ShowCOMDialog();
        }
         
        [ClassInterface(ClassInterfaceType.None)]
        [Guid("0490E147-F2D2-4909-A4B8-3533D2F264D0")]
        class MyDotNetClass : IMyDotNetInterface
        {
            // Need a public default constructor for COM Interop.
            public MyDotNetClass()
            {}
            public void ShowCOMDialog()
            {
                System.Windows.Forms.MessageBox.Show(“I am a" + 
                      "  Managed DotNET C# COM Object Dialog”);
            }
        }
    }
  4. Compile the solution.
  5. You will see inside the project directory->obj->debug directory, the file “MyInterop.dll” generated after compilation.

Requirement 2:

Registration of the COM Class and Interfaces

For a COM class to be accessible by the client at runtime, the COM infrastructure must know how to locate the code that implements the COM class. COM doesn't know about .NET classes, but .NET provides a general "surrogate" DLL - mscoree.dll -- which acts as the wrapper and intermediary between the COM client and the .NET class.

  1. Hard-code a specific version number in your AssemblyVersion attribute in the AssemblyInfo.cs file which is in your project.

    Example:

    [assembly: AssemblyVersion("1.0.0.0")]
  2. Create a strong-name key pair for your assembly and point to it via the AssemblyKeyFile attribute in the AssemblyInfo.cs file which is in your project. Example:
    sn -k TestKeyPair.snk
    [assembly: AssemblyKeyFile("TestKeyPair.snk")]
  3. Add your assembly to the GAC using the following command:
    gacutil /i MyInterop.dll
  4. Register your assembly for COM by using the REGASM command along with the "/tlb" option to generate a COM type library.
    REGASM MyInterop.dll /tlb:com.MyInterop.tlb
  5. Close the C# project.

Steps to create an Unmanaged C++ application to call a .NET Managed C# COM

  1. Open VS.NET2003->New Project->Visual C++ Projects->Win32->Win32 Console Project.
  2. Name: DotNet_COM_Call.
  3. Include the following line in your DoNet_COM_Call.cpp file:
    #import “<Full Path>\com.MyInterop.tlb" named_guids raw_interfaces_only
  4. Compile the solution.
  5. It will generate a “com.myinterop.tlh” file into your project->debug directory.
  6. You can open this file and see the contents. This is basically the proxy code of the C# COM code.
  7. Now, you can write the code to call the .NET Managed COM.
  8. Please add the following lines of code before calling the COM exported functions:
    CoInitialize(NULL);   //Initialize all COM Components
        
    // <namespace>::<InterfaceName>
    MyInterop::IMyDotNetInterfacePtr pDotNetCOMPtr;
    
    // CreateInstance parameters
    // e.g. CreateInstance (<namespace::CLSID_<ClassName>)
    HRESULT hRes = 
      pDotNetCOMPtr.CreateInstance(MyInterop::CLSID_MyDotNetClass);
    if (hRes == S_OK)
    {
        BSTR str;
        pDotNetCOMPtr->ShowCOMDialog ();
        //call .NET COM exported function ShowDialog ()
    }
    
    CoUninitialize ();   //DeInitialize all COM Components
  9. Run this console application.
  10. Expected result: a managed code (C# ) dialog should appear with the string “I am a Managed DotNET C# COM Object Dialog”.

Points of Interest

While creating an Interface for COM exported functions, creating GUIDs for the Interface and the class and registering the class are required steps, and doing all this is always interesting and fun. Calling parameterized exported functions also is very interesting.

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

Atul Mani
Web Developer
United States United States
Member
Atul Mani Tripathi has been a professional developer since 1997 and working as a senior consultant with Cognizant Technology Solutions (CTS) .
 
Most of the time, his focus is on creating a clean and simple solution to development problems. He is a C++, Visual C++ and C# (Dot NET) guy who loves Visual Studio, Google, Code Project and developing personal projects.
 

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

 
You must Sign In to use this message board.
Search this forum  
    Spacing  Noise  Layout  Per page   
QuestionHandle C# COM Events in VC++memberMember 737491217 Mar '13 - 21:42 
Hi Atul Mani,
 
I want to rasie events from C# COM DLL and handle them in unmanaged C++ client. Please if you have any sample let me know.
 
Many Thanks
IK
QuestionRe: Multiple constructors callable? [modified]membermla1543 Jul '12 - 10:12 
MyDotNetClass has a single constructor.  Is it possible to implement more constructors and call any of these constructors from C++ code?
Regards,
Mike


modified 17 Jul '12 - 10:21.

QuestionHow do I release the managed .net object.membersandeep_sinha21 Jun '12 - 22:52 
Hi,
 
thanks for a very nice article. this was really very useful.
 
If any one can guide on what needs to be done in the installer so the these binaries can be used in the installed location.
 
Regards
Sandeep
GeneralMy vote of 5membermanoj kumar choubey28 Feb '12 - 18:13 
Nice
QuestionUseful article, GACUtil and SNK not necessarily required, update to VS2010?memberDavidCarr17 Feb '12 - 10:41 
This is a great, compact article that is still relevant today.
 
I found doing this in Visual Studio 2010 that it wasn't necessary to create an SNK (also bug with step 13 with having AssemblyKeyFile in Assembly.cs), nor was it necessary to call GACUtil as jive_b noted 2012.Jan.25).
 
Given the article's usefullness so many years on, perhaps update this? Either way...'bump' and thanks!
QuestionGAC registration is not requiredmemberjive_b25 Jan '12 - 6:02 
It's worth pointing out (which also kind of answers Member3156877's post) it's not actually necessary to install the assembly into GAC. Using the GAC can be an unnecessary hassle with deployment due references being to a particular version, not to mention getting assemblies in there in the first place.
 
To register assemblies not in the GAC and get the tlb use:
REGASM /codebase blar.dll /tlb:blar.dll.tlb
GeneralMy vote of 5memberMenon Santosh15 Dec '11 - 0:43 
good Work
Questionhow to release onto production PCsmemberMember 315687714 Nov '11 - 8:58 
Hi, this is an excellent article that helped me build a managed component and call it from a legacy VC++ application. But how to build the release versions of the components and deploy this managed component in production? Not all the production PCs have gacutil. thanks in advance.
QuestionFile '...\ManagedCOM\Call_CSharp_COM\Call_CSharp_COM.vcproj' was not found.memberon2Gaute13 Jul '11 - 2:18 
Call_CSharp_COM.vcproj is not included in the project?
QuestionI am not able to access intefaces from C# DLLmemberMember 79736701 Jun '11 - 19:36 
I have prepeared project folllowing instrctions given in this article(Calling Managed .NET C# COM Objects from Unmanaged C++ Code
).I have used VS 2008 and .Net Framework 2.0 for both projects.Issue I have is I am not getting interfaces in namespace while trying to acces in C++ project
MyInterop::IMyDotNetInterfacePtr pDotNetCOMPtr; .I am not getting IMyDotNetInterfacePtr in namespace.ALos I loked into .tlh file of C++ project it is also not having any defination of interfaces.
 
Please let me know what I am missing?
QuestionRe: I am not able to access intefaces from C# DLLmemberMember 823172414 Sep '11 - 8:52 
I have the same problem ... did you find the solution ?
Thanks
AnswerRe: I am not able to access intefaces from C# DLLmemberMember 797367020 Sep '11 - 0:39 
Check your .tlh file in OLE viewer , you will find definations of the methods declared in interface.
To access interfaces from C# DLL they have to be first listed in .TLH and .TLB.
Delete old .tlb and .tlh and den check also clear there copies from ~\temp directory.
GeneralMy vote of 5memberAnoop Chandran M27 Oct '10 - 3:58 
This is well documented and helped me for my projects
GeneralRegarding Memory leakmembermbcvamsidhar4 Oct '10 - 11:38 
Hi, Have you ever observed, in the above process after creating CoCretingInstance the memory in the task manager went up and even though you called CoUnitilize it wont come back to normal. Do you have any inputs on this?
Thanks and Rgds,
VamsiDhar.MBC
SoftwareEngineer.

GeneralRe: Regarding Memory leakmemberMember 771320628 Dec '11 - 9:49 
Has anybody found a solution to the memory leaking issue ? I too have seen this behavior, and I'm not sure how to fix it.
GeneralCompile using DevC++memberyusof_hardy19 Mar '10 - 14:58 
I can compile the C++ code in Visual C++ 2008.
BUt I can't compile it using Dev-C++.
 
How do I make it compatible with Dev-C++?
GeneralYou forgot to put "public" before the interface and call definition in the c# project.memberzengamer211 Dec '09 - 7:37 
This post was driving me insane because it wasn't working. Then I found another similar tutorial on MSDN. In this other tutorial, they used the public keyword before the interface and class name in the c# project. Once I added the keyword and repeated the rest of the steps, it worked fine. Smile | :)
GeneralRe: You forgot to put "public" before the interface and call definition in the c# project.memberzengamer211 Dec '09 - 7:38 
I should have said "class" definition instead of "call" definition...
GeneralRe: You forgot to put "public" before the interface and call definition in the c# project.membersegber21 Jan '10 - 7:39 
Indicar el link de donde encontraste la solucion del proyecto, porque tambien me esta volviendo loco
QuestionWhat if path to tlb is differentmembercos081531 Jul '09 - 4:54 
Hello,
 
the example is very good and solve a lot of problem for me. But my question is, what is, if the path to the tlb is different from pc to pc. Some users can install my software on c:\ or d:\ but in my code i write the path to the tlb hard into it. Have anyone a idee, how i can solve this? Can i write the path into the registy an import the path from registry? Some idee?
Thx.
GeneralHRESULT:0x80040154membererdiay25 May '09 - 3:11 
Hi,
I followed your instructions and everything worked well. Thanks for article but I have a problem at client side actually. I copy my .tlb file to another computer and try to call it from a C++ project and when i'm creating instance of my com object, it returns "HRESULT = 0x80040154"
Any idea?
 
Thanks.
Questionwhat about properties ?memberroboticEDAR14 May '09 - 14:43 
how would i implement basic properties in the c# dll
 
and access them in native , sounds simple i know but i having problems with types, my property int in C# is being seen as a long*
 
dont get it , how do i do it ?
QuestionHow to handle Events.memberBicycleTheif16 Apr '09 - 1:44 
Please let me know how to handle the events raised by .NET object in C++ code.
GeneralAssembly used as COMmemberMarkusSchreiber24 Feb '09 - 20:28 
Hi,
 
its a very useful example. But I have a question:
Do I have to install the assembly in the GAC to use it with COM?
 
Regards Markus
General0x80040154 Class not registeredmemberxamlavoie13 Nov '08 - 3:18 
If you get this error message when you try this example for yourselves, try this:
 
- Open your C# project
- Go to your project's properties (right-click menu)
- Under the Build tab, make sure the "Register for COM interop" is checked (both for Debugging AND Release)
 
You might have to redo steps 14 and 15:
 
- Add your assembly to the GAC
- Register your assembly for COM by using the REGASM
 
And then try running your C++ again
 
Hope it saves some of you lots of trouble!
GeneralGetting Unhandled exception [modified]memberplenitude21 Sep '08 - 21:24 
Hi,
 
Please help me I am getting this exception
When trying to create interface this error occurs i.e hres is not S_OK :-
 
Unhandled exception at 0x7c812a5b in DotNet_COM_Call.exe: Microsoft C++ exception: _com_error at memory location 0x0012fd5c..
 
PLz tell me where I am wrong as most of ppl have succeded while trying this example...
Cry | :((
 
Thanx in advance.
 
modified on Monday, September 22, 2008 8:53 AM

QuestionDebug and ReleasememberAcinomyyf3 Aug '08 - 19:03 
I can use it with Debug mod but in Release mod it can't find the C# com
How can I do?
GeneralDebuggingmemberSitaram Sukumar29 Jul '08 - 23:13 
How do you debug this code? Lets say from my C++ client, from where I create this .NET object and then call the MessageBox.Show() function. If I place a break point in the C++ code how do I trace it to the .NET function
GeneralCannot instantiate abstract class. [modified]membertheCPkid4 Jul '08 - 4:24 
'namespace::interfaceName' : cannot instantiate abstract class
...
..
HRESULT IUnknown::QueryInterface(const IID &,void **)' : is abstract
etc etc
 
If someone get these errors on compiling Call_CSharp_COM.cpp,
then replace _tmain() body with this one...
 

CoInitialize(NULL); //Initialize all COM Components

CComPtr<namespaceName::interfaceName> pPtr;
HRESULT hr = pPtr.CoCreateInstance(namespaceName::CLSID_classname);
 
if (hr == S_OK)
{
pPtr->ShowDialog();
//call .NET COM exported function ShowDialog ()
}
 
CoUninitialize (); //DeInitialize all COM Components

 
Hope it helps.
 
the fruits of your success will be in direct ratio to the honesty and sincerity of your own efforts in keeping your own records, doing your own thinking and, reaching your own conclusions.
..surviving in autumn..in love with spring..
modified on Friday, July 4, 2008 10:32 AM

GeneralRe: Cannot instantiate abstract class.memberFoxhunter4214 Jun '10 - 4:17 
I do get this error, but CComPtr is undefined in your fix. Where does CComPtr come from?
GeneralRe: Cannot instantiate abstract class.membertheCPkid14 Jun '10 - 17:58 
you may need to include atlbase.h [^]
check where CComPtr is defined. You should use smart pointers with directshow. it will save you from lot of headache.
GeneralProblem with CLSID_MyDotNetClassmemberCharllemagne4 Jun '08 - 0:18 
i followed the example and did some of the corrections (lyk making the interface public, set Register for COM Interop to TRUE, etc.) but still im not able to run the console application...when i compiled the it, i got these errors:
 
error C2039: 'CLSID_MyDotNetClass' : is not a member of 'MyInterop'
error C2065: 'CLSID_MyDotNetClass' : undeclared indentifier
 

has anyone run into this problem?
 
tnx
GeneralRe: Problem with CLSID_MyDotNetClassmemberJeffgosurfing12 Jun '08 - 8:47 
it should be CreateInstance("namespace.classname").
stepping into CreateInstance, you will know what it needs.
GeneralRe: Problem with CLSID_MyDotNetClassmemberon2Gaute13 Jul '11 - 3:25 
I have the same problem:(
Generalcom.MyInterop.tlb file is not getting createdmemberMember 28779846 May '08 - 0:03 
After compling this #import “<Full Path>\com.MyInterop.tlb" named_guids raw_interfaces_only
 

com.MyInterop.tlb file is not getting created
GeneralCould'nt ablw to run the demo of MyInteropmemberMember 28779846 May '08 - 0:00 
I'm geting
Cryptographic failure while signing assembly 'C:\Documents and Settings\joxyej\Desktop\ManagedCOM\ManagedCOM\MyInterop\obj\Debug\MyInterop.dll' -- 'Error reading key file 'MyKeyPair.snk' -- The system cannot find the file specified. '
 
error ...pls help
GeneralRe: Could'nt ablw to run the demo of MyInteropmemberroboticEDAR13 May '09 - 22:16 
I had a similar problem i ended up changing the place where the key was then the error went away.
GeneralRe: Could'nt ablw to run the demo of MyInteropmemberxman_mylife23 Feb '11 - 1:22 
jus create the .snk file
using this command (from Visual studio command )
 
first go to the path and thn
 
sn -k TestKeyPair.snk
 
and add this in your assembly file
[assembly: AssemblyKeyFile("TestKeyPair.snk")]
GeneralProblem with CLSID_MyDotNetClassmembererick.gaither31 Mar '08 - 11:27 
For some reason the compliler says that the CLSID_MyDotNetClass is undefined in the following line:
 
pDotNetCOMPtr.CreateInstance(MyInterop::CLSID_MyDotNetClass);
 
Has anyone run into this problem?
 
Thx
GeneralRe: Problem with CLSID_MyDotNetClassmemberCharllemagne3 Jun '08 - 22:37 
hi erick,
 
i'd lyk to ask u f ur able to solve this error?
i encountered this problem also.
GeneralRe: Problem with CLSID_MyDotNetClassmemberRobby_19862 May '10 - 23:48 
This old, but i had the same problem.
The solution is: use #import with named_guid.
Compile one time and the IDE knows the constants.
QuestionLoadTypeLib() not working for C# Class Library dllmemberMember 42194197 Jan '08 - 20:03 
I have created a C# Class Library(MyInterop.dll) for COM Interop.
I have COM dll where in i need to retrieve the CLSID using LoadTypeLib() followed by GetTypeAttr()
 
LoadTypeLib("MyInterop.dll", &typeLib);
typeInfo->GetTypeAttr(&typeAttr);
clsid = typeAttr->guid;
 
But the call to LoadTypeLib returns TYPE_E_CANTLOADLIBRARY.
 
(Pls note that i dont want to use #import .tlb")
 
Your help is appreciated
Thanks
GeneralWon't wotk with a licensed .NET componentmemberVitalyTomilov15 Nov '07 - 8:27 
It won't work, if your .NET component makes use of .NET licensing, and most commercial ones do, because it this case the client must be signed as well. Getting C++ application signed seems tricky. Do you know how to do it? Too bad you didn't cover it here...
 

 
Free C++ libraries with source code on www.neatcpp.com: TWAIN, DirectShow, Interprocess Communications, etc...

GeneralRe: Won't wotk with a licensed .NET componentmemberdarkauskas4 Sep '08 - 2:54 
How it looks? Is it special error code or what? Im getting error "cannot instantiate abstract class" continiously on my own project like this one above.
GeneralRe: Won't wotk with a licensed .NET componentmemberVitaly Tomilov4 Sep '08 - 3:03 
It has been a while since i tried the last time, so i can't remember what error code was exactly, but i think that was one of license failure.
 
After I contacted the .NET component's manufacturer we were able to work it out in a tricky way. I wrote my own C# component that used their component, while my C# COM component was used from a C++ client via COM. In order to make it work I had to add a special attribute in my own C# component where I initialized the commercial component, and that attribute somehow allowed their license to be treated as internal to their component only, without trying to spread its requirements through the sub-hierarchy of clients, and then it worked.
 
Professional System Library on www.prosyslib.org

QuestionThe &quot;CreateInstance()&quot; shows that the class is not registered,why?Help me!memberLavie11 Oct '07 - 20:23 
I did as the above steps. But when I run the samples, the "CreateInstance()" shows that the class is not registered. Why ? Is there anyone who can help me out?
AnswerRe: The &quot;CreateInstance()&quot; shows that the class is not registered,why?Help me!memberA_K_A19 Nov '07 - 8:06 
After a full days searching and reading this is what fixed it for me.
 
Changed build events to read.
 
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\gacutil.exe /i "$(TargetPath)"
C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\RegAsm.exe "$(TargetPath)" /tlb:"$(TargetDir)com.MyInterop.tlb" /codebase
GeneralRe: The &quot;CreateInstance()&quot; shows that the class is not registered,why?Help me!membergaojie4 Mar '08 - 22:22 
Hi, I have the same problem, but the solution does not work for me.
Any body have a working solution using VS2005? Please give some detailed steps. Thanks a lot!!!
GeneralRe: The &quot;CreateInstance()&quot; shows that the class is not registered,why?Help me!memberBenjamin Gottschalk1 Feb '08 - 4:39 
Hi all,
 
I also have the problem, that my class seems to be not registered in the right way. When I call CreateInstace, it says: class not registered. I use Fwk 2.0, used gacutil.exe and regasm.exe in v2.0 and registered them according to the instruction given in the article.
 
In my class however, there are members and constructor-calls of other classes, which were NOT registered for COM (ComVisible(false)).
 
Could it be, that I mustn't uses those members and classes in my COM class?
 
Thanks for your reply,
beni
GeneralAnother similar examplememberryan7866 Aug '07 - 13:53 
Found this while looking for more information on the topic. Hope others find it useful as well: http://support.microsoft.com/kb/828736

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

Permalink | Advertise | Privacy | Mobile
Web02 | 2.6.130516.1 | Last Updated 11 Jan 2006
Article Copyright 2006 by Atul Mani
Everything else Copyright © CodeProject, 1999-2013
Terms of Use
Layout: fixed | fluid