COM学习笔记(五)

来源:互联网 发布:python mac绝对路径 编辑:程序博客网 时间:2024/05/17 01:39

 一般接口函数需要具备的函数是什么呢?

初始化,释放,显示,消息处理……至于它内部想干什么?我也管不着啊?我只要在需要的时候调用这七个函数,就可以了。

再次看接口的概念:

函数是通过vtable虚函数表来提供地址,从另外一个角度来看,不管是什么语言开发,编译器产生的代码都能生成这个表。这就实现了组件的“二进制特性”,轻松地实现了组件跨语言的要求。

每个接口都要有一个名字:叫IID.

接口一发表,就不能改了,如果改了,就容易出现向前兼容性,这个特性就叫做“接口不变性”。

任何接口都是从IUnknown 接口继承的,所以都包含IUnknown接口。

C/C++语言中需要事先对函数声明,那么就 会要求组件也必须提供C语言的头文件。不行!为了能使COM具有跨语言的能力,决定不再为任何语言提供对应的函数接口声明,而是独立地提供一个叫类型库(TLB)的声明。每个语言的IDE环境自己去根据TLB生成自己语言需要的包装。这个性质叫“接口声明的独立性”(注8)

客户程序和组件程序协商调用过程:

根据CLSID启动组件:CoCreateInstance();

你有IUnknown接口么?有,给你。

那你有其他接口么?IUnknown::QueryInterface(IID_IPersistStorage...);

判断有没有,有怎么处理,没有怎么处理?

有的话,差不多,下边就是初始化,然后是读取数据

执行一些消息处理函数,

如果用户要退出程序,那么你的数据保存了么?执行判断函数,

IPersistStreamInit:: IsDirty();

算算你需要多大的存储空间,

IPersistStreamInit::GetSizeMax();

然后执行save()函数保存你的数据。

然后是执行析构函数,释放接口和IUnknown接口

然后再退出

PostQuistMessage();

容器(或者说客户端)就是这样和组件进行对话,协商调用的。如果组件甲实现了 IA 接口,那么容器就会使用它,如果组件乙没有提供 IA 接口,但是它提供了 IB 接口,那么容器就会调用 IB 接口的函数......如此,容器程序根本就不需要知道组件到底是干什么的,组件到底是用什么语言开发的,组件的磁盘位置到底在哪里,它都可以正常运行。太奇妙了!太精彩了!怎一个“爽”字了得!

IID_IUnknown,如果用注册表样式表示,那么它的值是{00000000-0000-0000-C000-000000000046}。

注册表子键 ProgID 和 VersionIndependentProgID 分别表示真正的 ProgID 和版本无关的 ProgID。比如在我计算机上安装的 Excel,它的 ProgID = "Excel.Application.9",而 VersionIndependentProgID = "Excel.Application"。

IDispatch 接口的 IID 是多少?hresult = punk->QueryInterface(IID_IDispatch, &pdisp);

IPicture 接口的功能函数是什么?直接查找MSDN.

 

下边是COM的数据内型:

COM组件是运行在分布环境中的。

比如,你写了一个组件程序(DLL或EXE),那么使用者可能是在本机的某个进程内加载组件(INPROC_SERVER);也可能是从另一个进程中调用组件的进程(LOCAL_SERVER);也可能是在这台计算机上调用地球那边计算机上的组件(REMOTE_SERVER)。

double sin(double);

BOOL DeleteFile(LPCTSTR);布尔值,如果失败,需要GetLastError()才能取得失败的原因。

void* malloc(size_t): 内存指针

COM 的设计规范终于对他们进行了统一。组件API及接口指针中,除了IUnknown::AddRef()和IUnknown::Release()两个函数外,其它所有的函数,都以 HRESULT 作为返回值。

COM 组件是运行在分布式环境中的。也就是说,这个函数可能运行在“地球另一边”的计算机上,既然运行在那么遥远的地方,就有可能出现服务器关机、网络掉线、运行超时、对方不在服务区......等异常。于是,这个加法函数,除了需要返回运算结果以外,还应该返回一个值------函数是否被正常执行了。

