ActiveX指令碼宿主

來源:互聯網
上載者:User
    本文討論如何使用MFC實現ActiveX指令碼宿主.
    
    一個指令碼宿主就是一個能運行其它指令碼語言(VB指令碼,VBA,Java指令碼)並且能將應用程式內部的COM介面暴露給這些指令碼的應用程式.微軟的IE瀏覽器,Excel,Word,Access都是指令碼宿主.IE用的是VB指令碼,而其它的則是用的VBA.

    大部分關於如何?指令碼宿主的代碼來自於Developer Studio光碟片中的"Axscript"範例.該範例是用WIN32 API實現的.這裡準備了三個工程來說明用MFC如何?.

    第一步: 一個使你的MFC程式支援VB指令碼的例子
    第二步: 暴露內部Automation 物件到VB指令碼引擎
    第三步: 添加VB指令碼代碼來控制由C++代碼觸發的事件

    將三個工程一個個編譯運行,看看它們是如何工作的.運行程式後,選擇菜單"VBScript->Load script file",會提示你載入一個指令檔,選擇工程目錄下的"TestProgram.txt"檔案,然後選擇"VBScript->Run"菜單來運行指令碼.

    第一個範例會顯示一個訊息對話方塊,每二個則繪製一些矩形,第三個會允許你會滑鼠拖動這些矩形.

    注意:如果你的工程設定所有的編譯結果放在同一目錄,切換工程時不要忘記使用"Rebuild All"命令重新編譯.

    要理解ActiveX指令碼引擎是如何工作的請參考MSDN中的協助,你可以在ActiveX相關協助中的ActiveX scripting中找到.

    第一步:

    為了讓你的程式運行VB指令碼(或其它指令碼)你必須通過IActiveScript和IActiveScriptParse介面與指令碼引擎打交道.而且你的程式必須向IActiveScriptSite暴露對象,可能同時還要暴露給IActiveScriptSiteWindow.

    第一步的範例有一個CMainFrame對象,它同時支援IActiveScript和IActiveScriptParse介面.大部分的IActiveScript方法在該範例中沒有實現.要理解如何在MFC中實現OLE介面,請參考相關文檔.

    第二步:

    這一步要用類嚮導產生兩個Automation 物件.第一個對象CAXHostDoc由CDocument派生.第二個對象CAXSprite由CCmdTarget派生.CAXHostDoc有一個叫做Animation的對象,它能夠管理子圖形.CAXSprite是矩形子圖形的一個OLE封裝.子圖形能夠添加到Animation對象,能夠被刪除,移動,改變...,但是Animation對象什麼也不做,直到你要求它繪製映像到特定的DC.不要管它到底是如何?的.

    你的程式可以在指令碼引擎中加入頂級的條目名稱,它將在指令碼中有效.這些條目名稱應該與你的應用程式中的某些COM對象相對應.通常我們使用"Application"作為頂級對象.在這個樣本中,是一個SDI應用,CAXHostApp對象有一個m_pDoc成員儲存一個指向文檔的指標.在CMainFrame::CreateScriptEngine方法中,"Document"對象名稱被加入到指令碼引擎.

const TCHAR* szItemName=_T("Document");USES_CONVERSION;LPCOLESTRlpostrApp = T2COLE(szItemName);
hr=m_pIActiveScript->AddNamedItem(lpostrApp, SCRIPTITEM_ISVISIBLE);
COM對象與添加的條目名稱間的關聯就是在IActiveScriptSite::GetItemInfo方法中進行的.



