一句话:如果你真正理解dll那么实现com实在是太容易了。在我的blog里有我写的关于dll的系列文章。
当然我在这里说多是c++的实现。因为com就是利用了dll的动态连接的特性,加上面相对象的封装,同时由于dll为了实现动态链接 所以它不能不有些限制,正是由于这些限制导致了 特殊化的写com的方式。或者说倒至了特殊化的封装方式。
//一下是dll部分
//..............................interface.h...........................
//#define struct interface
//类 或者接口的标识符号
#define INO 10
#define IANO 11
#define IBNO 12
//................
#define CANO 20
//...................
class I
{
public:
virtual int AddRef()=0;
virtual bool Release()=0;
virtual bool QueryInterface (int I_ID, void **pOutInterface)=0;
};
class IA
{
public:
virtual void fna1(int i)=0;
virtual void fna2(int i)=0;
};
class IB
{
public:
virtual void fnb1(int i)=0;
virtual void fnb2(int i)=0;
};
//...................................................................
//................a.h................................
#pragma warning(disable: 4786)
#if !defined(AFX_A_H__BB099D15_A5F3_4E71_8760_1D2F67814BC4__INCLUDED_)
#define AFX_A_H__BB099D15_A5F3_4E71_8760_1D2F67814BC4__INCLUDED_
#if _MSC_VER > 1000
#pragma once
#endif // _MSC_VER > 1000
//..............................
#include "interface.h"
//#include "IUnknown.h"
#include <map>
#include <string>
using namespace std;
//................
class CA :public IA,public IB, public I
{
public:
CA();
virtual ~CA();
virtual int AddRef();
virtual bool Release();
virtual bool QueryInterface( int I_ID, void **pOutInterface);
virtual void fna1( int i );
virtual void fna2(int i );
virtual void fnb1( int i );
virtual void fnb2(int i );
protected:
int m_iRef;
map<int , void*> m_map_iidtoi;
public:
typedef map<int , void*>::value_type VTMAPIIDTOI;
typedef map<int , void*>::iterator ITMAPIIDTOI;
};
extern "C"
{
__declspec(dllexport) /*bool*/void CreateInstance(int CLS_ID, int I_ID, //in
void **ppi);//out
};
//只有一个用来生成类对象的导出函数
#endif // !defined(AFX_A_H__BB099D15_A5F3_4E71_8760_1D2F67814BC4__INCLUDED_)
//......................................................................
//......................................................
// A.cpp: implementation of the CA class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "A.h"
#include <iostream>
using namespace std;
// TryCom3.cpp : Defines the entry point for the DLL application.
BOOL APIENTRY DllMain( HANDLE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
// This is the constructor of a class that has been exported.
// see TryCom3.h for the class definition
//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CA::CA()
{
//this->m_iRef =1;
this->m_map_iidtoi.insert( VTMAPIIDTOI(INO, static_cast<void*>( static_cast<I*>(this) ) ) );
this->m_map_iidtoi.insert( VTMAPIIDTOI(IANO,static_cast<IA*>(this)) );//×Ô¶¯»áת»»
this->m_map_iidtoi.insert( VTMAPIIDTOI(IBNO,static_cast<IB*>(this)) );
}
CA::~CA()
{
}
int CA::AddRef()
{
++m_iRef;
return m_iRef;
}
bool CA::Release()
{
m_iRef -- ;
if ( m_iRef == 0)
{
delete this;
}
return true;
}
bool CA::QueryInterface(int I_ID, void **pOutInterface)
{
CA::ITMAPIIDTOI it=this->m_map_iidtoi.find(I_ID);
if( it== this->m_map_iidtoi.end() )
{
*pOutInterface=NULL;
return false;
}
*pOutInterface = (*it).second ;
return true;
}
void CA::fna1( int i )
{
cout<<"CA::IA::fna1 verison =2 "<<"input para: "<<i<<endl;
}
void CA::fna2(int i )
{
cout<<"CA::IA::fna2 verison =2 "<<"input para: "<<i<<endl;
}
void CA::fnb1( int i )
{
cout<<"CA::IA::fnb1 verison =2 "<<"input para: "<<i<<endl;
}
void CA::fnb2(int i )
{
cout<<"CA::IA::fnb2 verison =2 "<<"input para: "<<i<<endl;
}
//////////////////////////
//bool
void CreateInstance(int CLS_ID, int I_ID, //in
void **ppi)//out
{
if ( CLS_ID == CANO )
{
CA *p= new CA;
p->QueryInterface( I_ID , ppi );
if ( *ppi == NULL )
{
return ;//false;
}
return ;//true;/* */
}
else
{
*ppi = NULL;
return;// true;
}
}
//........................................................................
//一下是客户端部分
// UseTryCom3.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include "windows.h"
#include "../interface.h"
#include <iostream>
#include <string>
using namespace std ;
int main(int argc, char* argv[])
{
HMODULE hm= ::LoadLibrary("E://lianxi//DLL_LIB_DCOM_COM//TryCom3//Debug//TryCom3.dll");
if (hm == NULL)
{
cout<<"LoadLibrary error"<<endl;
return 0;
}
typedef /*bool*/ void (*pfnCreateInstance)(int , int , void **);
pfnCreateInstance pfnci=(pfnCreateInstance)::GetProcAddress(hm,"CreateInstance");
if (pfnci==NULL)
{
cout<<"get CreateInstance address error/n";
return 0;
}
I *pi=NULL;
pfnci(CANO, INO ,(void **)&pi);
if (pi==NULL)
{
cout<<"get pi error/n";
return 0;
}
pi->AddRef();
IA *pia= NULL;
pi->QueryInterface( IANO , (void **)&pia );
if (pia==NULL)
{
cout<<"get pia error/n";
return 0;
}
pi->AddRef();
pia->fna1(10);
pia->fna2(11);
IB *pib= NULL;
pi->QueryInterface( IBNO , (void **)&pib );
if (pib==NULL)
{
cout<<"get pib error/n";
return 0;
}
pi->AddRef();
pib->fnb1(20);
pib->fnb2(21);
pi->Release();
pi->Release();
return 0;
}
//..................................
现在发觉很多介绍com的书都没有很好的介绍显式使用dll的方法。或者说显式使用dll导出类的方法。事实上正是由于dll的特性导致了com必须这么写。或者说com之所以这样是由于dll的特性(或者说是 局限性)加上封装。