EternalWindows
URL Monikers / WebBrowser コントロールとの連携

Asynchronous Pluggable Protocolsを使用すれば、URL Monikersの呼び出しを検出できるようになりますが、 アプリケーションが明示的に行った呼び出しを検出することは、あまり意味がないといえます。 明示的に呼び出すということは、それがいつ行われるかが分かっているわけですから、 わざわざ別の個所で通知を受け取る必要もないわけです。 ただし、WebBrowser コントロールのように内部的にURL Monikersを使用するオブジェクトを扱う場合は、それなりに利点があります。 WebBrowser コントロールは、IEで使用されているWebページを表示するためのCOMオブジェクトですが、 このオブジェクトに返ることになるネットワークデータを変更できれば、 Webページの内容を変更できるからです。 ただし、パフォーマンスが若干悪くなる可能性もあるため、その点は注意するべきといえます。

今回のプログラムは、WebBrowser コントロールを使用してWebページにアクセスできるようにしますが、 一部のページについてはアクセス禁止にする処理が含まれています。 たとえば、http://eternalwindows.jp/にアクセスすれば、アクセスが禁止された旨が表示されることになります。 まず、ヘッダーファイル(protocol.h)を示します。

class CInternetProtocol : public IInternetProtocol
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP Start(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved);
	STDMETHODIMP Continue(PROTOCOLDATA *pProtocolData);
	STDMETHODIMP Abort(HRESULT hrReason, DWORD dwOptions);
	STDMETHODIMP Terminate(DWORD dwOptions);
	STDMETHODIMP Suspend();
	STDMETHODIMP Resume();

	STDMETHODIMP Read(void *pv, ULONG cb, ULONG *pcbRead);
	STDMETHODIMP Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition);
	STDMETHODIMP LockRequest(DWORD dwOptions);
	STDMETHODIMP UnlockRequest();

	CInternetProtocol();
	~CInternetProtocol();
	
private:
	LONG                m_cRef;
	BOOL                m_bDefaultHandler;
	BOOL                m_bComplete;
	IInternetProtocolEx *m_pProtocol;
	DWORD               m_dwDataSize;
	char                m_szData[256];
};

class CInternetProtocolFactory : public IClassFactory
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject);
	STDMETHODIMP LockServer(BOOL fLock);
};

続いて、ソースファイル(WebBrowser コントロール関連)を示します。

#include <windows.h>
#include <shlobj.h>
#include <urlmon.h>
#include "protocol.h"

#pragma comment (lib, "urlmon.lib")

class CWebBrowserHost : public IOleClientSite, public IOleInPlaceSite
{
public:
	STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject);
	STDMETHODIMP_(ULONG) AddRef();
	STDMETHODIMP_(ULONG) Release();
	
	STDMETHODIMP SaveObject();
	STDMETHODIMP GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk);
	STDMETHODIMP GetContainer(IOleContainer **ppContainer);
	STDMETHODIMP ShowObject();
	STDMETHODIMP OnShowWindow(BOOL fShow);
	STDMETHODIMP RequestNewObjectLayout();
	
	STDMETHODIMP GetWindow(HWND *phwnd);
	STDMETHODIMP ContextSensitiveHelp(BOOL fEnterMode);
	STDMETHODIMP CanInPlaceActivate();
	STDMETHODIMP OnInPlaceActivate();
	STDMETHODIMP OnUIActivate();
	STDMETHODIMP GetWindowContext(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo);
	STDMETHODIMP Scroll(SIZE scrollExtant);
	STDMETHODIMP OnUIDeactivate(BOOL fUndoable);
	STDMETHODIMP OnInPlaceDeactivate();
	STDMETHODIMP DiscardUndoState();
	STDMETHODIMP DeactivateAndUndo();
	STDMETHODIMP OnPosRectChange(LPCRECT lprcPosRect);

	CWebBrowserHost();
	~CWebBrowserHost();
	int Run(HINSTANCE hinst, int nCmdShow);
	LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
	BOOL Create();
	void CreateInternetSession(BOOL bRegister);
	void Navigate(LPWSTR lpszUrl);

