Click here to Skip to main content
15,879,326 members
Articles / Desktop Programming / ATL

A Beginner Tutorial for Writing Simple COM/ATL DLL For VS2012

Rate me:
Please Sign up or sign in to vote.
4.90/5 (30 votes)
19 Dec 2012CPOL9 min read 161.2K   5.4K   79   43
Article touching simple programming instrument like Property, Method, Event and invoking ATL Dialogs from ATL/COM Component

Introduction

From Previous Article "During discussions in forums, I come across many people who want to write COM/ATL DLL, but don't know how to create properties, methods or raise events from component or strive for fundamental knowledge to create them. For them, I had written this article in Visual C++ 6.0. Since Visual Studio 6 is now out of support, many beginners requested me for the same article using Visual Studio 2005/2008 as there is much GUI difference between the two.

So, I am presenting the same article using Visual Studio 2005 and as it’s a new article, I have to provide readers with something new, otherwise they will reject this article. .NET application is slowly eating into Visual C++ share of applications. So to follow popular demand, I will show how to use COM component in .NET application instead of Unmanaged Application. As it is a beginner article, I am included a few extra screenshots in the article. Please don't flame for using the same."

Now this article is all about the creating COM\ATL component in Visual Studio 2012 (thanks Microsoft!). There is massive change in UI from its previous version and also lot of new functionality has been added. Though I am still going through its functionality. Would look forward adding a tip on its functionality. Initially I have installed VS2012 to see MS implementation of C++11 Standards, since I would love see some of new standard working in front of my eyes, however that not a topic of today.

With newer Visual Studio, Microsoft has decided to dropped support for New Window form in managed C++ and C++/CLI is used for interoperability only(What that mean? Unmanaged C++ has some future now?). Also, with advancement of DotNet, COM technology is losing its sheen day by day, however we will keep grim picture aside, and focus on article topic. Since MS still supporting it, Still there is light at other side of tunnel.

Table of Contents 

  • Creating ATL Component
  • Using it in Visual C# Application (.NET 2012)
  • Creating ATL Dialog and Using that in C#.Net 2012 new 

Creating ATL Component 

