[转载]裸写一个含内嵌IE控件的窗口
引言
之前也做过一些含内嵌IE控件的东西,只是一直用MFC/ATL等框架,对于里面的原理其实一知半解,只有脱离它们写一遍,才算能真正懂。前不久在写一个SkyDriveClient的时候正好有一个需求,就练习了一下。技术含量没有,在此记录一笔,供后来人入门,供前辈们批评。
本文中,行文以流水帐、贴代码方式为主,同时为了不带来干扰,代码将尽量以不带或少带封装的方式书写。目标明确,只为“功利性”地实现一个带IE控件的窗口。对于未实现的接口也不进行任何解释。
建立普通窗口
首先,我们建立一个普通的窗口程序。向导生成即可,剔除点代码,整理一下,代码清单如下:
| #include<Windows.h> #include<tchar.h> 
 LRESULTCALLBACK WndProc(HWNDhWnd, UINTmessage, WPARAMwParam, LPARAMlParam) { switch (message) { caseWM_DESTROY: PostQuitMessage(0); break; default: returnDefWindowProc(hWnd, message, wParam, lParam); } 
 return 0; } 
 intAPIENTRY_tWinMain(_In_HINSTANCE hInstance, _In_opt_HINSTANCEhPrevInstance, _In_LPTSTR lpCmdLine, _In_int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); 
 constLPCTSTR CLASS_NAME = _T("WebBrowserContainer"); 
 WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszClassName = CLASS_NAME; 
 RegisterClassEx(&wcex); 
 HWND hWnd = CreateWindow(CLASS_NAME, _T("WebBrowser Sample"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); 
 if (hWnd == nullptr) { return 0; } 
 ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); 
 MyWebBrowser wb(hWnd); 
 MSG msg = {}; 
 while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, nullptr, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } 
 return (int)msg.wParam; } | 
