Index: src/plugin/PluginClass.h |
=================================================================== |
--- a/src/plugin/PluginClass.h |
+++ b/src/plugin/PluginClass.h |
@@ -41,6 +41,102 @@ |
class CPluginMimeFilterClient; |
+/** |
+ * Resource class for COM events. |
+ * If an instance exists, there's an active connection behind it. |
+ * |
+ * \tparam Sink |
+ * subclass of ATL::IDispEventImpl. |
+ * \tparam Source |
+ * Type of event source. Implements IConnectionPoint |
+ */ |
+template<class Sink, class Source> |
+class AtlEventRegistration |
sergei
2016/01/06 08:41:58
I doubt that we need two classes here, AtlEventReg
Eric
2016/01/07 16:24:48
It's not overengineering. You actually want two cl
sergei
2016/02/04 13:45:50
Answered in the message.
Eric
2016/02/04 17:00:27
Perhaps you are unclear about PImpl.
PImpl = Poin
sergei
2016/02/04 19:41:57
I don't want to debate here, but it's not PIMPL, I
Eric
2016/02/04 20:13:25
I don't want to debate here either, so I'll forgo
|
+{ |
+ CComPtr<Sink> consumer; |
+ CComPtr<Source> producer; |
+public: |
+ /** |
+ * \throw std::runtime_error |
+ * If connection is not established |
+ * |
+ * \par Precondition |
+ * - consumer is not nullptr (not checked) |
+ * - producer is not nullptr (not checked) |
+ */ |
+ AtlEventRegistration(Sink* consumer, Source* producer) |
+ : consumer(consumer), producer(producer) |
+ { |
+ auto hr = consumer->DispEventAdvise(producer); |
+ if (FAILED(hr)) |
+ { |
+ DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_SET_SITE, PLUGIN_ERROR_SET_SITE_ADVICE, "AtlEventRegistrationImpl::<constructor> - Advise"); |
+ throw std::runtime_error("DispEventAdvise() failed"); |
sergei
2016/01/06 08:41:57
I would prefer to make private constructor and hav
Eric
2016/01/07 16:24:48
I consider this suggestion an inferior design. See
sergei
2016/02/04 13:45:49
I consider the current impl as overengineering and
Eric
2016/02/04 17:00:27
Yes, I know. You said that already.
sergei
2016/02/04 19:41:57
I have not said whether I like exceptions or not,
Eric
2016/02/04 20:13:25
You'll have to tell me what "logic" you're talking
|
+ } |
+ } |
+ |
+ ~AtlEventRegistration() // noexcept |
+ { |
+ try |
+ { |
+ // Smart pointer members ensure that this statement does not fail because of life span |
+ auto hr = consumer->DispEventUnadvise(producer); |
+ if (FAILED(hr)) |
+ { |
+ DEBUG_ERROR_LOG(hr, PLUGIN_ERROR_SET_SITE, PLUGIN_ERROR_SET_SITE_UNADVISE, "AtlEventRegistrationImpl::<destructor> - Unadvise"); |
+ } |
+ } |
+ catch (...) |
+ { |
+ // Suppress exceptions |
+ } |
+ } |
+}; |
+ |
+/** |
+ * Resource holder for COM events. |
+ * |
+ * Manages the life time of an 'AtlEventRegistration' instance. |
+ * Allows events to be started and stopped at some time other than construction/destruction. |
+ * |
+ * \tparam Sink |
+ * subclass of ATL::IDispEventImpl. |
+ * \tparam Source |
+ * Type of event source. Implements IConnectionPoint |
+ */ |
+template<class Sink, class Source> |
+class AtlEventRegistrationHolder |
+{ |
+ typedef AtlEventRegistration<Sink, Source> ImplType; |
+ std::unique_ptr<ImplType> impl; |
+ |
+public: |
+ AtlEventRegistrationHolder() |
+ : impl(nullptr) |
sergei
2016/01/06 08:41:58
This line as well as the empty constructor and des
Eric
2016/01/07 16:24:48
Done.
|
+ {} |
+ |
+ ~AtlEventRegistrationHolder() // = default; |
+ {} |
+ |
+ bool Start(Sink* consumer, Source* producer) // noexcept |
+ { |
+ try |
+ { |
+ impl = new ImplType(consumer, producer); |
+ } |
+ catch (...) |
+ { |
+ impl = nullptr; |
sergei
2016/01/06 08:41:58
This line is not needed.
Eric
2016/01/07 16:24:48
It is. Start() has the semantics of stopping whate
sergei
2016/02/04 13:45:50
Actually expecting that Start can be called severa
|
+ return false; |
+ } |
+ return true; |
+ } |
+ |
+ Stop() // noexcept |
+ { |
+ impl = nullptr; |
sergei
2016/01/06 08:41:58
it's correct, but I find it better to use impl.res
Eric
2016/01/07 16:24:48
Done.
|
+ } |
+}; |
class ATL_NO_VTABLE CPluginClass : |
public ATL::CComObjectRootEx<ATL::CComMultiThreadModel>, |
@@ -73,7 +169,6 @@ |
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete) |
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_WINDOWSTATECHANGED, OnWindowStateChanged) |
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_COMMANDSTATECHANGE, OnCommandStateChange) |
- SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_ONQUIT, OnOnQuit) |
END_SINK_MAP() |
CPluginClass(); |
@@ -132,9 +227,7 @@ |
void STDMETHODCALLTYPE OnDocumentComplete(IDispatch* frameBrowserDisp, VARIANT* /*urlOrPidl*/); |
void STDMETHODCALLTYPE OnWindowStateChanged(unsigned long flags, unsigned long validFlagsMask); |
void STDMETHODCALLTYPE OnCommandStateChange(long command, VARIANT_BOOL enable); |
- void STDMETHODCALLTYPE OnOnQuit(); |
- void Unadvise(); |
- |
+ |
void ShowStatusBar(); |
bool IsStatusBarEnabled(); |
@@ -144,6 +237,12 @@ |
* It's values are set and reset solely in SetSite(). |
*/ |
CComPtr<IWebBrowser2> m_webBrowser2; |
+ /** |
+ * Event manager for events coming from our site object. |
sergei
2016/01/06 08:41:58
Why do we need this comment?
Eric
2016/01/07 16:24:48
Because we have multiple sources of events.
We ha
sergei
2016/02/04 13:45:49
I mean, it's clear from line `AtlEventRegistration
Eric
2016/02/04 17:00:27
It's a Doxygen comment.
|
+ */ |
+ AtlEventRegistrationHolder<CPluginClass, IWebBrowser2> browserEvents; |
+ bool detachedInitializationFailed; |
sergei
2016/01/06 08:41:58
The member naming is not consistent with other mem
sergei
2016/01/06 08:41:58
Since it's accessed from different threads, there
Eric
2016/01/07 16:24:48
You're raising a valid issue, certainly, but I've
Eric
2016/01/07 16:24:48
Right. It's not there because "m_" prefixes are ag
|
+ |
HWND m_hBrowserWnd; |
HWND m_hTabWnd; |
HWND m_hStatusBarWnd; |
@@ -157,7 +256,6 @@ |
NotificationMessage notificationMessage; |
- bool m_isAdvised; |
bool m_isInitializedOk; |
// Atom pane class |