所以:

HRESULT Add(long n1, long n2, long *pSum)

{

        *pSum = n1 + n2;

        return S_OK;
}

如果函数正常执行,则返回 S_OK,同时真正的函数运行结果则通过参数指针返回。如果遇到了异常情况,则COM系统经过判断,会返回相应的错误值。

      HRESULT hr = 调用组件函数;
      if( SUCCEEDED( hr ) ){...} // 如果成功
      ......
      if( FAILED( hr ) ){...} // 如果失败

HRESULT 其实是一个双字节的值,其最高位(bit)如果是0表示成功,1表示错误。具体参见 MSDN 之"Structure of COM Error Codes"说明。我们在程序中如果需要判断返回值,则可以使用比较运算符号;switch开关语句;也可以使用VC提供的宏:

说英语的人终于变“聪明”一些了。为了把全世界人民所有的所有的文字符号都统一进行编码,于是制定了UNICODE标准字符集。UNICODE 使用2个字节表示一个字符(unsigned shor int、WCHAR、_wchar_t、OLECHAR)。这下终于好啦,全世界任何一个地区的软件,可以不用修改地就能在另一个地区运行了。虽然我用 IE 浏览日本网站,显示出我不认识的日文文字,但至少不会是乱码了。UNICODE 的范围是 0x0000 - 0xFFFF 共6万多个字符,其中光汉字就占用了4万多个。嘿嘿,中国人赚大发了:0)

      const char * p = "Hello"; // 使用 ASCII 字符集
      const char * p = "你好"; // 使用 MBCS 字符集,由于 MBCS 完全兼容 ASCII,多数情况下,我们并不严格区分他们
      LPCSTR p = "Hello,你好"; // 意义同上
     
      const WCHAR * p = L"Hello,你好"; // 使用 UNICODE 字符集
      LPCOLESTR p = L"Hello,你好"; // 意义同上
     
      // 如果预定义了_UNICODE,则表示使用UNICODE字符集;如果定义了_MBCS,则表示使用 MBCS
      const TCHAR * p = _T("Hello,你好");
      LPCTSTR p = _T("Hello,你好"); // 意义同上

     使用 T 类型,是非常好的习惯,强烈推荐。

BSTR:其实是一个指针类型,最常用的自动化数据类型。

BSTR是一个指向UNICODE字符串的指针,且BSTR向前的4个字节中,使用DWORD保存这个字节的长度,没有结束符。

BSTR的处理函数:

各种字符串内型的转换

1、函数 WideCharToMultiByte(),转换 UNICODE 到 MBCS。

VARIANT
  C++、BASIC、Java、Pascal、Script......计算机语言多种多样,而它们各自又都有自己的数据类型,COM 产生目的,其中之一就是要跨语言(注3)。而 VARIANT 数据类型就具有跨语言的特性,同时它可以表示(存储)任意类型的数据。从C语言的角度来讲,VARIANT 其实是一个结构,结构中用一个域(vt)表示------该变量到底表示的是什么类型数据,同时真正的数据则存贮在 union 空间中。结构的定义太长了(虽然长,但其实很简单)大家去看 MSDN 的描述吧,这里给出如何使用的简单示例:

学生:我想用 VARIANT 表示一个4字节长的整数,如何做?
老师:VARIANT v; v.vt=VT_I4; v.lVal=100;

学生:我想用 VARIANT 表示布尔值“真”,如何做?
老师:VARIANT v; v.vt=VT_BOOL; v.boolVal=VARIANT_TRUE;

VARIANT v; v.vt=VT_BSTR; v.bstrVal=SysAllocString(L"Hello,你好");

CComVariant、COleVariant、_variant_t。比如上面三个问题就可以这样书写:CComVariant v1(100),v2(true),v3("Hello,你好");

那数组呢?

原创粉丝点击