加入WebBrowser
这一部分主要参考了《使用C++实现SDK之WebBrowser容器》(http://blog.csdn.net/norsd/article/details/2921389)。但是这篇文章里没有给出真正可运行的代码,有几处小笔误,然后呢,其实我也不是很喜欢原作者的封装方式。本篇的目的是尽可能简单、原始,让后来者容易上手。
实现OLE容器类
实现一个WebBrowser,先要实现一个OLE容器类。我不知道OLE容器类的精确定义是什么,但现在我们需要实现三个接口:IOleClientSite、IOleInPlaceSite、IOleInPlaceFrame(<OleIdl.h>)。它们的定义在MSDN上都可以查到。
代码清单如下:
| #pragmaonce 
 #include<Windows.h> #include<OleIdl.h> 
 classOleContainer : publicIOleClientSite, publicIOleInPlaceSite, publicIOleInPlaceFrame { public: OleContainer() : m_nRefCount(0), m_pStorage(nullptr), m_pOleObj(nullptr), m_pInPlaceObj(nullptr), m_hWindow(nullptr) { AddRef(); OleInitialize(nullptr); } 
 ~OleContainer() { if (m_pInPlaceObj != nullptr) { m_pInPlaceObj->Release(); m_pInPlaceObj = nullptr; } 
 if (m_pOleObj != nullptr) { m_pOleObj->Release(); m_pOleObj = nullptr; } 
 if (m_pStorage != nullptr) { m_pStorage->Release(); m_pStorage = nullptr; } 
 OleUninitialize(); } 
 public: bool CreateOleObject(constIID &clsid) { HRESULT hr = StgCreateDocfile(nullptr, STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_CREATE, 0, &m_pStorage); if (FAILED(hr)) { returnfalse; } 
 hr = OleCreate(clsid, IID_IOleObject, OLERENDER_DRAW, 0, this, m_pStorage, (LPVOID *)&m_pOleObj); 
 if (FAILED(hr)) { returnfalse; } 
 hr = m_pOleObj->QueryInterface(IID_IOleInPlaceObject, (LPVOID *)&m_pInPlaceObj); 
 if (FAILED(hr)) { returnfalse; } 
 returntrue; } 
 bool InPlaceActive(HWNDhWnd, LPCRECTlpRect) { if (hWnd == nullptr) { returnfalse; } 
 RECT rect = {}; 
 if (lpRect == nullptr) { GetClientRect(hWnd, &rect); lpRect = ▭ } 
 HRESULT hr = m_pOleObj->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, this, 0, hWnd, lpRect); 
 if (FAILED(hr)) { returnfalse; } 
 m_hWindow = hWnd; 
 returntrue; } 
 public: // IUnknown Methods HRESULTSTDMETHODCALLTYPE QueryInterface(REFIIDriid, LPVOID *ppvObject) { *ppvObject = nullptr; 
 if (riid == IID_IUnknown) { *ppvObject = (IOleClientSite *)this; } 
 elseif (riid == IID_IOleInPlaceSite) { *ppvObject = (IOleInPlaceSite *)this; } elseif (riid == IID_IOleInPlaceUIWindow) { *ppvObject = (IOleInPlaceUIWindow *)this; } elseif (riid == IID_IOleInPlaceFrame) { *ppvObject = (IOleInPlaceFrame *)this; } 
 if (*ppvObject == nullptr) { returnE_NOINTERFACE; } 
 AddRef(); returnS_OK; } 
 ULONGSTDMETHODCALLTYPE AddRef() { return (ULONG)InterlockedIncrement(&m_nRefCount); } 
 ULONGSTDMETHODCALLTYPE Release() { LONG nRefCount = InterlockedDecrement(&m_nRefCount); 
 if (nRefCount <= 0) { deletethis; } 
 return (ULONG)nRefCount; } 
 public: // IOleClientSite Methods STDMETHOD(SaveObject)() { returnE_NOTIMPL; } 
 STDMETHOD(GetMoniker)(DWORDdwAssign, DWORDdwWhichMoniker, IMoniker **ppmk) { returnE_NOTIMPL; } 
 STDMETHOD(GetContainer)(IOleContainer **ppContainer) { returnE_NOTIMPL; } 
 STDMETHOD(ShowObject)() { returnE_NOTIMPL; } 
 STDMETHOD(OnShowWindow)(BOOLfShow) { returnE_NOTIMPL; } 
 STDMETHOD(RequestNewObjectLayout)() { returnE_NOTIMPL; } 
 public: // IOleWindow Methods STDMETHOD(GetWindow)(HWND *phwnd) { returnE_NOTIMPL; } 
 STDMETHOD(ContextSensitiveHelp)(BOOLfEnterMode) { returnE_NOTIMPL; } 
 public: // IOleInPlaceSite Methods STDMETHOD(CanInPlaceActivate)() { return m_hWindow == nullptr ? S_OK : S_FALSE; } 
 STDMETHOD(OnInPlaceActivate)() { returnE_NOTIMPL; } 
 STDMETHOD(OnUIActivate)() { returnE_NOTIMPL; } 
 STDMETHOD(GetWindowContext)(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECTlprcPosRect, LPRECTlprcClipRect, LPOLEINPLACEFRAMEINFOlpFrameInfo) { if (m_hWindow == nullptr) { returnE_NOTIMPL; } 
 *ppFrame = (IOleInPlaceFrame*)this; *ppDoc = NULL; AddRef(); 
 GetClientRect(m_hWindow, lprcPosRect); GetClientRect(m_hWindow, lprcClipRect); 
 lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO); lpFrameInfo->fMDIApp = false; lpFrameInfo->hwndFrame = GetParent(m_hWindow); lpFrameInfo->haccel = nullptr; lpFrameInfo->cAccelEntries = 0; 
 returnS_OK; } 
 STDMETHOD(Scroll)(SIZEscrollExtant) { returnE_NOTIMPL; } 
 STDMETHOD(OnUIDeactivate)(BOOLfUndoable) { returnE_NOTIMPL; } 
 STDMETHOD(OnInPlaceDeactivate)() { returnE_NOTIMPL; } 
 STDMETHOD(DiscardUndoState)() { returnE_NOTIMPL; } 
 STDMETHOD(DeactivateAndUndo)() { returnE_NOTIMPL; } 
 STDMETHOD(OnPosRectChange)(LPCRECTlprcPosRect) { returnE_NOTIMPL; } 
 public: // IOleInPlaceUIWindow Methods STDMETHOD(GetBorder)(LPRECTlprectBorder) { returnE_NOTIMPL; } 
 STDMETHOD(RequestBorderSpace)(LPCBORDERWIDTHSpborderwidths) { returnE_NOTIMPL; } 
 STDMETHOD(SetBorderSpace)(LPCBORDERWIDTHSpborderwidths) { returnE_NOTIMPL; } 
 STDMETHOD(SetActiveObject)(IOleInPlaceActiveObject *pActiveObject, LPCOLESTRpszObjName) { returnE_NOTIMPL; } 
 public: // IOleInPlaceFrame Methods STDMETHOD(InsertMenus)(HMENUhmenuShared, LPOLEMENUGROUPWIDTHSlpMenuWidths) { returnE_NOTIMPL; } 
 STDMETHOD(SetMenu)(HMENUhmenuShared, HOLEMENUholemenu, HWNDhwndActiveObject) { returnE_NOTIMPL; } 
 STDMETHOD(RemoveMenus)(HMENUhmenuShared) { returnE_NOTIMPL; } 
 STDMETHOD(SetStatusText)(LPCOLESTRpszStatusText) { returnE_NOTIMPL; } 
 STDMETHOD(EnableModeless)(BOOLfEnable) { returnE_NOTIMPL; } 
 STDMETHOD(TranslateAccelerator)(LPMSGlpmsg, WORDwID) { returnE_NOTIMPL; } 
 private: LONG m_nRefCount; 
 protected: IStorage *m_pStorage; IOleObject *m_pOleObj; IOleInPlaceObject *m_pInPlaceObj; HWND m_hWindow; }; | 
