您的位置:首页 > 其它

组件对象模型与ATL实现

2007-10-10 18:02 351 查看
[align=center]组件对象模型与ATL实现[/align]
[align=center]Component Object Model and Implementation with ATL[/align]
应朋友之托,写这篇文章。
cheungmine
2007-10-10
 
本文所讲解的构建COM对象模型的技术主要面向对COM开发有一定经验却又难领悟其精髓的人,也许在高手看来,本文不过是东搬西凑出来的杂合体。然而,这是在COM领域实现大规模软件的基础。全部内容来自于我6年左右的COM学习和开发大型软件的经验。阅读并理解这篇文章,首先要求你是一名Windows平台的C++程序员,而且相当熟悉COM,并且熟练使用ATL开发。
 
一 概述
 
微软组件对象模型(COM)的出现是软件工业发展的一个重要进步。尽管到目前为止,它还主要运行于微软(MS)的操作系统平台。无论对COM喜欢或厌恶,它都充斥着整个互联网和Windows的计算环境。COM以难学易用而著称,与它一起恶名昭彰的还有微软的另外一个名词——ActiveX,我们称为控件。等到你真正按照示例代码实现了一个PolygonCtl或BullEye控件的时候,你才真正理解了一点COM的思想和方法。COM是理论,ActiveX是技术。深谙COM的精髓,熟练掌握ActiveX编程技术,是高级Windows程序员必须做到的。(有人提到MFC,遗憾的是,这是一个和VB一样应该抛弃的技术)。
 
本文不是讲解COM是什么的文章,也不是讲解如何进行ActiveX编程。我在这里要描述的是如何应用COM技术,建立类似于DOM(Document Object Model)的内容。也就是使用COM实现设计模式方面的问题。何谓设计模式,说白了,就是一种模型——在COM领域称为对象模型,它由一组互相关联的COM对象组合而成,存在于同一个类型库(type library)中,基本以DLL的面目出现。比如,搞地理信息系统的人耳熟能详:MapObjects(MO)、ArcObjects(AO)、SuperMap等等。这些东西是什么?它们就是使用COM技术实现的模式——对象模型。微软使用COM实现了W3C的XML解析器标准。一个HTML文件在浏览器(Internet Expolorer,IE)内部被实例化成DOM模型——一种典型的树状结构。使用JavaScript的人比较熟悉的window、document这些对象都是DOM中的组件。我要讲的就是,如何在架构的层次上实现上面这些东西。
 
作为一名COM程序员(当然不仅仅是开发COM,我就要同时写Web Services、网页、JavaScript AJAX、OpenGL、ACE、图形算法、Oracle OCI、C/C++等各种程序),必须精读过下面3本书,这也是我这篇文章的主要参考书:
1)COM技术内幕(Inside COM——by Dale Rogerson);
2)ATL技术内幕(ATL Internals——by Christopher Tavares, Kirk Fertitta, Brent Rector, Chris Sells);
3)COM本质论(Essential COM——by Don Box)。
 
其中,第2本书《ATL Internals》的——第8章 集合和枚举器——尤其是你必须弄清楚的,即:
Chapter 8. Collections and Enumerators。
 
原理在那里讲的很清楚了,我只是依样画葫芦告诉读者该如何应用。因为有必要指出的是:按照《ATL Internals: Working with ATL 8, Second Edition》一书的讲解例子,会出现编译不通过的情形。所以,我的实现或许对你有帮助。而且,系统提供的atlcom.h头文件实现的集合索引是从1开始的,这对我们习惯了从0开始的家伙,就如同让惯用右手的人使用左手吃饭一样不方便。我把它也一道改为0-based。
 
 
二 设计和初步建立对象模型
 
为便于说明问题,我以地图控件开发为例,目标是建立类似下面的对象模型,这是一种典型的树状结构:
 
<Canvas>
|— <Layers>
|— <Layer>
|— <Layer>
......
|— <Layer>
|— <Shapes>
|— <Shape>
|— <Shape>
... ...
|— <Shape>
 
     具体步骤如下:
 
第一步 创建类型库
 
打开VS2005创建ATL项目,可以取名为:MapLib。应用程序设置为:动态连接库。(绝对不要属性化和支持MFC)。按[完成]。然后设置项目属性[字符集]为未设置(我个人尤其讨厌使用Unicode字符集)。
查看IDL文件,如下:
[align=left]// MapLib.idl : MapLib 的IDL 源[/align]
[align=left]//[/align]
[align=left] [/align]
[align=left]// 此文件将由MIDL 工具处理以[/align]
[align=left]// 产生类型库(MapLib.tlb)和封送处理代码。[/align]
[align=left] [/align]
[align=left]import "oaidl.idl";[/align]
[align=left]import "ocidl.idl";[/align]
[align=left] [/align]
[align=left][[/align]
[align=left]     uuid(C8C046AB-3D44-45EF-B11C-C5822862049A),[/align]
[align=left]     version(1.0),[/align]
[align=left]     helpstring("MapLib 1.0 类型库")[/align]
[align=left]][/align]
[align=left]library MapLibLib[/align]
[align=left]{[/align]
[align=left]     importlib("stdole2.tlb");[/align]
};
 
第二步 添加Canvas控件
 
首先,我们这里要添加的Canvas组件是一个含有窗口控制的ActiveX控件,同时它也是MapLib对象模型的根组件。所以,选择向MapLib添加新的类,它属于[ATL]类别的[ATL控件]。选中后按[添加]按钮,出现[ATL控件向导 - MapLib]对话框,按下面的要求填写:
[名称]
C++/简称:Canvas。其他默认。
[选项]
控件类型/标准控件。线程模型/单元。支持/连接点/已授权。其他默认。
[接口]
可以全部支持。
[外观]
视图状态/不透明/单色背景。其他/全选中。杂项状态/全不选。其他默认。
[常用属性]
你可以选中几个简单的,如:Appearance和Background Color。
 
