* 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:
wcheng-msft 2019-03-06 15:08:12 -08:00 коммит произвёл GitHub
Родитель 2f2918ffd1
Коммит 5494d5566b
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
20 изменённых файлов: 484 добавлений и 55 удалений

Просмотреть файл

@ -1,4 +1,5 @@
@echo off
SETLOCAL
if "%~1" == "" goto USAGE
if /I "%~1" == "--help" goto USAGE
@ -7,12 +8,14 @@ if /I "%~1" == "/?" goto USAGE
:: Set up MSVC environment
:: Kudos to https://gist.github.com/AndrewPardoe/689a3b969670787d5dba538bb0a48a1e
pushd "%~dp0"
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 (
set VCINSTALLDIR=%%i\VC
)
popd
if exist %VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat (
if /I "%~1" == "x86" (
call "%VCINSTALLDIR%\Auxiliary\Build\vcvarsall.bat" %1

Двоичный файл не отображается.

Просмотреть файл

@ -47,3 +47,7 @@ Uninstallation -
```
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.

Двоичный файл не отображается.

Просмотреть файл

@ -55,6 +55,8 @@ function DoStop
}
}
Write-Host "Parsing trace logs..."
#convert the ETL file to XML data
$now = [datetime]::Now.ToString("yyyy_MM_dd_HHmmss")
$tempXmlfile = join-path $env:temp ("MsixTrace_{0}.xml" -f $now)

Просмотреть файл

@ -45,6 +45,8 @@ function ShowTestHeader($testname)
$global:testcase++
}
New-PSDrive -PSProvider registry -Root HKEY_CLASSES_ROOT -Name HKCR -errorAction SilentlyContinue
ShowTestHeader("Untrusted Package fails")
certutil -delstore root 19a0a57c05c3a4884123cd9dccf820ea > $null
@ -62,10 +64,10 @@ ShowTestHeader("Trusted Package succeeds")
certutil -addstore root APPX_TEST_ROOT.cer > $null
$output = & $executable -AddPackage notepadplus.msix -quietUx
$notepadDir = "C:\program files (x86)\notepad++"
$startLink = "c:\programdata\microsoft\windows\start menu\programs\notepad++.lnk"
if ($output -eq $null)
{
$notepadExists = (test-path $notepadDir\notepad++.exe)
$startLink = "c:\programdata\microsoft\windows\start menu\programs\notepad++.lnk"
$startlinkExists = (test-path $startlink)
if ($notepadExists -and $startlinkExists)
{
@ -176,8 +178,76 @@ else
writeFail
}
& .\msixtrace.ps1 -stop
# remove cases
ShowTestHeader("Install with FTA and registry entries succeeds")
$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.

Просмотреть файл

@ -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 registryDatFile = L"\\registry.dat";
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 "FileTypeAssociation.hpp"
#include "GeneralUtil.hpp"
#include "Constants.hpp"
#include <TraceLoggingProvider.h>
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,
"Adding FTA",
TraceLoggingValue(name, "Name"),
TraceLoggingValue(parameters, "Parameters"));
"Parsing Fta",
TraceLoggingValue(ftaName.Get(), "Name"));
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(), &parameters));
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;
}
HRESULT FileTypeAssociation::ExecuteForAddRequest()
HRESULT FileTypeAssociation::ParseManifest()
{
ComPtr<IMsixDocumentElement> 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));
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;
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
while (hasCurrent)
@ -37,13 +117,13 @@ HRESULT FileTypeAssociation::ExecuteForAddRequest()
ComPtr<IMsixElement> extensionElement;
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
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;
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));
if (hc_fta)
@ -51,13 +131,7 @@ HRESULT FileTypeAssociation::ExecuteForAddRequest()
ComPtr<IMsixElement> ftaElement;
RETURN_IF_FAILED(ftaEnum->GetCurrent(&ftaElement));
Text<wchar_t> ftaName;
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(ParseFtaElement(ftaElement.Get()));
}
}
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
@ -66,6 +140,154 @@ HRESULT FileTypeAssociation::ExecuteForAddRequest()
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)
{
std::unique_ptr<FileTypeAssociation> localInstance(new FileTypeAssociation(msixRequest));
@ -73,6 +295,14 @@ HRESULT FileTypeAssociation::CreateHandler(MsixRequest * msixRequest, IPackageHa
{
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();
return S_OK;

Просмотреть файл

@ -2,6 +2,44 @@
#include "GeneralUtil.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
{
@ -9,14 +47,35 @@ public:
/// Adds the file type associations to the registry so this application can handle specific file types.
HRESULT ExecuteForAddRequest();
/// Removes the file type associations from the registry.
HRESULT ExecuteForRemoveRequest();
static const PCWSTR HandlerName;
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
~FileTypeAssociation() {}
private:
MsixRequest* m_msixRequest = nullptr;
RegistryKey m_classesKey;
AutoPtr<RegistryDevirtualizer> m_registryDevirtualizer;
std::vector<Fta> m_Ftas;
FileTypeAssociation() {}
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 =
{
//HandlerName Function to create NextHandler
{PopulatePackageInfo::HandlerName, {PopulatePackageInfo::CreateHandler, CreateAndShowUI::HandlerName}},
{CreateAndShowUI::HandlerName, {CreateAndShowUI::CreateHandler, StartMenuLink::HandlerName}},
{StartMenuLink::HandlerName, {StartMenuLink::CreateHandler, AddRemovePrograms::HandlerName}},
{AddRemovePrograms::HandlerName, {AddRemovePrograms::CreateHandler, Protocol::HandlerName}},
@ -169,7 +168,12 @@ HRESULT MsixRequest::ProcessAddRequest()
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)
{

Просмотреть файл

@ -2,9 +2,10 @@
#include "PackageInfo.hpp"
#include "GeneralUtil.hpp"
#include "MsixRequest.hpp"
#include <TraceLoggingProvider.h>
HRESULT PackageInfo::SetExecutableAndAppIdFromManifestElement(IMsixElement* element)
HRESULT PackageInfo::SetExecutableAndAppIdFromManifestElement(IMsixElement* element, PCWSTR packageFullName, MsixRequest * msixRequest)
{
BOOL hc = FALSE;
ComPtr<IMsixElementEnumerator> applicationElementEnum;
@ -25,10 +26,11 @@ HRESULT PackageInfo::SetExecutableAndAppIdFromManifestElement(IMsixElement* elem
RETURN_IF_FAILED(applicationElementEnum->GetCurrent(&applicationElement));
Text<wchar_t> executablePath;
Text<wchar_t> applicationId;
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));
m_executableFilePath = executablePath.Get();
m_applicationId = applicationId.Get();
return S_OK;
@ -60,7 +62,7 @@ HRESULT PackageInfo::SetDisplayNameFromManifestElement(IMsixElement* element)
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());
if (instance == nullptr)
@ -68,14 +70,14 @@ HRESULT PackageInfo::MakeFromManifestReader(IAppxManifestReader * manifestReader
return E_OUTOFMEMORY;
}
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader, msix7DirectoryPath));
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader, msixRequest));
*packageInfo = instance.release();
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());
if (instance == nullptr)
@ -87,7 +89,7 @@ HRESULT PackageInfo::MakeFromPackageReader(IAppxPackageReader * packageReader, s
ComPtr<IAppxManifestReader> 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
DWORD numberOfPayloadFiles = 0;
@ -108,10 +110,12 @@ HRESULT PackageInfo::MakeFromPackageReader(IAppxPackageReader * packageReader, s
return S_OK;
}
HRESULT PackageInfo::SetManifestReader(IAppxManifestReader * manifestReader, std::wstring msix7DirectoryPath)
HRESULT PackageInfo::SetManifestReader(IAppxManifestReader * manifestReader, MsixRequest * msixRequest)
{
m_manifestReader = manifestReader;
std::wstring msix7DirectoryPath = msixRequest->GetFilePathMappings()->GetMsix7Directory();
// Also fill other fields that come from the manifest reader
ComPtr<IAppxManifestPackageId> manifestId;
RETURN_IF_FAILED(manifestReader->GetPackageId(&manifestId));
@ -129,7 +133,7 @@ HRESULT PackageInfo::SetManifestReader(IAppxManifestReader * manifestReader, std
ComPtr<IMsixElement> 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()));

Просмотреть файл

@ -3,6 +3,8 @@
#include "AppxPackaging.hpp"
#include "MSIXWindows.hpp"
class MsixRequest;
class PackageInfo
{
private:
@ -25,10 +27,10 @@ private:
///
/// @param manifestReader - manifestReader to set
/// @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
HRESULT SetExecutableAndAppIdFromManifestElement(IMsixElement * element);
/// Sets the executable path and app ID by reading it from the manifest element
HRESULT SetExecutableAndAppIdFromManifestElement(IMsixElement * element, PCWSTR packageFullName, MsixRequest * msixRequest);
/// Sets the display name by reading it from the manifest element
HRESULT SetDisplayNameFromManifestElement(IMsixElement * element);
@ -39,11 +41,11 @@ private:
public:
/// 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.
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
/// 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.
bool HasPackageReader() { return (m_packageReader.Get() != nullptr); };

Просмотреть файл

@ -24,7 +24,7 @@ HRESULT PopulatePackageInfo::CreatePackageReader()
RETURN_IF_FAILED(appxFactory->CreatePackageReader(inputStream.Get(), &packageReader));
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());
return S_OK;
@ -69,7 +69,7 @@ HRESULT PopulatePackageInfo::ExecuteForRemoveRequest()
RETURN_IF_FAILED(appxFactory->CreateManifestReader(stream.Get(), &manifestReader));
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());
return S_OK;

Просмотреть файл

@ -60,6 +60,41 @@ HRESULT RegistryDevirtualizer::Run(_In_ bool remove)
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)
{
const std::wstring excludeKeys[] =
@ -231,7 +266,7 @@ HRESULT RemoveSubKeyIfEmpty(RegistryKey* realKey, PCWSTR subKeyName)
DWORD valuesCount = 0;
DWORD valueNameMaxLength = 0;
DWORD valueDataMaxLength = 0;
RETURN_IF_FAILED(realKey->GetValuesInfo(&valuesCount, &valueNameMaxLength, &valueDataMaxLength));
RETURN_IF_FAILED(subKey.GetValuesInfo(&valuesCount, &valueNameMaxLength, &valueDataMaxLength));
subKey.Close();
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
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);
/// Creates a GUID string as a temporary registry key's name

Просмотреть файл

@ -6,7 +6,10 @@ HRESULT RegistryKey::Open(
_In_ const REGSAM sam)
{
Close();
m_path = subkey;
if (subkey != nullptr)
{
m_path = subkey;
}
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));
}
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);
}
@ -102,3 +105,7 @@ HRESULT RegistryKey::DeleteSubKey(PCWSTR 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(
_In_opt_ PCWSTR name,
_In_opt_ std::wstring& value);
_In_opt_ const std::wstring& value);
HRESULT DeleteValue(
_In_opt_ PCWSTR name);
HRESULT DeleteSubKey(
_In_ PCWSTR subkey);
HRESULT DeleteTree(
_In_ PCWSTR subkey);
template<typename TAction> static HRESULT EnumKeyAndDoActionForAllSubkeys(
_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 resolvedExecutableFullPath = m_msixRequest->GetFilePathMappings()->GetExecutablePath(packageInfo->GetExecutableFilePath(), packageInfo->GetPackageFullName().c_str());
std::wstring resolvedExecutableFullPath = packageInfo->GetExecutableFilePath();
std::wstring appUserModelId = m_msixRequest->GetPackageInfo()->GetAppModelUserId();
RETURN_IF_FAILED(CreateLink(resolvedExecutableFullPath.c_str(), filePath.c_str(), L"", appUserModelId.c_str()));

Просмотреть файл

@ -71,7 +71,7 @@ namespace MSIX {
} CATCH_RETURN();
protected:
IMsixFactory* m_factory;
ComPtr<IMsixFactory> m_factory;
std::string m_name;
std::uint64_t m_minVersion;
std::uint64_t m_maxVersion;
@ -107,7 +107,7 @@ namespace MSIX {
} CATCH_RETURN();
protected:
IMsixFactory* m_factory;
ComPtr<IMsixFactory> m_factory;
std::string m_aumid;
};
@ -160,7 +160,7 @@ namespace MSIX {
} CATCH_RETURN();
protected:
IMsixFactory* m_factory;
ComPtr<IMsixFactory> m_factory;
std::map<std::string, std::string> m_stringValues;
std::map<std::string, bool> m_boolValues;
};
@ -209,7 +209,7 @@ namespace MSIX {
} CATCH_RETURN();
protected:
IMsixFactory* m_factory;
ComPtr<IMsixFactory> m_factory;
UINT64 m_minVersion;
std::string m_publisher;
std::string m_name;
@ -255,7 +255,7 @@ namespace MSIX {
HRESULT STDMETHODCALLTYPE GetDocumentElement(IMsixElement** documentElement) noexcept override;
protected:
IMsixFactory* m_factory;
ComPtr<IMsixFactory> m_factory;
ComPtr<IStream> m_stream;
ComPtr<IAppxManifestPackageId> m_packageId;
MSIX_PLATFORMS m_platform = MSIX_PLATFORM_NONE;

Просмотреть файл

@ -89,7 +89,7 @@ namespace MSIX {
const auto& version = identityNode->GetAttributeValue(XmlAttributeName::Version);
const auto& resourceId = identityNode->GetAttributeValue(XmlAttributeName::ResourceId);
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;
});
m_dom->ForEachElementIn(m_dom->GetDocument(), XmlQueryName::Package_Identity, visitor);
@ -103,7 +103,7 @@ namespace MSIX {
auto name = tdfNode->GetAttributeValue(XmlAttributeName::Name);
auto min = tdfNode->GetAttributeValue(XmlAttributeName::MinVersion);
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));
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
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);
*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);
} CATCH_RETURN();
@ -248,7 +248,7 @@ namespace MSIX {
auto name = dependencyNode->GetAttributeValue(XmlAttributeName::Name);
auto publisher = dependencyNode->GetAttributeValue(XmlAttributeName::Publisher);
// 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));
return true;
});
@ -293,7 +293,7 @@ namespace MSIX {
return true;
});
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);
} CATCH_RETURN();
@ -327,7 +327,7 @@ namespace MSIX {
auto appId = applicationNode->GetAttributeValue(XmlAttributeName::Package_Applications_Application_Id);
auto packageIdInternal = context->self->m_packageId.As<IAppxManifestPackageIdInternal>();
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
// or make the AppxManifestApplication have a IXmlElement member to get attributes at will.
context->apps->push_back(std::move(application));