这三个接口,连带他们的父类,总共需要实现31个方法!不过还好,除了篇幅稍微长一点,基本上都是E_NOTIMPL。
其中CreateOleObject和InPlaceActive是我自己加的,将来要调用的。
OleCreate期间,除了若干次QueryInterface之外,GetContainer会被调到一次。OLEIVERB_INPLACEACTIVATE期间,除了若干次QueryInterface之外,CanInPlaceActivate、OnInPlaceActivate、ShowObject会被调到。
那些不返回E_NOTIMPL的接口,我也没有一一查询是干嘛的,都是抄的~
实现WebBrowser容器类
上一节可视为(通用的)OLE容器类(只是没有使用除WebBrowser之外的其他玩意儿测试过,共性不是很明确,不敢保证通用性),这一节来实现WebBrowser。作为最简单的WebBrowser,不用再额外实现别的借口了,直接使用上面的OleContainer,创建WebBrowser对象即可。
代码清单:
| #pragmaonce 
 #include"OleContainer.h" #include<ExDisp.h> 
 classWebBrowser : publicOleContainer { public: WebBrowser(HWNDhParent) : m_pWebBrowser(nullptr), m_hWnd(hParent) { 
 } 
 ~WebBrowser() { if (m_pWebBrowser != nullptr) { m_pWebBrowser->Release(); m_pWebBrowser = nullptr; } } 
 public: bool CreateWebBrowser() { if (!CreateOleObject(CLSID_WebBrowser)) { returnfalse; } 
 RECT rect = {}; GetClientRect(m_hWnd, &rect); 
 if (!InPlaceActive(m_hWnd, &rect)) { returnfalse; } 
 HRESULT hr = m_pOleObj->QueryInterface(IID_IWebBrowser2, (LPVOID *)&m_pWebBrowser); 
 if (FAILED(hr)) { returnfalse; } 
 returntrue; } 
 public: void Navigate(LPCTSTRlpUrl) { BSTR bstrUrl = SysAllocString(lpUrl); m_pWebBrowser->Navigate(bstrUrl, nullptr, nullptr, nullptr, nullptr); SysFreeString(bstrUrl); } 
 public: STDMETHOD(GetWindow)(HWND *phwnd) { *phwnd = m_hWnd; returnS_OK; 
 } 
 STDMETHOD(GetWindowContext)(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECTlprcPosRect, LPRECTlprcClipRect, LPOLEINPLACEFRAMEINFOlpFrameInfo) { *ppFrame = (IOleInPlaceFrame*)this; *ppDoc = NULL; AddRef(); 
 GetClientRect(m_hWnd, lprcPosRect); GetClientRect(m_hWnd, lprcClipRect); 
 lpFrameInfo->cb = sizeof(OLEINPLACEFRAMEINFO); lpFrameInfo->fMDIApp = false; lpFrameInfo->hwndFrame = GetParent(m_hWnd); lpFrameInfo->haccel = nullptr; lpFrameInfo->cAccelEntries = 0; 
 returnS_OK; } 
 protected: IWebBrowser2 *m_pWebBrowser; HWND m_hWnd; }; | 
