mirror of
https://github.com/qmlnet/qmlnet.git
synced 2026-05-21 06:45:32 -06:00
parent
182232703b
commit
820da15d20
23 changed files with 720 additions and 33 deletions
6
samples/hosting/.gitignore
vendored
Normal file
6
samples/hosting/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
build-*/
|
||||
*.pro.user
|
||||
net-output/
|
||||
net/.idea/
|
||||
net/obj/
|
||||
net/.vs/
|
||||
13
samples/hosting/native/NativeHost.pro
Normal file
13
samples/hosting/native/NativeHost.pro
Normal 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)
|
||||
18
samples/hosting/native/Page1Form.ui.qml
Normal file
18
samples/hosting/native/Page1Form.ui.qml
Normal 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
|
||||
}
|
||||
}
|
||||
18
samples/hosting/native/Page2Form.ui.qml
Normal file
18
samples/hosting/native/Page2Form.ui.qml
Normal 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
|
||||
}
|
||||
}
|
||||
40
samples/hosting/native/main.cpp
Normal file
40
samples/hosting/native/main.cpp
Normal 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);
|
||||
}
|
||||
49
samples/hosting/native/main.qml
Normal file
49
samples/hosting/native/main.qml
Normal 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
9
samples/hosting/native/qml.qrc
Normal file
9
samples/hosting/native/qml.qrc
Normal 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>
|
||||
6
samples/hosting/native/qtquickcontrols2.conf
Normal file
6
samples/hosting/native/qtquickcontrols2.conf
Normal 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
|
||||
13
samples/hosting/net/NetHost.csproj
Normal file
13
samples/hosting/net/NetHost.csproj
Normal 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>
|
||||
48
samples/hosting/net/NetHost.sln
Normal file
48
samples/hosting/net/NetHost.sln
Normal 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
|
||||
25
samples/hosting/net/Program.cs
Normal file
25
samples/hosting/net/Program.cs
Normal 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();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
12
src/native/QmlNet/Hosting.pri
Normal file
12
src/native/QmlNet/Hosting.pri
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
INCLUDEPATH += $$PWD
|
||||
|
||||
HEADERS += $$PWD/Hosting/coreclrhost.h \
|
||||
$$PWD/Hosting/CoreHost.h
|
||||
|
||||
SOURCES += \
|
||||
$$PWD/Hosting/CoreHost.cpp
|
||||
|
||||
unix {
|
||||
LIBS += -ldl
|
||||
}
|
||||
|
||||
200
src/native/QmlNet/Hosting/CoreHost.cpp
Normal file
200
src/native/QmlNet/Hosting/CoreHost.cpp
Normal 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;
|
||||
}
|
||||
44
src/native/QmlNet/Hosting/CoreHost.h
Normal file
44
src/native/QmlNet/Hosting/CoreHost.h
Normal 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
|
||||
80
src/native/QmlNet/Hosting/coreclrhost.h
Normal file
80
src/native/QmlNet/Hosting/coreclrhost.h
Normal 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
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
41
src/net/Qml.Net/Host.cs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue