diff options
Diffstat (limited to 'include')
-rw-r--r-- | include/iqmodelindex.h | 67 | ||||
-rw-r--r-- | include/iqvariant.h | 73 | ||||
-rw-r--r-- | include/qdotnetabstractlistmodel.h | 241 | ||||
-rw-r--r-- | include/qdotnetadapter.h | 39 | ||||
-rw-r--r-- | include/qdotnetarray.h | 4 | ||||
-rw-r--r-- | include/qdotnetcallback.h | 80 | ||||
-rw-r--r-- | include/qdotnetdelegate.h | 37 | ||||
-rw-r--r-- | include/qdotnetevent.h | 3 | ||||
-rw-r--r-- | include/qdotnethost.h | 205 | ||||
-rw-r--r-- | include/qdotnethostfxr.h | 34 | ||||
-rw-r--r-- | include/qdotnetinterface.h | 111 | ||||
-rw-r--r-- | include/qdotnetobject.h | 65 | ||||
-rw-r--r-- | include/qdotnetref.h | 31 | ||||
-rw-r--r-- | include/qdotnetstatic.h | 53 | ||||
-rw-r--r-- | include/qdotnettype.h | 75 |
15 files changed, 934 insertions, 184 deletions
diff --git a/include/iqmodelindex.h b/include/iqmodelindex.h new file mode 100644 index 0000000..84dd309 --- /dev/null +++ b/include/iqmodelindex.h @@ -0,0 +1,67 @@ +/*************************************************************************************************** + Copyright (C) 2024 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 "qdotnetinterface.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QModelIndex> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#include <functional> + +struct IQModelIndex : public QDotNetNativeInterface<QModelIndex> +{ + static inline const QString &AssemblyQualifiedName = + QStringLiteral("Qt.DotNet.IQModelIndex, Qt.DotNet.Adapter"); + + IQModelIndex(const void *objectRef = nullptr) + : QDotNetNativeInterface<QModelIndex>(objectRef) + { + } + + IQModelIndex(const QModelIndex &idx) + : QDotNetNativeInterface<QModelIndex>(AssemblyQualifiedName, new QModelIndex(idx), true) + { + init(); + } + + IQModelIndex(bool doCleanUp) + : QDotNetNativeInterface<QModelIndex>(AssemblyQualifiedName, new QModelIndex(), doCleanUp) + { + init(); + } + + void init() { + setCallback<bool>("IsValid", [this](void *data) + { + return reinterpret_cast<QModelIndex *>(data)->isValid(); + }); + setCallback<int>("Column", [this](void *data) + { + return reinterpret_cast<QModelIndex *>(data)->column(); + }); + setCallback<int>("Row", [this](void *data) + { + return reinterpret_cast<QModelIndex *>(data)->row(); + }); + setCallback<void *>("InternalPointer", [this](void *data) + { + return reinterpret_cast<QModelIndex *>(data)->internalPointer(); + }); + } + + static void staticInit(QDotNetInterface *sta) + { + sta->setCallback<IQModelIndex>("QModelIndex_Create", + [](void *) { return IQModelIndex(true); }); + } +}; diff --git a/include/iqvariant.h b/include/iqvariant.h new file mode 100644 index 0000000..255b082 --- /dev/null +++ b/include/iqvariant.h @@ -0,0 +1,73 @@ +/*************************************************************************************************** + Copyright (C) 2024 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 "qdotnetinterface.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QVariant> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#include <functional> + +struct IQVariant : public QDotNetNativeInterface<QVariant> +{ + static inline const QString &AssemblyQualifiedName = + QStringLiteral("Qt.DotNet.IQVariant, Qt.DotNet.Adapter"); + + IQVariant(const void *objectRef = nullptr) + : QDotNetNativeInterface<QVariant>(objectRef) + { + } + + IQVariant(QVariant &value, bool doCleanUp = false) + : QDotNetNativeInterface<QVariant>(AssemblyQualifiedName, &value, doCleanUp) + { + init(); + } + + IQVariant(const QString &value, bool doCleanUp = true) + : QDotNetNativeInterface<QVariant>(AssemblyQualifiedName, new QVariant(value), doCleanUp) + { + init(); + } + + IQVariant(bool doCleanUp) + : QDotNetNativeInterface<QVariant>(AssemblyQualifiedName, new QVariant(), doCleanUp) + { + init(); + } + + void init() { + setCallback<QString>("ToStringValue", [this](void *data) + { + QVariant *v = reinterpret_cast<QVariant *>(data); + if (!v) + return QString(); + return v->toString(); + }); + setCallback<void, QString>("SetValue", [this](void *data, const auto &newValue) + { + QVariant *v = reinterpret_cast<QVariant *>(data); + if (!v) + return; + v->setValue(newValue); + }); + } + + static void staticInit(QDotNetInterface *sta) + { + sta->setCallback<IQVariant, QString>("QVariant_Create", + [](void *, QString value) { return IQVariant(value, true); }); + sta->setCallback<IQVariant>("QVariant_Create", + [](void *) { return IQVariant(true); }); + } +}; diff --git a/include/qdotnetabstractlistmodel.h b/include/qdotnetabstractlistmodel.h new file mode 100644 index 0000000..d917d37 --- /dev/null +++ b/include/qdotnetabstractlistmodel.h @@ -0,0 +1,241 @@ +/*************************************************************************************************** + Copyright (C) 2024 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 "qdotnetinterface.h" +#include "qdotnetobject.h" +#include "qdotnetarray.h" +#include "iqvariant.h" +#include "iqmodelindex.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QAbstractListModel> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +#include <functional> + +template<typename Base> +struct IQAbstractListModel : public QDotNetNativeInterface<QAbstractListModel> +{ + static inline const QString &AssemblyQualifiedName = + QStringLiteral("Qt.DotNet.IQAbstractListModel, Qt.DotNet.Adapter"); + + IQAbstractListModel(Base *self) + : QDotNetNativeInterface<QAbstractListModel>(AssemblyQualifiedName, self, false) + { + setCallback<int, IQModelIndex>( + "Flags", [this](void *selfPtr, IQModelIndex index) + { + Base &self = *reinterpret_cast<Base *>(selfPtr); + return (int)self.base_flags(index); + }); + setCallback<bool, IQModelIndex, IQVariant, int>( + "SetData", [this](void *selfPtr, IQModelIndex index, IQVariant value, int role) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + return self->base_setData(index, value, role); + }); + setCallback<bool, int, int, IQModelIndex>( + "InsertRows", [this](void *selfPtr, int row, int count, IQModelIndex parent) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + return self->base_insertRows(row, count, parent); + }); + setCallback<bool, int, int, IQModelIndex>( + "RemoveRows", [this](void *selfPtr, int row, int count, IQModelIndex parent) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + return self->base_removeRows(row, count, parent); + }); + setCallback<void, IQModelIndex, int, int>( + "BeginInsertRows", [this](void *selfPtr, IQModelIndex parent, int first, int last) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + self->base_beginInsertRows(parent, first, last); + }); + setCallback<void>( + "EndInsertRows", [this](void *selfPtr) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + self->base_endInsertRows(); + }); + setCallback<void, IQModelIndex, int, int>( + "BeginRemoveRows", [this](void *selfPtr, IQModelIndex parent, int first, int last) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + self->base_beginRemoveRows(parent, first, last); + }); + setCallback<void>( + "EndRemoveRows", [this](void *selfPtr) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + self->base_endRemoveRows(); + }); + setCallback<IQModelIndex, int, int, void *>( + "CreateIndex", [this](void *selfPtr, int row, int col, void *ptr) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + return IQModelIndex(self->base_createIndex(row, col, ptr)); + }); + setCallback<void, IQModelIndex, IQModelIndex, QDotNetArray<int>>( + "EmitDataChanged", [this](void *selfPtr, + IQModelIndex topLeft, IQModelIndex bottomRight, QDotNetArray<int> roles) + { + auto *self = reinterpret_cast<Base *>(selfPtr); + if (roles.isValid()) { + QList<int> listRoles(roles.length()); + for (int i = 0; i < roles.length(); ++i) + listRoles[i] = roles[i]; + self->emit_base_dataChanged(topLeft, bottomRight, listRoles); + } + else { + self->emit_base_dataChanged(topLeft, bottomRight); + } + }); + } +}; + +class QDotNetAbstractListModel : public QAbstractListModel, public QDotNetObject +{ + +public: + using IBase = IQAbstractListModel<QDotNetAbstractListModel>; + Q_DOTNET_OBJECT_REF_INLINE(QDotNetAbstractListModel, Q_DOTNET_OBJECT_INIT(base(this))) + Q_DOTNET_OBJECT_COPY_INLINE(QDotNetAbstractListModel, Q_DOTNET_OBJECT_INIT(base(this))) + + QDotNetAbstractListModel(QDotNetObject &&movSrc) noexcept + : QDotNetObject(std::move(movSrc)), base(this) + {} + + QDotNetAbstractListModel &operator=(QDotNetObject &&movSrc) noexcept + { + QDotNetObject::operator=(std::move(movSrc)); + return *this; + } + + static void staticInit(QDotNetInterface *sta) + { + sta->setCallback<IBase, QDotNetObject>("QAbstractListModel_Create", + [](void *, QDotNetObject self) + { + auto *obj = new QDotNetAbstractListModel(std::move(self)); + return obj->base; + }); + } + + int rowCount(const QModelIndex &parent = QModelIndex()) const override + { + return method("RowCount", fnRowCount).invoke(*this, parent); + } + int base_rowCount(const QModelIndex &parent = QModelIndex()) const + { + return QAbstractListModel::rowCount(parent); + } + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override + { + return method("Data", fnData).invoke(*this, index, role); + } + QVariant base_data(const QModelIndex &index, int role = Qt::DisplayRole) const + { + return QAbstractListModel::data(index, role); + } + + Qt::ItemFlags flags(const QModelIndex &index) const override + { + return Qt::ItemFlags::fromInt(method("Flags", fnFlags).invoke(*this, index)); + } + Qt::ItemFlags base_flags(const QModelIndex &index) const + { + return QAbstractListModel::flags(index); + } + + bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override + { + return method("SetData", fnSetData) + .invoke(*this, index, const_cast<std::remove_const_t<QVariant &>>(value), role); + } + bool base_setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) + { + return QAbstractListModel::setData(index, value, role); + } + + bool insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) override + { + return method("InsertRows", fnInsertRows).invoke(*this, row, count, parent); + } + bool base_insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) + { + return QAbstractListModel::insertRows(row, count, parent); + } + + bool removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) override + { + return method("RemoveRows", fnRemoveRows).invoke(*this, row, count, parent); + } + bool base_removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) + { + return QAbstractListModel::removeRows(row, count, parent); + } + + QHash<int, QByteArray> roleNames() const override + { + auto names = method("RoleNames", fnRoleNames).invoke(*this); + if (names.isEmpty()) + return QAbstractListModel::roleNames(); + auto nameList = names.split(u',', Qt::SkipEmptyParts); + QHash<int, QByteArray> roles; + for (int i = 0; i < nameList.size(); ++i) + roles[Qt::UserRole + i + 1] = nameList[i].toUtf8(); + return roles; + } + + void base_beginInsertRows(const QModelIndex &parent, int first, int last) + { + QAbstractListModel::beginInsertRows(parent, first, last); + } + + void base_endInsertRows() + { + QAbstractListModel::endInsertRows(); + } + + void base_beginRemoveRows(const QModelIndex &parent, int first, int last) + { + QAbstractListModel::beginRemoveRows(parent, first, last); + } + + void base_endRemoveRows() + { + QAbstractListModel::endRemoveRows(); + } + + QModelIndex base_createIndex(int arow, int acolumn, const void *adata) const + { + return QAbstractListModel::createIndex(arow, acolumn, adata); + } + + void emit_base_dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, + const QList<int> &roles = QList<int>()) + { + emit QAbstractListModel::dataChanged(topLeft, bottomRight, roles); + } + +protected: + IBase base; + mutable QDotNetFunction<int, IQModelIndex> fnFlags = nullptr; + mutable QDotNetFunction<int, IQModelIndex> fnRowCount = nullptr; + mutable QDotNetFunction<IQVariant, IQModelIndex, int> fnData = nullptr; + mutable QDotNetFunction<bool, IQModelIndex, IQVariant, int> fnSetData = nullptr; + mutable QDotNetFunction<bool, int, int, IQModelIndex> fnInsertRows = nullptr; + mutable QDotNetFunction<bool, int, int, IQModelIndex> fnRemoveRows = nullptr; + mutable QDotNetFunction<QString> fnRoleNames = nullptr; +}; diff --git a/include/qdotnetadapter.h b/include/qdotnetadapter.h index fc9b74e..d9b5b95 100644 --- a/include/qdotnetadapter.h +++ b/include/qdotnetadapter.h @@ -33,6 +33,14 @@ private: ~QDotNetAdapter() { + if (staticInterface && dtor_staticInterface) { + dtor_staticInterface(staticInterface); + staticInterface = nullptr; + } + fnReset(); + //gcCollect(); + //gcWaitForPendingFinalizers(); + defaultHost.unload(); } @@ -53,6 +61,12 @@ public: .filePath(defaultDllName), defaultAssemblyName, defaultTypeName, externalHost); } + static void init(const QString &assemblyPath, const QString &typeAndAssemblyName, + QDotNetHost *externalHost = nullptr) + { + init(assemblyPath, typeAndAssemblyName, typeAndAssemblyName, externalHost); + } + static void init(const QString &assemblyPath, const QString &assemblyName, const QString &typeName, QDotNetHost *externalHost = nullptr) { @@ -92,10 +106,18 @@ public: host->resolveFunction(QDOTNETADAPTER_DELEGATE(SetInterfaceMethod)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(Stats)); host->resolveFunction(QDOTNETADAPTER_DELEGATE(GetObject)); + host->resolveFunction(QDOTNETADAPTER_DELEGATE(Reset)); #undef QDOTNETADAPTER_DELEGATE instance().host = host; + + if (ctor_staticInterface) + instance().staticInterface = ctor_staticInterface(); + gcCollect = instance().resolveStaticMethod("System.GC, System.Runtime", + "Collect", { QDotNetInbound<void>::Parameter }); + gcWaitForPendingFinalizers = instance().resolveStaticMethod("System.GC, System.Runtime", + "WaitForPendingFinalizers", { QDotNetInbound<void>::Parameter }); } static QDotNetAdapter &instance() @@ -225,12 +247,12 @@ public: fnFreeTypeRef(typeName); } - void *addInterfaceProxy(const QString &interfaceName) const + void *addInterfaceProxy(const QString &interfaceName, void *data, void *cleanUp) const { init(); if (interfaceName.isEmpty()) return nullptr; - return fnAddInterfaceProxy(interfaceName); + return fnAddInterfaceProxy(interfaceName, data, cleanUp); } void setInterfaceMethod(const QDotNetRef &obj, const QString &methodName, @@ -261,6 +283,8 @@ public: Stats s{ }; init(); fnStats(&s.refCount, &s.staticCount, &s.eventCount); + if (staticInterface && s.refCount > 0) + --s.refCount; return s; } @@ -287,13 +311,22 @@ private: mutable QDotNetFunction<void, void *> fnFreeDelegateRef; mutable QDotNetFunction<void, QDotNetRef> fnFreeObjectRef; mutable QDotNetFunction<void, QString> fnFreeTypeRef; - mutable QDotNetFunction<void *, QString> fnAddInterfaceProxy; + mutable QDotNetFunction<void *, QString, void *, void *> fnAddInterfaceProxy; mutable QDotNetFunction<void, QDotNetRef, QString, qint32, QList<QDotNetParameter>, void *, void *, void *> fnSetInterfaceMethod; mutable QDotNetFunction<void, qint32 *, qint32 *, qint32 *> fnStats; mutable QDotNetFunction<void *, QDotNetRef, QString> fnGetObject; + mutable QDotNetFunction<void> 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; + +public: + inline static std::function<void *()> ctor_staticInterface = nullptr; + inline static std::function<void(void *)> dtor_staticInterface = nullptr; + inline static QDotNetFunction<void> gcCollect = nullptr; + inline static QDotNetFunction<void> gcWaitForPendingFinalizers = nullptr; }; diff --git a/include/qdotnetarray.h b/include/qdotnetarray.h index 3a0289b..9f97b5f 100644 --- a/include/qdotnetarray.h +++ b/include/qdotnetarray.h @@ -36,9 +36,9 @@ public: QDotNetArray(qint32 length) { const QString elementTypeName = QDotNetTypeOf<T>::TypeName; - const QDotNetType elementType = QDotNetType::find(elementTypeName); + const QDotNetType elementType = QDotNetType::typeOf(elementTypeName); - QDotNetType arrayType = QDotNetType::find(QDotNetArray::FullyQualifiedTypeName); + QDotNetType arrayType = QDotNetType::typeOf(QDotNetArray::AssemblyQualifiedName); auto ctor = constructor<QDotNetArray, qint32>(); *this = ctor(length); } diff --git a/include/qdotnetcallback.h b/include/qdotnetcallback.h index 6efea1e..9f8c150 100644 --- a/include/qdotnetcallback.h +++ b/include/qdotnetcallback.h @@ -18,6 +18,19 @@ #include <functional> +template<typename T> +struct QDotNetCallbackArg : public QDotNetInbound<T> {}; + +template<typename T> +struct QDotNetCallbackReturn : public QDotNetOutbound<T> {}; + +template<> +struct QDotNetCallbackReturn<QString> : public QDotNetOutbound<QString> +{ + using SourceType = QString; + static inline const QDotNetParameter Parameter = QDotNetParameter::String; +}; + class QDotNetCallbackBase { protected: @@ -30,18 +43,18 @@ template<typename TResult, typename... TArg> class QDotNetCallback : public QDotNetCallbackBase { public: - using ReturnType = typename QDotNetInbound<TResult>::TargetType; - using FunctionType = std::function<ReturnType( - typename QDotNetInbound<TArg>::TargetType... arg)>; + using FunctionType = std::function<TResult(void *, TArg... arg)>; + using CleanUpType = std::function<void(TResult *)>; - using OutboundType = typename QDotNetOutbound<TResult>::OutboundType; + using OutboundType = typename QDotNetCallbackReturn<TResult>::OutboundType; using Delegate = OutboundType(QDOTNETFUNCTION_CALLTYPE *)( - QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType...); + QDotNetCallback *callback, quint64 key, + void *data, typename QDotNetCallbackArg<TArg>::InboundType...); using CleanUp = void(QDOTNETFUNCTION_CALLTYPE *)(QDotNetCallback *callback, quint64 key); - QDotNetCallback(FunctionType function) - : function(function) + QDotNetCallback(FunctionType fnCallback, CleanUpType fnCleanUp = nullptr) + : fnCallback(fnCallback), fnCleanUp(fnCleanUp) {} ~QDotNetCallback() override = default; @@ -59,64 +72,69 @@ public: private: struct Box { - ReturnType returnValue; + TResult returnValue; + Box(TResult &&ret) : returnValue(std::move(ret)) {} }; QMap<quint64, Box *> boxes; static OutboundType QDOTNETFUNCTION_CALLTYPE callbackDelegate( - QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType... arg) + QDotNetCallback *callback, quint64 key, + void *data, typename QDotNetCallbackArg<TArg>::InboundType... arg) { - Box *box = callback->boxes[key] = new Box - { - callback->function(QDotNetInbound<TArg>::convert(arg)...) - }; - const auto result = QDotNetOutbound<TResult>::convert(box->returnValue); - return result; + Box *box = callback->boxes[key] = new Box( + callback->fnCallback(data, QDotNetCallbackArg<TArg>::convert(arg)...)); + return QDotNetCallbackReturn<TResult>::convert(box->returnValue); } static void QDOTNETFUNCTION_CALLTYPE callbackCleanUp(QDotNetCallback *callback, quint64 key) { - if (const Box *box = callback->boxes.take(key)) + if (const Box *box = callback->boxes.take(key)) { + if (callback->fnCleanUp) + callback->fnCleanUp(const_cast<std::remove_const_t<TResult*>>(&(box->returnValue))); delete box; + } } - FunctionType function = nullptr; + FunctionType fnCallback = nullptr; + CleanUpType fnCleanUp = nullptr; }; template<typename... TArg> class QDotNetCallback<void, TArg...> : public QDotNetCallbackBase { public: - using FunctionType = std::function<void(typename QDotNetOutbound<TArg>::SourceType... arg)>; + using FunctionType = std::function<void(void *, TArg... arg)>; + using CleanUpType = nullptr_t; + + using Delegate = void(QDOTNETFUNCTION_CALLTYPE *)( + QDotNetCallback *callback, quint64 key, + void *data, typename QDotNetCallbackArg<TArg>::InboundType...); - QDotNetCallback(FunctionType function) - : function(function) + using CleanUp = nullptr_t; + + QDotNetCallback(FunctionType fnCallback, CleanUpType fnCleanUp = nullptr) + : fnCallback(fnCallback) {} ~QDotNetCallback() override = default; - using Delegate = void(QDOTNETFUNCTION_CALLTYPE *)( - QDotNetCallback *callback, quint64 key, typename QDotNetInbound<TArg>::InboundType...); static Delegate delegate() { return callbackDelegate; } - using CleanUp = void(QDOTNETFUNCTION_CALLTYPE *)(QDotNetCallback *callback, quint64 key); static CleanUp cleanUp() { - return callbackCleanUp; + return nullptr; } private: - static void QDOTNETFUNCTION_CALLTYPE callbackDelegate(QDotNetCallback *callback, quint64 key, - typename QDotNetInbound<TArg>::InboundType... arg) + static void QDOTNETFUNCTION_CALLTYPE callbackDelegate( + QDotNetCallback *callback, quint64 key, + void *data, typename QDotNetCallbackArg<TArg>::InboundType... arg) { - callback->function(QDotNetInbound<TArg>::convert(arg)...); + callback->fnCallback(data, QDotNetCallbackArg<TArg>::convert(arg)...); } - static void QDOTNETFUNCTION_CALLTYPE callbackCleanUp(QDotNetCallback *callback, quint64 key) - {} - - FunctionType function = nullptr; + FunctionType fnCallback = nullptr; }; diff --git a/include/qdotnetdelegate.h b/include/qdotnetdelegate.h new file mode 100644 index 0000000..25fc3a8 --- /dev/null +++ b/include/qdotnetdelegate.h @@ -0,0 +1,37 @@ +/*************************************************************************************************** + Copyright (C) 2025 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 "qdotnetobject.h" + +#ifdef __GNUC__ +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wconversion" +#endif +#include <QString> +#ifdef __GNUC__ +# pragma GCC diagnostic pop +#endif + +template<typename T, typename... TArg> +class QDotNetDelegate : public QDotNetObject +{ +public: + Q_DOTNET_OBJECT_INLINE(QDotNetDelegate, "System.Delegate"); + + T invoke(TArg... arg) const + { + return method("Invoke", fnInvoke).invoke(*this, arg...); + } + + T operator()(TArg... arg) const + { + return invoke(arg...); + } + +private: + mutable QDotNetFunction<T, TArg...> fnInvoke; +}; diff --git a/include/qdotnetevent.h b/include/qdotnetevent.h index 11df0a1..00ea036 100644 --- a/include/qdotnetevent.h +++ b/include/qdotnetevent.h @@ -20,7 +20,8 @@ class QDotNetPropertyEvent : public QDotNetObject { public: - Q_DOTNET_OBJECT_INLINE(QDotNetPropertyEvent, "System.ComponentModel.PropertyChangedEventArgs"); + Q_DOTNET_OBJECT_INLINE(QDotNetPropertyEvent, + "System.ComponentModel.PropertyChangedEventArgs, System.ObjectModel"); QString propertyName() const { diff --git a/include/qdotnethost.h b/include/qdotnethost.h index c2fe298..05b0922 100644 --- a/include/qdotnethost.h +++ b/include/qdotnethost.h @@ -35,7 +35,7 @@ public: unload(); } - bool load(const QString& runtimeConfig = defaultRuntimeConfig, const QString &runtimePath = {}) + bool load(const QString &runtimeConfig = defaultRuntimeConfig, const QString &runtimePath = {}) { if (isLoaded()) return true; @@ -48,6 +48,57 @@ public: return true; } + bool appMain(const QString &appHostPath, const QString &appLibPath) + { + if (isLoaded()) + return false; + + if (!loadRuntime({})) + return false; + const char_t *host_path = STR(appHostPath); + const char_t *app_path = STR(appLibPath); + const char_t *dotnet_root = (char_t *)L"C:\\Program Files\\dotnet\\"; + + int argc = 1; + const char_t *argv[] = { host_path, nullptr }; + + return fnMainStartup(argc, argv, host_path, dotnet_root, app_path) == 0; + } + + bool loadApp(const QString &appPath, const QStringList &args = {}, const QString &runtimePath = {}) + { + if (isLoaded()) + return false; + + if (!loadRuntime(runtimePath)) + return false; + + int argc = 1; + const char_t *argv[] = { STR(appPath), nullptr }; + + auto result = fnInitApp(argc, argv, nullptr, &hostContext); + if (HOSTFN_FAILED(result) || hostContext == nullptr) { + qCritical() << "Error calling function: hostfxr_initialize_for_dotnet_command_line"; + unloadRuntime(); + return false; + } + + setRuntimeProperty("STARTUP_HOOKS", + QDir(QCoreApplication::applicationDirPath()).filePath("Qt.DotNet.Adapter.dll")); + + setRuntimeProperty("QT_DOTNET_RESOLVE_FN", QString("%1") + .arg((qulonglong)(&fnLoadAssemblyAndGetFunctionPointer), 16, 16, QChar('0'))); + + return true; + } + + int runApp() + { + if (!isLoaded() || fnLoadAssemblyAndGetFunctionPointer != nullptr) + return false; + return fnRunApp(hostContext); + } + void unload() { if (!isLoaded()) @@ -61,6 +112,11 @@ public: return (hostContext != nullptr); } + bool isReady() const + { + return (fnLoadAssemblyAndGetFunctionPointer != nullptr); + } + bool resolveFunction(QDotNetFunction<quint32, void *, qint32> &outFunc, const QString &assemblyPath, const QString &typeName, const QString &methodName) { @@ -172,51 +228,49 @@ private: } const QString dotNetInfo(procDotNetInfo.readAllStandardOutput()); - QString runtimeDirPath = {}; - QString hostVersion = {}; - QVersionNumber maxVersion; + QVersionNumber selectedVersion = {}; + QString selectedRuntimePath = {}; const QRegularExpression dotNetInfoParser(regexParseDotNetInfo, QRegularExpression::MultilineOption | QRegularExpression::DotMatchesEverythingOption); for (const auto &match : dotNetInfoParser.globalMatch(dotNetInfo)) { - const auto version = QVersionNumber::fromString(match.captured("version")); - if (version > maxVersion) { - maxVersion = version; - hostVersion = match.captured("version"); - runtimeDirPath = match.captured("path"); - } - } - - if (runtimeDirPath.isEmpty()) { - qCritical() << "Error parsing dotnet info"; - return {}; - } - QDir runtimeDir(runtimeDirPath); - if (!runtimeDir.exists()) { - qCritical() << "Error dotnet runtime directory not found"; - return {}; - } - - runtimeDir.cd(QString("../../host/fxr/%1").arg(hostVersion)); - if (!runtimeDir.exists()) { - qCritical() << "Error dotnet host fxr directory not found"; - return {}; - } + const auto hostVersion = match.captured("version"); + const auto version = QVersionNumber::fromString(hostVersion); + if (version <= selectedVersion) + continue; + + const auto runtimeDirPath = match.captured("path"); + if (runtimeDirPath.isEmpty()) + continue; + QDir runtimeDir(runtimeDirPath); + if (!runtimeDir.exists()) + continue; + runtimeDir.cd(QString("../../host/fxr/%1").arg(hostVersion)); + if (!runtimeDir.exists()) + continue; #ifdef Q_OS_WINDOWS - QString runtimePath = runtimeDir.absoluteFilePath("hostfxr.dll"); + const auto runtimePath = runtimeDir.absoluteFilePath("hostfxr.dll"); #else - QString runtimePath = runtimeDir.absoluteFilePath("libhostfxr.so"); + const auto runtimePath = runtimeDir.absoluteFilePath("libhostfxr.so"); #endif - if (!QFile::exists(runtimePath)) { - qCritical() << "Error dotnet host fxr dll not found"; + if (!QFile::exists(runtimePath)) + continue; + + selectedVersion = version; + selectedRuntimePath = runtimePath; + } + + if (selectedVersion.isNull()) { + qCritical() << "Error locating runtime host library."; return {}; } - return runtimePath; + + return selectedRuntimePath; } bool loadRuntime(const QString & runtimePath) { - if (fnInitHost != nullptr) + if (fnCloseHost != nullptr) return true; if (!runtimePath.isEmpty()) { @@ -235,45 +289,53 @@ private: return false; } - fnInitHost = GET_FN(runtime, hostfxr_initialize_for_runtime_config_fn); - if (!fnInitHost) { - qCritical() << "Error loading function: hostfxr_initialize_for_runtime_config"; + if (!(fnMainStartup = GET_FN(runtime, hostfxr_main_startupinfo_fn))) { + qCritical() << "Error loading function: hostfxr_main_startupinfo"; return false; } - fnGetRuntimeDelegate = GET_FN(runtime, hostfxr_get_runtime_delegate_fn); - if (!fnGetRuntimeDelegate) { - qCritical() << "Error loading function: hostfxr_get_runtime_delegate"; + if (!(fnSetErrorWriter = GET_FN(runtime, hostfxr_set_error_writer_fn))) { + qCritical() << "Error loading function: hostfxr_set_error_writer"; return false; } - fnCloseHost = GET_FN(runtime, hostfxr_close_fn); - if (!fnCloseHost) { - qCritical() << "Error loading function: hostfxr_close"; + if (!(fnInitApp = GET_FN(runtime, hostfxr_initialize_for_dotnet_command_line_fn))) { + qCritical() << "Error loading function: hostfxr_initialize_for_dotnet_command_line"; return false; } - fnSetErrorWriter = GET_FN(runtime, hostfxr_set_error_writer_fn); - if (!fnSetErrorWriter) { - qCritical() << "Error loading function: hostfxr_set_error_writer"; + if (!(fnInitHost = GET_FN(runtime, hostfxr_initialize_for_runtime_config_fn))) { + qCritical() << "Error loading function: hostfxr_initialize_for_runtime_config"; + return false; + } + + if (!(fnRuntimeProperty = GET_FN(runtime, hostfxr_get_runtime_property_value_fn))) { + qCritical() << "Error loading function: hostfxr_get_runtime_property_value_fn"; + return false; + } + + if (!(fnSetRuntimeProperty = GET_FN(runtime, hostfxr_set_runtime_property_value_fn))) { + qCritical() << "Error loading function: hostfxr_set_runtime_property_value_fn"; return false; } - fnAllRuntimeProperties = GET_FN(runtime, hostfxr_get_runtime_properties_fn); - if (!fnAllRuntimeProperties) { + if (!(fnAllRuntimeProperties = GET_FN(runtime, hostfxr_get_runtime_properties_fn))) { qCritical() << "Error loading function: hostfxr_get_runtime_properties_fn"; return false; } - fnRuntimeProperty = GET_FN(runtime, hostfxr_get_runtime_property_value_fn); - if (!fnRuntimeProperty) { - qCritical() << "Error loading function: hostfxr_get_runtime_property_value_fn"; + if (!(fnRunApp = GET_FN(runtime, hostfxr_run_app_fn))) { + qCritical() << "Error loading function: hostfxr_run_app"; return false; } - fnSetRuntimeProperty = GET_FN(runtime, hostfxr_set_runtime_property_value_fn); - if (!fnSetRuntimeProperty) { - qCritical() << "Error loading function: hostfxr_set_runtime_property_value_fn"; + if (!(fnGetRuntimeDelegate = GET_FN(runtime, hostfxr_get_runtime_delegate_fn))) { + qCritical() << "Error loading function: hostfxr_get_runtime_delegate"; + return false; + } + + if (!(fnCloseHost = GET_FN(runtime, hostfxr_close_fn))) { + qCritical() << "Error loading function: hostfxr_close"; return false; } @@ -283,17 +345,26 @@ private: void unloadRuntime() { - runtime.unload(); + fnSetErrorWriter = nullptr; + fnInitApp = nullptr; fnInitHost = nullptr; - fnGetRuntimeDelegate = nullptr; - fnCloseHost = nullptr; - fnAllRuntimeProperties = nullptr; fnRuntimeProperty = nullptr; fnSetRuntimeProperty = nullptr; + fnAllRuntimeProperties = nullptr; + fnRunApp = nullptr; + fnGetRuntimeDelegate = nullptr; + fnCloseHost = nullptr; + fnLoadAssemblyAndGetFunctionPointer = nullptr; + fnLoadAssembly = nullptr; + fnGetFunctionPointer = nullptr; + runtime.unload(); } bool init(const QString &runtimeConfig) { + if (fnLoadAssemblyAndGetFunctionPointer) + return true; + if (fnInitHost == nullptr) return false; @@ -365,11 +436,11 @@ private: static inline const QString defaultRuntimeConfig = QStringLiteral(R"[json]( { "runtimeOptions": { - "tfm": "net6.0", + "tfm": "net8.0", "rollForward": "LatestMinor", "framework": { "name": "Microsoft.NETCore.App", - "version": "6.0.0" + "version": "8.0.0" } } } @@ -379,16 +450,24 @@ private: \bMicrosoft\.NETCore\.App[^0-9]*(?<version>[0-9\.]+)[^\[]*\[(?<path>[^\]]+)\] )[regex]").remove('\r').remove('\n'); + static inline const QString regexParseDotNetRoot = QStringLiteral(R"[regex]( +^(?:(?![\\\/]host[\\\/]).)*[\\\/] +)[regex]").remove('\r').remove('\n'); QLibrary runtime; - hostfxr_initialize_for_runtime_config_fn fnInitHost = nullptr; - hostfxr_get_runtime_delegate_fn fnGetRuntimeDelegate = nullptr; - hostfxr_close_fn fnCloseHost = nullptr; + hostfxr_main_startupinfo_fn fnMainStartup = nullptr; hostfxr_set_error_writer_fn fnSetErrorWriter = nullptr; - hostfxr_get_runtime_properties_fn fnAllRuntimeProperties = nullptr; + hostfxr_initialize_for_dotnet_command_line_fn fnInitApp = nullptr; + hostfxr_initialize_for_runtime_config_fn fnInitHost = nullptr; hostfxr_get_runtime_property_value_fn fnRuntimeProperty = nullptr; hostfxr_set_runtime_property_value_fn fnSetRuntimeProperty = nullptr; + hostfxr_get_runtime_properties_fn fnAllRuntimeProperties = nullptr; + hostfxr_run_app_fn fnRunApp = nullptr; + hostfxr_get_runtime_delegate_fn fnGetRuntimeDelegate = nullptr; + hostfxr_close_fn fnCloseHost = nullptr; hostfxr_handle hostContext = nullptr; load_assembly_and_get_function_pointer_fn fnLoadAssemblyAndGetFunctionPointer = nullptr; + load_assembly_fn fnLoadAssembly = nullptr; + get_function_pointer_fn fnGetFunctionPointer = nullptr; }; diff --git a/include/qdotnethostfxr.h b/include/qdotnethostfxr.h index e5e1d0c..dc57ce0 100644 --- a/include/qdotnethostfxr.h +++ b/include/qdotnethostfxr.h @@ -116,6 +116,13 @@ using get_hostfxr_path_fn = quint32(NETHOST_CALLTYPE *)( # define HOSTFXR_CALLTYPE #endif +using hostfxr_main_startupinfo_fn = int(HOSTFXR_CALLTYPE *)( + const int argc, + const char_t **argv, + const char_t *host_path, + const char_t *dotnet_root, + const char_t *app_path); + enum hostfxr_delegate_type { hdt_com_activation, @@ -124,7 +131,9 @@ enum hostfxr_delegate_type hdt_com_register, hdt_com_unregister, hdt_load_assembly_and_get_function_pointer, - hdt_get_function_pointer + hdt_get_function_pointer, + hdt_load_assembly, + hdt_load_assembly_bytes }; using hostfxr_error_writer_fn = void(HOSTFXR_CALLTYPE *)(const char_t *message); @@ -140,6 +149,13 @@ struct hostfxr_initialize_parameters const char_t *dotnet_root; }; +using hostfxr_initialize_for_dotnet_command_line_fn = int(HOSTFXR_CALLTYPE *)( + int argc, + const char_t *argv[], + const hostfxr_initialize_parameters *parameters, + /*out*/ hostfxr_handle *host_context_handle +); + using hostfxr_initialize_for_runtime_config_fn = quint32(HOSTFXR_CALLTYPE *)( const char_t *runtime_config_path, const hostfxr_initialize_parameters *parameters, @@ -161,6 +177,8 @@ using hostfxr_get_runtime_properties_fn = quint32(HOSTFXR_CALLTYPE *)( /*out*/ const char_t **keys, /*out*/ const char_t **values); +using hostfxr_run_app_fn = int (HOSTFXR_CALLTYPE *)(const hostfxr_handle host_context_handle); + using hostfxr_get_runtime_delegate_fn = quint32(HOSTFXR_CALLTYPE *)( hostfxr_handle host_context_handle, hostfxr_delegate_type type, @@ -168,7 +186,6 @@ using hostfxr_get_runtime_delegate_fn = quint32(HOSTFXR_CALLTYPE *)( using hostfxr_close_fn = quint32(HOSTFXR_CALLTYPE *)(hostfxr_handle host_context_handle); - /* adapted from: https://github.com/dotnet/runtime/blob/main/src/native/corehost/coreclr_delegates.h @@ -179,25 +196,26 @@ using hostfxr_close_fn = quint32(HOSTFXR_CALLTYPE *)(hostfxr_handle host_context # define CORECLR_DELEGATE_CALLTYPE #endif -using component_entry_point_fn = quint32(CORECLR_DELEGATE_CALLTYPE *)( - void *arg, qint32 arg_size_in_bytes); - using load_assembly_and_get_function_pointer_fn = quint32(CORECLR_DELEGATE_CALLTYPE *)( const char_t *assembly_path , const char_t *type_name , const char_t *method_name , const char_t *delegate_type_name, - void *reserved, + nullptr_t reserved, /*out*/ void **delegate ); using get_function_pointer_fn = quint32(CORECLR_DELEGATE_CALLTYPE *)( const char_t *type_name, const char_t *method_name, const char_t *delegate_type_name, - void *load_context, - void *reserved, + nullptr_t load_context, + nullptr_t reserved, /*out*/ void **delegate); +using load_assembly_fn = int (CORECLR_DELEGATE_CALLTYPE *)( + const char_t *assembly_path, + nullptr_t load_context, + nullptr_t reserved); /* adapted from: diff --git a/include/qdotnetinterface.h b/include/qdotnetinterface.h index 4dae61c..c6850e3 100644 --- a/include/qdotnetinterface.h +++ b/include/qdotnetinterface.h @@ -21,44 +21,71 @@ class QDotNetInterface : public QDotNetRef { public: - QDotNetInterface(const QString &interfaceName) - : QDotNetRef(adapter().addInterfaceProxy(interfaceName)) + QDotNetInterface(const QString &interfaceName, void *data = nullptr, void *cleanUp = nullptr) + : QDotNetRef(adapter().addInterfaceProxy(interfaceName, data, cleanUp)) {} - template<typename TResult, typename... TArg> - void setCallback(const QString &methodName, const QList<QDotNetParameter> ¶ms, - typename QDotNetCallback<TResult, TArg...>::FunctionType function) + QDotNetInterface(const void *objectRef = nullptr) + : QDotNetRef(objectRef) + {} + + QDotNetInterface(const QDotNetInterface &cpySrc) + : QDotNetRef(cpySrc) + {} + + QDotNetInterface &operator =(const QDotNetInterface &cpySrc) { - auto *callback = new QDotNetCallback<TResult, TArg...>(function); - callbacks.append(callback); + QDotNetRef::operator=(cpySrc); + return *this; + } - QList<QDotNetParameter> modifiedParams - { - params[0], - UnmanagedType::SysInt, - UnmanagedType::U8 - }; - for (qsizetype i = 1; i < params.size(); ++i) - modifiedParams.append(params[i]); + QDotNetInterface(QDotNetInterface &&movSrc) noexcept + : QDotNetRef(std::move(movSrc)) + {} - adapter().setInterfaceMethod(*this, methodName, modifiedParams, - reinterpret_cast<void *>(callback->delegate()), - reinterpret_cast<void *>(callback->cleanUp()), callback); + QDotNetInterface &operator=(QDotNetInterface &&movSrc) noexcept + { + QDotNetRef::operator=(std::move(movSrc)); + return *this; + } + + template<typename T> + T *dataAs() + { + if (!fnDataPtr.isValid()) { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<void *>::Parameter + }; + fnDataPtr = adapter().resolveInstanceMethod(*this, "get_Data", parameters); + } + return reinterpret_cast<T *>(fnDataPtr()); + } + + virtual ~QDotNetInterface() override + { + if (!isValid()) + return; + for (const QDotNetCallbackBase *callback : callbacks) + delete callback; + callbacks.clear(); } template<typename TResult, typename... TArg> void setCallback(const QString &methodName, - typename QDotNetCallback<TResult, TArg...>::FunctionType function) + typename QDotNetCallback<TResult, TArg...>::FunctionType function, + typename QDotNetCallback<TResult, TArg...>::CleanUpType cleanUp = nullptr) { - auto *callback = new QDotNetCallback<TResult, TArg...>(function); + auto *callback = new QDotNetCallback<TResult, TArg...>(function, cleanUp); callbacks.append(callback); const QList<QDotNetParameter> parameters { - QDotNetInbound<TResult>::Parameter, + QDotNetCallbackReturn<TResult>::Parameter, UnmanagedType::SysInt, UnmanagedType::U8, - QDotNetInbound<TArg>::Parameter... + UnmanagedType::SysInt, + QDotNetCallbackArg<TArg>::Parameter... }; adapter().setInterfaceMethod( @@ -66,20 +93,44 @@ public: callback->delegate(), callback->cleanUp(), callback); } - ~QDotNetInterface() override - { - for (const QDotNetCallbackBase *callback : callbacks) - delete callback; - callbacks.clear(); - } - private: QList<QDotNetCallbackBase *> callbacks; + QDotNetFunction<void *> fnDataPtr = nullptr; }; template<typename T> struct QDotNetTypeOf<T, std::enable_if_t<std::is_base_of_v<QDotNetInterface, T>>> { - static inline const QString TypeName = T::FullyQualifiedTypeName; + static inline const QString TypeName = T::AssemblyQualifiedName; static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef; }; + +template<typename T> +struct QDotNetNativeInterface : public QDotNetInterface +{ + QDotNetNativeInterface(const void *objectRef = nullptr) + : QDotNetInterface(objectRef) + { + } + + QDotNetNativeInterface(const QString &interfaceName, T *data, bool doCleanUp = true) + : QDotNetInterface(interfaceName, data, doCleanUp ? cleanUp : nullptr) + { + } + + T *data() + { + return dataAs<T>(); + } + + operator T&() + { + return *data(); + } + + static void QDOTNETFUNCTION_CALLTYPE cleanUp(void *data) + { + if (data) + delete reinterpret_cast<T *>(data); + } +}; diff --git a/include/qdotnetobject.h b/include/qdotnetobject.h index 34dbc3c..5657c6f 100644 --- a/include/qdotnetobject.h +++ b/include/qdotnetobject.h @@ -17,6 +17,8 @@ # pragma GCC diagnostic pop #endif +struct QDotNetEventHandler; + class QDotNetObject : public QDotNetRef { private: @@ -37,7 +39,7 @@ private: // Fully qualified .NET class name #define Q_DOTNET_OBJECT_TYPE(T,type_name)\ - static inline const QString &FullyQualifiedTypeName = QString(type_name) + static inline const QString &AssemblyQualifiedName = QString(type_name) // All required declarations #define Q_DOTNET_OBJECT(T,type_name)\ @@ -125,7 +127,7 @@ private: #define Q_DOTNET_OBJECT_INIT(...) , __VA_ARGS__ public: - static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Object"); + static inline const QString &AssemblyQualifiedName = QStringLiteral("System.Object"); QDotNetObject(const void *objectRef = nullptr) : QDotNetRef(objectRef) @@ -151,6 +153,8 @@ public: return *this; } + virtual ~QDotNetObject() override = default; + const QDotNetType &type() const { if (!fnGetType.isValid()) { @@ -160,16 +164,6 @@ public: return objType; } - QString toString() const - { - return method("ToString", fnToString).invoke(*this); - } - - bool equals(const QDotNetRef &obj) const - { - return method("Equals", fnEquals).invoke(*this, obj); - } - template<typename TResult, typename ...TArg> QDotNetFunction<TResult, TArg...> method(const QString &methodName) const { @@ -243,19 +237,12 @@ public: return QDotNetType::constructor(typeName, ctor); } - struct IEventHandler - { - virtual ~IEventHandler() = default; - virtual void handleEvent(const QString &eventName, QDotNetObject &eventSource, - QDotNetObject &eventArgs) = 0; - }; - - void subscribeEvent(const QString &eventName, IEventHandler *eventHandler) + void subscribe(const QString &eventName, QDotNetEventHandler *eventHandler) { adapter().addEventHandler(*this, eventName, eventHandler, eventCallback); } - void unsubscribeEvent(const QString &eventName, IEventHandler *eventHandler) + void unsubscribe(const QString &eventName, QDotNetEventHandler *eventHandler) { adapter().removeEventHandler(*this, eventName, eventHandler); } @@ -281,31 +268,24 @@ protected: template<typename T, typename ...TArg> static QDotNetFunction<T, TArg...> constructor() { - return QDotNetType::constructor<T, TArg...>(T::FullyQualifiedTypeName); + return QDotNetType::constructor<T, TArg...>(T::AssemblyQualifiedName); } template<typename T, typename ...TArg> static QDotNetFunction<T, TArg...> &constructor(QDotNetFunction<T, TArg...> &ctor) { - return QDotNetType::constructor(T::FullyQualifiedTypeName, ctor); + return QDotNetType::constructor(T::AssemblyQualifiedName, ctor); } template<typename T, typename ...TArg> static QDotNetSafeMethod<T, TArg...> &constructor(QDotNetSafeMethod<T, TArg...> &ctor) { - return QDotNetType::constructor(T::FullyQualifiedTypeName, ctor); + return QDotNetType::constructor(T::AssemblyQualifiedName, ctor); } private: - static void QDOTNETFUNCTION_CALLTYPE eventCallback(void *context, void *eventNameChars, - void *eventSourceRef, void *eventArgsRef) - { - auto *receiver = static_cast<IEventHandler *>(context); - const QString eventName(static_cast<const QChar *>(eventNameChars)); - QDotNetObject eventSource(eventSourceRef); - QDotNetObject eventArgs(eventArgsRef); - receiver->handleEvent(eventName, eventSource, eventArgs); - } + static inline void QDOTNETFUNCTION_CALLTYPE eventCallback(void *context, void *eventNameChars, + void *eventSourceRef, void *eventArgsRef); mutable QDotNetFunction<QDotNetType> fnGetType; mutable QDotNetType objType = nullptr; @@ -313,9 +293,26 @@ private: mutable QDotNetFunction<bool, QDotNetRef> fnEquals; }; +struct QDotNetEventHandler +{ + virtual ~QDotNetEventHandler() = default; + virtual void handleEvent(const QString &eventName, QDotNetObject &eventSource, + QDotNetObject &eventArgs) = 0; +}; + +inline void QDOTNETFUNCTION_CALLTYPE QDotNetObject::eventCallback(void *context, void *eventNameChars, + void *eventSourceRef, void *eventArgsRef) +{ + auto *receiver = static_cast<QDotNetEventHandler *>(context); + const QString eventName(static_cast<const QChar *>(eventNameChars)); + QDotNetObject eventSource(eventSourceRef); + QDotNetObject eventArgs(eventArgsRef); + receiver->handleEvent(eventName, eventSource, eventArgs); +} + template<typename T> struct QDotNetTypeOf<T, std::enable_if_t<std::is_base_of_v<QDotNetObject, T>>> { - static inline const QString TypeName = T::FullyQualifiedTypeName; + static inline const QString TypeName = T::AssemblyQualifiedName; static inline UnmanagedType MarshalAs = UnmanagedType::ObjectRef; }; diff --git a/include/qdotnetref.h b/include/qdotnetref.h index fff2e06..06ce02a 100644 --- a/include/qdotnetref.h +++ b/include/qdotnetref.h @@ -10,7 +10,7 @@ class QDotNetRef { public: - static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Object"); + static inline const QString &AssemblyQualifiedName = QStringLiteral("System.Object"); const void *gcHandle() const { return objectRef; } bool isValid() const { return gcHandle() != nullptr; } @@ -59,6 +59,32 @@ public: class Null {}; + QString toString() const + { + if (!fnToString.isValid()) { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<QString>::Parameter + }; + fnToString = adapter().resolveInstanceMethod(*this, "ToString", parameters); + } + return fnToString(); + } + + bool equals(const QDotNetRef &obj) const + { + if (!fnEquals.isValid()) { + const QList<QDotNetParameter> parameters + { + QDotNetInbound<bool>::Parameter, + QDotNetOutbound<QDotNetRef>::Parameter + }; + fnEquals = adapter().resolveInstanceMethod(*this, "Equals", parameters); + } + return fnEquals(obj); + } + + protected: static QDotNetAdapter &adapter() { return QDotNetAdapter::instance(); } @@ -93,6 +119,9 @@ private: } const void *objectRef = nullptr; + + mutable QDotNetFunction<QString> fnToString = nullptr; + mutable QDotNetFunction<bool, QDotNetRef> fnEquals = nullptr; }; template<typename T> diff --git a/include/qdotnetstatic.h b/include/qdotnetstatic.h new file mode 100644 index 0000000..7b07147 --- /dev/null +++ b/include/qdotnetstatic.h @@ -0,0 +1,53 @@ +/*************************************************************************************************** + Copyright (C) 2024 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 "qdotnetinterface.h" +#include "iqmodelindex.h" +#include "iqvariant.h" +#include "qdotnetabstractlistmodel.h" + +#include <functional> + +class QDotNetStatic : public QDotNetInterface +{ +public: + static inline const QString &AssemblyQualifiedName = + QStringLiteral("Qt.DotNet.Adapter+IStatic, Qt.DotNet.Adapter"); + + QDotNetStatic(const void *objectRef) : QDotNetInterface(objectRef) {} + + QDotNetStatic() : QDotNetInterface(AssemblyQualifiedName, nullptr) + { + IQVariant::staticInit(this); + IQModelIndex::staticInit(this); + QDotNetAbstractListModel::staticInit(this); + } +}; + +inline static bool ctor_static = std::invoke([]() + { + QDotNetAdapter::ctor_staticInterface = []() + { + auto *staticQVariant = new QDotNetStatic(); + auto setStatic = QDotNetType::staticMethod<void, QDotNetStatic>( + "Qt.DotNet.Adapter, Qt.DotNet.Adapter", "set_Static"); + setStatic(*staticQVariant); + return staticQVariant; + }; + return true; + }); + +inline static bool dtor_static = std::invoke([]() + { + QDotNetAdapter::dtor_staticInterface = [](void *that) + { + QtDotNet::call<void, QDotNetStatic>( + "Qt.DotNet.Adapter, Qt.DotNet.Adapter", "set_Static", nullptr); + delete reinterpret_cast<QDotNetStatic *>(that); + }; + return true; + }); diff --git a/include/qdotnettype.h b/include/qdotnettype.h index 4ab99bf..dc3bb90 100644 --- a/include/qdotnettype.h +++ b/include/qdotnettype.h @@ -19,7 +19,7 @@ class QDotNetType : public QDotNetRef { public: - static inline const QString &FullyQualifiedTypeName = QStringLiteral("System.Type"); + static inline const QString &AssemblyQualifiedName = QStringLiteral("System.Type"); QDotNetType(const void *typeRef = nullptr) : QDotNetRef(typeRef) @@ -45,6 +45,18 @@ public: return *this; } + QString assemblyQualifiedName() const + { + if (!isValid()) + return QStringLiteral(""); + if (!fnAssemblyQualifiedName.isValid()) { + fnAssemblyQualifiedName = adapter().resolveInstanceMethod(*this, + "get_AssemblyQualifiedName", { UnmanagedType::LPWStr }); + strFullName = fnAssemblyQualifiedName(); + } + return strFullName; + } + QString fullName() const { if (!isValid()) @@ -57,16 +69,16 @@ public: return strFullName; } - static QDotNetType find(const QString &typeName) + static QDotNetType typeOf(const QString &typeName) { QDotNetFunction<QDotNetType, QString> fnGetType; - return staticMethod(FullyQualifiedTypeName, "GetType", fnGetType).invoke(nullptr, typeName); + return staticMethod(AssemblyQualifiedName, "GetType", fnGetType).invoke(nullptr, typeName); } template<typename T> - static QDotNetType find() + static QDotNetType typeOf() { - return find(T::FullyQualifiedTypeName); + return typeOf(T::AssemblyQualifiedName); } template<typename TResult, typename ...TArg> @@ -102,7 +114,7 @@ public: template<typename TResult, typename ...TArg> QDotNetFunction<TResult, TArg...> staticMethod(const QString &methodName) const { - return staticMethod<TResult, TArg...>(fullName(), methodName); + return staticMethod<TResult, TArg...>(assemblyQualifiedName(), methodName); } template<typename TResult, typename ...TArg> @@ -155,24 +167,24 @@ public: template<typename T, typename ...TArg> QDotNetFunction<T, TArg...> constructor() const { - return constructor<T, TArg...>(fullName()); + return constructor<T, TArg...>(assemblyQualifiedName()); } template<typename T, typename ...TArg> QDotNetFunction<T, TArg...> &constructor(QDotNetFunction<T, TArg...> &ctor) const { - return constructor(fullName(), ctor); + return constructor(assemblyQualifiedName(), ctor); } template<typename T, typename ...TArg> QDotNetFunction<T, TArg...> &constructor(QDotNetSafeMethod<T, TArg...> &ctor) const { - return constructor(fullName(), ctor); + return constructor(assemblyQualifiedName(), ctor); } void freeTypeRef() { - freeTypeRef(fullName()); + freeTypeRef(assemblyQualifiedName()); } static void freeTypeRef(const QString &typeName) @@ -183,12 +195,53 @@ public: template<typename T> static void freeTypeRef() { - freeTypeRef(T::FullyQualifiedTypeName); + freeTypeRef(T::AssemblyQualifiedName); + } + + bool isAssignableFrom(QDotNetType c) const + { + if (!c.isValid()) + return false; + if (!fnIsAssignableFrom.isValid()) { + fnIsAssignableFrom = adapter().resolveInstanceMethod(*this, "IsAssignableFrom", + { UnmanagedType::Bool, QStringLiteral("System.Type") }); + if (!fnIsAssignableFrom.isValid()) + return false; + } + return fnIsAssignableFrom(c); + } + + template<typename T> + bool isAssignableFrom() const + { + return isAssignableFrom(typeOf<T>()); + } + + bool isAssignableTo(QDotNetType c) const + { + if (!c.isValid()) + return false; + if (!fnIsAssignableTo.isValid()) { + fnIsAssignableTo = adapter().resolveInstanceMethod(*this, "IsAssignableTo", + { UnmanagedType::Bool, QStringLiteral("System.Type") }); + if (!fnIsAssignableTo.isValid()) + return false; + } + return fnIsAssignableTo(c); + } + + template<typename T> + bool isAssignableTo() const + { + return isAssignableTo(typeOf<T>()); } private: + mutable QDotNetFunction<QString> fnAssemblyQualifiedName; mutable QDotNetFunction<QString> fnFullName; mutable QString strFullName; + mutable QDotNetFunction<bool, QDotNetRef> fnIsAssignableFrom; + mutable QDotNetFunction<bool, QDotNetRef> fnIsAssignableTo; }; namespace QtDotNet |