#include <Windows.h>
#include <tchar.h>
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_DESTROY:
PostQuitMessage(0);
break;
default:
return DefWindowProc(hWnd, message, wParam, lParam);
}
return 0;
}
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
const LPCTSTR 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上都可以查到。
代码清单如下:
#pragma once
#include <Windows.h>
#include <OleIdl.h>
class OleContainer : public IOleClientSite,
public IOleInPlaceSite,
public IOleInPlaceFrame
{
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(const IID &clsid)
{
HRESULT hr = StgCreateDocfile(nullptr,
STGM_READWRITE | STGM_SHARE_EXCLUSIVE | STGM_DIRECT | STGM_CREATE,
0,
&m_pStorage);
if (FAILED(hr))
{
return false;
}
hr = OleCreate(clsid, IID_IOleObject, OLERENDER_DRAW, 0, this, m_pStorage, (LPVOID *)&m_pOleObj);
if (FAILED(hr))
{
return false;
}
hr = m_pOleObj->QueryInterface(IID_IOleInPlaceObject, (LPVOID *)&m_pInPlaceObj);
if (FAILED(hr))
{
return false;
}
return true;
}
bool InPlaceActive(HWND hWnd, LPCRECT lpRect)
{
if (hWnd == nullptr)
{
return false;
}
RECT rect = {};
if (lpRect == nullptr)
{
GetClientRect(hWnd, &rect);
lpRect = ▭
}
HRESULT hr = m_pOleObj->DoVerb(OLEIVERB_INPLACEACTIVATE, nullptr, this, 0, hWnd, lpRect);
if (FAILED(hr))
{
return false;
}
m_hWindow = hWnd;
return true;
}
public: // IUnknown Methods
HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, LPVOID *ppvObject)
{
*ppvObject = nullptr;
if (riid == IID_IUnknown)
{
*ppvObject = (IOleClientSite *)this;
}
else if (riid == IID_IOleInPlaceSite)
{
*ppvObject = (IOleInPlaceSite *)this;
}
else if (riid == IID_IOleInPlaceUIWindow)
{
*ppvObject = (IOleInPlaceUIWindow *)this;
}
else if (riid == IID_IOleInPlaceFrame)
{
*ppvObject = (IOleInPlaceFrame *)this;
}
if (*ppvObject == nullptr)
{
return E_NOINTERFACE;
}
AddRef();
return S_OK;
}
ULONG STDMETHODCALLTYPE AddRef()
{
return (ULONG)InterlockedIncrement(&m_nRefCount);
}
ULONG STDMETHODCALLTYPE Release()
{
LONG nRefCount = InterlockedDecrement(&m_nRefCount);
if (nRefCount <= 0)
{
delete this;
}
return (ULONG)nRefCount;
}
public: // IOleClientSite Methods
STDMETHOD(SaveObject)()
{
return E_NOTIMPL;
}
STDMETHOD(GetMoniker)(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)
{
return E_NOTIMPL;
}
STDMETHOD(GetContainer)(IOleContainer **ppContainer)
{
return E_NOTIMPL;
}
STDMETHOD(ShowObject)()
{
return E_NOTIMPL;
}
STDMETHOD(OnShowWindow)(BOOL fShow)
{
return E_NOTIMPL;
}
STDMETHOD(RequestNewObjectLayout)()
{
return E_NOTIMPL;
}
public: // IOleWindow Methods
STDMETHOD(GetWindow)(HWND *phwnd)
{
return E_NOTIMPL;
}
STDMETHOD(ContextSensitiveHelp)(BOOL fEnterMode)
{
return E_NOTIMPL;
}
public: // IOleInPlaceSite Methods
STDMETHOD(CanInPlaceActivate)()
{
return m_hWindow == nullptr ? S_OK : S_FALSE;
}
STDMETHOD(OnInPlaceActivate)()
{
return E_NOTIMPL;
}
STDMETHOD(OnUIActivate)()
{
return E_NOTIMPL;
}
STDMETHOD(GetWindowContext)(IOleInPlaceFrame **ppFrame,
IOleInPlaceUIWindow **ppDoc,
LPRECT lprcPosRect,
LPRECT lprcClipRect,
LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
if (m_hWindow == nullptr)
{
return E_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;
return S_OK;
}
STDMETHOD(Scroll)(SIZE scrollExtant)
{
return E_NOTIMPL;
}
STDMETHOD(OnUIDeactivate)(BOOL fUndoable)
{
return E_NOTIMPL;
}
STDMETHOD(OnInPlaceDeactivate)()
{
return E_NOTIMPL;
}
STDMETHOD(DiscardUndoState)()
{
return E_NOTIMPL;
}
STDMETHOD(DeactivateAndUndo)()
{
return E_NOTIMPL;
}
STDMETHOD(OnPosRectChange)(LPCRECT lprcPosRect)
{
return E_NOTIMPL;
}
public: // IOleInPlaceUIWindow Methods
STDMETHOD(GetBorder)(LPRECT lprectBorder)
{
return E_NOTIMPL;
}
STDMETHOD(RequestBorderSpace)(LPCBORDERWIDTHS pborderwidths)
{
return E_NOTIMPL;
}
STDMETHOD(SetBorderSpace)(LPCBORDERWIDTHS pborderwidths)
{
return E_NOTIMPL;
}
STDMETHOD(SetActiveObject)(IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName)
{
return E_NOTIMPL;
}
public: // IOleInPlaceFrame Methods
STDMETHOD(InsertMenus)(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths)
{
return E_NOTIMPL;
}
STDMETHOD(SetMenu)(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject)
{
return E_NOTIMPL;
}
STDMETHOD(RemoveMenus)(HMENU hmenuShared)
{
return E_NOTIMPL;
}
STDMETHOD(SetStatusText)(LPCOLESTR pszStatusText)
{
return E_NOTIMPL;
}
STDMETHOD(EnableModeless)(BOOL fEnable)
{
return E_NOTIMPL;
}
STDMETHOD(TranslateAccelerator)(LPMSG lpmsg, WORD wID)
{
return E_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对象即可。
代码清单:
#pragma once
#include "OleContainer.h"
#include <ExDisp.h>
class WebBrowser : public OleContainer
{
public:
WebBrowser(HWND hParent) : m_pWebBrowser(nullptr), m_hWnd(hParent)
{
}
~WebBrowser()
{
if (m_pWebBrowser != nullptr)
{
m_pWebBrowser->Release();
m_pWebBrowser = nullptr;
}
}
public:
bool CreateWebBrowser()
{
if (!CreateOleObject(CLSID_WebBrowser))
{
return false;
}
RECT rect = {};
GetClientRect(m_hWnd, &rect);
if (!InPlaceActive(m_hWnd, &rect))
{
return false;
}
HRESULT hr = m_pOleObj->QueryInterface(IID_IWebBrowser2, (LPVOID *)&m_pWebBrowser);
if (FAILED(hr))
{
return false;
}
return true;
}
public:
void Navigate(LPCTSTR lpUrl)
{
BSTR bstrUrl = SysAllocString(lpUrl);
m_pWebBrowser->Navigate(bstrUrl, nullptr, nullptr, nullptr, nullptr);
SysFreeString(bstrUrl);
}
public:
STDMETHOD(GetWindow)(HWND *phwnd)
{
*phwnd = m_hWnd;
return S_OK;
}
STDMETHOD(GetWindowContext)(IOleInPlaceFrame **ppFrame,
IOleInPlaceUIWindow **ppDoc,
LPRECT lprcPosRect,
LPRECT lprcClipRect,
LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
*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;
return S_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,创建好主窗口后插入几行代码:
int APIENTRY _tWinMain(_In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
_In_ int nCmdShow)
{
UNREFERENCED_PARAMETER(hPrevInstance);
UNREFERENCED_PARAMETER(lpCmdLine);
const LPCTSTR 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);
WebBrowser wb(hWnd);
if (!wb.CreateWebBrowser())
{
return 0;
}
wb.Navigate(_T("http://www.baidu.com/"));
MSG msg = {};
while (GetMessage(&msg, nullptr, 0, 0))
{
if (!TranslateAccelerator(msg.hwnd, nullptr, &msg))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return (int)msg.wParam;