Add filetype associations (#53)
* Merged PR 2956189: Add FTA Adds FTA handling and test case Fixes remove to remove devirtualized registry keys correctly and adds test cases for remove. Fix missed addref by converting raw pointers to ComPtrs in the msix code Fix makewin.cmd from stomping all over the PATH env variable, which allows it to be run multiple times in the same command prompt window. Related work items: #19821206 * update release binaries * update to point to build pipeline and release * update to point to release section
This commit is contained in:
Родитель
2f2918ffd1
Коммит
5494d5566b
|
@ -1,4 +1,5 @@
|
||||||
@echo off
|
@echo off
|
||||||
|
SETLOCAL
|
||||||
|
|
||||||
if "%~1" == "" goto USAGE
|
if "%~1" == "" goto USAGE
|
||||||
if /I "%~1" == "--help" goto USAGE
|
if /I "%~1" == "--help" goto USAGE
|
||||||
|
@ -7,12 +8,14 @@ if /I "%~1" == "/?" goto USAGE
|
||||||
|
|
||||||
:: Set up MSVC environment
|
:: Set up MSVC environment
|
||||||
:: Kudos to https://gist.github.com/AndrewPardoe/689a3b969670787d5dba538bb0a48a1e
|
:: Kudos to https://gist.github.com/AndrewPardoe/689a3b969670787d5dba538bb0a48a1e
|
||||||
|
|
||||||
pushd "%~dp0"
|
pushd "%~dp0"
|
||||||
pushd %ProgramFiles(x86)%\"Microsoft Visual Studio"\Installer
|
pushd %ProgramFiles(x86)%\"Microsoft Visual Studio"\Installer
|
||||||
for /f "usebackq tokens=*" %%i in (`vswhere -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do (
|
for /f "usebackq tokens=*" %%i in (`vswhere -latest -prerelease -products * -requires Microsoft.VisualStudio.Component.VC.Tools.x86.x64 -property installationPath`) do (
|
||||||
set VCINSTALLDIR=%%i\VC
|
set VCINSTALLDIR=%%i\VC
|
||||||
)
|
)
|
||||||
popd
|
popd
|
||||||
|
|
||||||
if exist %VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat (
|
if exist %VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat (
|
||||||
if /I "%~1" == "x86" (
|
if /I "%~1" == "x86" (
|
||||||
call "%VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat" %1
|
call "%VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat" %1
|
||||||
|
|
Двоичные данные
preview/Win7Msix/Binaries/Win7MSIXInstallerSetup.msi
Двоичные данные
preview/Win7Msix/Binaries/Win7MSIXInstallerSetup.msi
Двоичный файл не отображается.
|
@ -47,3 +47,7 @@ Uninstallation -
|
||||||
```
|
```
|
||||||
Win7MSIXInstaller.exe -RemovePackage notepadplus_0.0.0.1_x64__8wekyb3d8bbwe
|
Win7MSIXInstaller.exe -RemovePackage notepadplus_0.0.0.1_x64__8wekyb3d8bbwe
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Build Status
|
||||||
|
[![Build status](https://microsoft.visualstudio.com/xPlatAppx/_apis/build/status/CIGitHub-for-Win7MsixInstaller)](https://microsoft.visualstudio.com/xPlatAppx/_build/latest?definitionId=36972)
|
||||||
|
If succeeded, the MSI can be downloaded from the release tab.
|
Двоичные данные
preview/Win7Msix/Tests/VLC-3.0.6_1.0.0.0_x64__8wekyb3d8bbwe-missingsomeftas.msix
Normal file
Двоичные данные
preview/Win7Msix/Tests/VLC-3.0.6_1.0.0.0_x64__8wekyb3d8bbwe-missingsomeftas.msix
Normal file
Двоичный файл не отображается.
|
@ -55,6 +55,8 @@ function DoStop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Write-Host "Parsing trace logs..."
|
||||||
|
|
||||||
#convert the ETL file to XML data
|
#convert the ETL file to XML data
|
||||||
$now = [datetime]::Now.ToString("yyyy_MM_dd_HHmmss")
|
$now = [datetime]::Now.ToString("yyyy_MM_dd_HHmmss")
|
||||||
$tempXmlfile = join-path $env:temp ("MsixTrace_{0}.xml" -f $now)
|
$tempXmlfile = join-path $env:temp ("MsixTrace_{0}.xml" -f $now)
|
||||||
|
|
|
@ -45,6 +45,8 @@ function ShowTestHeader($testname)
|
||||||
$global:testcase++
|
$global:testcase++
|
||||||
}
|
}
|
||||||
|
|
||||||
|
New-PSDrive -PSProvider registry -Root HKEY_CLASSES_ROOT -Name HKCR -errorAction SilentlyContinue
|
||||||
|
|
||||||
|
|
||||||
ShowTestHeader("Untrusted Package fails")
|
ShowTestHeader("Untrusted Package fails")
|
||||||
certutil -delstore root 19a0a57c05c3a4884123cd9dccf820ea > $null
|
certutil -delstore root 19a0a57c05c3a4884123cd9dccf820ea > $null
|
||||||
|
@ -62,10 +64,10 @@ ShowTestHeader("Trusted Package succeeds")
|
||||||
certutil -addstore root APPX_TEST_ROOT.cer > $null
|
certutil -addstore root APPX_TEST_ROOT.cer > $null
|
||||||
$output = & $executable -AddPackage notepadplus.msix -quietUx
|
$output = & $executable -AddPackage notepadplus.msix -quietUx
|
||||||
$notepadDir = "C:\program files (x86)\notepad++"
|
$notepadDir = "C:\program files (x86)\notepad++"
|
||||||
|
$startLink = "c:\programdata\microsoft\windows\start menu\programs\notepad++.lnk"
|
||||||
if ($output -eq $null)
|
if ($output -eq $null)
|
||||||
{
|
{
|
||||||
$notepadExists = (test-path $notepadDir\notepad++.exe)
|
$notepadExists = (test-path $notepadDir\notepad++.exe)
|
||||||
$startLink = "c:\programdata\microsoft\windows\start menu\programs\notepad++.lnk"
|
|
||||||
$startlinkExists = (test-path $startlink)
|
$startlinkExists = (test-path $startlink)
|
||||||
if ($notepadExists -and $startlinkExists)
|
if ($notepadExists -and $startlinkExists)
|
||||||
{
|
{
|
||||||
|
@ -176,8 +178,76 @@ else
|
||||||
writeFail
|
writeFail
|
||||||
}
|
}
|
||||||
|
|
||||||
& .\msixtrace.ps1 -stop
|
ShowTestHeader("Install with FTA and registry entries succeeds")
|
||||||
# remove cases
|
$output = & $executable -AddPackage VLC-3.0.6_1.0.0.0_x64__8wekyb3d8bbwe-missingsomeftas.msix -quietUx
|
||||||
|
$vlcExePath = "C:\program files (x86)\VideoLAN\VLC\vlc.exe"
|
||||||
|
$3gaRegPath = "HKCR:\.3ga"
|
||||||
|
if ($output -eq $null)
|
||||||
|
{
|
||||||
|
$vlcExists = (test-path $vlcExePath)
|
||||||
|
$3gaFTAExists = (test-path $3gaRegPath)
|
||||||
|
if ($vlcExists -and $3gaFTAExists)
|
||||||
|
{
|
||||||
|
writeSuccess
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write-host ("Expected paths not created: $vlcExePath exists = $vlcExists, $3gaRegPath exists = $3gaFTAExists")
|
||||||
|
writeFail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$output
|
||||||
|
writeFail
|
||||||
|
}
|
||||||
|
|
||||||
|
# remove cases
|
||||||
|
ShowTestHeader("Remove VLC succeeds")
|
||||||
|
$output = & $executable -RemovePackage VLC-3.0.6_1.0.0.0_x64__8wekyb3d8bbwe
|
||||||
|
if ($output -eq $null)
|
||||||
|
{
|
||||||
|
$vlcExists = (test-path $vlcExePath)
|
||||||
|
$3gaFTAExists = (test-path $3gaRegPath)
|
||||||
|
if (-not $vlcExists -and -not $3gaFTAExists)
|
||||||
|
{
|
||||||
|
writeSuccess
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write-host ("Expected paths not deleted: $vlcExePath exists = $vlcExists, $3gaRegPath exists = $3gaFTAExists")
|
||||||
|
writeFail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$output
|
||||||
|
writeFail
|
||||||
|
}
|
||||||
|
|
||||||
|
ShowTestHeader("Remove notepad succeeds")
|
||||||
|
$output = & $executable -RemovePackage notepadplus_0.0.0.1_x64__8wekyb3d8bbwe
|
||||||
|
if ($output -eq $null)
|
||||||
|
{
|
||||||
|
$notepadExists = (test-path $notepadDir\notepad++.exe)
|
||||||
|
$startlinkExists = (test-path $startlink)
|
||||||
|
if (-not $notepadExists -and -not $startlinkExists)
|
||||||
|
{
|
||||||
|
writeSuccess
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
write-host ("Expected paths not deleted: $notepadDir\notepad++.exe exists = $notepadExists, $startLink exists = $startlinkExists")
|
||||||
|
writeFail
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$output
|
||||||
|
writeFail
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
& .\msixtrace.ps1 -stop
|
||||||
|
|
||||||
# manual test: install with UX, launch the package using start menu shortcut, open appwiz.cpl and remove package.
|
# manual test: install with UX, launch the package using start menu shortcut, open appwiz.cpl and remove package.
|
||||||
|
|
|
@ -5,4 +5,5 @@ static const std::wstring uninstallKeyPath = L"SOFTWARE\\Microsoft\\Windows\\Cur
|
||||||
static const std::wstring uninstallKeySubPath = L"Microsoft\\Windows\\CurrentVersion\\Uninstall"; // this subpath could be under Software or Software\Wow6432Node
|
static const std::wstring uninstallKeySubPath = L"Microsoft\\Windows\\CurrentVersion\\Uninstall"; // this subpath could be under Software or Software\Wow6432Node
|
||||||
static const std::wstring registryDatFile = L"\\registry.dat";
|
static const std::wstring registryDatFile = L"\\registry.dat";
|
||||||
static const std::wstring blockMapFile = L"\\AppxBlockMap.xml";
|
static const std::wstring blockMapFile = L"\\AppxBlockMap.xml";
|
||||||
static const std::wstring manifestFile = L"\\AppxManifest.xml";
|
static const std::wstring manifestFile = L"\\AppxManifest.xml";
|
||||||
|
static const std::wstring classesKeyPath = L"SOFTWARE\\Classes";
|
|
@ -6,21 +6,101 @@
|
||||||
#include "FilePaths.hpp"
|
#include "FilePaths.hpp"
|
||||||
#include "FileTypeAssociation.hpp"
|
#include "FileTypeAssociation.hpp"
|
||||||
#include "GeneralUtil.hpp"
|
#include "GeneralUtil.hpp"
|
||||||
|
#include "Constants.hpp"
|
||||||
#include <TraceLoggingProvider.h>
|
#include <TraceLoggingProvider.h>
|
||||||
|
|
||||||
const PCWSTR FileTypeAssociation::HandlerName = L"FileTypeAssociation";
|
const PCWSTR FileTypeAssociation::HandlerName = L"FileTypeAssociation";
|
||||||
|
|
||||||
HRESULT FileTypeAssociation::AddFta(PCWSTR name, PCWSTR parameters)
|
std::wstring FileTypeAssociation::CreateProgID(PCWSTR name)
|
||||||
{
|
{
|
||||||
|
std::wstring packageFullName = m_msixRequest->GetPackageInfo()->GetPackageFullName();
|
||||||
|
std::wstring progID = msix7ProgIDPrefix + packageFullName.substr(0, packageFullName.find(L"_")) + name;
|
||||||
|
return progID;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT FileTypeAssociation::ParseFtaElement(IMsixElement* ftaElement)
|
||||||
|
{
|
||||||
|
Text<wchar_t> ftaName;
|
||||||
|
RETURN_IF_FAILED(ftaElement->GetAttributeValue(nameAttribute.c_str(), &ftaName));
|
||||||
|
|
||||||
|
std::wstring name = L"." + std::wstring(ftaName.Get());
|
||||||
|
std::wstring progID = CreateProgID(name.c_str());
|
||||||
|
|
||||||
|
Fta fta;
|
||||||
|
fta.name = name;
|
||||||
|
fta.progID = progID;
|
||||||
|
|
||||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
"Adding FTA",
|
"Parsing Fta",
|
||||||
TraceLoggingValue(name, "Name"),
|
TraceLoggingValue(ftaName.Get(), "Name"));
|
||||||
TraceLoggingValue(parameters, "Parameters"));
|
|
||||||
|
BOOL hasCurrent = FALSE;
|
||||||
|
ComPtr<IMsixElementEnumerator> ftaEnum;
|
||||||
|
RETURN_IF_FAILED(ftaElement->GetElements(fileTypeQuery.c_str(), &ftaEnum));
|
||||||
|
RETURN_IF_FAILED(ftaEnum->GetHasCurrent(&hasCurrent));
|
||||||
|
while (hasCurrent)
|
||||||
|
{
|
||||||
|
ComPtr<IMsixElement> fileTypeElement;
|
||||||
|
RETURN_IF_FAILED(ftaEnum->GetCurrent(&fileTypeElement));
|
||||||
|
|
||||||
|
Text<wchar_t> extension;
|
||||||
|
RETURN_IF_FAILED(fileTypeElement->GetText(&extension));
|
||||||
|
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Extension",
|
||||||
|
TraceLoggingValue(extension.Get(), "Extension"));
|
||||||
|
fta.extensions.push_back(extension.Get());
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(ftaEnum->MoveNext(&hasCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<IMsixElementEnumerator> logoEnum;
|
||||||
|
RETURN_IF_FAILED(ftaElement->GetElements(logoQuery.c_str(), &logoEnum));
|
||||||
|
RETURN_IF_FAILED(logoEnum->GetHasCurrent(&hasCurrent));
|
||||||
|
if (hasCurrent)
|
||||||
|
{
|
||||||
|
ComPtr<IMsixElement> logoElement;
|
||||||
|
RETURN_IF_FAILED(logoEnum->GetCurrent(&logoElement));
|
||||||
|
|
||||||
|
Text<wchar_t> logoPath;
|
||||||
|
RETURN_IF_FAILED(logoElement->GetText(&logoPath));
|
||||||
|
|
||||||
|
fta.logo = m_msixRequest->GetPackageInfo()->GetPackageDirectoryPath() + std::wstring(L"\\") + logoPath.Get();
|
||||||
|
}
|
||||||
|
|
||||||
|
ComPtr<IMsixElementEnumerator> verbsEnum;
|
||||||
|
RETURN_IF_FAILED(ftaElement->GetElements(verbQuery.c_str(), &verbsEnum));
|
||||||
|
RETURN_IF_FAILED(verbsEnum->GetHasCurrent(&hasCurrent));
|
||||||
|
while (hasCurrent)
|
||||||
|
{
|
||||||
|
ComPtr<IMsixElement> verbElement;
|
||||||
|
RETURN_IF_FAILED(verbsEnum->GetCurrent(&verbElement));
|
||||||
|
|
||||||
|
Text<wchar_t> verbName;
|
||||||
|
RETURN_IF_FAILED(verbElement->GetText(&verbName));
|
||||||
|
|
||||||
|
Text<wchar_t> parameters;
|
||||||
|
RETURN_IF_FAILED(verbElement->GetAttributeValue(parametersAttribute.c_str(), ¶meters));
|
||||||
|
|
||||||
|
Verb verb;
|
||||||
|
verb.verb = verbName.Get();
|
||||||
|
verb.parameter = parameters.Get();
|
||||||
|
fta.verbs.push_back(verb);
|
||||||
|
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Verb",
|
||||||
|
TraceLoggingValue(verbName.Get(), "Verb"),
|
||||||
|
TraceLoggingValue(parameters.Get(), "Parameter"));
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(verbsEnum->MoveNext(&hasCurrent));
|
||||||
|
}
|
||||||
|
|
||||||
|
m_Ftas.push_back(fta);
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT FileTypeAssociation::ExecuteForAddRequest()
|
HRESULT FileTypeAssociation::ParseManifest()
|
||||||
{
|
{
|
||||||
ComPtr<IMsixDocumentElement> domElement;
|
ComPtr<IMsixDocumentElement> domElement;
|
||||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||||
|
@ -29,7 +109,7 @@ HRESULT FileTypeAssociation::ExecuteForAddRequest()
|
||||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||||
|
|
||||||
ComPtr<IMsixElementEnumerator> extensionEnum;
|
ComPtr<IMsixElementEnumerator> extensionEnum;
|
||||||
RETURN_IF_FAILED(element->GetElements(L"/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='Extensions']/*[local-name()='Extension']", &extensionEnum));
|
RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum));
|
||||||
BOOL hasCurrent = FALSE;
|
BOOL hasCurrent = FALSE;
|
||||||
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
||||||
while (hasCurrent)
|
while (hasCurrent)
|
||||||
|
@ -37,13 +117,13 @@ HRESULT FileTypeAssociation::ExecuteForAddRequest()
|
||||||
ComPtr<IMsixElement> extensionElement;
|
ComPtr<IMsixElement> extensionElement;
|
||||||
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
||||||
Text<wchar_t> extensionCategory;
|
Text<wchar_t> extensionCategory;
|
||||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(L"Category", &extensionCategory));
|
RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory));
|
||||||
|
|
||||||
if (wcscmp(extensionCategory.Get(), L"windows.fileTypeAssociation") == 0)
|
if (wcscmp(extensionCategory.Get(), ftaCategoryNameInManifest.c_str()) == 0)
|
||||||
{
|
{
|
||||||
BOOL hc_fta;
|
BOOL hc_fta;
|
||||||
ComPtr<IMsixElementEnumerator> ftaEnum;
|
ComPtr<IMsixElementEnumerator> ftaEnum;
|
||||||
RETURN_IF_FAILED(extensionElement->GetElements(L"*[local-name()='FileTypeAssociation']", &ftaEnum));
|
RETURN_IF_FAILED(extensionElement->GetElements(ftaQuery.c_str(), &ftaEnum));
|
||||||
RETURN_IF_FAILED(ftaEnum->GetHasCurrent(&hc_fta));
|
RETURN_IF_FAILED(ftaEnum->GetHasCurrent(&hc_fta));
|
||||||
|
|
||||||
if (hc_fta)
|
if (hc_fta)
|
||||||
|
@ -51,13 +131,7 @@ HRESULT FileTypeAssociation::ExecuteForAddRequest()
|
||||||
ComPtr<IMsixElement> ftaElement;
|
ComPtr<IMsixElement> ftaElement;
|
||||||
RETURN_IF_FAILED(ftaEnum->GetCurrent(&ftaElement));
|
RETURN_IF_FAILED(ftaEnum->GetCurrent(&ftaElement));
|
||||||
|
|
||||||
Text<wchar_t> ftaName;
|
RETURN_IF_FAILED(ParseFtaElement(ftaElement.Get()));
|
||||||
RETURN_IF_FAILED(ftaElement->GetAttributeValue(L"Name", &ftaName));
|
|
||||||
|
|
||||||
Text<wchar_t> ftaParameters;
|
|
||||||
RETURN_IF_FAILED(ftaElement->GetAttributeValue(L"Parameters", &ftaParameters));
|
|
||||||
|
|
||||||
RETURN_IF_FAILED(AddFta(ftaName.Get(), ftaParameters.Get()));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
||||||
|
@ -66,6 +140,154 @@ HRESULT FileTypeAssociation::ExecuteForAddRequest()
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT FileTypeAssociation::ExecuteForAddRequest()
|
||||||
|
{
|
||||||
|
for (std::vector<Fta>::iterator fta = m_Ftas.begin(); fta != m_Ftas.end(); ++fta)
|
||||||
|
{
|
||||||
|
RETURN_IF_FAILED(ProcessFtaForAdd(*fta));
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT FileTypeAssociation::ProcessFtaForAdd(Fta& fta)
|
||||||
|
{
|
||||||
|
bool needToProcessAnyExtensions = false;
|
||||||
|
for (std::vector<std::wstring>::iterator extensionName = fta.extensions.begin(); extensionName != fta.extensions.end(); ++extensionName)
|
||||||
|
{
|
||||||
|
bool registryHasExtension = false;
|
||||||
|
RETURN_IF_FAILED(m_registryDevirtualizer->HasFTA(*extensionName, registryHasExtension));
|
||||||
|
|
||||||
|
if (registryHasExtension)
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Registry devirtualization already wrote an entry for this extension -- not processing extension",
|
||||||
|
TraceLoggingValue(extensionName->c_str(), "Extension"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
needToProcessAnyExtensions = true;
|
||||||
|
RegistryKey ftaKey;
|
||||||
|
RETURN_IF_FAILED(m_classesKey.CreateSubKey(extensionName->c_str(), KEY_WRITE, &ftaKey));
|
||||||
|
RETURN_IF_FAILED(ftaKey.SetStringValue(L"", fta.progID));
|
||||||
|
|
||||||
|
RegistryKey openWithProgIdsKey;
|
||||||
|
RETURN_IF_FAILED(ftaKey.CreateSubKey(openWithProgIdsKeyName.c_str(), KEY_WRITE, &openWithProgIdsKey));
|
||||||
|
RETURN_IF_FAILED(openWithProgIdsKey.SetValue(fta.progID.c_str(), nullptr, 0, REG_NONE));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needToProcessAnyExtensions)
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Registry devirtualization already wrote entries for all extensions associated with this FTA, nothing more to process for this FTA",
|
||||||
|
TraceLoggingValue(fta.name.c_str(), "Name"));
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
RegistryKey progIdKey;
|
||||||
|
RETURN_IF_FAILED(m_classesKey.CreateSubKey(fta.progID.c_str(), KEY_WRITE, &progIdKey));
|
||||||
|
|
||||||
|
if (fta.logo.c_str() != nullptr)
|
||||||
|
{
|
||||||
|
RegistryKey defaultIconKey;
|
||||||
|
RETURN_IF_FAILED(progIdKey.CreateSubKey(defaultIconKeyName.c_str(), KEY_WRITE, &defaultIconKey));
|
||||||
|
RETURN_IF_FAILED(defaultIconKey.SetStringValue(L"", fta.logo.c_str()));
|
||||||
|
}
|
||||||
|
|
||||||
|
RegistryKey shellKey;
|
||||||
|
RETURN_IF_FAILED(progIdKey.CreateSubKey(shellKeyName.c_str(), KEY_WRITE, &shellKey));
|
||||||
|
RETURN_IF_FAILED(shellKey.SetStringValue(L"", openKeyName));
|
||||||
|
|
||||||
|
RegistryKey openKey;
|
||||||
|
RETURN_IF_FAILED(shellKey.CreateSubKey(openKeyName.c_str(), KEY_WRITE, &openKey));
|
||||||
|
|
||||||
|
RegistryKey commandKey;
|
||||||
|
RETURN_IF_FAILED(openKey.CreateSubKey(commandKeyName.c_str(), KEY_WRITE, &commandKey));
|
||||||
|
|
||||||
|
std::wstring command = m_msixRequest->GetPackageInfo()->GetExecutableFilePath() + commandArgument;
|
||||||
|
RETURN_IF_FAILED(commandKey.SetStringValue(L"", command));
|
||||||
|
|
||||||
|
for (std::vector<Verb>::iterator verb = fta.verbs.begin(); verb != fta.verbs.end(); ++verb)
|
||||||
|
{
|
||||||
|
RegistryKey verbKey;
|
||||||
|
RETURN_IF_FAILED(shellKey.CreateSubKey(verb->verb.c_str(), KEY_WRITE, &verbKey));
|
||||||
|
|
||||||
|
RegistryKey verbCommandKey;
|
||||||
|
RETURN_IF_FAILED(verbKey.CreateSubKey(commandKeyName.c_str(), KEY_WRITE, &verbCommandKey));
|
||||||
|
|
||||||
|
std::wstring verbCommand = m_msixRequest->GetPackageInfo()->GetExecutableFilePath();
|
||||||
|
if (verb->parameter.c_str() != nullptr)
|
||||||
|
{
|
||||||
|
verbCommand += std::wstring(L" ") + verb->parameter.c_str();
|
||||||
|
}
|
||||||
|
RETURN_IF_FAILED(verbCommandKey.SetStringValue(L"", verbCommand));
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT FileTypeAssociation::ExecuteForRemoveRequest()
|
||||||
|
{
|
||||||
|
for (std::vector<Fta>::iterator fta = m_Ftas.begin(); fta != m_Ftas.end(); ++fta)
|
||||||
|
{
|
||||||
|
RETURN_IF_FAILED(ProcessFtaForRemove(*fta));
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT FileTypeAssociation::ProcessFtaForRemove(Fta& fta)
|
||||||
|
{
|
||||||
|
bool needToProcessAnyExtensions = false;
|
||||||
|
for (std::vector<std::wstring>::iterator extensionName = fta.extensions.begin(); extensionName != fta.extensions.end(); ++extensionName)
|
||||||
|
{
|
||||||
|
bool registryHasExtension = false;
|
||||||
|
RETURN_IF_FAILED(m_registryDevirtualizer->HasFTA(*extensionName, registryHasExtension));
|
||||||
|
|
||||||
|
if (registryHasExtension)
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Registry devirtualization already wrote an entry for this extension -- not processing extension",
|
||||||
|
TraceLoggingValue(extensionName->c_str(), "Extension"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
needToProcessAnyExtensions = true;
|
||||||
|
|
||||||
|
HRESULT hrDeleteKey = m_classesKey.DeleteTree(extensionName->c_str());
|
||||||
|
if (FAILED(hrDeleteKey))
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Unable to delete extension",
|
||||||
|
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||||
|
TraceLoggingValue(hrDeleteKey, "HR"),
|
||||||
|
TraceLoggingValue(extensionName->c_str(), "Extension"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!needToProcessAnyExtensions)
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Registry devirtualization already wrote entries for all extensions associated with this FTA, nothing more to process for this FTA",
|
||||||
|
TraceLoggingValue(fta.name.c_str(), "Name"));
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
HRESULT hrDeleteKey = m_classesKey.DeleteTree(fta.progID.c_str());
|
||||||
|
if (FAILED(hrDeleteKey))
|
||||||
|
{
|
||||||
|
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||||
|
"Unable to delete extension",
|
||||||
|
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||||
|
TraceLoggingValue(hrDeleteKey, "HR"),
|
||||||
|
TraceLoggingValue(fta.progID.c_str(), "ProgID"));
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
HRESULT FileTypeAssociation::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
HRESULT FileTypeAssociation::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||||
{
|
{
|
||||||
std::unique_ptr<FileTypeAssociation> localInstance(new FileTypeAssociation(msixRequest));
|
std::unique_ptr<FileTypeAssociation> localInstance(new FileTypeAssociation(msixRequest));
|
||||||
|
@ -73,6 +295,14 @@ HRESULT FileTypeAssociation::CreateHandler(MsixRequest * msixRequest, IPackageHa
|
||||||
{
|
{
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(localInstance->m_classesKey.Open(HKEY_CLASSES_ROOT, nullptr, KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||||
|
|
||||||
|
std::wstring registryFilePath = msixRequest->GetPackageInfo()->GetPackageDirectoryPath().c_str() + registryDatFile;
|
||||||
|
RETURN_IF_FAILED(RegistryDevirtualizer::Create(registryFilePath, msixRequest, &localInstance->m_registryDevirtualizer));
|
||||||
|
|
||||||
|
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||||
|
|
||||||
*instance = localInstance.release();
|
*instance = localInstance.release();
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
|
@ -2,6 +2,44 @@
|
||||||
|
|
||||||
#include "GeneralUtil.hpp"
|
#include "GeneralUtil.hpp"
|
||||||
#include "IPackageHandler.hpp"
|
#include "IPackageHandler.hpp"
|
||||||
|
#include "RegistryKey.hpp"
|
||||||
|
#include "RegistryDevirtualizer.hpp"
|
||||||
|
|
||||||
|
static const std::wstring msix7ProgIDPrefix = L"Msix7";
|
||||||
|
static const std::wstring openWithProgIdsKeyName = L"OpenWithProgids";
|
||||||
|
static const std::wstring shellKeyName = L"Shell";
|
||||||
|
static const std::wstring openKeyName = L"open";
|
||||||
|
static const std::wstring commandKeyName = L"command";
|
||||||
|
static const std::wstring defaultIconKeyName = L"DefaultIcon";
|
||||||
|
static const std::wstring commandArgument = L" \"%1\"";
|
||||||
|
|
||||||
|
static const std::wstring ftaCategoryNameInManifest = L"windows.fileTypeAssociation";
|
||||||
|
|
||||||
|
static const std::wstring categoryAttribute = L"Category";
|
||||||
|
static const std::wstring nameAttribute = L"Name";
|
||||||
|
static const std::wstring parametersAttribute = L"Parameters";
|
||||||
|
|
||||||
|
static const std::wstring extensionQuery = L"/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']/*[local-name()='Extensions']/*[local-name()='Extension']";
|
||||||
|
static const std::wstring ftaQuery = L"*[local-name()='FileTypeAssociation']";
|
||||||
|
static const std::wstring fileTypeQuery = L"*[local-name()='SupportedFileTypes']/*[local-name()='FileType']";
|
||||||
|
static const std::wstring logoQuery = L"*[local-name()='Logo']";
|
||||||
|
static const std::wstring verbQuery = L"*[local-name()='SupportedVerbs']/*[local-name()='Verb']";
|
||||||
|
|
||||||
|
/// Data structs to be filled in from the information in the manifest
|
||||||
|
struct Verb
|
||||||
|
{
|
||||||
|
std::wstring verb;
|
||||||
|
std::wstring parameter;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Fta
|
||||||
|
{
|
||||||
|
std::wstring name;
|
||||||
|
std::wstring progID;
|
||||||
|
std::vector<std::wstring> extensions;
|
||||||
|
std::wstring logo;
|
||||||
|
std::vector<Verb> verbs;
|
||||||
|
};
|
||||||
|
|
||||||
class FileTypeAssociation : IPackageHandler
|
class FileTypeAssociation : IPackageHandler
|
||||||
{
|
{
|
||||||
|
@ -9,14 +47,35 @@ public:
|
||||||
/// Adds the file type associations to the registry so this application can handle specific file types.
|
/// Adds the file type associations to the registry so this application can handle specific file types.
|
||||||
HRESULT ExecuteForAddRequest();
|
HRESULT ExecuteForAddRequest();
|
||||||
|
|
||||||
|
/// Removes the file type associations from the registry.
|
||||||
|
HRESULT ExecuteForRemoveRequest();
|
||||||
|
|
||||||
static const PCWSTR HandlerName;
|
static const PCWSTR HandlerName;
|
||||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||||
~FileTypeAssociation() {}
|
~FileTypeAssociation() {}
|
||||||
private:
|
private:
|
||||||
MsixRequest* m_msixRequest = nullptr;
|
MsixRequest* m_msixRequest = nullptr;
|
||||||
|
RegistryKey m_classesKey;
|
||||||
|
AutoPtr<RegistryDevirtualizer> m_registryDevirtualizer;
|
||||||
|
std::vector<Fta> m_Ftas;
|
||||||
|
|
||||||
FileTypeAssociation() {}
|
FileTypeAssociation() {}
|
||||||
FileTypeAssociation(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
FileTypeAssociation(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||||
|
|
||||||
HRESULT AddFta(PCWSTR name, PCWSTR parameters);
|
/// Parses the manifest and fills in the m_Ftas vector of FileTypeAssociation (Fta) data
|
||||||
|
HRESULT ParseManifest();
|
||||||
|
|
||||||
|
/// Parses the manifest element to populate one Fta struct entry of the m_Ftas vector
|
||||||
|
///
|
||||||
|
/// @param ftaElement - the manifest element representing an Fta
|
||||||
|
HRESULT ParseFtaElement(IMsixElement* ftaElement);
|
||||||
|
|
||||||
|
/// Adds the file type association (fta) entries if necessary
|
||||||
|
HRESULT ProcessFtaForAdd(Fta& fta);
|
||||||
|
|
||||||
|
/// Removes the file type association (fta) entries if necessary
|
||||||
|
HRESULT ProcessFtaForRemove(Fta& fta);
|
||||||
|
|
||||||
|
/// Creates a ProgID from the name of the fta. Simply take the package name and prepend it to the fta
|
||||||
|
std::wstring CreateProgID(PCWSTR name);
|
||||||
};
|
};
|
||||||
|
|
|
@ -53,7 +53,6 @@ std::map<PCWSTR, HandlerInfo> AddHandlers =
|
||||||
std::map<PCWSTR, HandlerInfo> RemoveHandlers =
|
std::map<PCWSTR, HandlerInfo> RemoveHandlers =
|
||||||
{
|
{
|
||||||
//HandlerName Function to create NextHandler
|
//HandlerName Function to create NextHandler
|
||||||
{PopulatePackageInfo::HandlerName, {PopulatePackageInfo::CreateHandler, CreateAndShowUI::HandlerName}},
|
|
||||||
{CreateAndShowUI::HandlerName, {CreateAndShowUI::CreateHandler, StartMenuLink::HandlerName}},
|
{CreateAndShowUI::HandlerName, {CreateAndShowUI::CreateHandler, StartMenuLink::HandlerName}},
|
||||||
{StartMenuLink::HandlerName, {StartMenuLink::CreateHandler, AddRemovePrograms::HandlerName}},
|
{StartMenuLink::HandlerName, {StartMenuLink::CreateHandler, AddRemovePrograms::HandlerName}},
|
||||||
{AddRemovePrograms::HandlerName, {AddRemovePrograms::CreateHandler, Protocol::HandlerName}},
|
{AddRemovePrograms::HandlerName, {AddRemovePrograms::CreateHandler, Protocol::HandlerName}},
|
||||||
|
@ -169,7 +168,12 @@ HRESULT MsixRequest::ProcessAddRequest()
|
||||||
|
|
||||||
HRESULT MsixRequest::ProcessRemoveRequest()
|
HRESULT MsixRequest::ProcessRemoveRequest()
|
||||||
{
|
{
|
||||||
PCWSTR currentHandlerName = PopulatePackageInfo::HandlerName;
|
// Run PopulatePackageInfo separately - if it fails (for instance, if the package is not found) it IS fatal.
|
||||||
|
AutoPtr<IPackageHandler> handler;
|
||||||
|
RETURN_IF_FAILED(PopulatePackageInfo::CreateHandler(this, &handler));
|
||||||
|
RETURN_IF_FAILED(handler->ExecuteForRemoveRequest());
|
||||||
|
|
||||||
|
PCWSTR currentHandlerName = CreateAndShowUI::HandlerName;
|
||||||
|
|
||||||
while (currentHandlerName != nullptr)
|
while (currentHandlerName != nullptr)
|
||||||
{
|
{
|
||||||
|
|
|
@ -2,9 +2,10 @@
|
||||||
|
|
||||||
#include "PackageInfo.hpp"
|
#include "PackageInfo.hpp"
|
||||||
#include "GeneralUtil.hpp"
|
#include "GeneralUtil.hpp"
|
||||||
|
#include "MsixRequest.hpp"
|
||||||
#include <TraceLoggingProvider.h>
|
#include <TraceLoggingProvider.h>
|
||||||
|
|
||||||
HRESULT PackageInfo::SetExecutableAndAppIdFromManifestElement(IMsixElement* element)
|
HRESULT PackageInfo::SetExecutableAndAppIdFromManifestElement(IMsixElement* element, PCWSTR packageFullName, MsixRequest * msixRequest)
|
||||||
{
|
{
|
||||||
BOOL hc = FALSE;
|
BOOL hc = FALSE;
|
||||||
ComPtr<IMsixElementEnumerator> applicationElementEnum;
|
ComPtr<IMsixElementEnumerator> applicationElementEnum;
|
||||||
|
@ -25,10 +26,11 @@ HRESULT PackageInfo::SetExecutableAndAppIdFromManifestElement(IMsixElement* elem
|
||||||
RETURN_IF_FAILED(applicationElementEnum->GetCurrent(&applicationElement));
|
RETURN_IF_FAILED(applicationElementEnum->GetCurrent(&applicationElement));
|
||||||
|
|
||||||
Text<wchar_t> executablePath;
|
Text<wchar_t> executablePath;
|
||||||
Text<wchar_t> applicationId;
|
|
||||||
RETURN_IF_FAILED(applicationElement->GetAttributeValue(L"Executable", &executablePath));
|
RETURN_IF_FAILED(applicationElement->GetAttributeValue(L"Executable", &executablePath));
|
||||||
|
m_executableFilePath = msixRequest->GetFilePathMappings()->GetExecutablePath(executablePath.Get(), packageFullName);
|
||||||
|
|
||||||
|
Text<wchar_t> applicationId;
|
||||||
RETURN_IF_FAILED(applicationElement->GetAttributeValue(L"Id", &applicationId));
|
RETURN_IF_FAILED(applicationElement->GetAttributeValue(L"Id", &applicationId));
|
||||||
m_executableFilePath = executablePath.Get();
|
|
||||||
m_applicationId = applicationId.Get();
|
m_applicationId = applicationId.Get();
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -60,7 +62,7 @@ HRESULT PackageInfo::SetDisplayNameFromManifestElement(IMsixElement* element)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT PackageInfo::MakeFromManifestReader(IAppxManifestReader * manifestReader, std::wstring msix7DirectoryPath, PackageInfo ** packageInfo)
|
HRESULT PackageInfo::MakeFromManifestReader(IAppxManifestReader * manifestReader, MsixRequest * msixRequest, PackageInfo ** packageInfo)
|
||||||
{
|
{
|
||||||
std::unique_ptr<PackageInfo> instance(new PackageInfo());
|
std::unique_ptr<PackageInfo> instance(new PackageInfo());
|
||||||
if (instance == nullptr)
|
if (instance == nullptr)
|
||||||
|
@ -68,14 +70,14 @@ HRESULT PackageInfo::MakeFromManifestReader(IAppxManifestReader * manifestReader
|
||||||
return E_OUTOFMEMORY;
|
return E_OUTOFMEMORY;
|
||||||
}
|
}
|
||||||
|
|
||||||
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader, msix7DirectoryPath));
|
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader, msixRequest));
|
||||||
|
|
||||||
*packageInfo = instance.release();
|
*packageInfo = instance.release();
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT PackageInfo::MakeFromPackageReader(IAppxPackageReader * packageReader, std::wstring msix7DirectoryPath, PackageInfo ** packageInfo)
|
HRESULT PackageInfo::MakeFromPackageReader(IAppxPackageReader * packageReader, MsixRequest * msixRequest, PackageInfo ** packageInfo)
|
||||||
{
|
{
|
||||||
std::unique_ptr<PackageInfo> instance(new PackageInfo());
|
std::unique_ptr<PackageInfo> instance(new PackageInfo());
|
||||||
if (instance == nullptr)
|
if (instance == nullptr)
|
||||||
|
@ -87,7 +89,7 @@ HRESULT PackageInfo::MakeFromPackageReader(IAppxPackageReader * packageReader, s
|
||||||
|
|
||||||
ComPtr<IAppxManifestReader> manifestReader;
|
ComPtr<IAppxManifestReader> manifestReader;
|
||||||
RETURN_IF_FAILED(packageReader->GetManifest(&manifestReader));
|
RETURN_IF_FAILED(packageReader->GetManifest(&manifestReader));
|
||||||
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader.Get(), msix7DirectoryPath));
|
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader.Get(), msixRequest));
|
||||||
|
|
||||||
// Get the number of payload files
|
// Get the number of payload files
|
||||||
DWORD numberOfPayloadFiles = 0;
|
DWORD numberOfPayloadFiles = 0;
|
||||||
|
@ -108,10 +110,12 @@ HRESULT PackageInfo::MakeFromPackageReader(IAppxPackageReader * packageReader, s
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT PackageInfo::SetManifestReader(IAppxManifestReader * manifestReader, std::wstring msix7DirectoryPath)
|
HRESULT PackageInfo::SetManifestReader(IAppxManifestReader * manifestReader, MsixRequest * msixRequest)
|
||||||
{
|
{
|
||||||
m_manifestReader = manifestReader;
|
m_manifestReader = manifestReader;
|
||||||
|
|
||||||
|
std::wstring msix7DirectoryPath = msixRequest->GetFilePathMappings()->GetMsix7Directory();
|
||||||
|
|
||||||
// Also fill other fields that come from the manifest reader
|
// Also fill other fields that come from the manifest reader
|
||||||
ComPtr<IAppxManifestPackageId> manifestId;
|
ComPtr<IAppxManifestPackageId> manifestId;
|
||||||
RETURN_IF_FAILED(manifestReader->GetPackageId(&manifestId));
|
RETURN_IF_FAILED(manifestReader->GetPackageId(&manifestId));
|
||||||
|
@ -129,7 +133,7 @@ HRESULT PackageInfo::SetManifestReader(IAppxManifestReader * manifestReader, std
|
||||||
ComPtr<IMsixElement> element;
|
ComPtr<IMsixElement> element;
|
||||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||||
|
|
||||||
RETURN_IF_FAILED(SetExecutableAndAppIdFromManifestElement(element.Get()));
|
RETURN_IF_FAILED(SetExecutableAndAppIdFromManifestElement(element.Get(), packageFullName.Get(), msixRequest));
|
||||||
|
|
||||||
RETURN_IF_FAILED(SetDisplayNameFromManifestElement(element.Get()));
|
RETURN_IF_FAILED(SetDisplayNameFromManifestElement(element.Get()));
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include "AppxPackaging.hpp"
|
#include "AppxPackaging.hpp"
|
||||||
#include "MSIXWindows.hpp"
|
#include "MSIXWindows.hpp"
|
||||||
|
|
||||||
|
class MsixRequest;
|
||||||
|
|
||||||
class PackageInfo
|
class PackageInfo
|
||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
|
@ -25,10 +27,10 @@ private:
|
||||||
///
|
///
|
||||||
/// @param manifestReader - manifestReader to set
|
/// @param manifestReader - manifestReader to set
|
||||||
/// @param msix7DirectoryPath - the root msix7 directory path, which is the parent directory of the package directory.
|
/// @param msix7DirectoryPath - the root msix7 directory path, which is the parent directory of the package directory.
|
||||||
HRESULT SetManifestReader(IAppxManifestReader* manifestReader, std::wstring msix7DirectoryPath);
|
HRESULT SetManifestReader(IAppxManifestReader* manifestReader, MsixRequest * msixRequest);
|
||||||
|
|
||||||
/// Sets the executable path by reading it from the manifest element
|
/// Sets the executable path and app ID by reading it from the manifest element
|
||||||
HRESULT SetExecutableAndAppIdFromManifestElement(IMsixElement * element);
|
HRESULT SetExecutableAndAppIdFromManifestElement(IMsixElement * element, PCWSTR packageFullName, MsixRequest * msixRequest);
|
||||||
|
|
||||||
/// Sets the display name by reading it from the manifest element
|
/// Sets the display name by reading it from the manifest element
|
||||||
HRESULT SetDisplayNameFromManifestElement(IMsixElement * element);
|
HRESULT SetDisplayNameFromManifestElement(IMsixElement * element);
|
||||||
|
@ -39,11 +41,11 @@ private:
|
||||||
public:
|
public:
|
||||||
/// Create a PackageInfo using the manifest reader and directory path. This is intended for Remove scenarios where
|
/// Create a PackageInfo using the manifest reader and directory path. This is intended for Remove scenarios where
|
||||||
/// the actual .msix package file is no longer accessible.
|
/// the actual .msix package file is no longer accessible.
|
||||||
static HRESULT MakeFromManifestReader(IAppxManifestReader* manifestReader, std::wstring msix7DirectoryPath, PackageInfo** packageInfo);
|
static HRESULT MakeFromManifestReader(IAppxManifestReader* manifestReader, MsixRequest * msixRequest, PackageInfo** packageInfo);
|
||||||
|
|
||||||
/// Create a PackageInfo using the package reader. This is intended for Add scenarios where
|
/// Create a PackageInfo using the package reader. This is intended for Add scenarios where
|
||||||
/// the actual .msix package file is given.
|
/// the actual .msix package file is given.
|
||||||
static HRESULT MakeFromPackageReader(IAppxPackageReader* packageReader, std::wstring msix7DirectoryPath, PackageInfo** packageInfo);
|
static HRESULT MakeFromPackageReader(IAppxPackageReader* packageReader, MsixRequest * msixRequest, PackageInfo** packageInfo);
|
||||||
|
|
||||||
/// When made from manifest reader, it won't have PackageReader available.
|
/// When made from manifest reader, it won't have PackageReader available.
|
||||||
bool HasPackageReader() { return (m_packageReader.Get() != nullptr); };
|
bool HasPackageReader() { return (m_packageReader.Get() != nullptr); };
|
||||||
|
|
|
@ -24,7 +24,7 @@ HRESULT PopulatePackageInfo::CreatePackageReader()
|
||||||
RETURN_IF_FAILED(appxFactory->CreatePackageReader(inputStream.Get(), &packageReader));
|
RETURN_IF_FAILED(appxFactory->CreatePackageReader(inputStream.Get(), &packageReader));
|
||||||
|
|
||||||
AutoPtr<PackageInfo> packageInfo;
|
AutoPtr<PackageInfo> packageInfo;
|
||||||
RETURN_IF_FAILED(PackageInfo::MakeFromPackageReader(packageReader.Get(), m_msixRequest->GetFilePathMappings()->GetMsix7Directory(), &packageInfo));
|
RETURN_IF_FAILED(PackageInfo::MakeFromPackageReader(packageReader.Get(), m_msixRequest, &packageInfo));
|
||||||
m_msixRequest->SetPackageInfo(packageInfo.Detach());
|
m_msixRequest->SetPackageInfo(packageInfo.Detach());
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
@ -69,7 +69,7 @@ HRESULT PopulatePackageInfo::ExecuteForRemoveRequest()
|
||||||
RETURN_IF_FAILED(appxFactory->CreateManifestReader(stream.Get(), &manifestReader));
|
RETURN_IF_FAILED(appxFactory->CreateManifestReader(stream.Get(), &manifestReader));
|
||||||
|
|
||||||
AutoPtr<PackageInfo> packageInfo;
|
AutoPtr<PackageInfo> packageInfo;
|
||||||
RETURN_IF_FAILED(PackageInfo::MakeFromManifestReader(manifestReader.Get(), m_msixRequest->GetFilePathMappings()->GetMsix7Directory(), &packageInfo));
|
RETURN_IF_FAILED(PackageInfo::MakeFromManifestReader(manifestReader.Get(), m_msixRequest, &packageInfo));
|
||||||
m_msixRequest->SetPackageInfo(packageInfo.Detach());
|
m_msixRequest->SetPackageInfo(packageInfo.Detach());
|
||||||
|
|
||||||
return S_OK;
|
return S_OK;
|
||||||
|
|
|
@ -60,6 +60,41 @@ HRESULT RegistryDevirtualizer::Run(_In_ bool remove)
|
||||||
return S_OK;
|
return S_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT RegistryDevirtualizer::HasFTA(std::wstring ftaName, bool & hasFTA)
|
||||||
|
{
|
||||||
|
hasFTA = false;
|
||||||
|
std::wstring rootPath = m_loadedHiveKeyName + L"\\Registry";
|
||||||
|
RETURN_IF_FAILED(m_rootKey.Open(HKEY_USERS, rootPath.c_str(), KEY_READ));
|
||||||
|
|
||||||
|
RegistryKey userClassesKey;
|
||||||
|
HRESULT hrOpenUserClassesKey = m_rootKey.OpenSubKey(L"USER\\[{AppVCurrentUserSID}]_CLASSES", KEY_READ, &userClassesKey);
|
||||||
|
if (SUCCEEDED(hrOpenUserClassesKey))
|
||||||
|
{
|
||||||
|
RegistryKey ftaKey;
|
||||||
|
HRESULT hrFtaKey = userClassesKey.OpenSubKey(ftaName.c_str(), KEY_READ, &ftaKey);
|
||||||
|
if (SUCCEEDED(hrFtaKey))
|
||||||
|
{
|
||||||
|
hasFTA = true;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
RegistryKey machineClassesKey;
|
||||||
|
HRESULT hrOpenMachineClassesKey = m_rootKey.OpenSubKey(L"MACHINE\\Software\\Classes", KEY_READ, &machineClassesKey);
|
||||||
|
if (SUCCEEDED(hrOpenMachineClassesKey))
|
||||||
|
{
|
||||||
|
RegistryKey ftaKey;
|
||||||
|
HRESULT hrFtaKey = machineClassesKey.OpenSubKey(ftaName.c_str(), KEY_READ, &ftaKey);
|
||||||
|
if (SUCCEEDED(hrFtaKey))
|
||||||
|
{
|
||||||
|
hasFTA = true;
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return S_OK;
|
||||||
|
}
|
||||||
|
|
||||||
bool RegistryDevirtualizer::IsExcludeKey(RegistryKey* realKey)
|
bool RegistryDevirtualizer::IsExcludeKey(RegistryKey* realKey)
|
||||||
{
|
{
|
||||||
const std::wstring excludeKeys[] =
|
const std::wstring excludeKeys[] =
|
||||||
|
@ -231,7 +266,7 @@ HRESULT RemoveSubKeyIfEmpty(RegistryKey* realKey, PCWSTR subKeyName)
|
||||||
DWORD valuesCount = 0;
|
DWORD valuesCount = 0;
|
||||||
DWORD valueNameMaxLength = 0;
|
DWORD valueNameMaxLength = 0;
|
||||||
DWORD valueDataMaxLength = 0;
|
DWORD valueDataMaxLength = 0;
|
||||||
RETURN_IF_FAILED(realKey->GetValuesInfo(&valuesCount, &valueNameMaxLength, &valueDataMaxLength));
|
RETURN_IF_FAILED(subKey.GetValuesInfo(&valuesCount, &valueNameMaxLength, &valueDataMaxLength));
|
||||||
|
|
||||||
subKey.Close();
|
subKey.Close();
|
||||||
if (valuesCount == 0)
|
if (valuesCount == 0)
|
||||||
|
|
|
@ -17,6 +17,11 @@ public:
|
||||||
/// @param remove - if true, remove all the keys and values; if false, add all the keys and values
|
/// @param remove - if true, remove all the keys and values; if false, add all the keys and values
|
||||||
HRESULT Run(_In_ bool remove);
|
HRESULT Run(_In_ bool remove);
|
||||||
|
|
||||||
|
/// Determines whether Registry.dat contains a FTA (file type association). Does not perform any modifications to the system.
|
||||||
|
///
|
||||||
|
/// @param ftaName - the name of the FTA extension (i.e. .mp4)
|
||||||
|
HRESULT HasFTA(_In_ std::wstring ftaName, _Out_ bool& hasFTA);
|
||||||
|
|
||||||
static HRESULT Create(_In_ std::wstring hiveFileName, _In_ MsixRequest* msixRequest, _Out_ RegistryDevirtualizer** instance);
|
static HRESULT Create(_In_ std::wstring hiveFileName, _In_ MsixRequest* msixRequest, _Out_ RegistryDevirtualizer** instance);
|
||||||
|
|
||||||
/// Creates a GUID string as a temporary registry key's name
|
/// Creates a GUID string as a temporary registry key's name
|
||||||
|
|
|
@ -6,7 +6,10 @@ HRESULT RegistryKey::Open(
|
||||||
_In_ const REGSAM sam)
|
_In_ const REGSAM sam)
|
||||||
{
|
{
|
||||||
Close();
|
Close();
|
||||||
m_path = subkey;
|
if (subkey != nullptr)
|
||||||
|
{
|
||||||
|
m_path = subkey;
|
||||||
|
}
|
||||||
return HRESULT_FROM_WIN32(RegOpenKeyExW(hkey, subkey, 0, sam, AddressOfHkey()));
|
return HRESULT_FROM_WIN32(RegOpenKeyExW(hkey, subkey, 0, sam, AddressOfHkey()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -82,7 +85,7 @@ HRESULT RegistryKey::SetValue(
|
||||||
return HRESULT_FROM_WIN32(RegSetValueExW(this->m_hkey, name, 0, type, static_cast<const BYTE*>(value), valueSize));
|
return HRESULT_FROM_WIN32(RegSetValueExW(this->m_hkey, name, 0, type, static_cast<const BYTE*>(value), valueSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
HRESULT RegistryKey::SetStringValue(PCWSTR name, std::wstring& value)
|
HRESULT RegistryKey::SetStringValue(PCWSTR name, const std::wstring& value)
|
||||||
{
|
{
|
||||||
return SetValue(name, value.c_str(), static_cast<DWORD>(value.size() * sizeof(WCHAR)), REG_SZ);
|
return SetValue(name, value.c_str(), static_cast<DWORD>(value.size() * sizeof(WCHAR)), REG_SZ);
|
||||||
}
|
}
|
||||||
|
@ -102,3 +105,7 @@ HRESULT RegistryKey::DeleteSubKey(PCWSTR subkey)
|
||||||
return HRESULT_FROM_WIN32(RegDeleteKeyW(this->m_hkey, subkey));
|
return HRESULT_FROM_WIN32(RegDeleteKeyW(this->m_hkey, subkey));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
HRESULT RegistryKey::DeleteTree(PCWSTR subkey)
|
||||||
|
{
|
||||||
|
return HRESULT_FROM_WIN32(RegDeleteTreeW(this->m_hkey, subkey));
|
||||||
|
}
|
||||||
|
|
|
@ -108,13 +108,16 @@ public:
|
||||||
|
|
||||||
HRESULT SetStringValue(
|
HRESULT SetStringValue(
|
||||||
_In_opt_ PCWSTR name,
|
_In_opt_ PCWSTR name,
|
||||||
_In_opt_ std::wstring& value);
|
_In_opt_ const std::wstring& value);
|
||||||
|
|
||||||
HRESULT DeleteValue(
|
HRESULT DeleteValue(
|
||||||
_In_opt_ PCWSTR name);
|
_In_opt_ PCWSTR name);
|
||||||
|
|
||||||
HRESULT DeleteSubKey(
|
HRESULT DeleteSubKey(
|
||||||
_In_ PCWSTR subkey);
|
_In_ PCWSTR subkey);
|
||||||
|
|
||||||
|
HRESULT DeleteTree(
|
||||||
|
_In_ PCWSTR subkey);
|
||||||
|
|
||||||
template<typename TAction> static HRESULT EnumKeyAndDoActionForAllSubkeys(
|
template<typename TAction> static HRESULT EnumKeyAndDoActionForAllSubkeys(
|
||||||
_In_ RegistryKey* registryKey,
|
_In_ RegistryKey* registryKey,
|
||||||
|
|
|
@ -50,7 +50,7 @@ HRESULT StartMenuLink::ExecuteForAddRequest()
|
||||||
|
|
||||||
std::wstring filePath = m_msixRequest->GetFilePathMappings()->GetMap()[L"Common Programs"] + L"\\" + packageInfo->GetDisplayName() + L".lnk";
|
std::wstring filePath = m_msixRequest->GetFilePathMappings()->GetMap()[L"Common Programs"] + L"\\" + packageInfo->GetDisplayName() + L".lnk";
|
||||||
|
|
||||||
std::wstring resolvedExecutableFullPath = m_msixRequest->GetFilePathMappings()->GetExecutablePath(packageInfo->GetExecutableFilePath(), packageInfo->GetPackageFullName().c_str());
|
std::wstring resolvedExecutableFullPath = packageInfo->GetExecutableFilePath();
|
||||||
std::wstring appUserModelId = m_msixRequest->GetPackageInfo()->GetAppModelUserId();
|
std::wstring appUserModelId = m_msixRequest->GetPackageInfo()->GetAppModelUserId();
|
||||||
RETURN_IF_FAILED(CreateLink(resolvedExecutableFullPath.c_str(), filePath.c_str(), L"", appUserModelId.c_str()));
|
RETURN_IF_FAILED(CreateLink(resolvedExecutableFullPath.c_str(), filePath.c_str(), L"", appUserModelId.c_str()));
|
||||||
|
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace MSIX {
|
||||||
} CATCH_RETURN();
|
} CATCH_RETURN();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IMsixFactory* m_factory;
|
ComPtr<IMsixFactory> m_factory;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
std::uint64_t m_minVersion;
|
std::uint64_t m_minVersion;
|
||||||
std::uint64_t m_maxVersion;
|
std::uint64_t m_maxVersion;
|
||||||
|
@ -107,7 +107,7 @@ namespace MSIX {
|
||||||
} CATCH_RETURN();
|
} CATCH_RETURN();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IMsixFactory* m_factory;
|
ComPtr<IMsixFactory> m_factory;
|
||||||
std::string m_aumid;
|
std::string m_aumid;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -160,7 +160,7 @@ namespace MSIX {
|
||||||
} CATCH_RETURN();
|
} CATCH_RETURN();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IMsixFactory* m_factory;
|
ComPtr<IMsixFactory> m_factory;
|
||||||
std::map<std::string, std::string> m_stringValues;
|
std::map<std::string, std::string> m_stringValues;
|
||||||
std::map<std::string, bool> m_boolValues;
|
std::map<std::string, bool> m_boolValues;
|
||||||
};
|
};
|
||||||
|
@ -209,7 +209,7 @@ namespace MSIX {
|
||||||
} CATCH_RETURN();
|
} CATCH_RETURN();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IMsixFactory* m_factory;
|
ComPtr<IMsixFactory> m_factory;
|
||||||
UINT64 m_minVersion;
|
UINT64 m_minVersion;
|
||||||
std::string m_publisher;
|
std::string m_publisher;
|
||||||
std::string m_name;
|
std::string m_name;
|
||||||
|
@ -255,7 +255,7 @@ namespace MSIX {
|
||||||
HRESULT STDMETHODCALLTYPE GetDocumentElement(IMsixElement** documentElement) noexcept override;
|
HRESULT STDMETHODCALLTYPE GetDocumentElement(IMsixElement** documentElement) noexcept override;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
IMsixFactory* m_factory;
|
ComPtr<IMsixFactory> m_factory;
|
||||||
ComPtr<IStream> m_stream;
|
ComPtr<IStream> m_stream;
|
||||||
ComPtr<IAppxManifestPackageId> m_packageId;
|
ComPtr<IAppxManifestPackageId> m_packageId;
|
||||||
MSIX_PLATFORMS m_platform = MSIX_PLATFORM_NONE;
|
MSIX_PLATFORMS m_platform = MSIX_PLATFORM_NONE;
|
||||||
|
|
|
@ -89,7 +89,7 @@ namespace MSIX {
|
||||||
const auto& version = identityNode->GetAttributeValue(XmlAttributeName::Version);
|
const auto& version = identityNode->GetAttributeValue(XmlAttributeName::Version);
|
||||||
const auto& resourceId = identityNode->GetAttributeValue(XmlAttributeName::ResourceId);
|
const auto& resourceId = identityNode->GetAttributeValue(XmlAttributeName::ResourceId);
|
||||||
ThrowErrorIf(Error::AppxManifestSemanticError, (publisher.empty()), "Invalid Identity element");
|
ThrowErrorIf(Error::AppxManifestSemanticError, (publisher.empty()), "Invalid Identity element");
|
||||||
self->m_packageId = ComPtr<IAppxManifestPackageId>::Make<AppxManifestPackageId>(self->m_factory, name, version, resourceId, architecture, publisher);
|
self->m_packageId = ComPtr<IAppxManifestPackageId>::Make<AppxManifestPackageId>(self->m_factory.Get(), name, version, resourceId, architecture, publisher);
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Identity, visitor);
|
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Identity, visitor);
|
||||||
|
@ -103,7 +103,7 @@ namespace MSIX {
|
||||||
auto name = tdfNode->GetAttributeValue(XmlAttributeName::Name);
|
auto name = tdfNode->GetAttributeValue(XmlAttributeName::Name);
|
||||||
auto min = tdfNode->GetAttributeValue(XmlAttributeName::MinVersion);
|
auto min = tdfNode->GetAttributeValue(XmlAttributeName::MinVersion);
|
||||||
auto max = tdfNode->GetAttributeValue(XmlAttributeName::Dependencies_Tdf_MaxVersionTested);
|
auto max = tdfNode->GetAttributeValue(XmlAttributeName::Dependencies_Tdf_MaxVersionTested);
|
||||||
auto tdf = ComPtr<IAppxManifestTargetDeviceFamily>::Make<AppxManifestTargetDeviceFamily>(self->m_factory, name, min, max);
|
auto tdf = ComPtr<IAppxManifestTargetDeviceFamily>::Make<AppxManifestTargetDeviceFamily>(self->m_factory.Get(), name, min, max);
|
||||||
self->m_tdf.push_back(std::move(tdf));
|
self->m_tdf.push_back(std::move(tdf));
|
||||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||||
const auto& tdfEntry = std::find(std::begin(targetDeviceFamilyList), std::end(targetDeviceFamilyList), name.c_str());
|
const auto& tdfEntry = std::find(std::begin(targetDeviceFamilyList), std::end(targetDeviceFamilyList), name.c_str());
|
||||||
|
@ -225,7 +225,7 @@ namespace MSIX {
|
||||||
});
|
});
|
||||||
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Properties, visitorProperties);
|
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Properties, visitorProperties);
|
||||||
*packageProperties = ComPtr<IAppxManifestProperties>::Make<AppxManifestProperties>(
|
*packageProperties = ComPtr<IAppxManifestProperties>::Make<AppxManifestProperties>(
|
||||||
m_factory, std::move(stringValues), std::move(boolValues)).Detach();
|
m_factory.Get(), std::move(stringValues), std::move(boolValues)).Detach();
|
||||||
return static_cast<HRESULT>(Error::OK);
|
return static_cast<HRESULT>(Error::OK);
|
||||||
} CATCH_RETURN();
|
} CATCH_RETURN();
|
||||||
|
|
||||||
|
@ -248,7 +248,7 @@ namespace MSIX {
|
||||||
auto name = dependencyNode->GetAttributeValue(XmlAttributeName::Name);
|
auto name = dependencyNode->GetAttributeValue(XmlAttributeName::Name);
|
||||||
auto publisher = dependencyNode->GetAttributeValue(XmlAttributeName::Publisher);
|
auto publisher = dependencyNode->GetAttributeValue(XmlAttributeName::Publisher);
|
||||||
// TODO: get MaxMajorVersionTested if needed
|
// TODO: get MaxMajorVersionTested if needed
|
||||||
auto dependency = ComPtr<IAppxManifestPackageDependency>::Make<AppxManifestPackageDependency>(context->self->m_factory, min, name, publisher);
|
auto dependency = ComPtr<IAppxManifestPackageDependency>::Make<AppxManifestPackageDependency>(context->self->m_factory.Get(), min, name, publisher);
|
||||||
context->packageDependencies->push_back(std::move(dependency));
|
context->packageDependencies->push_back(std::move(dependency));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
|
@ -293,7 +293,7 @@ namespace MSIX {
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Resources_Resource, visitorResource);
|
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Resources_Resource, visitorResource);
|
||||||
*resources = ComPtr<IAppxManifestResourcesEnumerator>::Make<EnumeratorString<IAppxManifestResourcesEnumerator, IAppxManifestResourcesEnumeratorUtf8>>(m_factory, appxResources).Detach();
|
*resources = ComPtr<IAppxManifestResourcesEnumerator>::Make<EnumeratorString<IAppxManifestResourcesEnumerator, IAppxManifestResourcesEnumeratorUtf8>>(m_factory.Get(), appxResources).Detach();
|
||||||
return static_cast<HRESULT>(Error::OK);
|
return static_cast<HRESULT>(Error::OK);
|
||||||
} CATCH_RETURN();
|
} CATCH_RETURN();
|
||||||
|
|
||||||
|
@ -327,7 +327,7 @@ namespace MSIX {
|
||||||
auto appId = applicationNode->GetAttributeValue(XmlAttributeName::Package_Applications_Application_Id);
|
auto appId = applicationNode->GetAttributeValue(XmlAttributeName::Package_Applications_Application_Id);
|
||||||
auto packageIdInternal = context->self->m_packageId.As<IAppxManifestPackageIdInternal>();
|
auto packageIdInternal = context->self->m_packageId.As<IAppxManifestPackageIdInternal>();
|
||||||
auto aumid = packageIdInternal->GetPackageFamilyName() + "!" + appId;
|
auto aumid = packageIdInternal->GetPackageFamilyName() + "!" + appId;
|
||||||
auto application = ComPtr<IAppxManifestApplication>::Make<AppxManifestApplication>(context->self->m_factory, aumid);
|
auto application = ComPtr<IAppxManifestApplication>::Make<AppxManifestApplication>(context->self->m_factory.Get(), aumid);
|
||||||
// TODO: get other attributes from the Application element and store them a map in AppxManifestApplication
|
// TODO: get other attributes from the Application element and store them a map in AppxManifestApplication
|
||||||
// or make the AppxManifestApplication have a IXmlElement member to get attributes at will.
|
// or make the AppxManifestApplication have a IXmlElement member to get attributes at will.
|
||||||
context->apps->push_back(std::move(application));
|
context->apps->push_back(std::move(application));
|
||||||
|
|
Загрузка…
Ссылка в новой задаче