C++中嵌入ie浏览器总结 .
C++中嵌入ie瀏覽器總結(1) - ie邊框 及上下文菜單
???? 最近項目中用html 來做界面,也就折騰了一下在wxwidget中嵌入瀏覽器的若干細節工作,mfc也基本是類似的,由于wxwidget中已經做了一個封裝wxie,但是開發過程中也遇到了不少問題,在此做一下總結:?
?? 普通嵌入到程序里面的瀏覽器頁面都會有一個灰色的邊框,這樣放到程序里面就很難看。目前網上流行的辦法添加css:
body{
??border:0;
}
?????? 但是這個方法的缺點是,必須要把頁面頭部的dtd申明去掉。網上的錯誤提法是類似以下鏈接的:???http://www.faceker.com/200801/webbrowser-no-border.html?? 這里面講,只要把頭部改成:
???
?????但是目前很多頁面用了比較新的css,改成這樣后,頁面就無法正常顯示了。讓網頁美工改樣式? 真是有點困難。
??? 后來繼續查閱了資料,發現比較好的辦法,那就是重載? IDocHostUIHandler 接口,其中,實現以下部分:
HRESULT?STDMETHODCALLTYPE?FrameSite::GetHostInfo(DOCHOSTUIINFO?*pInfo)
{
????pInfo->cbSize?=?sizeof(DOCHOSTUIINFO);
????pInfo->dwFlags?=?DOCHOSTUIFLAG_NO3DBORDER|DOCHOSTUIFLAG_SCROLL_NO;
????pInfo->dwDoubleClick?=?DOCHOSTUIDBLCLK_DEFAULT;
????return?S_OK;
}
?????? 其中? DOCHOSTUIFLAG_NO3DBORDER 就表示不要生成邊框,DOCHOSTUIFLAG_SCROLL_NO 表示不要生成滾動條
這樣就可以比較完美的解決邊框和滾動條的問題,不用依賴頁面的調整 。讓設計師愛用什么用什么。
?????另外一個就是禁用右鍵菜單,網上也有不少辦法,但是用這個接口可以很簡單的實現:?
?
?????????????????????????????????????????????????????????????IUnknown?*pcmdtReserved,?IDispatch?*pdispReserved)
{
????HRESULT?result????=?S_FALSE;?//Dont?Interfere
????BOOL????handled????=?FALSE;
????switch?(?m_contextMenuMode?)
????{
????case?kDefaultMenuSupport:
????????break;
????case?kNoContextMenu:
????????result????=?S_OK;
????????handled????=?TRUE;
????????break;
????case?kTextSelectionOnly:
????????if?(dwID?!=?CONTEXT_MENU_TEXTSELECT)
????????{
????????????result????=?S_OK;
????????????handled????=?TRUE;
????????}
????????break;
????case?kAllowAllButViewSource:
????????if?(dwID?==?CONTEXT_MENU_DEFAULT)
????????{
????????????//result????=?ModifyContextMenu(dwID,?ppt,?pcmdtReserved);
????????????handled????=?TRUE;
????????}
????????break;
????case?kCustomMenuSupport:
????????if?(dwID?==?CONTEXT_MENU_DEFAULT)
????????{
????????????//result?=?CustomContextMenu(ppt,?pcmdtReserved);
????????????handled????=?TRUE;
????????}
????????break;
????}
????if?(!?handled)
????{
?????????result?=?S_FALSE;
????}
????return?result;
}
??? 這里不僅可以控制右鍵菜單顯示,m_contextMenuMode? = kNoContextMenu,還可以做到自定義菜單顯示,m_contextMenuMode? =其他值。因為暫時還不需要自定義菜單,所以這里沒有實現。
???
?? 如果用wxie,就在FrameSite類增加這個接口即可,不關注的接口直接返回S_FALSE 或E_NOTIMPL;
? 如果用sdk或mfc,可以 調用IOleObject 的SetClientSite 方法,設置一個繼承了IOleClientSite 和 IDocHostUIHandler 的接口。
?
C++中嵌入ie瀏覽器總結(2) - 雙向通訊
?? 第一步解決了邊框和上下文菜單問題,第二部就是要解決c++程序和html頁面交互的問題。最開始的想法是通過c++去更新頁面內容的方式來完成c++->html的通訊,通過BeforeNavigate2 接口,截獲頁面url地址的方式來完成html->c++的通訊。但是這種方式存在以下缺點:?
???????(1) c++->html 的問題在于導致c++代碼復雜,需要通過c++代碼來完成頁面生成,如果修改頁面,將產生很大的工作量。雖然嘗試用了模板方法解決,但是還是比較繁瑣,而且會導致經常通訊的時候,頁面會經常刷新,產生其他的一些問題。
????? (2) html->c++ 的問題在于 傳遞參數不方便,解析也不方便、無法獲取返回值、腳本中要調用不方便
???? 為了解決這些問題,經過google后找到了問題的解決辦法 :?
??? (1) c++->html? ,可以通過調用頁面腳本方法來實現,調用方法如下:
{
????wxVariant?result(false);
????if?(!?m_webBrowser.Ok())
????????return?result;
????//?get?document?dispatch?interface
????IDispatch?*iDisp?=?NULL;
????HRESULT?hr?=?m_webBrowser->get_Document(&iDisp);
????if?(hr?!=?S_OK)
????????return?result;
????//?Query?for?Document?Interface
????wxAutoOleInterface<IHTMLDocument2>?hd(IID_IHTMLDocument2,?iDisp);
????iDisp->Release();
????if?(!?hd.Ok())
????????return?result;
????IDispatch?*spScript;
????hr?=?hd->get_Script(&spScript);
????if(FAILED(hr))
????????return?result;
????BSTR?bstrMember?=?wxConvertStringToOle(fun);
????DISPID?dispid?=?NULL;
????hr?=?spScript->GetIDsOfNames(IID_NULL,&bstrMember,1,
????????LOCALE_SYSTEM_DEFAULT,&dispid);
????if(FAILED(hr))
????{
????????
????????return?result;
????}
????//Putting?parameters
????DISPPARAMS?dispparams;
????memset(&dispparams,?0,?sizeof?dispparams);
????dispparams.cArgs??????=?params.size();
????dispparams.rgvarg?????=?new?VARIANT[dispparams.cArgs];
????dispparams.cNamedArgs?=?0;
????for(?int?i?=?0;?i?<?params.size();?i++)
????{
????????CComBSTR?bstr?=?wxConvertStringToOle(params[params.size()?-?1?-?i]);
????????//?back?reading
????????bstr.CopyTo(&dispparams.rgvarg[i].bstrVal);
????????dispparams.rgvarg[i].vt?=?VT_BSTR;
????}
????EXCEPINFO?excepInfo;
????memset(&excepInfo,?0,?sizeof?excepInfo);
????VARIANT???varRet;
????UINT?nArgErr?=?(UINT)-1;??????//?initialize?to?invalid?arg
????//Call?JavaScript?function
????hr?=?spScript->Invoke(dispid,IID_NULL,0,
????????DISPATCH_METHOD,&dispparams,
????????&varRet,&excepInfo,&nArgErr);
????delete?[]?dispparams.rgvarg;
????if(FAILED(hr))
????{
????????
????????return?result;
????}
????wxConvertOleToVariant(varRet,result);
????return?result;
}
? 這個方法實現了C++對頁面腳本調用,而且參數個數可以任意。比如頁面腳本是?:
{
}
??C++中的調用方法是?:
params.push_back("a");
params.push_back("b");
params.push_back("c");
xxx->ExecScripts("fun",params);
還可以獲得腳本返回的結果。
? (2) html->c++? 通過腳本的window.external 方法,首先,在前文提到過的IDocHostUIHandler 接口中,實現方法:?
{
?????
????IDispatch?*?pDisp?=?m_window->getExternal();
????if(pDisp)
????{
????????pDisp->AddRef();
????????*ppDispatch?=?pDisp;
????}
????return?S_OK;
}
其中 m_window->getExternal();
?返回的是自定義的一個IDispatch 接口類:?
?*?IDispimp.H
?*?IDispatch
?*
?*?Copyright?(c)1995-1999?Microsoft?Corporation,?All?Rights?Reserved
?*/?
#ifndef?_IDISPIMP_H_
#define?_IDISPIMP_H_
#include?<oaidl.h>
class?CustomFunction;
class?CImpIDispatch?:?public?IDispatch
{
????protected:
????????ULONG???????????????m_cRef;
????public:
????????CImpIDispatch(void);
????????~CImpIDispatch(void);
????????STDMETHODIMP?QueryInterface(REFIID,?void?**);
????????STDMETHODIMP_(ULONG)?AddRef(void);
????????STDMETHODIMP_(ULONG)?Release(void);
????????//IDispatch
????????STDMETHODIMP?GetTypeInfoCount(UINT*?pctinfo);
????????STDMETHODIMP?GetTypeInfo(/*?[in]?*/?UINT?iTInfo,
????????????/*?[in]?*/?LCID?lcid,
????????????/*?[out]?*/?ITypeInfo**?ppTInfo);
????????STDMETHODIMP?GetIDsOfNames(
????????????/*?[in]?*/?REFIID?riid,
????????????/*?[size_is][in]?*/?LPOLESTR?*rgszNames,
????????????/*?[in]?*/?UINT?cNames,
????????????/*?[in]?*/?LCID?lcid,
????????????/*?[size_is][out]?*/?DISPID?*rgDispId);
????????STDMETHODIMP?Invoke(
????????????/*?[in]?*/?DISPID?dispIdMember,
????????????/*?[in]?*/?REFIID?riid,
????????????/*?[in]?*/?LCID?lcid,
????????????/*?[in]?*/?WORD?wFlags,
????????????/*?[out][in]?*/?DISPPARAMS??*pDispParams,
????????????/*?[out]?*/?VARIANT??*pVarResult,
????????????/*?[out]?*/?EXCEPINFO?*pExcepInfo,
????????????/*?[out]?*/?UINT?*puArgErr);
????????????void?setCustomFunction(CustomFunction?*fun)?{m_fun?=?fun;}
private:
????CustomFunction?*m_fun;
};
#endif?//_IDISPIMP_H_
主要實現以下兩個方法:?
wxString cszCB_CustomFunction = wxT("CB_CustomFunction");? #define DISPID_CB_CustomFunction 3
STDMETHODIMP?CImpIDispatch::GetIDsOfNames(
????????????/*?[in]?*/?REFIID?riid,
????????????/*?[size_is][in]?*/?OLECHAR**?rgszNames,
????????????/*?[in]?*/?UINT?cNames,
????????????/*?[in]?*/?LCID?lcid,
????????????/*?[size_is][out]?*/?DISPID*?rgDispId)
{
????HRESULT?hr;
????UINT????i;
????//?Assume?some?degree?of?success
????hr?=?NOERROR;
????????for?(?i=0;?i?<?cNames;?i++)?{
????????wxString?cszName??=?rgszNames[i];
?
????????if(cszName?==?cszCB_CustomFunction)
????????{
????????????rgDispId[i]?=?DISPID_CB_CustomFunction;
????????}????
?????
????????else?{
????????????//?One?or?more?are?unknown?so?set?the?return?code?accordingly
????????????hr?=?ResultFromScode(DISP_E_UNKNOWNNAME);
????????????rgDispId[i]?=?DISPID_UNKNOWN;
????????}
????}
????return?hr;
}
STDMETHODIMP?CImpIDispatch::Invoke(
????????????/*?[in]?*/?DISPID?dispIdMember,
????????????/*?[in]?*/?REFIID?/*riid*/,
????????????/*?[in]?*/?LCID?/*lcid*/,
????????????/*?[in]?*/?WORD?wFlags,
????????????/*?[out][in]?*/?DISPPARAMS*?pDispParams,
????????????/*?[out]?*/?VARIANT*?pVarResult,
????????????/*?[out]?*/?EXCEPINFO*?/*pExcepInfo*/,
????????????/*?[out]?*/?UINT*?puArgErr)
{
?????
????
????if(dispIdMember?==?DISPID_CB_CustomFunction)?
????{
????????if(wFlags?&?DISPATCH_PROPERTYGET)
????????{
????????????if(pVarResult?!=?NULL)
????????????{
????????????????
????????????????VariantInit(pVarResult);
????????????????V_VT(pVarResult)=VT_BOOL;
????????????????V_BOOL(pVarResult)?=?true;
????????????}
????????}
????????
????????if?(?wFlags?&?DISPATCH_METHOD?)
????????{
????????????//arguments?come?in?reverse?order
????????????//for?some?reason
????????????if(!m_fun)?return?S_OK;
????????????wxString?arg1,arg2;
?????????
????????????if(pDispParams->cArgs<1)?return?S_FALSE;
????????????wxString?cmd?=?pDispParams->rgvarg[pDispParams->cArgs-1].bstrVal;
????????????std::vector<wxString>?args;
????????????if(pDispParams->cArgs>1)
????????????{
????????????????for(int?i=pDispParams->cArgs-2;i>=0;i--)
????????????????????args.push_back(pDispParams->rgvarg[i].bstrVal);
????????????}
????????????wxString?re?=?m_fun->execute(cmd,args);
????????????if(pVarResult?!=?NULL)
????????????{
????????????????VariantInit(pVarResult);
????????????????V_VT(pVarResult)=VT_BSTR;
????????????????wxVariant?wVar(re);
????????????????VariantToMSWVariant(wVar,*pVarResult);
????????????????
?????????????????
????????????}
?????????
????????}
????????
????}
?????
????return?S_OK;
}
? 其中 CustomFunction 定義如下:
#include?<wx/wx.h>
#include?<vector>
class?CustomFunction
{
public:
????CustomFunction(void)
????{
????}
????virtual?~CustomFunction(void)
????{
????}
????virtual?wxString?execute(const?wxString?&cmd,?const?std::vector<wxString>?&args)?=?0;
};
? 然后只要在自己類里面繼承這個接口,就可以接收來之腳本的調用請求。
腳本里面編寫函數:
????????????????{
?????????????????????
????????????????????var?fun?=?"window.external.CB_CustomFunction(";
????????????????????for(i=0;i<arguments.length;i++)
????????????????????{
????????????????????????if(i!=0)
????????????????????????????fun?=?fun+",";
????????????????????????fun?=?fun+"/""+arguments[i]+"/"";
????????????????????}????
????????????????????fun?=?fun+")";
????????????????????//alert(fun);
????????????????????return?(eval(fun));
????????????????}
?? 然后調用的地方寫:?
?
就可以調用c++的函數,并且可以得到返回值,從而解決了html->c++的通訊問題?
?? 解決了雙向通訊后,頁面就不需要用刷新來解決,網頁設計師和c++編程人員只要定義好通訊接口,大家各自實現好接口方法就可以完成界面功能了。
?
C++中嵌入ie瀏覽器總結(3) - wxIE 的 bug解決及最后效果展示
?????? 前面兩個問題的解決,心里想: 這下好了,解決了這兩個問題,下一步做界面就簡單多了,不像以前,直接用mfc或第三方庫做,要做出好看效果真是很難。編譯好,運行了,發現效果還不錯。但是實現到里面的腳本的時候,發現出了問題,不響應鍵盤消息了。而且在頁面文本框里面按tab鍵,光標不是跑到下一個文本框,而是不見了。之前有這個現象,但是忙于解決前面的問題,沒有注意到。這下可完了,不會前工盡棄吧,那可麻煩大了。
????? google了半天,問了朋友,還是不知道什么原因。因為wxIE及嵌入瀏覽器本身是比較偏門的問題,確實很難找到答案。
????? 山窮水盡疑無路,柳暗花明又一村。好不容易,在google上找到了一個類似的問題,回答的說,這是wxIE的bug,在wxPython的項目中,這個問題已經解決了。下載下來編譯后,運行試試看,搞定了。
????? 但是還得和之前的修改合并,合并過程中又發現了一個問題。前文中提到過,通過IOleObject 接口來設置IDocHostUIHandler方法,我開始就是用這種方法。結果合并完后,發現還是不響應鍵盤消息。
??? 調了半天,才發現,wxIE原來已經實現過IOleClientSite 接口,我把自己的接口設上去,把wxIE的給替換掉了,所以導致了不正常的結果。 經過一番調整后,終于正常了。
?? 自此,用html做c++程序界面的基礎工作算是告一段落了,下面就是完成接口工作和頁面腳本了。希望不要再遇到什么問題。
???? 我在這里把這些問題記錄下來,以備以后查用,也愿其他朋友不要再遇到我一樣的困擾。
??? 下面截一張做出來的界面圖:
這是一個對話框,完成前面的基礎工作后,只要設計師設計好頁面,我們幾分鐘就可以繼承到c++里面,再花點寫接口和腳本的時間,比原來用mfc做界面,不知要節省多少時間。這個界面比較簡單,但是只要是能設計出的界面,我們都能讓他集成進來。
有興趣的朋友也不妨試試這種做界面的方法。
C++中嵌入ie瀏覽器總結(4) - 對話框拖動
???? 前面忘了寫這個問題,就是對話框的拖動問題。就像我前面的圖片展示的對話框,一般的windows對話框是可以拖著標題欄移動的,但是我們這里沒有任何原來的標題欄了,只有html頁面,怎么拖呢? 好像有點麻煩。
?????? 冥思苦想之后,想到一種辦法,通過前面的接口給c++發指令,讓c++移動窗口,頁面上計算好拖動的距離。html里面頁面的拖動還是比較簡單的,c++里面移動也就是調用 MoveWindow。由于前面的通訊方法還是比較靈活,沒用多少時間,就把這個功能實現了。但是一運行看,不太對勁,拖動過程拖尾現象太明顯??赡苁莄++不斷調用 MoveWindow 重繪效率比較低。這可麻煩了。這時候,突然想到普通對話框拖動的時候,是一個虛框在那里動,原來的對話框是不動的,鼠標放開后,對話框才移過去。 能不能做到這樣呢? 但是windows實現這個方法的細節不得而知,怎么做呢???
?????? 還是google好啊,經過一番搜索,找到了答案:?
??
{
????if(args.size()<2)?return;
????long?x,y;
????args[0].ToLong(&x);
????args[1].ToLong(&y);
????int?ix,iy;
????ix?=?x;
????iy?=?y;
????ClientToScreen(&ix,&iy);
?
????::SendMessage((HWND)this->GetHWND(),WM_NCMOUSEMOVE,HTCAPTION,MAKELPARAM(ix,?iy));?
}
?? 搞定了,簡單吧,真是沒想到這么簡單。運行后發現,真的和windows的對話框移動一模一樣了,太好了
今天一鼓作氣把前面幾天的工作都總結了下來,還真是敲得手有點累。但是這些東西確實是不太常規的方法,找解決問題的方法很難,這里先把他們記錄下來,免得以后找不到了。以前確實有很多知識都是用了就丟一邊找不到了。 也希望給有類似疑問的朋友一個幫助。
總結
以上是生活随笔為你收集整理的C++中嵌入ie浏览器总结 .的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: SQLSERVER数据库经常置疑的原因
- 下一篇: OCCI读写Oracle Spatial