Added support for c++ to main entry point.

Closes #13
This commit is contained in:
Paul Knopf 2018-09-11 14:29:56 -04:00
parent 182232703b
commit 820da15d20
23 changed files with 720 additions and 33 deletions

6
samples/hosting/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
build-*/
*.pro.user
net-output/
net/.idea/
net/obj/
net/.vs/

View file

@ -0,0 +1,13 @@
QT += quick
CONFIG += c++11
DEFINES += QT_DEPRECATED_WARNINGS
SOURCES += \
main.cpp
RESOURCES += qml.qrc
DEFINES += "NET_ROOT=\"\\\"$$PWD/../net-output\\\"\""
include (../../../src/native/QmlNet/Hosting.pri)

View file

@ -0,0 +1,18 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
Page {
width: 600
height: 400
header: Label {
text: qsTr("Page 1")
font.pixelSize: Qt.application.font.pixelSize * 2
padding: 10
}
Label {
text: qsTr("You are on Page 1.")
anchors.centerIn: parent
}
}

View file

@ -0,0 +1,18 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
Page {
width: 600
height: 400
header: Label {
text: qsTr("Page 2")
font.pixelSize: Qt.application.font.pixelSize * 2
padding: 10
}
Label {
text: qsTr("You are on Page 2.")
anchors.centerIn: parent
}
}

View file

@ -0,0 +1,40 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <Hosting/CoreHost.h>
#include <QDebug>
#include <QFileInfo>
#include <QDir>
static int runCallback(QGuiApplication* app, QQmlApplicationEngine* engine)
{
engine->load(QUrl(QStringLiteral("qrc:/main.qml")));
if (engine->rootObjects().isEmpty())
return -1;
return app->exec();
}
int main(int argc, char *argv[])
{
QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
QString netDll = NET_ROOT;
netDll.append(QDir::separator());
netDll.append("NetHost.dll");
CoreHost::RunContext runContext;
runContext.hostFxrContext = CoreHost::findHostFxr();
runContext.managedExe = netDll;
// NOTE: You may set entry point to the current executable if
// the .NET runtime is deployed side-by-side.
runContext.entryPoint = runContext.hostFxrContext.dotnetRoot;
runContext.entryPoint.append(CORECLR_DOTNET_EXE_NAME);
return CoreHost::run(app,
engine,
runCallback,
runContext);
}

View file

@ -0,0 +1,49 @@
import QtQuick 2.9
import QtQuick.Controls 2.2
import test 1.0
ApplicationWindow {
visible: true
width: 640
height: 480
title: qsTr("Tabs")
SwipeView {
id: swipeView
anchors.fill: parent
currentIndex: tabBar.currentIndex
Page1Form {
}
Page2Form {
}
}
footer: TabBar {
id: tabBar
currentIndex: swipeView.currentIndex
TabButton {
text: qsTr("Page 1")
}
TabButton {
text: qsTr("Page 2")
}
}
Timer {
interval: 500; running: true; repeat: true
onTriggered: {
test.testMethod()
}
}
TestObject {
id: test
Component.onCompleted: {
// Call .NET
test.testMethod()
}
}
}

View file

@ -0,0 +1,9 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>Page1Form.ui.qml</file>
<file>Page2Form.ui.qml</file>
<file>main.qml</file>
<file>qtquickcontrols2.conf</file>
</qresource>
</RCC>

View file

@ -0,0 +1,6 @@
; This file can be edited to change the style of the application
; Read "Qt Quick Controls 2 Configuration File" for details:
; http://doc.qt.io/qt-5/qtquickcontrols2-configuration.html
[Controls]
Style=Default

