自动化(Automation) 潘爱民 http://www.icst.pku.edu.cn/compcourse 内容 ? 自动化基础 ? 自动化对象实现 ? 自动化对象应用 ? 自动化编程 自动化产生与发展 ? 弱类型的高级语言(比如Visual Basic)如何 使用COM? ?VBA(或VBScript) ? 自动化与COM的关系 ? 自动化的广泛应用 几个概念 ? 自动化对象 – 实现了IDispatch接口的COM对象 ? 属性和方法 ?ODL(对象描述语言) ? 类型库 ? 自动化兼容的数据类型 属性(property)和方法(method) ? 自动化对象的两个基本特性,都具有符 号化的名字,用DISPID来标识 ? 属性是指自动化对象的数据特征 – 属性可以由索引,索引可以是整数,也可以是其他 类型 ? 方法是指自动化对象所提供的功能服务 – 方法比属性要灵活 ,可以 数 类型库(typelib) ? 类型 是 程 与 对象 的基础 ?IDL和ODL – 接口类型 使用interface或dispinterface关 字描述 – 对象类型 使用coclass关 字描述 ? library关 字描述库 – 个 程 的所有对象 个ODL , 用library关 字描述库 ? 具MIDL ODL(IDL)编 TLB ODL描述 [ uuid(3C591B20-1F13-101B-B826-00DD01103DE1), // LIBID_Point. helpstring("Point 1.0 Type Library"), lcid(0x0000), version(1.0) ] library Point { importlib("stdole.tlb"); [ uuid(3C591B25-1F13-101B-B826-00DD01103DE1), // IID_IPoint. helpstring("IPoint interface."), ] interface IPoint : IUnknown { [propget, helpstring("Returns and sets x coordinate.")] HRESULT x([out, retval] int* retval); ODL描述 ( ) [propput, helpstring("Returns and sets x coordinate.")] HRESULT x([in] int Value); [propget, helpstring("Returns and sets y coordinate.")] HRESULT y([out, retval] int* retval); [propput, helpstring("Returns and sets y coordinate.")] HRESULT y([in] int Value); [helpstring("Move Point To.")] HRESULT MoveTo([in] int newX, [in] int newy); } [ uuid(3C591B26-1F13-101B-B826-00DD01103DE1), // IID_DIPoint. helpstring("DIPoint interface."), ] dispinterface DIPoint { interface IPoint; } ODL描述 ( ?) [ uuid(3C591B21-1F13-101B-B826-00DD01103DE1), // CLSID_Point helpstring("Point Class"), appobject ] coclass Point { [default] dispinterface IPoint; } } // for library ¢ 个ODL接口 £ [ uuid(BFB73347-822A-1068-8849-00DD011087E8), version(1.0), helpstring("Useful help string."), helpcontext(2480) ] dispinterface MyDispatchObject { properties: [id(1)] int x; // An integer property named x. [id(2)] BSTR y; // A string property named y. methods: [id(3)] void show(); // No arguments, no result. [id(11)] int computeit(int inarg, double *outarg); }; IDispatch接口 class IDispatch : public IUnknown { public: virtual HRESULT GetTypeInfoCount( UINT *pctinfo) = 0; virtual HRESULT GetTypeInfo( UINT iTInfo, LCID lcid, ITypeInfo **ppTInfo) = 0; virtual HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) = 0; virtual HRESULT Invoke( DISPID dispIdMember, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) = 0; }; ?发ID(DISPID) ? 整数,0和¥数有特? § ? 自动化接口 currency1?发ID'“方法和属性 ??的?发ID?其 § ?发ID fifl DISPID_VALUE 0 接口的–? ?,如· 程 指?属性或方 法,?用?–? ?? DISPID_UNKNOWN -1 GetIDsOfNames ”数 ?…?fi‰ 应的 ? 或 数名?有 `?发ID? DISPID_PROPERTYPUT -3 属性′?”数 指? 个接ˉfi的 数? DISPID_NEWENUM -4 ?˙对象的_NewEnum方法? DI SP I D_ EVAL UATE - 5 对象的Evaluate方法, ¨ ?的?本语言 可以用 方 号[]来‰?? DISPID_CONSTRUCTOR -6 ‰?具有与??”数 ˇ功能的方法? DISPID_DESTRUCTOR -7 ‰?具有与—?”数 ˇ功能的方法? 与自动化对象 ? 程 自动化对象 IPoint QueryInterface AddRef Release GetTypeInfoCount GetTypeInfo GetIDsOfNames Invoke vtable get_x set_x get_y set_y MoveTo IDispatch::Invoke ? 数dispIdMember 指?DISPID ? 数lcid 指?本 化标识 ? 数wFlags 指? 用类型 – DISPATCH_METHOD DISPATCH_PROPERTYGET DISPATCH_PROPERTYPUT DISPATCH_PROPERTYPUTREF ? 数pDispParams – 用的 数数 数的DISPID数 数 数个数 ? 数pVarResult ? ?…fi ? 数pExcepInfo ? ? 数puArgErr 数的索引fi 自动化兼容的数据类型( ) typedef struct tagVARIANT VARIANT; typedef struct tagVARIANT VARIANTARG; typedef struct tagVARIANT { VARTYPE vt; unsigned short wReserved1; unsigned short wReserved2; unsigned short wReserved3; union { // fi类型 short iVal; long lVal; byte bVal; float fltVal; double dblVal; VARIANT_BOOL bool; SCODE scode; CY cyVal; DATE date; BSTR bstrVal; IUnknown *punkVal; IDispatch *pdispVal; SAFEARRAY *parray; // 自动化兼容的数据类型(?) // 引用类型 short *piVal; long *plVal; byte *pbVal; float *pfltVal; double *pdblVal; VARIANT_BOOL *pbool; SCODE *pscode; CY *pcyVal; DATE *pdate; BSTR *pbstrVal; IUnknown **ppunkVal; IDispatch **ppdispVal; VARIANT *pvarVal; void *byref; }; }; 自动化兼容的数据类型( ) ? ? 型VARIANT_BOOL typedef short VARIANT_BOOL; /* 0 == FALSE, -1 == TRUE */ ? a 类型CY typedef struct tagCY { unsigned long Lo; long Hi; } CY; ? 类型DATE – ?数,整数??‰?自1899?12o30 以来的 数, 数 ?? fi Basic字符 类型BSTR ?OLE提供了 API”数?“BSTR SysAllocString SysAllocStringLen SysFreeString SysReAllocString SysReAllocStringLen以?SysStringLen count …… \0 4字 fi 字符 ,其 ?指?的fi 以0 LPTSTR psz SAFEARRAY类型 typedef struct tagSAFEARRAY { USHORT cDims; // 数 ?数 USHORT fFeatures; // 标? ULONG cbElements; // 数 ?个? 的 ULONG cLocks; // 数 的 数 PVOID pvData; // 指向数 的数据 SAFEARRAYBOUND rgsabound[ ]; // 数 ? ?的边界 }SAFEARRAY; typedef struct tagSAFEARRAYBOUND { ULONG cElements; // 该?的? 个数 LONG lLbound; // 该?的下界 } SAFEARRAYBOUND; ?OLE也提供了 套API”数用来?“SAFEARRAY ? 自动化数据类型的转换 ? Invoke”数的数据类型转换能力 弱数据类型 开发环境提供了极 的便利 ?OLE提供了两个类型转换”数 VariantChangeType和VariantChangeTypeEx ?OLE也提供了 专门的类型转换”数 Var<type>From<type>,比如VarR4FromI2 VarUI2FromDisp 属性和方法 用的 数 递 ? Invoke”数的 数pDispParams typedef struct tagDISPPARAMS { VARIANTARG *rgvarg; // 数数 DISPID *rgdispidNamedArgs; // 数的?发ID数 UINT cArgs; // 数 数个数 UINT cNamedArgs; // 命名 数个数 } DISPPARAMS; 数顺 ? rgvarg数 , 数的顺 与 程 用的 数左右顺 刚好 反 比如 Object.Method(arg1, arg2, arg3) 对应Invoke”数的pDispParams 数的DISPPARAMS ? ,cArgs 3,‰ 方法 用有3个 数,rgarg数 的 ??别 arg3对应rgvarg[0] arg2对应rgvarg[1] arg1对应rgvarg[2]? 可选 数 ? ODL ,我们可以把方法的 数标记 可 选的(optional) ? 可选 数也会出现 DISPPARAMS ? ? 如·vt域 VT_ERROR 且scode域 DISP_E_PARAMNOTFOUND,?? 数 可选 数 命名 数(named argument) ? DISPPARAMS ?的cNamedArgs ?指?了 rgarg数 命 名 数的个数 ? 命名 数可以 受 约束 比如 BSTR CompareWord (int cNumbers, BSTR Word1, BSTR Word2, BSTR Word3) retVal = Obj.CompareWord (3, Word1="dog", Word2="pig", Word3="elephant") rgdispidNamedArgs dog pig elephant 3 id (0) id (1) id (2) rgvarg 0 1 2 3 cArgs=4 cNamedArgs=3 IDispatchEx接口 ? 派生?IDispatch ? IDispatchEx接口最主要的特性是增加了 对 ?的'“,尤其是动态增加和删除 ?的特性 IDispatchEx接口使用 £ cmd1.CommandText = "AuthorsByYearBorn" cmd1.CommandType = adCmdStoredProc cmd1.Name = "AuthorsSP" cmd1.ActiveConnection = Cn Private Sub RunSPButton_Click() Cn.authorsSP 1947, 1948, rs End Sub 自动化对象实现 GetTypeInfoCount ? 类型库支持 – 首先把本接口所 的idl 编 tlb , 以便利用COM提供的类型库功能实现本接 口的类型支持 STDMETHODIMP CImpIDispatch::GetTypeInfoCount(UINT *pctInfo) { *pctInfo=1; //Because we have implemented GetTypeInfo member. return NOERROR; } 自动化对象实现 GetTypeInfo STDMETHODIMP CImpIDispatch::GetTypeInfo(UINT itInfo, LCID lcid , ITypeInfo **ppITypeInfo) { …... if (*ppITI == NULL) { hr=LoadRegTypeLib(LIBID_Point, 1, 0, lcid, &pITypeLib); if (FAILED(hr)) { hr=LoadTypeLib(OLETEXT("Point.TLB"), &pITypeLib); } hr=pITypeLib->GetTypeInfoOfGuid(IID_DIPoint, ppITI); pITypeLib->Release(); if (FAILED(hr)) return hr; } (*ppITI)->AddRef(); *ppITypeInfo=*ppITI; return NOERROR; } GetIDsOfNames实现 HRESULT GetIDsOfNames(REFIID riid, LPOLESTR *rgszNames, UINT cNames, LCID lcid, DISPID *rgDispId) ? 第 种方法,由?自动化对象知道自己所有的方法和 属性以?它们的?发ID,所以它可以利用这些知识提 供GetIDsOfNames服务 ? 第?种方法,利用自动化接口的类型 对象实现 GetIDsOfNames”数? – 用ITypeInfo::GetIDsOfNames”数 HRESULT GetIDsOfNames(LPOLESTR *rgszNames, UINT cNames, MEMBERID *pMemId); – 或者OLE的封装”数DispGetIDsOfNames ITypeLib和ITypeInfo接口 ?COM实现的typelib对象 – LoadRegTypeLib, currency1注册‰ – LoadTypeLib – currency1ITypeLib接口操纵typelib对象 – 第 方语言 currency1typelib支持COM ? typeinfo对象 – 类型库 的类型对象, 如接口 ? 对象 – currency1ITypeInfo接口操纵typeinfo对象 Invoke”数实现 ? 第 种方法适˙?比较简单的情形,Invoke” 数¥责所有的 ??“, 数提取 转换 ,然后根据 ??发ID的 ˇ,执行 ˇ 的?支 ? 第?种方法也要用`类型库 对象ITypeInfo 接口指针? 类型库与自动化对象 的关系 程 (¨ ?) 自动化对象 …… GetTypeInfoCount GetTypeInfo GetIDsOfNames Invoke DIPoint 类型 对象 ITypeInfo get_x set_x get_y set_y MoveTo TLB MIDL 具头 ODL IPoint MIDL生 的头 接口?§ MIDL_INTERFACE("3C591B25-1F13-101B-B826-00DD01103DE1") IPoint : public IUnknown { public: virtual /* [helpstring][propget] */ HRESULT STDMETHODCALLTYPE get_x( /* [retval][out] */ int __RPC_FAR *retval) = 0; virtual /* [helpstring][propput] */ HRESULT STDMETHODCALLTYPE put_x( /* [in] */ int Value) = 0; …… virtual /* [helpstring] */ HRESULT STDMETHODCALLTYPE MoveTo( /* [in] */ int newX, /* [in] */ int newy) = 0; }; MIDL_INTERFACE("3C591B26-1F13-101B-B826-00DD01103DE1") DIPoint : public IDispatch { }; Invoke”数实现 STDMETHODIMP_(long) CPoint::get_x (void) { return m_x; } …... STDMETHODIMP CImpIDispatch::Invoke( DISPID dispID, REFIID riid, LCID lcid, unsigned short wFlags, DISPPARAMS *pDispParams, VARIANT *pVarResult, EXCEPINFO *pExcepInfo, UINT *puArgErr) { …... hr=GetTypeInfo(0, lcid, &pTI); if (FAILED(hr)) return hr; hr=pTI->Invoke((IPoint *)m_pObj, dispID, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr); if (产生内? ) { …… // ?“ pTI->Release(); return DISP_E_EXCEPTION; } pTI->Release(); return hr; } Invoke”数 用currency1程 的 ?“( ) ? 由EXCEPINFO ??§ typedef struct tagEXCEPINFO { WORD wCode; // ( )代码 WORD wReserved; // ??字 BSTR bstrSource; // 发生 的源对象, 般 对象的ProgID BSTR bstrDescription; // fl BSTR bstrHelpFile; // 关的帮助 路径 DWORD dwHelpContext; // 关的帮助环境ID PVOID pvReserved; // ?? HRESULT (__stdcall *pfnDeferredFillIn)(struct tagEXCEPINFO *); // 用?填充EXCEPINFO ? // 由对象提供的… ”数 SCODE scode; // 的状态码,它与wCode 能ˇ 使用 } EXCEPINFO, * LPEXCEPINFO; Invoke”数 用currency1程 的 ?“(?) ? – Invoke”数直接填充 – 或者 currency1… ”数由 ¨ 填充 ? 如·用类型库实现Invoke”数,那么有两种支 持 的 法 – currency1 递 – 使用OLE提供的 对象 语种——本 化 ?LCID(LocaleID或者locale identifier)”数 数? LCID ?§ 个32 的整数? ? Winnt.h // +-------------+---------+-------------------------+ // | Reserved | Sort ID | Language ID | // +-------------+---------+-------------------------+ // 31 20 19 16 15 0 bit #define LANG_CHINESE 0x04 #define LANG_ENGLISH 0x09 #define SORT_DEFAULT 0x0 // sorting default 用 ˙方 实现自动化对象 ? CreateStdDispatch”数 内? ˙对象 ? 要类型库 ? 支持 语种 自动化对象应用 ? 接口(dual interface) ? ?和 ? ? 自动化?˙对象 ? 以IDispatch 出接口 * ? 自动化¨ ? dual interface ? 个接口, 有IDispatch接口的灵活性, 有 vtable接口的 ? IDispatch接口派生 – 使用自动化兼容的数据类型 ? currency1vtable可以 属性和方法 接口 ? 程 (¨ ?) 自动化对象 IPoint ( 接口) 类型 对象 ITypeInfo QueryInterface AddRef Release GetTypeInfoCount GetTypeInfo GetIDsOfNames Invoke get_x set_x get_y set_y MoveTo TLB MIDL 具头 ODL vtable 接口?§ [ uuid(3C591B25-1F13-101B-B826-00DD01103DE1), // IID_IPoint. helpstring("Point object."), dual ] interface IPoint : IDispatch { [propget, helpstring("Returns and sets x coordinate.")] HRESULT x([out, retval] int* retval); [propput, helpstring("Returns and sets x coordinate.")] HRESULT x([in] int Value); [propget, helpstring("Returns and sets y coordinate.")] HRESULT y([out, retval] int* retval); [propput, helpstring("Returns and sets y coordinate.")] HRESULT y([in] int Value); [helpstring("Move Point To.")] HRESULT MoveTo([in] int newX, [in] int newy); } ?和 ? ? ?(early binding) – compile time,利用typelib – vtable binding,dispid binding – 般用?编 环境, 如VB VC ,以?VBA, 高 ? ?(late binding) – runtime – dispid binding – 可以用??本环境, 如ASP ,灵活性 ? ? 如, VB ,开发 类型 自动化?˙对象 ? ?˙对象也是自动化对象, 要 –1 ˇ类对象(或数fi)的容?对象,它 提供 这些 ?的方法 –2 它 支持Add Remove和Item方法以?Count属性 ? 索引fi可以是整数,也可以是其他类型 ? 利用标 属性或方法_NewEnum提供“for…each”语 法 ?的 特性 ? ?˙对象的命名 对象 型? 对象 型 ? object hierarchy或object model ? Application对象,?¢对象,可 ? 数对象都是 可 的 ? 的关£ – ?˙与? 的关系, currency1 ? – 属性关系,下级对象是?级对象的 个属性 自动化¨ ?(automation controller) ? 自动化¨ ?也是 个COM ,功能¥ 的自动化 ¨ ???§currency1以下特性 – 对象 ' ? – 对象—?' ? – 如何“接` 个?? ?行的自动化对象? – 如何 自动化对象的属性? – 如何 用自动化对象的方法? – 提供fi ?“' ? – 个接口转fl`¢ 个接口?? – 向用 提供UI形 的对象类型 ? ?VB VBScript ?本 – 应用系? ?本引?和?本 者关系 应用系? (·主程 ) ?本引? ?本 currency1?本¨ 应用 引? 装 ?本 ?动引? ??引? ?本 – 7. fi 知 IActiveScriptSite 3. 装 ?本 应用系? ?本引? ?本 名字?对 应的对象 IDispatch IConnectionPointContainer IActiveScriptParse接口 2. 引?对象 4. 加 名字? 6. ”取名字? 8. 用对象的属性和方法 1. 要的受¨对象 IActiveScript接口 5. ?动引?,?行?本 与对象有 关的?本 自动化对象的marshaling ? Universal marshaler –CLSID {00020424-0000-0000-C000-000000000046} – ?要接口 的数据类型是自动化兼容的,…可以使用?接口可以 是自动化接口 – 描述接口 ,加?属性oleautomation或者dual ? 注册 [HKCR\Interface\<interface-iid>] @="IMyInterface" [HKCR\Interface\ <interface-iid>\ProxyStubClsid32] @="{00020424-0000-0000-C000-000000000046}" [HKCR\Interface\ <interface-iid>\ProxyStubClsid] @="{00020424-0000-0000-C000-000000000046}“ [HKCR\Interface\ <interface-iid>\TypeLib] @=" <typelib-id>“ Version="1.0" Universal marshaler? 类型库 接口 根接口代“ 用 ‰标对象 类型库 Visual Basic 使用自动化对象( ) Visual Basic 使用自动化对象(?) Dim PointObject As New PointComp.PointObj PointObject.x = 100 PointObject.y = 100 …… PointObject.MoveTo(200, 150) ? 对象 ?? £程 ( ) ? 程 1——具有 `?功能的自动化对象 £程 (?) ? 程 2——自动化 程 £程 ( ) ? 程 3—— Excel 使用 `?对象