前回(WebBrowserコントロールでJavaScriptほか色々禁止する (3) DISPID_AMBIENT_DLCONTROLその1)の続きです。ATLのDLLが廃止されたVisual C++ 2013以降を使いつつ、ATL::CAxWindowでDISPID_AMBIENT_DLCONTROLやその他のアンビエントプロパティを使う方法です。


分かってしまえば簡単なことでした。IAxWinAmbientDispatchExを自身のタイプライブラリに含めてしまえば良いのです。というわけで、早速コードを書いておきます。

まずは、以下のようなIDLファイルを作ります。

import "atliface.idl";
 
[
  uuid(c595583a-0d92-4490-94fe-42e3ab446071)
]
library Test
{
  interface IAxWinAmbientDispatchEx;
}

これをmidl Test.idlとしてコンパイルし、Test.tlbを得ます。

次にリソーススクリプトを作ります。

1 TYPELIB "Test.tlb"

こちらもrc Test.rcとしてコンパイルします。

そうしたら、次はソースコードです。前回のものと比べ、_ATL_DLLとquick_exitとconstexprのdefineを削除しただけです。Visual C++ 2015にはquick_exitもconstexprもあります。

#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 <mshtmdid.h>
 
#include <atlbase.h>
#include <atltypes.h>
#include <atlwin.h>
 
#include <atlapp.h>
#include <atlcrack.h>
 
#include <boost/implicit_cast.hpp>
 
class Module : public ATL::CAtlExeModuleT<Module>
{
};
 
Module module;
 
constexpr UINT ID_WEBBROWSER_AMBIENT = 0;
 
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));
}

これをcl test.cpp test.resなど、先ほど作ったリソースを含めてコンパイルします。すると、前回のVisual C++ 2012対象に書いたコードと同じように動作します。


これを明らかになるまで調べた結果を簡潔に書きます。

ATL::CAxHostWindowは、基底クラスにIDispatchImpl<IAxWinAmbientDispatchEx, &__uuidof(IAxWinAmbientDispatchEx), &CAtlModule::m_libid, 0xFFFF, 0xFFFF>があります。また、ATL::CAxHostWindow::Invokeが以下のように実装されています。

STDMETHOD(Invoke)(
	_In_ DISPID dispIdMember,
	_In_ REFIID riid,
	_In_ LCID lcid,
	_In_ WORD wFlags,
	_In_ DISPPARAMS *pDispParams,
	_Out_opt_ VARIANT *pVarResult,
	_Out_opt_ EXCEPINFO *pExcepInfo,
	_Out_opt_ UINT *puArgErr)
{
	HRESULT hr = IDispatchImpl<IAxWinAmbientDispatchEx, &__uuidof(IAxWinAmbientDispatchEx), &CAtlModule::m_libid, 0xFFFF, 0xFFFF>::Invoke
		(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
	if ((hr == DISP_E_MEMBERNOTFOUND || hr == TYPE_E_ELEMENTNOTFOUND) && m_spAmbientDispatch != NULL)
	{
		hr = m_spAmbientDispatch->Invoke(dispIdMember, riid, lcid, wFlags, pDispParams, pVarResult, pExcepInfo, puArgErr);
		if (SUCCEEDED(hr) && (wFlags & DISPATCH_PROPERTYPUT) != 0)
		{
			hr = FireAmbientPropertyChange(dispIdMember);
		}
	}
	return hr;
}

m_spAmbientDispatchに、SetAmbientDispatchで渡したオブジェクトが入っています。したがって、IDispatchImplのInvokeがDISP_E_MEMBERNOTFOUNDかTYPE_E_ELEMENTNOTFOUNDを返さないと、m_spAmbientDispatchが使われることはないのです。

タイプライブラリがリソースに入っていなかったり、タイプライブラリはあるけどIAxWinAmbientDispatchExが入っていなかったりする場合、エラーコードはそのどちらでもないので、うまくいきません。そのため、IAxWinAmbientDispatchExを含むタイプライブラリを含めるという方法に辿り着きました。


DISPID_AMBIENT_DLCONTROLを使う話、無事終わりました。なお、ウェブブラウザコントロールの話はまだ続きます。


スポンサード リンク

この記事のカテゴリ

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