[ Home | Syllabus | Course Notes | Assignments | Search]
NOTE: The version is chapter 5 of the book adds two interfaces
which are not necessary to understanding ATL.
Here we will develop a single interface version which is closer to the version
in chapter 4.
ATL handles much of the overhead for generating COM objects and is intended primarily for implementing COM servers. Consists of the following:
Create an ATL project using AppWizard
DLL management
Class Factory creation
Register/Unregister COM server
Create a ATL Object using the ATL Object wizard
Interface/Implementation Classes
COM attributes
These directions are similar to those on page 129
Select FILE/NEW/PROJECTS Tab/ATL COM APPWizard
Enter "ATLspellCheck" in project name
Click OK
Keep default "DLL" and click "finish"
You will get the following files
ATLspellCheck.cpp
ATLspellCheck.def: DLL entry points
ATLspellCheck.idl
ATLspellCheck.rc: contains registration info (with UUID's already chosen)
Now we will create a single ATL Object using the ATL Object Wizard
Select "Insert/New ATL Object"
Take the default of a "Simple Object" (shown
here)/ select "Next"
You should the "ATL Object Wizard
Properties" dialog, type "SpellChecker" in the "Short
Name" field, take the defaults on the other derived names (you should
see)
Click the "Attributes" tab, set to
"Custom" interface, select "OK"
SpellChecker.h
// SpellChecker.h : Declaration of the CSpellChecker #ifndef __SPELLCHECKER_H_ #define __SPELLCHECKER_H_ #include "resource.h" // main symbols ///////////////////////////////////////////////////////////////////////////// // CSpellChecker class ATL_NO_VTABLE CSpellChecker: // multiple inheritance public CComObjectRootEx<CComSingleThreadModel>, public CComCoClass<CSpellChecker, &CLSID_SpellChecker>, public ISpellChecker { public: CSpellChecker() { } DECLARE_REGISTRY_RESOURCEID(IDR_SPELLCHECKER)// registry info in this resource ID DECLARE_PROTECT_FINAL_CONSTRUCT() BEGIN_COM_MAP(CSpellChecker) COM_INTERFACE_ENTRY(ISpellChecker) END_COM_MAP() // ISpellChecker public: }; #endif //__SPELLCHECKER_H_
F1: This class provides the IUnknown interface. It does not assume that the code will be thread safe (because of "Apartment" threading model chosen (by default) when creating the ATL Object
F2: provides the class factory (book calls this the "class object")
F4: used by CComObjectRootEx to implement queryinterface - table driven access to the v-table
Need to define the ISpellChecker interface methods.
This requires entries in the IDL, header and implementation files.
You can do by hand or use the the class wizard - go to the "ClassView" tab, right view on the "ISpellChecker" interface and select "Add Method"
Add the following "IDL" prototype
click "OK"
Add the "UseCustomDictionary" method in a similar
fashion to look like this
Click "OK"
// IDL
// ATLspellCheck.idl : IDL source for ATLspellCheck.dll // // This file will be processed by the MIDL tool to // produce the type library (ATLspellCheck.tlb) and marshalling code. import "oaidl.idl"; import "ocidl.idl"; [ object, uuid(2FFB4D41-052A-497C-BE43-71B1D09B6360), helpstring("ISpellChecker Interface"), pointer_default(unique) ] interface ISpellChecker : IUnknown { [helpstring("method CheckSpelling")] HRESULT CheckSpelling([in] LPOLESTR word, [out,retval] VARIANT_BOOL *result); [helpstring("method UseCustomDictionary")] HRESULT UseCustomDictionary([in] LPOLESTR path); }; [ uuid(3FED244E-4367-4C8F-932C-C0DF67BEBAFC), version(1.0), helpstring("ATLspellCheck 1.0 Type Library") ] library ATLSPELLCHECKLib { importlib("stdole32.tlb"); importlib("stdole2.tlb"); [ uuid(3D233E11-3661-4EA5-87C8-ABCFCFEE76BF), helpstring("SpellChecker Class") ] coclass SpellChecker { [default] interface ISpellChecker; }; };
// header file
// ISpellChecker public: STDMETHOD(UseCustomDictionary)(/*[in]*/ LPOLESTR path); STDMETHOD(CheckSpelling)(/*[in]*/ LPOLESTR word, /*[out,retval]*/ VARIANT_BOOL *result); };
// implementation file
STDMETHODIMP CSpellChecker::CheckSpelling(LPOLESTR word, VARIANT_BOOL *result) { // TODO: Add your implementation code here return S_OK; } STDMETHODIMP CSpellChecker::UseCustomDictionary(LPOLESTR path) { // TODO: Add your implementation code here return S_OK; }
The implementation of this method is almost identical to that for the previous version.
Main difference is use of Wide characters (OLDSTR, wstring) which is compatible with OLE and type libraries.
Also, you need to add "/GX" to Project/Settings/C++ tab/General/Project Options"
No need to define IUnknown interface
No need to provide ClassFactory
No need to provide DLL management
Check out OLE view in visual studio.
Go to "type libraries"
Scroll to "ATLSpellCheck", double click to see
this
Similar interface built in MFC - but access to the COM object is different. Since we will use visual C++ NATIVE COM support by importing the type library. Notice we are using non-native types (OLESTR, LPOLESTR and VARIANT_BOOL. These types are language independent types.
With the type library you can examine the interface using OLEviewer or other suitable object browser.
By adding an "import" of the type library into "stdafx.h", when the project is built - Visual C++ will use the type library to write "wrapper" classes around the COM interface to make the COM object act like a normal C++ object.
Select "File/New/Projects/MFC AppWizard"
name the project ATLclient
Select "Dialog-based/Finish"
The dialog will be similar to that defined in chapter 4
InitInstance contains
if (!AfxOleInit())
AfxMessageBox("Could not initialize COM");
StdAfx.h contains
#import "..\ATLspellCheck.tlb"
which is relative path to server type library (will help provide access to
the COM object)
If you build the project now you will get a ATLspellCheck.tlh and .tli files in the directory containing the executable.
#include <comdef.h> namespace ATLSPELLCHECKLib { struct /* coclass */ SpellChecker; struct __declspec(uuid("2ffb4d41-052a-497c-be43-71b1d09b6360")) /* interface */ ISpellChecker; // // Smart pointer typedef declarations // _COM_SMARTPTR_TYPEDEF(ISpellChecker, __uuidof(ISpellChecker)); // // Type library items // struct __declspec(uuid("3d233e11-3661-4ea5-87c8-abcfcfee76bf")) SpellChecker; // [ default ] interface ISpellChecker struct __declspec(uuid("2ffb4d41-052a-497c-be43-71b1d09b6360"))// interface ID ISpellChecker : IUnknown { // // Wrapper methods for error-handling // VARIANT_BOOL CheckSpelling ( LPWSTR word ); HRESULT UseCustomDictionary ( LPWSTR path ); // // Raw methods provided by interface // virtual HRESULT __stdcall raw_CheckSpelling ( LPWSTR word, VARIANT_BOOL * result ) = 0; virtual HRESULT __stdcall raw_UseCustomDictionary ( LPWSTR path ) = 0; };
F5: smart pointer definitions for COM objects and a com-error class
F6: hides CLSID
F7: Hides "HResult" return
value - looks more like Native C++ class
Here is the definition
inline VARIANT_BOOL ISpellChecker::CheckSpelling ( LPWSTR word ) {
VARIANT_BOOL _result;
HRESULT _hr = raw_CheckSpelling(word, &_result);
if (FAILED(_hr)) _com_issue_errorex(_hr, this, __uuidof(this));
return _result;
}
---- To access this information - need to add the following line of code to the header file near top
using namespace ATLSPELLCHECKLib;
and the following inside the class definition
private:
ISpellCheckerPtr mSpellChecker;
---- to create the COM object, add the following to OnInitDialog
HRESULT hr=mSpellChecker.CreateInstance("ATLspellCheck.SpellChecker");
if (FAILED(hr)) {
AfxMessageBox("Could not instantiate the server.\n" \
"Check that it is registered.");
}
//Notice the use of the Prog.ID instead of the CLSID (which would have been the following)
HRESULT hr=mSpellChecker.CreateInstance(__uuidof(SpellChecker));
// Now just need to add the "logic"
For the client, the difference are small, but include
Simpler call to create an instance
Normal return values are supported
C++ native exception handling is available
No need to include MIDL generated header files and
"C" program containing UUIDs (the type library supplies these)