Click here to Skip to main content
15,867,686 members
Articles / Desktop Programming / MFC

Using COM aware DotNet Library in MFC Application

Rate me:
Please Sign up or sign in to vote.
4.77/5 (11 votes)
25 Jun 2012CPOL6 min read 55.4K   575   48   14
Article about using C# DLL in unmanaged VC++ application

Introduction

Recently (i.e. yesterday) I have published article for creating COM/ATL based component and using it in Dot-Net applications. There a thought strike my mind, why not used DotNet based library in unmanaged VisualCpp application. As usual, I start by searching over internet (Developer Mind, Huh! J), there I found some useful tips to get started in above topic.

Though I am not a seasoned dot-net developer, but I am able to create dot-net library quite quickly, but when it’s come for integration it unmanaged application, really I chewed iron nuts to integrate it, sometime com object is not created, some time exported files doesn’t contain information for exposed function. Here, in this article I tried to share everything, whatever I learned from above experience.

Table of Content

  • Creating com aware C#.Net class library
  • Using it in Visual C++ MFC Application
  • Quick Revision

Creating C#.Net class library

Let’s proceed step by step for creating C# class library
  1. Open Development Environment (Visual Studio 2005), Click on File| New | Project, create C# class library project as shown in below example :

                   Image 1
    Figure1: Selecting project

  2. Now include support of System.Runtime.InteropServices namespace, which provide classes and attribute to make custom class COM aware.
    C#
    using System.Runtime.InteropServices; 

  3. Now add interface and it should be publicly accessible and also add class which implement the interface like this :-
    C#
     1.	public interface IClassComVisibleLib
    2.	{
    3.	int CSharpMarks
    4.	{
    5.	get;
    6.	set;
    7.	}
    8.	int VCppMarks
    9.	{
    10.	get;
    11.	set;
    12.	}
    13.	int Calculate();
    14.	} 
    15.	public class ClassComVisibleLib : IClassComVisibleLib
    16.	{
    17.	private int m_iVCPPMarks = 0, m_iCSharpMarks = 0;
    18.	private string m_sStudentName = string.Empty;
    19.	public ClassComVisibleLib() { }
    
    20.	#region IClassComVisibleLib Members
    
    21.	public int CSharpMarks
    22.	{
    23.	get
    24.	{
    25.	return m_iCSharpMarks;
    26.	}
    27.	set
    28.	{
    29.	m_iCSharpMarks= value;
    30.	}
    31.	}
    32.	public int VCppMarks
    33.	{
    34.	get
    35.	{
    36.	return m_iVCPPMarks;
    37.	}
    38.	set
    39.	{
    40.	m_iVCPPMarks = value;
    41.	}
    42.	}
    43.	public int Calculate()
    44.	{
    45.	return m_iCSharpMarks + m_iVCPPMarks;
    46.	}
    47.	#endregion
    48.	}

  4. Now open AssemblyInfo.cs, it’s under Properties folder of the project. Pass true as parameter of [assembly: ComVisible(true)], which is false earlier, this is first step toward making assembly com visible, same attribute is already very well commented in file.

  5. On interface and class, add attribute [ComVisible(true)] and [Guid("GUID_string generated from guidgen")]. ComVisible attribute make your interface or class com visible (I know, I have repeated Com visible many times, please bear with me for same, as you going to read it some more times, hehehe). Guid string can be generated using GuidGen utility which generally available at following path :-

    C:\Program Files\Microsoft Visual Studio 8\Common7\Tools


                   Image 2
    Figure2: GuidGen Utility


    Copy the Guid string from utility and paste it as argument for Guid attribute. Interface and Class should have unique Guid.

  6. Add Attribute InterfaceType on Interface, which tell compiler how we exposing our interface to COM , which take ComInterfaceType Enumeration as a argument. Following are enumeration and there meaning from MSDN.

    Member name Description
    InterfaceIsDualIndicates the interface is exposed to COM as a dual interface, which enables both early and late binding. InterfaceIsDual is the default value.
    InterfaceIsIUnknownIndicates an interface is exposed to COM as an IUnknown -derived interface, which enables only early binding.
    InterfaceIsIDispatchIndicates an interface is exposed to COM as a dispinterface, which enables late binding only.

    We will use InterfaceIsDual as it will enable both early and late binding.

  7. Same way add attribute ClassInterface on class. It’s identifies the type of class interface that is generated for a class. It’s take ClassInterfaceType Enumeration as argument, following are enumeration and there meaning from MSDN

    Member name Description
    NoneIndicates that no class interface is generated for the class. If no interfaces are implemented explicitly, the class can only provide late bound access through the IDispatch interface. This is the recommended setting for ClassInterfaceAttribute. Using ClassInterfaceType.None is the only way to expose functionality through interfaces implemented explicitly by the class.
    AutoDispatchIndicates that the class only supports late binding for COM clients. A dispinterface for the class is automatically exposed to COM clients on request. The type library produced by the type Type Library Exporter (Tlbexp.exe) does not contain type information for the dispinterface in order to prevent clients from caching the DISPIDs of the interface. The dispinterface does not exhibit the versioning problems described in ClassInterfaceAttribute because clients can only late bind to the interface. This is the default setting for ClassInterfaceAttribute.
    AutoDualIndicates that a dual class interface is automatically generated for the class and exposed to COM. Type information is produced for the class interface and published in the type library. Using AutoDual is strongly discouraged because of the versioning limitations described in ClassInterfaceAttribute.

    We will use None as argument for ClassInterface
    .
  8. After all above addition, Our class library looks like to be:-
    C++
    1.	[ComVisible(true)]
    2.	[Guid("5DB724F2-763A-4eb2-A886-1DB3794585F6")]
    3.	[InterfaceType( ComInterfaceType.InterfaceIsDual)]
    4.	public interface IClassComVisibleLib
    5.	{
    6.	int CSharpMarks
    7.	{
    8.	get;
    9.	set;
    10.	}
    11.	int VCppMarks
    12.	{
    13.	get;
    14.	set;
    15.	}
    16.	int Calculate();
    17.	}
    18.	[ComVisible(true)]
    19.	[Guid("DCD9F4D2-A529-446b-A8CD-7AE28F544EAC")]
    20.	[ClassInterface( ClassInterfaceType.None)]
    21.	[ProgId("progid_ClassComVisibleLib")]
    22.	public class ClassComVisibleLib : IClassComVisibleLib
    23.	{
    24.	private int m_iVCPPMarks = 0, m_iCSharpMarks = 0;
    25.	private string m_sStudentName = string.Empty;
    26.	public ClassComVisibleLib() { }
    27.	#region IClassComVisibleLib Members
    28.	public int CSharpMarks
    29.	{
    30.	get
    31.	{
    32.	return m_iCSharpMarks;
    33.	}
    34.	set
    35.	{
    36.	m_iCSharpMarks= value;
    37.	}
    38.	}
    39.	public int VCppMarks
    40.	{
    41.	get
    42.	{
    43.	return m_iVCPPMarks;
    44.	}
    45.	set
    46.	{
    a.	m_iVCPPMarks = value;
    47.	}
    48.	}
    49.	public int Calculate()
    50.	{
    51.	return m_iCSharpMarks + m_iVCPPMarks;
    52.	}
    53.	#endregion
    54.	}
  9. Compile and Build above code to make ComVisibleLib.dll, since this DLL is .net based you have to use RegAsm utility supplied with Dev Studio to generate ComVisibleLib.tlb. following is syntax and result returned by RegAsm utility when we run it on ComVisibleLib.DLL
    Command_Prompt> regasm comvisiblelib.dll /tlb
    
    Microsoft (R) .NET Framework Assembly Registration Utility 2.0.50727.4016
    
    Copyright (C) Microsoft Corporation 1998-2004.  All rights reserved.
    
    Types registered successfully
    
    Assembly exported to 'C:\Projects\COM\ComVisibleLib\ComVisibleLib\bin\Debug\comvisiblelib.tlb',
    
    and the type library was registered successfully