STDMETHODIMP CMainFrame::XScriptSite::GetItemInfo
(
LPCOLESTR   pstrName,
DWORD       dwReturnMask,
IUnknown**  ppunkItemOut,
ITypeInfo** pptinfoOut
)
{
//HRESULT hr;
METHOD_PROLOGUE(CMainFrame, ScriptSite)
USES_CONVERSION;
LPCTSTR lpstrApplication=szItemName;
if (dwReturnMask & SCRIPTINFO_ITYPEINFO)
{
if (!pptinfoOut)
return E_INVALIDARG;
*pptinfoOut = NULL;
}
if (dwReturnMask & SCRIPTINFO_IUNKNOWN)
{
if (!ppunkItemOut)
return E_INVALIDARG;
*ppunkItemOut = NULL;
LPTSTR lpszName = OLE2T(pstrName);
//請注意這裡
if(!_tcsicmp(lpstrApplication, lpszName))
{
*ppunkItemOut = theApp.m_pDoc->GetIDispatch(TRUE);
return S_OK;
////////////////////////////
}
}
return TYPE_E_ELEMENTNOTFOUND;
}

    一旦這種關聯被建立,你可以使用文檔的方法與屬性.如果你的應用中有一些方法返回介面到其它COM對象,你可以使用它們的方法與屬性而不需要調用AddNamedItem.它們不是頂層對象,它們是由Document對象獲得的(本例中只有一個對象-CAXSprite).

第三步:

    

    來源程式下載
Step 3

In this example we make our program able to fire some events which can be handled by the VBScript engine. First of all we will need to provide ITypeInfo information to the script engine. The application object has some members to hold this information (you can use global variables).

ITypeLib*pITypeLib;    //library AXHost


ITypeInfo*ptinfoClsDoc; //coclass Document
ITypeInfo*ptinfoIntDoc; //dispinterface IAXHost
ITypeInfo*ptinfoClsSprite; //coclass Sprite
ITypeInfo*ptinfoIntSprite;//dispinterface IAXSprite

To make your object fire events you need to create another interface to your object's events. Modify your ODL file.

Example:

