Handle HTMLElementEvents2 when DWebBrowserEvents2 has been handled using ATL's macros - c++

I'm creating a Browser Helper Object using VS2008, C++. My class has been derived from IDispEventImpl among many others
class ATL_NO_VTABLE CHelloWorldBHO :
public CComObjectRootEx<CComSingleThreadModel>,
public CComCoClass<CHelloWorldBHO, &CLSID_HelloWorldBHO>,
public IObjectWithSiteImpl<CHelloWorldBHO>,
public IDispatchImpl<IHelloWorldBHO, &IID_IHelloWorldBHO, &LIBID_HelloWorldLib, /*wMajor =*/ 1, /*wMinor =*/ 0>,
public IDispEventImpl<1, CHelloWorldBHO, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 1>
{
.
.
.
BEGIN_SINK_MAP(CHelloWorldBHO)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, OnDocumentComplete)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, BeforeNavigate2)//Handle BeforeNavigate2
END_SINK_MAP()
.
.
.
}
As apparent from the code above, my DWebBrowserEvents2 are handled using the ATL's macros. Now I want to handle HTMLElementEvents2 (to detect clicks, scrollbars, etc.) For that, I QueryInterface() the IHTMLDocument2 object for IHTMLElement, QueryInterface() that for IConnectionPointContainer and call IConnectionPointContainer::FindConnectionPoint(DIID_HTMLElementEvents2). (See msdn's article on handling HTMLElementEvents2). The problem is, when I overwrite IDispatch::Invoke in my class, the DWebBrowserEvents2 handles (created using ATL macros) fail. Is there a way to handle HTMLElementEvents2 without overwriting Invoke, or implement invoke in such a way that it only handles HTMLElementEvents2?
Thanks, Any help will be appreciated.

There is no real need to override Invoke or get IConnectionPointContainer. Since this is an ATL project, Implementing another IDispEventImpl:
public IDispEventImpl<2, CHelloWorldBHO, &DIID_HTMLTextContainerEvents2, &LIBID_MSHTML, 4, 0>
does the trick. Then, sink the entry as:
SINK_ENTRY_EX(2, DIID_HTMLTextContainerEvents2, DISPID_ONSCROLL, OnScroll)
In OnDocumentComplete, call IWebBrowser2::get_Document, IHTMLDocument2::get_body, and then call DispEventAdvise to start receiving events.
Note that I've used DIID_HTMLTextContainerEvents2 instead of DIID_HTMLElementEvents. That's because the body object does not support HTMLElementEvents2, and for my purpose (to handle scrolling) this works just fine!

Related

Upgrading COM call-back interface

We have a problem with COM Connection point callback interfaces
In our Sample.idl we have few call-back interfaces ISomeEvents
interface ISomeEvents : IUnknown
{
HRESULT Event1([in]int nData);
HRESULT Event2([in]int nData);
HRESULT Event3([in]int nData);
}
And in the CoClass we have the following statement
coclass MyComp
{
[default] interface IMyInterface;
interface IMyInterFace2;
[default, source] interface ISomeEvents;
};
Now whenever we add new interfaces as part of enhancement,this does not break the existing client, but if the enhancement has
Any modifications to call-back then we endup updating the interface ISomeEvents, which is breaking existing clients, we are forced to do this because I think we can
Have only one [defaut,source] Interface.
Can anyone tell me What is the workaround for this?
Regards
tom
If you are developing a COM object where the client(s) are live/in production, you really shouldn't change an existing interface, any interface. That has always been the fundamental rule of COM development: "interfaces are immutable"; exceptions are made during early development, of course.
(Here's a quote for example: "COM interfaces are immutable. You cannot define a new version of an old interface and give it the same identifier." (http://msdn.microsoft.com/en-us/library/windows/desktop/ms688484(v=vs.85).aspx). Look up "interfaces are immutable" on the web for plenty more)
In this case, you should create new interfaces ISomeEvents2, ISomeEvents3, so on as needed, and have those interfaces inherit from the previous version. Make the new interface your new default source.
interface ISomeEvents1 : ISomeEvents
{
/* new enhanced events here, for use by newly compiled clients */
}
Eventually, if you need more changes:
interface ISomeEvents2 : ISomeEvents1
{
/* Even newer enhanced events here, for even newer clients */
}
And so on.

Exposing methods to JS on a CAxWindow from a BHO ( C++ )

I have this BHO which I successfully exposed method to JS from it using this thread: Calling BHO method from Javascript?.
When I open a CAxWindow in order to host HTML docs, I'd like to use this exported method but it seems that it doesn't work for that window as well.
I tried to make a custom class like:
class Bubble:
public CAxWindow,
public IDispEventImpl<1, Bubble, &DIID_DWebBrowserEvents2, &LIBID_SHDocVw, 1, 0>
{
public:
BEGIN_SINK_MAP(Bubble)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE , OnDocumentComplete)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_DOWNLOADCOMPLETE , OnDownloadComplete)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, BeforeNavigate2)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_ONQUIT, OnQuit)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR, NavigateError)
SINK_ENTRY_EX(1, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2 , OnNavigateComplete2)
END_SINK_MAP()
To try repeat the process of exposing the methods on document complete but it seems that the event is not being fired.
So basically my question is: is there anyway to expose methods to js on my CAxWindow?
Many thanks!
IDispEventImpl implements sink interface to handle event methods calls. You cannot extend it with your own additional methods directly. Additionally, JavaScript does not really see this interface from scripting code because it is connected to ActiveX control site, not the scripting engine. IDispEventImpl is at all a simplified implementation of IDispatch COM interface, reference counter free, suitable for event IDispatch::Invoke call on the connection point sink interface.
You need to either implement a type library enabled COM object with IDispatch interface (type library is used by scripting engine to discover actual methods), or custom IDispatch or IDispatchEx interface implementation (yes, this can be implemented directly on CAxWindow class as additional base class/interface) handling method name resolution without type library. Then you will pass this object to the scripting engine as external object or otherwise.