Using it in Visual C++ MFC Application

Now your .Net based COM aware DLL is ready, you will now create MFC based application, let follow step by step for this
  1. Add a new MFC project in above solution, by Clicking File | New | Project and selecting MFC based Dialog project.

                   Image 3
    Figure3: Select MFC Project

  2. Design the user interface like shown in the figure displayed below :-

                   Image 4
    Figure4: User interface

  3. Now import TLB in your project, by using following syntax :-
    #import "C:\Projects\COM\ComVisibleLib\ComVisibleLib\bin\Debug\ComVisibleLib.tlb" raw_interfaces_only
  4. Add code for communication with com component within your code
    C++
    1.	void CComVisibleLibTestDlg::OnBnClickedButtonSetvalues()
    2.	{
    3.	CoInitialize(NULL);
    4.	CLSID rclsid;
    5.	CLSIDFromProgID(L"progid_ClassComVisibleLib",&rclsid);
    6.	m_pToClass.CreateInstance(rclsid);
    7.	CString sName;
    8.	GetDlgItemText(IDC_EDIT_PNAME,sName);
    9.	HRESULT hr =m_pToClass->put_CSharpMarks(GetDlgItemInt(IDC_EDIT_PCSHARP));
    10.	hr =m_pToClass->put_VCppMarks(GetDlgItemInt(IDC_EDIT_PVCMARKS));
    11.	}
    12.	
    13.	void CComVisibleLibTestDlg::OnBnClickedButtonGetvalues()
    14.	{
    15.	long i = 0;
    16.	HRESULT hr=m_pToClass->get_CSharpMarks(&i);
    17.	SetDlgItemInt(IDC_EDIT_GCSHARP,i);
    18.	hr=m_pToClass->get_VCppMarks(&i);
    19.	SetDlgItemInt(IDC_EDIT_GVCMARK,i);
    20.	hr =m_pToClass->Calculate(&i);
    21.	SetDlgItemInt(IDC_EDIT_GTOTAL,i);
    22.	}

    m_pToClass is of type <code><code>ComVisibleLib::IClassComVisibleLibPtr declared as private class variable .

  5. Now, when you compile and run your application, it will throw "Class Not Registered"  error at time of creation of object. I spent almost 5-6 hr to solve this problem, during searching I found this comment from Ivan Towlson, which seems to be word send by god himself :-

    “Have a look in the registry to see if the CLSID mentioned in the TLH is>registered. If not, you may need to make the class ComVisible and>re-regasm.> Another possible issue is whether COM can actually find your assembly.>Your .NET assembly needs to be either in your app (EXE) directory or GAC. (Even though your class is registered as a COM component, it's>*not* globally accessible a la COM unless you put it in the GAC. The>registered COM handler for all .NET classes is mscoree.dll, not the.NET>assembly itself.)”

    Now you can solve this problem by either copying dll file to location where executable is present or create a strong key, associate with class library and register it with GAC.

    So, I have written post build step to run regasm on new created dll and then copy it to location where executable is present.
    C:\Windows\Microsoft.NET\Framework\v2.0.50727\regasm.exe  $(TargetPath)  /tlb
    copy  $(TargetPath)   $(SolutionDir)debug\ComVisibleLib.dll
  6. Now run the application to see the result.

                   Image 5
    Figure5: Result


Quick Reference for creating COM aware DotNet DLL

  • Create Class Library project
  • Add Interface and corresponding class to the project
  • Make ComVisible attribute in AssemblyInfo.cs to TRUE
  • Add ComVisible, Guid and InterfaceType on Interface
  • Add ComVisible, Guid and ClassInterface on class, ProgId attribute is optional
  • Compile and Build to create DotNet Aware DLL
  • Run RegAsm utility on DLL to create TLB file


Special Thanks

  • To My Mother (Late) and Father and off course My Wife
  • To CodeProject.com, for providing platform for Programmer Interaction.


License

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


Written By
Software Developer (Senior)
India India
He used to have biography here Smile | :) , but now he will hire someone (for free offcourse Big Grin | :-D ), Who writes his biography on his behalf Smile | :)