[uuid(B6032E41-636A-11d1-8F0B-F54176DCF130)]dispinterface  IAXHostEvents{properties:methods: // Events[id(1)]  void OnMouseDown([in] short x, [in] short y);[id(2)]  void OnMouseMove([in] short x, [in] short y);[id(3)]  void OnMouseUp([in] short x, [in] short y);[id(4)]  void OnMouseDblClk([in] short x, [in] short y);}//  Class information for CAXHostDoc[ uuid(34E25943-6314-11D1-8F0B-F54176DCF130) ]coclass Document{[default] dispinterface IAXHost;[default, source]  dispinterface IAXHostEvents;};

In the CAXHostApp::InitInstance() method the type information was loaded

// ##### BEGIN ACTIVEX SCRIPTING SUPPORT #####const TCHAR* lpstrTLB=_T("AXHost.tlb");if(S_OK!=LoadTypeInfo(lpstrTLB,0,1,0,0,IID_LIBAXHost,IID_IAXHostClass,IID_IAXHost,FALSE,&pITypeLib,&ptinfoClsDoc,&ptinfoIntDoc))return FALSE;if(S_OK!=LoadTypeInfo(lpstrTLB,0,1,0,0,IID_LIBAXHost,IID_IAXSpriteClass,IID_IAXSprite,FALSE,&pITypeLib,&ptinfoClsSprite,&ptinfoIntSprite))return FALSE;// #####  END  ACTIVEX SCRIPTING SUPPORT #####

In the MainFrm.module:

The AddNamedItem was called with the SCRIPTITEM_ISSOURCE flag.

pThis->m_pIActiveScript->AddNamedItem(lpszT, SCRIPTITEM_ISVISIBLE|SCRIPTITEM_ISSOURCE);

In the IActiveScriptSite::GetItemInfo ITypeInfo interface to the Document CLASS object has been provided to the scripting engine.

STDMETHODIMP CMainFrame::XScriptSite::GetItemInfo(LPCOLESTR   pstrName,DWORD       dwReturnMask,IUnknown**  ppunkItemOut,ITypeInfo** pptinfoOut){//HRESULT hr;METHOD_PROLOGUE(CMainFrame, ScriptSite)USES_CONVERSION;LPCTSTR lpstrApplication=szItemName;if (dwReturnMask & SCRIPTINFO_ITYPEINFO){if (!pptinfoOut)return E_INVALIDARG;*pptinfoOut = NULL;//Attention, pleaseLPTSTR lpszName = OLE2T(pstrName);if(!_tcsicmp(lpstrApplication, lpszName)){*pptinfoOut=theApp.ptinfoClsDoc;return S_OK;}////////////////////}if (dwReturnMask & SCRIPTINFO_IUNKNOWN){if (!ppunkItemOut)return E_INVALIDARG;*ppunkItemOut = NULL;//Attention, pleaseLPTSTR lpszName = OLE2T(pstrName);if(!_tcsicmp(lpstrApplication, lpszName)){*ppunkItemOut = theApp.m_pDoc->GetIDispatch(TRUE);return S_OK;}///////////////////}return TYPE_E_ELEMENTNOTFOUND;}Also the IActiveScriptSite::RequestTypeLibs method was implemented.//---------------------------------------------------------------------------// //---------------------------------------------------------------------------STDMETHODIMP CMainFrame::XScriptSite::RequestTypeLibs(void){METHOD_PROLOGUE(CMainFrame,ScriptSite);HRESULT hr=pThis->m_pIActiveScript->AddTypeLib(IID_LIBAXHost, 1, 0, 0);return hr;}

If you want your object support events, it must support IConnectionPointContainer interface and IProvideTypeInfo interface. See the article about CConnectionPoint MFC object. See how it was implemented with the CAXHostDoc object.

If you implement all the interfaces correctly, the scripting engine will establish a connection to your object and you will be able to fire events with help of a small function.

void CAXHostDoc::FireEvent(int iEvNum,VARIANTARG* pVar,int nParameters){const CPtrArray* pConnections = m_xConnPt.GetConnections();ASSERT(pConnections != NULL);int cConnections = pConnections->GetSize();IDispatch* pSink;for (int i = 0; i < cConnections; i++){pSink = (IDispatch*)(pConnections->GetAt(i));ASSERT(pSink != NULL);InvokeEvent(pSink, iEvNum, pVar, nParameters);}}

The iEvNum must be the same as in the ODL file. To fire the MouseDown event, for instance, i have to use iEvNum to be equal 1. Because [id(1)] void OnMouseDown([in] short x, [in] short y);

I should have made constants like IDEVENT_MOUSEDOWN and use them both in the ODL file and in my C++ code.

Example:

void CAXHostView::OnLButtonDown(UINT nFlags, CPoint point){// TODO: Add your message handler code here and/or call defaultSetCapture();VARIANTARG  var[2];VariantInit(&var[1]);var[1].vt = VT_I2;var[1].iVal = (short)point.x;VariantInit(&var[0]);var[0].vt = VT_I2;var[0].iVal = (short)point.y;GetDocument()->FireEvent(1,var,2);CView::OnLButtonDown(nFlags, point);}

Observe the sequence of passing parameters to the variant array.

I am not sure that in this small description i have written all the tips to make your program work, but i send you the code which you can investigate. And you should alse see the "Axscript" example which does a little bit more. It provides a way how to make subclassing with VBScript. See the BuildTypeInfo function in the example to see how it has been done there.

If you have any questions, you can send me a message and i will try to answer. But i must confess that i am a novice in COM. COM is not difficult to understand but there are so many different objects and interfaces. And one must know how to use them in one's programs. The same can be said about the C++ language itself.

I also would like to get some examples on this matter if you have them. For example, i don't know how to implement debugging features (they say that Microsoft has already made a debugger fo VBScript), how to interrupt a running script (i think i should create an another thread and use an appropriate function), i tried to implement DUAL interfaces to my objects but the VBScript used just the IDispatch interfaces and i thought that VBScript did not support DUAL interfaces.

If you have any clues concerning this technology, please send them to me. I would like also your opinion whether this feature is interesting, for i can send some more examples. I mean not this sprites but a way of using this technology in business applications. For example, my program use special kinds of objects to work with SQL server databases and Access databases such as "recordsets", "idsets, "caches", "reports", "views" and others.

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.