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.
Let us go step by step to create an ATL component in Visual Studio 2012:
SimpleATLCom
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.
Now insert ATL Object in Project which I will show in the next step.
Choose ATL|ATL Simple Object and Press ADD button.
Now if you check the following IDL (Interface Definition Language) will be generated and included into your project:
// 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; }; };
Add method Calculate with BOOL a_bFireEvent as IN and Long* a_lTotalMarks as OUT, RETVAL parameter like this:
Add Property ComMark of type Long as shown in the example:
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
PropPut
Method
[in]
[out,retval]
HRESULT
_ISimpleComEvents
Once everything is done, modified IDL File would look like this:
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; }; };
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.
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:
CSimpleCom
ComMarks
ATLMarks
StudentName
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.
Now again move forward step by step to create a project in C#.NET and include support of COM component in project.
SimpleATLcomTest1
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.
Textbox
Now, add a reference for SimpleATLCom.dll into your project, by right clicking on Project name and clicking on Add Reference menu item.
Click on COM Tab item and select SimpleATLCom.dll to add reference of Component into the project.
Now, write the following code to use the component into your project.
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.
using
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.
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
And Name it SimpleATLDialog.
#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.
#include "SimpleATLDialog.h" // CSimpleATLDLGController STDMETHODIMP CSimpleATLDLGController::InvokeDialog(void) { CSimpleATLDialog atlDLg; atlDLg.DoModal(); return S_OK; }
private void btnInvokeDLG_Click(object sender, EventArgs e) { SimpleATLComLib.SimpleATLDLGController simpleCTRLDLG = new SimpleATLDLGController(); simpleCTRLDLG.InvokeDialog(); }
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
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.
This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)
General News Suggestion Question Bug Answer Joke Rant Admin
Math Primers for Programmers