Home > Enterprise >  Release Interface Ref of Windows Runtime Class
Release Interface Ref of Windows Runtime Class

Time:08-27

Seemingly simple question. I'm needing to start managing lifetimes for my personal software that uses WinUI 3.0 and C /WinRT. When holding an Interface reference to a Windows Runtime Class how do I free that reference? As I understand it, all references to Windows Runtime Classes are Interfaces. When handling projected types of the Interfaces/Objects we are inherently holding a smart pointer. But how do we release a reference to the underlying object? It makes no sense to cast to the implementation, or to get IUknown somehow and calling release(). There's no function on the projected type to release. The winrt::final_release function seems to have been deprecated. There doesn't seem to be any other winrt function. ~IUknown does not sound like it would decrement the count. My only conclusion is to IInterface = nullptr. Great if that's the answer, and it might even work, but still be not the right answer.

Interface defined in proxy-stub server. .winmd is included in the calling process and the implementing process using a PropertySheet.

Midl 3.0 Interface

namespace ProxyStub
{
    [uuid(747B1BED-933E-4EB7-9B8C-DBB2F4AFC5E2)]
    [version(0x10000001)]
    interface IMyInterface : IInspectable
    {
        void SomeFunction;

    };

}

Midl 3.0 runtime class

namespace TestComponent
{
    [default_interface]
    runtimeclass RTClass : ProxyStub.ISomeInterface    
    {
        RTClass();

    }

}

hpp

#pragma once

#include "RTClass.g.h"

namespace winrt::TestComponent::implementation
{
    struct RTClass : RTClassT<RTClass>
    {
    public:
        RTClass();

        virtual void SomeFunction();

    };

};


namespace winrt::TestComponent::factory_implementation
{
    struct RTClass : RTClassT<RTClass, implementation::RTClass>
    {
    };
}

Calling function:

int32_t ret = 0;

void* dllHandle = WINRT_IMPL_LoadLibraryW(
    L"'long_path'\\TestComponent\\TestComponent.dll");

if (!dllHandle) { throw; };

void* proc = WINRT_IMPL_GetProcAddress(dllHandle, "DllGetActivationFactory");
auto DllGetActivationFactory = reinterpret_cast<int32_t(__stdcall*)(void* classId, void** factory)>(proc);

const wchar_t* cname{ L"TestComponent.RTClass" };
const UINT32 cnamelen = (UINT32)wcslen(cname);

HSTRING hcname = NULL;
HSTRING_HEADER header;
winrt::check_hresult(WindowsCreateStringReference(cname, cnamelen, &header, &hcname));

void** unkActivationFactory = new void* [1];
ret = (*DllGetActivationFactory)(&header, unkActivationFactory);

winrt::Windows::Foundation::IActivationFactory oObj{ nullptr };
winrt::copy_from_abi(oObj, *unkActivationFactory);

auto MyInterface = oObj.ActivateInstance<ProxyStub::IMyInterface>();

CodePudding user response:

When using C /WinRT the com_ptr class template manages lifetimes automatically. When an object of type com_ptr goes out of scope, its destructor calls Release() on the interface it's storing.

If you need to release an interface prior to the com_ptr instance going out of scope you have essentially two options:

  • You can pull out the interface calling com_ptr::detach, and then calling Release() on the returned interface (e.g. obj.detach()->Release();). This leaves the com_ptr empty and running its destructor is a no-op.
  • Assign a different pointer to the com_ptr instance. This Release()-es the previously held interface. Assigning a nullptr thus essentially drops the previous interface and leaves the com_ptr empty (e.g. obj = nullptr;).

CodePudding user response:

I was unaware that because I was implementing through a Proxy-Stub Server, that I was hiding the ->AddRef() and ->Release() functions. I assumed projection types hid this by default, which was an incorrect assumption. I had never implemented the expected way, or without 'late binding'. That said, projection type Midl 3.0 Interfaces are very much so smart pointers and when any reference goes out of scope the underlying object is decremented or assigning to another reference increments it. A simple test through the Proxy-Stub Server showed that setting any reference to nullptr decrements the internal objects count. ->Release() is not required. I love C /WinRT and WinUI 3.0, these are amazing technologies.

So looking back to my question, when I activate a runtimeclass and get the Proxy-Stub Interface, when I follow through by setting it to nullptr in the last line, the Interface isn't just set to nullptr, the object is decremented and destroyed:

int32_t ret = 0;

void* dllHandle = WINRT_IMPL_LoadLibraryW(
    L"'long_path'\\TestComponent\\TestComponent.dll");

if (!dllHandle) { throw; };

void* proc = WINRT_IMPL_GetProcAddress(dllHandle, "DllGetActivationFactory");
auto DllGetActivationFactory = reinterpret_cast<int32_t(__stdcall*)(void* classId, void** factory)>(proc);

const wchar_t* cname{ L"TestComponent.RTClass" };
const UINT32 cnamelen = (UINT32)wcslen(cname);

HSTRING hcname = NULL;
HSTRING_HEADER header;
winrt::check_hresult(WindowsCreateStringReference(cname, cnamelen, &header, &hcname));

void** unkActivationFactory = new void* [1];
ret = (*DllGetActivationFactory)(&header, unkActivationFactory);

winrt::Windows::Foundation::IActivationFactory oObj{ nullptr };
winrt::copy_from_abi(oObj, *unkActivationFactory);

auto MyInterface = oObj.ActivateInstance<ProxyStub::IMyInterface>();

MyInterface = nullptr; //decremented and destroyed(destructor called)
  • Related