引き続き、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)); } |
このプログラムは、次のようなことをやっています。
- _ATL_DLLを定義する。Visual Studioプロジェクトでビルドする場合の「ATLに動的にリンク」の設定に相当する。atl110.dll(VC++のバージョンによる)を使用するようになる。
- 以前の記事(ATL::IDispEventSimpleImplでIDispatchを実装する)で書いた方法でIDispatchおよびID_WEBBROWSER_AMBIENTを実装する。
- CAxWindow::QueryHost関数でホストオブジェクト(ATL::CAxHostWindow)を得る。そして、IAxWinAmbientDispatchEx::SetAmbientDispatch関数で、ID_WEBBROWSER_AMBIENTを実装したオブジェクトを指定する。参考: MSDNライブラリのコントロールのアンビエント プロパティはどのように設定しますか? (ATL)
一番大事なのが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を使う方法のコードを載せます。
スポンサード リンク |