0

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.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
James Pack
  • 824
  • 1
  • 10
  • 19
  • At least to C++ there is no discernible difference between COM properties and COM methods defined in IDL. Its only specific consumers such as VB where differences will appear. I'm not sure why the argument to 'get_Feed' would be null. – karmasponge Mar 18 '15 at 02:08
  • I noticed in our own ancient COM code that we actually check for null arguments and return 'E_POINTER' though I have no idea why that would ever occur. – karmasponge Mar 18 '15 at 02:15
  • Yea I didnt think there was a difference either. I think it is something in the projection but that is just a guess. The documentation for this stuff is so sparse. – James Pack Mar 18 '15 at 02:18
  • I'm not sure if it would help but can you tell us what language/program is consuming your COM component? – karmasponge Mar 18 '15 at 02:24
  • I have only tried in c++/cx so far. The GetFeed Methdod is projected as: `SyndicationFeed^ Downloader::GetFeed(String^ url)` and the Property is projected as `SyndicationFeed^ Downloader::Feed` – James Pack Mar 18 '15 at 02:26
  • So your calling it yourself from managed C++? Can you show the calling code for that? Or is that just using a stub generated from IDL? – karmasponge Mar 18 '15 at 02:34
  • C++/cx is not managed code. The calling code is: `auto feed = downloader->GetFeed(ref new String(L"some blog url");` After this method completes I am able to call `auto blogFeed = downloader->Feed;` and this is successful. – James Pack Mar 18 '15 at 02:40
  • Sorry, I'm not familiar with 'C++/cx' notation! So what does the code for accessing the property look like? – karmasponge Mar 18 '15 at 02:48
  • It's in the previous comment at the end. – James Pack Mar 18 '15 at 02:49
  • Now I'm really confused. Are you saying that when called in that order the parameter passed to 'get_Feed' is NOT null? – karmasponge Mar 18 '15 at 03:01

1 Answers1

1

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).

Peter Torr - MSFT
  • 11,824
  • 3
  • 18
  • 51
  • Thanks. That worked. The event object was left over from just trying to get the AsyncOperation to call my lambda. I am new to this. – James Pack Mar 18 '15 at 10:39
  • Unless you have specific needs for WRL (eg, a policy against exceptions) or you're just curious, I would use C++/CX since it is much easier to use. – Peter Torr - MSFT Mar 18 '15 at 15:13
  • Yeah I am just curious. I started to teach my self how to develop software a couple years ago. I started with .NET and have recently been learning lots of c++. It is helpful to understand how this stuff works at the lowest levels instead of just believing that there is magic happening somewhere in the computer. – James Pack Mar 18 '15 at 15:27
  • Totally understand; just wanted to make sure you weren't placing a burden on yourself unnecessarily. – Peter Torr - MSFT Mar 18 '15 at 17:13