private:
	LONG             m_cRef;
	HWND             m_hwnd;
	IWebBrowser2     *m_pWebBrowser;
	IInternetSession *m_pInternetSession;
	IClassFactory    *m_pInternetProtocolFactory;
};

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

CWebBrowserHost *g_pWebBrowserHost = NULL;

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	int nResult = 0;

	OleInitialize(NULL);
	
	g_pWebBrowserHost = new CWebBrowserHost;
	if (g_pWebBrowserHost != NULL) {
		nResult = g_pWebBrowserHost->Run(hinst, nCmdShow);
		g_pWebBrowserHost->Release();
	}

	OleUninitialize();

	return nResult;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	return g_pWebBrowserHost->WindowProc(hwnd, uMsg, wParam, lParam);
}


// CWebBrowserHost


CWebBrowserHost::CWebBrowserHost()
{
	m_cRef = 1;
	m_hwnd = NULL;
	m_pWebBrowser = NULL;
	m_pInternetSession = NULL;
	m_pInternetProtocolFactory = NULL;
}

CWebBrowserHost::~CWebBrowserHost()
{
}

STDMETHODIMP CWebBrowserHost::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IOleClientSite))
		*ppvObject = static_cast<IOleClientSite *>(this);
	else if (IsEqualIID(riid, IID_IOleWindow) || IsEqualIID(riid, IID_IOleInPlaceSite))
		*ppvObject = static_cast<IOleInPlaceSite *>(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CWebBrowserHost::AddRef()
{
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CWebBrowserHost::Release()
{
	return InterlockedDecrement(&m_cRef);
}

STDMETHODIMP CWebBrowserHost::SaveObject()
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk)
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::GetContainer(IOleContainer **ppContainer)
{
	*ppContainer = NULL;

	return E_NOINTERFACE;
}

STDMETHODIMP CWebBrowserHost::ShowObject()
{
	return S_OK;
}

STDMETHODIMP CWebBrowserHost::OnShowWindow(BOOL fShow)
{
	return S_OK;
}

STDMETHODIMP CWebBrowserHost::RequestNewObjectLayout()
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::GetWindow(HWND *phwnd)
{
	*phwnd = m_hwnd;

	return S_OK;
}

STDMETHODIMP CWebBrowserHost::ContextSensitiveHelp(BOOL fEnterMode)
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::CanInPlaceActivate()
{
	return S_OK;
}

STDMETHODIMP CWebBrowserHost::OnInPlaceActivate()
{
	return S_OK;
}

STDMETHODIMP CWebBrowserHost::OnUIActivate()
{
	return S_OK;
}

STDMETHODIMP CWebBrowserHost::GetWindowContext(IOleInPlaceFrame **ppFrame, IOleInPlaceUIWindow **ppDoc, LPRECT lprcPosRect, LPRECT lprcClipRect, LPOLEINPLACEFRAMEINFO lpFrameInfo)
{
	*ppFrame = NULL;
	*ppDoc = NULL;
	
	GetClientRect(m_hwnd, lprcPosRect);
	GetClientRect(m_hwnd, lprcClipRect);

	return S_OK;
}

STDMETHODIMP CWebBrowserHost::Scroll(SIZE scrollExtant)
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::OnUIDeactivate(BOOL fUndoable)
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::OnInPlaceDeactivate()
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::DiscardUndoState()
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::DeactivateAndUndo()
{
	return E_NOTIMPL;
}

STDMETHODIMP CWebBrowserHost::OnPosRectChange(LPCRECT lprcPosRect)
{
	return S_OK;
}

int CWebBrowserHost::Run(HINSTANCE hinst, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = ::WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CWebBrowserHost::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndButton = NULL;
	static HWND hwndEdit = NULL;

	switch (uMsg) {

	case WM_CREATE:
		m_hwnd = hwnd;
		if (!Create())
			return -1;
		hwndEdit = CreateWindowEx(0, TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER | ES_AUTOHSCROLL, 10, 10, 600, 35, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		hwndButton = CreateWindowEx(0, TEXT("BUTTON"), TEXT("アクセス"), WS_CHILD | WS_VISIBLE, 620, 10, 70, 30, hwnd, (HMENU)2, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateInternetSession(TRUE);
		return 0;
	
	case WM_COMMAND: {
		WCHAR szUrl[2048];
		BSTR  bstr;

		if ((HWND)lParam != hwndButton)
			return 0;

		GetWindowText(hwndEdit, szUrl, sizeof(szUrl) / sizeof(WCHAR));
		bstr = SysAllocString(szUrl);
		Navigate(bstr);
		SysFreeString(bstr);
		return 0;
	}

	case WM_SIZE: {
		RECT              rc = {0, 70, LOWORD(lParam), HIWORD(lParam)};
		IOleInPlaceObject *pOleInPlaceObject;

		m_pWebBrowser->QueryInterface(IID_PPV_ARGS(&pOleInPlaceObject));
		pOleInPlaceObject->SetObjectRects(&rc, &rc);
		pOleInPlaceObject->Release();
		return 0;
	}

	case WM_DESTROY:
		if (m_pWebBrowser != NULL)
			m_pWebBrowser->Release();
		CreateInternetSession(FALSE);
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL CWebBrowserHost::Create()
{
	IOleObject *pOleObject;
	HRESULT    hr;
	MSG        msg;
	RECT       rc;

	hr = CoCreateInstance(CLSID_WebBrowser, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pWebBrowser));
	if (FAILED(hr))
		return FALSE;
	
	m_pWebBrowser->QueryInterface(IID_PPV_ARGS(&pOleObject));
	pOleObject->SetClientSite(static_cast<IOleClientSite *>(this));

	SetRectEmpty(&rc);
	hr = pOleObject->DoVerb(OLEIVERB_INPLACEACTIVATE, &msg, static_cast<IOleClientSite *>(this), 0, m_hwnd, &rc);
	if (FAILED(hr)) {
		pOleObject->Release();
		m_pWebBrowser->Release();
		m_pWebBrowser = NULL;
		return FALSE;
	}

	Navigate(L"http://www.yahoo.co.jp/");
	
	pOleObject->Release();

	return TRUE;
}

void CWebBrowserHost::Navigate(LPWSTR lpszUrl)
{
	BSTR    bstrUrl;
	VARIANT varFlags, varTargetFrameName, varPostData, varHeaders;
	
	bstrUrl = SysAllocString(lpszUrl);
	VariantInit(&varFlags);
	VariantInit(&varTargetFrameName);
	VariantInit(&varPostData);
	VariantInit(&varHeaders);
	m_pWebBrowser->Navigate(bstrUrl, &varFlags, &varTargetFrameName, &varPostData, &varHeaders);

	SysFreeString(bstrUrl);
}

void CWebBrowserHost::CreateInternetSession(BOOL bRegister)
{
	const CLSID CLSID_InternetProtocol = {0xdf5ba506, 0xb531, 0x42b7, {0x87, 0xc7, 0xd0, 0x4f, 0xe, 0xf8, 0xa, 0xcd}};
	
	if (bRegister) {
		CoInternetGetSession(0, &m_pInternetSession, 0);
		
		m_pInternetProtocolFactory = new CInternetProtocolFactory;
		m_pInternetSession->RegisterNameSpace(m_pInternetProtocolFactory, CLSID_InternetProtocol, L"http", 0, NULL, 0);
	}
	else {
		if (m_pInternetSession != NULL) {
			m_pInternetSession->UnregisterNameSpace(m_pInternetProtocolFactory, L"http:");
			m_pInternetSession->Release();
		}
		
		if (m_pInternetProtocolFactory != NULL)
			m_pInternetProtocolFactory->Release();
	}
}

続いて、ソースファイル(IInternetProtocol関連)を示します。

#include <windows.h>
#include <urlmon.hgt;
#include <wininet.h>
#include "protocol.h"

#pragma comment (lib, "wininet.lib")

const LPWSTR g_lpszBlockUrls[] = {
	L"http://eternalwindows.jp/"
};
const int g_nBlockUrlCount = sizeof(g_lpszBlockUrls) / sizeof(g_lpszBlockUrls[0]);


// CInternetProtocol


CInternetProtocol::CInternetProtocol()
{
	m_cRef = 1;
	m_bDefaultHandler = FALSE;
	m_bComplete = FALSE;
	lstrcpyA(m_szData, "<html><body>アクセスがブロックされているページです。</body></html>");
	m_dwDataSize = lstrlenA(m_szData);

	CoCreateInstance(CLSID_HttpProtocol, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pProtocol));
}

CInternetProtocol::~CInternetProtocol()
{
}

STDMETHODIMP CInternetProtocol::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;
	
	if (IsEqualIID(riid, IID_IInternetProtocol))
		*ppvObject = static_cast<IInternetProtocol *>(this);
	else if (IsEqualIID(riid, IID_IInternetProtocolEx))
		return E_NOINTERFACE;
	else
		return m_pProtocol->QueryInterface(riid, ppvObject);

	AddRef();

	return S_OK;
}

STDMETHODIMP_(ULONG) CInternetProtocol::AddRef()
{
	m_pProtocol->AddRef();
	
	return InterlockedIncrement(&m_cRef);
}

STDMETHODIMP_(ULONG) CInternetProtocol::Release()
{
	m_pProtocol->Release();

	if (InterlockedDecrement(&m_cRef) == 0) {
		delete this;
		return 0;
	}

	return m_cRef;
}

STDMETHODIMP CInternetProtocol::Start(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved)
{
	int     i;
	HRESULT hr;
	BOOL    bBlock = FALSE;

	for (i = 0; i < g_nBlockUrlCount; i++) {
		if (lstrcmpW(szUrl, g_lpszBlockUrls[i]) == 0) {
			bBlock = TRUE;
			break;
		}
	}

	if (bBlock) {
		m_bDefaultHandler = FALSE;
		m_bComplete = FALSE;
		pOIProtSink->ReportProgress(BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE, L"text/html");
		pOIProtSink->ReportData(BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE, m_dwDataSize, m_dwDataSize);
		pOIProtSink->ReportResult(S_OK, 0, NULL);
		hr = S_OK;
	}
	else {
		m_bDefaultHandler = TRUE;
		hr = m_pProtocol->Start(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved);
	}

	return hr;
}

STDMETHODIMP CInternetProtocol::Continue(PROTOCOLDATA *pProtocolData)
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Continue(pProtocolData);
	else
		hr = S_OK;

	return hr;
}

STDMETHODIMP CInternetProtocol::Abort(HRESULT hrReason, DWORD dwOptions)
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Abort(hrReason, dwOptions);
	else
		hr = S_OK;

	return hr;
}

STDMETHODIMP CInternetProtocol::Terminate(DWORD dwOptions)
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Terminate(dwOptions);
	else
		hr = S_OK;

	return hr;
}

STDMETHODIMP CInternetProtocol::Suspend()
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Suspend();
	else
		hr = E_NOTIMPL;

	return hr;
}

STDMETHODIMP CInternetProtocol::Resume()
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Resume();
	else
		hr = E_NOTIMPL;

	return hr;
}

STDMETHODIMP CInternetProtocol::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Read(pv, cb, pcbRead);
	else {
		if (m_bComplete)
			hr = S_FALSE;
		else {
			CopyMemory(pv, m_szData, m_dwDataSize);
			*pcbRead = m_dwDataSize;
			m_bComplete = TRUE;
			hr = S_OK;
		}
	}

	return hr;
}

STDMETHODIMP CInternetProtocol::Seek(LARGE_INTEGER dlibMove, DWORD dwOrigin, ULARGE_INTEGER *plibNewPosition)
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Seek(dlibMove, dwOrigin, plibNewPosition);
	else
		hr = E_NOTIMPL;

	return hr;
}

STDMETHODIMP CInternetProtocol::LockRequest(DWORD dwOptions)
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->LockRequest(dwOptions);
	else
		hr = E_NOTIMPL;

	return hr;
}

