模块化软件设计,有一些DLL是MFC特有的,其他程序还不能用; 软件 = 模块1 + 模块2 + 模块3, COM为模块软件的开发提供了基础

com

  • 每一个组件都使用CLSID来唯一标识; **COM组件就是一个类的实例,**是二进制代码模块.
  • 组件一般继承多个接口,它实现了接口中的纯虚函数.
  • HKEY_CLASSES_ROOT描述了每一个安装在系统上的组件

interface

  • 接口其实就是一个纯虚类.接口不允许多继承**.**
  • COM接口又分为三种: 进程内(inproc), 本地(local), 远程(remote)
  • 实际开发中,接口分为:
    • custom interface(自动化接口) 它就只能用虚函数表的方式来调用接口了,使用比较麻烦
    • dual interface(双接口) 使用比较灵活!
    • dispinterface interface(纯粹的自动化接口) COM组件的事件一般都用的是这种形式的接口

常见CLSID, <系统组件>

我的文档:450D8FBA-AD25-11D0-98A8-0800361B1103
我的电脑:20D04FE0-3AEA-1069-A2D8-08002B30309D
网上邻居:208D2C60-3AEA-1069-A2D7-08002B30309D
回收站:  645FF040-5081-101B-9F08-00AA002F954E
IE:     871C5380-42A0-1069-A2EA-08002B30309D
控制面板:21EC2020-3AEA-1069-A2DD-08002B30309D
拨号网络/网络连接:992CFFA0-F557-101A-88EC-00DD010CCC48

demo

StgCreateDocfile(); // 建立复合文件,返回根存储接口指针(可以代表这个文件)
WriteFmtUserTypeStg(); // 写入用户类型数据到存储中
CreateStreamOnHGlobal(); // 内存句柄转换为流对象
GetHGlobalFromStream(); // 获得流对应的内存句柄
IStorage::CreateStorage(); // 建立子存储

typedef struct _GUID {
  DWORD Data1; WORD Data2; WORD Data3; BYTE Data4[8];
} GUID;
typedef GUID IID;
static const IID IID_IX = {0xa348fbdd, 0xe765, 0x4b41, {0x84, 0x71, 0x6d, 0x8b, 0x70, 0x38, 0xfc, 0xc6}};
struct __declspec(uuid("d9314c0b-4317-4c15-8b0e-044d5c493b7a")) IComm;  // 把一个GUID赋值给IComm
CoCreateGuid();    //产生GUID
StringFromGUID();  IsEqualGUID();
StringFromCLSID(); IsEqualCLSID();
StringFromIID();   IsEqualIID();
ProgIDFromCLSID(); //为了方便记忆微软还引用了ProgID: "Excel.Application"
CLSIDFromProgID();

// 此时会生成demo.tli,demo.tlh 文件
/*
demo.tlh
  文件中有com组件的接口名称以及IID:__uuid(IComm),__uuidof(Comm) //接口ID,类ID
  文件中还定义了一个智能指针: IDemoPtr
  使用这个智能指针,创建COM对象的时候就不用CoCreateInstance()了,
  使用这个指针:ICommPtr spDemo(__uuidof(Comm));
*/
#import demo.dll
spDemo->Close();
spDemo.Release(); // 注意最后释放时使用.而不是指针的->

// 组件使用之前需要先进行注册,因为COM是跨应用的(MFC,C#,VB)
// 最终使用时仍需找到对应的dll文件,遗憾的是这个组件模块的位置不是固定的
// 故使用前需要先行注册,使用注册表来登记 dll位置<-->CLSID(IID,ProgID)
bool RegisterCOM(char* szDllPath) {
  HINSTANCE h = ::LoadLibrary(szDllPath);
  if (h == NULL) return false;
  FARPROC pFunc = ::GetProcAddress((HMODULE)h, "DllRegisterServer");
  if (pFunc == NULL) return false;
  if (S_OK != (*pFunc)())
    ShellExecute(NULL, "open", "regsvr32.exe", szDllPath, NULL, SW_HIDE);
  return true;
}

UINT SysStringLen(BSTR);
void SysFreeString(BSTR);
BSTR bstrA = SysAllocString(L"Hello BSTR");
SysFreeString(bstrA);
CComBSTR bstr(L"hello"); // 比_bstr_t功能要强大点
bstr = L"hello";
bstr.AssignBSTR("hello");
bstr.Append("world");
bstr += L"!";
// COM就是要屏蔽许多语言中数据类型的不同,所以提出了VARIANT
// VARIANT的封装类有: CComVariant,其构造函数有多种形式
VARIANT var;
VariantInit(&var);
var.vt = VT_I4; // I4:long I2:int R4:float R8:double
var.lVal = 100;
var.vt = VT_BSTR;
var.bstrVal = SysAllocString(L"hello");
var.vt = VT_BOOL;
var.boolVal = VARIANT_FALSE;
VariantChangeType(&var, &var, 0, VT_I8); // 数据类型的转换
VariantClear(&var);