WICFactory: Observe COM apartment and invalidate factory

This commit is contained in:
Luca Bacci 2026-03-10 16:36:18 +01:00
parent eede753027
commit eb9b503f57
3 changed files with 134 additions and 13 deletions

View file

@ -159,25 +159,135 @@ public:
}
};
class COMWeakRegistration final
: public IInitializeSpy
{
public:
explicit COMWeakRegistration (void (*cleanup)(void)) noexcept
: cleanup (cleanup)
{
HRESULT hr;
hr = CoRegisterInitializeSpy (this, &registration_id);
if (FAILED (hr)) {
}
else {
registered = true;
}
}
void Register ()
{
}
void Unregister ()
{
}
void EnsureApartment ()
{
HRESULT hr = CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
if (FAILED (hr)) {
if (hr != RPC_E_CHANGED_MODE) {
_cairo ();
}
}
else {
}
}
public:
IFACEMETHOD (QueryInterface) (REFIID riid, void** ppv) noexcept override
{
if (!ppv)
return E_POINTER;
if (riid == IID_IUnknown || riid == IID_IInitializeSpy) {
*ppv = this;
AddRef ();
return S_OK;
}
*ppv = nullptr;
return E_NOINTERFACE;
}
IFACEMETHOD_(ULONG, AddRef) () noexcept override
{
return 2;
}
IFACEMETHOD_(ULONG, Release) () noexcept override
{
return 1;
}
IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override
{
bool user_requested_sta = !!(dwCoInit & COINIT_APARTMENTTHREADED);
bool cairo_holds_last_reference = (dwCurThreadAptRefs == 1);
if (user_requested_sta && cairo_holds_last_reference) {
cleanup ();
CoUninitialize();
}
return S_OK;
}
IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override
{
return S_OK;
}
IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override
{
return S_OK;
}
IFACEMETHOD (PreInitialize) (DWORD dwCoInit, DWORD dwCurThreadAptRefs) noexcept override
{
return S_OK;
}
private:
void (* cleanup) (void);
ULARGE_INTEGER registration_id { 0 };
bool registered { false };
bool have_reference { false };
};
class WICImagingFactory
{
public:
static RefPtr<IWICImagingFactory> Instance()
{
if (!mFactoryInstance) {
CoInitialize(NULL);
CoCreateInstance(CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&mFactoryInstance));
}
return mFactoryInstance;
}
private:
static RefPtr<IWICImagingFactory> mFactoryInstance;
};
cairo_win32_thread_data_t *thread_data = cairo_win32_thread_data_get ();
RefPtr<IWICImagingFactory> WICImagingFactory::mFactoryInstance;
if (!thread_data->wic_factory) {
if (!thread_data->com_observer) {
thread_data->com_observer = new (std::nothrow) COMObserver ();
}
thread_data->com_observer->EnsureApartment ();
CoInitializeEx (NULL, COINIT_MULTITHREADED | COINIT_DISABLE_OLE1DDE);
hr = CoCreateInstance (CLSID_WICImagingFactory,
NULL,
CLSCTX_INPROC_SERVER,
IID_PPV_ARGS (&thread_data->wic_factory));
if (FAILED (hr)) {
_cairo_win32_api_error_fatal_with_hr (hr, "%s (%s)",
"CoCreateInstance",
"CLSID_WICImagingFactory");
}
}
return thread_data->wic_factory;
}
};
cairo_atomic_once_t DWriteFactory::mOnceFactories = CAIRO_ATOMIC_ONCE_INIT;
RefPtr<IDWriteFactory> DWriteFactory::mFactoryInstance;
@ -186,6 +296,7 @@ RefPtr<IDWriteFactory2> DWriteFactory::mFactoryInstance2;
RefPtr<IDWriteFactory3> DWriteFactory::mFactoryInstance3;
RefPtr<IDWriteFactory4> DWriteFactory::mFactoryInstance4;
RefPtr<IDWriteFactory8> DWriteFactory::mFactoryInstance8;
cairo_atomic_once_t DWriteFactory::mOnceSystemCollection = CAIRO_ATOMIC_ONCE_INIT;
RefPtr<IDWriteFontCollection> DWriteFactory::mSystemCollection;

View file

@ -181,6 +181,9 @@ _cairo_win32_gdi_compositor_get (void);
cairo_status_t
_cairo_win32_print_api_error (const char *context, const char *api);
cairo_status_t
_cairo_win32_api_error_fatal (const char *format, ...);
cairo_bool_t
_cairo_surface_is_win32 (const cairo_surface_t *surface);
@ -249,6 +252,8 @@ cairo_win32_async_com_release (IUnknown *iface_ptr);
#if CAIRO_HAS_DWRITE_FONT
interface ID2D1Factory;
interface IWICImagingFactory;
struct COMObserver;
#endif
typedef struct {
@ -256,6 +261,8 @@ typedef struct {
#if CAIRO_HAS_DWRITE_FONT
interface ID2D1Factory *d2d1_factory;
interface IWICImagingFactory *wic_factory;
struct COMObserver *com_observer;
#endif
cairo_bool_t added_to_list;

View file

@ -100,6 +100,9 @@ thread_data_free (cairo_win32_thread_data_t *data)
*/
cairo_win32_async_stdcall_free ((stdcall_free_func_t) DeleteDC, data->hdc);
if (data->com_observer)
data->com_observer->Detach ();
#if CAIRO_HAS_DWRITE_FONT
cairo_win32_async_com_release ((IUnknown*) data->d2d1_factory);
#endif