Common (Windows): simplifies winrt helper

This commit is contained in:
李通洲
2026-09-24 09:41:16 +08:00
committed by Carter Li
parent 982313063d
commit 28f4f6464b
2 changed files with 100 additions and 140 deletions
+89 -138
View File
@@ -4,119 +4,110 @@
#include <wchar.h>
// A COM vtable is a flat array of function pointers: IUnknown's three methods, IInspectable's three,
// then the interface's own methods in declaration order. The pair below writes that array by hand
// instead of deriving from winrt::impl::abi<...>.
//
// Deriving is the idiomatic choice, and it would let the compiler check every slot -- but it cannot
// be used here. A class with virtual functions makes clang emit the vtable of its abstract base, and
// each pure slot of that table refers to libc++abi's __cxa_pure_virtual. fastfetch is linked by the C
// compiler and therefore has neither libc++ nor libc++abi, so that reference would be the only
// unresolved symbol in the program. A hand-written table has no virtual functions at all, which
// keeps this file as free of the C++ runtime as `media_windows.cpp` is.
#if defined(__GNUC__) || defined(__clang__)
extern "C" [[noreturn]] void __cxa_pure_virtual(void) {
__builtin_trap();
}
#endif
// C++/WinRT supplies the ABI vtable shape, including IUnknown and IInspectable. These concrete
// classes only provide the implementation-specific state and methods; placement new keeps their
// storage under the C allocator used by the rest of fastfetch.
namespace {
using WinrtIterableAbi = winrt::impl::abi<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>::type;
using WinrtIterableAbi = abi_t<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>;
using WinrtIteratorAbi = abi_t<winrt::Windows::Foundation::Collections::IIterator<winrt::hstring>>;
using WinrtHstringAbiOut = winrt::impl::arg_out<winrt::hstring>;
struct FFWinrtHstringIterable;
struct FFWinrtHstringIterator;
struct FFWinrtHstringIterableVtbl {
std::int32_t (STDMETHODCALLTYPE* QueryInterface)(FFWinrtHstringIterable* self, GUID const& riid, void** result);
std::uint32_t (STDMETHODCALLTYPE* AddRef)(FFWinrtHstringIterable* self);
std::uint32_t (STDMETHODCALLTYPE* Release)(FFWinrtHstringIterable* self);
std::int32_t (STDMETHODCALLTYPE* GetIids)(FFWinrtHstringIterable* self, std::uint32_t* count, GUID** iids);
std::int32_t (STDMETHODCALLTYPE* GetRuntimeClassName)(FFWinrtHstringIterable* self, void** name);
std::int32_t (STDMETHODCALLTYPE* GetTrustLevel)(FFWinrtHstringIterable* self, std::int32_t* level);
std::int32_t (STDMETHODCALLTYPE* First)(FFWinrtHstringIterable* self, void** result);
};
struct FFWinrtHstringIteratorVtbl {
std::int32_t (STDMETHODCALLTYPE* QueryInterface)(FFWinrtHstringIterator* self, GUID const& riid, void** result);
std::uint32_t (STDMETHODCALLTYPE* AddRef)(FFWinrtHstringIterator* self);
std::uint32_t (STDMETHODCALLTYPE* Release)(FFWinrtHstringIterator* self);
std::int32_t (STDMETHODCALLTYPE* GetIids)(FFWinrtHstringIterator* self, std::uint32_t* count, GUID** iids);
std::int32_t (STDMETHODCALLTYPE* GetRuntimeClassName)(FFWinrtHstringIterator* self, void** name);
std::int32_t (STDMETHODCALLTYPE* GetTrustLevel)(FFWinrtHstringIterator* self, std::int32_t* level);
std::int32_t (STDMETHODCALLTYPE* get_Current)(FFWinrtHstringIterator* self, HSTRING* value);
std::int32_t (STDMETHODCALLTYPE* get_HasCurrent)(FFWinrtHstringIterator* self, bool* value);
std::int32_t (STDMETHODCALLTYPE* MoveNext)(FFWinrtHstringIterator* self, bool* value);
std::int32_t (STDMETHODCALLTYPE* GetMany)(FFWinrtHstringIterator* self, std::uint32_t capacity, HSTRING* values, std::uint32_t* actual);
};
// Everything the pair allocates comes from malloc(), because the placement is driven by the C
// allocator rather than by `new` (see the note above).
struct FFWinrtHstringIterable {
const FFWinrtHstringIterableVtbl* lpVtbl;
struct FFWinrtHstringIterable final : WinrtIterableAbi {
std::uint32_t refCount;
FFlist strings; // HSTRING
std::int32_t STDMETHODCALLTYPE QueryInterface(winrt::guid const& riid, void** result) noexcept override;
std::uint32_t STDMETHODCALLTYPE AddRef() noexcept override;
std::uint32_t STDMETHODCALLTYPE Release() noexcept override;
std::int32_t STDMETHODCALLTYPE GetIids(std::uint32_t* count, winrt::guid** iids) noexcept override;
std::int32_t STDMETHODCALLTYPE GetRuntimeClassName(void** name) noexcept override;
std::int32_t STDMETHODCALLTYPE GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept override;
std::int32_t STDMETHODCALLTYPE First(void** result) noexcept override;
};
struct FFWinrtHstringIterator {
const FFWinrtHstringIteratorVtbl* lpVtbl;
struct FFWinrtHstringIterator final : WinrtIteratorAbi {
std::uint32_t refCount;
FFWinrtHstringIterable* owner;
std::uint32_t index;
std::int32_t STDMETHODCALLTYPE QueryInterface(winrt::guid const& riid, void** result) noexcept override;
std::uint32_t STDMETHODCALLTYPE AddRef() noexcept override;
std::uint32_t STDMETHODCALLTYPE Release() noexcept override;
std::int32_t STDMETHODCALLTYPE GetIids(std::uint32_t* count, winrt::guid** iids) noexcept override;
std::int32_t STDMETHODCALLTYPE GetRuntimeClassName(void** name) noexcept override;
std::int32_t STDMETHODCALLTYPE GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept override;
std::int32_t STDMETHODCALLTYPE get_Current(WinrtHstringAbiOut value) noexcept override;
std::int32_t STDMETHODCALLTYPE get_HasCurrent(bool* value) noexcept override;
std::int32_t STDMETHODCALLTYPE MoveNext(bool* value) noexcept override;
std::int32_t STDMETHODCALLTYPE GetMany(std::uint32_t capacity, WinrtHstringAbiOut values, std::uint32_t* actual) noexcept override;
};
// ---------------------------------------------------------------------------------------------
// IIterable<HSTRING>
// ---------------------------------------------------------------------------------------------
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_QueryInterface(FFWinrtHstringIterable* self, GUID const& riid, void** result) {
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::QueryInterface(winrt::guid const& riid, void** result) noexcept {
if (!result) {
return E_POINTER;
}
*result = nullptr;
if (IsEqualGUID(riid, winrt::guid_of<winrt::Windows::Foundation::IUnknown>()) ||
IsEqualGUID(riid, winrt::guid_of<winrt::Windows::Foundation::IInspectable>()) ||
IsEqualGUID(riid, winrt::guid_of<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>())) {
*result = self;
self->lpVtbl->AddRef(self);
if (riid == winrt::guid_of<winrt::Windows::Foundation::IUnknown>() ||
riid == winrt::guid_of<winrt::Windows::Foundation::IInspectable>() ||
riid == winrt::guid_of<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>()) {
*result = this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterable_AddRef(FFWinrtHstringIterable* self) {
return ++self->refCount;
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::AddRef() noexcept {
return ++refCount;
}
std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterable_Release(FFWinrtHstringIterable* self) {
std::uint32_t remaining = --self->refCount;
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterable::Release() noexcept {
std::uint32_t remaining = --refCount;
if (remaining == 0) {
FF_LIST_FOR_EACH(HSTRING, string, self->strings) {
FF_LIST_FOR_EACH(HSTRING, string, strings) {
WindowsDeleteString(*string);
}
ffListDestroy(&self->strings);
free(self);
ffListDestroy(&strings);
this->~FFWinrtHstringIterable();
free(this);
}
return remaining;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_GetIids(FFWinrtHstringIterable* self, std::uint32_t* count, GUID** iids) {
(void) self;
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept {
*count = 0;
*iids = nullptr;
return S_OK;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_GetRuntimeClassName(FFWinrtHstringIterable* self, void** name) {
(void) self;
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetRuntimeClassName(void** name) noexcept {
*name = nullptr;
return S_OK;
}
// `level` is the ABI's 32-bit TrustLevel enum; BaseTrust is 0.
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_GetTrustLevel(FFWinrtHstringIterable* self, std::int32_t* level) {
(void) self;
*level = 0;
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept {
*level = winrt::Windows::Foundation::TrustLevel::BaseTrust;
return S_OK;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_First(FFWinrtHstringIterable* self, void** result) {
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterable::First(void** result) noexcept {
if (!result) {
return E_POINTER;
}
@@ -127,119 +118,104 @@ std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterable_First(FFWinrtHstringIterab
return E_OUTOFMEMORY;
}
iterator->lpVtbl = nullptr; // assigned below, once the table is declared
::new (iterator) FFWinrtHstringIterator();
iterator->refCount = 1;
iterator->owner = self;
iterator->owner = this;
iterator->index = 0;
// The iterator outlives the call that created it, so it holds a reference of its own.
self->lpVtbl->AddRef(self);
extern const FFWinrtHstringIteratorVtbl ffWinrtHstringIteratorVtbl;
iterator->lpVtbl = &ffWinrtHstringIteratorVtbl;
AddRef();
*result = iterator;
return S_OK;
}
const FFWinrtHstringIterableVtbl ffWinrtHstringIterableVtbl = {
ffWinrtHstringIterable_QueryInterface,
ffWinrtHstringIterable_AddRef,
ffWinrtHstringIterable_Release,
ffWinrtHstringIterable_GetIids,
ffWinrtHstringIterable_GetRuntimeClassName,
ffWinrtHstringIterable_GetTrustLevel,
ffWinrtHstringIterable_First,
};
// ---------------------------------------------------------------------------------------------
// IIterator<HSTRING>
// ---------------------------------------------------------------------------------------------
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_QueryInterface(FFWinrtHstringIterator* self, GUID const& riid, void** result) {
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::QueryInterface(winrt::guid const& riid, void** result) noexcept {
if (!result) {
return E_POINTER;
}
*result = nullptr;
if (IsEqualGUID(riid, winrt::guid_of<winrt::Windows::Foundation::IUnknown>()) ||
IsEqualGUID(riid, winrt::guid_of<winrt::Windows::Foundation::IInspectable>()) ||
IsEqualGUID(riid, winrt::guid_of<winrt::Windows::Foundation::Collections::IIterator<winrt::hstring>>())) {
*result = self;
self->lpVtbl->AddRef(self);
if (riid == winrt::guid_of<winrt::Windows::Foundation::IUnknown>() ||
riid == winrt::guid_of<winrt::Windows::Foundation::IInspectable>() ||
riid == winrt::guid_of<winrt::Windows::Foundation::Collections::IIterator<winrt::hstring>>()) {
*result = this;
AddRef();
return S_OK;
}
return E_NOINTERFACE;
}
std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterator_AddRef(FFWinrtHstringIterator* self) {
return ++self->refCount;
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::AddRef() noexcept {
return ++refCount;
}
std::uint32_t STDMETHODCALLTYPE ffWinrtHstringIterator_Release(FFWinrtHstringIterator* self) {
std::uint32_t remaining = --self->refCount;
std::uint32_t STDMETHODCALLTYPE FFWinrtHstringIterator::Release() noexcept {
std::uint32_t remaining = --refCount;
if (remaining == 0) {
self->owner->lpVtbl->Release(self->owner);
free(self);
owner->Release();
this->~FFWinrtHstringIterator();
free(this);
}
return remaining;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetIids(FFWinrtHstringIterator* self, std::uint32_t* count, GUID** iids) {
(void) self;
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetIids(std::uint32_t* count, winrt::guid** iids) noexcept {
*count = 0;
*iids = nullptr;
return S_OK;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetRuntimeClassName(FFWinrtHstringIterator* self, void** name) {
(void) self;
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetRuntimeClassName(void** name) noexcept {
*name = nullptr;
return S_OK;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetTrustLevel(FFWinrtHstringIterator* self, std::int32_t* level) {
(void) self;
*level = 0;
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetTrustLevel(winrt::Windows::Foundation::TrustLevel* level) noexcept {
*level = winrt::Windows::Foundation::TrustLevel::BaseTrust;
return S_OK;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_get_Current(FFWinrtHstringIterator* self, HSTRING* value) {
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_Current(WinrtHstringAbiOut value) noexcept {
if (!value) {
return E_POINTER;
}
*value = nullptr;
if (self->index >= self->owner->strings.length) {
if (index >= owner->strings.length) {
return E_BOUNDS;
}
// The caller owns what it gets, so hand out a duplicate rather than the stored string.
return WindowsDuplicateString(*(HSTRING*) ffListGet(&self->owner->strings, sizeof(HSTRING), self->index), value);
return WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index), reinterpret_cast<HSTRING*>(value));
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_get_HasCurrent(FFWinrtHstringIterator* self, bool* value) {
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::get_HasCurrent(bool* value) noexcept {
if (!value) {
return E_POINTER;
}
*value = self->index < self->owner->strings.length;
*value = index < owner->strings.length;
return S_OK;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_MoveNext(FFWinrtHstringIterator* self, bool* value) {
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::MoveNext(bool* value) noexcept {
if (!value) {
return E_POINTER;
}
if (self->index < self->owner->strings.length) {
++self->index;
if (index < owner->strings.length) {
++index;
}
*value = self->index < self->owner->strings.length;
*value = index < owner->strings.length;
return S_OK;
}
std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetMany(FFWinrtHstringIterator* self, std::uint32_t capacity, HSTRING* values, std::uint32_t* actual) {
std::int32_t STDMETHODCALLTYPE FFWinrtHstringIterator::GetMany(std::uint32_t capacity, WinrtHstringAbiOut values, std::uint32_t* actual) noexcept {
if (!actual) {
return E_POINTER;
}
@@ -249,47 +225,22 @@ std::int32_t STDMETHODCALLTYPE ffWinrtHstringIterator_GetMany(FFWinrtHstringIter
return E_POINTER;
}
std::uint32_t available = self->owner->strings.length - self->index;
std::uint32_t available = owner->strings.length - index;
std::uint32_t taken = capacity < available ? capacity : available;
HSTRING* hstrings = reinterpret_cast<HSTRING*>(values);
for (std::uint32_t i = 0; i < taken; ++i) {
WindowsDuplicateString(*(HSTRING*) ffListGet(&self->owner->strings, sizeof(HSTRING), self->index + i), &values[i]);
WindowsDuplicateString(*(HSTRING*) ffListGet(&owner->strings, sizeof(HSTRING), index + i), &hstrings[i]);
}
self->index += taken;
index += taken;
*actual = taken;
return taken == capacity ? S_OK : S_FALSE;
}
const FFWinrtHstringIteratorVtbl ffWinrtHstringIteratorVtbl = {
ffWinrtHstringIterator_QueryInterface,
ffWinrtHstringIterator_AddRef,
ffWinrtHstringIterator_Release,
ffWinrtHstringIterator_GetIids,
ffWinrtHstringIterator_GetRuntimeClassName,
ffWinrtHstringIterator_GetTrustLevel,
ffWinrtHstringIterator_get_Current,
ffWinrtHstringIterator_get_HasCurrent,
ffWinrtHstringIterator_MoveNext,
ffWinrtHstringIterator_GetMany,
};
} // namespace
void ffDeleteHstring(HSTRING* value) {
if (*value) {
WindowsDeleteString(*value);
*value = nullptr;
}
}
void ffStrbufSetHstring(FFstrbuf* destination, HSTRING value) {
uint32_t length = 0;
const wchar_t* raw = WindowsGetStringRawBuffer(value, &length);
ffStrbufSetNWS(destination, length, raw);
}
HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count, abi_t<winrt::Windows::Foundation::Collections::IIterable<winrt::hstring>>** result) {
*result = nullptr;
@@ -302,7 +253,7 @@ HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count
return E_OUTOFMEMORY;
}
iterable->lpVtbl = &ffWinrtHstringIterableVtbl;
::new (iterable) FFWinrtHstringIterable();
iterable->refCount = 1;
ffListInitA(&iterable->strings, sizeof(HSTRING), count > 0 ? count : FF_LIST_DEFAULT_ALLOC);
@@ -310,12 +261,12 @@ HRESULT ffWinrtCreateHstringIterable(const wchar_t* const* items, uint32_t count
HSTRING* slot = (HSTRING*) ffListAdd(&iterable->strings, sizeof(HSTRING));
HRESULT hr = WindowsCreateString(items[i], (UINT32) ::wcslen(items[i]), slot);
if (FAILED(hr)) {
iterable->lpVtbl->Release(iterable);
iterable->Release();
return hr;
}
}
*result = reinterpret_cast<WinrtIterableAbi*>(iterable);
*result = static_cast<WinrtIterableAbi*>(iterable);
return S_OK;
}