Should I write components in Delphi instead of C++ Builder? How do I add events to a component?

I use C++ Builder (XE2) and I would need to develop some VCL components that would also be used in Delphi. As I understand C++ Builder supports Delphi code and Delphi components but not the other way around? If so, it would be better to start writing it in Delphi so that I don't do a double job?
Second part of my question is more technical; I know how to add a property in a VCL component but don't know how to add events. Could someone give me an example please (no matter Delphi or C++ Builder).
Thanks.
As I understand C++ Builder supports Delphi code and Delphi components but not the other way around?
On source level - yes.
But if you choose to distribute your library sourceless - BPL+DCP+DCU - then it would not matter, except for maybe some small incompatibilities, like Delphi lacking [] operator and C++ lacking virtual overloaded constructors.
Turns out this estimation was wrong. Read Remy's comment below
Most close to you example ov events is the VCL itself, sources are usually shipped with Delphi. If you have Delphi Starter/Trial without VCL sources - then get any opensource VCL library or component. Such as JediVCL or basically almost ANY VCL component with sources. For example any "FWS" (Free with sources) component 99% uses events.
Most basic and widely used event notifications type - such as TButton.OnClick, TForm.OnCreate and a lot of - is TNotifyEvent
Open Delphi Help for that type. Scroll to "See also" and see two links there.
Procedural types are like int (*f)(void) in C.
Events creating manual
Such as:
(borrowed code from about.delphi.com)
type
TState = (stStarted, stStopped);
TStateChangeEvent = procedure
(Sender : TObject; State : TState) of object;
TThirdComponent = class(TSecondComponent) // or whatever
private
{ Private declarations }
FState : TState;
FOnStart,
FOnStop : TNotifyEvent;
FOnStateChange : TStateChangeEvent;
protected
{ Protected declarations }
public
{ Public declarations }
constructor Create(AOwner : TComponent); override;
destructor Destroy; override;
procedure Start; override;
procedure Stop; override;
property State : TState
read FState;
published
{ Published declarations }
property OnStart : TNotifyEvent
read FOnStart
write FOnStart;
property OnStateChange : TStateChangeEvent
read FOnStateChange
write FOnStateChange;
property OnStop : TNotifyEvent
read FOnStop
write FOnStop;
end
Then you can do
procedure TThirdComponent.Start;
begin
inherited;
FState := stStarted;
if Assigned(OnStart) then OnStart(Self);
if Assigned(OnStateChange) then
OnStateChange(Self, State);
end;

Implementing a GObject interface in C++

