前回(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を使う話、無事終わりました。なお、ウェブブラウザコントロールの話はまだ続きます。
スポンサード リンク |