View file

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<OutDir>$(MSBuildProjectDirectory)/../net-output</OutDir>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\src\net\Qml.Net\Qml.Net.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,48 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 15
VisualStudioVersion = 15.0.26124.0
MinimumVisualStudioVersion = 15.0.26124.0
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "NetHost", "NetHost.csproj", "{A787B77B-F87B-44A1-AD3D-38E21A92A84A}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Qml.Net", "..\..\..\src\net\Qml.Net\Qml.Net.csproj", "{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Debug|x64.ActiveCfg = Debug|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Debug|x64.Build.0 = Debug|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Debug|x86.ActiveCfg = Debug|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Debug|x86.Build.0 = Debug|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Release|Any CPU.Build.0 = Release|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Release|x64.ActiveCfg = Release|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Release|x64.Build.0 = Release|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Release|x86.ActiveCfg = Release|Any CPU
{A787B77B-F87B-44A1-AD3D-38E21A92A84A}.Release|x86.Build.0 = Release|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Debug|x64.ActiveCfg = Debug|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Debug|x64.Build.0 = Debug|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Debug|x86.ActiveCfg = Debug|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Debug|x86.Build.0 = Debug|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Release|Any CPU.Build.0 = Release|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Release|x64.ActiveCfg = Release|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Release|x64.Build.0 = Release|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Release|x86.ActiveCfg = Release|Any CPU
{7258C1D8-8508-4ED9-8F89-5AE2EAEBED9D}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
EndGlobal

View file

@ -0,0 +1,25 @@
using System;
using Qml.Net;
namespace NetHost
{
class Program
{
public class TestObject
{
public void TestMethod()
{
Console.WriteLine("test method");
}
}
static int Main(string[] _)
{
return Host.Run(_, (args, app, engine, runCallback) =>
{
QQmlApplicationEngine.RegisterType<TestObject>("test");
return runCallback();
});
}
}
}

View file

@ -0,0 +1,12 @@
INCLUDEPATH += $$PWD
HEADERS += $$PWD/Hosting/coreclrhost.h \
$$PWD/Hosting/CoreHost.h
SOURCES += \
$$PWD/Hosting/CoreHost.cpp
unix {
LIBS += -ldl
}

View file

