Skip to content

Instantly share code, notes, and snippets.

@kennykerr
Last active February 1, 2018 05:04
Show Gist options
  • Save kennykerr/be66a7227f4ff42a2c3329746c1fbae3 to your computer and use it in GitHub Desktop.
Save kennykerr/be66a7227f4ff42a2c3329746c1fbae3 to your computer and use it in GitHub Desktop.
Builds a WinRT factory quality report
#include "winrt/base.h"
#include <set>
using namespace winrt;
using namespace Windows::Foundation;
using namespace std::chrono_literals;
extern "C"
{
HRESULT __stdcall OS_RoGetActivationFactory(HSTRING classId, GUID const& iid, void** factory);
}
#ifdef _M_IX86
#pragma comment(linker, "/alternatename:_OS_RoGetActivationFactory@12=_RoGetActivationFactory@12")
#else
#pragma comment(linker, "/alternatename:OS_RoGetActivationFactory=RoGetActivationFactory")
#endif
struct registry_traits
{
using type = HKEY;
static void close(type value) noexcept
{
WINRT_VERIFY_(ERROR_SUCCESS, RegCloseKey(value));
}
static constexpr type invalid() noexcept
{
return nullptr;
}
};
using registry_key = handle_type<registry_traits>;
HRESULT GetActivationFactory(hstring const& class_name) noexcept
{
try
{
com_ptr<IAgileObject> factory;
return OS_RoGetActivationFactory(get_abi(class_name), guid_of<IAgileObject>(), factory.put_void());
}
catch (...)
{
return HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION);
}
}
IAsyncOperation<HRESULT> GetActivationFactoryAsync(hstring class_name)
{
co_await resume_background();
co_return GetActivationFactory(class_name);
}
IAsyncOperation<hstring> GetFactoryReportAsync()
{
co_await resume_background();
registry_key key;
check_win32(RegOpenKeyEx(HKEY_LOCAL_MACHINE, LR"(SOFTWARE\Microsoft\WindowsRuntime\ActivatableClassId)", 0, KEY_READ, key.put()));
std::array<wchar_t, 255> name_buffer;
std::set<hstring> agile;
std::set<hstring> non_agile;
std::map<HRESULT, std::set<hstring>> unknown;
std::set<hstring> crash;
std::set<hstring> hang;
for (DWORD index = 0; ; ++index)
{
DWORD name_length{ static_cast<DWORD>(name_buffer.size()) };
LONG const enum_result = RegEnumKeyEx(key.get(), index, name_buffer.data(), &name_length, nullptr, nullptr, nullptr, nullptr);
if (enum_result == ERROR_NO_MORE_ITEMS)
{
break;
}
check_win32(enum_result);
hstring class_name{ name_buffer.data(), name_length };
IAsyncOperation<HRESULT> async = GetActivationFactoryAsync(class_name);
slim_mutex m;
slim_condition_variable cv;
bool completed = false;
async.Completed([&](auto&&...)
{
{
slim_lock_guard const guard(m);
completed = true;
}
cv.notify_one();
});
slim_lock_guard guard(m);
cv.wait_for(m, 2s, [&] { return completed; });
HRESULT activate_result = HRESULT_FROM_WIN32(ERROR_APP_HANG);
if (async.Status() == AsyncStatus::Completed)
{
activate_result = async.GetResults();
}
if (activate_result == S_OK)
{
agile.insert(class_name);
}
else if (activate_result == E_NOINTERFACE)
{
non_agile.insert(class_name);
}
else if (activate_result == HRESULT_FROM_WIN32(ERROR_UNHANDLED_EXCEPTION))
{
crash.insert(class_name);
}
else if (activate_result == HRESULT_FROM_WIN32(ERROR_APP_HANG))
{
hang.insert(class_name);
}
else
{
unknown[activate_result].insert(class_name);
}
}
std::wstring report;
report += L"\n\nNON-AGILE\n\n";
for (std::wstring_view class_name : non_agile)
{
report += class_name;
report += L"\n";
}
report += L"\n\nCRASH\n\n";
for (std::wstring_view class_name : crash)
{
report += class_name;
report += L"\n";
}
report += L"\n\nHANG\n\n";
for (std::wstring_view class_name : hang)
{
report += class_name;
report += L"\n";
}
report += L"\n\nUNKNOWN\n\n";
for (auto&& pair : unknown)
{
hresult_error error(pair.first);
report += L"\n" + error.message() + L" (" + std::to_wstring(error.code()) + L")\n";
for (std::wstring_view class_name : pair.second)
{
report += class_name;
report += L"\n";
}
}
co_return hstring(report);
}
int main()
{
init_apartment();
hstring report = GetFactoryReportAsync().get();
printf("%ls\n", report.c_str());
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment