自动化(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 使用 `?对象