以文本方式查看主题 - 计算机科学论坛 (http://bbs.xml.org.cn/index.asp) -- 『 C/C++编程思想 』 (http://bbs.xml.org.cn/list.asp?boardid=61) ---- COM 组件设计与应用(三、四)——数据类型 (http://bbs.xml.org.cn/dispbbs.asp?boardid=61&rootid=&id=54064) |
-- 作者:卷积内核 -- 发布时间:10/19/2007 8:31:00 AM -- COM 组件设计与应用(三、四)——数据类型 一、前言 上回书介绍了GUID、CLSID、IID和接口的概念。本回的重点是介绍 COM 中的数据类型。咋还不介绍组件程序的设计步骤呀?咳......别着急,别着急!孔子曰:“饭要一口一口地吃”;老子语:“心急吃不了热豆腐”,孙子云:“走一步看一步吧” ...... 先掌握必要的知识,将来写起程序来才会得心应手也:-) 走入正题之前,请大家牢牢记住一条原则:COM 组件是运行在分布式环境中的。比如,你写了一个组件程序(DLL或EXE),那么使用者可能是在本机的某个进程内加载组件(INPROC_SERVER);也可能是从另一个进程中调用组件的进程(LOCAL_SERVER);也可能是在这台计算机上调用地球那边计算机上的组件(REMOTE_SERVER)。所以在理解和设计的时候,要时时刻刻想起这句话。快!拿出小本本,记下来! 二、HRESULT 函数返回值 每个人在做程序设计的时候,都有他们各自的哲学思想。拿函数返回值来说,就有好多种形式。 函数 返回值 返回值信息 一会儿表示文件个数,一会儿表示文件名长度,一会儿表示字符长度 long Add( long n1, long n2 ) HRESULT Add( long n1, long n2, long *pSum ) HRESULT 值 含义 图一、HRESULT 的结构 HRESULT 其实是一个双字节的值,其最高位(bit)如果是0表示成功,1表示错误。具体参见 MSDN 之"Structure of COM Error Codes"说明。我们在程序中如果需要判断返回值,则可以使用比较运算符号;switch开关语句;也可以使用VC提供的宏:HRESULT hr = 调用组件函数; 三、UNICODE 计算机发明后,为了在计算机中表示字符,人们制定了一种编码,叫ASCII码。ASCII码由一个字节中的7位(bit)表示,范围是0x00 - 0x7F 共128个字符。他们以为这128个数字就足够表示abcd....ABCD....1234 这些字符了。 咳......说英语的人就是“笨”!后来他们突然发现,如果需要按照表格方式打印这些字符的时候,缺少了“制表符”。于是又扩展了ASCII的定义,使用一个字节的全部8位(bit)来表示字符了,这就叫扩展ASCII码。范围是0x00 - 0xFF 共256个字符。 咳......说中文的人就是聪明!中国人利用连续2个扩展ASCII码的扩展区域(0xA0以后)来表示一个汉字,该方法的标准叫GB-2312。后来,日文、韩文、阿拉伯文、台湾繁体BIG-5)......都使用类似的方法扩展了本地字符集的定义,现在统一称为 MBCS 字符集(多字节字符集)。这个方法是有缺陷的,因为各个国家地区定义的字符集有交集,因此使用GB-2312的软件,就不能在BIG-5的环境下运行(显示乱码),反之亦然。 咳......说英语的人终于变“聪明”一些了。为了把全世界人民所有的所有的文字符号都统一进行编码,于是制定了UNICODE标准字符集。UNICODE 使用2个字节表示一个字符(unsigned shor int、WCHAR、_wchar_t、OLECHAR)。这下终于好啦,全世界任何一个地区的软件,可以不用修改地就能在另一个地区运行了。虽然我用 IE 浏览日本网站,显示出我不认识的日文文字,但至少不会是乱码了。UNICODE 的范围是 0x0000 - 0xFFFF 共6万多个字符,其中光汉字就占用了4万多个。嘿嘿,中国人赚大发了:0) 在程序中使用各种字符集的方法: const char * p = "Hello"; // 使用 ASCII 字符集 四、BSTR COM 中除了使用一些简单标准的数据类型外(注2),字符串类型需要特别重点地说明一下。还记得原则吗?COM 组件是运行在分布式环境中的。通俗地说,你不能直接把一个内存指针直接作为参数传递给COM函数。你想想,系统需要把这块内存的内容传递到“地球另一 边”的计算机上,因此,我至少需要知道你这块内存的尺寸吧?不然让我如何传递呀?传递多少字节呀?!而字符串又是非常常用的一种类型,因此 COM 设计者引入了 BASIC 中字符串类型的表示方式---BSTR。BSTR 其实是一个指针类型,它的内存结构是:(输入程序片段 BSTR p = ::SysAllocString(L"Hello,你好");断点执行,然后观察p的内存) BSTR 是一个指向 UNICODE 字符串的指针,且 BSTR 向前的4个字节中,使用DWORD保存着这个字符串的字节长度( 没有含字符串的结束符)。因此系统就能够正确处理并传送这个字符串到“地球另一 边”了。特别需要注意的是,由于BSTR的指针就是指向 UNICODE 串,因此 BSTR 和 LPOLESTR 可以在一定程度上混用,但一定要注意: 有函数 fun(LPCOLESTR lp),则你调用 BSTR p=...; fun(p); 正确 有函数 fun(const BSTR bstr),则你调用 LPCOLESTR p=...; fun(p); 错误!!! 有关 BSTR 的处理函数: ATL 的 BSTR 包装类。在 atlbase.h 中定义 LPCOLESTR lpw = L"Hello,你好"; // 此时,lpa 中保存着转换后的 MBCS 字符串 2、函数 MultiByteToWideChar(),转换 MBCS 到 UNICODE。使用范例: 3、使用 ATL 提供的转换宏。 A2BSTR OLE2A T2A W2A 使用范例: #include <atlconv.h> 学生:我想用 VARIANT 表示一个4字节长的整数,如何做? 学生:我想用 VARIANT 表示布尔值“真”,如何做? 所以如果你 v.boolVal=true 这样赋值,那么将来 if(VARIANT_TRUE==v.boolVal) 的时候会出问题(-1 != 1)。但是你注意观察,任何布尔类型的“假”都是0,因此作为一个好习惯,在做布尔判断的时候,不要和“真值”相比较,而要与“假值”做比较。 学生:我想用 VARIANT 保存字符串,如何做? 学生:哦......我明白了。可是这么操作真够麻烦的,有没有简单一些的方法? 学生:老师,我再问最后一个问题,我如何用 VARIANT 保存一个数组? 七、小结
注1:在后续的 ISupportErrorInfo 接口中介绍。 一、前言 二、HRESULT 函数返回值
[此贴子已经被作者于2007-10-22 9:35:06编辑过]
|
-- 作者:卷积内核 -- 发布时间:10/19/2007 8:35:00 AM -- COM 组件设计与应用(四)——简单调用组件 二、组件的启动和释放 在第三回中,大家用“小本本”记录了一个原则:COM 组件是运行在分布式环境中的 。于是,如何启动组件立刻就遇到了严重的问题,大家看这段代码: p = new 对象; 由上图可以看出,当调用组件的时候,其实是依靠代理(运行在本地)和存根(运行在远端)之间的通讯完成的。具体来说,当客户程序通过 CoCreateInstance() 函数启动组件,则代理接管该调用,它和存根通讯,存根则它所在的本地(相对于客户程序来说就是远程了)执行 new 操作加载对象。对于初学者,你可以不用理它,代理和存根对我们来说是透明的。只要大约知道是怎么一回事就一切OK了。 问题又来了,这个远程的对象什么时候消灭呢?在第二回介绍接口概念的时候,当时我们特意忽略了两个函数,就是IUnknown::AddRef()和IUnknown::Release(),从函数名就能猜到了,一个是对内部引用记数器(Ref)加1,一个是释放(减1),当记数器减为0的时候,就是释放的机会啦。看起来很复杂,没办法,因为这是在介绍原理。其实在我们写程序的时候到比较简单,请大家遵守几个原则: 1、启动组件得到一个接口指针(Interface)后,不要调用AddRef()。因为系统知道你得到了一个指针,所以它已经帮你调用了AddRef()函数; 2、通过QueryInterface()得到另一个接口指针后,不要调用AddRef()。因为......和上面的道理一样; 3、当你把接口指针赋值给(保存到)另一个变量中的时候,请调用AddRef(); 4、当不需要再使用接口指针的时候,务必执行Release()释放; 5、当使用智能指针的时候,可以省略指针的维护工作;(注1) 三、内存分配和释放 自从学习了C语言,老师就教导我们说:对于动态内存的申请和释放,一定要遵守“谁申请,谁释放”的原则。在此原则的指导下,不仅是我、不仅是你,就连特级大师都设计了这样怪怪的函数: 函数 说明 评论 CListBox::GetText(int,LPTSTR) C语言 C++语言 Windows 平台 COM IMalloc 接口 BSTR 1、BSTR 内存在上回书中,已经有比较丰富的介绍了,不再重复; 2、CoTaskXXX()函数族,其本质上就是调用C语言的函数(malloc...); 3、IMalloc 接口又是对 CoTaskXXX() 函数族的一个包装。包装后,同时增强了一些功能,比如:IMalloc::GetSize()可以取得尺寸,使用 IMallocSpy 可以监视内存的使用; 四、参数传递方向 在C语言的函数声明中,尤其当参数为指针的时候,你是看不出它传递方向的。比如: void fun(char * p1, int * p2); 请问,p1、p2 哪个是入参?哪个是出参?甚或都是入参或都是出参?由于牵扯到内存分配和释放等问题,COM 需要明确标注参数方向。以后我们写程序,就类似下面的样子: HRESULT Add([in] long n1, [in] long n2, [out] long *pnSum); // IDL文件(注2) 方向 申请人 释放人 提示 示例一、由 CLSID 得到 ProgID。(程序以 word 为例子。如果运行不正确,嘿嘿,你没有安装 word 吧?) ::CoInitialize( NULL ); CString BrowseFolder(HWND hWnd, LPCTSTR lpTitle) void CxxxView::OnDraw(CDC* pDC) |
-- 作者:oldnwind -- 发布时间:2/1/2009 4:53:00 PM -- 二三楼之间调用OleLoadPicture中,参数有遗缺。 查MSDN,大概应为 hr = ::OleLoadPicture( pStream, dwSize, TRUE, IID_IPicture, &pPicture); 不知对否? |
W 3 C h i n a ( since 2003 ) 旗 下 站 点 苏ICP备05006046号《全国人大常委会关于维护互联网安全的决定》《计算机信息网络国际联网安全保护管理办法》 |
78.125ms |