我正在尝试在C ++中运行JavaScript函数,而不使用任何MFC或GUI。
我试图创build一个webbrowser指针,然后从中获取文档。
CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_SERVER, IID_IWebBrowser2, (void**)&pBrowser2); if (pBrowser2) { VARIANT vEmpty; VariantInit(&vEmpty); BSTR bstrURL = SysAllocString(L"file://D:/file.html"); HRESULT hr = pBrowser2->Navigate(bstrURL, &vEmpty, &vEmpty, &vEmpty, &vEmpty); if (SUCCEEDED(hr)) { IDispatch *pDisp=NULL; hr=pBrowser2->get_Document(&pDisp); <- This is NULL hr=pDisp->QueryInterface(IID_IHTMLDocument,(void**)&pDoc); }
}
基本上,我需要一个指向IHtmlDocument2结构的指针,因为在这个指针上我可以使'get_script'和'invoke'。 你知道如何做到这一点,或者我做错了什么?
你也知道另一种方法来运行没有MFC和GUI的JS函数吗?
谢谢,
PS使用MFC,我能够运行JS函数使用http://www.codeproject.com/Articles/2352/JavaScript-call-from-C
我不建议使用IWebBrowser2
来获取脚本主机的接口。 创建一个浏览器对象具有在后台启动Internet Explorer实例的恶意副作用,至少在Windows 7中是这样的。它还引入了一些可能与您正在尝试完成的命名对象(文档,窗口等)冲突的对象。
相反,我建议你使用活动脚本接口 。 使用它们需要一些额外的代码,但它更加灵活,给你更多的控制权。 具体而言,您可以设置脚本控制站点,而无需担心冲突或创建使用IWebBrowser2
控件时所需的转发适配器。 即使它需要一些额外的代码,也不难实现。
第一步是获取要使用的特定语言的GUID
。 这可以是Javascript,VBScript,Python或为Windows脚本宿主设计的任何脚本引擎。 您可以自己提供GUID
也可以使用如下所示的COM应用程序ID通过名称从系统获取GUID
。
GUID languageId; CLSIDFromProgID(L"JavaScript" , &languageId);
下一步是通过使用上面的GUID作为类ID调用CoCreateInstance
来创建脚本引擎的实例。
CoCreateInstance( languageId, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, reinterpret_cast<void**>(&scriptEngine_));
这将返回一个指向脚本引擎主接口的IActiveScript对象的指针。
下一步是设置脚本站点。 这是您提供的IActiveScriptSite的实现。 这是脚本引擎用来获取某些信息和分派某些事件(如脚本状态变化)的接口。
scriptEngine_->SetScriptSite(siteObjectPointer);
此时,您将拥有所有必需的对象,以便开始使用脚本引擎来调用脚本函数, 并允许脚本访问本机C ++对象。 要将对象(如window
或document
添加到根“名称空间”,请调用IActiveScript :: AddNamedItem或IActiveScript :: AddTypeLib 。
scriptEngine_->AddNamedItem( "objectname", SCRIPTITEM_ISVISIBLE | SCRIPTITEM_NOCODE);
要公开到脚本的对象必须实现IDispatch
或IDispatchEx
。 您实现哪个接口取决于对象的要求,但如果可能的话使用IDispatch,因为它需要较少的代码。
现在你已经初始化脚本引擎,你可以开始添加脚本。 您需要在脚本引擎上调用QueryInterface
来获取指向IActiveScriptParse
接口的指针,初始化解析器,然后添加脚本。 您只需要初始化解析器一次。
scriptEngine_->QueryInterface( IID_IActiveScriptParse, reinterpret_cast<void **>(&parser)); // Initialize the parser parser->InitNew(); parser->ParseScriptText("script source" ....);
现在,您已经初始化引擎并添加了一两个脚本,通过将引擎状态更改为SCRIPTSTATE_CONNECTED
开始执行。
scriptEngine_->SetScriptState(SCRIPTSTATE_CONNECTED);
这些是将脚本引擎嵌入到应用程序中的基本步骤。 下面的例子是一个完整的工作框架,它实现了脚本站点和一个向脚本公开功能的对象。 它还包含稍微修改版本的代码,以调用您在问题中引用的脚本函数。 这不是完美的,但它的工作,并希望让你更接近你想要完成的。
BasicScriptHost.h
#ifndef BASICSCRIPTHOST_H_ #define BASICSCRIPTHOST_H_ #include <string> #include <vector> #include <activscp.h> #include <comdef.h> #include <atlbase.h> class BasicScriptObject; class BasicScriptHost : public IActiveScriptSite { public: typedef IActiveScriptSite Interface; // Constructor to explicit BasicScriptHost(const GUID& languageId); BasicScriptHost( const GUID& languageId, const std::wstring& objectName, CComPtr<IDispatch> 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& script); virtual HRESULT Run(); virtual HRESULT Terminate(); virtual _variant_t CallFunction( const std::wstring& strFunc, const std::vector<std::wstring>& paramArray); private: ULONG refCount_; protected: CComPtr<IActiveScript> scriptEngine_; CComPtr<IDispatch> application_; }; #endif // BASICSCRIPTHOST_H_
BasicScriptHost.cpp
#include "BasicScriptHost.h" #include "BasicScriptObject.h" #include <stdexcept> #include <atlbase.h> BasicScriptHost::BasicScriptHost( const GUID& languageId, const std::wstring& objectName, CComPtr<IDispatch> object) : refCount_(1) { CComPtr<IActiveScript> engine; HRESULT hr = CoCreateInstance( languageId, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, reinterpret_cast<void**>(&engine)); if(FAILED(hr) || engine == nullptr) { throw std::runtime_error("Unable to create active script object"); } hr = engine->SetScriptSite(this); if(FAILED(hr)) { throw std::runtime_error("Unable to set scripting site"); } hr = engine->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& languageId) : refCount_(1) { CComPtr<IActiveScript> engine; HRESULT hr = CoCreateInstance( languageId, 0, CLSCTX_INPROC_SERVER, IID_IActiveScript, reinterpret_cast<void**>(&engine)); if(FAILED(hr) || engine == nullptr) { throw std::runtime_error("Unable to create active script object"); } hr = engine->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<IDispatch*>(this); } else { *object = nullptr; } if(*object != nullptr) { AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG BasicScriptHost::AddRef() { return ::InterlockedIncrement(&refCount_); } ULONG BasicScriptHost::Release() { ULONG oldCount = refCount_; ULONG newCount = ::InterlockedDecrement(&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 & SCRIPTINFO_IUNKNOWN && obj != nullptr) { *obj = application_; if(*obj != nullptr) { (*obj)->AddRef(); } } if(req & SCRIPTINFO_ITYPEINFO && type != nullptr) { *type = nullptr; } return S_OK; } HRESULT BasicScriptHost::Initialize() { CComPtr<IActiveScriptParse> parse; HRESULT hr = scriptEngine_->QueryInterface( IID_IActiveScriptParse, reinterpret_cast<void **>(&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->InitNew(); return hr; } HRESULT BasicScriptHost::Run() { // Sets state to SCRIPTSTATE_CONNECTED HRESULT hr = scriptEngine_->SetScriptState(SCRIPTSTATE_CONNECTED); return hr; } HRESULT BasicScriptHost::Terminate() { HRESULT hr = scriptEngine_->SetScriptState(SCRIPTSTATE_DISCONNECTED); if(SUCCEEDED(hr)) { hr = scriptEngine_->Close(); } return hr; } HRESULT BasicScriptHost::Parse(const std::wstring& source) { CComPtr<IActiveScriptParse> parser; HRESULT hr = scriptEngine_->QueryInterface( IID_IActiveScriptParse, reinterpret_cast<void **>(&parser)); if(FAILED(hr) || parser == nullptr) { throw std::runtime_error("Unable to get pointer to script parsing interface"); } hr = parser->ParseScriptText( source.c_str(), nullptr, nullptr, nullptr, 0, 0, 0, nullptr, nullptr); return hr; } #include <iostream> HRESULT BasicScriptHost::OnScriptError(IActiveScriptError *err) { EXCEPINFO e; err->GetExceptionInfo(&e); std::wcout << L"Script error: "; std::wcout << (e.bstrDescription == nullptr ? L"unknown" : e.bstrDescription); std::wcout << std::endl; std::wcout << std::hex << L"scode = " << e.scode << L" wcode = " << e.wCode << std::endl; return S_OK; } _variant_t BasicScriptHost::CallFunction( const std::wstring& strFunc, const std::vector<std::wstring>& paramArray) { CComPtr<IDispatch> scriptDispatch; scriptEngine_->GetScriptDispatch(nullptr, &scriptDispatch); //Find dispid for given function in the object CComBSTR bstrMember(strFunc.c_str()); DISPID dispid = 0; HRESULT hr = scriptDispatch->GetIDsOfNames( IID_NULL, &bstrMember, 1, LOCALE_SYSTEM_DEFAULT, &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 < 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->Invoke( dispid, IID_NULL, 0, DISPATCH_METHOD, &dispparams, &vaResult, &excepInfo, &nArgErr); delete [] dispparams.rgvarg; if(FAILED(hr)) { throw std::runtime_error("Unable to get invoke function"); } return vaResult; }
BasicScriptObject.h
#ifndef BASICSCRIPTOBJECT_H_ #define BASICSCRIPTOBJECT_H_ #include "stdafx.h" #include <string> #include <comdef.h> 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_
BasicScriptObject.cpp
#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<IDispatch*>(this); } else { *object = nullptr; } if(*object != nullptr) { AddRef(); return S_OK; } return E_NOINTERFACE; } ULONG BasicScriptObject::AddRef () { return ::InterlockedIncrement(&refCount_); } ULONG BasicScriptObject::Release() { ULONG oldCount = refCount_; ULONG newCount = ::InterlockedDecrement(&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 < 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->cArgs != 1) { return DISP_E_BADPARAMCOUNT; } if(args->rgvarg->vt != VT_BSTR) { return DISP_E_TYPEMISMATCH; } MessageBox(NULL, args->rgvarg->bstrVal, L"Script Alert", MB_OK); return S_OK; }
main.cpp
#include "BasicScriptHost.h" #include "BasicScriptObject.h" #include <iostream> 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" , &javascriptId); if(FAILED(hr)) { throw std::runtime_error("Unable to acquire javascript engine GUID"); } // Create our object that exposes functionality to the script CComPtr<BasicScriptObject> appObject; appObject.Attach(new BasicScriptObject()); CComPtr<IDispatch> appObjectDispatch; hr = appObject.QueryInterface<IDispatch>(&appObjectDispatch); if(FAILED(hr)) { throw std::runtime_error("Unable to acquire host dispatch"); } // Create the script site CComPtr<BasicScriptHost> host; host.Attach(new BasicScriptHost(javascriptId, L"window", appObjectDispatch)); // Do stuff! hr = host->Initialize(); if(SUCCEEDED(hr)) { wchar_t* source = L"function ProcessData(msg) { window.alert(msg); }" L"window.alert('cplusplus.com SUCKS!');" ; hr = host->Parse(source); } if(SUCCEEDED(hr)) { hr = host->Run(); } if(SUCCEEDED(hr)) { std::vector<std::wstring> args; args.push_back(L"use cppreference.com instead!"); host->CallFunction(L"ProcessData", args); } if(SUCCEEDED(hr)) { hr = host->Terminate(); } } catch(std::exception& e) { std::cout << e.what() << std::endl; } };