STDMETHODIMP CInternetProtocol::UnlockRequest()
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->UnlockRequest();
	else
		hr = E_NOTIMPL;

	return hr;
}


// CInternetProtocolFactory


STDMETHODIMP CInternetProtocolFactory::QueryInterface(REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IUnknown) || IsEqualIID(riid, IID_IClassFactory))
		*ppvObject = static_cast<IClassFactory *>(this);
	else
		return E_NOINTERFACE;

	AddRef();
	
	return S_OK;
}

STDMETHODIMP_(ULONG) CInternetProtocolFactory::AddRef()
{
	return 2;
}

STDMETHODIMP_(ULONG) CInternetProtocolFactory::Release()
{
	return 1;
}

STDMETHODIMP CInternetProtocolFactory::CreateInstance(IUnknown *pUnkOuter, REFIID riid, void **ppvObject)
{
	CInternetProtocol *p;
	HRESULT           hr;
	
	*ppvObject = NULL;

	if (pUnkOuter != NULL)
		return CLASS_E_NOAGGREGATION;

	p = new CInternetProtocol();
	if (p == NULL)
		return E_OUTOFMEMORY;

	hr = p->QueryInterface(riid, ppvObject);
	p->Release();

	return hr;
}

STDMETHODIMP CInternetProtocolFactory::LockServer(BOOL fLock)
{
	return S_OK;
}