其中CreateWebBrowser 只是简单的调用了一下CreateOleObject,传入CLSID_WebBrowser。然后InPlaceActive就可以了。最后拿的m_pWebBrowser指针是备用的,用于实现一个示例功能:Navigate。
后面的GetWindow和GetWindowContext在OleContainer中已经有了,之所以再写一遍,是因为WebBrowser的创建过程中,就会调用GetWindow,需要给出一个窗口句柄;InPlaceActive过程中会调用GetWindowContext,也要做事情,不能return E_NOTIMPL了事。而OleContainer中,暂时我是在InPlaceActive的时候传入窗口句柄的。可能是OleContianer根本应该更早的知道窗口的存在,但这一点我还没弄清楚,所以暂时写成那样。但到WebBrowser层面,创建一个WebBrowser之前就需要先创建好窗口。
使用WebBrowser容器类
我们回到Main,创建好主窗口后插入几行代码:
| intAPIENTRY_tWinMain(_In_HINSTANCE hInstance, _In_opt_HINSTANCEhPrevInstance, _In_LPTSTR lpCmdLine, _In_int nCmdShow) { UNREFERENCED_PARAMETER(hPrevInstance); UNREFERENCED_PARAMETER(lpCmdLine); 
 constLPCTSTR CLASS_NAME = _T("WebBrowserContainer"); 
 WNDCLASSEX wcex = { sizeof(WNDCLASSEX) }; wcex.style = CS_HREDRAW | CS_VREDRAW; wcex.lpfnWndProc = WndProc; wcex.cbClsExtra = 0; wcex.cbWndExtra = 0; wcex.hInstance = hInstance; wcex.hCursor = LoadCursor(nullptr, IDC_ARROW); wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1); wcex.lpszClassName = CLASS_NAME; 
 RegisterClassEx(&wcex); 
 HWND hWnd = CreateWindow(CLASS_NAME, _T("WebBrowser Sample"), WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, nullptr, nullptr, hInstance, nullptr); 
 if (hWnd == nullptr) { return 0; } 
 ShowWindow(hWnd, nCmdShow); UpdateWindow(hWnd); 
 
 MSG msg = {}; 
 while (GetMessage(&msg, nullptr, 0, 0)) { if (!TranslateAccelerator(msg.hwnd, nullptr, &msg)) { TranslateMessage(&msg); DispatchMessage(&msg); } } 
 return (int)msg.wParam; } 
 | 
然后运行,截图如下:
小结
上文演示了一个最简单的在正常窗口嵌入WebBrowser对象的例子。例子代码可在 http://pan.baidu.com/s/18PtQu(WebBrowserSample.rar)下载。
处于直观性考虑,没有做过多的封装和梳理。后面再来梳理这些。
作者:Jingle Guo
出处:http://www.cnblogs.com/studynote/
若标题中有“转载”字样,则本文版权归原作者所有。若无转载字样,本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利.
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号