Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    text
    copied!<p>I do not recommend using <a href="http://msdn.microsoft.com/en-us/library/aa752127(v=vs.85).aspx" rel="noreferrer"><code>IWebBrowser2</code></a> to acquire an interface to a scripting host. Creating a browser object has the nasty side effect of launching an instance of Internet Explorer in the background, at least on Windows 7. It also pulls in several named objects (document, window, etc) that may conflict with what you are trying to accomplish.</p> <p>Instead I suggest you use the <a href="http://msdn.microsoft.com/en-us/library/ccd0zt2w(v=vs.94).aspx" rel="noreferrer">Active Scripting Interfaces</a>. Using them requires a bit of extra code but it is far more flexible and gives you a lot more control. Specifically you can set the scripting control <em>site</em> without having to worry about conflicts or creating a forwarding adapter that would be required if you used the <code>IWebBrowser2</code> controls. Even though it requires a bit of extra code, it's not difficult to implement.</p> <p>The first step is to acquire the <code>GUID</code> for the particular language you want to use. This can be Javascript, VBScript, Python or any scripting engine designed for Windows Scripting Host. You can either supply the <code>GUID</code> yourself or acquire it from the system by name using a COM Application ID like below.</p> <pre><code>GUID languageId; CLSIDFromProgID(L"JavaScript" , &amp;languageId); </code></pre> <p>The next step is to create an instance of the scripting engine by calling <code>CoCreateInstance</code> using the GUID from above as the class id.</p> <pre><code>CoCreateInstance( languageId, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, reinterpret_cast&lt;void**&gt;(&amp;scriptEngine_)); </code></pre> <p>This will return a pointer to an <a href="http://msdn.microsoft.com/en-us/library/ky29ffxd(v=vs.94).aspx" rel="noreferrer">IActiveScript</a> object that is the primary interface to the scripting engine.</p> <p>The next step is to set the scripting site. This is an implementation of <a href="http://msdn.microsoft.com/en-us/library/a3a8btht(v=vs.94).aspx" rel="noreferrer">IActiveScriptSite</a> that you provide. This is the interface the scripting engine uses to acquire certain information and dispatch certain events such as changes in the scripting state.</p> <pre><code>scriptEngine_-&gt;SetScriptSite(siteObjectPointer); </code></pre> <p>At this point you have all the necessary objects to begin using the scripting engine to call scripting functions <em>and</em> allow the script to access native C++ objects. To add objects such as <code>window</code> or <code>document</code> to the root "namespace" you call <a href="http://msdn.microsoft.com/en-us/library/s8eyc3sh(v=vs.94).aspx" rel="noreferrer">IActiveScript::AddNamedItem</a> or <a href="http://msdn.microsoft.com/en-us/library/4hb95zwx(v=vs.94).aspx" rel="noreferrer">IActiveScript::AddTypeLib</a>.</p> <pre><code>scriptEngine_-&gt;AddNamedItem( "objectname", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_NOCODE); </code></pre> <p>Objects that you want to expose to the scripts must implement either <a href="http://msdn.microsoft.com/en-us/library/windows/desktop/dd318520(v=vs.85).aspx" rel="noreferrer"><code>IDispatch</code></a> or <a href="http://msdn.microsoft.com/en-us/library/sky96ah7(v=vs.94).aspx" rel="noreferrer"><code>IDispatchEx</code></a>. Which interface you implement depends on the requirements of the object but if possible use IDispatch as it requires less code.</p> <p>Now that you have initialized the scripting engine you can start adding scripts to it. You need to call <code>QueryInterface</code> on the scripting engine to acquire a pointer to the <a href="http://msdn.microsoft.com/en-us/library/f2822wbt(v=vs.94).aspx" rel="noreferrer"><code>IActiveScriptParse</code></a> interface, initialize the parser and then add scripts. You only need to initialize the parser once.</p> <pre><code>scriptEngine_-&gt;QueryInterface( IID_IActiveScriptParse, reinterpret_cast&lt;void **&gt;(&amp;parser)); // Initialize the parser parser-&gt;InitNew(); parser-&gt;ParseScriptText("script source" ....); </code></pre> <p>Now that you have initialized the engine and added a script or two you start execution by changing the engine state to <a href="http://msdn.microsoft.com/en-us/library/f7z7cxxa(v=vs.94).aspx" rel="noreferrer"><code>SCRIPTSTATE_CONNECTED</code></a>.</p> <pre><code>scriptEngine_-&gt;SetScriptState(SCRIPTSTATE_CONNECTED); </code></pre> <p>Those are the basic steps to embed the scripting engine into your application. The example below is a fully working skeleton that implements the scripting site and an object that exposes functionality to the script. It also includes a slightly modified version of the code to call script functions that you referenced in your question. It's not <em>perfect</em> but it works and should hopefully get you closer to what you are trying to accomplish.</p> <p><code>BasicScriptHost.h</code></p> <pre><code>#ifndef BASICSCRIPTHOST_H_ #define BASICSCRIPTHOST_H_ #include &lt;string&gt; #include &lt;vector&gt; #include &lt;activscp.h&gt; #include &lt;comdef.h&gt; #include &lt;atlbase.h&gt; class BasicScriptObject; class BasicScriptHost : public IActiveScriptSite { public: typedef IActiveScriptSite Interface; // Constructor to explicit BasicScriptHost(const GUID&amp; languageId); BasicScriptHost( const GUID&amp; languageId, const std::wstring&amp; objectName, CComPtr&lt;IDispatch&gt; object); virtual ~BasicScriptHost(); // Implementation of IUnknown virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void ** object); virtual ULONG STDMETHODCALLTYPE AddRef (); virtual ULONG STDMETHODCALLTYPE Release(); // Implementation of IActiveScriptSite virtual HRESULT STDMETHODCALLTYPE GetLCID(DWORD *lcid); virtual HRESULT STDMETHODCALLTYPE GetDocVersionString(BSTR* ver); virtual HRESULT STDMETHODCALLTYPE OnScriptTerminate(const VARIANT *,const EXCEPINFO *); virtual HRESULT STDMETHODCALLTYPE OnStateChange(SCRIPTSTATE state); virtual HRESULT STDMETHODCALLTYPE OnEnterScript(); virtual HRESULT STDMETHODCALLTYPE OnLeaveScript(); virtual HRESULT STDMETHODCALLTYPE GetItemInfo( const WCHAR* name, DWORD req, IUnknown** obj, ITypeInfo** type); virtual HRESULT STDMETHODCALLTYPE OnScriptError(IActiveScriptError *err); // Our implementation virtual HRESULT Initialize(); virtual HRESULT Parse(const std::wstring&amp; script); virtual HRESULT Run(); virtual HRESULT Terminate(); virtual _variant_t CallFunction( const std::wstring&amp; strFunc, const std::vector&lt;std::wstring&gt;&amp; paramArray); private: ULONG refCount_; protected: CComPtr&lt;IActiveScript&gt; scriptEngine_; CComPtr&lt;IDispatch&gt; application_; }; #endif // BASICSCRIPTHOST_H_ </code></pre> <p><code>BasicScriptHost.cpp</code></p> <pre><code>#include "BasicScriptHost.h" #include "BasicScriptObject.h" #include &lt;stdexcept&gt; #include &lt;atlbase.h&gt; BasicScriptHost::BasicScriptHost( const GUID&amp; languageId, const std::wstring&amp; objectName, CComPtr&lt;IDispatch&gt; object) : refCount_(1) { CComPtr&lt;IActiveScript&gt; engine; HRESULT hr = CoCreateInstance( languageId, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, reinterpret_cast&lt;void**&gt;(&amp;engine)); if(FAILED(hr) || engine == nullptr) { throw std::runtime_error("Unable to create active script object"); } hr = engine-&gt;SetScriptSite(this); if(FAILED(hr)) { throw std::runtime_error("Unable to set scripting site"); } hr = engine-&gt;AddNamedItem( objectName.c_str(), SCRIPTITEM_ISVISIBLE | SCRIPTITEM_NOCODE); if(FAILED(hr)) { throw std::runtime_error("Unable to set application object"); } // Done, set the application object and engine application_ = object; scriptEngine_ = engine; } BasicScriptHost::BasicScriptHost(const GUID&amp; languageId) : refCount_(1) { CComPtr&lt;IActiveScript&gt; engine; HRESULT hr = CoCreateInstance( languageId, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, reinterpret_cast&lt;void**&gt;(&amp;engine)); if(FAILED(hr) || engine == nullptr) { throw std::runtime_error("Unable to create active script object"); } hr = engine-&gt;SetScriptSite(this); if(FAILED(hr)) { throw std::runtime_error("Unable to set scripting site"); } // Done, set the engine scriptEngine_ = engine; } BasicScriptHost::~BasicScriptHost() { } HRESULT BasicScriptHost::QueryInterface(REFIID riid,void ** object) { if(riid == IID_IActiveScriptSite) { *object = this; } if(riid == IID_IDispatch) { *object = reinterpret_cast&lt;IDispatch*&gt;(this); } else { *object = nullptr; } if(*object != nullptr) { AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG BasicScriptHost::AddRef() { return ::InterlockedIncrement(&amp;refCount_); } ULONG BasicScriptHost::Release() { ULONG oldCount = refCount_; ULONG newCount = ::InterlockedDecrement(&amp;refCount_); if(0 == newCount) { delete this; } return oldCount; } HRESULT BasicScriptHost::GetLCID(DWORD *lcid) { *lcid = LOCALE_USER_DEFAULT; return S_OK; } HRESULT BasicScriptHost::GetDocVersionString(BSTR* ver) { *ver = nullptr; return S_OK; } HRESULT BasicScriptHost::OnScriptTerminate(const VARIANT *,const EXCEPINFO *) { return S_OK; } HRESULT BasicScriptHost::OnStateChange(SCRIPTSTATE state) { return S_OK; } HRESULT BasicScriptHost::OnEnterScript() { return S_OK; } HRESULT BasicScriptHost::OnLeaveScript() { return S_OK; } HRESULT BasicScriptHost::GetItemInfo( const WCHAR* /*name*/, DWORD req, IUnknown** obj, ITypeInfo** type) { if(req &amp; SCRIPTINFO_IUNKNOWN &amp;&amp; obj != nullptr) { *obj = application_; if(*obj != nullptr) { (*obj)-&gt;AddRef(); } } if(req &amp; SCRIPTINFO_ITYPEINFO &amp;&amp; type != nullptr) { *type = nullptr; } return S_OK; } HRESULT BasicScriptHost::Initialize() { CComPtr&lt;IActiveScriptParse&gt; parse; HRESULT hr = scriptEngine_-&gt;QueryInterface( IID_IActiveScriptParse, reinterpret_cast&lt;void **&gt;(&amp;parse)); if(FAILED(hr) || parse == nullptr) { throw std::runtime_error("Unable to get pointer to script parsing interface"); } // Sets state to SCRIPTSTATE_INITIALIZED hr = parse-&gt;InitNew(); return hr; } HRESULT BasicScriptHost::Run() { // Sets state to SCRIPTSTATE_CONNECTED HRESULT hr = scriptEngine_-&gt;SetScriptState(SCRIPTSTATE_CONNECTED); return hr; } HRESULT BasicScriptHost::Terminate() { HRESULT hr = scriptEngine_-&gt;SetScriptState(SCRIPTSTATE_DISCONNECTED); if(SUCCEEDED(hr)) { hr = scriptEngine_-&gt;Close(); } return hr; } HRESULT BasicScriptHost::Parse(const std::wstring&amp; source) { CComPtr&lt;IActiveScriptParse&gt; parser; HRESULT hr = scriptEngine_-&gt;QueryInterface( IID_IActiveScriptParse, reinterpret_cast&lt;void **&gt;(&amp;parser)); if(FAILED(hr) || parser == nullptr) { throw std::runtime_error("Unable to get pointer to script parsing interface"); } hr = parser-&gt;ParseScriptText( source.c_str(), nullptr, nullptr, nullptr, 0, 0, 0, nullptr, nullptr); return hr; } #include &lt;iostream&gt; HRESULT BasicScriptHost::OnScriptError(IActiveScriptError *err) { EXCEPINFO e; err-&gt;GetExceptionInfo(&amp;e); std::wcout &lt;&lt; L"Script error: "; std::wcout &lt;&lt; (e.bstrDescription == nullptr ? L"unknown" : e.bstrDescription); std::wcout &lt;&lt; std::endl; std::wcout &lt;&lt; std::hex &lt;&lt; L"scode = " &lt;&lt; e.scode &lt;&lt; L" wcode = " &lt;&lt; e.wCode &lt;&lt; std::endl; return S_OK; } _variant_t BasicScriptHost::CallFunction( const std::wstring&amp; strFunc, const std::vector&lt;std::wstring&gt;&amp; paramArray) { CComPtr&lt;IDispatch&gt; scriptDispatch; scriptEngine_-&gt;GetScriptDispatch(nullptr, &amp;scriptDispatch); //Find dispid for given function in the object CComBSTR bstrMember(strFunc.c_str()); DISPID dispid = 0; HRESULT hr = scriptDispatch-&gt;GetIDsOfNames( IID_NULL, &amp;bstrMember, 1, LOCALE_SYSTEM_DEFAULT, &amp;dispid); if(FAILED(hr)) { throw std::runtime_error("Unable to get id of function"); } // Putting parameters DISPPARAMS dispparams = {0}; const int arraySize = paramArray.size(); dispparams.cArgs = arraySize; dispparams.rgvarg = new VARIANT[dispparams.cArgs]; dispparams.cNamedArgs = 0; for( int i = 0; i &lt; arraySize; i++) { // FIXME - leak _bstr_t bstr = paramArray[arraySize - 1 - i].c_str(); // back reading dispparams.rgvarg[i].bstrVal = bstr.Detach(); dispparams.rgvarg[i].vt = VT_BSTR; } //Call JavaScript function EXCEPINFO excepInfo = {0}; _variant_t vaResult; UINT nArgErr = (UINT)-1; // initialize to invalid arg hr = scriptDispatch-&gt;Invoke( dispid, IID_NULL, 0, DISPATCH_METHOD, &amp;dispparams, &amp;vaResult, &amp;excepInfo, &amp;nArgErr); delete [] dispparams.rgvarg; if(FAILED(hr)) { throw std::runtime_error("Unable to get invoke function"); } return vaResult; } </code></pre> <p><code>BasicScriptObject.h</code></p> <pre><code>#ifndef BASICSCRIPTOBJECT_H_ #define BASICSCRIPTOBJECT_H_ #include "stdafx.h" #include &lt;string&gt; #include &lt;comdef.h&gt; class BasicScriptObject : public IDispatch { public: BasicScriptObject(); virtual ~BasicScriptObject(); // IUnknown implementation HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid,void ** object); ULONG STDMETHODCALLTYPE AddRef (); ULONG STDMETHODCALLTYPE Release(); // IDispatchimplementation HRESULT STDMETHODCALLTYPE GetTypeInfoCount(UINT *count); HRESULT STDMETHODCALLTYPE GetTypeInfo(UINT, LCID, ITypeInfo** typeInfo); HRESULT STDMETHODCALLTYPE GetIDsOfNames( REFIID riid, LPOLESTR* nameList, UINT nameCount, LCID lcid, DISPID* idList); HRESULT STDMETHODCALLTYPE Invoke( DISPID id, REFIID riid, LCID lcid, WORD flags, DISPPARAMS* args, VARIANT* ret, EXCEPINFO* excp, UINT* err); private: static const std::wstring functionName; ULONG refCount_; }; #endif // BASICSCRIPTOBJECT_H_ </code></pre> <p><code>BasicScriptObject.cpp</code></p> <pre><code>#include "BasicScriptObject.h" const std::wstring BasicScriptObject::functionName(L"alert"); BasicScriptObject::BasicScriptObject() : refCount_(1) {} BasicScriptObject::~BasicScriptObject() {} HRESULT BasicScriptObject::QueryInterface(REFIID riid,void ** object) { if(riid == IID_IDispatch) { *object = static_cast&lt;IDispatch*&gt;(this); } else { *object = nullptr; } if(*object != nullptr) { AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG BasicScriptObject::AddRef () { return ::InterlockedIncrement(&amp;refCount_); } ULONG BasicScriptObject::Release() { ULONG oldCount = refCount_; ULONG newCount = ::InterlockedDecrement(&amp;refCount_); if(0 == newCount) { delete this; } return oldCount; } HRESULT BasicScriptObject::GetTypeInfoCount(UINT *count) { *count = 0; return S_OK; } HRESULT BasicScriptObject::GetTypeInfo(UINT, LCID, ITypeInfo** typeInfo) { *typeInfo = nullptr; return S_OK; } // This is where we register procs (or vars) HRESULT BasicScriptObject::GetIDsOfNames( REFIID riid, LPOLESTR* nameList, UINT nameCount, LCID lcid, DISPID* idList) { for(UINT i = 0; i &lt; nameCount; i++) { if(0 == functionName.compare(nameList[i])) { idList[i] = 1; } else { return E_FAIL; } } return S_OK; } // And this is where they are called from script HRESULT BasicScriptObject::Invoke( DISPID id, REFIID riid, LCID lcid, WORD flags, DISPPARAMS* args, VARIANT* ret, EXCEPINFO* excp, UINT* err) { // We only have one function so no need to a lot of logic here. Just validate // the call signature! if(id != 1) { return DISP_E_MEMBERNOTFOUND; } if(args-&gt;cArgs != 1) { return DISP_E_BADPARAMCOUNT; } if(args-&gt;rgvarg-&gt;vt != VT_BSTR) { return DISP_E_TYPEMISMATCH; } MessageBox(NULL, args-&gt;rgvarg-&gt;bstrVal, L"Script Alert", MB_OK); return S_OK; } </code></pre> <p><code>main.cpp</code></p> <pre><code>#include "BasicScriptHost.h" #include "BasicScriptObject.h" #include &lt;iostream&gt; int main() { HRESULT hr; hr = CoInitialize(nullptr); if(FAILED(hr)) { throw std::runtime_error("Unable to initialize COM subsystem"); } try { // Initialize the application object GUID javascriptId; HRESULT hr = CLSIDFromProgID(L"JavaScript" , &amp;javascriptId); if(FAILED(hr)) { throw std::runtime_error("Unable to acquire javascript engine GUID"); } // Create our object that exposes functionality to the script CComPtr&lt;BasicScriptObject&gt; appObject; appObject.Attach(new BasicScriptObject()); CComPtr&lt;IDispatch&gt; appObjectDispatch; hr = appObject.QueryInterface&lt;IDispatch&gt;(&amp;appObjectDispatch); if(FAILED(hr)) { throw std::runtime_error("Unable to acquire host dispatch"); } // Create the script site CComPtr&lt;BasicScriptHost&gt; host; host.Attach(new BasicScriptHost(javascriptId, L"window", appObjectDispatch)); // Do stuff! hr = host-&gt;Initialize(); if(SUCCEEDED(hr)) { wchar_t* source = L"function ProcessData(msg) { window.alert(msg); }" L"window.alert('cplusplus.com SUCKS!');" ; hr = host-&gt;Parse(source); } if(SUCCEEDED(hr)) { hr = host-&gt;Run(); } if(SUCCEEDED(hr)) { std::vector&lt;std::wstring&gt; args; args.push_back(L"use cppreference.com instead!"); host-&gt;CallFunction(L"ProcessData", args); } if(SUCCEEDED(hr)) { hr = host-&gt;Terminate(); } } catch(std::exception&amp; e) { std::cout &lt;&lt; e.what() &lt;&lt; std::endl; } }; </code></pre>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload