// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
#include "platform.h"
#include <interoplib.h>
#include <interoplibimports.h>
#ifdef FEATURE_COMWRAPPERS
#include "comwrappers.hpp"
#endif // FEATURE_COMWRAPPERS
using OBJECTHANDLE = InteropLib::OBJECTHANDLE;
using RuntimeCallContext = InteropLibImports::RuntimeCallContext;
namespace InteropLib
{
#ifdef FEATURE_COMWRAPPERS
// Exposed COM related API
namespace Com
{
HRESULT CreateWrapperForObject(
_In_ OBJECTHANDLE instance,
_In_ INT32 vtableCount,
_In_ void* vtablesRaw,
_In_ enum CreateComInterfaceFlags flags,
_Outptr_ IUnknown** wrapper) noexcept
{
_ASSERTE(instance != nullptr && wrapper != nullptr);
// Validate the supplied vtable data is valid with a
// reasonable count.
if ((vtablesRaw == nullptr && vtableCount != 0) || vtableCount < 0)
return E_INVALIDARG;
HRESULT hr;
// Convert input to appropriate types.
auto vtables = static_cast<::ABI::ComInterfaceEntry*>(vtablesRaw);
ManagedObjectWrapper* mow;
RETURN_IF_FAILED(ManagedObjectWrapper::Create(flags, instance, vtableCount, vtables, &mow));
*wrapper = static_cast<IUnknown*>(mow->As(IID_IUnknown));
return S_OK;
}
void DestroyWrapperForObject(_In_ void* wrapperMaybe) noexcept
{
ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknownWithQueryInterface(static_cast<IUnknown*>(wrapperMaybe));
// A caller should not be destroying a wrapper without knowing if the wrapper is valid.
_ASSERTE(wrapper != nullptr);
ManagedObjectWrapper::Destroy(wrapper);
}
HRESULT IsWrapperRooted(_In_ IUnknown* wrapperMaybe) noexcept
{
ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknown(wrapperMaybe);
if (wrapper == nullptr)
return E_INVALIDARG;
return wrapper->IsRooted() ? S_OK : S_FALSE;
}
HRESULT GetObjectForWrapper(_In_ IUnknown* wrapper, _Outptr_result_maybenull_ OBJECTHANDLE* object) noexcept
{
_ASSERTE(wrapper != nullptr && object != nullptr);
*object = nullptr;
// Attempt to get the managed object wrapper.
ManagedObjectWrapper *mow = ManagedObjectWrapper::MapFromIUnknownWithQueryInterface(wrapper);
if (mow == nullptr)
return E_INVALIDARG;
*object = mow->Target;
return S_OK;
}
HRESULT MarkComActivated(_In_ IUnknown* wrapperMaybe) noexcept
{
ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknownWithQueryInterface(wrapperMaybe);
if (wrapper == nullptr)
return E_INVALIDARG;
wrapper->SetFlag(CreateComInterfaceFlagsEx::IsComActivated);
return S_OK;
}
HRESULT IsComActivated(_In_ IUnknown* wrapperMaybe) noexcept
{
ManagedObjectWrapper* wrapper = ManagedObjectWrapper::MapFromIUnknownWithQueryInterface(wrapperMaybe);
if (wrapper == nullptr)
return E_INVALIDARG;
return wrapper->IsSet(CreateComInterfaceFlagsEx::IsComActivated) ? S_OK : S_FALSE;
}
HRESULT DetermineIdentityAndInnerForExternal(
_In_ IUnknown* external,
_In_ enum CreateObjectFlags flags,
_Outptr_ IUnknown** identity,
_Inout_ IUnknown** innerMaybe) noexcept
{
_ASSERTE(external != nullptr && identity != nullptr && innerMaybe != nullptr);
IUnknown* checkForIdentity = external;
// Check if the flags indicate we are creating
// an object for an external IReferenceTracker instance
// that we are aggregating with.
bool refTrackerInnerScenario = (flags & CreateObjectFlags_TrackerObject)
&& (flags & CreateObjectFlags_Aggregated);
ComHolder<IReferenceTracker> trackerObject;
if (refTrackerInnerScenario)
{
// We are checking the supplied external value
// for IReferenceTracker since in .NET 5 this could
// actually be the inner and we want the true identity
// not the inner . This is a trick since the only way
// to get identity from an inner is through a non-IUnknown
// interface QI. Once we have the IReferenceTracker
// instance we can be sure the QI for IUnknown will really
// be the true identity.
HRESULT hr = external->QueryInterface(IID_IReferenceTracker, (void**)&trackerObject);
if (SUCCEEDED(hr))
checkForIdentity = trackerObject.p;
}
HRESULT hr;
IUnknown* identityLocal;
RETURN_IF_FAILED(checkForIdentity->QueryInterface(IID_IUnknown, (void **)&identityLocal));
// Set the inner if scenario dictates an update.
if (*innerMaybe == nullptr // User didn't supply inner - .NET 5 API scenario sanity check.
&& checkForIdentity != external // Target of check was changed - .NET 5 API scenario sanity check.
&& external != identityLocal // The supplied object doesn't match the computed identity.
&& refTrackerInnerScenario) // The appropriate flags were set.
{
*innerMaybe = external;
}
*identity = identityLocal;
return S_OK;
}
HRESULT CreateWrapperForExternal(
_In_ IUnknown* external,
_In_opt_ IUnknown* inner,
_In_ enum CreateObjectFlags flags,
_In_ size_t contextSize,
_Out_ ExternalWrapperResult* result) noexcept
{
_ASSERTE(external != nullptr && result != nullptr);
HRESULT hr;
NativeObjectWrapperContext* wrapperContext;
RETURN_IF_FAILED(NativeObjectWrapperContext::Create(external, inner, flags, contextSize, &wrapperContext));
result->Context = wrapperContext->GetRuntimeContext();
result->FromTrackerRuntime = (wrapperContext->GetReferenceTracker() != nullptr);
result->ManagedObjectWrapper = (ManagedObjectWrapper::MapFromIUnknownWithQueryInterface(external) != nullptr);
return S_OK;
}
void NotifyWrapperForExternalIsBeingCollected(_In_ void* contextMaybe) noexcept
{
NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe);
// A caller should not be destroying a context without knowing if the context is valid.
_ASSERTE(context != nullptr);
// Check if the tracker object manager should be informed of collection.
IReferenceTracker* trackerMaybe = context->GetReferenceTracker();
if (trackerMaybe != nullptr)
{
// We only call this during a GC so ignore the failure as
// there is no way we can handle it at this point.
HRESULT hr = TrackerObjectManager::BeforeWrapperFinalized(trackerMaybe);
_ASSERTE(SUCCEEDED(hr));
(void)hr;
}
}
void DestroyWrapperForExternal(_In_ void* contextMaybe, _In_ bool notifyIsBeingCollected) noexcept
{
NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe);
// A caller should not be destroying a context without knowing if the context is valid.
_ASSERTE(context != nullptr);
if (notifyIsBeingCollected)
NotifyWrapperForExternalIsBeingCollected(contextMaybe);
NativeObjectWrapperContext::Destroy(context);
}
void SeparateWrapperFromTrackerRuntime(_In_ void* contextMaybe) noexcept
{
NativeObjectWrapperContext* context = NativeObjectWrapperContext::MapFromRuntimeContext(contextMaybe);
// A caller should not be separating a context without knowing if the context is valid.
_ASSERTE(context != nullptr);
context->DisconnectTracker();
}
void GetIUnknownImpl(
_Out_ void** fpQueryInterface,
_Out_ void** fpAddRef,
_Out_ void** fpRelease) noexcept
{
ManagedObjectWrapper::GetIUnknownImpl(fpQueryInterface, fpAddRef, fpRelease);
}
HRESULT BeginExternalObjectReferenceTracking(_In_ RuntimeCallContext* cxt) noexcept
{
return TrackerObjectManager::BeginReferenceTracking(cxt);
}
HRESULT EndExternalObjectReferenceTracking() noexcept
{
return TrackerObjectManager::EndReferenceTracking();
}
}
#endif // FEATURE_COMWRAPPERS
}