按[完成]按钮。其实,上面的很多选项都可以以后添加,要求你熟悉ATL向导生成的代码。本文为简单起见,忽略了许多实际需要的因素。记住一点:在任何时候绝对不要使用属性化。这个在VS2003里作为默认选中的选项,在VC2005里被去掉了。首先,我不喜欢属性化。其次,属性化看似简单,但引入了另一个复杂性。微软在新版本里去掉默认属性化说明属性化的确有相当的负作用。
好拉,让我们再看看类型库(MapLib.idl):
[align=left]// MapLib.idl : MapLib 的IDL 源[/align]
[align=left]//[/align]
[align=left] [/align]
[align=left]// 此文件将由MIDL 工具处理以[/align]
[align=left]// 产生类型库(MapLib.tlb)和封送处理代码。[/align]
[align=left] [/align]
[align=left]#include "olectl.h"[/align]
[align=left]import "oaidl.idl";[/align]
[align=left]import "ocidl.idl";[/align]
[align=left] [/align]
[align=left][[/align]
[align=left]    object,[/align]
[align=left]    uuid(A74C036D-B046-4F53-B53A-F8EF611F576D),[/align]
[align=left]    dual,[/align]
[align=left]    nonextensible,[/align]
[align=left]    helpstring("ICanvas 接口"),[/align]
[align=left]    pointer_default(unique)[/align]
[align=left]][/align]
[align=left]interface ICanvas : IDispatch{[/align]
[align=left]    [propput, bindable, requestedit, id(DISPID_BACKCOLOR)][/align]
[align=left]    HRESULT BackColor([in]OLE_COLOR clr);[/align]
[align=left]    [propget, bindable, requestedit, id(DISPID_BACKCOLOR)][/align]
[align=left]    HRESULT BackColor([out,retval]OLE_COLOR* pclr);[/align]
[align=left]    [propput, bindable, requestedit, id(DISPID_APPEARANCE)][/align]
[align=left]    HRESULT Appearance([in]short nAppearance);[/align]
[align=left]    [propget, bindable, requestedit, id(DISPID_APPEARANCE)][/align]
[align=left]    HRESULT Appearance([out, retval]short* pnAppearance);[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left][[/align]
[align=left]     uuid(C8C046AB-3D44-45EF-B11C-C5822862049A),[/align]
[align=left]     version(1.0),[/align]
[align=left]     helpstring("MapLib 1.0 类型库")[/align]
[align=left]][/align]
[align=left]library MapLibLib[/align]
[align=left]{[/align]
[align=left]     importlib("stdole2.tlb");[/align]
[align=left]     [[/align]
[align=left]         uuid(BC3D7FCC-C1AE-4476-A59C-431457A1173C),[/align]
[align=left]         control,[/align]
[align=left]         helpstring("Canvas Class")[/align]
[align=left]     ][/align]
[align=left]     coclass Canvas[/align]
[align=left]     {[/align]
[align=left]         [default] interface ICanvas;[/align]
[align=left]     };[/align]
};
 
遗憾的是,这不是我需要的结果,我们需要修改类型库文件,修改后的结果如下:
[align=left]// MapLib.idl : MapLib 的IDL 源[/align]
[align=left]//[/align]
[align=left] [/align]
[align=left]// 此文件将由MIDL 工具处理以[/align]
[align=left]// 产生类型库(MapLib.tlb)和封送处理代码。[/align]
[align=left] [/align]
[align=left]#include "olectl.h"[/align]
[align=left]import "oaidl.idl";[/align]
[align=left]import "ocidl.idl";[/align]
[align=left] [/align]
[align=left][[/align]
[align=left]     uuid(C8C046AB-3D44-45EF-B11C-C5822862049A),[/align]
[align=left]     version(1.0),[/align]
[align=left]     helpstring("MapLib 1.0 类型库")[/align]
[align=left]][/align]
[align=left]library MapLibLib[/align]
[align=left]{[/align]
[align=left]     importlib("stdole2.tlb");[/align]
[align=left] [/align]
[align=left]     interface ICanvas; // 预先声明[/align]
[align=left] [/align]
[align=left]    [[/align]
[align=left]        object,[/align]
[align=left]        uuid(A74C036D-B046-4F53-B53A-F8EF611F576D),[/align]
[align=left]        dual,[/align]
[align=left]        nonextensible,[/align]
[align=left]        helpstring("ICanvas 接口"),[/align]
[align=left]        pointer_default(unique)[/align]
[align=left]    ][/align]
[align=left]    interface ICanvas : IDispatch{[/align]
[align=left]        [propput, bindable, requestedit, id(DISPID_BACKCOLOR)][/align]
[align=left]        HRESULT BackColor([in]OLE_COLOR clr);[/align]
[align=left]        [propget, bindable, requestedit, id(DISPID_BACKCOLOR)][/align]
[align=left]        HRESULT BackColor([out,retval]OLE_COLOR* pclr);[/align]
[align=left]        [propput, bindable, requestedit, id(DISPID_APPEARANCE)][/align]
[align=left]        HRESULT Appearance([in]short nAppearance);[/align]
[align=left]        [propget, bindable, requestedit, id(DISPID_APPEARANCE)][/align]
[align=left]        HRESULT Appearance([out, retval]short* pnAppearance);[/align]
[align=left]    };[/align]
[align=left] [/align]
[align=left]     [[/align]
[align=left]         uuid(BC3D7FCC-C1AE-4476-A59C-431457A1173C),[/align]
[align=left]         control,[/align]
[align=left]         helpstring("Canvas Class")[/align]
[align=left]     ][/align]
[align=left]     coclass Canvas[/align]
[align=left]     {[/align]
[align=left]         [default] interface ICanvas;[/align]
[align=left]     };[/align]
};
 
我只是把ICanvas接口部分移到了library MapLibLib{... ...}内,并增加了接口的预先声明:interface ICanvas;    我习惯把下面一句添加到CCanvas的构造方法里:
m_bWindowOnly = TRUE;            // 必须:总是创建自己的窗口
[align=left] [/align]
第三步 添加其他对象
 
这些对象都是ATL简单类型。名称为:Layers、Layer、Shapes、Shape。除了选择支持ISupportErrorInfo,其他都是采取默认设置。最后,手动修改类型库,修改后的IDL如下:
[align=left]// MapLib.idl : MapLib 的IDL 源[/align]
[align=left]//[/align]
[align=left] [/align]
[align=left]// 此文件将由MIDL 工具处理以[/align]
[align=left]// 产生类型库(MapLib.tlb)和封送处理代码。[/align]
[align=left] [/align]
[align=left]#include "olectl.h"[/align]
[align=left]import "oaidl.idl";[/align]
[align=left]import "ocidl.idl";[/align]
[align=left] [/align]
[align=left] [/align]
[align=left][[/align]
[align=left]     uuid(C8C046AB-3D44-45EF-B11C-C5822862049A),[/align]
[align=left]     version(1.0),[/align]
[align=left]     helpstring("MapLib 1.0 类型库")[/align]
[align=left]][/align]
[align=left]library MapLibLib[/align]
[align=left]{[/align]
[align=left]     importlib("stdole2.tlb");[/align]
[align=left] [/align]
[align=left]     // 预先声明[/align]
[align=left]     interface ICanvas; [/align]
[align=left]     interface ILayers;[/align]
[align=left]     interface ILayer;[/align]
[align=left]     interface IShapes;[/align]
[align=left]     interface IShape;[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]     [[/align]
[align=left]         object,[/align]
[align=left]         uuid(A74C036D-B046-4F53-B53A-F8EF611F576D),[/align]
[align=left]         dual,[/align]
[align=left]         nonextensible,[/align]
[align=left]         helpstring("ICanvas 接口"),[/align]
[align=left]         pointer_default(unique)[/align]
[align=left]     ][/align]
[align=left]     interface ICanvas : IDispatch{[/align]
[align=left]         [propput, bindable, requestedit, id(DISPID_BACKCOLOR)][/align]
[align=left]         HRESULT BackColor([in]OLE_COLOR clr);[/align]
[align=left]         [propget, bindable, requestedit, id(DISPID_BACKCOLOR)][/align]
[align=left]         HRESULT BackColor([out,retval]OLE_COLOR* pclr);[/align]
[align=left]         [propput, bindable, requestedit, id(DISPID_APPEARANCE)][/align]
[align=left]         HRESULT Appearance([in]short nAppearance);[/align]
[align=left]         [propget, bindable, requestedit, id(DISPID_APPEARANCE)][/align]
[align=left]         HRESULT Appearance([out, retval]short* pnAppearance);[/align]
[align=left]     };       [/align]
[align=left]     [[/align]
[align=left]         object,[/align]
[align=left]         uuid(874C2033-17F1-4534-BAB3-8F0367C45D14),[/align]
[align=left]         dual,[/align]
[align=left]         nonextensible,[/align]
[align=left]         helpstring("ILayers 接口"),[/align]
[align=left]         pointer_default(unique)[/align]
[align=left]     ][/align]
[align=left]     interface ILayers : IDispatch{[/align]
[align=left]     };[/align]
[align=left]     [[/align]
[align=left]         object,[/align]
[align=left]         uuid(8D7872CF-9D97-4C4D-A26F-2BBEC59B7CB6),[/align]
[align=left]         dual,[/align]
[align=left]         nonextensible,[/align]
[align=left]         helpstring("ILayer 接口"),[/align]
[align=left]         pointer_default(unique)[/align]
[align=left]     ][/align]
[align=left]     interface ILayer : IDispatch{[/align]
[align=left]     };[/align]
[align=left]     [[/align]
[align=left]         object,[/align]
[align=left]         uuid(E374F693-C4B3-49E7-948D-10C38C170DF7),[/align]
[align=left]         dual,[/align]
[align=left]         nonextensible,[/align]
[align=left]         helpstring("IShapes 接口"),[/align]
[align=left]         pointer_default(unique)[/align]
[align=left]     ][/align]
[align=left]     interface IShapes : IDispatch{[/align]
[align=left]     };[/align]
[align=left]     [[/align]
[align=left]         object,[/align]
[align=left]         uuid(C576412D-69D0-42A8-AA96-FFD534472C0C),[/align]
[align=left]         dual,[/align]
[align=left]         nonextensible,[/align]
[align=left]         helpstring("IShape 接口"),[/align]
[align=left]         pointer_default(unique)[/align]
[align=left]     ][/align]
[align=left]     interface IShape : IDispatch{[/align]
[align=left]     };[/align]
[align=left] [/align]
[align=left]     [[/align]
[align=left]         uuid(BC3D7FCC-C1AE-4476-A59C-431457A1173C),[/align]
[align=left]         control,[/align]
[align=left]         helpstring("Canvas Class")[/align]
[align=left]     ][/align]
[align=left]     coclass Canvas[/align]
[align=left]     {[/align]
[align=left]         [default] interface ICanvas;[/align]
[align=left]     };[/align]
[align=left]     [[/align]
[align=left]         uuid(155C20C5-F3A0-47D7-AC5E-EB3F31EF3AD1),[/align]
[align=left]         helpstring("Layers Class")[/align]
[align=left]     ][/align]
[align=left]     coclass Layers[/align]
[align=left]     {[/align]
[align=left]         [default] interface ILayers;[/align]
[align=left]     };[/align]
[align=left]     [[/align]
[align=left]         uuid(F6543476-E5D9-4CC0-84B4-DD772D555686),[/align]
[align=left]         helpstring("Layer Class")[/align]
[align=left]     ][/align]
[align=left]     coclass Layer[/align]
[align=left]     {[/align]
[align=left]         [default] interface ILayer;[/align]
[align=left]     };[/align]
[align=left]     [[/align]
[align=left]         uuid(754A7947-8EDD-4B81-8522-9AF6B35F290D),[/align]
[align=left]         helpstring("Shapes Class")[/align]
[align=left]     ][/align]
[align=left]     coclass Shapes[/align]
[align=left]     {[/align]
[align=left]         [default] interface IShapes;[/align]
[align=left]     };[/align]
[align=left]     [[/align]
[align=left]         uuid(DE163AAE-62CF-46D7-956C-5884685DE634),[/align]
[align=left]         helpstring("Shape Class")[/align]
[align=left]     ][/align]
[align=left]     coclass Shape[/align]
[align=left]     {[/align]
[align=left]         [default] interface IShape;[/align]
[align=left]     };[/align]
[align=left]};[/align]
 
到目前为止,我们已经把需要的ATL类添加到类型库(MapLib.dll)中,编译全部通过。这样,基本的对象全都有了,对象模型初步建立OK。
 
三 实现集合对象
 
下面增加具体的实现代码,以把Layers和Shapes变成集合类(Collection)。
 
第一步 把下面的代码添加到stdafx.h中
[align=left]// stdafx.h : 标准系统包含文件的包含文件,[/align]
[align=left]// 或是经常使用但不常更改的[/align]
[align=left]// 特定于项目的包含文件[/align]
[align=left] [/align]
[align=left](原有内容不动)[/align]
[align=left] [/align]
[align=left]// 后添加的内容[/align]
[align=left]template <typename T>[/align]
[align=left]struct _CopyVariantFromAdaptItf {[/align]
[align=left] static HRESULT copy(VARIANT* p1, const CAdapt< CComPtr<T> >& p2) {[/align]
[align=left]    HRESULT hr = p2.m_T->QueryInterface(IID_IDispatch, (void**)&p1->pdispVal);[/align]
[align=left]    if (SUCCEEDED(hr)) {[/align]
[align=left]      p1->vt = VT_DISPATCH;[/align]
[align=left]    }[/align]
[align=left]    else {[/align]
[align=left]      hr = p2.m_T->QueryInterface(IID_IUnknown, (void**)&p1->punkVal);[/align]
[align=left]      if( SUCCEEDED(hr) ) {[/align]
[align=left]        p1->vt = VT_UNKNOWN;[/align]
[align=left]      }[/align]
[align=left]    }[/align]
[align=left] [/align]
[align=left]    return hr;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] static void init(VARIANT* p) { VariantInit(p); }[/align]
[align=left] static void destroy(VARIANT* p) { VariantClear(p); }[/align]
[align=left]};[/align]
[align=left] [/align]
[align=left]template <typename T>[/align]
[align=left]struct _CopyItfFromAdaptItf {[/align]
[align=left]    static HRESULT copy(T** p1, const CAdapt< CComPtr<T> >& p2) {[/align]
[align=left]    if( *p1 = p2.m_T ) return (*p1)->AddRef(), S_OK;[/align]
[align=left]    return E_POINTER;[/align]
[align=left] }[/align]
[align=left] [/align]
[align=left] static void init(T** p) {}[/align]
[align=left] static void destroy(T** p) { if( *p ) (*p)->Release(); }[/align]
};
 
第二步 把Layers变成集合对象
 
把下面的代码添加到Layers.h中,添加后的Layers.h为:
[align=left]// Layers.h : CLayers 的声明[/align]
[align=left] [/align]
[align=left]#pragma once[/align]
[align=left]#include "resource.h"       // 主符号[/align]
[align=left] [/align]
[align=left]#include "MapLib.h"[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)[/align]
[align=left]#error "Windows CE 平台(如不提供完全DCOM 支持的Windows Mobile 平台)上无法正确支持单线程COM 对象。定义_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA 可强制ATL 支持创建单线程COM 对象实现并允许使用其单线程COM 对象实现。rgs 文件中的线程模型已被设置为“Free”,原因是该模型是非DCOM Windows CE 平台支持的唯一线程模型。"[/align]
[align=left]#endif[/align]
[align=left] [/align]
[align=left]// 下面加粗的文字是我添加的代码[/align]
[align=left]#include "Layer.h"[/align]
[align=left] [/align]
[align=left]typedef CComEnumOnSTL<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, [/align]
[align=left]_CopyVariantFromAdaptItf<ILayer>,[/align]
[align=left]list< CAdapt< CComPtr<ILayer> > > >[/align]
[align=left]CComEnumVariantOnListOfLayers;[/align]
[align=left] [/align]
[align=left]typedef ICollectionOnSTLImpl<IDispatchImpl<ILayers, &IID_ILayers>,[/align]
[align=left]list< CAdapt< CComPtr<ILayer> > >,[/align]
[align=left]ILayer*,[/align]
[align=left]_CopyItfFromAdaptItf<ILayer>,[/align]
[align=left]CComEnumVariantOnListOfLayers>[/align]
[align=left]ILayerCollImpl;[/align]
[align=left] [/align]
[align=left]// CLayers[/align]
[align=left] [/align]
[align=left]class ATL_NO_VTABLE CLayers :[/align]
[align=left]    public CComObjectRootEx<CComSingleThreadModel>,[/align]
[align=left]    public CComCoClass<CLayers>,    // non-createable,去掉注册表项[/align]
[align=left]    public ISupportErrorInfo,[/align]
[align=left]    public ILayerCollImpl[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left]    CLayers()[/align]
[align=left]    {[/align]
[align=left]    }[/align]
[align=left] [/align]
[align=left]//DECLARE_REGISTRY_RESOURCEID(IDR_LAYERS)[/align]
[align=left]DECLARE_NO_REGISTRY()   // non-createable,去掉注册表项[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]// 原来的代码被我注释掉[/align]
[align=left]// CHEUNGMINE: [/align]
[align=left]// CHEUNGMINE: // CLayers[/align]
[align=left] [/align]
[align=left]// CHEUNGMINE: class ATL_NO_VTABLE CLayers :[/align]
[align=left]// CHEUNGMINE:     public CComObjectRootEx<CComSingleThreadModel>,[/align]
[align=left]// CHEUNGMINE:     public CComCoClass<CLayers, &CLSID_Layers>,[/align]
[align=left]// CHEUNGMINE:     public ISupportErrorInfo,[/align]
[align=left]// CHEUNGMINE:     public IDispatchImpl<ILayers, &IID_ILayers, &LIBID_MapLibLib, /*wMajor =*/ 1, /*wMinor =*/ 0>[/align]
[align=left]// CHEUNGMINE: {[/align]
[align=left]// CHEUNGMINE: public:[/align]
[align=left]// CHEUNGMINE:     CLayers()[/align]
[align=left]// CHEUNGMINE:     {[/align]
[align=left]// CHEUNGMINE:     }[/align]
[align=left] [/align]
[align=left]// CHEUNGMINE: DECLARE_REGISTRY_RESOURCEID(IDR_LAYERS)[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]BEGIN_COM_MAP(CLayers)[/align]
[align=left]     COM_INTERFACE_ENTRY(ILayers)[/align]
[align=left]     COM_INTERFACE_ENTRY(IDispatch)[/align]
[align=left]     COM_INTERFACE_ENTRY(ISupportErrorInfo)[/align]
[align=left]END_COM_MAP()[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]// ISupportsErrorInfo[/align]
[align=left]     STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]     DECLARE_PROTECT_FINAL_CONSTRUCT()[/align]
[align=left] [/align]
[align=left]     HRESULT FinalConstruct()[/align]
[align=left]     {[/align]
[align=left]         return S_OK;[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]     void FinalRelease()[/align]
[align=left]     {[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]public:[/align]
[align=left] [/align]
[align=left]};[/align]
[align=left] [/align]
OBJECT_ENTRY_AUTO(__uuidof(Layers), CLayers)
 
然后编译,出现一堆的错误。首先在文件“stdafx.h”中的适当位置加入使用STL的语句,如下:
[align=left]// Standard C++ STL supports[/align]
[align=left]#include <stack>[/align]
[align=left]#include <vector>[/align]
[align=left]#include <list>[/align]
[align=left]#include <map>[/align]
[align=left]#include <string>[/align]
[align=left]#include <algorithm>[/align]
[align=left]#include <functional>[/align]
using namespace std;
 
然后,重新编译,全部OK。接下来,修改“MapLib.idl”文件的ILayers部分,使其如下所示:
 
[align=left]interface ILayers : IDispatch{[/align]
[align=left]     [propget,helpstring("Layer元素数目")] HRESULT Count([out,retval] long *nItems);[/align]
[align=left]    [id(DISPID_VALUE),propget,helpstring("取得指定索引的Layer元素。索引以为基数")] [/align]
[align=left]HRESULT Item([in] long index, [out,retval] ILayer** ppRef);[/align]
[align=left]    [id(DISPID_NEWENUM),propget,hidden] HRESULT _NewEnum([out,retval] IUnknown** ppEnum);[/align]
//   [id(1),helpstring("向集合增加一个元素,返回引用")] HRESULT Add([out,retval] ILayer** ppRef);
};
 
第三步 把Shapes变成集合对象
 
修改后的完整的Shapes.h为:
[align=left]// Shapes.h : CShapes 的声明[/align]
[align=left] [/align]
[align=left]#pragma once[/align]
[align=left]#include "resource.h"       // 主符号[/align]
[align=left] [/align]
[align=left]#include "MapLib.h"[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)[/align]
[align=left]#error "Windows CE 平台(如不提供完全DCOM 支持的Windows Mobile 平台)上无法正确支持单线程COM 对象。定义_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA 可强制ATL 支持创建单线程COM 对象实现并允许使用其单线程COM 对象实现。rgs 文件中的线程模型已被设置为“Free”,原因是该模型是非DCOM Windows CE 平台支持的唯一线程模型。"[/align]
[align=left]#endif[/align]
[align=left] [/align]
[align=left]#include "Shape.h"[/align]
[align=left] [/align]
[align=left]typedef CComEnumOnSTL<IEnumVARIANT, &IID_IEnumVARIANT, VARIANT, [/align]
[align=left]_CopyVariantFromAdaptItf<IShape>,[/align]
[align=left]vector< CAdapt< CComPtr<IShape> > > >[/align]
[align=left]CComEnumVariantOnArrayOfShapes;[/align]
[align=left] [/align]
[align=left]typedef ICollectionOnSTLImpl<IDispatchImpl<IShapes, &IID_IShapes>,[/align]
[align=left]vector< CAdapt< CComPtr<IShape> > >,[/align]
[align=left]IShape*,[/align]
[align=left]_CopyItfFromAdaptItf<IShape>,[/align]
[align=left]CComEnumVariantOnArrayOfShapes>[/align]
[align=left]IShapesCollImpl;[/align]
[align=left] [/align]
[align=left]// CShapes[/align]
[align=left] [/align]
[align=left]class ATL_NO_VTABLE CShapes :[/align]
[align=left]     public CComObjectRootEx<CComSingleThreadModel>,[/align]
[align=left]     public CComCoClass<CShapes>, // OLD: public CComCoClass<CShapes, &CLSID_Shapes>, [/align]
[align=left]// non-createable,去掉注册表项[/align]
[align=left]     public ISupportErrorInfo,[/align]
[align=left]     // public IDispatchImpl<IShapes, &IID_IShapes, &LIBID_MapLibLib, /*wMajor =*/ 1, /*wMinor =*/ 0>[/align]
[align=left]     public IShapesCollImpl[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left]     CShapes()[/align]
[align=left]     {[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]// DECLARE_REGISTRY_RESOURCEID(IDR_SHAPES)[/align]
[align=left]DECLARE_NO_REGISTRY()    // non-createable,去掉注册表项[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]BEGIN_COM_MAP(CShapes)[/align]
[align=left]     COM_INTERFACE_ENTRY(IShapes)[/align]
[align=left]     COM_INTERFACE_ENTRY(IDispatch)[/align]
[align=left]     COM_INTERFACE_ENTRY(ISupportErrorInfo)[/align]
[align=left]END_COM_MAP()[/align]
[align=left] [/align]
[align=left]// ISupportsErrorInfo[/align]
[align=left]     STDMETHOD(InterfaceSupportsErrorInfo)(REFIID riid);[/align]
[align=left] [/align]
[align=left] [/align]
[align=left]     DECLARE_PROTECT_FINAL_CONSTRUCT()[/align]
[align=left] [/align]
[align=left]     HRESULT FinalConstruct()[/align]
[align=left]     {[/align]
[align=left]         return S_OK;[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]     void FinalRelease()[/align]
[align=left]     {[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]public:[/align]
[align=left] [/align]
[align=left]};[/align]
[align=left] [/align]
OBJECT_ENTRY_AUTO(__uuidof(Shapes), CShapes)
 
接下来,修改“MapLib.idl”文件的IShapes部分,使其如下所示:
[align=left]interface IShapes : IDispatch{[/align]
[align=left]     [propget,helpstring("Shape元素数目")] HRESULT Count([out,retval] long *nItems);[/align]
[align=left]     [id(DISPID_VALUE),propget,helpstring("取得指定索引的Shape元素。索引以为基数")] [/align]
[align=left]HRESULT Item([in] long index, [out,retval] IShape** ppRef);[/align]
[align=left]     [id(DISPID_NEWENUM),propget,hidden] HRESULT _NewEnum([out,retval] IUnknown** ppEnum);[/align]
//   [id(1),helpstring("向集合增加一个元素,返回引用")] HRESULT Add([out,retval] IShape** ppRef);
};
 
到此,整个集合的架构建立起来了。其中Layers集合是基于STL的list,而Shapes是基于STL的vector。用户可以根据自己要实现的模型的特点,选择使用适合的STL集合类。
 
四 添加实现代码
 
下面增加添加元素的方法,即去除前面IDL文件中的注释为:
   
[id(1),helpstring("向集合增加一个元素,返回引用")] HRESULT Add([out,retval] ILayer** ppRef);
    [id(1),helpstring("向集合增加一个元素,返回引用")] HRESULT Add([out,retval] IShape** ppRef);
 
在Layers.h中,添加方法的实现代码如下:
[align=left]public:[/align]
[align=left]    STDMETHODIMP Add(ILayer** ppRef)[/align]
[align=left]    {[/align]
[align=left]        CComPtr<ILayer> spObj;[/align]
[align=left]        HRESULT hr = CLayer::CreateInstance(&spObj);[/align]
[align=left]        if (SUCCEEDED(hr))[/align]
[align=left]        {[/align]
[align=left]            m_coll.push_back(spObj);[/align]
[align=left]            return spObj.CopyTo(ppRef);[/align]
[align=left]        }[/align]
[align=left]        return hr;[/align]
[align=left]    }[/align]
 
在Shapes.h中,添加方法的实现代码如下:
[align=left]public:[/align]
[align=left]     STDMETHODIMP Add(IShape** ppRef)[/align]
[align=left]    {[/align]
[align=left]        CComPtr<IShape> spObj;[/align]
[align=left]        HRESULT hr = CShape::CreateInstance(&spObj);[/align]
[align=left]        if (SUCCEEDED(hr))[/align]
[align=left]        {[/align]
[align=left]            m_coll.push_back(spObj);[/align]
[align=left]            return spObj.CopyTo(ppRef);[/align]
[align=left]        }[/align]
[align=left]        return hr;[/align]
    }
 
现在,我们的集合对象已经基本好了。同时,通过一系列的改动,我们把Layers和Shapes指定为不可创建的对象,所以,我们可以把它们的注册表条目彻底删除:把Layers.rgs和Shapes.rgs大胆地彻底消灭掉。当你消灭了它们,编译会提示2条错误,双击错误,在文件MapLib.rc中,把下面的条目删除:
[align=left]IDR_LAYERS              REGISTRY                "Layers.rgs"[/align]
[align=left]IDR_SHAPES              REGISTRY                "Shapes.rgs"[/align]
[align=left][/align]
[align=left]特别值得注意:[/align]
[align=left]        依照此方法,你可以删除任何不需要独立创建的COM对象。比如本例中的Shape和Layer对象也不需要在注册表里注册,所以你也可以仿照Layers和Shapes的处理方式,改变向导生成的代码,删除它们的条目。如果你仍然想单独创建它们,你可以在根对象接口(这里是ICanvas)中添加创建的方法,如CreateObject,而在CreateObject方法内部实现上,采用C++创建对象,而不是使用COM类厂(需要通过注册表的GUID机制创建对象,速度比用C++创建对象慢了不知道多少倍。尤其在脚本中创建对象,使用new ActiveXObject方法是异常慢的)这样做的好处是,你在注册表中只需要留有根对象条目,简洁的多拉。[/align]
 
我们的Layers是通过Canvas得到的,因此,添加属性到ICanvas中:
[align=left]// MapLib.idl : MapLib 的IDL 源[/align]
[align=left]...[/align]
interface ICanvas : IDispatch{
     ......
[align=left]     [propget,id(1),helpstring("得到图层对象的引用")] HRESULT Layers([out, retval] ILayers** ppRef);[/align]
[align=left]// [id(2),helpstring("使用C++创建对象的例子")] HRESULT CreateObject([in] BSTR bstrObjName, [out, retval] IDispatch** ppOutObj);[/align]
}
 
添加实现代码到Canvas.h中,你只需注意加粗的文字:
[align=left]#include "Layers.h"[/align]
[align=left] [/align]
[align=left]// CCanvas[/align]
[align=left]class ATL_NO_VTABLE CCanvas :[/align]
[align=left]     public CComObjectRootEx<CComSingleThreadModel>,[/align]
[align=left]     ...[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left]     CComPtr<ILayers> m_spLayers;[/align]
[align=left] [/align]
[align=left]     CCanvas()[/align]
[align=left]     {[/align]
[align=left]m_bWindowOnly = TRUE;           // 必须:总是创建自己的窗口[/align]
     }
 
     ...
 
[align=left]HRESULT FinalConstruct()[/align]
[align=left]     {[/align]
[align=left]         return CLayers::CreateInstance(&m_spLayers);[/align]
[align=left]    //   return S_OK;[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]     void FinalRelease()[/align]
[align=left]     {[/align]
[align=left]     }[/align]
[align=left]public:[/align]
[align=left]    STDMETHODIMP CCanvas::get_Layers(ILayers** ppRef)[/align]
[align=left]    {[/align]
[align=left]        return m_spLayers.CopyTo(ppRef);[/align]
    }
[align=left] [/align]
[align=left]// 此方法在Canvas.cpp中实现,以避免交叉引用头文件[/align]
[align=left]// STDMETHOD(CreateObject)(BSTR bstrObjName, IDispatch** ppOutObj);[/align]
   
};
 
在Canvas.cpp中,CreateObject可以如下实现:
[align=left]// Canvas.cpp : CCanvas 的实现[/align]
[align=left]#include "stdafx.h"[/align]
[align=left]#include "Canvas.h"[/align]
[align=left] [/align]
[align=left]// CCanvas[/align]
[align=left]#include "Layers.h"[/align]
[align=left]#include "Layer.h"[/align]
[align=left]#include "Shapes.h"[/align]
[align=left]#include "Shape.h"[/align]
[align=left]/*[/align]
[align=left]STDMETHODIMP CCanvas::CreateObject(BSTR bstrObjName, IDispatch** ppOutObj)[/align]
[align=left]{[/align]
[align=left]     if (!bstrObjName || !ppOutObj)[/align]
[align=left]         return E_POINTER;[/align]
[align=left] [/align]
[align=left]     if (wcsicmp(bstrObjName, "layers")==0)[/align]
[align=left]     {[/align]
[align=left]         CComPtr<ILayers> spOut;[/align]
[align=left]         CLayers::CreateInstance(&spOut);[/align]
[align=left]         if(spOut)[/align]
[align=left]              return spOut->QueryInterface(IID_IDispatch, (void**)ppOutObj);[/align]
[align=left]     }[/align]
[align=left]     else if (wcsicmp(bstrObjName, "layer")==0)[/align]
[align=left]     {    [/align]
[align=left]         CComPtr<ILayer> spOut;[/align]
[align=left]         CLayer::CreateInstance(&spOut);[/align]
[align=left]         if(spOut)[/align]
[align=left]              return spOut->QueryInterface(IID_IDispatch, (void**)ppOutObj);[/align]
[align=left]     }[/align]
[align=left]     else if(wcsicmp(bstrObjName, "shapes")==0)[/align]
[align=left]     {    [/align]
[align=left]         CComPtr<IShapes> spOut;[/align]
[align=left]         CShapes::CreateInstance(&spOut);[/align]
[align=left]         if(spOut)[/align]
[align=left]              return spOut->QueryInterface(IID_IDispatch, (void**)ppOutObj);[/align]
[align=left]     }[/align]
[align=left]     else if(wcsicmp(bstrObjName, "shape")==0)[/align]
[align=left]     {[/align]
[align=left]         CComPtr<IShape> spOut;[/align]
[align=left]         CShape::CreateInstance(&spOut);[/align]
[align=left]         if(spOut)[/align]
[align=left]              return spOut->QueryInterface(IID_IDispatch, (void**)ppOutObj);[/align]
[align=left]     }[/align]
[align=left]     [/align]
[align=left]     return E_INVALIDARG;   [/align]
}
*/
Canvas.cpp的CreateObject例子使用简单的字符串比较,实际应用中,可以采用hash map等加快对象查找的速度。所以,COM对象有时候也要用其它方法创建为好!
 
同样的方法,给Layer对象添加属性,以得到 Shapes集合属性:
[align=left]// MapLib.idl : MapLib 的IDL 源[/align]
[align=left]...[/align]
 
[align=left]interface ILayer : IDispatch{[/align]
[align=left]     [propget,id(1),helpstring("得到图形集合对象的引用")] HRESULT Shapes([out, retval] IShapes** ppRef);[/align]
};
 
添加实现代码到Layer.h中,你只需注意加粗的文字:
[align=left]// Layer.h : CLayer 的声明[/align]
[align=left]...[/align]
[align=left]#include "Shapes.h"[/align]
[align=left] [/align]
[align=left]// CLayer[/align]
[align=left]class ATL_NO_VTABLE CLayer :[/align]
[align=left]...[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left]     CComPtr<IShapes>   m_spShapes;[/align]
[align=left] [/align]
[align=left]     CLayer()[/align]
[align=left]     {[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]...[/align]
[align=left] [/align]
[align=left]     HRESULT FinalConstruct()[/align]
[align=left]    {[/align]
[align=left]        return CShapes::CreateInstance(&m_spShapes);[/align]
[align=left]    // return S_OK;[/align]
[align=left]    }[/align]
[align=left] [/align]
[align=left]     void FinalRelease()[/align]
[align=left]     {[/align]
[align=left]     }[/align]
[align=left] [/align]
[align=left]public: [/align]
[align=left]    STDMETHODIMP get_Shapes(IShapes** ppRef)[/align]
[align=left]    {[/align]
[align=left]        return m_spShapes.CopyTo(ppRef);[/align]
[align=left]    }[/align]
[align=left]};[/align]
 
现在,你的对象模型就建好了。你可以从Canvas根对象得到图层集合(Layers)对象,你还可以向Layers中添加图层。你可以根据索引(目前是以1为基数)得到图层对象(Layer)的引用。从Layer对象得到图形集合对象(Shapes),并进一步操纵Shapes。
 
五 改变默认的索引基数和修改atlcom.h
 
最后要把以1为基数的索引,改为以0为基数的索引。这需要修改系统的头文件:atlcom.h。首先找到它,复制一份,更名为atlcom0.h,需要修改的地方我用粗体做了标记:
// atlcom.h--->atlcom0.h
[align=left] [/align]
[align=left]template <class T, class CollType, class ItemType, class CopyItem, class EnumType>[/align]
[align=left]class ICollectionOnSTLImpl : public T[/align]
[align=left]{[/align]
[align=left]public:[/align]
[align=left]     STDMETHOD(get_Count)(long* pcount)[/align]
[align=left]     {[/align]
[align=left]         if (pcount == NULL)[/align]
[align=left]              return E_POINTER;[/align]
[align=left]         ATLASSUME(m_coll.size()<=LONG_MAX);[/align]
[align=left] [/align]
[align=left]         *pcount = (long)m_coll.size();[/align]
[align=left] [/align]
[align=left]         return S_OK;[/align]
[align=left]     }[/align]
[align=left]     STDMETHOD(get_Item)(long Index, ItemType* pvar)[/align]
[align=left]     {[/align]
[align=left]#ifdef ITEM_INDEX_0_BASED[/align]
[align=left]        //Index is 0-based[/align]
[align=left]        if (pvar == NULL)[/align]
[align=left]            return E_POINTER;[/align]
[align=left]        if (Index < 0)[/align]
[align=left]            return E_INVALIDARG;[/align]
[align=left]        HRESULT hr = E_FAIL;[/align]
[align=left]        CollType::iterator iter = m_coll.begin();[/align]
[align=left]        while (iter != m_coll.end() && Index > 0)[/align]
[align=left]        {[/align]
[align=left]            iter++;[/align]
[align=left]            Index--;[/align]
[align=left]        }[/align]
[align=left]        if (iter != m_coll.end())[/align]
[align=left]            //hr = CopyItem::copy(pvar, &*iter);[/align]
[align=left]            hr = CopyItem::copy(pvar, *iter);   // CL2[/align]
[align=left]        return hr;[/align]
[align=left]#else[/align]
[align=left]        //Index is 1-based[/align]
[align=left]         if (pvar == NULL)[/align]
[align=left]              return E_POINTER;[/align]
[align=left]         if (Index < 1)[/align]
[align=left]              return E_INVALIDARG;[/align]
[align=left]         HRESULT hr = E_FAIL;[/align]
[align=left]         Index--;[/align]
[align=left]         CollType::const_iterator iter = m_coll.begin();[/align]
[align=left]         while (iter != m_coll.end() && Index > 0)[/align]
[align=left]         {[/align]
[align=left]              iter++;[/align]
[align=left]              Index--;[/align]
[align=left]         }[/align]
[align=left]         if (iter != m_coll.end())[/align]
[align=left]              //hr = CopyItem::copy(pvar, &*iter);[/align]
[align=left]              hr = CopyItem::copy(pvar, *iter);        // CL2[/align]
[align=left]         return hr;[/align]
[align=left]#endif      [/align]
     }
 
原来的atlcom.h中有个小BUG,导致VS2005编译无法通过。在atlcom0.h文件中找到下面的函数:
STDMETHODIMP IEnumOnSTLImpl<Base, piid, T, Copy, CollType>::Next
更正之(只需要改变粗体的地方,一句话而已 ),即将
hr = Copy::copy(pelt, &*m_iter);
改为
hr = Copy::copy(pelt, *m_iter); 
改过之后的完整的函数如下:
[align=left]template <class Base, const IID* piid, class T, class Copy, class CollType>[/align]
[align=left]STDMETHODIMP IEnumOnSTLImpl<Base, piid, T, Copy, CollType>::Next(ULONG celt, T* rgelt,[/align]
[align=left]     ULONG* pceltFetched)[/align]
[align=left]{[/align]
[align=left]     if (rgelt == NULL || (celt != 1 && pceltFetched == NULL))[/align]
[align=left]         return E_POINTER;[/align]
[align=left]     if (pceltFetched != NULL)[/align]
[align=left]         *pceltFetched = 0;[/align]
[align=left]     if (m_pcollection == NULL)[/align]
[align=left]         return E_FAIL;[/align]
[align=left] [/align]
[align=left]     ULONG nActual = 0;[/align]
[align=left]     HRESULT hr = S_OK;[/align]
[align=left]     T* pelt = rgelt;[/align]
[align=left]     while (SUCCEEDED(hr) && m_iter != m_pcollection->end() && nActual < celt)[/align]
[align=left]     {[/align]
[align=left]         // hr = Copy::copy(pelt, &*m_iter);[/align]
[align=left]         hr = Copy::copy(pelt, *m_iter); // cheungmine[/align]
[align=left]         if (FAILED(hr))[/align]
[align=left]         {[/align]
[align=left]              while (rgelt < pelt)[/align]
[align=left]                   Copy::destroy(rgelt++);[/align]
[align=left]              nActual = 0;[/align]
[align=left]         }[/align]
[align=left]         else[/align]
[align=left]         {[/align]
[align=left]              pelt++;[/align]
[align=left]              m_iter++;[/align]
[align=left]              nActual++;[/align]
[align=left]         }[/align]
[align=left]     }[/align]
[align=left]     if (SUCCEEDED(hr))[/align]
[align=left]     {[/align]
[align=left]         if (pceltFetched)[/align]
[align=left]              *pceltFetched = nActual;[/align]
[align=left]         if (nActual < celt)[/align]
[align=left]              hr = S_FALSE;[/align]
[align=left]     }[/align]
[align=left]     return hr;[/align]
[align=left]}[/align]
 
这样,在你所有包含atlcom.h的地方,用atlcom0.h替换,并在包含atlcom0.h前面,加入如下的宏:
[align=left]// stdafx.h[/align]
[align=left]...[/align]
[align=left]// NOT: #include <atlcom.h>[/align]
[align=left]#define ITEM_INDEX_0_BASED[/align]
#include "atlcom0.h"        // 0-based index supports
 
六 结束语
 
好了,我要讲的内容都讲完了。你可以编写如下面的js脚本(Canvas.htm)使用这个对象模型:
[align=left]<HTML>[/align]
[align=left]<HEAD>[/align]
[align=left]<TITLE>cheungmine@gmail.com</TITLE>[/align]
[align=left]<script language="javascript" type="text/javascript">[/align]
[align=left]    function Button1_onclick() {[/align]
[align=left]        lyrs = Canvas.Layers;[/align]
[align=left]        lyr = lyrs.Add();[/align]
[align=left]        alert(lyr);[/align]
[align=left]        [/align]
[align=left]        lyr.Shapes.Add();[/align]
[align=left]        lyr.Shapes.Add();[/align]
[align=left]        lyr.Shapes.Add();[/align]
[align=left]                [/align]
[align=left]        alert("Layers Count:"+lyrs.Count);[/align]
[align=left]        alert("Shapes Count:"+lyr.Shapes.Count);        [/align]
[align=left]    }[/align]
[align=left]    function Button2_onclick() {[/align]
[align=left]        var cvs = new ActiveXObject("MapLib.Canvas");[/align]
[align=left]        alert(cvs);[/align]
[align=left]        [/align]
[align=left]        cvs.Layers.Add();[/align]
[align=left]        cvs.Layers.Add();[/align]
[align=left]        cvs.Layers.Add();[/align]
[align=left]        [/align]
[align=left]        alert("Layers Count:"+cvs.Layers.Count); [/align]
[align=left]alert ( cvs.Layers.Item(0) );   // 如果显示undefined,说明索引不是以-based         [/align]
[align=left]    }[/align]
[align=left] [/align]
[align=left]</script>[/align]
[align=left]</HEAD>[/align]
[align=left]<BODY>[/align]
[align=left]<OBJECT ID="Canvas" CLASSID="CLSID:BC3D7FCC-C1AE-4476-A59C-431457A1173C"></OBJECT>[/align]
[align=left]    <input id="Button1" type="button" value="button" onclick="return Button1_onclick()" />[/align]
[align=left]    <input id="Button2" type="button" value="button" onclick="return Button2_onclick()" />[/align]
[align=left]</BODY>[/align]
</HTML>
 
以上内容,希望对朋友们有所帮助。这些内容看似简单,如果有兴趣,你就完整地做几遍,最后就变成你自己的知识了。我费了一天的时间把它整理出来,希望得到你们的批评指正!点击下面的链接可以得到本文的配套例子代码:
 http://download.csdn.net/source/260939
 
[align=right]张亮[/align]
[align=right]2007-10-10初稿[/align]
[align=right]2007-10-11修改[/align]
[align=right]上海浦东[/align]
[align=right]转载请注明出处![/align]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: