ActiveX Scripting(后面简称[AS])。
简单的说[AS]就是:MS来帮我们解析脚本里的基本语句(如if、for、表态式、赋值等),我们负责解释、执行它所不认识的函数,对象。
下面再简单说一下操作[AS]的流程:
1. 实例化一个脚本对象(IActiveScript), 一般装了IE的电脑上应该都有JScript和VBScript。
2. 告诉IActiveScript谁来解释脚本中的对象(IActiveScript ::SetScriptSite)
3. 告诉IActiveScript脚本里会用到哪些对象(用IActiveScript ::AddNamedItem)
4. 装入脚本(JScript或VBScript代码,UNICODE格式)
5. 运行脚本(通过设置IActiveScript ::SetScriptState实现)
6. [AS]在运行脚本过程中如果遇到第3步里告诉它的对象,它就会向我们要此对象的接口以便继续执行(它会调用IActiveScriptSite::GetItemInfo,第2步里告诉它的)。
7. 打完收工。当然也可以强制停下运行中的脚本(比如不小心编了一个死循环的脚本)。也是通过设置IActiveScript ::SetScriptState实现。
从上面可以看出,我们的主要工作是实现脚本里的对象的解释工作。在COM编程中,毫无悬念地,这个光荣而又艰巨的任务就又落到了IDispatch身上。IDispatch的生平事迹咱就不介绍了,不明白的去问明白的,都不明白的去Google,心急的可以看后面的示例代码。
对我们编程的来说,说再多也不如源代码来得直接有效,下面我们就来做一个支持脚本的小程序。这里我们使用BCB来做,其它如VC,GCC当然也行,不过对于快速原型开发方面,BCB绝对是不二选择(广告时间)。
先看偶写的一个JScript脚本:
1. var bForward = true;
2. for(var i=0;i<ScreenWidth-200;i+=100)
3. {
4. for(var j=0;j<ScreenHeight-200;j+=10)
5. {
6. var x = i;
7. var y = bForward? j : (ScreenHeight-200-j)
8. MyWin.MoveTo(x,y);
9. MyWin.Caption = "X:" + x + " Y:"+y;
10. MyWin.Color = (x<<16|y)&0xffffff;
11. Sleep(10);
12. }
13. bForward = !bForward;
14. }
此脚本的目的是让一个叫MyWin的窗口从左到右地上下移动,同时改变标题和颜色。
前面说了MS只帮我们解决脚本语言上的问题,我们来处理对象和函数。在这个脚本里,我们的任务有:ScreenWidth、ScreenHeight、Sleep、MyWin对象以及它的方法属性。
这里还得说一下IDispatch的调用过程:比如当[AS] 执行到MyWin.MoveTo(x,y);时,它先得到MyWin的IDispatch接口(它是怎么得到的?后面会讲先按下不提),然后调用IDispatch的GetIDsOfNames(riid,L"MoveTo",1,lcid,&rgDispId)获得MoveTo对应的"Member Id"(rgDispId参数)。 再用这个"Member Id"去调用Invoke(rgDispId,...)。所以我们只需关注GetIDsOfNames和Invoke两个方法即可。
先编写一个TMyGlobalFunc来处理ScreenWidth、ScreenHeight、Sleep这三个全局函数:
1. struct TMyGlobalFunc : TDispatch{
2. enum {itemScreenWidth,itemScreenHeight,itemSleep};
3. static wchar_t Name[];
4. STDMETHOD(GetIDsOfNames)(REFIID riid,LPOLESTR *rgszNames,UINT cNames,LCID lcid,
5. DISPID *rgDispId){
6. if(lstrcmpW(rgszNames[0],L"ScreenWidth") == 0){
7. *rgDispId = itemScreenWidth;
8. }
9. else if(lstrcmpW(rgszNames[0],L"ScreenHeight") == 0) {
10. *rgDispId = itemScreenHeight;
11. }
12. else if(lstrcmpW(rgszNames[0],L"Sleep") == 0) {
13. *rgDispId = itemSleep;
14. }
15. else
16. return E_NOTIMPL;
17. return S_OK;
18. }
19.
20. STDMETHOD(Invoke)(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,
21. DISPPARAMS *pDispParams,
22. VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr){
23. switch(dispIdMember)
24. {
25. case itemScreenWidth:
26. pVarResult->vt=VT_I4;
27. pVarResult->intVal = Screen->Width;
28. break;
29. case itemScreenHeight:
30. pVarResult->vt=VT_I4;
31. pVarResult->intVal = Screen->Height;
32. break;
33. case itemSleep:
34. if(pDispParams->cArgs!=1) return DISP_E_BADPARAMCOUNT;
35. if(pDispParams->rgvarg[0].vt != VT_I4) {
36. *puArgErr = 0;
37. return DISP_E_TYPEMISMATCH;
38. }
39. Application->ProcessMessages();
40. Sleep(pDispParams->rgvarg[0].intVal);
41. break;
42. default:
43. return DISP_E_MEMBERNOTFOUND;
44. }
45. return S_OK;
46. }
47. };
48. wchar_t TMyGlobalFunc::Name[]=L"MyGlobalFunc";
再写个TMyWin来处理MyWin对象的方法和属性,和上面一样
1. struct TMyWin : TDispatch{
2. enum {itemCaption,itemColor,itemMoveTo};
3. static wchar_t Name[];
4. // IDispatch
5. STDMETHOD(GetIDsOfNames)(REFIID riid,LPOLESTR *rgszNames,UINT cNames,LCID lcid,
6. DISPID *rgDispId){
7. if(lstrcmpW(rgszNames[0],L"Caption") == 0) {
8. *rgDispId = itemCaption;
9. }
10. else if(lstrcmpW(rgszNames[0],L"Color") == 0) {
11. *rgDispId = itemColor;
12. }
13. else if(lstrcmpW(rgszNames[0],L"MoveTo") == 0) {
14. *rgDispId = itemMoveTo;
15. }
16. else
17. return E_NOTIMPL;
18. return S_OK;
19. }
20.
21. STDMETHOD(Invoke)(DISPID dispIdMember,REFIID riid,LCID lcid,WORD wFlags,
22. DISPPARAMS *pDispParams,
23. VARIANT *pVarResult,EXCEPINFO *pExcepInfo,UINT *puArgErr){
24. switch(dispIdMember)
25. {
26. case itemCaption:
27. {
28. if(wFlags==DISPATCH_PROPERTYGET)
29. {
30. pVarResult->vt = VT_BSTR;
31. pVarResult->bstrVal = GetCaption();
32. }
33. else if(wFlags==DISPATCH_PROPERTYPUT)
34. {
35. if(pDispParams->cArgs!=1) return DISP_E_BADPARAMCOUNT;
36. if(pDispParams->rgvarg[0].vt != VT_BSTR) {
37. *puArgErr = 0;
38. return DISP_E_TYPEMISMATCH;
39. }
40. SetCaption(pDispParams->rgvarg[0].bstrVal);
41. }
42. else
43. return DISP_E_MEMBERNOTFOUND;
44. break;
45. }
46. case itemColor:
47. {
48. if(wFlags==DISPATCH_PROPERTYGET)
49. {
50. pVarResult->vt = VT_I4;
51. pVarResult->intVal = GetColor();
52. }
53. else if(wFlags==DISPATCH_PROPERTYPUT)
54. {
55. if(pDispParams->cArgs!=1) return DISP_E_BADPARAMCOUNT;
56. if(pDispParams->rgvarg[0].vt != VT_I4) {
57. *puArgErr = 0;
58. return DISP_E_TYPEMISMATCH;
59. }
60. SetColor(pDispParams->rgvarg[0].intVal);
61. }
62. else
63. return DISP_E_MEMBERNOTFOUND;
64. break;
65. }
66. case itemMoveTo:
67. {
68. if(wFlags==DISPATCH_METHOD)
69. {
70. if(pDispParams->cArgs!=2) return DISP_E_BADPARAMCOUNT;
71. if(pDispParams->rgvarg[0].vt != VT_I4) {
72. *puArgErr = 0;
73. return DISP_E_TYPEMISMATCH;
74. }
75. else if(pDispParams->rgvarg[1].vt != VT_I4){
76. *puArgErr = 1;
77. return DISP_E_TYPEMISMATCH;
78. }
79. MoveTo( pDispParams->rgvarg[1].intVal,
80. pDispParams->rgvarg[0].intVal);
81. }
82. else
83. return DISP_E_MEMBERNOTFOUND;
84. break;
85. }
86. default:
87. return DISP_E_MEMBERNOTFOUND;
88. }
89. return S_OK;
90. }
91.
92. // TMyWin上所有的方法属性都对这个TForm *fm_Opt操作
93. TMyWin(TForm *fm_Opt) : TDispatch(),m_Form(fm_Opt){ ; }
94.
95. BSTR GetCaption(){
96. return WideString(m_Form->Caption).Detach();
97. }
98. void SetCaption(BSTR bstrCaption){
99. m_Form->Caption = bstrCaption;
100. }
101. int GetColor(){
102. return (int)m_Form->Color;
103. }
104. void SetColor(int iColor){
105. m_Form->Color = TColor(iColor);
106. }
107.
108. void MoveTo(int X,int Y){
109. m_Form->Left=X;
110. m_Form->Top=Y;
111. }
112.
113. private:
114. TForm *m_Form;
115. };
116. wchar_t TMyWin::Name[]=L"MyWin";
主要任务完成,接着我们要实现IActiveScriptSite,它用于上面所说的第2步和第6步。[AS]解析上面的脚本时遇到MyWin及ScreenWidth、ScreenHeight、Sleep时会通过GetItemInfo向它要接口,它则负责把我们刚才写的IDispatch喂给[AS]。
这个IActiveScriptSite要包含头文件#include <activscp.h>
1. struct TMyActiveScriptSite
2. : IActiveScriptSite
3. {
4. TMyActiveScriptSite(TMyWin *pMyWin,TMyGlobalFunc *pMyGlobalFunc)
5. : m_iRefCount(1),m_pMyWin(pMyWin),m_pMyGlobalFunc(pMyGlobalFunc){
6. }
7.
8. // IUNKnown,不得不写
9. HRESULT __stdcall QueryInterface(REFIID iid, void **ppv){
10. if(iid==IID_IUnknown||iid==IID_IActiveScriptSite)
11. {
12. *ppv=this;
13. AddRef();
14. return S_OK;
15. }
16. *ppv=NULL;
17. return E_NOINTERFACE;
18. }
19. ULONG __stdcall AddRef(void) {
20. return ++m_iRefCount;
21. }
22. ULONG __stdcall Release(void) {
23. if(--m_iRefCount==0){
24. delete this;
25. return 0;
26. }
27. return m_iRefCount;
28. }
29.
30. // IActiveScriptSite
31. STDMETHOD(GetLCID)(LCID* /**//*plcid*/) {
32. return E_NOTIMPL;
33. }
34.
35. //主要的就是这个
36. STDMETHOD(GetItemInfo)(LPCOLESTR pstrName,
37. DWORD dwReturnMask,
38. IUnknown** ppiunkItem,
39. ITypeInfo** ppti) {
40. if( (dwReturnMask & SCRIPTINFO_ITYPEINFO)!=0 ){
41. *ppti = NULL;
42. return E_FAIL;
43. }
44. if( (dwReturnMask & SCRIPTINFO_IUNKNOWN)==0 ) return E_FAIL;
45. if( ppiunkItem==NULL ) return E_POINTER;
46. *ppiunkItem = NULL;
47. if( lstrcmpW( pstrName, TMyWin::Name )==0 ) {
48. //[AS]要MyWin,送上!
49. m_pMyWin->AddRef();
50. *ppiunkItem = m_pMyWin;
51. return S_OK;
52. }
53. else if( lstrcmpW( pstrName, TMyGlobalFunc::Name )==0 ) {
54. //[AS]要MyGlobalFunc,送上!
55. m_pMyGlobalFunc->AddRef();
56. *ppiunkItem = m_pMyGlobalFunc;
57. return S_OK;
58. }
59. return E_FAIL; //要其它的,没有!
60. }
61.
62. STDMETHOD(GetDocVersionString)(BSTR* pbstrVersion) {
63. if( pbstrVersion==NULL ) return E_POINTER;
64. *pbstrVersion = ::SysAllocString(OLESTR("Script 1.0"));
65. return S_OK;
66. }
67.
68. STDMETHOD(OnScriptTerminate)(
69. const VARIANT* /**//*pvarResult*/,
70. const EXCEPINFO* /**//*pexcepinfo*/) {
71. return S_OK;
72. }
73.
74. STDMETHOD(OnStateChange)(SCRIPTSTATE /**//*ssScriptState*/) {
75. return S_OK;
76. }
77. // 脚本里有错误时会调用 OnScriptError
78. STDMETHOD(OnScriptError)(IActiveScriptError* pScriptError) {
79. EXCEPINFO e;
80. DWORD dwContext;
81. ULONG ulLine;
82. LONG lPos;
83. pScriptError->GetExceptionInfo(&e);
84. pScriptError->GetSourcePosition(&dwContext, &ulLine, &lPos);
85. char *pstrFormat = "An error occured while parsing script:"
86. " Source: %ws Error: %08X Description: %ws Line: %d";
87. char pstrStr[1024];
88. ::wsprintf( pstrStr, pstrFormat,
89. e.bstrSource,
90. e.scode,
91. e.bstrDescription,
92. ulLine+1);
93. ::MessageBox(::GetActiveWindow(), pstrStr,
94. _T("Compile Error"), MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND);
95. return S_OK;
96. }
97.
98. STDMETHOD(OnEnterScript)() {
99. return S_OK;
100. }
101.
102. STDMETHOD(OnLeaveScript)() {
103. return S_OK;
104. }
105. private:
106. TMyWin *m_pMyWin;
107. TMyGlobalFunc *m_pMyGlobalFunc;
108. int m_iRefCount;
109. };
在自己的程序中加入脚本支持(一)
最新推荐文章于 2020-10-10 11:53:01 发布