WebBrowserコントロールの機能を制限する話、これで最後です。

今回は、これまでやっていなかったこと、まとめてコードを書きました。

  • ページ遷移の禁止: DWebBrowserEvents2のBeforeNavigate2イベントを受信しての処理
  • コンテキストメニュー(右クリックなど)の禁止: IAxWinAmbientDispatchのput_AllowContextMenu
  • その他: IAxWinAmbientDispatchのput_AllowShowUI、IWebBrowser2のput_RegisterAsBrowserとput_RegisterAsDropTargetとput_Silent

ソースコードを載せます。

#define UNICODE
#define _UNICODE
 
#define WINVER 0x0501
#define _ATL_XP_TARGETING
#define _ATL_NO_AUTOMATIC_NAMESPACE
#define _WTL_NO_AUTOMATIC_NAMESPACE
 
#include <cstdlib>
 
#include <windows.h>
#include <exdisp.h>
#include <exdispid.h>
 
#include <atlbase.h>
#include <atltypes.h>
#include <atlwin.h>
 
#include <atlapp.h>
#include <atlcrack.h>
 
class Module : public ATL::CAtlExeModuleT<Module>
{
};
 
Module module;
 
constexpr UINT ID_WEBBROWSER_EVENTS = 1;
 
class TestWindow :
  public ATL::CWindowImpl<TestWindow>,
  public WTL::CMessageFilter,
  public ATL::IDispEventImpl<ID_WEBBROWSER_EVENTS, TestWindow, &__uuidof(DWebBrowserEvents2), &LIBID_SHDocVw, 1, 1>
{
public:
  int Run(int cmdShow, _Inout_ WTL::CMessageLoop& msgLoop)
  {
    if (!Create(nullptr, ATL::CWindow::rcDefault,
      TEXT("Hello, world"), WS_OVERLAPPEDWINDOW))
    {
      return -1;
    }
 
    ShowWindow(cmdShow);
    UpdateWindow();
 
    msgLoop.AddMessageFilter(this);
    return msgLoop.Run();
  }
 
  BOOL PreTranslateMessage(_In_ MSG* pmsg) override
  {
    if (m_webBrowser.IsWindow()
      && m_webBrowser.SendMessage(
        WM_FORWARDMSG, 0, reinterpret_cast<LPARAM>(pmsg)))
    {
      return TRUE;
    }
    return IsDialogMessage(pmsg);
  }
 
  DECLARE_WND_CLASS(TEXT("Test Window Class"));
 
  BEGIN_MSG_MAP(TestWindow)
    MSG_WM_SIZE(OnSize)
    MSG_WM_SETFOCUS(OnSetFocus)
    MSG_WM_CREATE(OnCreate)
    MSG_WM_DESTROY(OnDestroy)
  END_MSG_MAP()
 
  BEGIN_SINK_MAP(TestWindow)
    SINK_ENTRY_EX(ID_WEBBROWSER_EVENTS, __uuidof(DWebBrowserEvents2), DISPID_BEFORENAVIGATE2, &TestWindow::BeforeNavigate2)
  END_SINK_MAP()
 
private:
  void __stdcall BeforeNavigate2(
    _In_ IDispatch* /*disp*/,
    _In_ VARIANT* /*url*/,
    _In_ VARIANT* /*flags*/,
    _In_ VARIANT* /*targetFrameName*/,
    _In_ VARIANT* /*postData*/,
    _In_ VARIANT* /*headers*/,
    _Inout_ VARIANT_BOOL* cancel)
  {
    if (cancel != nullptr)
    {
      *cancel = VARIANT_TRUE;
    }
  }
 
  void OnSize(UINT, SIZE size)
  {
    m_webBrowser.ResizeClient(size.cx, size.cy);
  }
 
  void OnSetFocus(HWND)
  {
    m_webBrowser.SetFocus();
  }
 
  LRESULT OnCreate(const CREATESTRUCT*)
  {
    constexpr DWORD IDC_WEBBROWSER_CONTROL = 1;
    RECT empty{};
    m_webBrowser.Create(m_hWnd, empty, L"https://www.google.co.jp/",
      WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_GROUP | WS_VSCROLL | WS_HSCROLL, 0,
      IDC_WEBBROWSER_CONTROL);
 
    ATL::CComPtr<IAxWinAmbientDispatch> ambient;
    m_webBrowser.QueryHost(&ambient);
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(ambient->put_AllowContextMenu(VARIANT_FALSE)),
      -1);
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(ambient->put_AllowShowUI(VARIANT_FALSE)),
      -1);
 
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(InitializeWebBrowserControl()),
      -1);
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(ATL::AtlAdviseSinkMap(this, true)),
      -1);
 
    return 0;
  }
 
  HRESULT InitializeWebBrowserControl()
  {
    ATL::CComPtr<IWebBrowser2> wb;
    if (SUCCEEDED(m_webBrowser.QueryControl(&wb)))
    {
      auto hrRB = wb->put_RegisterAsBrowser(VARIANT_FALSE);
      ATLENSURE_RETURN_HR(SUCCEEDED(hrRB), hrRB);
      auto hrRDT = wb->put_RegisterAsDropTarget(VARIANT_TRUE);
      ATLENSURE_RETURN_HR(SUCCEEDED(hrRDT), hrRDT);
      auto hrSl = wb->put_Silent(VARIANT_TRUE);
      ATLENSURE_RETURN_HR(SUCCEEDED(hrSl), hrSl);
    }
    return S_OK;
  }
 
  void OnDestroy()
  {
    PostQuitMessage(0);
  }
 
  ATL::CAxWindow m_webBrowser;
};
 
int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int cmdShow)
{
  if (FAILED(OleInitialize(nullptr)))
  {
    std::quick_exit(-1);
  }
 
  WTL::CMessageLoop msgLoop;
  TestWindow wnd;
  std::quick_exit(wnd.Run(cmdShow, msgLoop));
}

コンテキストメニュー(いわゆる右クリックメニュー)の禁止は、本来IDocHostUIHandlerのShowContextMenuで制御します。ところが、ATL::CAxHostWindowがIDocHostUIHandlerを実装していて、ATL::CAxWindowを使う側ではAllowContextMenuプロパティで簡単に制御できるようになっています。AllowShowUIも同じです。

DWebBrowserEvents2のイベントの受信のコードは、BEGIN_SINK_MAPやAtlAdviseSinkMap関数などをオーソドックスに使って書き上げています。BeforeNavigate2で、cancelにVARIANT_TRUEを書き込むことで、リンクをクリックしてもページ遷移を実行させないようにしています。

ここまで、単なるHTMLビューアーとしてWebBroserコントロールを使いたいと思って、いろいろ試してきました。これでだいたいやり尽くしたのではないかと考えています。


スポンサード リンク

この記事のカテゴリ

  • ⇒ WebBrowserコントロールでコンテキストメニューとページ遷移とその他制限を加える
  • ⇒ WebBrowserコントロールでコンテキストメニューとページ遷移とその他制限を加える
  • ⇒ WebBrowserコントロールでコンテキストメニューとページ遷移とその他制限を加える