WebBrowser コントロールを表示するには、OLEにおけるインプレースアクティベーションという技術を使用します。 このためには、IOleClientSiteとIOleInPlaceSiteを実装したクラスが必要になるため、 それらを継承したCWebBrowserHostというクラスを定義しています。 CreateではWebBrowser コントロールを作成し、IOleObject::SetClientSiteでCWebBrowserHostをWebBrowser コントロールに渡しています。 インプレースアクティベーションは、IOleObject::DoVerbにOLEIVERB_INPLACEACTIVATEを指定することで可能であり、 これによりWebBrowser コントロールのウインドウが、m_hwndの子ウインドウになります。 IInternetProtocolを実装したオブジェクトの登録は、前節と同じくIInternetSessionを使用します。

今回のCInternetProtocolで行いたいのは、特定のURLが渡された場合はそのURLへのアクセスを禁止し、 それ以外の場合は既定の処理を行うというものです。 httpプロトコルの既定の処理は、CLSID_HttpProtocolで識別できるオブジェクトが実装しているため、 これを取得しておきます。

CInternetProtocol::CInternetProtocol()
{
	m_cRef = 1;
	m_bDefaultHandler = FALSE;
	m_bComplete = FALSE;
	lstrcpyA(m_szData, "<html><body>アクセスがブロックされているページです。</body></html>");
	m_dwDataSize = lstrlenA(m_szData);

	CoCreateInstance(CLSID_HttpProtocol, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&m_pProtocol));
}

CoCreateInstanceにCLSID_HttpProtocolを指定すれば、既定のオブジェクトを作成できます。 m_szDataにはアクセスが禁止されたときに返されるHTMLデータが格納され、 m_dwDataSizeにはそのサイズが格納されています。

IInternetProtocol::Startでは、URLへのアクセスを禁止するかの判断を行います。

