Upgrading COM call-back interface - c++

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.

Related

Implementing IServiceProvider in an MFC application

I need to implement the IServiceProvider interface in an open source MFC application; specifically my TTSApp application.
I am attempting to add support for the IAccessibleApplication interface, which is used by screen readers to obtain information about an application's name and version information.
It appears that Google Chrome implements the IServiceProvider interface via the AXPlatformNodeWin class, which is derived from the CComObjectRootEx class and other classes and interface. The problem is that an MFC application does not use the CComObjectRootEx class; it is used by ATL.
I have found the IServiceProviderImpl Class. Unfortunately, I cannot find any information on how it fits in the context of an application. Which class in my class hierarchy needs to be derived from the IServiceProviderImpl Class; my CWinApp derived class, my CDialogEx derived class, or some other class?
I learned a great deal while on my quest in search for an answer to this question. During the quest I fell down the rabbit hole (Alice's Adventures in Wonderland by Charles Lutwidge Dodgson, a.k.a. Lewis Carroll) only to find Cthulhu (The Call of Cthulhu by H. P. Lovecraft) waiting for me.
My initial research led me to the following macros defined in afxwin.h.
DECLARE_INTERFACE_MAP
BEGIN_INTERFACE_MAP
END_INTERFACE_MAP
BEGIN_INTERFACE_PART
END_INTERFACE_PART
The best documentation I could find for these macros is in the TN038: MFC/OLE IUnknown Implementation technical note. A good sample demonstrating the use of these macros and the implementation of the QueryService function is the TstCon sample.
Of course, this led to another question, what window do I need to do this for? To answer this question I looked at the source code of a certain screen reader to see how it uses the IAccessibleApplication interface.
The following function, though not the actual code used, demonstrates the technique (I cannot share the actual code since the screen reader is not open source).
std::wstring GetApplicationNameUsingTheIAccessibleApplicationInterface(
HWND hwnd, long idObject, long idChild)
{
CComPtr<IAccessible> acc;
CComVariant var;
auto hr = AccessibleObjectFromEvent(hwnd, idObject, idChild, &acc, &var);
if (hr != S_OK) return L"";
if (!acc) return L"";
CComQIPtr<IServiceProvider> serviceProvider = acc;
if (!serviceProvider) return L"";
CComQIPtr<IAccessibleApplication> application;
hr = serviceProvider->QueryService(
IID_IAccessible, __uuidof(IAccessibleApplication),
reinterpret_cast<void**>(&application));
if (FAILED(hr)) return L"";
if (!application) return L"";
CComBSTR appName;
hr = application->get_appName(&text);
if (FAILED(hr)) return L"";
return appName.m_str;
}
This function, or something like it, is called from our WinEventProc callback function in response to the EVENT_OBJECT_FOCUS event. This indicates that I need to do this for every window that can gain focus.
Armed with what I thought was the answer to my question, I dove in and implemented the IAccessibleApplication interface and added the necessary code to all of my focus-able windows. Much to my horror, my QueryService function was never called. When I debugged the screen reader to find out why, I discovered that the implicit QueryInterface implied by the following line of code failed.
CComQIPtr<IServiceProvider> serviceProvider = acc;
This led to a long and arduous quest to discover why the call to QueryInterface was failing.
I was working on a personal project at first so I could not call upon resources I have at my employer. Then, entirely by chance, I was assigned a task that required me to provide information on how to implement the IAccessible2 interface in a C++ application to a client who needed the information to help them make their applications more accessible. Hurrah, I was finally able to call upon coworkers for aid!
My coworker guided me down the correct path.
Create a customized version of the IAccessibleProxyImpl class and CAccessibleProxy class using source code obtained from atlacc.h.
Add a COM_INTERFACE_ENTRY for IAccessibleApplication in the COM_MAP (BEGIN_COM_MAP/END_COM_MAP) for my custom IAccessibleProxyImpl class.
Use the BEGIN_SERVICE_MAP, END_SERVICE_MAP, and SERVICE_ENTRY macros to provide an implementation of the IServiceProvider interface.
Provide an override for the CWnd::CreateAccessibleProxy function to cause my windows to use my custom accessible proxy and thus my implementation of the IAccessibleApplication interface.
Now the screen reader uses the application name I provide for the IAccessibleApplication interface for my application.
The application I did this for is open source. It is my TTSApp application. I have also made an example that demonstrates how to use a similar technique to support the IAccessible2 interface available here.
I am sharing this in the hopes that the information will prove to be helpful.

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.

What is a good way to get a C++ and WPF app to communicate

I have a C++ dll which is a plug-in to ADOBE Acrobat. It needs to talk often ( to and fro) and with a fair amount of complicated data-structures to a WPF process.
Any thoughts of what might be the best way to go . Need something that is a little long term and maintainable, in other words would love some ideas around something that lets both process make what looks like methods calls and some infrastructure piece does the marshaling and dispatch . I've tried Windows messages but ran into some conflict issues on ADOBE, also not really interested in anything that causes the dll to get adobe to load the CLR. Only other things that come to my mind are named pipes or http.
Thanks in Advance
Named pipes could do but you won't get a feeling of just calling functions. Named pipe are quite low-level IPC. Other IPC options are:
Windows RPC, you definitely get a feeling of just calling functions.
What about hosting a COM object in WPF application and calling it from the Adobe plugin?
I would go with COM:
Implement an interface in WPF app
generate a typelib (e.g by using regasm)
import the typelib into C++ dll
communicate
if you need bidirectional communication, C++ dll can also implement a COM interface which is then accessed from WPF application.
This is what I have used to connect legacy C++ app with new .NET service, and it works great. The biggest issue is to find people who know COM, but fortunately this doesn't require a deep understanding of COM.
your hint with COM is very intersting. I tried to implement this concept.
I have created an interface in my WPF CallDllFromWpf3Interface project:
using System.Runtime.InteropServices;
namespace CallDllFromWpf3Interface
{
[Guid("F6E0E2E8-CCC6-487B-8BF1-261265061E6A")]
public interface SetValueInterface
{
void SetValue(int value);
}
}
Then I have generated the typelib with the regasm tool:
regasm CallDllFromWpf3Interface.exe /tlb
With the "oleview" tool I can see the typelib and the interface.
The next step was to create a c++ dll project called "CallSetValueInterface".
In my CallSetValueInterface.cpp file I wrote this lines:
#import "D:\Thomas\Programming\WPF\Basics\CallDllFromWpf\CallDllFromWpf3Interface\CallDllFromWpf3Interface\bin\Debug\CallDllFromWpf3Interface.tlb"
void
CallSetValueInterface::startAcq(void)
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
CallDllFromWpf3Interface::SetValueInterfacePtr Svip("f6e0e2e8-ccc6-487b-8bf1-261265061e6a");
Svip->SetValue(55);
Svip = NULL;
CoUninitialize();
}
After a successful build of the dll project I copied "CallSetValueInterface.dll" to the "CallDllFromWpf3Interface" project.
Finally I changed my WPF code to:
#region SetValueInterface Members
public void SetValue(int value)
{
MyValue = value;
}
#endregion
[DllImport("CallSetValueInterface.dll", EntryPoint = "startAcq", ExactSpelling = true, SetLastError = true)]
public static extern void StartAcqFromDll();
private void Button_Click(object sender, RoutedEventArgs e)
{
StartAcqFromDll();
}
And when the debugger came to StartAcqFromDll() there occurred an error dialog "An unhandled exception of type 'System.Runtime.InteropServices.SEHException' occurred in CallDllFromWpf3Interface.exe".
Does anybody know whats going wrong?
Regards,
ThomasL.

