Search code examples
c++windows-runtimewrl

What is the difference between COM property methods and regular interface methods?


I have been poking around with WRL at the ABI layer for the last couple of weeks and have run into this problem.

I have an interface defined in IDL as follows:

namespace Async{
[uuid(f469e110-7ef5-41df-a237-9ddef9aed55c), version(1.0)]
interface IDownloader : IInspectable
{
    HRESULT GetFeed([in] HSTRING url,[out, retval] Windows.Web.Syndication.SyndicationFeed ** feed);
    [propget]HRESULT Feed([out, retval]Windows.Web.Syndication.SyndicationFeed ** feed);
}

[version(1.0), activatable(1.0)]
runtimeclass Downloader
{
    [default] interface IDownloader;
}

}

Which I have defined in my header file like so:

#pragma once

#include "Async_h.h"

namespace ABI {
    namespace Async {


    class Downloader : public Microsoft::WRL::RuntimeClass<ABI::Async::IDownloader>
    {
        InspectableClass(L"Async.Downloader", BaseTrust);

    public:
        Downloader();
        STDMETHOD(GetFeed)(HSTRING url, ABI::Windows::Web::Syndication::ISyndicationFeed ** feed);
        STDMETHOD(get_Feed)(ABI::Windows::Web::Syndication::ISyndicationFeed ** feed);

    private:

        //Microsoft::WRL::ComPtr<ABI::Windows::Foundation::Uri> feedUrl;
        Microsoft::WRL::ComPtr<ABI::Windows::Web::Syndication::ISyndicationFeed> m_feed;

    };

    ActivatableClass(Downloader);
}

}

In my cpp file I implement the functions:

    STDMETHODIMP Downloader::GetFeed(HSTRING url, ISyndicationFeed** feed)
    {

        HRESULT hr;
        RoInitializeWrapper ro(RO_INIT_MULTITHREADED);
        ComPtr<IUriRuntimeClass> uri;
        ComPtr<IUriRuntimeClassFactory> uriFactory;
        hr = GetActivationFactory(HStringReference(RuntimeClass_Windows_Foundation_Uri).Get(), &uriFactory);
        hr = uriFactory->CreateUri(url, uri.GetAddressOf());

        ComPtr<ISyndicationClient> client;
        ComPtr<IInspectable> inspectable;

        RoActivateInstance(HStringReference(RuntimeClass_Windows_Web_Syndication_SyndicationClient).Get(), &inspectable);

        hr = inspectable.As(&client);
        Event timerCompleted(CreateEventEx(nullptr, nullptr, CREATE_EVENT_MANUAL_RESET, WRITE_OWNER | EVENT_ALL_ACCESS));
        auto callback = Callback<IAsyncOperationWithProgressCompletedHandler<SyndicationFeed*,RetrievalProgress>>([&](IAsyncOperationWithProgress<SyndicationFeed*,RetrievalProgress> *op, AsyncStatus status) ->HRESULT 
        {
            auto error = GetLastError();
            if (status == AsyncStatus::Completed)
            {
                hr = op->GetResults(m_feed.GetAddressOf());
                *feed = m_feed.Get();

            }


            return S_OK;
        });
        ComPtr<IAsyncOperationWithProgress<SyndicationFeed*,RetrievalProgress>>  operation;
        hr = client->RetrieveFeedAsync(uri.Get(), operation.GetAddressOf());
        operation->put_Completed(callback.Get());
        return S_OK;
    }


    STDMETHODIMP Downloader::get_Feed(ISyndicationFeed** feed)
    {
        *feed = m_feed.Get();
        return S_OK;
    }

The property works as expected it is projected to c++/cx as it should be. However,in the GetFeed method, when I attempt to set the feed parameter to the retrieved feed I get an access violation. Obviously I know that the memory is bad but the way I understand COM properties, they are essentially function calls and the property method and the GetFeed method are doing exactly the same thing minus the retrieval part.

Here are my questions:

  1. What is the difference between COM property methods and regular interface methods in terms of the projected return value if any?
  2. Why is the parameter to the property method initialized to nullptr and the parameter to the GetFeed Method not when they are described exactly the same in IDL?
  3. If the out parameters in property methods are initialized, what part of the COM runtime is doing that for me and is that controllable? IE is there a way to get memory that I can write to passed to me?

I know that I could probably design that away but that is not the point. I am just trying to learn how it all works.

Thanks.


Solution

  • In your lambda you are capturing by reference with [&]. You need to capture the feed parameter by value, since the stack frame is long gone by the time your lambda executes.

    The bigger issue is that the client has no idea when they can retrieve the results since you don't provide that information. (I see you create an unused Win32 Event object, so maybe there's some other code to make that work that you've deleted).