在自己的程序中加入脚本支持(一)

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. };

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值