Problems accessing uccapi.dll COM interface C++

I'm working on a project involving the Microsoft Unified Communications Client API; uccapi.dll. I'm also using Codegear C++Builder 2010, not Visual Studio. After registering the dll with regsvr32 and importing it as type library into C++Builder 2010, uccapi_tlb- and uccapi_ocx-files were generated. When having imported these into my new project I'm trying to follow the msdn guideline for creating a Office Communicator Client able of signing into the Office Communication server.
In this regard I have two questions:
What is the correct way of accessing the com-interfaces made available through the ocx?
I've so far found several ways of creating access points, such as.
TCOMIUccPlatform plat;
plat = CoUccPlatform::Create();
and
IUccPlatformPtr im;
im = CreateComObject(CLSID_UccPlatform);
and
IUccPlatform* pIUccPlatform;
hr = CoCreateInstance(CLSID_UccPlatform,
NULL,
CLSCTX_INPROC_SERVER,
__uuidof(IUccPlatform),
(void**)&pIUccPlatform);
and
IUccPlatformPtr pIPlat;
pIPlat.CreateInstance(__uuidof(IUccPlatform));
The three first seem to work well. The latter will give me an Assertion failed: intf!=0 error with 0×40000015 exception. Using any of the three top ones I can access methods and initialize the platform interface.
However when trying any of the same tactics to access any other interface, such as IUccContext, IUccUriManager or IUccUri, all of which have a clsid defined in the _tlb.h file, I either get a "class not registered" error in the first two cases, or a hresult failure in the third case. Which brings me to my next question.
Using ole-viewer all interfaces are registered as they should. Why wouldn't all co-creatable classes in the dll be registered when registering the dll? And what could be the reasons why don't they act similarly?
Edit1 from UCCAPILib_tlb.h:
//
// COCLASS DEFAULT INTERFACE CREATOR
// CoClass : UccPlatform
// Interface: TCOMIUccPlatform
//
typedef TCoClassCreatorT<TCOMIUccPlatform, IUccPlatform, &CLSID_UccPlatform, &IID_IUccPlatform> CoUccPlatform;
//
// COCLASS DEFAULT INTERFACE CREATOR
// CoClass : UccUriManager
// Interface: TCOMIUccUriManager
//
typedef TCoClassCreatorT<TCOMIUccUriManager, IUccUriManager, &CLSID_UccUriManager, &IID_IUccUriManager> CoUccUriManager;
This issue is already being discussed in detail in the Embarcadero forums.

