/*************************************************************************************************** Copyright (C) 2023 The Qt Company Ltd. SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only ***************************************************************************************************/ #pragma once #include "qdotnethost.h" #ifdef __GNUC__ # pragma GCC diagnostic push # pragma GCC diagnostic ignored "-Wconversion" #endif #include #ifdef QT_QUICK_LIB #include #else struct QQmlEngine {}; #endif #include #include #include #include #ifdef __GNUC__ # pragma GCC diagnostic pop #endif class QDotNetRef; class QDotNetAdapter final { private: QDotNetAdapter() = default; QDotNetAdapter(const QDotNetAdapter &) = delete; QDotNetAdapter(QDotNetAdapter &&) = delete; QDotNetAdapter &operator=(const QDotNetAdapter &) = delete; QDotNetAdapter &operator=(QDotNetAdapter &&) = delete; ~QDotNetAdapter() { if (staticInterface && dtor_staticInterface) { dtor_staticInterface(staticInterface); staticInterface = nullptr; } fnReset(); //gcCollect(); //gcWaitForPendingFinalizers(); defaultHost.unload(); } static void init() { if (instance().isValid()) return; init(QDir(QCoreApplication::applicationDirPath()) .filePath(defaultDllName), defaultAssemblyName, defaultTypeName); } public: static void init(QDotNetHost *externalHost) { if (instance().isValid()) return; init(QDir(QCoreApplication::applicationDirPath()) .filePath(defaultDllName), defaultAssemblyName, defaultTypeName, externalHost); } static void init(const QString &assemblyPath, const QString &typeAndAssemblyName, QDotNetHost *externalHost = nullptr, QQmlEngine *qml = nullptr) { init(assemblyPath, typeAndAssemblyName, typeAndAssemblyName, externalHost, qml); } static void init(const QString &assemblyPath, const QString &assemblyName, const QString &typeName, QDotNetHost *externalHost = nullptr, QQmlEngine *qml = nullptr) { if (instance().isValid()) return; const QString typeFullName = QString("%1, %2").arg(typeName, assemblyName); const QString delegateName = QString("%1+Delegates+%3, %2") .arg(typeName, assemblyName, "%1"); QDotNetHost *host = nullptr; if (externalHost != nullptr) host = externalHost; else host = &instance().defaultHost; if (!host->load()) { qCritical() << "QDotNetAdapter: error loading host"; return; } #define QDOTNETADAPTER_DELEGATE(d) \ instance().fn##d, assemblyPath, typeFullName, #d, delegateName.arg(#d) host->resolveFunction(QDOTNETADAPTER_DELEGATE(LoadAssembly)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveStaticMethod)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveConstructor)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveInstanceMethod)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveStaticFieldGet)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveStaticFieldSet)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveInstanceFieldGet)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveInstanceFieldSet)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(ResolveSafeMethod)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(AddEventHandler)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(RemoveEventHandler)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(RemoveAllEventHandlersByContext)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(AddObjectRef)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(FreeDelegateRef)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(FreeObjectRef)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(FreeTypeRef)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(AddInterfaceProxy)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(SetInterfaceMethod)); #ifndef QT_NO_DEBUG host->resolveFunction(QDOTNETADAPTER_DELEGATE(Stats)); #endif host->resolveFunction(QDOTNETADAPTER_DELEGATE(GetObject)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(Reset)); #undef QDOTNETADAPTER_DELEGATE instance().host = host; instance().qml = qml; if (ctor_staticInterface) instance().staticInterface = ctor_staticInterface(); gcCollect = instance().resolveStaticMethod("System.GC, System.Runtime", "Collect", { QDotNetInbound::Parameter }); gcWaitForPendingFinalizers = instance().resolveStaticMethod("System.GC, System.Runtime", "WaitForPendingFinalizers", { QDotNetInbound::Parameter }); } static QDotNetAdapter &instance() { static QDotNetAdapter adapter; return adapter; } bool isValid() const { return host != nullptr; } public: bool loadAssembly(const QString &assemblyName) const { init(); return fnLoadAssembly(assemblyName); } void *resolveStaticMethod(const QString &typeName, const QString &methodName, const QList ¶ms) const { init(); if (typeName.isEmpty() || methodName.isEmpty()) return nullptr; return fnResolveStaticMethod(typeName, methodName, static_cast(params.size()), params); } void *resolveConstructor(const QList ¶ms) const { init(); return fnResolveConstructor(static_cast(params.size()), params); } void *resolveInstanceMethod(const QDotNetRef &objectRef, const QString &methodName, const QList ¶ms) const { init(); if (QtDotNet::isNull(objectRef) || methodName.isEmpty()) return nullptr; return fnResolveInstanceMethod( objectRef, methodName, static_cast(params.size()), params); } void *resolveStaticFieldGet(const QString &typeName, const QString &fieldName, const QList ¶ms) { init(); if (typeName.isEmpty() || fieldName.isEmpty()) return nullptr; return fnResolveStaticFieldGet( typeName, fieldName, static_cast(params.size()), params); } void *resolveStaticFieldSet(const QString &typeName, const QString &fieldName, const QList ¶ms) { init(); if (typeName.isEmpty() || fieldName.isEmpty()) return nullptr; return fnResolveStaticFieldSet( typeName, fieldName, static_cast(params.size()), params); } void *resolveInstanceFieldGet(const QDotNetRef &objectRef, const QString &fieldName, const QList ¶ms) { init(); if (QtDotNet::isNull(objectRef) || fieldName.isEmpty()) return nullptr; return fnResolveInstanceFieldGet( objectRef, fieldName, static_cast(params.size()), params); } void *resolveInstanceFieldSet(const QDotNetRef &objectRef, const QString &fieldName, const QList ¶ms) { init(); if (QtDotNet::isNull(objectRef) || fieldName.isEmpty()) return nullptr; return fnResolveInstanceFieldSet( objectRef, fieldName, static_cast(params.size()), params); } using EventCallback = void(QDOTNETFUNCTION_CALLTYPE *)(void *, void *, void *, void *); void *resolveSafeMethod(void *funcPtr, const QList ¶ms) const { init(); if (!funcPtr) return nullptr; return fnResolveSafeMethod( funcPtr, static_cast(params.size()), params); } void addEventHandler(const QDotNetRef &eventSource, const QString &eventName, void *context, EventCallback eventCallback) const { init(); if (QtDotNet::isNull(eventSource) || eventName.isEmpty() || !eventCallback) return; fnAddEventHandler(eventSource, eventName, context, eventCallback); } void addEventHandler(QDotNetRef &eventSource, const QString &eventName, EventCallback eventCallback) const { init(); if (QtDotNet::isNull(eventSource) || eventName.isEmpty() || !eventCallback) return; fnAddEventHandler(eventSource, eventName, &eventSource, eventCallback); } void removeEventHandler(const QDotNetRef &eventSource, const QString &eventName, void *context) const { init(); if (QtDotNet::isNull(eventSource) || eventName.isEmpty()) return; fnRemoveEventHandler(eventSource, eventName, context); } void removeEventHandler(QDotNetRef &eventSource, const QString &eventName) const { init(); if (QtDotNet::isNull(eventSource) || eventName.isEmpty()) return; fnRemoveEventHandler(eventSource, eventName, &eventSource); } void removeAllEventHandlersByContext(void *context) const { if (!isValid() || !fnRemoveAllEventHandlersByContext.isValid()) return; fnRemoveAllEventHandlersByContext(context); } void *addObjectRef(const QDotNetRef &objectRef, bool weakRef = false) const { init(); if (QtDotNet::isNull(objectRef)) return nullptr; return fnAddObjectRef(objectRef, weakRef); } void freeDelegateRef(void *delegateRef) const { init(); if (!delegateRef) return; fnFreeDelegateRef(delegateRef); } void freeObjectRef(const QDotNetRef &objectRef) const { init(); if (QtDotNet::isNull(objectRef)) return; fnFreeObjectRef(objectRef); } void freeTypeRef(const QString &typeName) const { init(); if (typeName.isEmpty()) return; fnFreeTypeRef(typeName); } void *addInterfaceProxy(const QString &interfaceName, void *data, void *cleanUp) const { init(); if (interfaceName.isEmpty()) return nullptr; return fnAddInterfaceProxy(interfaceName, data, cleanUp); } void setInterfaceMethod(const QDotNetRef &obj, const QString &methodName, const QList ¶ms, void *callback, void *cleanUp, void *context) const { init(); if (QtDotNet::isNull(obj) || methodName.isEmpty() || !callback) return; return fnSetInterfaceMethod(obj, methodName, static_cast(params.size()), params, callback, cleanUp, context); } #ifndef QT_NO_DEBUG struct Stats { qint32 refCount; qint32 staticCount; qint32 eventCount; bool allZero() const { return refCount == 0 && staticCount == 0 && eventCount == 0; } }; Stats stats() const { Stats s{ }; init(); fnStats(&s.refCount, &s.staticCount, &s.eventCount); if (staticInterface && s.refCount > 0) --s.refCount; return s; } #endif void *object(const QDotNetRef &obj, const QString &path) { init(); return fnGetObject(obj, path); } QQmlEngine *qmlEngine() const { return qml; } private: QDotNetHost defaultHost; mutable QDotNetHost *host = nullptr; mutable QDotNetFunction fnLoadAssembly; mutable QDotNetFunction> fnResolveStaticMethod; mutable QDotNetFunction> fnResolveConstructor; mutable QDotNetFunction> fnResolveInstanceMethod; mutable QDotNetFunction> fnResolveStaticFieldGet; mutable QDotNetFunction> fnResolveStaticFieldSet; mutable QDotNetFunction> fnResolveInstanceFieldGet; mutable QDotNetFunction> fnResolveInstanceFieldSet; mutable QDotNetFunction> fnResolveSafeMethod; mutable QDotNetFunction fnAddEventHandler; mutable QDotNetFunction fnRemoveEventHandler; mutable QDotNetFunction fnRemoveAllEventHandlersByContext; mutable QDotNetFunction fnAddObjectRef; mutable QDotNetFunction fnFreeDelegateRef; mutable QDotNetFunction fnFreeObjectRef; mutable QDotNetFunction fnFreeTypeRef; mutable QDotNetFunction fnAddInterfaceProxy; mutable QDotNetFunction, void *, void *, void *> fnSetInterfaceMethod; #ifndef QT_NO_DEBUG mutable QDotNetFunction fnStats; #endif mutable QDotNetFunction fnGetObject; mutable QDotNetFunction fnReset; static inline const QString defaultDllName = QLatin1String("Qt.DotNet.Adapter.dll"); static inline const QString defaultAssemblyName = QLatin1String("Qt.DotNet.Adapter"); static inline const QString defaultTypeName = QLatin1String("Qt.DotNet.Adapter"); mutable void *staticInterface = nullptr; mutable QQmlEngine *qml = nullptr; public: inline static std::function ctor_staticInterface = nullptr; inline static std::function dtor_staticInterface = nullptr; inline static QDotNetFunction gcCollect = nullptr; inline static QDotNetFunction gcWaitForPendingFinalizers = nullptr; };