|
|
|
@ -16,6 +16,17 @@ limitations under the License.
|
|
|
|
|
#include "GloSC.h"
|
|
|
|
|
#include <memory>
|
|
|
|
|
#include "UpdateChecker.h"
|
|
|
|
|
#include <collection.h>
|
|
|
|
|
#include <appmodel.h>
|
|
|
|
|
#include <shlwapi.h>
|
|
|
|
|
#include <strsafe.h>
|
|
|
|
|
#include <windows.h>
|
|
|
|
|
#include <appxpackaging.h>
|
|
|
|
|
#pragma comment(lib, "Shlwapi.lib")
|
|
|
|
|
|
|
|
|
|
using namespace Windows::Management::Deployment;
|
|
|
|
|
using namespace Windows::Foundation::Collections;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
GloSC::GloSC(QWidget *parent)
|
|
|
|
|
: QMainWindow(parent), updater_(this)
|
|
|
|
@ -508,112 +519,183 @@ void GloSC::on_pbSearchPath_clicked()
|
|
|
|
|
|
|
|
|
|
void GloSC::on_pbUWP_clicked()
|
|
|
|
|
{
|
|
|
|
|
auto settings = std::make_unique<QSettings>(R"(HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Launch\PackageId)", QSettings::NativeFormat);
|
|
|
|
|
|
|
|
|
|
QStringList childs = settings->childGroups();
|
|
|
|
|
QStringList packages;
|
|
|
|
|
PackageManager^ packageManager = ref new PackageManager();
|
|
|
|
|
IIterable<Windows::ApplicationModel::Package^>^ packages = packageManager->FindPackages();
|
|
|
|
|
|
|
|
|
|
for (auto& child : childs)
|
|
|
|
|
int packageCount = 0;
|
|
|
|
|
// Only way to get the count of packages is to iterate through the whole collection first
|
|
|
|
|
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
|
|
|
|
|
[&](Windows::ApplicationModel::Package^ package)
|
|
|
|
|
{
|
|
|
|
|
packages << child;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
packageCount += 1;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
QProgressDialog progDialog("Searching for UWP apps...", "Cancel", 0, packages.size(), this);
|
|
|
|
|
QProgressDialog progDialog("Searching for UWP apps...", "Cancel", 0, packageCount, this);
|
|
|
|
|
progDialog.setWindowModality(Qt::WindowModal);
|
|
|
|
|
// Without this the progress dialog doesn't update in the lambda function.
|
|
|
|
|
progDialog.show();
|
|
|
|
|
QApplication::processEvents();
|
|
|
|
|
|
|
|
|
|
QList<UWPPair> pairs;
|
|
|
|
|
|
|
|
|
|
QStringList AppNames;
|
|
|
|
|
QStringList AppUMIds;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (auto &package : packages)
|
|
|
|
|
int currPackage = 0;
|
|
|
|
|
// Iterate through all the packages
|
|
|
|
|
std::for_each(Windows::Foundation::Collections::begin(packages), Windows::Foundation::Collections::end(packages),
|
|
|
|
|
[&](Windows::ApplicationModel::Package^ package)
|
|
|
|
|
{
|
|
|
|
|
progDialog.setValue(packages.indexOf(package));
|
|
|
|
|
progDialog.setValue(currPackage);
|
|
|
|
|
progDialog.update();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (progDialog.wasCanceled())
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
settings = std::make_unique<QSettings>(R"(HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Launch\PackageId\)" + package, QSettings::NativeFormat);
|
|
|
|
|
|
|
|
|
|
HRESULT hr = S_OK;
|
|
|
|
|
IStream* inputStream = NULL;
|
|
|
|
|
UINT32 pathLen = 0;
|
|
|
|
|
IAppxManifestReader* manifestReader = NULL;
|
|
|
|
|
IAppxFactory* appxFactory = NULL;
|
|
|
|
|
LPWSTR appId = NULL;
|
|
|
|
|
LPWSTR manifestAppName = NULL;
|
|
|
|
|
|
|
|
|
|
// Get the package path on disk so we can load the manifest XML and get the PRAID
|
|
|
|
|
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, NULL);
|
|
|
|
|
|
|
|
|
|
if (pathLen > 0) {
|
|
|
|
|
|
|
|
|
|
// Length of the path + "\\AppxManifest.xml" that we'll be appending
|
|
|
|
|
UINT32 manifestLen = pathLen + 20;
|
|
|
|
|
PWSTR pathBuf = (PWSTR)malloc(manifestLen * sizeof(wchar_t));
|
|
|
|
|
|
|
|
|
|
GetPackagePathByFullName(package->Id->FullName->Data(), &pathLen, pathBuf);
|
|
|
|
|
PWSTR manifest_xml = L"\\AppxManifest.xml";
|
|
|
|
|
|
|
|
|
|
for (auto& child : settings->childGroups())
|
|
|
|
|
{
|
|
|
|
|
if (child == "ActivatableClassId")
|
|
|
|
|
{
|
|
|
|
|
const auto classIDSettings = std::make_unique<QSettings>(
|
|
|
|
|
R"(HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Launch\PackageId\)" + package + "\\" + child,
|
|
|
|
|
QSettings::NativeFormat);
|
|
|
|
|
hr = StringCchCatW(pathBuf, manifestLen, manifest_xml);
|
|
|
|
|
|
|
|
|
|
if (classIDSettings->childGroups().length() > 0)
|
|
|
|
|
{
|
|
|
|
|
QString pkgNameCleaned = package.mid(0, package.indexOf("_"));
|
|
|
|
|
QStringList tmp = package.split("__");
|
|
|
|
|
if (tmp.size() > 1)
|
|
|
|
|
{
|
|
|
|
|
pkgNameCleaned += "_" + tmp.at(1);
|
|
|
|
|
} else {
|
|
|
|
|
pkgNameCleaned += package.mid(package.lastIndexOf("_"), package.size()-1);
|
|
|
|
|
// Let's ignore a bunch of built in apps and such
|
|
|
|
|
if (wcsstr(pathBuf, L"SystemApps")) {
|
|
|
|
|
hr = E_FAIL;
|
|
|
|
|
}
|
|
|
|
|
else if (wcsstr(pathBuf, L".NET.Native."))
|
|
|
|
|
hr = E_FAIL;
|
|
|
|
|
else if (wcsstr(pathBuf, L".VCLibs."))
|
|
|
|
|
hr = E_FAIL;
|
|
|
|
|
else if (wcsstr(pathBuf, L"Microsoft.UI"))
|
|
|
|
|
hr = E_FAIL;
|
|
|
|
|
else if (wcsstr(pathBuf, L"Microsoft.Advertising"))
|
|
|
|
|
hr = E_FAIL;
|
|
|
|
|
else if (wcsstr(pathBuf, L"Microsoft.Services.Store"))
|
|
|
|
|
hr = E_FAIL;
|
|
|
|
|
|
|
|
|
|
BOOL hasCurrent = FALSE;
|
|
|
|
|
|
|
|
|
|
// Open the manifest XML
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
|
|
|
|
|
QString AppUMId = pkgNameCleaned + "!" + classIDSettings->childGroups().at(0);
|
|
|
|
|
|
|
|
|
|
const auto appInfoSettings = std::make_unique<QSettings>(
|
|
|
|
|
R"(HKEY_CLASSES_ROOT\Extensions\ContractId\Windows.Launch\PackageId\)"
|
|
|
|
|
+ package + "\\" + child + "\\" + classIDSettings->childGroups().at(0),
|
|
|
|
|
QSettings::NativeFormat);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QString AppName = appInfoSettings->value("DisplayName").toString();
|
|
|
|
|
|
|
|
|
|
if (!AppNames.contains(AppName) && !AppUMIds.contains(AppUMId) && AppUMId.size() > 0)
|
|
|
|
|
{
|
|
|
|
|
if (AppName.size() != 0)
|
|
|
|
|
AppNames << AppName;
|
|
|
|
|
hr = SHCreateStreamOnFileEx(
|
|
|
|
|
pathBuf,
|
|
|
|
|
STGM_READ | STGM_SHARE_EXCLUSIVE,
|
|
|
|
|
0, // default file attributes
|
|
|
|
|
FALSE, // do not create new file
|
|
|
|
|
NULL, // no template
|
|
|
|
|
&inputStream);
|
|
|
|
|
}
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
|
|
|
|
|
AppUMIds << AppUMId;
|
|
|
|
|
hr = CoCreateInstance(
|
|
|
|
|
__uuidof(AppxFactory),
|
|
|
|
|
NULL,
|
|
|
|
|
CLSCTX_INPROC_SERVER,
|
|
|
|
|
__uuidof(IAppxFactory),
|
|
|
|
|
(LPVOID*)(&appxFactory));
|
|
|
|
|
}
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
hr = appxFactory->CreateManifestReader(inputStream, &manifestReader);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (AppName.size() == 0)
|
|
|
|
|
{
|
|
|
|
|
AppName = "Unknown";
|
|
|
|
|
// Grab application ID (PRAID) and DisplayName from the XML
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
IAppxManifestApplicationsEnumerator* applications = NULL;
|
|
|
|
|
manifestReader->GetApplications(&applications);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
hr = applications->GetHasCurrent(&hasCurrent);
|
|
|
|
|
if (hasCurrent) {
|
|
|
|
|
IAppxManifestApplication* application = NULL;
|
|
|
|
|
hr = applications->GetCurrent(&application);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
application->GetStringValue(L"Id", &appId);
|
|
|
|
|
application->GetStringValue(L"DisplayName", &manifestAppName);
|
|
|
|
|
application->Release();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
hr = S_FALSE;
|
|
|
|
|
}
|
|
|
|
|
else if (AppName.at(0) == '@') {
|
|
|
|
|
QString packageName = AppName.mid(AppName.indexOf('{') + 1, AppName.size() - 1);
|
|
|
|
|
packageName = packageName.mid(0, packageName.indexOf('?'));
|
|
|
|
|
QSettings settings("HKEY_CLASSES_ROOT\\Local Settings\\MrtCache", QSettings::NativeFormat);
|
|
|
|
|
applications->Release();
|
|
|
|
|
}
|
|
|
|
|
manifestReader->Release();
|
|
|
|
|
inputStream->Release();
|
|
|
|
|
|
|
|
|
|
QStringList cachedNameChildGroups = settings.childGroups();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (auto &childGroup : cachedNameChildGroups)
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
PWSTR appNameBuf;
|
|
|
|
|
QString AppUMId = QString::fromWCharArray(package->Id->FamilyName->Data());
|
|
|
|
|
QString AppName;
|
|
|
|
|
if (manifestAppName != NULL)
|
|
|
|
|
{
|
|
|
|
|
|
|
|
|
|
if (childGroup.contains(packageName))
|
|
|
|
|
// If the display name is an indirect string, we'll try and load it using SHLoadIndirectString
|
|
|
|
|
if (wcsstr(manifestAppName, L"ms-resource:"))
|
|
|
|
|
{
|
|
|
|
|
QSettings settings(R"(HKEY_CLASSES_ROOT\Local Settings\MrtCache\)" + childGroup, QSettings::NativeFormat);
|
|
|
|
|
PWSTR res_name = wcsdup(&manifestAppName[12]);
|
|
|
|
|
appNameBuf = (PWSTR)malloc(1026);
|
|
|
|
|
LPCWSTR resource_str = L"@{";
|
|
|
|
|
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/resources/" + res_name + L"}";
|
|
|
|
|
PCWSTR res_str = reslookup.c_str();
|
|
|
|
|
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
|
|
|
|
|
// Try several resource paths
|
|
|
|
|
if (!SUCCEEDED(hr)) {
|
|
|
|
|
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/Resources/" + res_name + L"}";
|
|
|
|
|
PCWSTR res_str = reslookup.c_str();
|
|
|
|
|
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
|
|
|
|
|
// If the third one doesn't work, we give up and use the package name from PackageManager
|
|
|
|
|
if (!SUCCEEDED(hr)) {
|
|
|
|
|
std::wstring reslookup = std::wstring(resource_str) + package->Id->FullName->Data() + L"?ms-resource://" + package->Id->Name->Data() + L"/" + res_name + L"}";
|
|
|
|
|
PCWSTR res_str = reslookup.c_str();
|
|
|
|
|
hr = SHLoadIndirectString(res_str, appNameBuf, 512, NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
QStringList allKeys = settings.allKeys();
|
|
|
|
|
|
|
|
|
|
AppName.replace("/", "\\");
|
|
|
|
|
for (auto &key : allKeys)
|
|
|
|
|
{
|
|
|
|
|
if (key.contains(AppName))
|
|
|
|
|
{
|
|
|
|
|
AppName = settings.value(key).toString();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (!SUCCEEDED(hr))
|
|
|
|
|
AppName = QString::fromWCharArray(package->Id->Name->Data());
|
|
|
|
|
else
|
|
|
|
|
AppName = QString::fromWCharArray(appNameBuf);
|
|
|
|
|
free(appNameBuf);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
appNameBuf = manifestAppName;
|
|
|
|
|
AppName = QString::fromWCharArray(appNameBuf);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
if (AppName.at(0) == '@') {
|
|
|
|
|
AppName = "Unknown";
|
|
|
|
|
else {
|
|
|
|
|
AppName = QString::fromWCharArray(package->Id->Name->Data());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QString PRAID = QString::fromWCharArray(appId);
|
|
|
|
|
CoTaskMemFree(appId);
|
|
|
|
|
if (!PRAID.isEmpty()) {
|
|
|
|
|
AppUMId = AppUMId.append("!");
|
|
|
|
|
AppUMId = AppUMId.append(PRAID);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const UWPPair uwpPair = {
|
|
|
|
@ -621,27 +703,24 @@ void GloSC::on_pbUWP_clicked()
|
|
|
|
|
AppUMId,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
pairs.push_back(uwpPair);
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
free(pathBuf);
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
pairs.push_back(uwpPair);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
currPackage += 1;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
uwp_pairs_ = pairs;
|
|
|
|
|
|
|
|
|
|
progDialog.close();
|
|
|
|
|
|
|
|
|
|
UWPSelectDialog dialog(this);
|
|
|
|
|
dialog.setUWPList(uwp_pairs_);
|
|
|
|
|
int selection = dialog.exec();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (selection > -1)
|
|
|
|
|
{
|
|
|
|
|
ui_.lePath->setText(uwp_pairs_.at(selection).AppUMId);
|
|
|
|
|