An OLE Automation server exposes automation objects that have methods and properties as their external interfaces. The methods and properties exposed by the automation objects can be directly accessed by the automation controllers such as Visual Basic or by invoking methods defined in the IDispatch interface.
In this chapter, an automation server will be created through the implementation of the IDispatch interface to illustrate the fundamental OLE automation concept. This automation server will be used in both C++ and Visual Basic.
Then, this automation server will be created through the use of MFC AppWizard (exe), Control Wizard, and the Active Template Library (ATL).
At the end of the chapter, this automation server will be used in Internet Explorer 3.0 and the HTTP Web Server.
lst41.exe is an automation server implemented without using any wizards or code generation tools. This automation server exposes one method called GetMachineName. GetMachineName has no input parameter. The return value is the name of the computer where the application runs.
To expose the objects, IDispatch interface must be implemented. There are two ways to implement IDispatch interface. One way is to implement four methods in the IDispatch interface. The other way used in lst41.exe is to expose the objects through OLE automation by using the CreateStdDispatch method. CreateStdDispatch creates a standard implementation of the IDispatch interface through a single function call.
The following steps illustrates how to create lst41.exe.
[ uuid(9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B), helpstring("Lst41 Type Library"), lcid(0x0409), version(1.0) ] library Lst41 { importlib("stdole32.tlb"); [ odl, uuid(9FBBEDE3-1B40-11d0-88E0-00AA004A7C7B), ] interface ILst41: IUnknown { BSTR GetMachineName(void); } [ uuid(9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B), ] dispinterface DLst41 { interface ILst41; } [ uuid(9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B), helpstring("Lst41") ] coclass CLst41 { dispinterface DLst41; interface ILst41; } };
{ BSTR GetMachineName(void); } [ uuid(9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B), ] dispinterface DLst41 { interface ILst41; }
dispinterface DLst41 { BSTR GetMachineName(void); }
The coclass statement defines the class ID for class name CLst41 and the interfaces supported by CLst41.
mktyplib -h ilst41.h lst41.odl
Listing 4.2. C++ header file: ilst41.h
/* This header file machine-generated by mktyplib.exe */ /* Interface to type library: Lst41 */ #ifndef _Lst41_H_ #define _Lst41_H_ DEFINE_GUID(LIBID_Lst41,0x9FBBEDE2L,0x1B40,0x11D0,0x88,0xE0,0x00, 0xAA,0x00,0x4A,0x7C,0x7B); #ifndef BEGIN_INTERFACE #define BEGIN_INTERFACE #endif DEFINE_GUID(IID_ILst41,0x9FBBEDE3L,0x1B40,0x11D0,0x88,0xE0,0x00, 0xAA,0x00,0x4A,0x7C,0x7B); /* Definition of interface: ILst41 */ #undef INTERFACE #define INTERFACE ILst41 DECLARE_INTERFACE_(ILst41, IUnknown) { BEGIN_INTERFACE #ifndef NO_BASEINTERFACE_FUNCS /* IUnknown methods */ STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; STDMETHOD_(ULONG, AddRef)(THIS) PURE; STDMETHOD_(ULONG, Release)(THIS) PURE; #endif /* ILst41 methods */ STDMETHOD_(BSTR, GetMachineName)(THIS) PURE; }; DEFINE_GUID(DIID_DLst41,0x9FBBEDE4L,0x1B40,0x11D0,0x88,0xE0,0x00, 0xAA,0x00,0x4A,0x7C,0x7B); /* Definition of dispatch interface: DLst41 */ #undef INTERFACE #define INTERFACE DLst41 DECLARE_INTERFACE_(DLst41, IDispatch) { BEGIN_INTERFACE #ifndef NO_BASEINTERFACE_FUNCS /* IUnknown methods */ STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; STDMETHOD_(ULONG, AddRef)(THIS) PURE; STDMETHOD_(ULONG, Release)(THIS) PURE; /* IDispatch methods */ STDMETHOD(GetTypeInfoCount)(THIS_ UINT FAR* pctinfo) PURE; STDMETHOD(GetTypeInfo)( THIS_ UINT itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo) PURE; STDMETHOD(GetIDsOfNames)( THIS_ REFIID riid, OLECHAR FAR* FAR* rgszNames, UINT cNames, LCID lcid, DISPID FAR* rgdispid) PURE; STDMETHOD(Invoke)( THIS_ DISPID dispidMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR* pdispparams, VARIANT FAR* pvarResult, EXCEPINFO FAR* pexcepinfo, UINT FAR* puArgErr) PURE; #endif /* Capable of dispatching all the methods of interface ILst41 */ }; DEFINE_GUID(CLSID_CLst41,0x9FBBEDE5L,0x1B40,0x11D0,0x88,0xE0,0x00, 0xAA,0x00,0x4A,0x7C,0x7B); #ifdef __cplusplus class CLst41; #endif #endif
dispinterface DLst41 { interface ILst41; }
Listing 4.3. ILst41 implementation.
Clst41I and CLst41 class definition
#include <objbase.h> #include "clsid.h" #include "ilst41.h" class CLst41; class CLst41I : public ILst41 { public: STDMETHOD(QueryInterface)(THIS_ REFIID riid, LPVOID* ppvObj); STDMETHOD_(ULONG, AddRef)(THIS); STDMETHOD_(ULONG, Release)(THIS); STDMETHOD_(BSTR, GetMachineName)(THIS); CLst41* m_pLst41; }; class CLst41 : public IUnknown { public: CLst41(); ~CLst41(); static CLst41* Create(); STDMETHOD(QueryInterface)(REFIID riid, void ** ppv); STDMETHOD_(ULONG, AddRef)(void); STDMETHOD_(ULONG, Release)(void); private: ULONG m_refs; // Reference count. IUnknown* m_disp_interface; // Pointer to the standard dispatch object. CLst41I* m_prog_interface; //programmable interface. };Clst41I and CLst41 class implementation
#include <objbase.h> #include "resource.h" #include "lst41c.h" BSTR CreateBSTR(char *lpString) { BSTR bsz; UINT cch; cch = strlen(lpString); bsz = SysAllocStringLen(NULL , cch); if(bsz == NULL) return NULL; if(cch > 0) MultiByteToWideChar(CP_ACP, 0, lpString, cch, bsz, cch); bsz[cch] = NULL; return bsz; }
STDMETHODIMP_(BSTR) CLst41I::GetMachineName() { BSTR b; ULONG ulLen; char *lpName; lpName = new char[MAX_PATH]; ulLen = MAX_PATH; GetComputerName(lpName, &ulLen); b = CreateBSTR(lpName); return b; } //standard IUunknown methods implementation STDMETHODIMP CLst41I::QueryInterface(REFIID riid, void** ppv) { return m_pLst41->QueryInterface(riid, ppv); } STDMETHODIMP_(ULONG) CLst41I::AddRef() { return m_pLst41->AddRef(); } STDMETHODIMP_(ULONG) CLst41I::Release() { return m_pLst41->Release(); } IUnknown FAR* CreateDispatchInterface(IUnknown* punkController, void * pProgInterface) { HRESULT hresult; ITypeLib* ptlib; ITypeInfo* ptinfo; IUnknown* punkStdDisp; hresult = LoadRegTypeLib(LIBID_Lst41, 1, 0, 0x0409, &ptlib); if (hresult != S_OK) { if((hresult = LoadTypeLib(L"lst41.tlb", &ptlib)) != S_OK) return NULL; } hresult = ptlib->GetTypeInfoOfGuid(IID_ILst41, &ptinfo); if (hresult != S_OK) return NULL; ptlib->Release(); hresult = CreateStdDispatch(punkController,pProgInterface, ptinfo, &punkStdDisp); if (hresult != S_OK) return NULL; ptinfo->Release(); return punkStdDisp; }
{ m_refs = 1; m_disp_interface = NULL; m_prog_interface = new CLst41I; m_prog_interface->m_pLst41 = this; }CLst41::~CLst41()
{ delete m_prog_interface; } CLst41 * CLst41::Create() { CLst41* pLst41; IUnknown* punkStdDisp; pLst41 = new CLst41(); if(pLst41 == NULL) return NULL; punkStdDisp = CreateDispatchInterface((IUnknown *) pLst41, pLst41->m_prog_interface); if (punkStdDisp == NULL) { pLst41->Release(); return NULL; } pLst41->m_disp_interface = punkStdDisp; return pLst41; } STDMETHODIMP CLst41::QueryInterface(REFIID riid, void ** ppv) { if (riid == IID_IUnknown) *ppv = this; else if (riid == IID_IDispatch || riid == DIID_DLst41) return m_disp_interface->QueryInterface(IID_IDispatch, ppv); else if (riid == IID_ILst41) *ppv = &m_prog_interface; else { *ppv = NULL; return ResultFromScode(E_NOINTERFACE); } AddRef(); return S_OK; }
STDMETHODIMP_(ULONG) CLst41::AddRef() { return ++m_refs; } STDMETHODIMP_(ULONG) CLst41::Release() { if(--m_refs == 0) { if(m_disp_interface != NULL) m_disp_interface->Release(); PostQuitMessage(0); delete this; return 0; } return m_refs; }
Listing 4.4. CLst41 class factory.
class CLst41CF : public IClassFactory { public: CLst41CF(); static IClassFactory* Create(); STDMETHOD(QueryInterface)(REFIID riid, void ** ppv); STDMETHOD_(ULONG, AddRef)(void); STDMETHOD_(ULONG, Release)(void); STDMETHOD(CreateInstance)( IUnknown* punkOuter, REFIID riid, void** ppv); STDMETHOD(LockServer)(BOOL fLock); private: ULONG m_refs; }; CLst41CF::CLst41CF() { m_refs = 1; } IClassFactory* CLst41CF::Create() { return new CLst41CF(); } STDMETHODIMP CLst41CF::QueryInterface(REFIID riid, void** ppv) { if(riid == IID_IUnknown || riid == IID_IClassFactory) { AddRef(); *ppv = this; return S_OK; } *ppv = NULL; return ResultFromScode(E_NOINTERFACE); } STDMETHODIMP_(ULONG) CLst41CF::AddRef() { return ++m_refs; } STDMETHODIMP_(ULONG) CLst41CF::Release() { if(--m_refs == 0) { delete this; return 0; } return m_refs; } STDMETHODIMP CLst41CF::CreateInstance(IUnknown* punkOuter,REFIID riid, void** ppv) { extern CLst41 * g_pLst41; return g_pLst41->QueryInterface(riid, ppv); } STDMETHODIMP CLst41CF::LockServer(BOOL fLock) { return S_OK; }
Listing 4.5. Main entry for Lst41.exe.
#include <objbase.h> #include "lst41c.h" #include <stdio.h> CLst41 FAR* g_pLst41 = NULL; void main() { MSG msg; DWORD g_dwLst41CF = 0; HRESULT hr; IClassFactory FAR* pcf; if((hr = OleInitialize(NULL)) != S_OK) { printf("OleInitialize Failed [0x%x]\n", hr); return; } if((g_pLst41 = CLst41::Create()) == NULL) return; pcf = CLst41CF::Create(); if (pcf == NULL) goto Clean; hr = CoRegisterClassObject(CLSID_CLst41, pcf, CLSCTX_LOCAL_SERVER, REGCLS_MULTIPLEUSE, &g_dwLst41CF); if (hr != NOERROR) goto Clean; pcf->Release(); while(GetMessage(&msg, NULL, NULL, NULL)) { TranslateMessage(&msg); DispatchMessage(&msg); } Clean: if(g_dwLst41CF != 0) CoRevokeClassObject(g_dwLst41CF); if (g_pLst41 != NULL) g_pLst41->Release(); OleUninitialize(); }
REGEDIT HKEY_CLASSES_ROOT\Lst41.Application.1 = Lst41 Automation Server HKEY_CLASSES_ROOT\Lst41.Application.1\Clsid = {9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B} HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B} = IDispatch Lst41 HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B}\ProgID = Lst41.Application.1 HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B}\ VersionIndependentProgID = Lst41.Application HKEY_CLASSES_ROOT\CLSID\{9FBBEDE5-1B40-11d0-88E0-00AA004A7C7B}\ LocalServer32 =c:\ch4\lst41\debug\lst41.exe /Automation ; registration info Lst41 TypeLib HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B} HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B}\ 1.0 = Lst41 Type Library HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B}\ 1.0\HELPDIR = ;Localized language is US english HKEY_CLASSES_ROOT\TypeLib\{9FBBEDE2-1B40-11d0-88E0-00AA004A7C7B}\ 1.0\409\win32 = c:\ch4\lst41\lst41.tlb HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B} = DLst41 HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B}\ ProxyStubClsid = {00020420-0000-0000-C000-000000000046} HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B}\ NumMethod = 7 HKEY_CLASSES_ROOT\Interface\{9FBBEDE4-1B40-11d0-88E0-00AA004A7C7B}\ BaseInterface = {00020400-0000-0000-C000-000000000046}
lst41use.exe is a C++ application that uses lst41.exe. It uses IDispatch to access exposed objects.
Listing 4.7 demonstrates how to use IDispatch to access the methods exposed by lst41.exe.
#include <objbase.h> #include <initguid.h> #include <stdio.h> DEFINE_GUID(CLSID_CLst41,0x9FBBEDE5L,0x1B40,0x11D0,0x88,0xE0, 0x00,0xAA,0x00,0x4A,0x7C,0x7B); LPSTR BstrToSz(LPCOLESTR pszW) { ULONG cbAnsi, cCharacters; DWORD dwError; LPSTR lpString; if(pszW == NULL) return NULL; cCharacters = wcslen(pszW) + 1; cbAnsi = cCharacters * 2; lpString = (LPSTR) CoTaskMemAlloc(cbAnsi); if(NULL == lpString) return NULL; if(WideCharToMultiByte(CP_ACP, 0, pszW, cCharacters, lpString, cbAnsi, NULL, NULL) == 0) { dwError = GetLastError(); CoTaskMemFree(lpString); lpString = NULL; } return lpString; }
Function BstrToSz converts a wide-character (Unicode) string to an ASCII string. CoTaskMemAlloc allocates a memory block using the default allocator. It behaves the same way as IMalloc::Alloc. The application should always check the return value from this function. Function WideCharToMultiByte maps a wide character string pointed by pszW to an ASCII string pointed by lpString.
void main() { HRESULT hr; IDispatch *pIDispatch; DISPPARAMS dispparms = {NULL, NULL, 0,0}; DISPID dispidGetMachineName; OLECHAR *pGetMachineName = L"GetMachineName"; IUnknown *pIUnknown; VARIANT varResult; hr = OleInitialize(NULL); hr = CoCreateInstance(CLSID_CLst41, 0, CLSCTX_SERVER, IID_IUnknown, (void**)&pIUnknown); if(FAILED(hr)) printf("the error is %x\n", hr); pIUnknown->QueryInterface(IID_IDispatch, (void**)&pIDispatch); pIUnknown->Release(); pIDispatch->GetIDsOfNames(IID_NULL, &pGetMachineName, 1, LOCALE_SYSTEM_DEFAULT, &dispidGetMachineName); pIDispatch->Invoke(dispidGetMachineName, IID_NULL, LOCALE_SYSTEM_DEFAULT, DISPATCH_METHOD, &dispparms, &varResult, NULL, NULL); printf("the striing is %s\n", BstrToSz(varResult.bstrVal)); pIDispatch->Release(); CoUninitialize(); }
Function CoCreateInstance creates an CLst41 object. Because lst41.exe is a local server, execution context CLSCTX_SERVER is used. CLSCTX_SERVER is defined as
#define CLSCTX_SERVER (CLSCTX_INPROC_SERVER| CLSCTX_LOCAL_SERVER| CLSCTX_REMOTE_SERVER)
GetIDsOfNames retrieves the DISPID and will be stored in dispidGetMachineName. There are five parameters in GetIDsOfNames. IID_NULL must be NULL; it is reserved for future use. pGetMachineName points to the method name. 1 indicates there is only one name to be mapped. LOCALE_SYSTEM_DEFAULT indicates the locale context in which to interpret the name.
Invoke accesses the GetMachineName method by providing its DISPID varResult is used to hold the return value from GetMachineName method.
With Visual Basic, using lst41.exe is straightforward. Listing 4.8 demonstrates how to use lst41.exe.
Listing 4.8. lst41.exe used in Visual Basic.
Dim x As Object Dim strMachineName As String Set x = CreateObject("lst41.application.1") strMachineName = x.getmachinename MsgBox strMachineName
In Listing 4.8, variable x is declared as an object and assigned the return of CreateObject call. The parameter in the CreateObject call is the ProgID of lst41.exe.
After the automation server (lst41.exe) is instantiated, the method GetMachineName can be invoked; the return value is assigned to strMachineName.
To try the example, place a CommandButton control on a form, and type Listing 4.8 into the command button's click proc. Run the example and click the Command1 button. A dialog box with the computer name on which the application is running will be displayed.
There are two MFC ClassWizard option provided by the New Project Workspace as shown in Figure 4.1.
Figure 4.1. New Project Workspace.
MFC AppWizard is designed to configure the skeleton of a new C++ application using the MFC.
MFC AppWizard (exe) is designed to create MFC extension .EXE, whereas MFC AppWizard (dll) is designed to create an MFC extension .DLL. The following steps illustrate how to create lst42.exe.
Figure 4.2. New Project Workspace dialog box.
The single-document option allows the application to work with one document at a time
The multiple-document option allows the application to work with multiple documents, each document with its own view.
Figure 4.3. MFC AppWizardStep 1.
Figure 4.4. MFC AppWizardmdStep 3 of 6.
The Mini-server option allows the application to create and manage the compound document object. Mini-server cannot run standalone, and only supports embedded objects, whereas Full-server can run standalone and supports both linked and embedded objects.
The OLE automation option allows the application to be accessed by the automation clients, such as Visual Basic, Access, Excel.
Figure 4.5. New Project Information.
// lst42.odl : type library source for lst42.exe // This file will be processed by the Make Type Library (mktyplib) tool to // produce the type library (lst42.tlb). [ uuid(06B70DA1-1818-11D0-A6AD-00AA00602553), version(1.0) ] library Lst42 { importlib("stdole32.tlb"); // Primary dispatch interface for CLst42Doc [ uuid(06B70DA2-1818-11D0-A6AD-00AA00602553) ] dispinterface ILst42 { properties: // NOTE - ClassWizard will maintain property information here. // Use extreme caution when editing this section. //{{AFX_ODL_PROP(CLst42Doc) //}}AFX_ODL_PROP methods: // NOTE - ClassWizard will maintain method information here. // Use extreme caution when editing this section. //{{AFX_ODL_METHOD(CLst42Doc) [id(1)] BSTR GetMachineName(); //}}AFX_ODL_METHOD }; // Class information for CLst42Doc [ uuid(06B70DA0-1818-11D0-A6AD-00AA00602553) ] coclass Document { [default] dispinterface ILst42; }; //{{AFX_APPEND_ODL}} };
[ uuid(06B70DA1-1818-11D0-A6AD-00AA00602553), version(1.0) ]
Figure 4.6. MFC ClassWizardOLE Automation.
Figure 4.7. Add Method dialog box.
External name is used by the automation clients to invoke the exposed method, whereas Internal name is the member function that implements the exposed method.
Listing 4.10. Method implementation.
BSTR CLst42Doc::GetMachineName() { CString strResult; ULONG ulLen; char *lpName; lpName = new char[MAX_PATH]; ulLen = MAX_PATH; GetComputerName(lpName, &ulLen ); strResult = lpName; delete [] lpName; return strResult.AllocSysString(); }
Listing 4.11. Dispatch map for lst42.exe.
BEGIN_DISPATCH_MAP(CLst42Doc, COleServerDoc) //{{AFX_DISPATCH_MAP(CLst42Doc) DISP_FUNCTION(CLst42Doc, "GetMachineName", GetMachineName, VT_BSTR, VTS_NONE) //}}AFX_DISPATCH_MAP END_DISPATCH_MAP()
class CLst42Doc : public COleServerDoc ... // Generated OLE dispatch map functions //{{AFX_DISPATCH(CLst42Doc) afx_msg BSTR GetMachineName(); //}}AFX_DISPATCH DECLARE_DISPATCH_MAP() DECLARE_INTERFACE_MAP() }
Automation server generated by the MFC AppWizard (exe) provides the self registration features. In other words, lst42.exe accepts the program argument /regserver to register itself to the system registry.
After lst42.exe is registered with the system, it can be used by automation clients, such as Visual Basic, Access, Excel. lst42.exe can be accessed by using IDispatch interface in the C++ application.
The following example demonstrates how lst42.exe be used in Visual Basic 4.0.
With Visual Basic, using lst42.exe is straightforward. Listing 4.12 demonstrates how to use lst42.exe inside Visual Basic.
Listing 4.12. lst42.exe used in Visual Basic.
Dim x As Object Dim strMachineName As String Set x = CreateObject("lst42.document") strMachineName = x.getmachinename MsgBox strMachineName
In Listing 4.12, variable x is declared as an object and assigned the return of the CreateObject call. The parameter in the CreateObject call is the ProgID of lst42.exe. For any application generated by the MFC AppWizard with OLE automation enabled, the ProgID is always Name for the new project workspace plus the .document.
After the automation server (lst42.exe) is instantiated, the method GetMachineName can be invoked and the return value is assigned to strMachineName.
To try the example, place a CommandButton control on a form and type Listing 4.13 into the command button's click proc. Run the example and click the Command1 button. A dialog box with the name of the computer the application is running on will be displayed.
Besides using MFC AppWizard (exe) to create the automation server, Control Wizard can be used. The IsInvokeAllowed() method is required to be overriden to support the automation.
The following steps demonstrates how to implement an automation server supporting the same functionality as lst41.exe.
Figure 4.8 New Project WorkspaceOLE Control Wizard
Figure 4.9. Files generated by OLE ControlWizard.
Among the files shown in Figure 4.9, the following files are specifically related to the Control.
lst43.odl: containing .ODL script.
lst43Ctl.h, lst43Ctl.cpp: all the methods, properties, and events will be placed in this class.
lst43Ppg.h, lst43Ppg.cpp: default property page class.
lst43Ctl.bmp: containing a bitmap displayed in the container's toolbox, such as Visual Basic's toolbox.
Figure 4.10. OLE Automation tab in MFC ClassWizard.
Figure 4.11. Add Method in OLE Automation tab.
BSTR CLst43Ctrl::GetMachineName() { CString strResult; ULONG ulLen; char *lpName; lpName = new char[MAX_PATH]; ulLen = MAX_PATH; GetComputerName(lpName, &ulLen ); strResult = lpName; delete [] lpName; return strResult.AllocSysString(); }
private: BOOL IsInvokeAllowed(DISPID dispid);
BOOL CLst43Ctrl::IsInvokeAllowed(DISPID dispid) { return TRUE; }
Listing 4.13. lst43.ocx used in Visual Basic.
Dim x As Object Dim strMachineName As String Set x = CreateObject("lst43.lst43ctrl.1") strMachineName = x.getmachinename MsgBox strMachineName
Listing 4.14. Dispatch map for lst43.ocx.
BEGIN_DISPATCH_MAP(CLst43Ctrl, COleControl) //{{AFX_DISPATCH_MAP(CLst43Ctrl) DISP_FUNCTION(CLst43Ctrl, "GetMachineName", GetMachineName, VT_BSTR, VTS_NONE) //}}AFX_DISPATCH_MAP DISP_FUNCTION_ID(CLst43Ctrl, "AboutBox", DISPID_ABOUTBOX, AboutBox, VT_EMPTY, VTS_NONE) END_DISPATCH_MAP()
ATL provides an OLE COM AppWizard to create COM objects. It supports COM objects with a custom interface, IDispatch, and IConnectionPoint and so on. ATL is designed to create COM objects. For more information on ATL, please refer to Appendix E, "ActiveX Template Library."
The following example illustrates how to use ATL to create an automation server supporting the same functionality as lst41.exe.
Figure 4.12. OLE COM AppWizard.
Figure 4.13. OLE COM AppWizardStep 1 of 2.
The Dual Interface option indicates that the interface supports IDispatch and IUnknown.
interface ILst44 : IDispatch { import "oaidl.idl"; HRESULT GetMachineName([out,retval] BSTR *retval); }
interface ILst44 : IDispatch { HRESULT GetMachineName([out,retval] BSTR *retval); };
public: STDMETHOD(GetMachineName)(BSTR *retval);
Listing 4.15. Addition to lst44obj.cpp.
BSTR CreateBSTR(LPCSTR lpa) { BSTR bsz; UINT cch; cch = strlen(lpa); bsz = SysAllocStringLen(NULL, cch); if (bsz == NULL) return NULL; if (cch > 0) MultiByteToWideChar(CP_ACP, 0, lpa, cch, bsz, cch); bsz[cch] = NULL; return bsz; } STDMETHODIMP CLst44Object::GetMachineName(BSTR* retval) { ULONG ulLen; char *lpName; lpName = new char[MAX_PATH]; ulLen = MAX_PATH; GetComputerName(lpName, &ulLen); if(ulLen == 0) *retval = NULL; else *retval = CreateBSTR(lpName); return S_OK; }
lst44.dll can be used in Visual Basic the same way as lst42.exe. Instead of input Listing 4.11, code in Listing 4.16 should be entered.
Listing 4.16. Lst44.dll used in Visual Basic.
Dim x As Object Dim strMachineName As String Set x = CreateObject("lst44.lst44object.1") strMachineName = x.getmachinename MsgBox strMachineName
Before running the preceding code, lst44.dll needs to be registered by running regsvr32 lst44.dll.
In Listing 4.16, ProgID for lst44.dll is lst44.lst44object.1. The default ProgID generated by OLE COM AppWizard is the name of the project workspace plus .lst44object.1.
The default ProgID can be modified by replacing the code in bold font as shown in the following; this code is contained in lst44.cpp.
BEGIN_OBJECT_MAP(ObjectMap) OBJECT_ENTRY(CLSID_Lst44, CLst44Object, "LST44.Lst44Object.1", "LST44.Lst44Object.1", IDS_LST44_DESC, THREADFLAGS_BOTH) END_OBJECT_MAP()
Reusable components such as automation server not only can be used in the automation clients such as Visual Basic, Access, Excel, or in the application that accesses the automation server via IDispatch. Microsoft Internet Explorer (IE) 3.0 also supports the use of automation server.
The automation server inside IE 3.0 requires an <OBJECT> tag to be used to include the object.
Listing 4.17 demonstrates how to use lst44.dll inside an HTML page and displayed in IE 3.0 browser.
Listing 4.17. lst44.dll used in IE 3.0.
<HTML> <HEAD> <OBJECT classid="clsid:C566CC25-182E-11D0-A6AD-00AA00602553" id= MachineName </OBJECT> <SCRIPT language="VBScript"> msgbox MachineName.getmachinename </SCRIPT> </HEAD> </HTML>
In Listing 4.17, "clsid:..." is the string representation of the CLSID, denoted as {CLSID}, for lst44.dll. The following steps illustrates how to get the {CLSID} for lst44.dll.
Figure 4.14. ProgID and {CLSID} registry key for lst44.dll.
Figure 4.15. String Editor for data.
<% Request.[ServerVariables|QueryString|Body]("variable name") %>
ServerVariable, QueryString, Body.
Listing 4.18. Use lst44.dll on Web server.
getmachinename.asp file
<% x = server.createobject("lst44.lst44object.1") %> <% = x.getmachinename %> machinename.html file <HTML> <BODY> <A HREF="/scripts/machinename.asp"> Get the Server Machine Name</A> </BODY> </HTML>
An OLE automation server is a COM server with the support of the IDispatch interface. Applications can use IDispatch to access exposed objects in automation server. A lot of tools can be used to develop the automation servers by using MFC AppWizard, Control Wizard, and Active Template Library. The automation server is a reusable component, which can be used in the automation controller, IE 3.0, and Web server.