He is Great Fan of Mr. Johan Rosengren (his idol),Lim Bio Liong, Nishant S and DavidCrow and Believes that, he will EXCEL in his life by following there steps!!!

He started with Visual C++ then moved to C# then he become language agnostic, you give him task,tell him the language or platform, he we start immediately, if he knows the language otherwise he quickly learn it and start contributing productively

Last but not the least, For good 8 years he was Visual CPP MSMVP!

Comments and Discussions

 
QuestionHow about template class ?? and template interface .... Pin
PrafullaVedante26-Aug-13 21:11
PrafullaVedante26-Aug-13 21:11 
QuestionCOM Base DLL or ... ? Pin
Moharram1-Jul-12 21:38
Moharram1-Jul-12 21:38 
GeneralMy vote of 5 Pin
Sharjith27-Jun-12 1:43
professionalSharjith27-Jun-12 1:43 
AnswerRe: My vote of 5 Pin
ThatsAlok27-Jun-12 21:23
ThatsAlok27-Jun-12 21:23 
GeneralMy vote of 5 Pin
clawton26-Jun-12 9:34
clawton26-Jun-12 9:34 
GeneralRe: My vote of 5 Pin
ThatsAlok26-Jun-12 19:48
ThatsAlok26-Jun-12 19:48 
GeneralRe: My vote of 5 Pin
ThatsAlok26-Jun-12 19:49
ThatsAlok26-Jun-12 19:49 
SuggestionPractical Example? Pin
Vitaly Tomilov26-Jun-12 3:09
Vitaly Tomilov26-Jun-12 3:09 
AnswerRe: Practical Example? Pin
ThatsAlok26-Jun-12 19:55
ThatsAlok26-Jun-12 19:55 
GeneralRe: Practical Example? Pin
ThatsAlok26-Jun-12 19:55
ThatsAlok26-Jun-12 19:55 
GeneralMy vote of 5 Pin
Member 391656826-Dec-11 2:28
Member 391656826-Dec-11 2:28 
QuestionHow to keep the event from DLL DotNet? Pin
apaque18-May-10 7:28
apaque18-May-10 7:28 
GeneralNice Article Pin
Leeland Clay19-Jul-09 14:59
Leeland Clay19-Jul-09 14:59 
Very nice follow up to your last article.
GeneralRe: Nice Article Pin
ThatsAlok19-Jul-09 15:34
ThatsAlok19-Jul-09 15:34 

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.