I try to implement a GType interface in C++ using Glibmm (part of Gtkmm). The object will be passed to an API in C. Unfortunately, the documentation for gtkmm does not cover many details of how it wraps the GObject system.
What I have so far:
class MonaCompletionProvider : public gtksourceview::SourceCompletionProvider, public Glib::Object
{
public:
MonaCompletionProvider();
virtual ~MonaCompletionProvider();
Glib::ustring get_name_vfunc() const;
// ... and some more
}
All method and constructor implementations are empty. The code is used like this:
Glib::RefPtr<MonaCompletionProvider> provider(new MonaCompletionProvider());
bool success = completion->add_provider(provider);
success will be false after executing this code and the following message appears in the command line:
(monagui:24831):
GtkSourceView-CRITICAL **:
gtk_source_completion_add_provider:
assertion
`GTK_IS_SOURCE_COMPLETION_PROVIDER
(provider)' failed
It seems that the underlying gobj() is not aware that it is supposed to implement this interface. If the class does not derive from Glib::Object, gobj() even returns null. I hope that I do not have to write a GObject implementing this interface in C manually.
So what is the correct way to do this? Thanks in advance.
PS: For those who are interested: SourceCompletionProvider
Finally, I found a solution.
Class definition (order of subclasses matters):
class MonaCompletionProvider : public Glib::Object, public gtksourceview::SourceCompletionProvider {
...
Constructor (again, order matters):
MonaCompletionProvider::MonaCompletionProvider() :
Glib::ObjectBase(typeid(MonaCompletionProvider)),
Glib::Object(),
gtksourceview::SourceCompletionProvider() {
...
Solution found by inspecting how it has been done in Guikachu.

Outlook Add-in. How to manage Items Events

I'm doing an add-in for Outlook 2007 in C++.
I need to capture the events like create, change or delete from the Outlook Items (Contact, Appointment, Tasks and Notes) but the only information/examples I've found are for Visual Basic so I don't know how to connect the event handler.
Here is some information related: http://msdn.microsoft.com/en-us/library/bb208390(v=office.12).aspx
Any help is welcome :) Thanks
Update
Sorry for taking this long to update, I've been out of town. I have some doubts/problems that you may know how to help.
In my case, I'm taking this project that was started so I'm a bit confused about all this. I have the class OutlookAddin that derives from IDTExtensibility2. I also have this other class, called AutoSync, were I'd like to do all the methods when the event fires and so. An object of this class is initialized in OutlookAddin.cpp OnStartupComplete.
According to your post MyClass should extends from IDispEventSimpleImpl<1 /*N*/, MyClass, &__uuidof(Outlook::ItemsEvents)> but which one of them? OutlookAddin or AutoSync ?
Where I should put this code also?
CComPtr<Outlook::MAPIFolder> folder;
// get the folder you're interested in
CComPtr<Outlook::_Items> items;
hr = folder->get_Items(&items);
hr = MyItemEvents::DispEventAdvise(items, &__uuidof(Outlook::ItemsEvents));
typedef IDispEventSimpleImpl<1 /*N*/, MyClass,
&__uuidof(Outlook::ItemsEvents)> MyItemEvents;
I've read the links you posted but still having these doubts...
Update 2
This is more complicated to understand than I though in a first instance.
So I have like this:
OutlookAddin.h
class OutlookAddin :
public IDTExtensibility2,
public IDispEventSimpleImpl<1, OutlookAddin, &__uuidof(Outlook::ItemEvents)>
...
BEGIN_SINK_MAP(OutlookAddin)
SINK_ENTRY_INFO(1, __uuidof(Outlook::ItemEvents), 0xf002, OutlookAddin::OnItemChange, &OnSimpleEventInfo)
END_SINK_MAP()
...
void __stdcall OnItemChange();
'OnSimpleEventInfo' is defined like:
extern _ATL_FUNC_INFO OnSimpleEventInfo;
_ATL_FUNC_INFO OnSimpleEventInfo = {CC_STDCALL,VT_EMPTY,0};
then in OutlookAddin.cpp, OnConnection method:
CComPtr<Outlook::MAPIFolder> folder;
CComPtr<Outlook::_Items> items;
OutlookWorker::GetInstance()->GetNameSpacePtr()->GetDefaultFolder(olFolderContacts, &folder);
folder->get_Items(&items);
DispEventAdvise(items, &__uuidof(Outlook::ItemsEvents));
being 'OutlookWorker::GetInstance()->GetNameSpacePtr()' the _NameSpacePtr where all the environment is kept.
The expected behaviour here is to fire the function 'OnItemChange' from OutlookAddin class when an ContactItem is created/edited/deleted but that's not happening... I changed a little bit the structure to everything is in the main class OutlookAddin. Then on the function 'OnItemChange' I'll start the object of 'AutoSync' that I told you before.
Anyway I'm following the articles you gave me, really useful, thank you. Do you still have any other suggestion for me?
Thanks your patience.
Its been a while, but you should get these item events by advising for Folder.Items:
CComPtr<Outlook::MAPIFolder> folder;
// get the folder you're interested in
CComPtr<Outlook::_Items> items;
hr = folder->get_Items(&items);
hr = MyItemEvents::DispEventAdvise(items, &__uuidof(Outlook::ItemsEvents));
Where your class MyClass derives from:
IDispEventSimpleImpl<1 /*N*/, MyClass, &__uuidof(Outlook::ItemsEvents)>
And MyItemEvents is:
typedef IDispEventSimpleImpl<1 /*N*/, MyClass,
&__uuidof(Outlook::ItemsEvents)> MyItemEvents;
N identifies your sink here. Then there is the joy of the remaining macros to setup and the handler functions to implement - i refer you to this and this article for examples and to the dispinterface ItemsEvents that you can look up in oleview.exe.
Regarding update 1:
If you want to receive the events in AutoSync, implement the interface there - you are not required to sink the events to any specific instance. However, you know your design best :)
I'd just personally keep as much logic out of the central addin class as possible.
The registration code would go into some method of the class implementing the events then and called whenever it should start to receive events, while the typedef would be e.g. well placed in the class' declaration.
Regarding update 2:
From a quick glance it looks mostly right, but OnItemChange() takes one parameter - an IDispatch:
_ATL_FUNC_INFO AtlCallDispatch = {CC_STDCALL, VT_EMPTY, 1, {VT_DISPATCH}};