现在的位置: 首页 > 综合 > 正文

COM学习笔记(四)

2012年11月29日 ⁄ 综合 ⁄ 共 13584字 ⁄ 字号 评论关闭

        在潘老师的《COM原理与应用》这本书里有一个字典组件例子程序很好的模拟实现了COM对象和接口的原理。下面就对这一程序做详细介绍和记录。
        整个工程由一个DictComp组件程序(以dll方式提供服务)和一个客户程序DictCtrl(Win32 Console形式)组成。先来看组件程序DictComp。

        这个组件程序主要实现两个接口,一个主要是提供单词的查询,插入和删除功能,另外一个是用来实现单词拼写的检查,分别定义在IDictionary.h和ISpellCheck.h两个头文件中。由前几篇可知,组件的所有接口都是由IUnknown这个接口继承而来的。所以首先来看这个接口的定义。代码如下:

//---------------------------------------------------------------------
//IMyUnknown.h
//---------------------------------------------------------------------
#ifndef __IMyUnknown_H__
#define __IMyUnknown_H__

typedef unsigned long DWORD;
typedef unsigned short WORD;
typedef unsigned char BYTE;
typedef int BOOL;

typedef struct _GUID GUID;

typedef GUID IID;

//IUnknown接口IID的定义
extern "C" const IID IID_IUnknown =
{ 0x00000000, 0x0000, 0x0000,
{ 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46} } ;

//IUnknown接口本身的定义
class IMyUnknown
{
public:
virtual HRESULT __stdcall QueryInterface(const IID& iid, void **ppv) = 0 ;
virtual ULONG __stdcall AddRef() = 0;
virtual ULONG __stdcall Release() = 0;
};

#endif // __IMyUnknown_H__

     上述代码的关键之处就是IUnknown接口IID的定义和IUnknown接口本身的定义。这两处的定义在前文中都介绍过,这里就是照搬过来的。接下来就是IDictionary接口和ISpellCheck接口的定义。

//---------------------------------------------------------------------
//IDictionary.h
//---------------------------------------------------------------------
#ifndef __IDictionary_H__
#define __IDictionary_H__

#ifndef __IMyUnknown_H__
#include "IMyUnknown.h"
#endif

typedef unsigned short *String;

// IDictionary接口的IID:{54BF6568-1007-11D1-B0AA-444553540000}
extern "C" const GUID IID_Dictionary =
{ 0x54bf6568, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;

//IDictionary接口的定义
class IDictionary : public IMyUnknown
{
public :
virtual BOOL __stdcall Initialize() = 0;
virtual BOOL __stdcall LoadLibrary(String) = 0;
virtual BOOL __stdcall InsertWord(String, String) = 0;
virtual void __stdcall DeleteWord(String) = 0;
virtual BOOL __stdcall LookupWord(String, String *) = 0;
virtual BOOL __stdcall RestoreLibrary(String) = 0;
virtual void __stdcall FreeLibrary() = 0;
};

#endif // __IDictionary_H__



//---------------------------------------------------------------------
//ISpellCheck.h
//---------------------------------------------------------------------
#ifndef __ICheckSpell_H__
#define __ICheckSpell_H__

#ifndef __IMyUnknown_H__
#include "IMyUnknown.h"
#endif

#ifndef String
typedef unsigned short *String;
#endif

//ISpellCheck接口的IID:{54BF6569-1007-11D1-B0AA-444553540000}
extern "C" const GUID IID_SpellCheck =
{ 0x54bf6569, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;

//ISpellCheck接口的定义
class ISpellCheck : public IMyUnknown
{
public :
virtual BOOL __stdcall CheckWord(String, String*) = 0;
};

#endif // __ISpellCheck_H__

从以上代码可以看出接口的定义实际上就是定义虚表。IDictionary接口的功能是:

  • 初始化字典库:Initialize( )
  • 加载字典库:LoadLibrary(String)
  • 插入单词:InsertWord(String, String)
  • 删除单词:DeleteWord(String)
  • 查询单词:LookupWord(String, String *)
  • 重新保存字典库:RestoreLibrary(String)
  • 释放字典库:FreeLibrary( )

而ISpellCheck接口的功能就是检查单词的拼写:CheckWord(String, String*) 。接下来就需要定义一个组件类来保存对象的属性数据,代码如下:

//---------------------------------------------------------------------
//DictComp.h
//---------------------------------------------------------------------
#ifndef __DictComp_H__
#define __DictComp_H__

#ifndef __IDictionary_H__
#include "IDictionary.h"
#endif

#ifndef __ISpellCheck_H__
#include "ISpellCheck.h"
#endif

#define MaxWordLength 32
struct DictWord
{
char wordForLang1[MaxWordLength];
char wordForLang2[MaxWordLength];
};

class CDictionary : public IDictionary , public ISpellCheck
{
public :
CDictionary();
~CDictionary();
public :
// IUnknown member function
virtual HRESULT __stdcall QueryInterface(const IID& iid, void **ppv) ;
virtual ULONG __stdcall AddRef() ;
virtual ULONG __stdcall Release() ;

// IDictionary member function
virtual BOOL __stdcall Initialize();
virtual BOOL __stdcall LoadLibrary(String);
virtual BOOL __stdcall InsertWord(String, String);
virtual void __stdcall DeleteWord(String);
virtual BOOL __stdcall LookupWord(String, String *);
virtual BOOL __stdcall RestoreLibrary(String);
virtual void __stdcall FreeLibrary();

// ISpellCheck member function
virtual BOOL __stdcall CheckWord (String word, String *);

private :
struct DictWord *m_Data;
char *m_DictFilename[128];
int m_Ref ;
int m_nWordNumber, m_nStructNumber;
};

#endif // __DictComp_H__

        上述代码中定义了字典单词的数据类型DictWord,其中wordForLang1字符数组表示单词wordForLang2字符数组表示对应的中文解释。m_Ref为组件对象的引用数,m_nWordNumber为字典库中的单词数,m_nStructNumber为扩展后字典库中的单词数。

        下面来看这些函数的具体定义,代码在DictComp.cpp中。

//---------------------------------------------------------------------
//DictComp.cpp
//---------------------------------------------------------------------
#include "stdafx.h"
#include "DictComp.h"
#include <comutil.h>
#include <stdio.h>

#pragma comment(lib, "comsuppw.lib")

//dll的入口函数
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
return TRUE;
}

//字典组件的CLSID:{54BF6567-1007-11D1-B0AA-444553540000}
extern "C" const GUID CLSID_Dictionary =
{ 0x54bf6567, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;

//创建字典组件的对象

BOOL __stdcall CreateObject(const CLSID& clsid, const IID& iid, void **ppv)
{
if (clsid == CLSID_Dictionary ) {
CDictionary *pObject = new CDictionary;
HRESULT result = pObject->QueryInterface(iid, ppv);
return (result == S_OK) ? TRUE : FALSE;
}
return FALSE;
}

// class CDictionary implementation

CDictionary::CDictionary()
{
m_Ref = 0;
m_nWordNumber = 0;
m_nStructNumber = 0;
m_Data = NULL;
}

CDictionary::~CDictionary()
{
if (m_Data != NULL)
{
delete [] m_Data;
}
}

HRESULT CDictionary::QueryInterface(const IID& iid, void **ppv)
{
if ( iid == IID_IUnknown )
{
*ppv = (IDictionary *) this ;
((IDictionary *)(*ppv))->AddRef() ;
} else if ( iid == IID_Dictionary )
{
*ppv = (IDictionary *) this ;
((IDictionary *)(*ppv))->AddRef() ;
} else if ( iid == IID_SpellCheck )
{
*ppv = (ISpellCheck *) this ;
((ISpellCheck *)(*ppv))->AddRef() ;
}
else
{
*ppv = NULL;
return E_NOINTERFACE ;
}
return S_OK;
}

ULONG CDictionary::AddRef()
{
m_Ref ++;
return (ULONG) m_Ref;
}

ULONG CDictionary::Release()
{
m_Ref --;
if (m_Ref == 0 ) {
delete this;
return 0;
}
return (ULONG) m_Ref;
}

//字典库数据初始化
BOOL CDictionary::Initialize()
{
m_nWordNumber = 0;
m_nStructNumber = 0;
if (m_Data != NULL)
{
delete [] m_Data;
}
m_Data = NULL;
return TRUE;
}

//加载字典库文件
BOOL CDictionary::LoadLibrary(String filename)
{
char *pFileName = _com_util::ConvertBSTRToStrin(filename);
FILE *fp;
if( (fp = fopen( pFileName, "rt" )) == NULL ) {
printf("Open dictionary file : %s failed.\n", pFileName);
delete pFileName;
return FALSE;
}
char LineBuffer[128];
if (feof(fp)) {
printf("It is a null file!\n");
fclose(fp);
delete pFileName;
return FALSE;
}
//读入字典库文件的第一行数据到LineBuffer中
if (fgets(LineBuffer, 128, fp) == NULL) {
printf("Read TotalNumber failed!\n");
fclose(fp);
delete pFileName;
return FALSE;
}

int nTotalNumber = 0;
//nTotalNumber中保存字典中单词的数目
sscanf(LineBuffer, "%d", &nTotalNumber);
if ( (nTotalNumber < 1) && (nTotalNumber > 5000) ) {
printf("The Number of words is invalid!\n");
fclose(fp);
delete pFileName;
return FALSE;
}

Initialize();
//字典最多扩充100个单词
m_nStructNumber = nTotalNumber+100;
m_Data = new DictWord[m_nStructNumber];
m_nWordNumber = 0;
//将字典库加载到内存
while(!feof(fp)) {
if (fgets(LineBuffer, MaxWordLength, fp) == NULL) {
printf("Read the first string failed!\n");
break;
}
sscanf(LineBuffer, "%s", m_Data[m_nWordNumber].wordForLang1);
if (fgets(LineBuffer, MaxWordLength, fp) == NULL) {
printf("Read the second string failed!\n");
break;
}
sscanf(LineBuffer, "%s", m_Data[m_nWordNumber].wordForLang2);
m_nWordNumber ++;
if (m_nWordNumber == nTotalNumber)
break;
if (m_nWordNumber &amp;gt; m_nStructNumber)
break;
}

fclose(fp);
delete pFileName;
return TRUE;
}

//插入单词到字典库中
BOOL CDictionary::InsertWord(String word1, String word2)
{
char *pWord1, *pWord2;
if (m_nWordNumber < m_nStructNumber) {
pWord1 = _com_util::ConvertBSTRToString(word1);
pWord2 = _com_util::ConvertBSTRToString(word2);

if (strlen(pWord1) > MaxWordLength)
*(pWord1+MaxWordLength-1) = '\0';
if (strlen(pWord2) > MaxWordLength)
*(pWord2+MaxWordLength-1) = '\0';
strcpy(m_Data[m_nWordNumber].wordForLang1, pWord1);
strcpy(m_Data[m_nWordNumber].wordForLang2, pWord2);
m_nWordNumber ++ ;
delete pWord1;
delete pWord2;
return TRUE;
}
return FALSE;
}

//删除字典库中的某个单词
void CDictionary::DeleteWord(String word)
{
char *pWord = _com_util::ConvertBSTRToString(word);
char *pUpperWord = strupr(pWord);
for (int i = 0; i < m_nWordNumber; i++)
{
char *tmpWord = strupr(m_Data[i].wordForLang1);
if (strcmp(tmpWord, pWord) == 0) {
for(int j = i + 1; j < m_nWordNumber; j++) {
strcpy( m_Data[j].wordForLang1, m_Data[j + 1].wordForLang1);
strcpy( m_Data[j].wordForLang2, m_Data[j + 1].wordForLang2);
}
m_nWordNumber ++ ;
break;
}
}
delete pWord;
}

//查找一个单词,返回对应的解释到resultWord中
BOOL CDictionary::LookupWord(String word, String *resultWord)
{
char *pWord = _com_util::ConvertBSTRToString(word);
char *pUpperWord = strupr(pWord);
for (int i = 0; i < m_nWordNumber; i++)
{
char *tmpWord = strupr(m_Data[i].wordForLang1);
if (strcmp(tmpWord, pWord) == 0) {
*resultWord = (String)_com_util::ConvertStringToBSTR(m_Data[i].wordForLang2);
delete pWord;
return TRUE;
}
}
*resultWord = NULL;
delete pWord;
return FALSE;
}

//重新保存字典库文件
BOOL CDictionary::RestoreLibrary(String filename)
{
char *pFileName = _com_util::ConvertBSTRToStrin(filename);
FILE *fp;
if( (fp = fopen( pFileName, "wt" )) == NULL ) {
printf("Open dictionary file : %s failed.\n", pFileName);
delete pFileName;
return FALSE;
}
char LineBuffer[128];
sprintf(LineBuffer, "%d\n", m_nWordNumber);
if (fputs(LineBuffer, fp) == EOF) {
printf("Write TotalNumber failed!\n");
fclose(fp);
delete pFileName;
return FALSE;
}

for(int i = 0; i < m_nWordNumber; i ++ ) {
if (fputs(m_Data[i].wordForLang1, fp) == EOF) {
printf("Write the first string failed!\n");
fclose(fp);
delete pFileName;
return FALSE;
}
fputs("\n", fp);
if (fputs(m_Data[i].wordForLang2, fp) == EOF) {
printf("Write the second string failed!\n");
fclose(fp);
delete pFileName;
return FALSE;
}
fputs("\n", fp);
}

fclose(fp);
delete pFileName;
return TRUE;
}

//将字典库文件从内存中清除
void CDictionary::FreeLibrary()
{
Initialize();
}

//检查某个单词的拼写是否正确,若不正确返回最相近的单词
BOOL CDictionary::CheckWord (String word, String *resultWord)
{
char *pWord = _com_util::ConvertBSTRToString(word);
char *pUpperWord = strupr(pWord);
char *pMinMaxWord, *pMaxMinWord;
int nMinIndex = -1, nMaxIndex = -1;
pMinMaxWord = pMaxMinWord = NULL;
for (int i = 0; i < m_nWordNumber; i++)
{
char *tmpWord = strupr(m_Data[i].wordForLang1);
if (strcmp(tmpWord, pWord) == 0) {
delete pWord;
return TRUE;
} else if (strcmp(tmpWord, pWord) < 0) {
if ((pMinMaxWord == NULL) || (strcmp(tmpWord, pMinMaxWord) > 0))
{
pMinMaxWord = tmpWord;
nMinIndex = i;
}
} else {
if ((pMaxMinWord == NULL) || (strcmp(tmpWord, pMaxMinWord) < 0))
{
pMaxMinWord = tmpWord;
nMaxIndex = i;
}
}
}

*resultWord = NULL;
if (nMinIndex != -1)
*resultWord = (String)_com_util::ConvertStringToBSTR(m_Data[nMinIndex].wordForLang1);
else if (nMaxIndex != -1)
*resultWord = (String)_com_util::ConvertStringToBSTR(m_Data[nMaxIndex].wordForLang1);
delete pWord;
return FALSE;
}

         COM对象的创建是个相对复杂的过程。在这个例子程序中采取了一个简单的模拟方法:在组件程序中引出一个CreateObject函数,在客户程序中调用该导出函数以便完成创建过程。而一旦创建对象的工作完成,则客户程序就严格按照COM规范调用接口功能或者控制对象的生存期。而CreateObject函数实际上非常简单,只要
传入的类标识符是字典组件标识符,则它就用new操作符生成一个字典对象,并利用该对象完成初始接口的获取工作。 在函数LoadLibrary中,首先调用ConvertBSTRToString把文件名转换成ANSI字符串,然后打开文件,读入第一行的数字,该数字指定了字典文件中单词的总数;然后根据总数分配内存空间。考虑之后加入单词的需要,实际分配的空间比现有的单词数多100个。在依次读入每一个单词的两个字符串(单词和解释),最后关闭文件完成字典库的装载工作。 在函数LookupWord中,通过一个for循环,依次查询每一个单词,如果找到了,则把该单词的解释放到输出参数resultWord中,并返回TRUE;如果没有找到单词,则在输出参数中赋值NULL,并返回FALSE。 在函数CheckWord中,目标是看是否有拼写相同的单词,若有返回TRUE,若没有则找到最相近的单词,并放到输出参数resultWord中。 另外在写个def文件导出CreateObject函数:

; DictComp.def : Declares the module parameters for the DLL.

LIBRARY "DictComp"
DESCRIPTION 'Dictionary Component Windows Dynamic Link Library'

EXPORTS
; Explicit exports can go here
CreateObject @1

         接下来就是客户程序。

//---------------------------------------------------------------------
//DictCtrl.cpp
//---------------------------------------------------------------------
#include "stdafx.h"
#include "windows.h"
#include <stdio.h>
#include <comutil.h>

#pragma comment(lib, "comsuppw.lib")

#include "IMyUnknown.h"
#include "IDictionary.h"
#include "ISpellCheck.h"

//字典组件对象的CLSID: {54BF6567-1007-11D1-B0AA-444553540000}
extern "C" const GUID CLSID_Dictionary =
{ 0x54bf6567, 0x1007, 0x11d1,
{ 0xb0, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} } ;
HMODULE hModual;

BOOL CreateObject(const CLSID& clsid, const IID& iid, void **ppv)
{

hModual = LoadLibrary("DictComp.dll"); //加载组件
if (hModual == NULL) return 0;

typedef BOOL (__stdcall* MyFunctionType)(const CLSID& clsid, const IID& iid, void **ppv);
MyFunctionType fnCreateObject;

fnCreateObject = (MyFunctionType)GetProcAddress(hModual, "CreateObject");
if (fnCreateObject == NULL) {
FreeLibrary(hModual);
return FALSE;
}

return fnCreateObject(clsid, iid, ppv); //调用导出函数CreateObject创建组件对象
}

int main(int argc, char* argv[])
{
hModual = NULL;
IMyUnknown *pMyUnknown;
IDictionary *pDictionary;
ISpellCheck *pSpellCheck;
String stringResult;
BOOL bResult;
HRESULT hResult;


//创建组件对象
bResult = CreateObject(CLSID_Dictionary, IID_IUnknown, (void **)&pMyUnknown);
if (bResult == FALSE)
{
printf("Create object failed!\n");
return -1;
}
//查询IDictionary接口
hResult = pMyUnknown->QueryInterface(IID_Dictionary, (void **)&pDictionary);
if (hResult != S_OK) {
pMyUnknown->Release();
printf("QueryInterface IDictionary failed!\n");
return -2;
}

//加载字典库
bResult = pDictionary->LoadLibrary(L"animal.dict");
if (bResult) {

bResult = pDictionary->LookupWord(L"tiger", &stringResult);

if (bResult) {
char *pTiger = _com_util::ConvertBSTRToString(stringResult);
printf("find the word \"tiger\" -- %s\n", pTiger);
delete pTiger;
}

pDictionary->InsertWord(L"elephant", L"");
bResult = pDictionary->LookupWord(L"elephant", &stringResult);
if (bResult) {
//保存到新的字典库文件
pDictionary->RestoreLibrary(L"animal1.dict");
}
} else {
printf("Load Library \"animal.dict\"\n");
return -2;
}

//查询ISpellCheck接口
hResult = pDictionary->QueryInterface(IID_SpellCheck, (void **)&pSpellCheck);
pDictionary->Release();
if (hResult != S_OK) {
pMyUnknown->Release();
printf("QueryInterface IDictionary failed!\n");
return -2;
}

bResult = pSpellCheck->CheckWord(L"lion", &stringResult);
if (bResult) {
printf("Word \"lion\" spelling right.\n");
} else {
char *pLion = _com_util::ConvertBSTRToString(stringResult);
printf("Word \"lion\" spelling is wrong. Maybe it is %s.\n", pLion);
delete pLion;
}
bResult = pSpellCheck->CheckWord(L"dot", &stringResult);
if (bResult) {
printf("Word \"dot\" spelling right.\n");
} else {
char *pDot = _com_util::ConvertBSTRToString(stringResult);
printf("Word \"dot\" spelling is wrong. Maybe it is %s.\n", pDot);
delete pDot;
}

pSpellCheck->Release();

//当引用计数为0时释放组件
if (pMyUnknown->Release()== 0)
FreeLibrary(hModual);
return 0;
}

         该客户程序首先调用CreateObject函数创建字典组件对象,如果成功的话就得到了指向IUnknown接口的指针。然后可以通过QueryInterface函数获得IDictionary接口指针,利用IDictionary接口就可以完成查字典的功能。接下来再通过IDictionary接口的QueryInterface函数获得ISpellCheck接口指针,从而实现检查拼写的功能。

抱歉!评论已关闭.