@ -0,0 +1,200 @@
#include <Hosting/CoreHost.h>
#include <Hosting/coreclrhost.h>
#include <QFileInfo>
#include <QDir>
#include <QDirIterator>
#include <QVersionNumber>
#ifdef _WIN32
#include <Windows.h>
#else
#include <dlfcn.h>
#endif
#ifdef __APPLE__
#define HOSTFXR_DLL_NAME "libhostfxr.dylib"
#elif _WIN32
#define HOSTFXR_DLL_NAME "hostfxr.dll"
#else
#define HOSTFXR_DLL_NAME "libhostfxr.so"
#endif
QList<QString> CoreHost::getPotientialDotnetRoots()
{
QList<QString> result;
#ifdef _WIN32
result.push_back("C:\\Program Files\\dotnet");
#else
result.push_back("/usr/local/share/dotnet");
result.push_back("/usr/share/dotnet");
result.push_back("/opt/dotnet");
#endif
QByteArray dotnetRoot = qgetenv("DOTNET_ROOT");
if(!dotnetRoot.isEmpty()) {
// We are overriding the roots, forcing ourselves to look in a particular spot.
result.clear();
result.push_back(dotnetRoot);
}
return result;
}
CoreHost::HostFxrContext CoreHost::findHostFxr()
{
HostFxrContext result;
result.success = false;
QList<QString> roots = getPotientialDotnetRoots();
for(QString root : roots) {
qDebug("looking for %s in root %s", HOSTFXR_DLL_NAME, qPrintable(root));
if(!root.endsWith(QDir::separator())) {
root.append(QDir::separator());
}
QFileInfo rootInfo(root);
if(!rootInfo.exists()) {
qDebug("%s doesn't exist", qPrintable(rootInfo.path()));
continue;
}
QString host = root;
host.append("host");
host.append(QDir::separator());
QFileInfo hostInfo(host);
if(!hostInfo.exists()) {
qDebug("%s doesn't exist", qPrintable(host));
continue;
}
QString fxr = host;
fxr.append("fxr");
fxr.append(QDir::separator());
QFileInfo fxrInfo(fxr);
if(!fxrInfo.exists()) {
qDebug("%s doesn't exist.", qPrintable(fxr));
continue;
}
QString currentFxrLib;
QVersionNumber currentFxrLibVersion;
QDir fxrDir = fxrInfo.dir();
fxrDir.setFilter(QDir::Dirs | QDir::NoDot | QDir::NoDotDot);
QDirIterator it(fxrDir, QDirIterator::Subdirectories);
while(it.hasNext()) {
QString fxrVersion = it.next();
QFileInfo fxrVersionInfo(fxrVersion);
fxrVersion.append(QDir::separator());
fxrVersion.append(HOSTFXR_DLL_NAME);
QFileInfo fxrLibInfo(fxrVersion);
if(!fxrLibInfo.exists()) {
qDebug("%s doesn't exist", qPrintable(fxrLibInfo.absoluteFilePath()));
continue;
}
QVersionNumber version = QVersionNumber::fromString(fxrVersionInfo.fileName());
if(currentFxrLibVersion.isNull() || version > currentFxrLibVersion) {
qDebug("found potentional file %s with version %s", qPrintable(fxrLibInfo.absoluteFilePath()), qPrintable((version.toString())));
currentFxrLibVersion = version;
currentFxrLib = fxrLibInfo.absoluteFilePath();
} else {
qDebug("ignore file %s with version %s", qPrintable(fxrLibInfo.absoluteFilePath()), qPrintable((version.toString())));
}
}
if(!currentFxrLib.isEmpty()) {
qDebug("returning hostfx lib: %s", qPrintable(currentFxrLib));
result.success = true;
result.hostFxrLib = currentFxrLib;
result.dotnetRoot = root;
return result;
}
}
return result;
}
int CoreHost::run(QGuiApplication& app, QQmlApplicationEngine& engine, runCallback runCallback, RunContext runContext)
{
QList<QString> execArgs;
execArgs.push_back(runContext.entryPoint);
execArgs.push_back("exec");
execArgs.push_back(runContext.managedExe);
QString appPtr;
appPtr.sprintf("%llu", (quintptr)&app);
QString enginePtr;
enginePtr.sprintf("%llu", (quintptr)&engine);
QString callbackPtr;
callbackPtr.sprintf("%llu", (quintptr)runCallback);
execArgs.push_back(appPtr);
execArgs.push_back(enginePtr);
execArgs.push_back(callbackPtr);
for (QString arg : runContext.args) {
execArgs.push_back(arg);
}
std::vector<const CORECLR_CHAR_TYPE*> hostFxrArgs;
#ifdef _WIN32
for (QString arg : execArgs) {
hostFxrArgs.push_back(arg.utf16());
}
#else
QList<QByteArray> execArgs8bit;
for (QString arg : execArgs) {
execArgs8bit.push_back(arg.toLocal8Bit());
}
for (QByteArray arg : execArgs8bit) {
hostFxrArgs.push_back(arg);
}
#endif
hostfxr_main_ptr hostfxr_main = nullptr;
#ifdef _WIN32
HMODULE dll = LoadLibraryA(qPrintable(runContext.hostFxrContext.hostFxrLib));
if(dll == nullptr) {
qCritical("Couldn't load lib at %s", qPrintable(runContext.hostFxrContext.hostFxrLib));
return LoadHostFxrResult::Failed;
}
hostfxr_main = reinterpret_cast<hostfxr_main_ptr>(GetProcAddress(dll, "hostfxr_main"));
#else
void* dll = dlopen(qPrintable(runContext.hostFxrContext.hostFxrLib), RTLD_NOW | RTLD_LOCAL);
if(dll == nullptr) {
qCritical("Couldn't load lib at %s", qPrintable(runContext.hostFxrContext.hostFxrLib));
return LoadHostFxrResult::Failed;
}
hostfxr_main = reinterpret_cast<hostfxr_main_ptr>(dlsym(dll, "hostfxr_main"));
#endif
if(hostfxr_main == nullptr) {
qCritical("Couldn't load 'hostfxr_main' from %s", qPrintable(runContext.hostFxrContext.hostFxrLib));
return -1;
}
int result = hostfxr_main(static_cast<int>(hostFxrArgs.size()), &hostFxrArgs[0]);
#ifdef _WIN32
FreeLibrary(dll);
#else
dlclose(dll);
#endif
return result;
}

View file

@ -0,0 +1,44 @@
#ifndef COREHOST_H
#define COREHOST_H
#include <QSharedPointer>
#include <Hosting/coreclrhost.h>
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#ifdef _WIN32
#define CORECLR_DOTNET_EXE_NAME "dotnet.exe"
#else
#define CORECLR_DOTNET_EXE_NAME "dotnet"
#endif
class CoreHost : public QObject
{
Q_OBJECT
public:
struct HostFxrContext {
bool success;
QString dotnetRoot;
QString hostFxrLib;
};
struct RunContext {
HostFxrContext hostFxrContext;
QString entryPoint;
QString managedExe;
QList<QString> args;
};
enum LoadHostFxrResult
{
Loaded,
AlreadyLoaded,
Failed
};
typedef int (*runCallback)(QGuiApplication* app, QQmlApplicationEngine* engine);
static QList<QString> getPotientialDotnetRoots();
static HostFxrContext findHostFxr();
static int run(QGuiApplication& app, QQmlApplicationEngine& engine, runCallback runCallback, RunContext runContext);
};
#endif

View file

@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//
// APIs for hosting CoreCLR
//
#ifndef CORECLR_HOST_H
#define CORECLR_HOST_H
#ifdef _WIN32
typedef unsigned short CORECLR_CHAR_TYPE;
#else
typedef char CORECLR_CHAR_TYPE;
#endif
// For each hosting API, we define a function prototype and a function pointer
// The prototype is useful for implicit linking against the dynamic coreclr
// library and the pointer for explicit dynamic loading (dlopen, LoadLibrary)
#define CORECLR_HOSTING_API(function, ...) \
extern "C" int function(__VA_ARGS__); \
typedef int (*function##_ptr)(__VA_ARGS__)
CORECLR_HOSTING_API(coreclr_initialize,
const char* exePath,
const char* appDomainFriendlyName,
int propertyCount,
const char** propertyKeys,
const char** propertyValues,
void** hostHandle,
unsigned int* domainId);
CORECLR_HOSTING_API(coreclr_shutdown,
void* hostHandle,
unsigned int domainId);
CORECLR_HOSTING_API(coreclr_shutdown_2,
void* hostHandle,
unsigned int domainId,
int* latchedExitCode);
CORECLR_HOSTING_API(coreclr_create_delegate,
void* hostHandle,
unsigned int domainId,
const char* entryPointAssemblyName,
const char* entryPointTypeName,
const char* entryPointMethodName,
void** delegate);
CORECLR_HOSTING_API(coreclr_execute_assembly,
void* hostHandle,
unsigned int domainId,
int argc,
const char** argv,
const char* managedAssemblyPath,
unsigned int* exitCode);
// hostfxr
CORECLR_HOSTING_API(hostfxr_get_native_search_directories,
const int argc,
const char* argv[],
char buffer[],
int buffer_size,
int* required_buffer_size);
CORECLR_HOSTING_API(hostfxr_main_startupinfo,
const int argc,
const char* argv[],
const char* host_path,
const char* dotnet_root,
const char* app_path);
CORECLR_HOSTING_API(hostfxr_main,
const int argc,
const CORECLR_CHAR_TYPE* argv[]);
#undef CORECLR_HOSTING_API
#endif // CORECLR_HOST_H

View file

@ -5,7 +5,7 @@
extern "C" {
Q_DECL_EXPORT void net_test_helper_runQml(QQmlApplicationEngineContainer* qmlEngineContainer, LPWSTR qml) {
QQmlComponent component(qmlEngineContainer->qmlEngine.data());
QQmlComponent component(qmlEngineContainer->qmlEngine);
QString qmlString = QString::fromUtf16((const char16_t*)qml);
component.setData(qmlString.toUtf8(), QUrl());
QObject *object = component.create();

View file

@ -14,27 +14,32 @@ void GuiThreadContextTriggerCallback::trigger() {
extern "C" {
Q_DECL_EXPORT QGuiApplicationContainer* qguiapplication_create(NetVariantListContainer* argsContainer) {
Q_DECL_EXPORT QGuiApplicationContainer* qguiapplication_create(NetVariantListContainer* argsContainer, QGuiApplication* existingApp) {
QGuiApplicationContainer* result = new QGuiApplicationContainer();
// Build our args
if(argsContainer != nullptr) {
QSharedPointer<NetVariantList> args = argsContainer->list;
for(int x = 0; x < args->count(); x++) {
QByteArray arg = args->get(x)->getString().toLatin1();
result->args.append(arg);
char* cstr = nullptr;
cstr = new char [arg.size()+1];
strcpy(cstr, arg.data());
result->argsPointer.push_back(cstr);
}
result->argCount = result->args.size();
if (existingApp != nullptr) {
result->ownsGuiApp = false;
result->guiApp = existingApp;
} else {
result->argCount = 0;
result->ownsGuiApp = true;
// Build our args
if(argsContainer != nullptr) {
QSharedPointer<NetVariantList> args = argsContainer->list;
for(int x = 0; x < args->count(); x++) {
QByteArray arg = args->get(x)->getString().toLatin1();
result->args.append(arg);
char* cstr = nullptr;
cstr = new char [arg.size()+1];
strcpy(cstr, arg.data());
result->argsPointer.push_back(cstr);
}
result->argCount = result->args.size();
} else {
result->argCount = 0;
}
result->guiApp = new QGuiApplication(result->argCount, &result->argsPointer[0], 0);
}
result->guiApp = QSharedPointer<QGuiApplication>(new QGuiApplication(result->argCount, &result->argsPointer[0], 0));
result->callback = QSharedPointer<GuiThreadContextTriggerCallback>(new GuiThreadContextTriggerCallback());
return result;
@ -44,6 +49,10 @@ Q_DECL_EXPORT void qguiapplication_destroy(QGuiApplicationContainer* container)
for (auto i : container->argsPointer) {
delete i;
}
container->callback.clear();
if(container->ownsGuiApp) {
delete container->guiApp;
}
delete container;
}
@ -63,4 +72,8 @@ Q_DECL_EXPORT void qguiapplication_exit(QGuiApplicationContainer* container, int
container->guiApp->exit(returnCode);
}
Q_DECL_EXPORT QGuiApplication* qguiapplication_internalPointer(QGuiApplicationContainer* container) {
return container->guiApp;
}
}

View file

@ -20,7 +20,8 @@ struct QGuiApplicationContainer {
int argCount;
QList<QString> args;
std::vector<char*> argsPointer;
QSharedPointer<QGuiApplication> guiApp;
QGuiApplication* guiApp;
bool ownsGuiApp;
QSharedPointer<GuiThreadContextTriggerCallback> callback;
};

View file

@ -12,19 +12,34 @@ static int netValueTypeNumber = 0;
extern "C" {
Q_DECL_EXPORT QQmlApplicationEngineContainer* qqmlapplicationengine_create() {
QSharedPointer<QQmlApplicationEngine> qmlEngine = QSharedPointer<QQmlApplicationEngine>(new QQmlApplicationEngine());
Q_DECL_EXPORT QQmlApplicationEngineContainer* qqmlapplicationengine_create(QQmlApplicationEngine* existingEngine) {
bool ownsEngine = true;
QQmlApplicationEngine* engine = nullptr;
QV4::ExecutionEngine* v4Engine = QQmlEnginePrivate::getV4Engine(qmlEngine.data());
if (existingEngine != nullptr) {
engine = existingEngine;
ownsEngine = false;
} else {
engine = new QQmlApplicationEngine();
ownsEngine = true;
}
QV4::ExecutionEngine* v4Engine = QQmlEnginePrivate::getV4Engine(engine);
QV4::Scope scope(v4Engine);
QV4::ScopedObject net(scope, v4Engine->memoryManager->allocObject<QV4::NetObject>());
v4Engine->globalObject->defineDefaultProperty("Net", net);;
return new QQmlApplicationEngineContainer{qmlEngine};
return new QQmlApplicationEngineContainer{
engine,
ownsEngine
};
}
Q_DECL_EXPORT void qqmlapplicationengine_destroy(QQmlApplicationEngineContainer* container) {
if(container->ownsEngine) {
delete container->qmlEngine;
}
delete container;
}
@ -179,4 +194,8 @@ Q_DECL_EXPORT void qqmlapplicationengine_addImportPath(QQmlApplicationEngineCont
container->qmlEngine->addImportPath(pathString);
}
Q_DECL_EXPORT QQmlApplicationEngine* qqmlapplicationengine_internalPointer(QQmlApplicationEngineContainer* container) {
return container->qmlEngine;
}
}

View file

@ -3,10 +3,10 @@
#include <QmlNet.h>
#include <QQmlApplicationEngine>
#include <QSharedPointer>
struct QQmlApplicationEngineContainer {
QSharedPointer<QQmlApplicationEngine> qmlEngine;
QQmlApplicationEngine* qmlEngine;
bool ownsEngine;
};
#endif // NET_QQMLAPPLICATIONENGINE_H

41
src/net/Qml.Net/Host.cs Normal file
View file

@ -0,0 +1,41 @@
using System;
using System.Linq;
using System.Runtime.InteropServices;
namespace Qml.Net
{
public static class Host
{
[UnmanagedFunctionPointer(CallingConvention.Cdecl)]
public delegate int NativeRunCallbackDelegate(IntPtr app, IntPtr engine);
public delegate int NetRunCallbackDelegate();
public static int Run(string[] args, Func<string[], QGuiApplication, QQmlApplicationEngine, NetRunCallbackDelegate, int> action)
{
if (args.Length < 3)
{
throw new Exception("Args is invalid, must contain three entries which are pointers to native types.");
}
var appPtr = new IntPtr((long)ulong.Parse(args[0]));
var enginePtr = new IntPtr((long)ulong.Parse(args[1]));
var callbackPtr = new IntPtr((long)ulong.Parse(args[2]));
using (var app = new QGuiApplication(appPtr))
{
using (var engine = new QQmlApplicationEngine(enginePtr))
{
var runCallback = new NetRunCallbackDelegate(() =>
{
var callback = Marshal.GetDelegateForFunctionPointer<NativeRunCallbackDelegate>(callbackPtr);
// ReSharper disable AccessToDisposedClosure
return callback.Invoke(app.InternalPointer, engine.InternalPointer);
// ReSharper restore AccessToDisposedClosure
});
return action(args.Skip(2).ToArray(), app, engine, runCallback);
}
}
}
}
}

View file

@ -33,6 +33,18 @@ namespace Qml.Net
SynchronizationContext.SetSynchronizationContext(new QtSynchronizationContext(this));
}
internal QGuiApplication(IntPtr existingApp)
:base(CreateFromExisting(existingApp))
{
TriggerDelegate triggerDelegate = Trigger;
_triggerHandle = GCHandle.Alloc(triggerDelegate);
Interop.QGuiApplication.AddTriggerCallback(Handle, Marshal.GetFunctionPointerForDelegate(triggerDelegate));
_oldSynchronizationContext = SynchronizationContext.Current;
SynchronizationContext.SetSynchronizationContext(new QtSynchronizationContext(this));
}
public int Exec()
{
return Interop.QGuiApplication.Exec(Handle);
@ -62,6 +74,8 @@ namespace Qml.Net
Interop.QGuiApplication.RequestTrigger(Handle);
}
internal IntPtr InternalPointer => Interop.QGuiApplication.InternalPointer(Handle);
private void Trigger()
{
Action action;
@ -79,6 +93,11 @@ namespace Qml.Net
_triggerHandle.Free();
}
private static IntPtr CreateFromExisting(IntPtr app)
{
return Interop.QGuiApplication.Create(IntPtr.Zero, app);
}
private static IntPtr Create(List<string> args)
{
if (args == null)
@ -101,7 +120,7 @@ namespace Qml.Net
strings.Add(variant);
}
}
return Interop.QGuiApplication.Create(strings.Handle);
return Interop.QGuiApplication.Create(strings.Handle, IntPtr.Zero);
}
}
@ -127,7 +146,7 @@ namespace Qml.Net
internal interface IQGuiApplicationInterop
{
[NativeSymbol(Entrypoint = "qguiapplication_create")]
IntPtr Create(IntPtr args);
IntPtr Create(IntPtr args, IntPtr existingApp);
[NativeSymbol(Entrypoint = "qguiapplication_destroy")]
void Destroy(IntPtr app);
@ -139,5 +158,7 @@ namespace Qml.Net
void RequestTrigger(IntPtr app);
[NativeSymbol(Entrypoint = "qguiapplication_exit")]
void Exit(IntPtr app, int returnCode);
[NativeSymbol(Entrypoint = "qguiapplication_internalPointer")]
IntPtr InternalPointer(IntPtr app);
}
}

View file

@ -10,7 +10,13 @@ namespace Qml.Net
public sealed class QQmlApplicationEngine : BaseDisposable
{
public QQmlApplicationEngine()
:base(Interop.QQmlApplicationEngine.Create())
:base(Interop.QQmlApplicationEngine.Create(IntPtr.Zero))
{
}
internal QQmlApplicationEngine(IntPtr existingEngine)
:base(Interop.QQmlApplicationEngine.Create(existingEngine))
{
}
@ -24,6 +30,13 @@ namespace Qml.Net
{
Interop.QQmlApplicationEngine.LoadData(Handle, data);
}
public void AddImportPath(string path)
{
Interop.QQmlApplicationEngine.AddImportPath(Handle, path);
}
internal IntPtr InternalPointer => Interop.QQmlApplicationEngine.InternalPointer(Handle);
public static int RegisterType<T>(string uri, int versionMajor = 1, int versionMinor = 0)
{
@ -49,11 +62,6 @@ namespace Qml.Net
InteropBehaviors.RegisterQmlInteropBehavior(new MvvmQmlInteropBehavior(), false);
}
public void AddImportPath(string path)
{
Interop.QQmlApplicationEngine.AddImportPath(Handle, path);
}
protected override void DisposeUnmanaged(IntPtr ptr)
{
Interop.QQmlApplicationEngine.Destroy(ptr);
@ -63,7 +71,7 @@ namespace Qml.Net
internal interface IQQmlApplicationEngine
{
[NativeSymbol(Entrypoint = "qqmlapplicationengine_create")]
IntPtr Create();
IntPtr Create(IntPtr existingEngine);
[NativeSymbol(Entrypoint = "qqmlapplicationengine_destroy")]
void Destroy(IntPtr engine);
@ -78,5 +86,8 @@ namespace Qml.Net
[NativeSymbol(Entrypoint = "qqmlapplicationengine_addImportPath")]
void AddImportPath(IntPtr engine, [MarshalAs(UnmanagedType.LPWStr), CallerFree]string path);
[NativeSymbol(Entrypoint = "qqmlapplicationengine_internalPointer")]
IntPtr InternalPointer(IntPtr app);
}
}