COM初探(四)——本地EXE COM服务器
(一)目标
讲述如何建立本地(Local,EXE)COM服务器的例子似乎不多,为此,本文专门讲述如何用vc手工建立本地EXE COM服务器程序。本文假设读者对COM有一定的基础。
(二)准备
1)使用vc6建立Win32 Application工程,选择a simple application。
2)从stdafx.h中删除:#define WIN32_LEAN_AND_MEAN
(三)建立IDL文件
此文件中定义了接口ITimeBeijing和COM类TimeBeijingClass
import "oaidl.idl";
[
object,
uuid(61A1C81D-82CA-44e4-A60E-7BDD8E58A833),
oleautomation
]
interface ITimeBeijing : IUnknown//定义ITimeBeijing接口
{
HRESULT GetTimeBeijing([out]int * hour, [out]int * min, [out]int * sec);
};
[
uuid(1E850106-D5C5-4249-86DF-40256DB3A5D2),
version(1.0)
]
library TimeBeijingLib
{
importlib("stdole32.tlb");
[
uuid(57E9BE40-AE4F-493b-A79B-FBBF7EC7F2AE)
]
coclass TimeBeijingClass//定义TimeBeijingClass类
{
[default] interface ITimeBeijing;
};
};
(四)添加COM类TimeBeijingClass
TimeBeijingClass在由上面idl文件编译生成的文件中已经前置声明了。这里我们实现了它的功能(获取当前北京时间)
// TimeBeijingClass.h: interface for the TimeBeijingClass class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_TIMEBEIJINGCLASS_H__E9D2F0AF_8EBB_438B_A2BC_6206DDAB91B6__INCLUDED_)
#define AFX_TIMEBEIJINGCLASS_H__E9D2F0AF_8EBB_438B_A2BC_6206DDAB91B6__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include "TimeBeijing.h"
class TimeBeijingClass : public ITimeBeijing
{
public:
TimeBeijingClass();
~TimeBeijingClass();
private:
ULONG m_cRef;
public:
//IUnknown 方法
STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
STDMETHOD_(ULONG, AddRef)();
STDMETHOD_(ULONG, Release)();
//ITimeBeijing 方法
STDMETHOD(GetTimeBeijing)(int * hour, int* min, int * sec);
};
#endif // !defined(AFX_TIMEBEIJINGCLASS_H__E9D2F0AF_8EBB_438B_A2BC_6206DDAB91B6__INCLUDED_)
// TimeBeijingClass.cpp: implementation of the TimeBeijingClass class.
//
//////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "TimeBeijingClass.h"
#include "time.h"
#include "stdio.h"
#include "TimeBeijing_i.c"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
TimeBeijingClass::TimeBeijingClass()
{
printf("TimeBeijingClass - constructor\n");
m_cRef = 0;
}
TimeBeijingClass::~TimeBeijingClass()
{
printf("TimeBeijingClass - destructor\n");
}
STDMETHODIMP TimeBeijingClass::QueryInterface(REFIID riid, void **ppv)
{
printf("TimeBeijingClass - QueryInterface\n");
if(riid == IID_ITimeBeijing)
*ppv = static_cast<ITimeBeijing *>(this);
else if(riid == IID_IUnknown)
*ppv = static_cast<ITimeBeijing *>(this);
else {
*ppv = 0;
return E_NOINTERFACE;
}
reinterpret_cast<IUnknown *>(*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) TimeBeijingClass::AddRef()
{
printf("TimeBeijingClass - AddRef\n");
return ++m_cRef;
}
STDMETHODIMP_(ULONG) TimeBeijingClass::Release()
{
printf("TimeBeijingClass - Release\n");
ULONG res = --m_cRef;
if(res == 0)
delete this;
return res;
}
STDMETHODIMP TimeBeijingClass::GetTimeBeijing(int * hour, int* min, int * sec)
{
printf("TimeBeijingClass - GetTimeBeijing\n");
time_t ltime;
struct tm *today;
time(<ime);
today = localtime(<ime);
*hour = today->tm_hour;
*min = today->tm_min;
*sec = today->tm_sec;
return S_OK;
}
(五)添加工厂类TimeBeijingClassFactory
类工厂可以生成类的实例。在标准的COM实现中,我们都是通过使用类工厂来得到目的接口指针的。
类工厂从IClassFactory接口派生,有两个接口函数:CreateInstance和LockServer。
CreateInstance用于建立目标COM类的实例并返回相应接口。
LockServer用于全局的引用计数。当计数为0时,服务器自动关闭。
// TimeBeijingClassFactory.h: interface for the TimeBeijingClassFactory class.
//
//////////////////////////////////////////////////////////////////////
#if !defined(AFX_TIMEBEIJINGCLASSFACTORY_H__AB2637DD_45D2_48CD_BAD8_46E372268A8C__INCLUDED_)
#define AFX_TIMEBEIJINGCLASSFACTORY_H__AB2637DD_45D2_48CD_BAD8_46E372268A8C__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
#include <unknwn.h>
class TimeBeijingClassFactory : public IClassFactory
{
public:
TimeBeijingClassFactory();
~TimeBeijingClassFactory();
public:
//IUnknow 方法
STDMETHODIMP QueryInterface(REFIID,void**);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
//IClassFactory 方法
STDMETHODIMP CreateInstance(IUnknown * ,REFIID ,void **);
STDMETHODIMP LockServer(BOOL fLock);
private:
LONG m_cRef;
};
#endif // !defined(AFX_TIMEBEIJINGCLASSFACTORY_H__AB2637DD_45D2_48CD_BAD8_46E372268A8C__INCLUDED_)
// TimeBeijingClassFactory.cpp: implementation of the TimeBeijingClassFactory class.
//
//////////////////////////////////////////////////////////////////////
#include "StdAfx.h"
#include "TimeBeijingClassFactory.h"
#include "stdio.h"
#include "TimeBeijingClass.h"
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
extern LONG g_cObjectAndLocks;
TimeBeijingClassFactory::TimeBeijingClassFactory()
{
printf("TimeBeijingClassFactory - constructor\n");
m_cRef = 0;
}
TimeBeijingClassFactory::~TimeBeijingClassFactory()
{
printf("TimeBeijingClassFactory - destructor\n");
}
STDMETHODIMP_(ULONG) TimeBeijingClassFactory::AddRef(void)
{
printf("TimeBeijingClassFactory - AddRef\n");
return InterlockedIncrement(&m_cRef);
}
STDMETHODIMP_(ULONG) TimeBeijingClassFactory::Release(void)
{
printf("TimeBeijingClassFactory - Release\n");
return ::InterlockedDecrement(&m_cRef);
}
STDMETHODIMP TimeBeijingClassFactory::QueryInterface(REFIID riid,void ** ppv)
{
printf("TimeBeijingClassFactory - QueryInterface\n");
*ppv = NULL;
if(riid == IID_IUnknown || riid == IID_IClassFactory)
{
*ppv = static_cast<IClassFactory *>(this);
reinterpret_cast<IUnknown*>(*ppv)->AddRef();
return S_OK;
}
else
return (*ppv=0),E_NOINTERFACE;
}
STDMETHODIMP TimeBeijingClassFactory::CreateInstance(IUnknown * pUnkOuter,REFIID riid,void ** ppv)
{
printf("TimeBeijingClassFactory - CreateInstance\n");
*ppv=NULL;
if(pUnkOuter != NULL)
return CLASS_E_NOAGGREGATION;
TimeBeijingClass * pTimeBeijingClass = new TimeBeijingClass;
if(pTimeBeijingClass == NULL)
return E_OUTOFMEMORY;
HRESULT hr = pTimeBeijingClass->QueryInterface(riid,ppv);
if(FAILED(hr))
delete pTimeBeijingClass;
return hr;
}
STDMETHODIMP TimeBeijingClassFactory::LockServer(BOOL fLock)
{
printf("TimeBeijingClassFactory - LockServer\n");
if(fLock)
::InterlockedIncrement(&g_cObjectAndLocks);
else
{
if(0 == ::InterlockedDecrement(&g_cObjectAndLocks))
PostQuitMessage(0);
}
return NOERROR;
}
(六)服务器端主文件
客户端主文件负责注册工厂类,使得外部程序可以访问本地的COM类。
#include "stdafx.h"
#include "TimeBeijing_i.c"
#include "TimeBeijingClassFactory.h"
LONG g_cObjectAndLocks;//用于全局的应用计数
int APIENTRY WinMain(HINSTANCE hInstance,//程序的入口点
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
HRESULT hr;
CoInitialize(NULL); //初始化COM环境
//读入并注册类型库
ITypeLib* pTLib = NULL;
hr = LoadTypeLibEx(L"TimeBeijing.tlb", REGKIND_REGISTER, &pTLib);
pTLib->Release();
if ( FAILED(hr) )
{
MessageBox(NULL, "LoadTypeLibEx() failed", "", MB_OK);
exit(1);
}
// 判断是否被SCM调用
if(strstr(lpCmdLine, "/Embedding") || strstr(lpCmdLine, "-Embedding"))
{
MessageBox(NULL, "TimeBeijing Server is registering the classes",
"EXE Message!", MB_OK | MB_SETFOREGROUND);
// 工厂类
TimeBeijingClassFactory ClassFactory;
// 注册工厂类
DWORD regID = 0;
hr = CoRegisterClassObject(CLSID_TimeBeijingClass,
(IClassFactory*)&ClassFactory,
CLSCTX_LOCAL_SERVER,
REGCLS_MULTIPLEUSE,
®ID);
if ( FAILED(hr) )
{
MessageBox(NULL, "CoRegisterClassObject()", "", MB_OK);
CoUninitialize();
exit(1);
}
// 进入消息循环,直到WM_QUIT到达
MSG ms;
while(GetMessage(&ms, 0, 0, 0))
{
TranslateMessage(&ms);
DispatchMessage(&ms);
}
// 注销工厂类
CoRevokeClassObject(regID);
}
// 退出COM环境
CoUninitialize();
MessageBox(NULL, "Server is dying",
"EXE Message!", MB_OK | MB_SETFOREGROUND);
return 0;
}
(七)建立注册文件
注册文件用于添加程序注册表信息,文件内容如下:
REGEDIT
HKEY_CLASSES_ROOT\Justin.TimeBeijingServer.TimeBeijingClass\CLSID = {57E9BE40-AE4F-493b-A79B-FBBF7EC7F2AE}
HKEY_CLASSES_ROOT\CLSID\{57E9BE40-AE4F-493b-A79B-FBBF7EC7F2AE} = Justin.TimeBeijingServer.TimeBeijingClass
HKEY_CLASSES_ROOT\CLSID\{57E9BE40-AE4F-493b-A79B-FBBF7EC7F2AE}\LocalServer32 = D:\COM\Debug\COM.exe
双击此文件,将注册信息加入注册表。
(八)建立客户端
上面的程序编译链接后就可以得到一个本地EXE COM服务器。下面为了测试,我们将建立一个客户程序。
// TEST.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "TEST.h"
#include "../TimeBeijing.h"
#include "../TimeBeijing_i.c"
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
// The one and only application object
CWinApp theApp;
using namespace std;
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
cout << "初始化COM环境" << endl;
CoInitialize(NULL);
HRESULT hr;
IClassFactory* pICF = NULL;//定义接口指针
ITimeBeijing *pTimeBeijing = NULL;
cout << "获取工厂接口指针" << endl;
hr = CoGetClassObject(CLSID_TimeBeijingClass,
CLSCTX_LOCAL_SERVER,
NULL,
IID_IClassFactory,
(void**)&pICF);
if ( FAILED(hr) )
{
MessageBox(NULL, "CoGetClassObject()", "fail", MB_OK);
exit(1);
}
cout << "获取ITimeBeijing接口指针" << endl;
hr = pICF->CreateInstance(NULL,
IID_ITimeBeijing,
(void**)&pTimeBeijing);
if ( FAILED(hr) )
{
MessageBox(NULL, "CoCreateInstance()", "fail", MB_OK);
exit(1);
}
int h, m, s;
pTimeBeijing->GetTimeBeijing(&h, &m, &s);//使用接口
printf("Time NOW : %d : %d : %d\n", h, m, s);//显示结果
if ( pICF ) pICF->Release();//释放接口
if ( pTimeBeijing ) pTimeBeijing->Release();
cout << "退出COM环境" << endl;
CoUninitialize();
cout << "END" << endl;
getchar();
return 0;
}
(九)测试结果
初始化COM环境
获取工厂接口指针
获取ITimeBeijing接口指针
Time NOW : 12 : 54 : 23
退出COM环境
END