4

Use of IUserNotification2 from Microsoft

I'm using IUserNotification2 to show a notification to the software user.

I use the existing implementation from microsoft, see here. (Note, I removed the standard headers and simplified a bit).

#include <Shobjidl.h> //IUserNotification2 interface header

void NotifyUser(const std::wstring &title,
    const std::wstring &text){
if (!SUCCEEDED(CoInitializeEx(nullptr, COINIT_MULTITHREADED)))
    throw std::exception("could not init COM");

IUserNotification2 * handleNotification = nullptr;
auto result = CoCreateInstance(CLSID_UserNotification, 0, CLSCTX_ALL, IID_IUserNotification2, (void**)&handleNotification);
if (!SUCCEEDED(result) || !handleNotification) {
    throw std::exception("could not create CLSID_UserNotification");
}
DWORD notif_flags = NIIF_RESPECT_QUIET_TIME|NIIF_WARNING;
result = handleNotification->SetBalloonInfo(title.c_str(), text.c_str(), notif_flags);
if (!SUCCEEDED(result))
    throw std::exception("could not SetBalloonInfo of notification");
if (!SUCCEEDED(handleNotification->Show(nullptr, 5000,nullptr))
        throw std::exception("failed Show of notification");

When doing it without callbacks (i,e showing a message with no action on click events) I have no problem and my code works.

Add a callback on click event

In order to add such a callback, it is requested to pass a IUserNotificationCallback Object to the Show Method of the IUserNotification2 Object.

See here I changed just the call to Show.

Callback cbk;
if (!SUCCEEDED(handleNotification->Show(nullptr, 5000,& cbk)))
        throw std::exception("failed Show of notification");

the Callback class implements IUserNotificationCallback. See the implementation of the class below.

class Callback : public IUserNotificationCallback {
public:
    virtual HRESULT STDMETHODCALLTYPE OnBalloonUserClick(POINT * pt) override {return S_OK;}
    virtual HRESULT STDMETHODCALLTYPE OnContextMenu(POINT * pt) override {}
    virtual HRESULT STDMETHODCALLTYPE OnLeftClick(POINT * pt) override {return S_OK;}

    /// Implementing IUnknown Interface
    virtual ULONG STDMETHODCALLTYPE AddRef()override { return 1; }
    virtual ULONG STDMETHODCALLTYPE Release() override { return 0; }
    virtual HRESULT STDMETHODCALLTYPE QueryInterface(REFIID inRiid, void ** outAddressOfObjectPointer)override {
        if (outAddressOfObjectPointer) {
            if (inRiid == IID_IUnknown || inRiid == IID_IUserNotificationCallback)
            {
                *outAddressOfObjectPointer = this;
                AddRef();
                return NOERROR;
            }
        }
        *outAddressOfObjectPointer = nullptr;
        return E_NOINTERFACE;
    }
};

Now the problem,

Is that when I call the Showmethod, My IUserNotificationCallback objects gets called on QueryInterface of the IUnknown Interface. In a classic COM style. But I received a query for the IID_IMarshall which I checked by adding

else if (inRiid == IID_IMarshal) {
    printf("interface queried is IMarshall\n");

In the query interface method.

BUT WHY should I receive this, nowhere in the documentation it is mentionned that I should answer to this IID. In this case, I let E_NOINTERFACE be returned with a nullptr in the void** parameter.

I this generates a fail of the show Method.

note

I read the code of notifu to see if it could help, but it is basically the same than mine.

Solution

Apparently a working solution is provided by Roman R.. Changing the call to CoInitializeEx to if (!SUCCEEDED(CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED))) makes it works!

Thank you again

Community
  • 1
  • 1
dzada
  • 5,344
  • 5
  • 29
  • 37
  • 1
    It's trying to marshal the interface between apartments (http://www.codeguru.com/cpp/com-tech/activex/apts/article.php/c5529/Understanding-COM-Apartments-Part-I.htm) and cannot do so using typelib marshaller (for whatever reason). See this http://stackoverflow.com/q/1714926/57428 I'd say your best bet is to make your code run in such apartment configuration that marshaling is not needed. If you provide more data on how you specify the threading model in the registration script of your component someone could give further advice on that. – sharptooth Jan 27 '15 at 09:42
  • And btw how do you call `CoInitializeEx()`? – sharptooth Jan 27 '15 at 09:55
  • @sharptooth, the code is in the post: CoInitializeEx(nullptr, COINIT_MULTITHREADED) – dzada Jan 27 '15 at 10:05
  • 1
    Yes, I see now. If you look up the `CLSID_UserNotification` in the registry you'll see that it has "threading model" set to "apartment". Your code asks for MTA. There's no way for calls between your code and the COM server to work without marshalling in this configuration. One option is to ask for `COINIT_APARTMENTTHREADED`. – sharptooth Jan 27 '15 at 13:00
  • 1
    Btw does your code call `CoInitializeEx()` every time that function is called? – sharptooth Jan 27 '15 at 13:12
  • If this function doesn't run in its own thread, the question that sharptooth asks is concerning. You shouldn't initialize COM in threads you don't own, because they might be initialized already or they might try to initialize later with a different apartment type. – acelent Jan 29 '15 at 09:49
  • Hello thank you Sharptooth for the comment, indeed I see the issue, for now I was just testing but it will be a problem. So I guess I should enquire about the COM engine status. Isn't the COM engine able to discard a second initialization if not needed? Also this code is in a Lib, so I might beeing called from a lot of various threads, that are not COM aware and I don't want to bother the user of this to bother about calling CoInitializeEx – dzada Jan 29 '15 at 14:33

2 Answers2

3

nowhere in the documentation it is mentionned that I should answer to this IID

Any COM object might be queried for an interface, which is unknown to the object. It is normal and more to this, it is mandatory for a COM object to respond to this correctly and then any COM object has to implement IUnknown and this method.

So you are basically supposed to set *ppvObject to NULL and return E_NOINTERFACE if you don't implement the interface, or otherwise return S_OK and initialize *ppvObject with a valid pointer, prepared for IUnknown::Release call once the caller does not need the pointer anymore.

Your solution to

Apparently the solution (suggested by a friend) is too always give back the this pointer even when returning E_NOINTERFACE.

is hence incorrect. It might be pretty dangerous sometimes as well if caller provides a variable managed by smart pointer and then tries to release it anyway. This is unlikely with standard marshaling OS code which would more likely prefer to discard the value immediately though.

Your implementation is going to be correct if you do

*outAddressOfObjectPointer = NULL;
if (inRiid == IID_IUnknown)
{
        printf("interface required is IUnknown\n");
        *outAddressOfObjectPointer = this;
        AddRef();
        return NOERROR;
}
// ...

NULL or nullptr does not cause a failure here. Your code is however dangerous in other ways: COM initialization, return of zero in Release call, and local stack-backed callback class instance which is destroyed going out of scope but might still be called later through exposed interface pointer.

Now back to original question, what is marshaling doing here at all? You are initializing an MTA thread and you are creating apartment threaded COM object CLSID_UserNotification there. COM creates the notification API in a side STA thread for you and then it tries to pass your callback there to match threading. It has to ask your COM object whether it does marshaling itself, or it needs this thing thing to be supplied. Your object has to say it does not marshal itself and it has no idea about it. This is what is going on. Make it COINIT_APARTMENTTHREADED and you will see a different picture.

Roman R.
  • 68,205
  • 6
  • 94
  • 158
0

When doing inter-apartment calls that involve interface pointers, COM first queries the object for the IMarshal interface:

COM uses your implementation of IMarshal in the following manner: When it's necessary to create a remote interface pointer to your object (that is, when a pointer to your object is passed as an argument in a remote function call), COM queries your object for the IMarshal interface. If your object implements it, COM uses your IMarshal implementation to create the proxy object. If your object does not implement IMarshal, COM uses its default implementation.

So, this is indeed documented.

As for QueryInterface, just like you inform that your object implements IUnknown and other useful interfaces, you must inform when you don't implement an interface. That is done by nulling the outgoing interface pointer (to avoid its marshaling) and returning E_NOINTERFACE.

  • Note: the implementation of QueryInterface is full of subtleties. For instance, in C++, when you implement an interface, you should cast this to the appropriate interface pointer type, and if you ever intend to use aggregation or tear-offs, you should AddRef the actual interface pointer.

If you don't implement custom marshaling, I suggest you do nothing special when IMarshal is asked for, just handle it as a non-implemented interface.

COM will then look for a standard marshaler in the current activation context (see comClass in assembly manifests, and clrClass), then in the registry (see the Interface registry key). Type library marshaling is a specific kind of standard marshaling where the ProxyStubClsid32 refers to one of the type library marshalers ({00020420-0000-0000-C000-000000000046} aka PSDispatch for dispinterfaces, {00020424-0000-0000-C000-000000000046} aka PSOAInterface for [oleautomation] interfaces), which expect a TypeLib subkey referring to the type library containing the interface definition.

acelent
  • 7,965
  • 21
  • 39