STDMETHODIMP CInternetProtocol::Start(LPCWSTR szUrl, IInternetProtocolSink *pOIProtSink, IInternetBindInfo *pOIBindInfo, DWORD grfPI, HANDLE_PTR dwReserved)
{
	int     i;
	HRESULT hr;
	BOOL    bBlock = FALSE;

	for (i = 0; i < g_nBlockUrlCount; i++) {
		if (lstrcmpW(szUrl, g_lpszBlockUrls[i]) == 0) {
			bBlock = TRUE;
			break;
		}
	}

	if (bBlock) {
		m_bDefaultHandler = FALSE;
		m_bComplete = FALSE;
		pOIProtSink->ReportProgress(BINDSTATUS_VERIFIEDMIMETYPEAVAILABLE, L"text/html");
		pOIProtSink->ReportData(BSCF_FIRSTDATANOTIFICATION | BSCF_LASTDATANOTIFICATION | BSCF_DATAFULLYAVAILABLE, m_dwDataSize, m_dwDataSize);
		pOIProtSink->ReportResult(S_OK, 0, NULL);
		hr = S_OK;
	}
	else {
		m_bDefaultHandler = TRUE;
		hr = m_pProtocol->Start(szUrl, pOIProtSink, pOIBindInfo, grfPI, dwReserved);
	}

	return hr;
}

g_lpszBlockUrlsはブロックするべきURLを格納しているため、 これとszUrlが一致する場合は、m_bDefaultHandlerにFALSEを格納します。 これは既定のオブジェクトを使用しないということなので、アプリケーションが明示的にIInternetProtocolSinkのメソッドを使用します。 一方、bBlockがFALSEの場合はURLへアクセスしても問題ないため、m_bDefaultHandlerにTRUEを格納し、 既定のIInternetProtocol::Startを呼び出すようにしています。 m_bDefaultHandlerがTRUEの際に既定のメソッドを呼び出すのは、InternetProtocolの他のメソッドにおいても共通しています。

IInternetProtocol::Readの実装は次のようになっています。

STDMETHODIMP CInternetProtocol::Read(void *pv, ULONG cb, ULONG *pcbRead)
{
	HRESULT hr;

	if (m_bDefaultHandler)
		hr = m_pProtocol->Read(pv, cb, pcbRead);
	else {
		if (m_bComplete)
			hr = S_FALSE;
		else {
			CopyMemory(pv, m_szData, m_dwDataSize);
			*pcbRead = m_dwDataSize;
			m_bComplete = TRUE;
			hr = S_OK;
		}
	}

	return hr;
}

m_bDefaultHandlerがTRUEの場合は既定のメソッドを呼び出しますが、 そうでない場合はpvにm_szDataをコピーします。 このときにはS_OKを返すことになるため、再びReadが呼ばれることになります。 しかし、今度は返すべきデータはありませんから、S_FALSEを返すようにします。

IServiceProvider::QueryServiceの使用

WebBrowser コントロールでは、IInternetProtocolを実装したオブジェクトを返す方法として、 IServiceProvider::QueryServiceを使用する方法もあります。 CWebBrowserHostがIServiceProviderを実装し、QueryInterfaceでIServiceProviderを考慮しているものとして、 次に例を示します。

STDMETHODIMP CWebBrowserHost::QueryService(REFGUID guidService, REFIID riid, void **ppvObject)
{
	*ppvObject = NULL;

	if (IsEqualIID(riid, IID_IInternetProtocol))
		*ppvObject = new CInternetProtocol;
	else
		return E_NOINTERFACE;

	return S_OK;
}

新しいURLへアクセスしようとした場合、IServiceProvider::QueryServiceにIID_IInternetProtocolが渡されるため、 ここでIInternetProtocolを実装したオブジェクトを返すようにします。 そうすると、IInternetProtocol::Startが呼ばれることになります。 ただし、このオブジェクトにはhttpプロトコルだけでなく、全てのプロトコルに対応することが求められるため、 たとえばhttpsによるアクセスが生じた場合は、今回の実装では上手くいかないことになるでしょう。 IInternetProtocol::StartでINET_E_USE_DEFAULT_PROTOCOLHANDLERを返す方法も試してみましたが、 エラーが発生して失敗するようです。



戻る