Need help with events in COM in pure C++!

guys! Very important question:
Please, look at my project (300Kb). I can`t use MFC/ATL, pure C++ only.
I have COM library (niapi.dll), but no sources/headers available, dll only.
There is class for connecting to server (NiApi::SrvrSession), class has login event handler (NiApi::SrvrSession::OnLogin).
I used
#import "NiApi.dll"
to generate wrappers/information,
then
ISrvrSessionPtr session(L"NiApi.SrvrSession");
to create object, then trying
session->put_OnLogin();
to assign events, but there is no one put_On or such member.
niapi.tlh have _ISrvrSessionEvents struct inside, but it have no relations with SrvrSession.
I need to use events from NiApi::SrvrSession for handling connection status.
Please help or my boss kill me! (sorry for broken english, I read better than speak;)
COM events are handled via connection points. You need to write your own COM object that implements whichever event interface you are interested in. Then you need to connect it to the COM object that fires the events. First you QI the COM object for its IConnectionPointContainer, then find the corresponding connection point of the GUID of the event interface. The you call its Advise method to connect it to your event sink.
class CSrvrSessionEvents: public _ISrvrSessionEvents
{
public:
HRESULT OnLogin(long LoginResult)
{
// do something
return S_OK;
}
// implement rest of _ISrvrSessionEvents
};
ISrvrSession* pSrvrSession = ...; // get ISrvrSession from somewhere
_ISrvrSessionEvents* pSrvrSessionEvents = new CSrvrSessionEvents();
IConnectionPointContainer* pCPC = NULL;
pSrvrSession->QueryInterface(IID_IConnectionPointContainer, &pCPC);
IConnectionPoint* pCP = NULL;
pCPC->FindConnectionPoint(__uuidof(_ISrvrSessionEvents), &pCP);
DWORD dwCookie = 0;
pCP->Advise(pSrvrSessionEvents, &dwCookie);
pSrvrSession->Connect(); // I assume this fires some events
pCP->Unadvise(dwCookie);
What is really necessary, is to carefully read
codeproject_TEventHandler.
All explained here.
The put_ prefix is the default prefix for the raw interface (customizable via the raw_property_prefixes attribute). Since you are not using the raw interface, use session->OnLogin=... instead.
For event handling see ADO Events Model Example (VC++)