summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSergio Ahumada <sahumada@blackberry.com>2014-06-14 18:21:04 +0200
committerSergio Ahumada <sahumada@blackberry.com>2014-06-14 18:21:04 +0200
commit7017fe8165c84ba6e9388810d3cc946c9df04dc0 (patch)
treeb68e293c833444f627b4ad76b06da4ae465bfdac
parent788638bab869ddd94b9d1967aee9be9c04817ec5 (diff)
parentaa35d132010f4410d72e30d03e3dd713c7a2241d (diff)
Merge remote-tracking branch 'origin/stable' into 5.3v5.3.1
-rw-r--r--src/winrtrunner/appxengine.cpp100
-rw-r--r--src/winrtrunner/appxengine.h1
-rw-r--r--src/winrtrunner/appxengine_p.h16
-rw-r--r--src/winrtrunner/appxphoneengine.cpp544
-rw-r--r--src/winrtrunner/appxphoneengine.h88
-rw-r--r--src/winrtrunner/runner.cpp22
-rw-r--r--src/winrtrunner/winrtrunner.pro9
7 files changed, 774 insertions, 6 deletions
diff --git a/src/winrtrunner/appxengine.cpp b/src/winrtrunner/appxengine.cpp
index b79668bdb..a0b5ea653 100644
--- a/src/winrtrunner/appxengine.cpp
+++ b/src/winrtrunner/appxengine.cpp
@@ -135,8 +135,10 @@ AppxEngine::AppxEngine(Runner *runner, AppxEnginePrivate *dd)
: d_ptr(dd)
{
Q_D(AppxEngine);
+ if (d->hasFatalError)
+ return;
+
d->runner = runner;
- d->hasFatalError = false;
d->processHandle = NULL;
d->pid = -1;
d->exitCode = UINT_MAX;
@@ -147,9 +149,7 @@ AppxEngine::AppxEngine(Runner *runner, AppxEnginePrivate *dd)
return;
}
- HRESULT hr = CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);
- CHECK_RESULT_FATAL("Failed to initialize COM.", return);
-
+ HRESULT hr;
hr = RoGetActivationFactory(HString::MakeReference(RuntimeClass_Windows_Foundation_Uri).Get(),
IID_PPV_ARGS(&d->uriFactory));
CHECK_RESULT_FATAL("Failed to instantiate URI factory.", return);
@@ -343,3 +343,95 @@ bool AppxEngine::installDependencies()
return true;
}
+
+bool AppxEngine::createPackage(const QString &packageFileName)
+{
+ Q_D(AppxEngine);
+
+ static QHash<QString, QString> contentTypes;
+ if (contentTypes.isEmpty()) {
+ contentTypes.insert(QStringLiteral("dll"), QStringLiteral("application/x-msdownload"));
+ contentTypes.insert(QStringLiteral("exe"), QStringLiteral("application/x-msdownload"));
+ contentTypes.insert(QStringLiteral("png"), QStringLiteral("image/png"));
+ contentTypes.insert(QStringLiteral("xml"), QStringLiteral("vnd.ms-appx.manifest+xml"));
+ }
+
+ // Check for package map, or create one if needed
+ QDir base = QFileInfo(d->manifest).absoluteDir();
+ QFile packageFile(packageFileName);
+
+ QHash<QString, QString> files;
+ QFile mappingFile(base.absoluteFilePath(QStringLiteral("AppxManifest.map")));
+ if (mappingFile.exists()) {
+ qCWarning(lcWinRtRunner) << "Creating package from mapping file:" << mappingFile.fileName();
+ if (!mappingFile.open(QFile::ReadOnly)) {
+ qCWarning(lcWinRtRunner) << "Unable to read mapping file:" << mappingFile.errorString();
+ return false;
+ }
+
+ QRegExp pattern(QStringLiteral("^\"([^\"]*)\"\\s*\"([^\"]*)\"$"));
+ bool inFileSection = false;
+ while (!mappingFile.atEnd()) {
+ const QString line = QString::fromUtf8(mappingFile.readLine()).trimmed();
+ if (line.startsWith(QLatin1Char('['))) {
+ inFileSection = line == QStringLiteral("[Files]");
+ continue;
+ }
+ if (pattern.cap(2).compare(QStringLiteral("AppxManifest.xml"), Qt::CaseInsensitive) == 0)
+ continue;
+ if (inFileSection && pattern.indexIn(line) >= 0 && pattern.captureCount() == 2) {
+ QString inputFile = pattern.cap(1);
+ if (!QFile::exists(inputFile))
+ inputFile = base.absoluteFilePath(inputFile);
+ files.insert(QDir::toNativeSeparators(inputFile), QDir::toNativeSeparators(pattern.cap(2)));
+ }
+ }
+ } else {
+ qCWarning(lcWinRtRunner) << "No mapping file exists. Only recognized files will be packaged.";
+ // Add executable
+ files.insert(QDir::toNativeSeparators(d->executable), QFileInfo(d->executable).fileName());
+ // Add potential Qt files
+ const QStringList fileTypes = QStringList()
+ << QStringLiteral("*.dll") << QStringLiteral("*.png") << QStringLiteral("*.qm")
+ << QStringLiteral("*.qml") << QStringLiteral("*.qmldir");
+ QDirIterator dirIterator(base.absolutePath(), fileTypes, QDir::Files, QDirIterator::Subdirectories);
+ while (dirIterator.hasNext()) {
+ const QString filePath = dirIterator.next();
+ files.insert(QDir::toNativeSeparators(filePath), QDir::toNativeSeparators(base.relativeFilePath(filePath)));
+ }
+ }
+
+ ComPtr<IStream> outputStream;
+ HRESULT hr = SHCreateStreamOnFile(wchar(packageFile.fileName()), STGM_WRITE|STGM_CREATE, &outputStream);
+ RETURN_FALSE_IF_FAILED("Failed to create package file output stream");
+
+ ComPtr<IUri> hashMethod;
+ hr = CreateUri(L"http://www.w3.org/2001/04/xmlenc#sha512", Uri_CREATE_CANONICALIZE, 0, &hashMethod);
+ RETURN_FALSE_IF_FAILED("Failed to create the has method URI");
+
+ APPX_PACKAGE_SETTINGS packageSettings = { FALSE, hashMethod.Get() };
+ ComPtr<IAppxPackageWriter> packageWriter;
+ hr = d->packageFactory->CreatePackageWriter(outputStream.Get(), &packageSettings, &packageWriter);
+ RETURN_FALSE_IF_FAILED("Failed to create package writer");
+
+ for (QHash<QString, QString>::const_iterator i = files.begin(); i != files.end(); ++i) {
+ qCDebug(lcWinRtRunner) << "Packaging" << i.key() << i.value();
+ ComPtr<IStream> inputStream;
+ hr = SHCreateStreamOnFile(wchar(i.key()), STGM_READ, &inputStream);
+ RETURN_FALSE_IF_FAILED("Failed to open file");
+ const QString contentType = contentTypes.value(QFileInfo(i.key()).suffix().toLower(),
+ QStringLiteral("application/octet-stream"));
+ hr = packageWriter->AddPayloadFile(wchar(i.value()), wchar(contentType),
+ APPX_COMPRESSION_OPTION_NORMAL, inputStream.Get());
+ RETURN_FALSE_IF_FAILED("Failed to add payload file");
+ }
+
+ // Write out the manifest
+ ComPtr<IStream> manifestStream;
+ hr = SHCreateStreamOnFile(wchar(d->manifest), STGM_READ, &manifestStream);
+ RETURN_FALSE_IF_FAILED("Failed to open manifest for packaging");
+ hr = packageWriter->Close(manifestStream.Get());
+ RETURN_FALSE_IF_FAILED("Failed to finalize package.");
+
+ return true;
+}
diff --git a/src/winrtrunner/appxengine.h b/src/winrtrunner/appxengine.h
index 0390eeaec..2e1b74e1b 100644
--- a/src/winrtrunner/appxengine.h
+++ b/src/winrtrunner/appxengine.h
@@ -67,6 +67,7 @@ protected:
virtual bool installPackage(IAppxManifestReader *reader, const QString &filePath) = 0;
bool installDependencies();
+ bool createPackage(const QString &packageFileName);
static bool getManifestFile(const QString &fileName, QString *manifest = 0);
QScopedPointer<AppxEnginePrivate> d_ptr;
diff --git a/src/winrtrunner/appxengine_p.h b/src/winrtrunner/appxengine_p.h
index dde97b73a..c2e4ad524 100644
--- a/src/winrtrunner/appxengine_p.h
+++ b/src/winrtrunner/appxengine_p.h
@@ -56,7 +56,21 @@ struct IAppxFactory;
class AppxEnginePrivate
{
public:
- virtual ~AppxEnginePrivate() { }
+ AppxEnginePrivate()
+ {
+ HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
+ if (FAILED(hr)) {
+ qCWarning(lcWinRtRunner) << "Failed to initialize COM:" << qt_error_string(hr);
+ hasFatalError = true;
+ }
+ hasFatalError = false;
+ }
+
+ virtual ~AppxEnginePrivate()
+ {
+ CoUninitialize();
+ }
+
Runner *runner;
bool hasFatalError;
diff --git a/src/winrtrunner/appxphoneengine.cpp b/src/winrtrunner/appxphoneengine.cpp
new file mode 100644
index 000000000..b1d246aec
--- /dev/null
+++ b/src/winrtrunner/appxphoneengine.cpp
@@ -0,0 +1,544 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "appxphoneengine.h"
+#include "appxengine_p.h"
+
+#include <QtCore/QDir>
+#include <QtCore/QDirIterator>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QHash>
+#include <QtCore/QUuid>
+#include <QtCore/QLoggingCategory>
+#include <QtCore/QDateTime>
+
+#include <comdef.h>
+#include <psapi.h>
+
+#include <ShlObj.h>
+#include <Shlwapi.h>
+#include <wsdevlicensing.h>
+#include <AppxPackaging.h>
+#include <xmllite.h>
+#include <wrl.h>
+#include <windows.applicationmodel.h>
+#include <windows.management.deployment.h>
+
+using namespace Microsoft::WRL;
+using namespace Microsoft::WRL::Wrappers;
+using namespace ABI::Windows::Foundation;
+using namespace ABI::Windows::Management::Deployment;
+using namespace ABI::Windows::ApplicationModel;
+using namespace ABI::Windows::System;
+
+// From Microsoft.Phone.Tools.Deploy assembly
+namespace PhoneTools {
+ enum DeploymentOptions
+ {
+ None = 0,
+ PA = 1,
+ Debug = 2,
+ Infused = 4,
+ Lightup = 8,
+ Enterprise = 16,
+ Sideload = 32,
+ TypeMask = 255,
+ UninstallDisabled = 256,
+ SkipUpdateAppInForeground = 512,
+ DeleteXap = 1024,
+ InstallOnSD = 65536,
+ OptOutSD = 131072
+ };
+ enum PackageType
+ {
+ UnknownAppx = 0,
+ Main = 1,
+ Framework = 2,
+ Resource = 4,
+ Bundle = 8,
+ Xap = 0
+ };
+}
+
+QT_USE_NAMESPACE
+
+#include <corecon.h>
+#include <ccapi_12.h>
+Q_GLOBAL_STATIC_WITH_ARGS(CoreConServer, coreConServer, (12))
+
+#undef RETURN_IF_FAILED
+#define RETURN_IF_FAILED(msg, ret) \
+ if (FAILED(hr)) { \
+ qCWarning(lcWinRtRunner).nospace() << msg << ": 0x" << QByteArray::number(hr, 16).constData() \
+ << ' ' << coreConServer->formatError(hr); \
+ ret; \
+ }
+
+// Set a break handler for gracefully breaking long-running ops
+static bool g_ctrlReceived = false;
+static bool g_handleCtrl = false;
+static BOOL WINAPI ctrlHandler(DWORD type)
+{
+ switch (type) {
+ case CTRL_C_EVENT:
+ case CTRL_CLOSE_EVENT:
+ case CTRL_LOGOFF_EVENT:
+ g_ctrlReceived = g_handleCtrl;
+ return g_handleCtrl;
+ case CTRL_BREAK_EVENT:
+ case CTRL_SHUTDOWN_EVENT:
+ default:
+ break;
+ }
+ return false;
+}
+
+class AppxPhoneEnginePrivate : public AppxEnginePrivate
+{
+public:
+ QString productId;
+
+ ComPtr<ICcConnection> connection;
+ CoreConDevice *device;
+ QSet<QString> dependencies;
+};
+
+static ProcessorArchitecture toProcessorArchitecture(APPX_PACKAGE_ARCHITECTURE appxArch)
+{
+ switch (appxArch) {
+ case APPX_PACKAGE_ARCHITECTURE_X86:
+ return ProcessorArchitecture_X86;
+ case APPX_PACKAGE_ARCHITECTURE_ARM:
+ return ProcessorArchitecture_Arm;
+ case APPX_PACKAGE_ARCHITECTURE_X64:
+ return ProcessorArchitecture_X64;
+ case APPX_PACKAGE_ARCHITECTURE_NEUTRAL:
+ // fall-through intended
+ default:
+ return ProcessorArchitecture_Neutral;
+ }
+}
+
+static bool getPhoneProductId(IStream *manifestStream, QString *productId)
+{
+ // Read out the phone product ID (not supported by AppxManifestReader)
+ ComPtr<IXmlReader> xmlReader;
+ HRESULT hr = CreateXmlReader(IID_PPV_ARGS(&xmlReader), NULL);
+ RETURN_FALSE_IF_FAILED("Failed to create XML reader");
+
+ hr = xmlReader->SetInput(manifestStream);
+ RETURN_FALSE_IF_FAILED("Failed to set manifest as input");
+
+ while (!xmlReader->IsEOF()) {
+ XmlNodeType nodeType;
+ hr = xmlReader->Read(&nodeType);
+ RETURN_FALSE_IF_FAILED("Failed to read next node in manifest");
+ if (nodeType == XmlNodeType_Element) {
+ PCWSTR uri;
+ hr = xmlReader->GetNamespaceUri(&uri, NULL);
+ RETURN_FALSE_IF_FAILED("Failed to read namespace URI of current node");
+ if (wcscmp(uri, L"http://schemas.microsoft.com/appx/2014/phone/manifest") == 0) {
+ PCWSTR localName;
+ hr = xmlReader->GetLocalName(&localName, NULL);
+ RETURN_FALSE_IF_FAILED("Failed to get local name of current node");
+ if (wcscmp(localName, L"PhoneIdentity") == 0) {
+ hr = xmlReader->MoveToAttributeByName(L"PhoneProductId", NULL);
+ if (hr == S_FALSE)
+ continue;
+ RETURN_FALSE_IF_FAILED("Failed to seek to the PhoneProductId attribute");
+ PCWSTR phoneProductId;
+ UINT length;
+ hr = xmlReader->GetValue(&phoneProductId, &length);
+ RETURN_FALSE_IF_FAILED("Failed to read the value of the PhoneProductId attribute");
+ *productId = QLatin1Char('{') + QString::fromWCharArray(phoneProductId, length) + QLatin1Char('}');
+ return true;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+bool AppxPhoneEngine::canHandle(Runner *runner)
+{
+ return getManifestFile(runner->app());
+}
+
+RunnerEngine *AppxPhoneEngine::create(Runner *runner)
+{
+ QScopedPointer<AppxPhoneEngine> engine(new AppxPhoneEngine(runner));
+ if (engine->d_ptr->hasFatalError)
+ return 0;
+
+ return engine.take();
+}
+
+QStringList AppxPhoneEngine::deviceNames()
+{
+ QStringList deviceNames;
+ foreach (const CoreConDevice *device, coreConServer->devices())
+ deviceNames.append(device->name());
+ return deviceNames;
+}
+
+AppxPhoneEngine::AppxPhoneEngine(Runner *runner)
+ : AppxEngine(runner, new AppxPhoneEnginePrivate)
+{
+ Q_D(AppxPhoneEngine);
+ if (d->hasFatalError)
+ return;
+ d->hasFatalError = true;
+
+ ComPtr<IStream> manifestStream;
+ HRESULT hr = SHCreateStreamOnFile(wchar(d->manifest), STGM_READ, &manifestStream);
+ RETURN_VOID_IF_FAILED("Failed to open manifest stream");
+
+ if (!getPhoneProductId(manifestStream.Get(), &d->productId)) {
+ qCWarning(lcWinRtRunner) << "Failed to read phone product ID from the manifest.";
+ return;
+ }
+
+ if (!coreConServer->initialize()) {
+ while (!coreConServer.exists())
+ Sleep(1);
+ }
+
+ // Get the device
+ d->device = coreConServer->devices().value(d->runner->deviceIndex());
+ if (!d->device || !d->device->handle()) {
+ d->hasFatalError = true;
+ qCWarning(lcWinRtRunner) << "Invalid device specified:" << d->runner->deviceIndex();
+ return;
+ }
+
+
+
+ // Set a break handler for gracefully exiting from long-running operations
+ SetConsoleCtrlHandler(&ctrlHandler, true);
+ d->hasFatalError = false;
+}
+
+AppxPhoneEngine::~AppxPhoneEngine()
+{
+}
+
+QString AppxPhoneEngine::extensionSdkPath() const
+{
+ HKEY regKey;
+ LONG hr = RegOpenKeyEx(
+ HKEY_LOCAL_MACHINE,
+ L"SOFTWARE\\Wow6432Node\\Microsoft\\Microsoft SDKs\\WindowsPhoneApp\\v8.1",
+ 0, KEY_READ, &regKey);
+ if (hr != ERROR_SUCCESS) {
+ qCWarning(lcWinRtRunner) << "Failed to open registry key:" << qt_error_string(hr);
+ return QString();
+ }
+
+ wchar_t pathData[MAX_PATH];
+ DWORD pathLength = MAX_PATH;
+ hr = RegGetValue(regKey, L"Install Path", L"Install Path", RRF_RT_REG_SZ, NULL, pathData, &pathLength);
+ if (hr != ERROR_SUCCESS) {
+ qCWarning(lcWinRtRunner) << "Failed to get installation path value:" << qt_error_string(hr);
+ return QString();
+ }
+
+ return QString::fromWCharArray(pathData, (pathLength - 1) / sizeof(wchar_t))
+ + QLatin1String("ExtensionSDKs");
+}
+
+bool AppxPhoneEngine::installPackage(IAppxManifestReader *reader, const QString &filePath)
+{
+ Q_D(AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__ << filePath;
+
+ ComPtr<ICcConnection3> connection;
+ HRESULT hr = d->connection.As(&connection);
+ RETURN_FALSE_IF_FAILED("Failed to obtain connection object");
+
+ ComPtr<IStream> manifestStream;
+ hr = reader->GetStream(&manifestStream);
+ RETURN_FALSE_IF_FAILED("Failed to get manifest stream from reader");
+
+ QString productIdString;
+ if (!getPhoneProductId(manifestStream.Get(), &productIdString)) {
+ qCWarning(lcWinRtRunner) << "Failed to get phone product ID from manifest reader.";
+ return false;
+ }
+ _bstr_t productId(wchar(productIdString));
+
+ VARIANT_BOOL isInstalled;
+ hr = connection->IsApplicationInstalled(productId, &isInstalled);
+ RETURN_FALSE_IF_FAILED("Failed to determine if package is installed");
+ if (isInstalled) {
+ qCDebug(lcWinRtRunner) << "Package" << productIdString << "is already installed";
+ return true;
+ }
+
+ ComPtr<IAppxManifestProperties> properties;
+ hr = reader->GetProperties(&properties);
+ RETURN_FALSE_IF_FAILED("Failed to get manifest properties");
+
+ BOOL isFramework;
+ hr = properties->GetBoolValue(L"Framework", &isFramework);
+ RETURN_FALSE_IF_FAILED("Failed to determine whether package is a framework");
+
+ const QString deploymentFlags = QString::number(isFramework ? PhoneTools::None : PhoneTools::Sideload);
+ _bstr_t deploymentFlagsAsGenre(wchar(deploymentFlags));
+ const QString packageType = QString::number(isFramework ? PhoneTools::Framework : PhoneTools::Main);
+ _bstr_t packageTypeAsIconPath(wchar(packageType));
+ _bstr_t packagePath(wchar(QDir::toNativeSeparators(filePath)));
+ hr = connection->InstallApplication(productId, productId, deploymentFlagsAsGenre,
+ packageTypeAsIconPath, packagePath);
+ RETURN_FALSE_IF_FAILED("Failed to install the package");
+
+ return true;
+}
+
+bool AppxPhoneEngine::connect()
+{
+ Q_D(AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ HRESULT hr;
+ if (!d->connection) {
+ _bstr_t connectionName;
+ hr = static_cast<ICcServer *>(coreConServer->handle())->GetConnection(
+ static_cast<ICcDevice *>(d->device->handle()), 5000, NULL, connectionName.GetAddress(), &d->connection);
+ RETURN_FALSE_IF_FAILED("Failed to connect to device");
+ }
+
+ VARIANT_BOOL connected;
+ hr = d->connection->IsConnected(&connected);
+ RETURN_FALSE_IF_FAILED("Failed to determine connection state");
+ if (connected)
+ return true;
+
+ hr = d->connection->ConnectDevice();
+ RETURN_FALSE_IF_FAILED("Failed to connect to device");
+
+ return true;
+}
+
+bool AppxPhoneEngine::install(bool removeFirst)
+{
+ Q_D(AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ if (!connect())
+ return false;
+
+ ComPtr<ICcConnection3> connection;
+ HRESULT hr = d->connection.As(&connection);
+ RETURN_FALSE_IF_FAILED("Failed to obtain connection object");
+
+ _bstr_t productId(wchar(d->productId));
+ VARIANT_BOOL isInstalled;
+ hr = connection->IsApplicationInstalled(productId, &isInstalled);
+ RETURN_FALSE_IF_FAILED("Failed to obtain the installation status");
+ if (isInstalled) {
+ if (!removeFirst)
+ return true;
+ if (!remove())
+ return false;
+ }
+
+ if (!installDependencies())
+ return false;
+
+ const QDir base = QFileInfo(d->manifest).absoluteDir();
+ const QString packageFileName = base.absoluteFilePath(d->packageFamilyName + QStringLiteral(".appx"));
+ if (!createPackage(packageFileName))
+ return false;
+
+ ComPtr<IStream> manifestStream;
+ hr = SHCreateStreamOnFile(wchar(d->manifest), STGM_READ, &manifestStream);
+ RETURN_FALSE_IF_FAILED("Failed to open manifest stream");
+
+ ComPtr<IAppxManifestReader> manifestReader;
+ hr = d->packageFactory->CreateManifestReader(manifestStream.Get(), &manifestReader);
+ RETURN_FALSE_IF_FAILED("Failed to create manifest reader for installation");
+
+ return installPackage(manifestReader.Get(), packageFileName);
+}
+
+bool AppxPhoneEngine::remove()
+{
+ Q_D(AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ if (!connect())
+ return false;
+
+ if (!d->connection)
+ return false;
+
+ ComPtr<ICcConnection3> connection;
+ HRESULT hr = d->connection.As(&connection);
+ RETURN_FALSE_IF_FAILED("Failed to obtain connection object");
+
+ _bstr_t app = wchar(d->productId);
+ hr = connection->UninstallApplication(app);
+ RETURN_FALSE_IF_FAILED("Failed to uninstall the package");
+
+ return true;
+}
+
+bool AppxPhoneEngine::start()
+{
+ Q_D(AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ if (!connect())
+ return false;
+
+ if (!d->runner->arguments().isEmpty())
+ qCWarning(lcWinRtRunner) << "Arguments are not currently supported for Windows Phone Appx packages.";
+
+ ComPtr<ICcConnection3> connection;
+ HRESULT hr = d->connection.As(&connection);
+ RETURN_FALSE_IF_FAILED("Failed to cast connection object");
+
+ _bstr_t productId(wchar(d->productId));
+ DWORD pid;
+ hr = connection->LaunchApplication(productId, &pid);
+ RETURN_FALSE_IF_FAILED("Failed to start the package");
+
+ d->pid = pid;
+ return true;
+}
+
+bool AppxPhoneEngine::enableDebugging(const QString &debuggerExecutable, const QString &debuggerArguments)
+{
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+ Q_UNUSED(debuggerExecutable);
+ Q_UNUSED(debuggerArguments);
+ return false;
+}
+
+bool AppxPhoneEngine::disableDebugging()
+{
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+ return false;
+}
+
+bool AppxPhoneEngine::suspend()
+{
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+ return false;
+}
+
+bool AppxPhoneEngine::waitForFinished(int secs)
+{
+ Q_D(AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ ComPtr<ICcConnection3> connection;
+ HRESULT hr = d->connection.As(&connection);
+ RETURN_FALSE_IF_FAILED("Failed to cast connection");
+
+ g_handleCtrl = true;
+ int time = 0;
+ forever {
+ ++time;
+ if ((secs && time > secs) || g_ctrlReceived) {
+ g_handleCtrl = false;
+ return false;
+ }
+
+ Sleep(1000); // Wait one second between checks
+ qCDebug(lcWinRtRunner) << "Waiting for app to quit - msecs to go: " << secs - time;
+ }
+ g_handleCtrl = false;
+ return true;
+}
+
+bool AppxPhoneEngine::stop()
+{
+ Q_D(AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ if (!connect())
+ return false;
+
+ ComPtr<ICcConnection3> connection;
+ HRESULT hr = d->connection.As(&connection);
+ RETURN_FALSE_IF_FAILED("Failed to cast connection object");
+
+ _bstr_t productId(wchar(d->productId));
+ hr = connection->TerminateRunningApplicationInstances(productId);
+ RETURN_FALSE_IF_FAILED("Failed to stop the package");
+
+ return true;
+}
+
+QString AppxPhoneEngine::devicePath(const QString &relativePath) const
+{
+ Q_D(const AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ return QStringLiteral("%FOLDERID_APPID_ISOROOT%\\") + d->productId
+ + QStringLiteral("\\%LOCL%\\") + relativePath;
+}
+
+bool AppxPhoneEngine::sendFile(const QString &localFile, const QString &deviceFile)
+{
+ Q_D(const AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ HRESULT hr = d->connection->SendFile(_bstr_t(wchar(localFile)), _bstr_t(wchar(deviceFile)),
+ CREATE_ALWAYS, NULL);
+ RETURN_FALSE_IF_FAILED("Failed to send the file");
+
+ return true;
+}
+
+bool AppxPhoneEngine::receiveFile(const QString &deviceFile, const QString &localFile)
+{
+ Q_D(const AppxPhoneEngine);
+ qCDebug(lcWinRtRunner) << __FUNCTION__;
+
+ HRESULT hr = d->connection->ReceiveFile(_bstr_t(wchar(deviceFile)),
+ _bstr_t(wchar(localFile)), uint(2));
+ RETURN_FALSE_IF_FAILED("Failed to receive the file");
+
+ return true;
+}
diff --git a/src/winrtrunner/appxphoneengine.h b/src/winrtrunner/appxphoneengine.h
new file mode 100644
index 000000000..9f4933355
--- /dev/null
+++ b/src/winrtrunner/appxphoneengine.h
@@ -0,0 +1,88 @@
+/****************************************************************************
+**
+** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the tools applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef APPXPHONEENGINE_H
+#define APPXPHONEENGINE_H
+
+#include "appxengine.h"
+#include "runnerengine.h"
+#include "runner.h"
+
+#include <QtCore/QScopedPointer>
+
+QT_USE_NAMESPACE
+
+class AppxPhoneEnginePrivate;
+class AppxPhoneEngine : public AppxEngine
+{
+public:
+ static bool canHandle(Runner *runner);
+ static RunnerEngine *create(Runner *runner);
+ static QStringList deviceNames();
+
+ bool install(bool removeFirst = false) Q_DECL_OVERRIDE;
+ bool remove() Q_DECL_OVERRIDE;
+ bool start() Q_DECL_OVERRIDE;
+ bool enableDebugging(const QString &debuggerExecutable,
+ const QString &debuggerArguments) Q_DECL_OVERRIDE;
+ bool disableDebugging() Q_DECL_OVERRIDE;
+ bool suspend() Q_DECL_OVERRIDE;
+ bool waitForFinished(int secs) Q_DECL_OVERRIDE;
+ bool stop() Q_DECL_OVERRIDE;
+
+ QString devicePath(const QString &relativePath) const Q_DECL_OVERRIDE;
+ bool sendFile(const QString &localFile, const QString &deviceFile) Q_DECL_OVERRIDE;
+ bool receiveFile(const QString &deviceFile, const QString &localFile) Q_DECL_OVERRIDE;
+
+private:
+ explicit AppxPhoneEngine(Runner *runner);
+ ~AppxPhoneEngine();
+
+ QString extensionSdkPath() const;
+ bool installPackage(IAppxManifestReader *reader, const QString &filePath) Q_DECL_OVERRIDE;
+
+ bool connect();
+
+ friend struct QScopedPointerDeleter<AppxPhoneEngine>;
+ Q_DECLARE_PRIVATE(AppxPhoneEngine)
+};
+
+#endif // APPXPHONEENGINE_H
diff --git a/src/winrtrunner/runner.cpp b/src/winrtrunner/runner.cpp
index 5b4725991..fa31c30d8 100644
--- a/src/winrtrunner/runner.cpp
+++ b/src/winrtrunner/runner.cpp
@@ -43,6 +43,9 @@
#include "runnerengine.h"
+#ifndef RTRUNNER_NO_APPXPHONE
+#include "appxphoneengine.h"
+#endif
#ifndef RTRUNNER_NO_APPXLOCAL
#include "appxlocalengine.h"
#endif
@@ -80,6 +83,9 @@ QMap<QString, QStringList> Runner::deviceNames()
#ifndef RTRUNNER_NO_APPXLOCAL
deviceNames.insert(QStringLiteral("Appx"), AppxLocalEngine::deviceNames());
#endif
+#ifndef RTRUNNER_NO_APPXPHONE
+ deviceNames.insert(QStringLiteral("Phone"), AppxPhoneEngine::deviceNames());
+#endif
#ifndef RTRUNNER_NO_XAP
deviceNames.insert(QStringLiteral("Xap"), XapEngine::deviceNames());
#endif
@@ -98,6 +104,22 @@ Runner::Runner(const QString &app, const QStringList &arguments,
bool deviceIndexKnown;
d->deviceIndex = deviceName.toInt(&deviceIndexKnown);
+#ifndef RTRUNNER_NO_APPXPHONE
+ if (!deviceIndexKnown) {
+ d->deviceIndex = AppxPhoneEngine::deviceNames().indexOf(deviceName);
+ if (d->deviceIndex < 0)
+ d->deviceIndex = 0;
+ }
+ if ((d->profile.isEmpty() || d->profile.toLower() == QStringLiteral("appxphone"))
+ && AppxPhoneEngine::canHandle(this)) {
+ if (RunnerEngine *engine = AppxPhoneEngine::create(this)) {
+ d->engine.reset(engine);
+ d->isValid = true;
+ qCWarning(lcWinRtRunner) << "Using the AppxPhone profile.";
+ return;
+ }
+ }
+#endif
#ifndef RTRUNNER_NO_APPXLOCAL
if (!deviceIndexKnown) {
d->deviceIndex = AppxLocalEngine::deviceNames().indexOf(deviceName);
diff --git a/src/winrtrunner/winrtrunner.pro b/src/winrtrunner/winrtrunner.pro
index eb2fdfcb6..e1b59846a 100644
--- a/src/winrtrunner/winrtrunner.pro
+++ b/src/winrtrunner/winrtrunner.pro
@@ -6,7 +6,7 @@ DEFINES += QT_NO_CAST_FROM_ASCII QT_NO_CAST_TO_ASCII WINRT_LIBRARY
SOURCES += main.cpp runner.cpp
HEADERS += runner.h runnerengine.h
-DEFINES += RTRUNNER_NO_APPXLOCAL RTRUNNER_NO_XAP
+DEFINES += RTRUNNER_NO_APPXLOCAL RTRUNNER_NO_APPXPHONE RTRUNNER_NO_XAP
win32-msvc2012|win32-msvc2013 {
SOURCES += appxengine.cpp appxlocalengine.cpp
@@ -19,6 +19,13 @@ win32-msvc2012|win32-msvc2013 {
HEADERS += xapengine.h
DEFINES -= RTRUNNER_NO_XAP
+ win32-msvc2013 {
+ LIBS += -lurlmon -lxmllite
+ SOURCES += appxphoneengine.cpp
+ HEADERS += appxphoneengine.h
+ DEFINES -= RTRUNNER_NO_APPXPHONE
+ }
+
# Use zip class from qtbase
SOURCES += \
qzip/qzip.cpp