Let us go step by step to create an ATL component in Visual Studio 2012:

  1. Open Visual Studio 2012, and Click on File| New | Project menu, which will show you the following File-Save dialog box, where you can provide a name for your new project. Here I put the name as SimpleATLCom and it is advisable to choose the destination folder, where you want to create your project, otherwise it will clutter your Document Folder. Press OK to confirm your changes.  This will create a blank ATL component. Note: since i am using window7 as OS, you have to start VS2012 in administative mode, you can do that right click on VS2012  Image 1

    Image 2

    Figure 1: File Open Dialog

    If you remember earlier version, there used to three-four project template, Currently its seems it combined into one. Now, in the next dialog box (ATL Project wizard), choose default setting and press Finish. If you need MFC Support, check Support MFC checkbox. Here I find security development lifecycle (SDL) check, being student of CSSLP, i could say its a good addition.

    Image 3

    Figure 2: ATL Project Wizard

    Now insert ATL Object in Project which I will show in the next step.

  2. Choose Project | Add Class. The following dialog box will appear:, as exepected it has also some new added templates.

    Image 4

    Figure 3: Add Simple ATL Object

    Choose ATL|ATL Simple Object and Press ADD button.

  3. Enter new Interface name in ATL Simple Object Wizard. You will see rest of the details are auto filled by Visual Studio development environment when you begin typing the interface name. Also check the Connection Point check box in the Option Tab which provides component ability to fire events.Also, for error message support you can optionally check ISupportErrorInfo checkbox.

    Image 5

    Figure 4: ATL Simple Object

    Image 6

    Figure 5: Select Connection Point Check Box
  4. Now if you check the following IDL (Interface Definition Language) will be generated and included into your project:

    C++
       // SimpleATLCom.idl : IDL source for SimpleATLCom
    //
    
    // This file will be processed by the MIDL tool to
    // produce the type library (SimpleATLCom.tlb) and marshalling code.
    
    import "oaidl.idl";
    import "ocidl.idl";
    
    [
    	object,
    	uuid(06598046-EBCA-424A-9F6A-4C01DBE17C8A), ------(1)
    	dual,
    	nonextensible,
    	pointer_default(unique)
    ]
    interface ISimpleCom : IDispatch{
    };
    [
    	uuid(84E5C77E-58FA-4D2D-A00F-73E319EF601E), -------(2)
    	version(1.0),
    ]
    library SimpleATLComLib
    {
    	importlib("stdole2.tlb");
    	[
    		uuid(67652A2B-278A-4B84-9F61-65F899A1004F)		
    	]
    	dispinterface _ISimpleComEvents  -------(3)
    	{
    		properties:
    		methods:
    	};
    	[
    		uuid(435356F9-F33F-403D-B475-1E4AB512FF95)		
    	]
    	coclass SimpleCom
    	{
    		[default] interface ISimpleCom;
    		[default, source] dispinterface _ISimpleComEvents;
    	};
    };
    • (1): This is default UUID (Unique ID) generated by Dev environment, this makes the component uniquely identifiable.
    • (2): That’s our component name and UUID, its contents can be used to uniquely identify a component, you can use this id to create component.
    • (3): Our dis-interface i.e. event function.
  5. Now Add Property and method using Add Property and Add Method Wizard. This can be found in Class view, right click on ISimpleCom interface then ADD->Add Method or Add Property:

    Image 7

    Figure 6: Add Method/Add Property

    Add method Calculate with BOOL a_bFireEvent as IN and  Long* a_lTotalMarks as OUT, RETVAL parameter like this:

    Image 8

    Figure 7: Add Method

    Add Property ComMark of type Long as shown in the example:

    Image 9
    Figure 8: Add Property

    Similarly add property AtlMarks as long and StudentName as BSTR yourself. Sorry I forgot to mention about IN, OUT and RETVAL type in parameter.

    • Propget – stands for property for getting the value from Component
    • PropPut – stands for property for putting property to Component. This can be optional and if you remove it, this can make your property readonly
    • Method — simple function to perform some calculation
    • [in] - means data is going in or you are putting some value to Component
    • [out,retval] - Notation states that argument using this will return with data
    • HRESULT - Standard Error reporting variable
  6. Now add Event almost in the same way, we add method in interface. Now right click on _ISimpleComEvents and Add | Add method.

    Image 10

    Figure 9: Add Event
  7. Once everything is done, modified IDL File would look like this:

    C++
    import "oaidl.idl";
    import "ocidl.idl";
    
    [
    	object,
    	uuid(06598046-EBCA-424A-9F6A-4C01DBE17C8A),
    	dual,
    	nonextensible,
    	pointer_default(unique)
    ]
    interface ISimpleCom : IDispatch{
    	[id(1)] HRESULT Calculate([in] VARIANT_BOOL a_bFireEvent, [out,retval] LONG* a_lTotalMarks);
    	[propget, id(2)] HRESULT ComMarks([out, retval] LONG* pVal);
    	[propput, id(2)] HRESULT ComMarks([in] LONG newVal);
    	[propget, id(3)] HRESULT AtlMarks([out, retval] LONG* pVal);
    	[propput, id(3)] HRESULT AtlMarks([in] LONG newVal);
    	[propget, id(4)] HRESULT StudentName([out, retval] BSTR* pVal);
    	[propput, id(4)] HRESULT StudentName([in] BSTR newVal);
    };
    [
    	uuid(84E5C77E-58FA-4D2D-A00F-73E319EF601E),
    	version(1.0),
    ]
    library SimpleATLComLib
    {
    	importlib("stdole2.tlb");
    	[
    		uuid(67652A2B-278A-4B84-9F61-65F899A1004F)		
    	]
    	dispinterface _ISimpleComEvents
    	{
    		properties:
    		methods:
    			[id(1)] HRESULT TotalMarks(LONG a_lTotalMarks);
    	};
    	[
    		uuid(435356F9-F33F-403D-B475-1E4AB512FF95)		
    	]
    	coclass SimpleCom
    	{
    		[default] interface ISimpleCom;
    		[default, source] dispinterface _ISimpleComEvents;
    	};
    };
  8. Now add connection points into the main interface,before doing previous please compile . For adding it, right click on CSimpleCom and click ADD | Add Connection Point. The following screenshot will appear:

    Image 11

    Figure 10: Chose Add Connection Point

    Choose > button to move _ISimpleComEvents source interface to implement connection point and click on finish. A new function Fire_TotalMarks is added to CSimpleCom class which can be used to raise event. Remember to select SimpleATLcomLib in Available type libraries, in my computer i have to repeat this step two times, i don't know the reason.

    Image 12

    Figure 11: Add Connection Wizard
  9. Now code all properties and functions present in CSimpleCom class, i.e. Add Class Variable for ComMarks, ATLMarks and

    StudentName
    for storing information coming from the outer world. After coding, our class would look like this:

    C++
               STDMETHODIMP CSimpleCom::InterfaceSupportsErrorInfo(REFIID riid)
    {
    	static const IID* const arr[] = 
    	{
    		&IID_ISimpleCom
    	};
    
    	for (int i=0; i < sizeof(arr) / sizeof(arr[0]); i++)
    	{
    		if (InlineIsEqualGUID(*arr[i],riid))
    			return S_OK;
    	}
    	return S_FALSE;
    }
    
    STDMETHODIMP CSimpleCom::Calculate(VARIANT_BOOL a_bFireEvent, LONG* a_lTotalMarks)
    {
    	*a_lTotalMarks = m_lATLMarks + m_lComMarks;
    	if(a_bFireEvent == VARIANT_TRUE)
    		Fire_TotalMarks(*a_lTotalMarks);
    
    	return S_OK;
    }
    
    STDMETHODIMP CSimpleCom::get_ComMarks(LONG* pVal)
    {
    	*pVal= m_lComMarks;
    	return S_OK;
    }
    
    STDMETHODIMP CSimpleCom::put_ComMarks(LONG newVal)
    {
    	m_lComMarks = newVal;
    	return S_OK;
    }
    
    STDMETHODIMP CSimpleCom::get_AtlMarks(LONG* pVal)
    {
    	*pVal= m_lATLMarks;
    	return S_OK;
    }
    
    STDMETHODIMP CSimpleCom::put_AtlMarks(LONG newVal)
    {
    	m_lATLMarks = newVal;
    	return S_OK;
    }
    
    STDMETHODIMP CSimpleCom::get_StudentName(BSTR* pVal)
    {
    	*pVal = m_bstStudName.Copy();
    	return S_OK;
    }
    
    STDMETHODIMP CSimpleCom::put_StudentName(BSTR newVal)
    {
    	m_bstStudName = newVal;
    	return S_OK;
    }

    Now Build the solution, your DLL would be registered automatically, when the build process is over.

Using it in Visual C# Application

Now again move forward step by step to create a project in C#.NET and include support of COM component in project.

  1. Add a new C# Window application Project (SimpleATLcomTest1) in the same solution and click to accept default configuration to create a test project.
    Image 13
    Figure 12: Add a New C# Project
  2. Design the interface as shown in the screenshot given below; add three Textboxes for putting values into the Component, two boxes for retrieving all values including the calculated value amd One checkbox to get Data by event instead of sync.

    Image 14

    Figure 13: Test Application Design
  3. Now, add a reference for SimpleATLCom.dll into your project, by right clicking on Project name and clicking on Add Reference menu item.

    Image 15

    Figure 14: Add Reference Menu

    Click on COM Tab item and select SimpleATLCom.dll to add reference of Component into the project.

    Image 16

    Figure 15: Add Reference
  4. Now, write the following code to use the component into your project.

    C#
    using SimpleATLComLib;
    
    namespace SimpleATLComTest1
    {
        public partial class FrmSimpleATLComTest1 : Form
        {
            SimpleATLComLib.SimpleCom objSimpleObj = null;
            int _totalMarks = 0;
    
            public FrmSimpleATLComTest1()
            {
                InitializeComponent();
                objSimpleObj = new SimpleCom();
                objSimpleObj.TotalMarks += objSimpleObj_TotalMarks;
            }
    
            void objSimpleObj_TotalMarks(int a_lTotalMarks)
            {
                string txtMsg = string.Format(" {0} got  {1} Marks", txtName.Text, _totalMarks);
                MessageBox.Show(txtMsg);
            }
    
            private void btnCalculate_Click(object sender, EventArgs e)
            {
                objSimpleObj.AtlMarks = int.Parse(txtATLMarks.Text);
                objSimpleObj.ComMarks = int.Parse(txtComMarks.Text);
                
                bool fireEvent = chkFireEvent.Checked;
                _totalMarks = objSimpleObj.Calculate(fireEvent);
    
                txtResultName.Text = txtName.Text;
                txtMarks.Text = _totalMarks.ToString();
            }
        }
    } 

    There quite improvement in code generation in VS2012, using COM component is quite easy now. for reference, add import SimpleATLComLib using using keyword, and add eventhandller on class object TotalMarks event.

  5. Run the Test application, and you will see the result:

    Image 17   Image 18
    Without Fire Event With FireEvent

    Figure 16: Running Application

    On Checking FireEvent checkbox, the "true" argument is passed to ATL Dll, which in turn call TotalMarks event from COM DLL. which we catching in our delegate.

Creating ATL Dialog and Using that in C#.Net 2012 new 

Source of inspiration for writing ATL-Dialog support in this article is programming forum, recently I am discussing the prospect of using ATL-Dialog invoked through ATL-COM DLL. So here it our very own ATLDialog implementation  which will take input and calculate all the marks.

Here I am demonstrating above thing without event sink, since implementing event itself is a big topic and don't want to complicate the stuff 

  1. add new class in our SimpleATLCom dll of type ATLDialog, Project->Add Class ,
    Image 19
    Figure 17: Adding ATL Dialog Class

    And Name it SimpleATLDialog.

    Image 20
    Figure 18: Adding ATL Dialog Class
  2. Now Design the interface similar to C# application, after design is complete the dialog box looks like this :-
    Image 21
    Figure 19: ATL Dialog Interface Design
  3. Add following code to handle event in the Dialog Box
    C++
    #include "SimpleCom.h"
    // CSimpleATLDialog
    
    LRESULT CSimpleATLDialog::OnBnClickedButtonCalculate(WORD /*wNotifyCode*/,
     WORD /*wID*/, HWND /*hWndCtl*/, BOOL& /*bHandled*/)
    {
    	
    	int lATLMarks = this->GetDlgItemInt(IDC_EDIT_ATLMARKS);
    	int lCOMMarks = this->GetDlgItemInt(IDC_EDIT_COMMARKS);
    	TCHAR strName[100];
    	GetDlgItemText(IDC_EDIT_NAME,strName,250);
    	CComObject<CSimpleCom>* simpleComObj;
    	HRESULT hRes = CComObject<CSimpleCom>::CreateInstance(&simpleComObj);
    
    	if(SUCCEEDED(hRes))
    	{
    		long totalMarks =0;
    		simpleComObj->put_AtlMarks(lATLMarks);
    		simpleComObj->put_ComMarks(lCOMMarks);
    		simpleComObj->Calculate(VARIANT_FALSE,&totalMarks);
    
    		TCHAR strMsg[150];
    		wsprintf(strMsg,_T("%s got following Marks %d"),strName,totalMarks);
    
    		MessageBox(strMsg);
    
    	}
    	else
    	{
    		MessageBox(_T("Initialization Failed"));	
    	}
    
    	return 0;
    }
       
    Here I am retrieving the ATLMarks and COMMarks from dialog text boxes and the using CComObject Creating the SimpleATLCom object (since later is the com class) and passing all the values to created object with event firing equal to false.
  4. Add separate simple ATL Object SimpleATLDLGController, and a method InvokeDialog to it. Once done code of new interface would look like this.
    C++
       #include "SimpleATLDialog.h"
    // CSimpleATLDLGController
    
    STDMETHODIMP CSimpleATLDLGController::InvokeDialog(void)
    {
    	CSimpleATLDialog atlDLg;
    	atlDLg.DoModal();
    	return S_OK;
    }
  5. Redesign our previous C# Project, include a new button, from which we will handle SimpleATLDLGController InvokeDialog method.
    Image 22
    Figure 20: C# Form Interface Design
    code for the button click is as follow :-
    C#
    private void btnInvokeDLG_Click(object sender, EventArgs e)
      {
          SimpleATLComLib.SimpleATLDLGController simpleCTRLDLG = new SimpleATLDLGController();
          simpleCTRLDLG.InvokeDialog();
    
      }
    
  6. Simply Run your application
    Image 23
    Figure 20: Application Demo ---2

About the Download Code

Source Code Includes

  • SimpleATLCom Test1 Project: Contain basic Project
  • SimpleATLCom Test2 Project: Contain basic Project along with ATL Dialog Project

Using of Demo Application

If you like, use the Com DLL and Test application first. Don't forget to register Com DLL, i.e., SimpleAtlCom.dll, to your computer. You can use this command line to register the Component. For Window Vista onward you have to start cmdline in admin mode 

Drive:> %sys%regsvr32 path_to_dll\SimpleAtlCom.dll  

Author Comment

I tried my level best to tell each and every simple aspect of com DLL. If it is missing something, feel free to contact me or leave your precious comments in the discussion forum below. 

Special Thanks 

  • To my mother (Late) and father and off course my wife  
  • To CodeProject.com, for providing a platform for Programmer Interaction.

Other Article of this series

History 

  • 9th December, 2012: Initial 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 (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

 
Questionsuccess in register, failed creating object Pin
MyungLee4819-Feb-21 12:40
MyungLee4819-Feb-21 12:40 
AnswerRe: success in register, failed creating object Pin
MyungLee4819-Feb-21 12:44
MyungLee4819-Feb-21 12:44 
QuestionThere is a bug in method objSimpleObj_TotalMarks( ) Pin
truthadjustr27-Sep-18 5:47
truthadjustr27-Sep-18 5:47 
QuestionCOM/ATL in VS2017 is Broken! Do not use it Pin
truthadjustr26-Sep-18 8:43
truthadjustr26-Sep-18 8:43 
Questioncrystal tears from my eyes... Pin
truthadjustr25-Sep-18 19:21
truthadjustr25-Sep-18 19:21 
QuestionUsing VS2015 and compiling SimpleATLCom Solution Pin
MoonRiverLarry26-Oct-17 4:36
MoonRiverLarry26-Oct-17 4:36 
AnswerRe: Using VS2015 and compiling SimpleATLCom Solution Pin
ThatsAlok20-Nov-17 20:13
ThatsAlok20-Nov-17 20:13 
QuestionHow to use this COM object in VB Script Pin
GTAVLover11-Jul-17 14:47
GTAVLover11-Jul-17 14:47 
AnswerRe: How to use this COM object in VB Script Pin
ThatsAlok20-Nov-17 20:17
ThatsAlok20-Nov-17 20:17 
QuestionPassing more complex parameters Pin
Member 1300257523-Feb-17 3:24
Member 1300257523-Feb-17 3:24 
QuestionI dont see atl components when I go to add item in visual studio 2013 community edition Pin
sly_Chandan6-Jun-16 0:43
sly_Chandan6-Jun-16 0:43 
AnswerRe: I dont see atl components when I go to add item in visual studio 2013 community edition Pin
truthadjustr26-Sep-18 19:58
truthadjustr26-Sep-18 19:58 
QuestionBeginner Tutorial for Writing Simple COM/ATL DLL For VS2012 Pin
Member 114329358-Sep-15 13:36
Member 114329358-Sep-15 13:36 
QuestionExplanation for events Pin
tweber201214-Feb-14 10:58
tweber201214-Feb-14 10:58 
AnswerRe: Explanation for events Pin
ThatsAlok16-Feb-14 21:49
ThatsAlok16-Feb-14 21:49 
GeneralRe: Explanation for events Pin
tweber201217-Feb-14 1:15
tweber201217-Feb-14 1:15 
GeneralRe: Explanation for events Pin
tweber201217-Feb-14 5:13
tweber201217-Feb-14 5:13 
GeneralRe: Explanation for events Pin
tweber201217-Feb-14 7:10
tweber201217-Feb-14 7:10 
GeneralRe: Explanation for events Pin
ThatsAlok18-Feb-14 17:24
ThatsAlok18-Feb-14 17:24 
QuestionWhere/how to declare m_lXXX variables? Pin
Member 1057474412-Feb-14 12:33
Member 1057474412-Feb-14 12:33 
AnswerRe: Where/how to declare m_lXXX variables? Pin
ThatsAlok12-Feb-14 17:20
ThatsAlok12-Feb-14 17:20 
QuestionWhy there is no need to prefix event function with [in] or [in, out] attributes ? Pin
Ak47AL8-Oct-13 8:15
Ak47AL8-Oct-13 8:15 
AnswerRe: Why there is no need to prefix event function with [in] or [in, out] attributes ? Pin
ThatsAlok8-Oct-13 20:11
ThatsAlok8-Oct-13 20:11 
Question.idl dispinterface method not added Pin
rtischer827719-Sep-13 6:31
rtischer827719-Sep-13 6:31 
AnswerRe: .idl dispinterface method not added Pin
sunbelt5712-Oct-13 7:09
sunbelt5712-Oct-13 7:09 

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.