引き続き、ATLでWebBrowserコントロールで使用する場合にJavaScriptその他を禁止する方法の話です。3回目からは、ちょっと違うやり方、DISPID_AMBIENT_DLCONTROLです。

インターネット上で検索すると、DISPID_AMBIENT_DLCONTROLを使うやり方のほうが多く見受けられるのですが、ちょっと問題があって後回しにしていました。その理由は、「Visual C++ 2013以降だと、ATL::CAxWindowと組み合わせての利用が少し困難だから」です。

というわけで、今回のプログラムは、Visual C++ 2012で動作確認しました。

#define UNICODE
#define _UNICODE
 
#define WINVER 0x0501
#define _ATL_DLL
#define _ATL_XP_TARGETING
#define _ATL_NO_AUTOMATIC_NAMESPACE
#define _WTL_NO_AUTOMATIC_NAMESPACE
 
#include <cstdlib>
 
#include <windows.h>
#include <mshtmdid.h>
 
#include <atlbase.h>
#include <atltypes.h>
#include <atlwin.h>
 
#include <atlapp.h>
#include <atlcrack.h>
 
#include <boost/implicit_cast.hpp>
 
#define quick_exit exit
#define constexpr const
 
class Module : public ATL::CAtlExeModuleT<Module>
{
};
 
Module module;
 
constexpr UINT ID_WEBBROWSER_EVENTS = 1;
 
class WebBrowserAmbient :
  public ATL::IDispEventSimpleImpl<ID_WEBBROWSER_AMBIENT, WebBrowserAmbient, &IID_NULL>
{
  static ATL::_ATL_FUNC_INFO* GetDlControlInfo()
  {
    static ATL::_ATL_FUNC_INFO GetDlControlInfo = { CC_STDCALL, VT_I4, 0, {} };
    return &GetDlControlInfo;
  }
 
public:
  BEGIN_SINK_MAP(WebBrowserAmbient)
    SINK_ENTRY_INFO(ID_WEBBROWSER_AMBIENT, IID_NULL, DISPID_AMBIENT_DLCONTROL, &WebBrowserAmbient::GetDlControl, GetDlControlInfo())
  END_SINK_MAP()
 
public:
  int __stdcall GetDlControl()
  {
    return DLCTL_NO_SCRIPTS
      | DLCTL_NO_JAVA
      | DLCTL_NO_RUNACTIVEXCTLS
      | DLCTL_NO_DLACTIVEXCTLS
      | DLCTL_DOWNLOADONLY
      | DLCTL_NO_FRAMEDOWNLOAD
      | DLCTL_NO_BEHAVIORS
      | DLCTL_NOFRAMES
      | DLCTL_SILENT;
  }
 
  IDispatch* GetDispatch()
  {
    return reinterpret_cast<IDispatch*>(
      boost::implicit_cast<IDispEventSimpleImpl*>(this));
  }
};
 
class TestWindow :
  public ATL::CWindowImpl<TestWindow>,
  public WTL::CMessageFilter
{
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()
 
private:
  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 = {};
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(m_webBrowser.Create(m_hWnd, empty, L"",
        WS_CHILD | WS_VISIBLE | WS_BORDER | WS_TABSTOP | WS_GROUP, 0,
        IDC_WEBBROWSER_CONTROL)),
      -1);
 
    ATL::CComPtr<IAxWinAmbientDispatchEx> ambientDispatch;
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(m_webBrowser.QueryHost(&ambientDispatch)),
      -1);
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(ambientDispatch->SetAmbientDispatch(m_ambient.GetDispatch())),
      -1);
 
    ATLENSURE_RETURN_VAL(
      SUCCEEDED(m_webBrowser.CreateControl(L"http://enable-javascript.com/ja/")),
      -1);
 
    return 0;
  }
 
  void OnDestroy()
  {
    PostQuitMessage(0);
  }
 
  WebBrowserAmbient m_ambient;
  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));
}

このプログラムは、次のようなことをやっています。

一番大事なのがWebBrowserAmbientクラスのGetDlControl関数で、ID_WEBBROWSER_AMBIENTのgetに対する処理を書いているところです。ここを変えればWebBrowserコントロールの挙動が変化します。


ここから先は、atl110.dllを使うようにした理由の話です。

ActiveXコントロールからのアンビエントプロパティの取得を受け付けるのはATL::CAxHostWindowです。このクラスのInvoke関数の実装を読みました。すると、SetAmbientDispatchで設定したIDispatchオブジェクトに処理が渡されるには、ちょっとした条件を満たしている必要があることが分かりました。その条件とは、

  • モジュール自身のリソースとしてタイプライブラリがある。
  • そのタイプライブラリの中にIAxWinAmbientDispatchExの情報が含まれている。

atl110.dllには、ATL::CAxHostWindowの実装が含まれており、しかもこの条件を満たしています。そのため、atl110.dllを使うのが簡単だと考えました。

今回、Visual C++ 2012を使った理由もそこにあります。Visual C++ 2013以降、ATLのDLLは廃止されたので、この方法は使えなくなりました: MSDNライブラリのATL アプリケーションの再配布


次回は、VC++ 2013以降でATL::CAxHostWindowを使いつつ、SetAmbientDispatchを使う方法のコードを載せます。


スポンサード リンク

この記事のカテゴリ

  • ⇒ WebBrowserコントロールでJavaScriptほか色々禁止する (3) DISPID_AMBIENT_DLCONTROLその1
  • ⇒ WebBrowserコントロールでJavaScriptほか色々禁止する (3) DISPID_AMBIENT_DLCONTROLその1
  • ⇒ WebBrowserコントロールでJavaScriptほか色々禁止する (3) DISPID_AMBIENT_DLCONTROLその1