Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using COM aware DotNet Library in MFC Application

0.00/5 (No votes)
25 Jun 2012 2  
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 :

                  
    Figure1: Selecting project

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

  3. Now add interface and it should be publicly accessible and also add class which implement the interface like this :-
     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


                  
    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
    InterfaceIsDual Indicates the interface is exposed to COM as a dual interface, which enables both early and late binding. InterfaceIsDual is the default value.
    InterfaceIsIUnknown Indicates an interface is exposed to COM as an IUnknown -derived interface, which enables only early binding.
    InterfaceIsIDispatch Indicates 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
    None Indicates 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.
    AutoDispatch Indicates 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.
    AutoDual Indicates 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:-
    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.

                  
    Figure3: Select MFC Project

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

                  
    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
    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 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.

                  
    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 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