Bug fixes
This commit is contained in:
Родитель
533474b989
Коммит
7308ddf46b
|
@ -0,0 +1,6 @@
|
|||
root = true
|
||||
|
||||
[*.{cpp,h,hpp}]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
trim_trailing_whitespace = true
|
|
@ -0,0 +1,66 @@
|
|||
# MSIXCore
|
||||
Copyright (c) 2018 Microsoft Corp. All rights reserved.
|
||||
|
||||
# Description
|
||||
MSIXCore is a preview for downlevel handler for MSIX. MSIXCore enables MSIX installation on supported Windows operating systems prior to Windows 10 1709 (such as Windows 7 SP1 or Windows Server 2012). The codebase comprises of a Visual Studio solution file that builds msixmgr Application.
|
||||
|
||||
The current preview includes a user interface that will initiate the install of the MSIX package. It will unpack the files in the VFS folder to the local computer, register the application in Add/Remove Programs, places a link to the executable in the Start Menu. The application can then be uninstalled by removing it from Add\Remove Programs.
|
||||
|
||||
The main folder of interest is the MSIXCoreApps directory created in Program Files. MSIXCoreApps contain the extracted packages that have been installed specifically with the directory name being packageFullName.
|
||||
|
||||
(NOTE: Uninstalling the application from Add/Remove Programs in the MSIXCore preview requires the msixmgr application to be installed using the default settings of the MSI project detailed below.)
|
||||
|
||||
# Prerequisites
|
||||
In order to create the MSI project in Visual Studio, the MSI Installer Projects for Visual Studio must be installed -
|
||||
(https://marketplace.visualstudio.com/items?itemName=VisualStudioProductTeam.MicrosoftVisualStudio2017InstallerProjects)
|
||||
|
||||
# Build
|
||||
Clone the msix-packaging repository to a local workspace and build it x64 (see https://github.com/Microsoft/msix-packaging for prerequisites) using the -mt flag, which is necessary to avoid vclibs dependency; this should output the msix.dll, which is consumed by MSIXCore project.
|
||||
|
||||
```
|
||||
Makewin.cmd x64 -mt
|
||||
```
|
||||
Open the msix-packaging/preview/MsixCore/msixmgr.sln file in Visual Studio 2017. Build the msixmgr project in release/x64 to create the msixmgr.exe
|
||||
|
||||
## Using a MSI Setup Project
|
||||
Once the msixmgr project has been built, the msixmgrSetup project can be built.
|
||||
The msixmgrSetup Project creates a .msi package to deploy the msix.dll and msixmgr.exe onto a Windows 7 SP1 or higher machine. The MSI Setup Project will register the specific file type association for the .msix and .appx extensions such that the installer is initiated directly from double-clicking a MSIX or APPX package.
|
||||
|
||||
## MSIX Package Requirements
|
||||
Apps packaged as MSIX must be compatible with the operating system in which they are being deployed. To ensure the app is intended for the operating system the MSIX package manifest must contain a proper TargetDeviceFamily with the name MSIXCore.Desktop and a MinVersion matching the operating system build number. Make sure to also include the relevant Windows 10 1709 and later entry as well so the app will deploy properly on operating systems that natively support MSIX.
|
||||
|
||||
Example for Windows 7 SP1 as a minimum version:
|
||||
|
||||
```
|
||||
<Dependencies>
|
||||
<TargetDeviceFamily Name="MSIXCore.Desktop" MinVersion="6.1.7601.0" MaxVersionTested="10.0.10240.0" />
|
||||
<TargetDeviceFamily Name="Windows.Desktop" MinVersion="10.0.16299.0" MaxVersionTested="10.0.18362.0" />
|
||||
</Dependencies>
|
||||
```
|
||||
|
||||
All MSIXCore.Desktop apps will deploy to the server (with a gui) based operating systems with the same build number. If the app is intended only for a server operating sytem then use the TargetDeviceFamily of MSIXCore.Server. Deployment to Windows Server Core is not supported.
|
||||
|
||||
|
||||
## Using the Command Line
|
||||
The executables can also be manually deployed to a Windows 7 SP1 or higher machine without using the MSI setup project. Place the msix.dll and msixmgr.exe in the same location.
|
||||
Sample packages can be found at msix-packaging/preview/MsixCore/tests; these can be copied over to a Windows 7 SP1 or higher machine and installed, although they may require adding the APPX_TEST_ROOT.cer (in the same tests folder) to the trusted store in order to install. Some of these packages are large and are stored using git lfs (Large File Storage).
|
||||
|
||||
```
|
||||
certutil -addstore root APPX_TEST_ROOT.cer
|
||||
```
|
||||
|
||||
Installation - Using command prompt or PowerShell, navigate to the directory that contains the executables and run the following command to install notepadplus.msix.
|
||||
|
||||
```
|
||||
msixmgr.exe -AddPackage C:\SomeDirectory\notepadplus.msix
|
||||
```
|
||||
|
||||
Uninstallation -
|
||||
|
||||
```
|
||||
msixmgr.exe -RemovePackage notepadplus_0.0.0.1_x64__8wekyb3d8bbwe
|
||||
```
|
||||
|
||||
## Build Status
|
||||
[![Build status](https://microsoft.visualstudio.com/xPlatAppx/_apis/build/status/CIGitHub-for-MsixCoreInstaller)](https://github.com/Microsoft/msix-packaging/releases/tag/MsixCoreInstaller-preview)
|
||||
If succeeded, the built MSI can be downloaded from the release tab.
|
Двоичный файл не отображается.
|
@ -0,0 +1,172 @@
|
|||
// MsixCoreTest.cpp : Defines the exported functions for the DLL application.
|
||||
//
|
||||
|
||||
#include "stdafx.h"
|
||||
#include "MsixCoreTest.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <experimental/filesystem>
|
||||
|
||||
const std::wstring scribbleFileName = L"scribble.appx";
|
||||
const std::wstring scribblePackageFullName = L"ScribbleOleDocumentSample_1.1.0.0_x86__8wekyb3d8bbwe";
|
||||
const std::wstring windows10PackageRoot = L"C:\\Program Files\\Windowsapps";
|
||||
|
||||
MsixCoreTest::~MsixCoreTest()
|
||||
{
|
||||
delete m_packageManager;
|
||||
m_packageManager = nullptr;
|
||||
}
|
||||
|
||||
bool MsixCoreTest::SetupTest()
|
||||
{
|
||||
// This assumes the machine already trusts the certificates that the packages are signed with,
|
||||
// and that the test packages are in the same directory as TE.exe and the dll.
|
||||
// Also if on windows 10, that the machine is sideloading enabled.
|
||||
VERIFY_SUCCEEDED(MsixCoreLib_CreatePackageManager(&m_packageManager));
|
||||
VERIFY_SUCCEEDED(WEX::TestExecution::RuntimeParameters::TryGetValue(L"TestDeploymentDir", m_testDeploymentDir));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MsixCoreTest::CleanupTest()
|
||||
{
|
||||
delete m_packageManager;
|
||||
m_packageManager = nullptr;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MsixCoreTest::SetupMethod()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool MsixCoreTest::CleanupMethod()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
void MsixCoreTest::InstallQueryAndRemoveWithLibTest()
|
||||
{
|
||||
std::wstring packagePath = std::wstring(m_testDeploymentDir) + L"\\" + scribbleFileName;
|
||||
std::wstring expectedPackageFullName = scribblePackageFullName;
|
||||
VERIFY_SUCCEEDED(m_packageManager->AddPackage(packagePath, DeploymentOptions::None));
|
||||
|
||||
std::unique_ptr<std::vector<std::shared_ptr<MsixCoreLib::IInstalledPackage>>> packages;
|
||||
VERIFY_SUCCEEDED(m_packageManager->FindPackages(packages));
|
||||
|
||||
bool found = false;
|
||||
for (auto& package : *packages)
|
||||
{
|
||||
if (expectedPackageFullName == package->GetPackageFullName())
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!MsixCoreLib::IsWindows10RS3OrLater())
|
||||
{
|
||||
VERIFY_IS_TRUE(found);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FindPackages is NOT redirected on windows10, so we should not be able to find it using MsixCore because we MsixCore not actually install it.
|
||||
VERIFY_IS_FALSE(found);
|
||||
|
||||
std::wstring windows10Location = windows10PackageRoot + L"\\" + expectedPackageFullName;
|
||||
VERIFY_IS_TRUE(std::experimental::filesystem::exists(windows10Location));
|
||||
}
|
||||
|
||||
VERIFY_SUCCEEDED(m_packageManager->RemovePackage(expectedPackageFullName));
|
||||
}
|
||||
|
||||
void MsixCoreTest::InstallWithLibAndGetProgressTest()
|
||||
{
|
||||
std::wstring packagePath = std::wstring(m_testDeploymentDir) + L"\\" + scribbleFileName;
|
||||
std::wstring expectedPackageFullName = scribblePackageFullName;
|
||||
|
||||
HANDLE completion = CreateEvent(nullptr, false, false, nullptr);
|
||||
UINT32 receivedCallbacks = 0;
|
||||
auto response = m_packageManager->AddPackageAsync(packagePath, DeploymentOptions::None, [&](const MsixCoreLib::IMsixResponse & sender)
|
||||
{
|
||||
receivedCallbacks++;
|
||||
if (sender.GetStatus() == MsixCoreLib::InstallationStep::InstallationStepCompleted)
|
||||
{
|
||||
SetEvent(completion);
|
||||
}
|
||||
WEX::Logging::Log::Comment(WEX::Common::String().Format(L"Received progress callback: %f", sender.GetPercentage()));
|
||||
});
|
||||
|
||||
// Wait 10 seconds for completion.
|
||||
DWORD waitReturn = WaitForSingleObject(completion, 10000);
|
||||
VERIFY_IS_TRUE(receivedCallbacks > 1);
|
||||
VERIFY_ARE_EQUAL(waitReturn, WAIT_OBJECT_0);
|
||||
|
||||
VERIFY_SUCCEEDED(m_packageManager->RemovePackage(expectedPackageFullName));
|
||||
}
|
||||
|
||||
void MsixCoreTest::InstallIStreamPackageTest()
|
||||
{
|
||||
std::wstring packagePath = std::wstring(m_testDeploymentDir) + L"\\" + scribbleFileName;
|
||||
std::wstring expectedPackageFullName = scribblePackageFullName;
|
||||
|
||||
//Create package stream for scribble.appx
|
||||
IStream *packageStream = NULL;
|
||||
CreateStreamOnFileUTF16(packagePath.c_str(), /*forRead */ true, &packageStream);
|
||||
|
||||
VERIFY_SUCCEEDED(m_packageManager->AddPackage(packageStream, DeploymentOptions::None));
|
||||
|
||||
std::unique_ptr<std::vector<std::shared_ptr<MsixCoreLib::IInstalledPackage>>> packages;
|
||||
VERIFY_SUCCEEDED(m_packageManager->FindPackages(packages));
|
||||
|
||||
bool found = false;
|
||||
for (auto& package : *packages)
|
||||
{
|
||||
if (expectedPackageFullName == package->GetPackageFullName())
|
||||
{
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!MsixCoreLib::IsWindows10RS3OrLater())
|
||||
{
|
||||
VERIFY_IS_TRUE(found);
|
||||
}
|
||||
else
|
||||
{
|
||||
// FindPackages is NOT redirected on windows10, so we should not be able to find it using MsixCore because we MsixCore not actually install it.
|
||||
VERIFY_IS_FALSE(found);
|
||||
|
||||
std::wstring windows10Location = windows10PackageRoot + L"\\" + expectedPackageFullName;
|
||||
VERIFY_IS_TRUE(std::experimental::filesystem::exists(windows10Location));
|
||||
}
|
||||
|
||||
VERIFY_SUCCEEDED(m_packageManager->RemovePackage(expectedPackageFullName));
|
||||
}
|
||||
|
||||
void MsixCoreTest::InstallIStreamAndGetProgressTest()
|
||||
{
|
||||
std::wstring packagePath = std::wstring(m_testDeploymentDir) + L"\\" + scribbleFileName;
|
||||
std::wstring expectedPackageFullName = scribblePackageFullName;
|
||||
|
||||
HANDLE completion = CreateEvent(nullptr, false, false, nullptr);
|
||||
UINT32 receivedCallbacks = 0;
|
||||
|
||||
//Create package stream for scribble.appx
|
||||
IStream *packageStream = NULL;
|
||||
CreateStreamOnFileUTF16(packagePath.c_str(), /*forRead */ true, &packageStream);
|
||||
|
||||
auto response = m_packageManager->AddPackageAsync(packageStream, DeploymentOptions::None, [&](const MsixCoreLib::IMsixResponse & sender)
|
||||
{
|
||||
receivedCallbacks++;
|
||||
if (sender.GetStatus() == MsixCoreLib::InstallationStep::InstallationStepCompleted)
|
||||
{
|
||||
SetEvent(completion);
|
||||
}
|
||||
WEX::Logging::Log::Comment(WEX::Common::String().Format(L"Received progress callback: %f", sender.GetPercentage()));
|
||||
});
|
||||
|
||||
// Wait 10 seconds for completion.
|
||||
DWORD waitReturn = WaitForSingleObject(completion, 10000);
|
||||
VERIFY_IS_TRUE(receivedCallbacks > 1);
|
||||
VERIFY_ARE_EQUAL(waitReturn, WAIT_OBJECT_0);
|
||||
|
||||
VERIFY_SUCCEEDED(m_packageManager->RemovePackage(expectedPackageFullName));
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
#pragma once
|
||||
#include "msixmgrActions.hpp"
|
||||
|
||||
/// This class tests the API surface of the Msixcorelib PackageManager interface.
|
||||
/// This uses the TAEF (test-authoring and execution framework) available from the WDK.
|
||||
/// This builds a test DLL binary, that can be passed into the TAEF te.exe to execute the tests
|
||||
class MsixCoreTest : public WEX::TestClass<MsixCoreTest>
|
||||
{
|
||||
public:
|
||||
TEST_CLASS(MsixCoreTest)
|
||||
|
||||
TEST_CLASS_SETUP(SetupTest)
|
||||
TEST_CLASS_CLEANUP(CleanupTest)
|
||||
|
||||
TEST_METHOD_SETUP(SetupMethod)
|
||||
TEST_METHOD_CLEANUP(CleanupMethod)
|
||||
|
||||
BEGIN_TEST_METHOD(InstallQueryAndRemoveWithLibTest)
|
||||
TEST_METHOD_PROPERTY(L"RunAs", L"ElevatedUser")
|
||||
END_TEST_METHOD()
|
||||
|
||||
BEGIN_TEST_METHOD(InstallWithLibAndGetProgressTest)
|
||||
TEST_METHOD_PROPERTY(L"RunAs", L"ElevatedUser")
|
||||
END_TEST_METHOD()
|
||||
|
||||
BEGIN_TEST_METHOD(InstallIStreamPackageTest)
|
||||
TEST_METHOD_PROPERTY(L"RunAs", L"ElevatedUser")
|
||||
END_TEST_METHOD()
|
||||
|
||||
BEGIN_TEST_METHOD(InstallIStreamAndGetProgressTest)
|
||||
TEST_METHOD_PROPERTY(L"RunAs", L"ElevatedUser")
|
||||
END_TEST_METHOD()
|
||||
|
||||
private:
|
||||
MsixCoreLib::IPackageManager * m_packageManager;
|
||||
WEX::Common::String m_testDeploymentDir;
|
||||
~MsixCoreTest();
|
||||
};
|
|
@ -0,0 +1,31 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28307.645
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "MsixCoreTest", "MsixCoreTest.vcxproj", "{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Debug|x64.Build.0 = Debug|x64
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Debug|x86.Build.0 = Debug|Win32
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Release|x64.ActiveCfg = Release|x64
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Release|x64.Build.0 = Release|x64
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Release|x86.ActiveCfg = Release|Win32
|
||||
{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {44D4F285-51D4-47BF-9918-2C692F010433}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,189 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{F3E46DC2-59C5-4BB9-8D51-344DF6DA25CD}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>MsixCoreTest</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17763.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;MSIXCORETEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>C:\Program Files (x86)\Windows Kits\10\Testing\Development\inc;..\..\msixmgrLib\inc;..\..\..\..\.vs\src\msix;..\..\msixmgrLib</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\..\x64\Release;..\..\..\..\.vs\lib;C:\Program Files (x86)\Windows Kits\10\Testing\Development\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;msi.lib;comctl32.lib;gdiplus.lib;version.lib;taskschd.lib;windowsapp_downlevel.lib;windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>api-ms-win-core-winrt-string-l1-1-0.dll;api-ms-win-core-winrt-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-1.dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;MSIXCORETEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>C:\Program Files (x86)\Windows Kits\10\Testing\Development\inc;..\..\msixmgrLib\inc;..\..\..\..\.vs\src\msix;..\..\msixmgrLib</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>C:\Program Files (x86)\Windows Kits\10\Testing\Development\lib\x86</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;MSIXCORETEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<AdditionalIncludeDirectories>C:\Program Files (x86)\Windows Kits\10\Testing\Development\inc;..\..\msixmgrLib\inc;..\..\..\..\.vs\src\msix;..\..\msixmgrLib</AdditionalIncludeDirectories>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>C:\Program Files (x86)\Windows Kits\10\Testing\Development\lib\x64</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>Use</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;MSIXCORETEST_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>C:\Program Files (x86)\Windows Kits\10\Testing\Development\inc;..\..\msixmgrLib\inc;..\..\..\..\.vs\src\msix;..\..\msixmgrLib</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>C:\Program Files (x86)\Windows Kits\10\Testing\Development\lib\x86</AdditionalLibraryDirectories>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;msi.lib;comctl32.lib;gdiplus.lib;version.lib;taskschd.lib;windowsapp_downlevel.lib;windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>api-ms-win-core-winrt-string-l1-1-0.dll;api-ms-win-core-winrt-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-1.dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="MsixCoreTest.hpp" />
|
||||
<ClInclude Include="stdafx.h" />
|
||||
<ClInclude Include="targetver.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="dllmain.cpp" />
|
||||
<ClCompile Include="MsixCoreTest.cpp" />
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,39 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="stdafx.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="targetver.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="MsixCoreTest.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="stdafx.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="MsixCoreTest.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="dllmain.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,19 @@
|
|||
// dllmain.cpp : Defines the entry point for the DLL application.
|
||||
#include "stdafx.h"
|
||||
|
||||
BOOL APIENTRY DllMain( HMODULE hModule,
|
||||
DWORD ul_reason_for_call,
|
||||
LPVOID lpReserved
|
||||
)
|
||||
{
|
||||
switch (ul_reason_for_call)
|
||||
{
|
||||
case DLL_PROCESS_ATTACH:
|
||||
case DLL_THREAD_ATTACH:
|
||||
case DLL_THREAD_DETACH:
|
||||
case DLL_PROCESS_DETACH:
|
||||
break;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
|
@ -0,0 +1 @@
|
|||
#include "stdafx.h"
|
|
@ -0,0 +1,18 @@
|
|||
// stdafx.h : include file for standard system include files,
|
||||
// or project specific include files that are used frequently, but
|
||||
// are changed infrequently
|
||||
//
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "targetver.h"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers
|
||||
// Windows Header Files
|
||||
#include <windows.h>
|
||||
|
||||
|
||||
|
||||
// reference additional headers your program requires here
|
||||
|
||||
#include <WexTestClass.h>
|
|
@ -0,0 +1,8 @@
|
|||
#pragma once
|
||||
|
||||
// Including SDKDDKVer.h defines the highest available Windows platform.
|
||||
|
||||
// If you wish to build your application for a previous Windows platform, include WinSDKVer.h and
|
||||
// set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h.
|
||||
|
||||
#include <SDKDDKVer.h>
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:e2dc8a94f243a5bfa1db800d0594adf5c88f654b86acd2fc7523da15df61c9dd
|
||||
size 77869300
|
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:247b3038bebeb21a7c2725d6645ef9685dfde46504ae28ccc87733add2a4bb43
|
||||
size 77879122
|
Двоичный файл не отображается.
|
@ -0,0 +1,3 @@
|
|||
version https://git-lfs.github.com/spec/v1
|
||||
oid sha256:dd4b5edd6d65ae8299270b4f19de8307321801ccca53473e0ba54fb2efb1d33a
|
||||
size 6283431
|
|
@ -0,0 +1,136 @@
|
|||
param (
|
||||
[Parameter(ParameterSetName="start", Mandatory=$true)]
|
||||
[switch]$start,
|
||||
|
||||
[Parameter(ParameterSetName="stop", Mandatory=$true)]
|
||||
[switch]$stop,
|
||||
|
||||
[Parameter(ParameterSetName="wait", Mandatory=$true)]
|
||||
[switch]$wait,
|
||||
|
||||
[Parameter(ParameterSetName="stop")]
|
||||
[Parameter(ParameterSetName="wait")]
|
||||
[string]$logFile
|
||||
)
|
||||
|
||||
function DoStart
|
||||
{
|
||||
Write-Host "Starting tracing..."
|
||||
$query = logman query MsixTrace
|
||||
if ($query.count -lt 4)
|
||||
{
|
||||
logman create trace MsixTrace -p "{033321d3-d599-48e0-868d-c59f15901637}" -o c:\msixtrace.etl
|
||||
}
|
||||
|
||||
logman start MsixTrace > $null
|
||||
}
|
||||
|
||||
function LevelToString($level)
|
||||
{
|
||||
switch ($level)
|
||||
{
|
||||
1{ return "Fatal"}
|
||||
2{ return "Error"}
|
||||
3{ return "Warning"}
|
||||
4{ return "Info"}
|
||||
5{ return "Verbose"}
|
||||
default { return "Level $level"}
|
||||
}
|
||||
}
|
||||
|
||||
function DoStop
|
||||
{
|
||||
Write-Host "Stopping tracing..."
|
||||
logman stop MsixTrace > $null
|
||||
|
||||
#find the ETL file that was created
|
||||
$query = logman query MsixTrace
|
||||
$etlFile = ""
|
||||
foreach ($line in $query)
|
||||
{
|
||||
if ($line.endswith(".etl"))
|
||||
{
|
||||
$splits = $line.split()
|
||||
$etlFile = $splits[$splits.count - 1]
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
tracerpt -l $etlFile -o $tempXmlFile > $null
|
||||
|
||||
if (-not $PSBoundParameters.ContainsKey('logFile'))
|
||||
{
|
||||
#logfile parameter not specified, create a file in the current directory
|
||||
$logfile = join-path $pwd ("MsixTrace_{0}.log" -f $now)
|
||||
$warningfile = join-path $pwd ("MsixTraceWarnings_{0}.log" -f $now)
|
||||
}
|
||||
|
||||
#convert XML data into readable text, filtering out "irrelevant" event data
|
||||
$xmlData = [xml] (get-content $tempXmlFile)
|
||||
|
||||
# event 0 should be Header, start at 1
|
||||
for($i=1; $i -lt $xmldata.Events.Event.count; $i++)
|
||||
{
|
||||
$outputLine = $xmldata.Events.Event[$i].system.timecreated.systemtime + ", "
|
||||
$outputLine += LevelToString($xmldata.Events.Event[$i].system.level)
|
||||
$outputLine += ", "
|
||||
$outputLine += $xmlData.Events.Event[$i].renderinginfo.task
|
||||
|
||||
foreach ($data in $xmldata.Events.Event[$i].eventdata.data)
|
||||
{
|
||||
if (-not ($data -eq $null)) # on win7 this is necessary for some reason you can foreach on an null element and get an null object
|
||||
{
|
||||
if ($data.name.tostring().toupper() -eq "HR")
|
||||
{
|
||||
#convert int to hex for HResults (hardcoded fieldname)
|
||||
$outputLine += ", " + $data.name +": " + ("0x{0:x}" -f [System.Convert]::ToInt32($data.'#text'))
|
||||
}
|
||||
else
|
||||
{
|
||||
$outputLine += ", " + $data.name
|
||||
if (($data.'#text'))
|
||||
{
|
||||
$outputLine += ": " + ($data.'#text').trim()
|
||||
}
|
||||
else
|
||||
{
|
||||
$outputLine += ": <null>"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$outputLine >> $logfile
|
||||
if (-not $PSBoundParameters.ContainsKey('logFile') -and $xmldata.Events.Event[$i].system.level -lt 4)
|
||||
{
|
||||
$hasWarnings = $true
|
||||
$outputLine >> $warningfile
|
||||
}
|
||||
}
|
||||
|
||||
write-host "Raw logfile at " $etlFile
|
||||
write-host "Parsed logfile at " $logfile
|
||||
if (-not $PSBoundParameters.ContainsKey('logFile') -and $hasWarnings)
|
||||
{
|
||||
write-host "Logfile contains warnings at " $warningfile
|
||||
}
|
||||
}
|
||||
|
||||
if ($start)
|
||||
{
|
||||
DoStart
|
||||
}
|
||||
elseif ($stop)
|
||||
{
|
||||
DoStop
|
||||
}
|
||||
elseif ($wait)
|
||||
{
|
||||
DoStart
|
||||
Read-Host "Tracing has started, press enter to stop"
|
||||
DoStop
|
||||
}
|
|
@ -0,0 +1,356 @@
|
|||
# Caution: MAY DESTROY TEST ENVIRONMENT!!! Do Not Execute on a production machine
|
||||
# Setup: Copy entire msixtest folder (which includes this script) onto Win7 SP1 or higher test machine. Install the MSI or copy the binaries (msix.dll and msixmgr.exe) to the test machine
|
||||
# assumption: all the files in the msixtest folder are co-located and in the working directory when this script is executed.
|
||||
# assumption: Windows installed to c:\ drive, Program Files etc. folders are in expected places
|
||||
|
||||
# binaryFolder points to the location of the msix.dll and msixmgr.exe binaries
|
||||
# if omitted, assumes you've copied the binaries into the working directory
|
||||
param (
|
||||
[Parameter(ParameterSetName="binaryFolder", Mandatory=$false)]
|
||||
[string]$binaryFolder,
|
||||
[bool]$takeTrace
|
||||
)
|
||||
|
||||
# if not specified, assume binaries are in the current directory
|
||||
if (-not $PSBoundParameters.ContainsKey('binaryFolder'))
|
||||
{
|
||||
$binaryFolder = $pwd
|
||||
}
|
||||
|
||||
$executable = join-path $binaryFolder "msixmgr.exe"
|
||||
|
||||
if (-not (test-path $executable))
|
||||
{
|
||||
write-host ("Cannot run tests: $executable not found. Use -binaryFolder parameter to point to location of msixmgr.exe") -foregroundcolor red
|
||||
exit
|
||||
}
|
||||
|
||||
function writeSuccess
|
||||
{
|
||||
write-host "Success" -foregroundcolor green
|
||||
}
|
||||
|
||||
function writeFail
|
||||
{
|
||||
write-host "FAIL" -foregroundcolor red
|
||||
}
|
||||
|
||||
if ($takeTrace)
|
||||
{
|
||||
& .\msixtrace.ps1 -start
|
||||
}
|
||||
|
||||
$global:testcase = 0
|
||||
|
||||
function ShowTestHeader($testname)
|
||||
{
|
||||
$now = [datetime]::Now.tostring("yyyy-MM-dd hh:mm:ss.fffffff")
|
||||
write-host "$now Testcase $global:testcase : $testname"
|
||||
$global:testcase++
|
||||
}
|
||||
|
||||
New-PSDrive -PSProvider registry -Root HKEY_CLASSES_ROOT -Name HKCR -errorAction SilentlyContinue
|
||||
|
||||
|
||||
ShowTestHeader("Untrusted Package fails")
|
||||
certutil -delstore root 19a0a57c05c3a4884123cd9dccf820ea > $null
|
||||
$output = & $executable -AddPackage notepadplus.msix -quietUx
|
||||
if ($output.tostring().contains("8bad0042"))
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
else
|
||||
{
|
||||
writeFail
|
||||
}
|
||||
|
||||
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 = "$env:allusersprofile\Microsoft\Windows\Start Menu\Programs\notepad++.lnk"
|
||||
if ($output -eq $null)
|
||||
{
|
||||
$notepadExists = (test-path $notepadDir\notepad++.exe)
|
||||
$startlinkExists = (test-path $startlink)
|
||||
if ($notepadExists -and $startlinkExists)
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host ("Expected paths not created: $notepadDir\notepad++.exe exists = $notepadExists, $startLink exists = $startlinkExists")
|
||||
writeFail
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output
|
||||
writeFail
|
||||
}
|
||||
|
||||
ShowTestHeader("FindAllPackages")
|
||||
$output = & $executable -FindAllPackages
|
||||
if ($output -contains "notepadplus_0.0.0.0_x64__8wekyb3d8bbwe" -and -not ($output -contains "fakedoesnotexist_1.0.0.1_x64__8wekyb3d8bbwe"))
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
else
|
||||
{
|
||||
writeFail
|
||||
}
|
||||
|
||||
ShowTestHeader("FindPackage succeeds")
|
||||
$output = & $executable -FindPackage notepadplus_0.0.0.0_x64__8wekyb3d8bbwe
|
||||
if (($output -match "notepadplus_0.0.0.0_x64__8wekyb3d8bbwe").count -gt 0)
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
else
|
||||
{
|
||||
writeFail
|
||||
}
|
||||
|
||||
ShowTestHeader("FindPackage fails to find non-existent package")
|
||||
$output = & $executable -FindPackage fakedoesnotexist_1.0.0.1_x64__8wekyb3d8bbwe
|
||||
if (($output -match "fakedoesnotexist_1.0.0.1_x64__8wekyb3d8bbwe").count -gt 0)
|
||||
{
|
||||
writeFail
|
||||
}
|
||||
else
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
|
||||
ShowTestHeader("Re-installing package succeeds, and overwrites files it should")
|
||||
if (test-path $notepadDir)
|
||||
{
|
||||
# notepad++ is higher version than scilexer, and uninstall.exe is unversioned
|
||||
# if a higher version scilexer exists, installing the package should not overwrite it.
|
||||
copy $notepadDir\notepad++.exe $notepadDir\scilexer.dll
|
||||
# if an unversioned file exists, installing the package *should* overwrite it with a versioned file
|
||||
copy $notepadDir\uninstall.exe $notepadDir\notepad++.exe
|
||||
# if an unmodified, unversioned file exists, installing the package should overwrite it with another unversioned file
|
||||
# explicitly set the created/modified time so that it appears as unmodified. (it is otherwise hard to control the created time)
|
||||
del $notepadDir\readme.txt
|
||||
add-content $notepadDir\readme.txt "newFileCreated and unmodified"
|
||||
(dir $notepadDir\readme.txt).CreationTime = (dir $notepadDir\readme.txt).LastWriteTime
|
||||
# if an unversioned file exists and was modified, installing the package should NOT overwrite it with an unversioned file
|
||||
# explicitly set the modified time so that it appears to be modified
|
||||
add-content $notepadDir\change.log "modification"
|
||||
(dir $notepadDir\change.log).LastWriteTime = (dir $notepadDir\change.log).CreationTime.AddHours(2)
|
||||
|
||||
$beginNotepadExeSize = (dir $notepadDir\notepad++.exe).length
|
||||
$beginSciLexerSize = (dir $notepadDir\scilexer.dll).length
|
||||
$beginReadmeSize = (dir $notepadDir\readme.txt).length
|
||||
$beginChangeLogSize = (dir $notepadDir\change.log).length
|
||||
|
||||
$output = & $executable -AddPackage notepadplus.msix -quietUx
|
||||
if ($output -eq $null)
|
||||
{
|
||||
$afterNotepadExeSize = (dir $notepadDir\notepad++.exe).length
|
||||
$afterSciLexerSize = (dir $notepadDir\scilexer.dll).length
|
||||
$afterReadmeSize = (dir $notepadDir\readme.txt).length
|
||||
$afterChangeLogSize = (dir $notepadDir\change.log).length
|
||||
|
||||
if (($beginNotepadExeSize -ne $afterNotepadExeSize) -and
|
||||
($beginSciLexerSize -eq $afterSciLexerSize) -and
|
||||
($beginReadmeSize -ne $afterReadmeSize) -and
|
||||
($beginChangeLogSize -eq $afterChangeLogSize))
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host ("Reinstall did not overwrite expected files")
|
||||
write-host ("Notepad before/after (should Not equal): $beginNotepadExeSize $afterNotepadExeSize")
|
||||
write-host ("SciLexer before/after (should equal): $beginSciLexerSize $afterSciLexerSize")
|
||||
write-host ("Readme before/after (should Not equal): $beginReadmeSize $afterReadmeSize")
|
||||
write-host ("ChangeLog before/after (should equal): $beginChangeLogSize $afterChangeLogSize")
|
||||
writeFail
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output
|
||||
writeFail
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host ("Previous test failed: expecting notepad to already exist in $notepadDir")
|
||||
writeFail
|
||||
}
|
||||
|
||||
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.0_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
|
||||
}
|
||||
|
||||
ShowTestHeader("Update package removes old package and checks windows.protocol extensions")
|
||||
$output = & $executable -AddPackage VLC-3.0.6_1.0.0.0_x64__8wekyb3d8bbwe-missingsomeftas.msix -quietUx
|
||||
$vlc1msixpath = "C:\program files\msixcoreapps\VLC-3.0.6_1.0.0.0_x64__8wekyb3d8bbwe"
|
||||
$vlcProtocolRegPath = "HKCR:\VLC"
|
||||
if ($output -eq $null)
|
||||
{
|
||||
$vlc1Exists = (test-path $vlc1msixpath)
|
||||
if ($vlc1Exists)
|
||||
{
|
||||
$output = & $executable -AddPackage VLC-3.0.6_2.0.0.0_x64__8wekyb3d8bbwe-withprotocol.msix -quietUx
|
||||
if ($output -eq $null)
|
||||
{
|
||||
$vlc2msixpath = "C:\program files\msixcoreapps\VLC-3.0.6_2.0.0.0_x64__8wekyb3d8bbwe"
|
||||
$vlc1Exists = (test-path $vlc1msixpath)
|
||||
$vlc2Exists = (test-path $vlc2msixpath)
|
||||
$vlcRegExists = (test-path $vlcProtocolRegPath)
|
||||
if ($vlc2Exists -and $vlcRegExists -and -not $vlc1Exists)
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host ("Expected paths not created: $vlc1msixpath exists = $vlc1Exists, $vlc2msixpath exists = $vlc2Exists")
|
||||
writeFail
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output
|
||||
writeFail
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host ("Expected paths not created: $vlc1msixpath exists = $vlc1Exists")
|
||||
writeFail
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output
|
||||
writeFail
|
||||
}
|
||||
|
||||
ShowTestHeader("Remove package with windows.protocol extensions")
|
||||
$output = & $executable -RemovePackage VLC-3.0.6_2.0.0.0_x64__8wekyb3d8bbwe
|
||||
if ($output -eq $null)
|
||||
{
|
||||
$vlcRegExists = (test-path $vlcProtocolRegPath)
|
||||
if (-not $vlcRegExists)
|
||||
{
|
||||
writeSuccess
|
||||
}
|
||||
else
|
||||
{
|
||||
write-host ("Expected paths not deleted: $vlcProtocolRegPath exists = $vlcRegExists")
|
||||
writeFail
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output
|
||||
writeFail
|
||||
}
|
||||
|
||||
# ACDual was taken from the sample mentioned here: https://blogs.windows.com/buildingapps/2017/04/13/com-server-ole-document-support-desktop-bridge/#o481I86oAPPvX428.97
|
||||
ShowTestHeader("Add, launch and remove package with comserver extension")
|
||||
$output = & $executable -AddPackage ACDual.msix -quietUx
|
||||
if ($output -eq $null)
|
||||
{
|
||||
& ".\WindowsFormsApp2.exe"
|
||||
start-sleep 1
|
||||
$appprocess = get-process |? {$_.processname -eq "WindowsFormsApp2" }
|
||||
if ($appprocess -eq $null)
|
||||
{
|
||||
write-host ("Unable to launch WindowsFormsApp2, implies comserver/cominterface registrations were not properly written")
|
||||
writeFail
|
||||
}
|
||||
else
|
||||
{
|
||||
$appprocess | stop-process
|
||||
stop-process -name acdual
|
||||
$output = & $executable -RemovePackage AutoClickComServerSample_1.1.0.0_x86__8wekyb3d8bbwe
|
||||
if ($output -ne $null)
|
||||
{
|
||||
$output
|
||||
write-host ("Add/launch passed, but remove package failed")
|
||||
}
|
||||
writeSuccess
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
$output
|
||||
writeFail
|
||||
}
|
||||
|
||||
if ($takeTrace)
|
||||
{
|
||||
& .\msixtrace.ps1 -stop
|
||||
}
|
||||
|
||||
# manual test: install with UX, launch the package using start menu shortcut, open appwiz.cpl and remove package.
|
|
@ -0,0 +1,58 @@
|
|||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.27703.2026
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msixmgr", "msixmgr\msixmgr.vcxproj", "{AAD31763-5E65-47FD-958A-08E35AB83025}"
|
||||
ProjectSection(ProjectDependencies) = postProject
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064} = {46BE19BD-72E0-4452-9919-33A1F8A5F064}
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{54435603-DBB4-11D2-8724-00A0C9A8B90C}") = "msixmgrSetup", "msixmgrSetup\msixmgrSetup.vdproj", "{C79C79A4-00CE-40C8-BCCE-A5C8F28A4E8D}"
|
||||
EndProject
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msixmgrLib", "msixmgrLib\msixmgrLib.vcxproj", "{46BE19BD-72E0-4452-9919-33A1F8A5F064}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{B0C6E315-832B-4665-B979-4111A8C2F93B}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Debug|x64.Build.0 = Debug|x64
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Debug|x64.Deploy.0 = Debug|x64
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Debug|x86.Build.0 = Debug|Win32
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Debug|x86.Deploy.0 = Debug|Win32
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Release|x64.ActiveCfg = Release|x64
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Release|x64.Build.0 = Release|x64
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Release|x64.Deploy.0 = Release|x64
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Release|x86.ActiveCfg = Release|Win32
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Release|x86.Build.0 = Release|Win32
|
||||
{AAD31763-5E65-47FD-958A-08E35AB83025}.Release|x86.Deploy.0 = Release|Win32
|
||||
{C79C79A4-00CE-40C8-BCCE-A5C8F28A4E8D}.Debug|x64.ActiveCfg = Debug
|
||||
{C79C79A4-00CE-40C8-BCCE-A5C8F28A4E8D}.Debug|x86.ActiveCfg = Debug
|
||||
{C79C79A4-00CE-40C8-BCCE-A5C8F28A4E8D}.Release|x64.ActiveCfg = Release
|
||||
{C79C79A4-00CE-40C8-BCCE-A5C8F28A4E8D}.Release|x86.ActiveCfg = Release
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Debug|x64.Build.0 = Debug|x64
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Debug|x86.ActiveCfg = Debug|Win32
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Debug|x86.Build.0 = Debug|Win32
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Release|x64.ActiveCfg = Release|x64
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Release|x64.Build.0 = Release|x64
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Release|x86.ActiveCfg = Release|Win32
|
||||
{46BE19BD-72E0-4452-9919-33A1F8A5F064}.Release|x86.Build.0 = Release|Win32
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {5E600B53-441D-434D-810A-4F0D87698183}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
|
@ -0,0 +1,98 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "RegistryKey.hpp"
|
||||
#include "AddRemovePrograms.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include "Constants.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR AddRemovePrograms::HandlerName = L"AddRemovePrograms";
|
||||
|
||||
HRESULT AddRemovePrograms::ExecuteForAddRequest()
|
||||
{
|
||||
if (m_msixRequest->GetMsixResponse()->GetIsInstallCancelled())
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
|
||||
}
|
||||
|
||||
RegistryKey uninstallKey;
|
||||
RETURN_IF_FAILED(uninstallKey.Open(HKEY_LOCAL_MACHINE, uninstallKeyPath.c_str(), KEY_WRITE));
|
||||
|
||||
RETURN_IF_FAILED(WriteUninstallKey(uninstallKey));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AddRemovePrograms::ExecuteForRemoveRequest()
|
||||
{
|
||||
RegistryKey uninstallKey;
|
||||
RETURN_IF_FAILED(uninstallKey.Open(HKEY_LOCAL_MACHINE, uninstallKeyPath.c_str(), KEY_WRITE));
|
||||
|
||||
RETURN_IF_FAILED(uninstallKey.DeleteSubKey(m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str()));
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Removed Uninstall key successfully");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AddRemovePrograms::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<AddRemovePrograms> localInstance(new AddRemovePrograms(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AddRemovePrograms::WriteUninstallKey(RegistryKey & uninstallKey)
|
||||
{
|
||||
auto packageInfo = m_msixRequest->GetPackageInfo();
|
||||
std::wstring packageFullName = packageInfo->GetPackageFullName();
|
||||
|
||||
RegistryKey packageKey;
|
||||
RETURN_IF_FAILED(uninstallKey.CreateSubKey(packageFullName.c_str(), KEY_WRITE, &packageKey));
|
||||
|
||||
std::wstring displayName = packageInfo->GetDisplayName();
|
||||
RETURN_IF_FAILED(packageKey.SetStringValue(L"DisplayName", displayName));
|
||||
|
||||
std::wstring directoryPath = m_msixRequest->GetPackageDirectoryPath();
|
||||
RETURN_IF_FAILED(packageKey.SetStringValue(L"InstallLocation", directoryPath));
|
||||
|
||||
WCHAR filePath[MAX_PATH];
|
||||
DWORD lengthCopied = GetModuleFileNameW(nullptr, filePath, MAX_PATH);
|
||||
if (lengthCopied == 0)
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
std::wstring uninstallCommand = filePath + std::wstring(L" -RemovePackage ") + packageFullName;
|
||||
RETURN_IF_FAILED(packageKey.SetStringValue(L"UninstallString", uninstallCommand));
|
||||
|
||||
RETURN_IF_FAILED(packageKey.SetStringValue(L"Publisher", packageInfo->GetPublisherDisplayName()));
|
||||
|
||||
auto versionString = packageInfo->GetVersion();
|
||||
RETURN_IF_FAILED(packageKey.SetStringValue(L"DisplayVersion", versionString));
|
||||
|
||||
std::wstring packageIconString = m_msixRequest->GetPackageDirectoryPath() + L"\\" + packageInfo->GetRelativeExecutableFilePath();
|
||||
RETURN_IF_FAILED(packageKey.SetStringValue(L"DisplayIcon", packageIconString));
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Added Uninstall key successfully",
|
||||
TraceLoggingValue(packageFullName.c_str(), "packageFullName"),
|
||||
TraceLoggingValue(uninstallCommand.c_str(), "uninstallString"),
|
||||
TraceLoggingValue(displayName.c_str(), "displayName"),
|
||||
TraceLoggingValue(directoryPath.c_str(), "installLocation"),
|
||||
TraceLoggingValue(packageInfo->GetPublisher().c_str(), "publisher"),
|
||||
TraceLoggingValue(versionString.c_str(), "displayVersion"),
|
||||
TraceLoggingValue(packageIconString.c_str(), "displayIcon"));
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
#include "RegistryKey.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// Handles adding/removing the entry that allows an app to show up in AddRemovePrograms in the Control Panel (appwiz.cpl)
|
||||
class AddRemovePrograms : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Creates a registry entry in the machine's Uninstall key (HKLM)
|
||||
/// This is read by the control panel's AddRemovePrograms to show packages that can be removed.
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the registry entry from the machine's Uninstall key (HKLM)
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~AddRemovePrograms() {}
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
AddRemovePrograms() {}
|
||||
AddRemovePrograms(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Writes the data to the already opened Uninstall key
|
||||
HRESULT WriteUninstallKey(MsixCoreLib::RegistryKey & uninstallKey);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,617 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
|
||||
#include "RegistryKey.hpp"
|
||||
#include "AutoPlay.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include "CryptoProvider.hpp"
|
||||
#include "Base32Encoding.hpp"
|
||||
#include <StrSafe.h>
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR AutoPlay::HandlerName = L"AutoPlay";
|
||||
|
||||
HRESULT AutoPlay::ExecuteForAddRequest()
|
||||
{
|
||||
for (auto autoPlay = m_autoPlay.begin(); autoPlay != m_autoPlay.end(); ++autoPlay)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessAutoPlayForAdd(*autoPlay));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::ExecuteForRemoveRequest()
|
||||
{
|
||||
for (auto autoPlay = m_autoPlay.begin(); autoPlay != m_autoPlay.end(); ++autoPlay)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessAutoPlayForRemove(*autoPlay));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::ProcessAutoPlayForRemove(AutoPlayObject& autoPlayObject)
|
||||
{
|
||||
RegistryKey explorerKey;
|
||||
RETURN_IF_FAILED(explorerKey.Open(HKEY_LOCAL_MACHINE, explorerRegKeyName.c_str(), KEY_READ | KEY_WRITE));
|
||||
|
||||
RegistryKey handlerRootKey;
|
||||
HRESULT hrCreateSubKey = explorerKey.CreateSubKey(handlerKeyName.c_str(), KEY_READ | KEY_WRITE, &handlerRootKey);
|
||||
if (SUCCEEDED(hrCreateSubKey))
|
||||
{
|
||||
const HRESULT hrDeleteSubKeyTree = handlerRootKey.DeleteTree(autoPlayObject.generatedhandlerName.c_str());
|
||||
if (FAILED(hrDeleteSubKeyTree) && hrDeleteSubKeyTree != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete autoplay generatedHandlerName",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteSubKeyTree, "HR"),
|
||||
TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"),
|
||||
TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName "));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete autoplay reg key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrCreateSubKey, "HR"),
|
||||
TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"),
|
||||
TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName "));
|
||||
}
|
||||
|
||||
if (autoPlayObject.autoPlayType == DesktopAppxContent)
|
||||
{
|
||||
RegistryKey classesRootKey;
|
||||
HRESULT hrCreateSubKey = classesRootKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC);
|
||||
|
||||
if (SUCCEEDED(hrCreateSubKey))
|
||||
{
|
||||
const HRESULT hrDeleteSubKeyTree = classesRootKey.DeleteTree(autoPlayObject.generatedProgId.c_str());
|
||||
if (FAILED(hrDeleteSubKeyTree) && hrDeleteSubKeyTree != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete autoplay progId reg key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteSubKeyTree, "HR"),
|
||||
TraceLoggingValue(autoPlayObject.generatedProgId.c_str(), "GeneratedProgId"),
|
||||
TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName "));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
RegistryKey handleEventRootKey;
|
||||
hrCreateSubKey = explorerKey.CreateSubKey(eventHandlerRootRegKeyName.c_str(), KEY_READ | KEY_WRITE, &handleEventRootKey);
|
||||
|
||||
if (SUCCEEDED(hrCreateSubKey))
|
||||
{
|
||||
RegistryKey handleEventKey;
|
||||
HRESULT hrCreateHandleSubKey = handleEventRootKey.CreateSubKey(autoPlayObject.handleEvent.c_str(), KEY_READ | KEY_WRITE, &handleEventKey);
|
||||
|
||||
if (SUCCEEDED(hrCreateHandleSubKey))
|
||||
{
|
||||
const HRESULT hrDeleteValue = handleEventKey.DeleteValue(autoPlayObject.generatedhandlerName.c_str());
|
||||
if (FAILED(hrDeleteValue) && hrDeleteValue != HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete autoplay handleEventKey generatedHandlerName reg key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteValue, "HR"),
|
||||
TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"),
|
||||
TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName "));
|
||||
return hrDeleteValue;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete autoplay reg key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrCreateHandleSubKey, "HR"),
|
||||
TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"),
|
||||
TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName "));
|
||||
return hrCreateHandleSubKey;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log failure of creating the handleEventRootKey
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete autoplay reg key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrCreateSubKey, "HR"),
|
||||
TraceLoggingValue(autoPlayObject.generatedhandlerName.c_str(), "GeneratedHandlerName"),
|
||||
TraceLoggingValue(m_msixRequest->GetPackageFullName(), "PackageFullName "));
|
||||
return hrCreateSubKey;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::ParseManifest()
|
||||
{
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> extensionEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum));
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
||||
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> extensionElement;
|
||||
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
||||
Text<wchar_t> extensionCategory;
|
||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory));
|
||||
|
||||
if (wcscmp(extensionCategory.Get(), desktopAppXExtensionCategory.c_str()) == 0)
|
||||
{
|
||||
BOOL hc_invokeAction = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> invokeActionEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(invokeActionQuery.c_str(), &invokeActionEnum));
|
||||
RETURN_IF_FAILED(invokeActionEnum->GetHasCurrent(&hc_invokeAction));
|
||||
|
||||
while (hc_invokeAction)
|
||||
{
|
||||
ComPtr<IMsixElement> invokeActionElement;
|
||||
RETURN_IF_FAILED(invokeActionEnum->GetCurrent(&invokeActionElement));
|
||||
|
||||
//desktop appx content element
|
||||
BOOL has_DesktopAppxContent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> desktopAppxContentEnum;
|
||||
RETURN_IF_FAILED(invokeActionElement->GetElements(invokeActionContentQuery.c_str(), &desktopAppxContentEnum));
|
||||
RETURN_IF_FAILED(desktopAppxContentEnum->GetHasCurrent(&has_DesktopAppxContent));
|
||||
|
||||
while (has_DesktopAppxContent)
|
||||
{
|
||||
ComPtr<IMsixElement> desktopAppxContentElement;
|
||||
RETURN_IF_FAILED(desktopAppxContentEnum->GetCurrent(&desktopAppxContentElement));
|
||||
|
||||
AutoPlayObject autoPlay;
|
||||
|
||||
//autoplay type
|
||||
autoPlay.autoPlayType = DesktopAppxContent;
|
||||
|
||||
//action
|
||||
Text<wchar_t> action;
|
||||
RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(actionAttributeName.c_str(), &action));
|
||||
autoPlay.action = action.Get();
|
||||
|
||||
//provider
|
||||
Text<wchar_t> provider;
|
||||
RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(providerAttributeName.c_str(), &provider));
|
||||
autoPlay.provider = provider.Get();
|
||||
|
||||
// Get the App's app user model id
|
||||
autoPlay.appUserModelId = m_msixRequest->GetPackageInfo()->GetId();
|
||||
|
||||
//get the logo
|
||||
autoPlay.defaultIcon = m_msixRequest->GetPackageInfo()->GetPackageDirectoryPath() + m_msixRequest->GetPackageInfo()->GetRelativeLogoPath();
|
||||
|
||||
//verb
|
||||
Text<wchar_t> id;
|
||||
RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(idAttributeName.c_str(), &id));
|
||||
autoPlay.id = id.Get();
|
||||
|
||||
//content event
|
||||
Text<wchar_t> handleEvent;
|
||||
RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(contentEventAttributeName.c_str(), &handleEvent));
|
||||
autoPlay.handleEvent = handleEvent.Get();
|
||||
|
||||
//drop target handler
|
||||
Text<wchar_t> dropTargetHandler;
|
||||
RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(dropTargetHandlerAttributeName.c_str(), &dropTargetHandler));
|
||||
if (dropTargetHandler.Get() != nullptr)
|
||||
{
|
||||
autoPlay.dropTargetHandler = dropTargetHandler.Get();
|
||||
}
|
||||
|
||||
//parameters
|
||||
Text<wchar_t> parameters;
|
||||
RETURN_IF_FAILED(desktopAppxContentElement->GetAttributeValue(parametersAttributeName.c_str(), ¶meters));
|
||||
if (parameters.Get() != nullptr)
|
||||
{
|
||||
autoPlay.parameters = parameters.Get();
|
||||
}
|
||||
|
||||
//GenerateProgId
|
||||
std::wstring uniqueProgId;
|
||||
uniqueProgId.append(id.Get());
|
||||
uniqueProgId.append(handleEvent.Get());
|
||||
|
||||
std::wstring generatedProgId;
|
||||
RETURN_IF_FAILED(GenerateProgId(desktopAppXExtensionCategory.c_str(), uniqueProgId.c_str(), generatedProgId));
|
||||
autoPlay.generatedProgId = generatedProgId.c_str();
|
||||
|
||||
//GenerateHandlerName
|
||||
std::wstring uniqueHandlerName;
|
||||
uniqueHandlerName.append(id.Get());
|
||||
uniqueHandlerName.append(handleEvent.Get());
|
||||
|
||||
std::wstring generatedHandlerName;
|
||||
RETURN_IF_FAILED(GenerateHandlerName(L"DesktopAppXContent", uniqueHandlerName.c_str(), generatedHandlerName));
|
||||
autoPlay.generatedhandlerName = generatedHandlerName.c_str();
|
||||
|
||||
m_autoPlay.push_back(autoPlay);
|
||||
RETURN_IF_FAILED(desktopAppxContentEnum->MoveNext(&has_DesktopAppxContent));
|
||||
|
||||
}
|
||||
|
||||
//desktop appx device element
|
||||
BOOL has_DesktopAppxDevice = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> desktopAppxDeviceEnum;
|
||||
RETURN_IF_FAILED(invokeActionElement->GetElements(invokeActionDeviceQuery.c_str(), &desktopAppxDeviceEnum));
|
||||
RETURN_IF_FAILED(desktopAppxDeviceEnum->GetHasCurrent(&has_DesktopAppxDevice));
|
||||
|
||||
while (has_DesktopAppxDevice)
|
||||
{
|
||||
ComPtr<IMsixElement> desktopAppxDeviceElement;
|
||||
RETURN_IF_FAILED(desktopAppxDeviceEnum->GetCurrent(&desktopAppxDeviceElement));
|
||||
|
||||
AutoPlayObject autoPlay;
|
||||
|
||||
//autoplay type
|
||||
autoPlay.autoPlayType = DesktopAppxDevice;
|
||||
|
||||
//action
|
||||
Text<wchar_t> action;
|
||||
RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(actionAttributeName.c_str(), &action));
|
||||
autoPlay.action = action.Get();
|
||||
|
||||
//provider
|
||||
Text<wchar_t> provider;
|
||||
RETURN_IF_FAILED(invokeActionElement->GetAttributeValue(providerAttributeName.c_str(), &provider));
|
||||
autoPlay.provider = provider.Get();
|
||||
|
||||
// Get the App's app user model id
|
||||
autoPlay.appUserModelId = m_msixRequest->GetPackageInfo()->GetId();
|
||||
|
||||
//get the logo
|
||||
autoPlay.defaultIcon = m_msixRequest->GetPackageInfo()->GetPackageDirectoryPath() + m_msixRequest->GetPackageInfo()->GetRelativeLogoPath();
|
||||
|
||||
//handle event
|
||||
Text<wchar_t> handleEvent;
|
||||
RETURN_IF_FAILED(desktopAppxDeviceElement->GetAttributeValue(deviceEventAttributeName.c_str(), &handleEvent));
|
||||
autoPlay.handleEvent = handleEvent.Get();
|
||||
|
||||
//hwEventHandler
|
||||
Text<wchar_t> hwEventHandler;
|
||||
RETURN_IF_FAILED(desktopAppxDeviceElement->GetAttributeValue(hwEventHandlerAttributeName.c_str(), &hwEventHandler));
|
||||
autoPlay.hwEventHandler = hwEventHandler.Get();
|
||||
|
||||
//init cmd line
|
||||
Text<wchar_t> initCmdLine;
|
||||
RETURN_IF_FAILED(desktopAppxDeviceElement->GetAttributeValue(InitCmdLineAttributeName.c_str(), &initCmdLine));
|
||||
if (initCmdLine.Get() != nullptr)
|
||||
{
|
||||
autoPlay.initCmdLine = initCmdLine.Get();
|
||||
}
|
||||
|
||||
//GenerateHandlerName
|
||||
std::wstring uniqueHandlerName;
|
||||
uniqueHandlerName.append(handleEvent.Get());
|
||||
|
||||
std::wstring generatedHandlerName;
|
||||
RETURN_IF_FAILED(GenerateHandlerName(L"DesktopAppXDevice", uniqueHandlerName.c_str(), generatedHandlerName));
|
||||
autoPlay.generatedhandlerName = generatedHandlerName.c_str();
|
||||
|
||||
m_autoPlay.push_back(autoPlay);
|
||||
RETURN_IF_FAILED(desktopAppxDeviceEnum->MoveNext(&has_DesktopAppxDevice));
|
||||
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(invokeActionEnum->MoveNext(&hc_invokeAction));
|
||||
}
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::GenerateProgId(std::wstring categoryName, std::wstring subCategory, std::wstring & generatedProgId)
|
||||
{
|
||||
std::wstring packageFamilyName = m_msixRequest->GetPackageInfo()->GetPackageFamilyName();
|
||||
std::wstring applicationId = m_msixRequest->GetPackageInfo()->GetApplicationId();
|
||||
|
||||
if (packageFamilyName.empty() || applicationId.empty() || categoryName.empty())
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
// Constants
|
||||
std::wstring AppXPrefix = L"AppX";
|
||||
static const size_t MaxProgIDLength = 39;
|
||||
|
||||
// The maximum number of characters we can have as base32 encoded is 32.
|
||||
// We arrive at this due to the interface presented by the GetChars function;
|
||||
// it is byte only. Ideally, the MaxBase32EncodedStringLength would be
|
||||
// 35 [39 total for the progID, minus 4 for the prefix]. 35 characters means
|
||||
// a maximum of 22 bytes in the digest [ceil(35*5/8)]. However, 22 bytes of digest
|
||||
// actually encodes 36 characters [ceil(22/8*5)]. So we have to reduce the
|
||||
// character count of the encoded string. At 33 characters, this translates into
|
||||
// a 21 byte buffer. A 21 byte buffer translates into 34 characters. Although
|
||||
// this meets the requirements, it is confusing since a 33 character limit
|
||||
// results in a 34 character long encoding. In comparison, a 32 character long
|
||||
// encoding divides evenly and always results in a 20 byte digest.
|
||||
|
||||
static const size_t MaxBase32EncodedStringLength = 32;
|
||||
|
||||
// The maximum number of bytes the digest can be is 20. We arrive at this by:
|
||||
// - The maximum count of characters [without prefix] in the encoding (32):
|
||||
// - multiplied by 5 (since each character in base 32 encoding is based off of 5 bits)
|
||||
// - divided by 8 (to find how many bytes are required)
|
||||
// - The initial plus 7 is to enable integer math (equivalent of writing ceil)
|
||||
|
||||
static const ULONG MaxByteCountOfDigest = (MaxBase32EncodedStringLength * 5 + 7) / 8;
|
||||
|
||||
// Build the progIdSeed by appending the incoming strings
|
||||
// The package family name and the application ID are case sensitive
|
||||
std::wstring tempProgIDBuilder;
|
||||
tempProgIDBuilder.append(packageFamilyName);
|
||||
std::transform(tempProgIDBuilder.begin(), tempProgIDBuilder.end(), tempProgIDBuilder.begin(), ::tolower);
|
||||
tempProgIDBuilder.append(applicationId);
|
||||
|
||||
// The category name and the subcategory are not case sensitive
|
||||
// so we should lower case them
|
||||
|
||||
std::wstring tempLowerBuffer;
|
||||
tempLowerBuffer.assign(categoryName);
|
||||
std::transform(tempLowerBuffer.begin(), tempLowerBuffer.end(), tempLowerBuffer.begin(), ::tolower);
|
||||
tempProgIDBuilder.append(tempLowerBuffer);
|
||||
|
||||
if (!subCategory.empty())
|
||||
{
|
||||
tempLowerBuffer.assign(subCategory);
|
||||
std::transform(tempLowerBuffer.begin(), tempLowerBuffer.end(), tempLowerBuffer.begin(), ::tolower);
|
||||
tempProgIDBuilder.append(tempLowerBuffer);
|
||||
}
|
||||
|
||||
// Create the crypto provider and start the digest / hash
|
||||
AutoPtr<CryptoProvider> cryptoProvider;
|
||||
RETURN_IF_FAILED(CryptoProvider::Create(&cryptoProvider));
|
||||
RETURN_IF_FAILED(cryptoProvider->StartDigest());
|
||||
COMMON_BYTES data = { 0 };
|
||||
data.length = (ULONG)tempProgIDBuilder.size() * sizeof(WCHAR);
|
||||
data.bytes = (LPBYTE)tempProgIDBuilder.c_str();
|
||||
|
||||
RETURN_IF_FAILED(cryptoProvider->DigestData(&data));
|
||||
|
||||
// Grab the crypto digest
|
||||
COMMON_BYTES digest = { 0 };
|
||||
RETURN_IF_FAILED(cryptoProvider->GetDigest(&digest));
|
||||
|
||||
// Ensure the string buffer has enough capacity
|
||||
std::wstring base32EncodedDigest;
|
||||
base32EncodedDigest.resize(MaxBase32EncodedStringLength);
|
||||
|
||||
// Base 32 encode the bytes of the digest and put them into the string buffer
|
||||
ULONG base32EncodedDigestCharCount = 0;
|
||||
RETURN_IF_FAILED(Base32Encoding::GetChars(
|
||||
digest.bytes,
|
||||
std::min(digest.length, MaxByteCountOfDigest),
|
||||
MaxBase32EncodedStringLength,
|
||||
base32EncodedDigest.data(),
|
||||
&base32EncodedDigestCharCount));
|
||||
|
||||
// Set the length of the string buffer to the appropriate value
|
||||
base32EncodedDigest.resize(base32EncodedDigestCharCount);
|
||||
|
||||
// ProgID name is formed by appending the encoded digest to the "AppX" prefix string
|
||||
tempProgIDBuilder.clear();
|
||||
|
||||
tempProgIDBuilder.append(AppXPrefix);
|
||||
tempProgIDBuilder.append(base32EncodedDigest.c_str());
|
||||
|
||||
// Set the return value
|
||||
generatedProgId.assign(tempProgIDBuilder.c_str());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::GenerateHandlerName(LPWSTR type, const std::wstring handlerNameSeed, std::wstring & generatedHandlerName)
|
||||
{
|
||||
// Constants
|
||||
static const ULONG HashedByteCount = 32; // SHA256 generates 256 hashed bits, which is 32 bytes
|
||||
static const ULONG Base32EncodedLength = 52; // SHA256 generates 256 hashed bits, which is 52 characters after base 32 encoding (5 bits per character)
|
||||
|
||||
std::wstring packageFamilyName = m_msixRequest->GetPackageInfo()->GetPackageFamilyName();
|
||||
std::wstring applicationId = m_msixRequest->GetPackageInfo()->GetApplicationId();
|
||||
std::wstring handlerNameBuilder;
|
||||
HCRYPTPROV hProv = NULL;
|
||||
HCRYPTHASH hHash = NULL;
|
||||
DWORD hashLength;
|
||||
BYTE bytes[HashedByteCount];
|
||||
ULONG base32EncodedDigestCharCount;
|
||||
std::wstring base32EncodedDigest;
|
||||
size_t typeLength;
|
||||
|
||||
// First, append Package family name and App Id to a std::wstring variable for convenience - lowercase the values so that the comparison
|
||||
// in future versions or other code will be case insensitive
|
||||
handlerNameBuilder.append(packageFamilyName);
|
||||
handlerNameBuilder.append(applicationId);
|
||||
std::transform(handlerNameBuilder.begin(), handlerNameBuilder.end(), handlerNameBuilder.begin(), ::tolower);
|
||||
|
||||
// Next, SHA256 hash the Package family name and Application Id
|
||||
if (!CryptAcquireContext(&hProv, nullptr, MS_ENH_RSA_AES_PROV, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (!CryptCreateHash(hProv, CALG_SHA_256, 0, 0, &hHash))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (!CryptHashData(hHash, (BYTE *)handlerNameBuilder.c_str(), (DWORD)handlerNameBuilder.size() * sizeof(wchar_t), 0))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
hashLength = HashedByteCount;
|
||||
if (!CryptGetHashParam(hHash, HP_HASHVAL, bytes, &hashLength, 0))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
// Ensure the string has enough capacity for the string and a null terminator
|
||||
base32EncodedDigest.resize(Base32EncodedLength + 1);
|
||||
|
||||
// Base 32 encode the bytes of the digest and put them into the string buffer
|
||||
RETURN_IF_FAILED(Base32Encoding::GetChars(
|
||||
bytes,
|
||||
HashedByteCount,
|
||||
Base32EncodedLength,
|
||||
base32EncodedDigest.data(),
|
||||
&base32EncodedDigestCharCount));
|
||||
|
||||
// Set the length of the string to the appropriate value
|
||||
base32EncodedDigest.resize(base32EncodedDigestCharCount);
|
||||
|
||||
// Find the length of the type string
|
||||
RETURN_IF_FAILED(StringCchLength(type, STRSAFE_MAX_CCH, &typeLength));
|
||||
|
||||
// Finally, construct the string
|
||||
handlerNameBuilder.clear();
|
||||
handlerNameBuilder.append(base32EncodedDigest.c_str());
|
||||
handlerNameBuilder.append(L"!", 1);
|
||||
handlerNameBuilder.append(type, (ULONG)typeLength);
|
||||
handlerNameBuilder.append(L"!", 1);
|
||||
handlerNameBuilder.append(handlerNameSeed);
|
||||
|
||||
// Set the return value
|
||||
generatedHandlerName.assign(handlerNameBuilder.c_str());
|
||||
std::transform(generatedHandlerName.begin(), generatedHandlerName.end(), generatedHandlerName.begin(), ::tolower);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::ProcessAutoPlayForAdd(AutoPlayObject& autoPlayObject)
|
||||
{
|
||||
RegistryKey explorerKey;
|
||||
RETURN_IF_FAILED(explorerKey.Open(HKEY_LOCAL_MACHINE, explorerRegKeyName.c_str(), KEY_READ | KEY_WRITE));
|
||||
|
||||
RegistryKey handlerRootKey;
|
||||
RETURN_IF_FAILED(explorerKey.CreateSubKey(handlerKeyName.c_str(), KEY_WRITE, &handlerRootKey));
|
||||
|
||||
//generatedhandlername
|
||||
RegistryKey handlerKey;
|
||||
RETURN_IF_FAILED(handlerRootKey.CreateSubKey(autoPlayObject.generatedhandlerName.c_str(), KEY_WRITE, &handlerKey));
|
||||
|
||||
// Keys associated with all types
|
||||
RETURN_IF_FAILED(handlerKey.SetStringValue(L"Action", autoPlayObject.action));
|
||||
|
||||
RETURN_IF_FAILED(handlerKey.SetStringValue(L"Provider", autoPlayObject.provider));
|
||||
|
||||
//Get the default icon
|
||||
RETURN_IF_FAILED(handlerKey.SetStringValue(L"DefaultIcon", autoPlayObject.defaultIcon));
|
||||
|
||||
RegistryKey handleEventRootKey;
|
||||
RETURN_IF_FAILED(explorerKey.CreateSubKey(eventHandlerRootRegKeyName.c_str(), KEY_WRITE, &handleEventRootKey));
|
||||
|
||||
RegistryKey handleEventKey;
|
||||
RETURN_IF_FAILED(handleEventRootKey.CreateSubKey(autoPlayObject.handleEvent.c_str(), KEY_WRITE, &handleEventKey));
|
||||
|
||||
RETURN_IF_FAILED(handleEventKey.SetStringValue(autoPlayObject.generatedhandlerName.c_str(), L""));
|
||||
|
||||
RETURN_IF_FAILED(handlerKey.SetUInt32Value(L"DesktopAppX", 1));
|
||||
|
||||
if (autoPlayObject.autoPlayType == DesktopAppxContent)
|
||||
{
|
||||
RETURN_IF_FAILED(handlerKey.SetStringValue(L"InvokeProgID", autoPlayObject.generatedProgId));
|
||||
|
||||
RETURN_IF_FAILED(handlerKey.SetStringValue(L"InvokeVerb", autoPlayObject.id));
|
||||
|
||||
RegistryKey verbRootKey;
|
||||
RETURN_IF_FAILED(BuildVerbKey(autoPlayObject.generatedProgId, autoPlayObject.id, verbRootKey));
|
||||
|
||||
if (autoPlayObject.dropTargetHandler.size() > 0)
|
||||
{
|
||||
RegistryKey dropTargetKey;
|
||||
RETURN_IF_FAILED(verbRootKey.CreateSubKey(dropTargetRegKeyName.c_str(), KEY_WRITE, &dropTargetKey));
|
||||
|
||||
std::wstring regDropTargetHandlerBuilder;
|
||||
regDropTargetHandlerBuilder.append(L"{");
|
||||
regDropTargetHandlerBuilder.append(autoPlayObject.dropTargetHandler);
|
||||
regDropTargetHandlerBuilder.append(L"}");
|
||||
|
||||
RETURN_IF_FAILED(dropTargetKey.SetStringValue(L"CLSID", regDropTargetHandlerBuilder));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(verbRootKey.SetStringValue(L"AppUserModelID", autoPlayObject.appUserModelId));
|
||||
|
||||
std::wstring resolvedExecutableFullPath = m_msixRequest->GetPackageDirectoryPath() + L"\\" + m_msixRequest->GetPackageInfo()->GetRelativeExecutableFilePath();
|
||||
RETURN_IF_FAILED(verbRootKey.SetStringValue(L"PackageRelativeExecutable", resolvedExecutableFullPath));
|
||||
|
||||
RETURN_IF_FAILED(verbRootKey.SetStringValue(L"Parameters", autoPlayObject.parameters));
|
||||
|
||||
RETURN_IF_FAILED(verbRootKey.SetStringValue(L"ContractId", L"Windows.File"));
|
||||
|
||||
RETURN_IF_FAILED(verbRootKey.SetUInt32Value(L"DesiredInitialViewState", 0));
|
||||
|
||||
RETURN_IF_FAILED(verbRootKey.SetStringValue(L"PackageId", m_msixRequest->GetPackageFullName()));
|
||||
|
||||
RegistryKey commandKey;
|
||||
RETURN_IF_FAILED(verbRootKey.CreateSubKey(commandKeyRegName.c_str(), KEY_WRITE, &commandKey));
|
||||
|
||||
RETURN_IF_FAILED(commandKey.SetStringValue(L"DelegateExecute", desktopAppXProtocolDelegateExecuteValue));
|
||||
}
|
||||
}
|
||||
else if (autoPlayObject.autoPlayType == DesktopAppxDevice)
|
||||
{
|
||||
std::wstring regHWEventHandlerBuilder;
|
||||
regHWEventHandlerBuilder.append(L"{");
|
||||
regHWEventHandlerBuilder.append(autoPlayObject.hwEventHandler);
|
||||
regHWEventHandlerBuilder.append(L"}");
|
||||
|
||||
RETURN_IF_FAILED(handlerKey.SetStringValue(L"CLSID", regHWEventHandlerBuilder));
|
||||
|
||||
RETURN_IF_FAILED(handlerKey.SetStringValue(L"InitCmdLine", autoPlayObject.initCmdLine));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::BuildVerbKey(std::wstring generatedProgId, std::wstring id, RegistryKey & verbRootKey)
|
||||
{
|
||||
RegistryKey classesRootKey;
|
||||
RETURN_IF_FAILED(classesRootKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
|
||||
RegistryKey progIdRootKey;
|
||||
RETURN_IF_FAILED(classesRootKey.CreateSubKey(generatedProgId.c_str(), KEY_READ | KEY_WRITE, &progIdRootKey));
|
||||
|
||||
RegistryKey shellRootKey;
|
||||
RETURN_IF_FAILED(progIdRootKey.CreateSubKey(shellKeyName.c_str(), KEY_READ | KEY_WRITE, &shellRootKey));
|
||||
|
||||
RETURN_IF_FAILED(shellRootKey.CreateSubKey(id.c_str(), KEY_READ | KEY_WRITE, &verbRootKey));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AutoPlay::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<AutoPlay > localInstance(new AutoPlay(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
enum AutoPlayType
|
||||
{
|
||||
InvalidAutoPlayType = 0,
|
||||
DesktopAppxContent,
|
||||
DesktopAppxDevice
|
||||
};
|
||||
|
||||
/// the autoplay structure
|
||||
struct AutoPlayObject
|
||||
{
|
||||
std::wstring id;
|
||||
std::wstring action;
|
||||
std::wstring handleEvent;
|
||||
std::wstring provider;
|
||||
std::wstring defaultIcon;
|
||||
std::wstring appUserModelId;
|
||||
std::wstring generatedProgId;
|
||||
std::wstring generatedhandlerName;
|
||||
std::wstring dropTargetHandler;
|
||||
std::wstring parameters;
|
||||
std::wstring hwEventHandler;
|
||||
std::wstring initCmdLine;
|
||||
AutoPlayType autoPlayType;
|
||||
};
|
||||
|
||||
class AutoPlay : IPackageHandler
|
||||
{
|
||||
public:
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
private:
|
||||
MsixRequest * m_msixRequest = nullptr;
|
||||
std::vector<AutoPlayObject> m_autoPlay;
|
||||
|
||||
AutoPlay() {}
|
||||
AutoPlay(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
HRESULT ParseManifest();
|
||||
|
||||
HRESULT ProcessAutoPlayForAdd(AutoPlayObject& autoPlayObject);
|
||||
|
||||
HRESULT ProcessAutoPlayForRemove(AutoPlayObject& autoPlayObject);
|
||||
|
||||
HRESULT GenerateProgId(_In_ std::wstring categoryName, _In_opt_ std::wstring subCategory, _Out_ std::wstring & generatedProgId);
|
||||
|
||||
HRESULT GenerateHandlerName(_In_ LPWSTR type, _In_ const std::wstring handlerNameSeed, _Out_ std::wstring & generatedHandlerName);
|
||||
|
||||
HRESULT BuildVerbKey(_In_ std::wstring generatedProgId, _In_ std::wstring id, _Out_ RegistryKey & verbRootKey);
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
#include <Windows.h>
|
||||
#include <Base32Encoding.hpp>
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <assert.h>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
HRESULT Base32Encoding::GetCharCount(
|
||||
_In_ ULONG byteCount,
|
||||
_Out_ ULONG* outWCharCount)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
assert(outWCharCount != NULL);
|
||||
|
||||
if (byteCount != 0)
|
||||
{
|
||||
static const ULONG bitsPerByte = 8;
|
||||
static const ULONG bitsPerBase32Digit = 5;
|
||||
ULONG bitCount;
|
||||
|
||||
RETURN_IF_FAILED(Multiply(byteCount, bitsPerByte, &bitCount));
|
||||
|
||||
*outWCharCount = bitCount / bitsPerBase32Digit;
|
||||
|
||||
// We need to round up the division. When there aren't exactly enough bits to fill in the final base32 digit,
|
||||
// the remaining bits are set to zero.
|
||||
if (bitCount % bitsPerBase32Digit > 0)
|
||||
{
|
||||
++*outWCharCount;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
*outWCharCount = 0;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
HRESULT Base32Encoding::GetChars(
|
||||
_In_reads_(byteCount) const BYTE* bytes,
|
||||
_In_ ULONG byteCount,
|
||||
_In_ ULONG maxWCharCount,
|
||||
__out_ecount_part_opt(maxWCharCount, *outWCharCount) WCHAR* wchars,
|
||||
__out_range(0, maxWCharCount) ULONG* outWCharCount)
|
||||
{
|
||||
HRESULT hr = S_OK;
|
||||
|
||||
assert(bytes != NULL);
|
||||
assert(wchars != NULL);
|
||||
assert(outWCharCount != NULL);
|
||||
|
||||
ULONG wcharCount = 0;
|
||||
ULONG wcharsIdx = 0;
|
||||
|
||||
RETURN_IF_FAILED(GetCharCount(byteCount, &wcharCount));
|
||||
if (wcharCount > maxWCharCount)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(E_INVALIDARG);
|
||||
}
|
||||
|
||||
// Consider groups of five bytes. This is the smallest number of bytes that has a number of bits
|
||||
// that's evently divisible by five.
|
||||
// Every five bits starting with the most significant of the first byte are made into a base32 value.
|
||||
// Each value is used to index into the alphabet array to produce a base32 digit.
|
||||
// When out of bytes but the corresponding base32 value doesn't yet have five bits, 0 is used.
|
||||
// Normally in these cases a particular number of '=' characters are appended to the resulting base32
|
||||
// string to indicate how many bits didn't come from the actual byte value. For our purposes no
|
||||
// such padding characters are necessary.
|
||||
//
|
||||
// Bytes: aaaaaaaa bbbbbbbb cccccccc dddddddd eeeeeeee
|
||||
// Base32 Values: 000aaaaa 000aaabb 000bbbbb 000bcccc 000ccccd 000ddddd 000ddeee 000eeeee
|
||||
//
|
||||
// Combo of byte a & F8 a & 07 b & 3E b & 01 c & 0F d & 7C d & 03 e & 1F
|
||||
// values except b & C0 c & F0 d & 80 e & E0
|
||||
// for shifting
|
||||
|
||||
// Make sure the following math doesn't overflow.
|
||||
if (maxWCharCount > MaxValue / sizeof(*wchars))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(E_INVALIDARG);
|
||||
}
|
||||
|
||||
DebugFillMemory(wchars, maxWCharCount * sizeof(*wchars));
|
||||
|
||||
for (ULONG byteIdx = 0; byteIdx < byteCount; byteIdx += 5)
|
||||
{
|
||||
BYTE firstByte = bytes[byteIdx];
|
||||
BYTE secondByte = (byteIdx + 1) < byteCount ? bytes[byteIdx + 1] : 0;
|
||||
|
||||
__assume(wcharsIdx + 2 <= wcharCount);
|
||||
wchars[wcharsIdx++] = ValueToDigit((firstByte & 0xF8) >> 3);
|
||||
wchars[wcharsIdx++] = ValueToDigit(
|
||||
((firstByte & 0x07) << 2) | ((secondByte & 0xC0) >> 6));
|
||||
|
||||
if (byteIdx + 1 < byteCount)
|
||||
{
|
||||
BYTE thirdByte = (byteIdx + 2) < byteCount ? bytes[byteIdx + 2] : 0;
|
||||
|
||||
__assume(wcharsIdx + 2 <= wcharCount);
|
||||
wchars[wcharsIdx++] = ValueToDigit((secondByte & 0x3E) >> 1);
|
||||
wchars[wcharsIdx++] = ValueToDigit(
|
||||
((secondByte & 0x01) << 4) | ((thirdByte & 0xF0) >> 4));
|
||||
|
||||
if (byteIdx + 2 < byteCount)
|
||||
{
|
||||
BYTE fourthByte = (byteIdx + 3) < byteCount ? bytes[byteIdx + 3] : 0;
|
||||
|
||||
__assume(wcharsIdx + 1 <= wcharCount);
|
||||
wchars[wcharsIdx++] = ValueToDigit(
|
||||
((thirdByte & 0x0F) << 1) | ((fourthByte & 0x80) >> 7));
|
||||
|
||||
if (byteIdx + 3 < byteCount)
|
||||
{
|
||||
BYTE fifthByte = (byteIdx + 4) < byteCount ? bytes[byteIdx + 4] : 0;
|
||||
|
||||
__assume(wcharsIdx + 2 <= wcharCount);
|
||||
wchars[wcharsIdx++] = ValueToDigit((fourthByte & 0x7C) >> 2);
|
||||
wchars[wcharsIdx++] = ValueToDigit(
|
||||
((fourthByte & 0x03) << 3) | ((fifthByte & 0xE0) >> 5));
|
||||
|
||||
if (byteIdx + 4 < byteCount)
|
||||
{
|
||||
__assume(wcharsIdx + 1 <= wcharCount);
|
||||
wchars[wcharsIdx++] = ValueToDigit(fifthByte & 0x1F);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*outWCharCount = wcharCount;
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
WCHAR Base32Encoding::ValueToDigit(
|
||||
_In_ BYTE value)
|
||||
{
|
||||
assert(value < 0x20);
|
||||
// Douglas Crockford's base 32 alphabet variant is 0-9, A-Z except for i, l, o, and u.
|
||||
static const WCHAR base32DigitList[] = L"0123456789abcdefghjkmnpqrstvwxyz";
|
||||
C_ASSERT(ARRAYSIZE(base32DigitList) == 0x20 + 1); // Plus one due to NULL terminator
|
||||
return base32DigitList[value];
|
||||
}
|
||||
|
||||
HRESULT Base32Encoding::Multiply(ULONG d1, ULONG d2, ULONG * result)
|
||||
{
|
||||
(*result) = d1 * d2;
|
||||
if ((d2 == 0 || d1 <= MaxValue / d2))
|
||||
{
|
||||
return NOERROR;
|
||||
}
|
||||
return HRESULT_FROM_WIN32(ERROR_ARITHMETIC_OVERFLOW);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
|
||||
#define DebugFillMemory(pv, cb)
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class Base32Encoding
|
||||
{
|
||||
public:
|
||||
|
||||
static const ULONG MaxValue = 0xFFFFFFFF;
|
||||
|
||||
static HRESULT GetCharCount(
|
||||
_In_ ULONG byteCount,
|
||||
_Out_ ULONG* outWCharCount);
|
||||
|
||||
// The resulting string is not NULL terminated and
|
||||
// the base32 string produced does not use trailing padding characters.
|
||||
// This means that the result is not decodable unless the original number
|
||||
// of bytes is known.
|
||||
static HRESULT GetChars(
|
||||
_In_reads_(byteCount) const BYTE* bytes,
|
||||
_In_ ULONG byteCount,
|
||||
_In_ ULONG maxWCharCount,
|
||||
__out_ecount_part_opt(maxWCharCount, *outWCharCount) WCHAR* wchars,
|
||||
__out_range(0, maxWCharCount) ULONG* outWCharCount);
|
||||
|
||||
private:
|
||||
static WCHAR ValueToDigit(
|
||||
_In_ BYTE value);
|
||||
|
||||
static HRESULT Multiply(
|
||||
_In_ ULONG d1,
|
||||
_In_ ULONG d2,
|
||||
_Out_ ULONG *result);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,58 @@
|
|||
#include <Windows.h>
|
||||
#include <WinCrypt.h>
|
||||
#include <BcryptLibrary.hpp>
|
||||
#include <assert.h>
|
||||
|
||||
/// @note We intentionally load BCRYPT once on first use and never explicitly unload it;
|
||||
/// it's intentionally kept loaded until process termination unloads it for us.
|
||||
///
|
||||
/// @note We initialize the module handle to nullptr @ compile-time and only Load() modifies it,
|
||||
/// and then only on first need.
|
||||
///
|
||||
/// @note In the exceedingly rare event 2 threads concurrently call Load(), we'd call
|
||||
/// LoadLibrary() twice but producing the same result (the 2nd call finds the DLL already
|
||||
/// in the LoadedModuleTable and returns the same handle after bumping its reference
|
||||
/// count). Thus we don't need any special synchronization to manage this, as worst case
|
||||
/// we'll wind up overwriting the global with the same value on the 2nd load.
|
||||
volatile PVOID MsixCoreLib::BcryptLibrary::bcryptModule = nullptr;
|
||||
FARPROC MsixCoreLib::BcryptLibrary::functions[MsixCoreLib::BcryptLibrary::FunctionCount];
|
||||
PCSTR MsixCoreLib::BcryptLibrary::functionNames[MsixCoreLib::BcryptLibrary::FunctionCount] = {
|
||||
"BCryptOpenAlgorithmProvider",
|
||||
"BCryptCloseAlgorithmProvider",
|
||||
"BCryptGetProperty",
|
||||
"BCryptCreateHash",
|
||||
"BCryptHashData",
|
||||
"BCryptFinishHash",
|
||||
"BCryptDestroyHash"
|
||||
};
|
||||
|
||||
_Check_return_ HRESULT MsixCoreLib::BcryptLibrary::Load()
|
||||
{
|
||||
if (ReadPointerAcquire(&bcryptModule) == nullptr)
|
||||
{
|
||||
HMODULE h = LoadLibraryExW(L"Bcrypt.dll", nullptr, 0);
|
||||
if (h == nullptr)
|
||||
{
|
||||
_Analysis_assume_(GetLastError() != ERROR_SUCCESS);
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
C_ASSERT(ARRAYSIZE(functions) == ARRAYSIZE(functionNames));
|
||||
FARPROC exports[ARRAYSIZE(functions)];
|
||||
for (size_t i = 0; i < ARRAYSIZE(functions); ++i)
|
||||
{
|
||||
exports[i] = GetProcAddress(h, functionNames[i]);
|
||||
if (exports[i] == nullptr)
|
||||
{
|
||||
DWORD lastError = GetLastError();
|
||||
_Analysis_assume_(lastError != ERROR_SUCCESS);
|
||||
assert(FreeLibrary(h));
|
||||
return HRESULT_FROM_WIN32(lastError);
|
||||
}
|
||||
}
|
||||
|
||||
CopyMemory(functions, exports, sizeof(functions));
|
||||
WritePointerRelease(&bcryptModule, h);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
#pragma once
|
||||
#include <assert.h>
|
||||
|
||||
///@note All functions are pretty much passthru to BCRYPT.DLL so prototypes are identical to BCRYPT.H
|
||||
/// and not necessarily compliant with AppX Coding Standards (e.g. Hungarian Notation).
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class BcryptLibrary
|
||||
{
|
||||
private:
|
||||
enum
|
||||
{
|
||||
OpenAlgorithmProviderFunction = 0,
|
||||
CloseAlgorithmProviderFunction,
|
||||
GetPropertyFunction,
|
||||
CreateHashFunction,
|
||||
HashDataFunction,
|
||||
FinishHashFunction,
|
||||
DestroyHashFunction,
|
||||
|
||||
FunctionCount
|
||||
};
|
||||
|
||||
private:
|
||||
static volatile PVOID bcryptModule;
|
||||
static FARPROC functions[FunctionCount];
|
||||
static PCSTR functionNames[FunctionCount];
|
||||
|
||||
public:
|
||||
static _Check_return_ HRESULT Load();
|
||||
|
||||
inline static _Must_inspect_result_ NTSTATUS BCryptOpenAlgorithmProvider(
|
||||
_Out_ BCRYPT_ALG_HANDLE* phAlgorithm,
|
||||
_In_ PCWSTR pszAlgId,
|
||||
_In_opt_ PCWSTR pszImplementation,
|
||||
_In_ ULONG dwFlags)
|
||||
{
|
||||
FARPROC function = functions[OpenAlgorithmProviderFunction];
|
||||
assert(function != NULL);
|
||||
return (((NTSTATUS(WINAPI*) (BCRYPT_ALG_HANDLE*, PCWSTR, PCWSTR, ULONG))
|
||||
(function)))(phAlgorithm, pszAlgId, pszImplementation, dwFlags);
|
||||
}
|
||||
|
||||
inline static NTSTATUS BCryptCloseAlgorithmProvider(
|
||||
_Inout_ BCRYPT_ALG_HANDLE hAlgorithm,
|
||||
_In_ ULONG dwFlags)
|
||||
{
|
||||
FARPROC function = functions[CloseAlgorithmProviderFunction];
|
||||
assert(function != NULL);
|
||||
return (((NTSTATUS(WINAPI*)(BCRYPT_ALG_HANDLE, ULONG))(function)))(hAlgorithm, dwFlags);
|
||||
}
|
||||
|
||||
inline static NTSTATUS BCryptGetProperty(
|
||||
_In_ BCRYPT_HANDLE hObject,
|
||||
_In_ PCWSTR pszProperty,
|
||||
_Out_writes_bytes_to_opt_(cbOutput, *pcbResult) PUCHAR pbOutput,
|
||||
_In_ ULONG cbOutput,
|
||||
_Out_ ULONG* pcbResult,
|
||||
_In_ ULONG dwFlags)
|
||||
{
|
||||
FARPROC function = functions[GetPropertyFunction];
|
||||
assert(function != NULL);
|
||||
#pragma prefast(suppress:26045, "Annotations on function pointers don't work")
|
||||
return (((NTSTATUS(WINAPI*)(BCRYPT_HANDLE, PCWSTR, PUCHAR, ULONG, ULONG*, ULONG))
|
||||
(function)))(hObject, pszProperty, pbOutput, cbOutput, pcbResult, dwFlags);
|
||||
}
|
||||
|
||||
inline static _Must_inspect_result_ NTSTATUS BCryptCreateHash(
|
||||
_Inout_ BCRYPT_ALG_HANDLE hAlgorithm,
|
||||
_Out_ BCRYPT_HASH_HANDLE* phHash,
|
||||
_Out_writes_bytes_all_opt_(cbHashObject) PUCHAR pbHashObject,
|
||||
_In_ ULONG cbHashObject,
|
||||
_In_reads_bytes_opt_(cbSecret) PUCHAR pbSecret,
|
||||
_In_ ULONG cbSecret,
|
||||
_In_ ULONG dwFlags)
|
||||
{
|
||||
FARPROC function = functions[CreateHashFunction];
|
||||
assert(function != NULL);
|
||||
return (((NTSTATUS(WINAPI*)(BCRYPT_ALG_HANDLE, BCRYPT_HASH_HANDLE*, PUCHAR, ULONG, PUCHAR, ULONG, ULONG))
|
||||
(function)))(hAlgorithm, phHash, pbHashObject, cbHashObject, pbSecret, cbSecret, dwFlags);
|
||||
}
|
||||
|
||||
inline static _Must_inspect_result_ NTSTATUS BCryptHashData(
|
||||
_Inout_ BCRYPT_HASH_HANDLE hHash,
|
||||
_In_reads_bytes_(cbInput) PUCHAR pbInput,
|
||||
_In_ ULONG cbInput,
|
||||
_In_ ULONG dwFlags)
|
||||
{
|
||||
FARPROC function = functions[HashDataFunction];
|
||||
assert(function != NULL);
|
||||
return (((NTSTATUS(WINAPI*)(BCRYPT_HASH_HANDLE, PUCHAR, ULONG, ULONG))
|
||||
(function)))(hHash, pbInput, cbInput, dwFlags);
|
||||
}
|
||||
|
||||
inline static _Must_inspect_result_ NTSTATUS BCryptFinishHash(
|
||||
_Inout_ BCRYPT_HASH_HANDLE hHash,
|
||||
_Out_writes_bytes_all_(cbOutput) PUCHAR pbOutput,
|
||||
_In_ ULONG cbOutput,
|
||||
_In_ ULONG dwFlags)
|
||||
{
|
||||
FARPROC function = functions[FinishHashFunction];
|
||||
assert(function != NULL);
|
||||
return (((NTSTATUS(WINAPI*)(BCRYPT_HASH_HANDLE, PUCHAR, ULONG, ULONG))
|
||||
(function)))(hHash, pbOutput, cbOutput, dwFlags);
|
||||
}
|
||||
|
||||
inline static NTSTATUS BCryptDestroyHash(
|
||||
_Inout_ BCRYPT_HASH_HANDLE hHash)
|
||||
{
|
||||
FARPROC function = functions[DestroyHashFunction];
|
||||
assert(function != NULL);
|
||||
return (((NTSTATUS(WINAPI*)(BCRYPT_HASH_HANDLE))(function)))(hHash);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,344 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "ComInterface.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR ComInterface::HandlerName = L"ComInterface";
|
||||
|
||||
HRESULT ComInterface::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
RETURN_IF_FAILED(AddInterfacesAndTypeLibs());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::AddInterfacesAndTypeLibs()
|
||||
{
|
||||
for (auto& comInterface : m_interfaces)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessInterfaceForAddRequest(comInterface));
|
||||
}
|
||||
|
||||
for (auto& typeLib : m_typeLibs)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessTypeLibForAddRequest(typeLib));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::ExecuteForRemoveRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
RETURN_IF_FAILED(RemoveInterfacesAndTypeLibs());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::RemoveInterfacesAndTypeLibs()
|
||||
{
|
||||
for (auto& comInterface : m_interfaces)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessInterfaceForRemoveRequest(comInterface));
|
||||
}
|
||||
|
||||
for (auto& typeLib : m_typeLibs)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessTypeLibForRemoveRequest(typeLib));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT ComInterface::ProcessInterfaceForAddRequest(Interface& comInterface)
|
||||
{
|
||||
RegistryKey interfaceKey;
|
||||
RETURN_IF_FAILED(m_classesKey.CreateSubKey(interfaceKeyName.c_str(), KEY_WRITE, &interfaceKey));
|
||||
|
||||
RegistryKey interfaceIdKey;
|
||||
RETURN_IF_FAILED(interfaceKey.CreateSubKey(comInterface.id.c_str(), KEY_WRITE, &interfaceIdKey));
|
||||
|
||||
RegistryKey proxyStubClsidKey;
|
||||
RETURN_IF_FAILED(interfaceIdKey.CreateSubKey(proxyStubClsidKeyName.c_str(), KEY_WRITE, &proxyStubClsidKey));
|
||||
RETURN_IF_FAILED(proxyStubClsidKey.SetStringValue(L"", comInterface.proxyStubClsid));
|
||||
|
||||
RegistryKey typeLibKey;
|
||||
RETURN_IF_FAILED(interfaceIdKey.CreateSubKey(typeLibKeyName.c_str(), KEY_WRITE, &typeLibKey));
|
||||
RETURN_IF_FAILED(typeLibKey.SetStringValue(L"", comInterface.typeLibId));
|
||||
RETURN_IF_FAILED(typeLibKey.SetStringValue(versionValueName.c_str(), comInterface.typeLibVersion));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::ProcessTypeLibForAddRequest(TypeLib& typeLib)
|
||||
{
|
||||
RegistryKey typeLibKey;
|
||||
RETURN_IF_FAILED(m_classesKey.CreateSubKey(typeLibKeyName.c_str(), KEY_WRITE, &typeLibKey));
|
||||
|
||||
RegistryKey typeLibIdKey;
|
||||
RETURN_IF_FAILED(typeLibKey.CreateSubKey(typeLib.id.c_str(), KEY_WRITE, &typeLibIdKey));
|
||||
|
||||
for (auto version = typeLib.version.begin(); version != typeLib.version.end(); ++version)
|
||||
{
|
||||
RegistryKey versionNumberKey;
|
||||
RETURN_IF_FAILED(typeLibIdKey.CreateSubKey(version->versionNumber.c_str(), KEY_WRITE, &versionNumberKey));
|
||||
if (!version->displayName.empty())
|
||||
{
|
||||
RETURN_IF_FAILED(versionNumberKey.SetStringValue(L"", version->displayName));
|
||||
}
|
||||
|
||||
RegistryKey localeIdKey;
|
||||
RETURN_IF_FAILED(versionNumberKey.CreateSubKey(version->localeId.c_str(), KEY_WRITE, &localeIdKey));
|
||||
|
||||
if (!version->win32Path.empty())
|
||||
{
|
||||
RegistryKey win32Key;
|
||||
RETURN_IF_FAILED(localeIdKey.CreateSubKey(win32KeyName.c_str(), KEY_WRITE, &win32Key));
|
||||
|
||||
std::wstring win32FullPath = FilePathMappings::GetInstance().GetExecutablePath(version->win32Path, m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str());
|
||||
RETURN_IF_FAILED(win32Key.SetStringValue(L"", win32FullPath));
|
||||
}
|
||||
|
||||
if (!version->win64Path.empty())
|
||||
{
|
||||
RegistryKey win64Key;
|
||||
RETURN_IF_FAILED(localeIdKey.CreateSubKey(win64KeyName.c_str(), KEY_WRITE, &win64Key));
|
||||
|
||||
std::wstring win64FullPath = FilePathMappings::GetInstance().GetExecutablePath(version->win64Path, m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str());
|
||||
RETURN_IF_FAILED(win64Key.SetStringValue(L"", win64FullPath));
|
||||
}
|
||||
|
||||
if (!version->libraryFlag.empty())
|
||||
{
|
||||
RegistryKey flagsKey;
|
||||
RETURN_IF_FAILED(versionNumberKey.CreateSubKey(flagsKeyName.c_str(), KEY_WRITE, &flagsKey));
|
||||
RETURN_IF_FAILED(flagsKey.SetStringValue(L"", version->libraryFlag));
|
||||
}
|
||||
|
||||
if (!version->helpDirectory.empty())
|
||||
{
|
||||
RegistryKey helpDirKey;
|
||||
RETURN_IF_FAILED(versionNumberKey.CreateSubKey(helpDirKeyName.c_str(), KEY_WRITE, &helpDirKey));
|
||||
|
||||
std::wstring helpDirFullPath = FilePathMappings::GetInstance().GetExecutablePath(version->helpDirectory, m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str());
|
||||
RETURN_IF_FAILED(helpDirKey.SetStringValue(L"", helpDirFullPath));
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::ProcessInterfaceForRemoveRequest(Interface& comInterface)
|
||||
{
|
||||
RegistryKey interfaceKey;
|
||||
RETURN_IF_FAILED(m_classesKey.OpenSubKey(interfaceKeyName.c_str(), KEY_WRITE, &interfaceKey));
|
||||
|
||||
const HRESULT hrDeleteKey = interfaceKey.DeleteTree(comInterface.id.c_str());
|
||||
if (FAILED(hrDeleteKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete com interface",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteKey, "HR"),
|
||||
TraceLoggingValue(comInterface.id.c_str(), "interface"));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::ProcessTypeLibForRemoveRequest(TypeLib& typeLib)
|
||||
{
|
||||
RegistryKey typeLibKey;
|
||||
RETURN_IF_FAILED(m_classesKey.OpenSubKey(typeLibKeyName.c_str(), KEY_WRITE, &typeLibKey));
|
||||
|
||||
const HRESULT hrDeleteKey = typeLibKey.DeleteTree(typeLib.id.c_str());
|
||||
if (FAILED(hrDeleteKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete com interface typeLib",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteKey, "HR"),
|
||||
TraceLoggingValue(typeLib.id.c_str(), "interface"));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::ParseManifest()
|
||||
{
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> extensionEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum));
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> extensionElement;
|
||||
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
||||
Text<wchar_t> extensionCategory;
|
||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory));
|
||||
|
||||
if (wcscmp(extensionCategory.Get(), comInterfaceCategoryNameInManifest.c_str()) == 0)
|
||||
{
|
||||
BOOL hc_extension = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> comInterfaceEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(interfaceQuery.c_str(), &comInterfaceEnum));
|
||||
RETURN_IF_FAILED(comInterfaceEnum->GetHasCurrent(&hc_extension));
|
||||
|
||||
while (hc_extension)
|
||||
{
|
||||
ComPtr<IMsixElement> comInterfaceElement;
|
||||
RETURN_IF_FAILED(comInterfaceEnum->GetCurrent(&comInterfaceElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseComInterfaceElement(comInterfaceElement.Get()));
|
||||
|
||||
RETURN_IF_FAILED(comInterfaceEnum->MoveNext(&hc_extension));
|
||||
}
|
||||
|
||||
ComPtr<IMsixElementEnumerator> typeLibEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(typeLibQuery.c_str(), &typeLibEnum));
|
||||
RETURN_IF_FAILED(typeLibEnum->GetHasCurrent(&hc_extension));
|
||||
|
||||
while (hc_extension)
|
||||
{
|
||||
ComPtr<IMsixElement> typeLibElement;
|
||||
RETURN_IF_FAILED(typeLibEnum->GetCurrent(&typeLibElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseTypeLibElement(typeLibElement.Get()));
|
||||
|
||||
RETURN_IF_FAILED(typeLibEnum->MoveNext(&hc_extension));
|
||||
}
|
||||
}
|
||||
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::ParseComInterfaceElement(IMsixElement* interfaceElement)
|
||||
{
|
||||
Interface comInterface;
|
||||
std::wstring id;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(interfaceElement, idAttribute, id));
|
||||
comInterface.id = GuidFromManifestId(id);
|
||||
|
||||
std::wstring clsid;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(interfaceElement, proxyStubClsidAttribute, clsid));
|
||||
|
||||
if (!clsid.empty())
|
||||
{
|
||||
comInterface.proxyStubClsid = GuidFromManifestId(clsid);
|
||||
}
|
||||
else
|
||||
{
|
||||
comInterface.proxyStubClsid = proxyStubClsidForUniversalMarshaler;
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> typelibEnum;
|
||||
RETURN_IF_FAILED(interfaceElement->GetElements(typeLibForInterfaceQuery.c_str(), &typelibEnum));
|
||||
RETURN_IF_FAILED(typelibEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> typeLibElement;
|
||||
RETURN_IF_FAILED(typelibEnum->GetCurrent(&typeLibElement));
|
||||
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(typeLibElement.Get(), idAttribute, id));
|
||||
comInterface.typeLibId = GuidFromManifestId(id);
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(typeLibElement.Get(), versionNumberAttribute, comInterface.typeLibVersion));
|
||||
|
||||
RETURN_IF_FAILED(typelibEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
}
|
||||
m_interfaces.push_back(comInterface);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::ParseTypeLibElement(IMsixElement * typeLibElement)
|
||||
{
|
||||
TypeLib typeLib;
|
||||
|
||||
std::wstring id;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(typeLibElement, idAttribute, id));
|
||||
typeLib.id = GuidFromManifestId(id);
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> versionEnum;
|
||||
RETURN_IF_FAILED(typeLibElement->GetElements(versionQuery.c_str(), &versionEnum));
|
||||
RETURN_IF_FAILED(versionEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> versionElement;
|
||||
RETURN_IF_FAILED(versionEnum->GetCurrent(&versionElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseVersionElement(typeLib, versionElement.Get()));
|
||||
|
||||
RETURN_IF_FAILED(versionEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
m_typeLibs.push_back(typeLib);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT ComInterface::ParseVersionElement(TypeLib & typeLib, IMsixElement * versionElement)
|
||||
{
|
||||
Version version;
|
||||
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(versionElement, displayNameAttribute, version.displayName));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(versionElement, versionNumberAttribute, version.versionNumber));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(versionElement, localeIdAttribute, version.localeId));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(versionElement, libraryFlagAttribute, version.libraryFlag));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(versionElement, helpDirectoryAttribute, version.helpDirectory));
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> pathEnum;
|
||||
RETURN_IF_FAILED(versionElement->GetElements(win32PathQuery.c_str(), &pathEnum));
|
||||
RETURN_IF_FAILED(pathEnum->GetHasCurrent(&hasCurrent));
|
||||
if (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> pathElement;
|
||||
RETURN_IF_FAILED(pathEnum->GetCurrent(&pathElement));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(pathElement.Get(), pathAttribute, version.win32Path));
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(versionElement->GetElements(win64PathQuery.c_str(), &pathEnum));
|
||||
RETURN_IF_FAILED(pathEnum->GetHasCurrent(&hasCurrent));
|
||||
if (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> pathElement;
|
||||
RETURN_IF_FAILED(pathEnum->GetCurrent(&pathElement));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(pathElement.Get(), pathAttribute, version.win64Path));
|
||||
}
|
||||
|
||||
typeLib.version.push_back(version);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComInterface::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<ComInterface> localInstance(new ComInterface(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,105 @@
|
|||
#pragma once
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "RegistryKey.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class ComInterface : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Adds the com interface registrations to the per-machine registry
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the com interface registrations from the per-machine registry.
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~ComInterface() {}
|
||||
private:
|
||||
|
||||
struct Interface
|
||||
{
|
||||
std::wstring id;
|
||||
std::wstring proxyStubClsid;
|
||||
std::wstring typeLibId;
|
||||
std::wstring typeLibVersion;
|
||||
};
|
||||
|
||||
struct Version
|
||||
{
|
||||
std::wstring displayName;
|
||||
std::wstring versionNumber;
|
||||
std::wstring localeId;
|
||||
std::wstring libraryFlag;
|
||||
std::wstring helpDirectory;
|
||||
std::wstring win32Path;
|
||||
std::wstring win64Path;
|
||||
};
|
||||
|
||||
struct TypeLib
|
||||
{
|
||||
std::wstring id;
|
||||
std::vector<Version> version;
|
||||
};
|
||||
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
RegistryKey m_classesKey;
|
||||
|
||||
std::vector<Interface> m_interfaces;
|
||||
std::vector<TypeLib> m_typeLibs;
|
||||
|
||||
/// Parses the manifest and fills in the m_interfaces and m_typeLibs data
|
||||
HRESULT ParseManifest();
|
||||
|
||||
/// Parses the manifest element to populate one Interface struct entry of the m_interfaces vector
|
||||
///
|
||||
/// @param interfaceElement - the manifest element representing an interface
|
||||
HRESULT ParseComInterfaceElement(IMsixElement* interfaceElement);
|
||||
|
||||
/// Parses the manifest element to populate one TypeLib struct entry of the m_TypeLibs vector
|
||||
///
|
||||
/// @param typeLibElement - the manifest element representing an typeLib
|
||||
HRESULT ParseTypeLibElement(IMsixElement* typeLibElement);
|
||||
|
||||
/// Parses the manifest element to populate one Version struct for one of the type libs
|
||||
///
|
||||
/// @param typeLib - a TypeLib for which to populate a version
|
||||
/// @param versionElement - the version element from the manifest
|
||||
HRESULT ParseVersionElement(TypeLib & typeLib, IMsixElement * versionElement);
|
||||
|
||||
/// Adds the registry entries corresponding to an Interface
|
||||
///
|
||||
/// @param comInterface - Interface struct representing data from the manifest
|
||||
HRESULT ProcessInterfaceForAddRequest(Interface & comInterface);
|
||||
|
||||
/// Adds the registry entries corresponding to an TypeLib
|
||||
///
|
||||
/// @param typeLib - TypeLib struct representing data from the manifest
|
||||
HRESULT ProcessTypeLibForAddRequest(TypeLib & typeLib);
|
||||
|
||||
/// Removes the registry entries corresponding to an Interface
|
||||
///
|
||||
/// @param comInterface - Interface struct representing data from the manifest
|
||||
HRESULT ProcessInterfaceForRemoveRequest(Interface & comInterface);
|
||||
|
||||
/// Removes the registry entries corresponding to an TypeLib
|
||||
///
|
||||
/// @param typeLib - TypeLib struct representing data from the manifest
|
||||
HRESULT ProcessTypeLibForRemoveRequest(TypeLib & typeLib);
|
||||
|
||||
/// Adds all the registry entries associated with the interfaces and typelibs
|
||||
HRESULT AddInterfacesAndTypeLibs();
|
||||
|
||||
/// Removes all the registry entries associated with the interfaces and typelibs
|
||||
HRESULT RemoveInterfacesAndTypeLibs();
|
||||
|
||||
ComInterface() {}
|
||||
ComInterface(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,717 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "ComServer.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR ComServer::HandlerName = L"ComServer";
|
||||
|
||||
HRESULT ComServer::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
RETURN_IF_FAILED(AddServersAndProgIds());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::AddServersAndProgIds()
|
||||
{
|
||||
for (auto& exeServer : m_exeServers)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessExeServerForAdd(exeServer));
|
||||
}
|
||||
|
||||
for (auto& progId : m_progIds)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessProgIdForAdd(progId));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ExecuteForRemoveRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
RETURN_IF_FAILED(RemoveServersAndProgIds());
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::RemoveServersAndProgIds()
|
||||
{
|
||||
for (auto& exeServer : m_exeServers)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessExeServerForRemove(exeServer));
|
||||
}
|
||||
|
||||
for (auto& progId : m_progIds)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessProgIdForRemove(progId));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ProcessExeServerForAdd(ExeServer& exeServer)
|
||||
{
|
||||
RegistryKey clsidKey;
|
||||
RETURN_IF_FAILED(m_classesKey.CreateSubKey(clsidKeyName.c_str(), KEY_WRITE, &clsidKey));
|
||||
|
||||
for (auto exeServerClass = exeServer.classes.begin(); exeServerClass != exeServer.classes.end(); ++exeServerClass)
|
||||
{
|
||||
RegistryKey classIdKey;
|
||||
RETURN_IF_FAILED(clsidKey.CreateSubKey(exeServerClass->id.c_str(), KEY_WRITE, &classIdKey));
|
||||
RETURN_IF_FAILED(classIdKey.SetStringValue(L"", exeServerClass->displayName));
|
||||
|
||||
std::wstring executableFullPath = FilePathMappings::GetInstance().GetExecutablePath(exeServer.executable, m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str());
|
||||
RegistryKey localServerKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(localServerKeyName.c_str(), KEY_WRITE, &localServerKey));
|
||||
RETURN_IF_FAILED(localServerKey.SetStringValue(L"", executableFullPath));
|
||||
|
||||
RegistryKey progIdKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(progIdKeyName.c_str(), KEY_WRITE, &progIdKey));
|
||||
RETURN_IF_FAILED(progIdKey.SetStringValue(L"", exeServerClass->progId));
|
||||
|
||||
RegistryKey versionIndependentProgIdKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(versionIndependentProgIdKeyName.c_str(), KEY_WRITE, &versionIndependentProgIdKey));
|
||||
RETURN_IF_FAILED(versionIndependentProgIdKey.SetStringValue(L"", exeServerClass->versionIndependentProgId));
|
||||
|
||||
RegistryKey auxUserTypeKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(auxUserTypeKeyName.c_str(), KEY_WRITE, &auxUserTypeKey));
|
||||
if (!exeServerClass->shortDisplayName.empty())
|
||||
{
|
||||
RegistryKey shortDisplayNameKey;
|
||||
RETURN_IF_FAILED(auxUserTypeKey.CreateSubKey(shortDisplayNameKeyName.c_str(), KEY_WRITE, &shortDisplayNameKey));
|
||||
RETURN_IF_FAILED(shortDisplayNameKey.SetStringValue(L"", exeServerClass->shortDisplayName));
|
||||
}
|
||||
|
||||
if (!exeServerClass->enableOleDefaultHandler.empty())
|
||||
{
|
||||
RegistryKey inprocHandlerKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(inprocHandlerKeyName.c_str(), KEY_WRITE, &inprocHandlerKey));
|
||||
RETURN_IF_FAILED(inprocHandlerKey.SetStringValue(L"", defaultInprocHandler));
|
||||
}
|
||||
|
||||
if (!exeServerClass->insertableObject.empty())
|
||||
{
|
||||
RegistryKey insertableObjectKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(insertableObjectKeyName.c_str(), KEY_WRITE, &insertableObjectKey));
|
||||
}
|
||||
|
||||
if (!exeServerClass->autoConvertTo.empty())
|
||||
{
|
||||
RETURN_IF_FAILED(classIdKey.SetStringValue(autoConvertToValueName.c_str(), exeServerClass->autoConvertTo));
|
||||
}
|
||||
|
||||
if (!exeServerClass->implementedCategories.empty())
|
||||
{
|
||||
RegistryKey implementedCategoriesKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(implementedCategoriesKeyName.c_str(), KEY_WRITE, &implementedCategoriesKey));
|
||||
|
||||
for (auto category = exeServerClass->implementedCategories.begin(); category != exeServerClass->implementedCategories.end(); ++category)
|
||||
{
|
||||
RegistryKey categoryKey;
|
||||
RETURN_IF_FAILED(implementedCategoriesKey.CreateSubKey(category->c_str(), KEY_WRITE, &categoryKey));
|
||||
}
|
||||
}
|
||||
|
||||
if (!exeServerClass->conversionReadableFormat.empty())
|
||||
{
|
||||
RegistryKey readableKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(readableKeyName.c_str(), KEY_WRITE, &readableKey));
|
||||
RETURN_IF_FAILED(readableKey.SetStringValue(L"", exeServerClass->conversionReadableFormat));
|
||||
}
|
||||
|
||||
if (!exeServerClass->conversionReadWritableFormat.empty())
|
||||
{
|
||||
RegistryKey readWritableKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(readWritableKeyName.c_str(), KEY_WRITE, &readWritableKey));
|
||||
RETURN_IF_FAILED(readWritableKey.SetStringValue(L"", exeServerClass->conversionReadWritableFormat));
|
||||
}
|
||||
|
||||
if (!exeServerClass->dataFormats.empty() && !exeServerClass->defaultFileDataFormat.empty())
|
||||
{
|
||||
RegistryKey dataFormatsKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(dataFormatsKeyName.c_str(), KEY_WRITE, &dataFormatsKey));
|
||||
|
||||
RegistryKey defaultFileKey;
|
||||
RETURN_IF_FAILED(dataFormatsKey.CreateSubKey(defaultFileKeyName.c_str(), KEY_WRITE, &defaultFileKey));
|
||||
RETURN_IF_FAILED(defaultFileKey.SetStringValue(L"", exeServerClass->defaultFileDataFormat));
|
||||
|
||||
RegistryKey getSetKey;
|
||||
RETURN_IF_FAILED(dataFormatsKey.CreateSubKey(getSetKeyName.c_str(), KEY_WRITE, &getSetKey));
|
||||
|
||||
int i = 0;
|
||||
for (auto dataFormat = exeServerClass->dataFormats.begin(); dataFormat != exeServerClass->dataFormats.end(); ++i, ++dataFormat)
|
||||
{
|
||||
RegistryKey formatKey;
|
||||
RETURN_IF_FAILED(getSetKey.CreateSubKey(std::to_wstring(i).c_str(), KEY_WRITE, &formatKey));
|
||||
RETURN_IF_FAILED(formatKey.SetStringValue(L"", *dataFormat));
|
||||
}
|
||||
}
|
||||
|
||||
if (!exeServerClass->miscStatusOleMiscFlag.empty())
|
||||
{
|
||||
RegistryKey miscStatusKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(miscStatusKeyName.c_str(), KEY_WRITE, &miscStatusKey));
|
||||
RETURN_IF_FAILED(miscStatusKey.SetStringValue(L"", exeServerClass->miscStatusOleMiscFlag));
|
||||
|
||||
for (auto aspect = exeServerClass->miscStatusAspects.begin(); aspect != exeServerClass->miscStatusAspects.end(); ++aspect)
|
||||
{
|
||||
RegistryKey aspectKey;
|
||||
RETURN_IF_FAILED(miscStatusKey.CreateSubKey(aspect->type.c_str(), KEY_WRITE, &aspectKey));
|
||||
RETURN_IF_FAILED(aspectKey.SetStringValue(L"", aspect->oleMiscFlag));
|
||||
}
|
||||
}
|
||||
|
||||
if (!exeServerClass->verbs.empty())
|
||||
{
|
||||
RegistryKey verbKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(verbKeyName.c_str(), KEY_WRITE, &verbKey));
|
||||
|
||||
for (auto verb = exeServerClass->verbs.begin(); verb != exeServerClass->verbs.end(); ++verb)
|
||||
{
|
||||
RegistryKey specificVerbKey;
|
||||
RETURN_IF_FAILED(verbKey.CreateSubKey(verb->id.c_str(), KEY_WRITE, &specificVerbKey));
|
||||
RETURN_IF_FAILED(specificVerbKey.SetStringValue(L"", verb->verb));
|
||||
}
|
||||
}
|
||||
|
||||
if (!exeServerClass->defaultIcon.empty())
|
||||
{
|
||||
RegistryKey defaultIconKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(defaultIconKeyName.c_str(), KEY_WRITE, &defaultIconKey));
|
||||
RETURN_IF_FAILED(defaultIconKey.SetStringValue(L"", exeServerClass->defaultIcon));
|
||||
}
|
||||
|
||||
if (!exeServerClass->toolboxBitmap.empty())
|
||||
{
|
||||
RegistryKey toolboxBitmapKey;
|
||||
RETURN_IF_FAILED(classIdKey.CreateSubKey(toolboxBitmapKeyName.c_str(), KEY_WRITE, &toolboxBitmapKey));
|
||||
RETURN_IF_FAILED(toolboxBitmapKey.SetStringValue(L"", exeServerClass->toolboxBitmap));
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ProcessProgIdForAdd(ProgId& progId)
|
||||
{
|
||||
RegistryKey progIdKey;
|
||||
RETURN_IF_FAILED(m_classesKey.CreateSubKey(progId.id.c_str(), KEY_WRITE, &progIdKey));
|
||||
|
||||
if (!progId.clsid.empty())
|
||||
{
|
||||
RegistryKey clsidKey;
|
||||
RETURN_IF_FAILED(progIdKey.CreateSubKey(clsidKeyName.c_str(), KEY_WRITE, &clsidKey));
|
||||
RETURN_IF_FAILED(clsidKey.SetStringValue(L"", progId.clsid));
|
||||
|
||||
for (auto exeServer = m_exeServers.begin(); exeServer != m_exeServers.end(); ++exeServer)
|
||||
{
|
||||
for (auto exeServerClass = exeServer->classes.begin(); exeServerClass != exeServer->classes.end(); ++exeServerClass)
|
||||
{
|
||||
if (exeServerClass->id.compare(progId.clsid) == 0 && !exeServerClass->insertableObject.empty())
|
||||
{
|
||||
RegistryKey insertableObjectKey;
|
||||
RETURN_IF_FAILED(progIdKey.CreateSubKey(insertableObjectKeyName.c_str(), KEY_WRITE, &insertableObjectKey));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
RegistryKey curVerKey;
|
||||
RETURN_IF_FAILED(progIdKey.CreateSubKey(curVerKeyName.c_str(), KEY_WRITE, &curVerKey));
|
||||
RETURN_IF_FAILED(curVerKey.SetStringValue(L"", progId.currentVersion));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ProcessExeServerForRemove(ExeServer& exeServer)
|
||||
{
|
||||
RegistryKey clsidKey;
|
||||
RETURN_IF_FAILED(m_classesKey.OpenSubKey(clsidKeyName.c_str(), KEY_WRITE, &clsidKey));
|
||||
|
||||
for (auto exeServerClass = exeServer.classes.begin(); exeServerClass != exeServer.classes.end(); ++exeServerClass)
|
||||
{
|
||||
const HRESULT hrDeleteKey = clsidKey.DeleteTree(exeServerClass->id.c_str());
|
||||
if (FAILED(hrDeleteKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete clsid for com exeServer",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteKey, "HR"),
|
||||
TraceLoggingValue(exeServerClass->id.c_str(), "CLSID"));
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ProcessProgIdForRemove(ProgId& progId)
|
||||
{
|
||||
const HRESULT hrDeleteKey = m_classesKey.DeleteTree(progId.id.c_str());
|
||||
if (FAILED(hrDeleteKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete progId for comServer",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteKey, "HR"),
|
||||
TraceLoggingValue(progId.id.c_str(), "CLSID"));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseManifest()
|
||||
{
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> extensionEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum));
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> extensionElement;
|
||||
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
||||
Text<wchar_t> extensionCategory;
|
||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory));
|
||||
|
||||
if (wcscmp(extensionCategory.Get(), ComServerCategoryNameInManifest.c_str()) == 0)
|
||||
{
|
||||
BOOL hc_exeServer = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> exeServerEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(exeServerQuery.c_str(), &exeServerEnum));
|
||||
RETURN_IF_FAILED(exeServerEnum->GetHasCurrent(&hc_exeServer));
|
||||
|
||||
while (hc_exeServer)
|
||||
{
|
||||
ComPtr<IMsixElement> exeServerElement;
|
||||
RETURN_IF_FAILED(exeServerEnum->GetCurrent(&exeServerElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseExeServerElement(exeServerElement.Get()));
|
||||
|
||||
RETURN_IF_FAILED(exeServerEnum->MoveNext(&hc_exeServer));
|
||||
}
|
||||
|
||||
BOOL hc_progId = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> progIdEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(progIdQuery.c_str(), &progIdEnum));
|
||||
RETURN_IF_FAILED(progIdEnum->GetHasCurrent(&hc_progId));
|
||||
|
||||
while (hc_progId)
|
||||
{
|
||||
ComPtr<IMsixElement> progIdElement;
|
||||
RETURN_IF_FAILED(progIdEnum->GetCurrent(&progIdElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseProgIdElement(progIdElement.Get()));
|
||||
|
||||
RETURN_IF_FAILED(progIdEnum->MoveNext(&hc_progId));
|
||||
}
|
||||
}
|
||||
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseExeServerElement(IMsixElement* exeServerElement)
|
||||
{
|
||||
ExeServer exeServer;
|
||||
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(exeServerElement, executableAttribute, exeServer.executable));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(exeServerElement, argumentsAttribute, exeServer.arguments));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(exeServerElement, displayNameAttribute, exeServer.displayName));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(exeServerElement, launchAndActivationPermissionAttribute, exeServer.launchAndActivationPermission));
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> classesEnum;
|
||||
RETURN_IF_FAILED(exeServerElement->GetElements(exeServerClassQuery.c_str(), &classesEnum));
|
||||
RETURN_IF_FAILED(classesEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> classElement;
|
||||
RETURN_IF_FAILED(classesEnum->GetCurrent(&classElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseExeServerClassElement(exeServer, classElement.Get()));
|
||||
RETURN_IF_FAILED(classesEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
m_exeServers.push_back(exeServer);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseProgIdElement(IMsixElement* progIdElement)
|
||||
{
|
||||
ProgId progId;
|
||||
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(progIdElement, idAttribute, progId.id));
|
||||
std::wstring id;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(progIdElement, clsidAttribute, id));
|
||||
if (!id.empty())
|
||||
{
|
||||
progId.clsid = GuidFromManifestId(id);
|
||||
}
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(progIdElement, currentVersionAttribute, progId.currentVersion));
|
||||
|
||||
m_progIds.push_back(progId);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseExeServerClassElement(ExeServer & exeServer, IMsixElement * classElement)
|
||||
{
|
||||
ExeServerClass exeServerClass;
|
||||
|
||||
std::wstring id;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, idAttribute, id));
|
||||
exeServerClass.id = GuidFromManifestId(id);
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, displayNameAttribute, exeServerClass.displayName));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, progIdAttribute, exeServerClass.progId));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, versionIndependentProgIdAttribute, exeServerClass.versionIndependentProgId));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, insertableObjectAttribute, exeServerClass.insertableObject));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, shortDisplayNameAttribute, exeServerClass.shortDisplayName));
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, enableOleDefaultHandlerAttribute, exeServerClass.enableOleDefaultHandler));
|
||||
|
||||
std::wstring autoConvertToId;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(classElement, autoConvertToAttribute, autoConvertToId));
|
||||
if (!autoConvertToId.empty())
|
||||
{
|
||||
exeServerClass.autoConvertTo = GuidFromManifestId(autoConvertToId);
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(ParseImplementedCategories(classElement, exeServerClass));
|
||||
RETURN_IF_FAILED(ParseConversionFormats(classElement, readableFormatsQuery, exeServerClass.conversionReadableFormat));
|
||||
RETURN_IF_FAILED(ParseConversionFormats(classElement, readWritableFormatsQuery, exeServerClass.conversionReadWritableFormat));
|
||||
RETURN_IF_FAILED(ParseDataFormats(classElement, exeServerClass));
|
||||
RETURN_IF_FAILED(ParseMiscStatus(classElement, exeServerClass));
|
||||
RETURN_IF_FAILED(ParseVerbs(classElement, exeServerClass));
|
||||
RETURN_IF_FAILED(ParseDefaultIcon(classElement, exeServerClass));
|
||||
RETURN_IF_FAILED(ParseToolboxBitmap(classElement, exeServerClass));
|
||||
|
||||
exeServer.classes.push_back(exeServerClass);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseImplementedCategories(IMsixElement* classElement, ExeServerClass& exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> implementedCategoriesEnum;
|
||||
RETURN_IF_FAILED(classElement->GetElements(implementedCategoriesQuery.c_str(), &implementedCategoriesEnum));
|
||||
RETURN_IF_FAILED(implementedCategoriesEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> categoryElement;
|
||||
RETURN_IF_FAILED(implementedCategoriesEnum->GetCurrent(&categoryElement));
|
||||
|
||||
std::wstring categoryId;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(categoryElement.Get(), idAttribute, categoryId));
|
||||
exeServerClass.implementedCategories.push_back(GuidFromManifestId(categoryId));
|
||||
|
||||
RETURN_IF_FAILED(implementedCategoriesEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseConversionFormats(IMsixElement* rootElement, const std::wstring & formatsQuery, std::wstring & formats)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> readableFormatsEnum;
|
||||
RETURN_IF_FAILED(rootElement->GetElements(formatsQuery.c_str(), &readableFormatsEnum));
|
||||
RETURN_IF_FAILED(readableFormatsEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> formatElement;
|
||||
RETURN_IF_FAILED(readableFormatsEnum->GetCurrent(&formatElement));
|
||||
|
||||
std::wstring formatName;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(formatElement.Get(), formatNameAttribute, formatName));
|
||||
|
||||
std::wstring standardFormat;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(formatElement.Get(), standardFormatAttribute, standardFormat));
|
||||
|
||||
if (!formats.empty())
|
||||
{
|
||||
formats += L",";
|
||||
}
|
||||
|
||||
if (!formatName.empty())
|
||||
{
|
||||
formats += formatName;
|
||||
}
|
||||
else
|
||||
{
|
||||
formats += standardFormat;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(readableFormatsEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseDataFormats(IMsixElement* classElement, ExeServerClass& exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> dataFormatsEnum;
|
||||
RETURN_IF_FAILED(classElement->GetElements(dataFormatsQuery.c_str(), &dataFormatsEnum));
|
||||
RETURN_IF_FAILED(dataFormatsEnum->GetHasCurrent(&hasCurrent));
|
||||
if (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> dataFormatsElement;
|
||||
RETURN_IF_FAILED(dataFormatsEnum->GetCurrent(&dataFormatsElement));
|
||||
|
||||
std::wstring defaultFormat;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(dataFormatsElement.Get(), defaultFormatNameAttribute, defaultFormat));
|
||||
if (defaultFormat.empty())
|
||||
{
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(dataFormatsElement.Get(), defaultStandardFormatAttribute, defaultFormat));
|
||||
}
|
||||
exeServerClass.defaultFileDataFormat = defaultFormat;
|
||||
|
||||
RETURN_IF_FAILED(ParseDataFormat(dataFormatsElement.Get(), exeServerClass))
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// Converts the DataFormat direction from the Manifest type to the registry key name for the direction, following the DATADIR enum.
|
||||
HRESULT ConvertDataFormatDirectionStringToRegistryFlag(std::wstring direction, std::wstring& directionRegistryFlag)
|
||||
{
|
||||
if (direction.compare(L"Get") == 0)
|
||||
{
|
||||
directionRegistryFlag = L"1";
|
||||
return S_OK;
|
||||
}
|
||||
else if (direction.compare(L"Set") == 0)
|
||||
{
|
||||
directionRegistryFlag = L"2";
|
||||
return S_OK;
|
||||
}
|
||||
else if (direction.compare(L"GetAndSet") == 0)
|
||||
{
|
||||
directionRegistryFlag = L"3";
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseDataFormat(IMsixElement* dataFormatsElement, ExeServerClass& exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> dataFormatEnum;
|
||||
RETURN_IF_FAILED(dataFormatsElement->GetElements(dataFormatQuery.c_str(), &dataFormatEnum));
|
||||
RETURN_IF_FAILED(dataFormatEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> dataFormatElement;
|
||||
RETURN_IF_FAILED(dataFormatEnum->GetCurrent(&dataFormatElement));
|
||||
|
||||
std::wstring aspectFlag;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(dataFormatElement.Get(), aspectFlagAttribute, aspectFlag));
|
||||
|
||||
std::wstring mediumFlag;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(dataFormatElement.Get(), mediumFlagAttribute, mediumFlag));
|
||||
|
||||
std::wstring direction;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(dataFormatElement.Get(), directionAttribute, direction));
|
||||
|
||||
std::wstring directionRegistryFlag;
|
||||
RETURN_IF_FAILED(ConvertDataFormatDirectionStringToRegistryFlag(direction, directionRegistryFlag));
|
||||
|
||||
std::wstring formatName;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(dataFormatElement.Get(), formatNameAttribute, formatName));
|
||||
if (formatName.empty())
|
||||
{
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(dataFormatElement.Get(), standardFormatAttribute, formatName));
|
||||
}
|
||||
|
||||
std::wstring dataFormat = formatName + L',' + aspectFlag + L',' + mediumFlag + L',' + directionRegistryFlag;
|
||||
exeServerClass.dataFormats.push_back(dataFormat);
|
||||
|
||||
RETURN_IF_FAILED(dataFormatEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseMiscStatus(IMsixElement* classElement, ExeServerClass& exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> miscStatusEnum;
|
||||
RETURN_IF_FAILED(classElement->GetElements(miscStatusQuery.c_str(), &miscStatusEnum));
|
||||
RETURN_IF_FAILED(miscStatusEnum->GetHasCurrent(&hasCurrent));
|
||||
if (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> miscStatusElement;
|
||||
RETURN_IF_FAILED(miscStatusEnum->GetCurrent(&miscStatusElement));
|
||||
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(miscStatusElement.Get(), oleMiscFlagAttribute, exeServerClass.miscStatusOleMiscFlag));
|
||||
RETURN_IF_FAILED(ParseAspects(miscStatusElement.Get(), exeServerClass));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// Converts the aspect type from manifest string to the registry key name for the aspect type, following the DVASPECT enum.
|
||||
HRESULT ConvertAspectTypeToRegistryName(std::wstring aspect, std::wstring& aspectRegistryName)
|
||||
{
|
||||
if (aspect.compare(L"Content") == 0)
|
||||
{
|
||||
aspectRegistryName = L"1";
|
||||
return S_OK;
|
||||
}
|
||||
else if (aspect.compare(L"Thumbnail") == 0)
|
||||
{
|
||||
aspectRegistryName = L"2";
|
||||
return S_OK;
|
||||
}
|
||||
else if (aspect.compare(L"Icon") == 0)
|
||||
{
|
||||
aspectRegistryName = L"4";
|
||||
return S_OK;
|
||||
}
|
||||
else if (aspect.compare(L"DocPrint") == 0)
|
||||
{
|
||||
aspectRegistryName = L"8";
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseAspects(IMsixElement* miscStatusElement, ExeServerClass& exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> aspectEnum;
|
||||
RETURN_IF_FAILED(miscStatusElement->GetElements(aspectQuery.c_str(), &aspectEnum));
|
||||
RETURN_IF_FAILED(aspectEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> aspectElement;
|
||||
RETURN_IF_FAILED(aspectEnum->GetCurrent(&aspectElement));
|
||||
|
||||
Aspect aspect;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(aspectElement.Get(), oleMiscFlagAttribute, aspect.oleMiscFlag));
|
||||
|
||||
std::wstring aspectType;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(aspectElement.Get(), typeAttribute, aspectType));
|
||||
RETURN_IF_FAILED(ConvertAspectTypeToRegistryName(aspectType, aspect.type));
|
||||
|
||||
exeServerClass.miscStatusAspects.push_back(aspect);
|
||||
RETURN_IF_FAILED(aspectEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseVerbs(IMsixElement* classElement, ExeServerClass& exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> verbEnum;
|
||||
RETURN_IF_FAILED(classElement->GetElements(comVerbQuery.c_str(), &verbEnum));
|
||||
RETURN_IF_FAILED(verbEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> verbElement;
|
||||
RETURN_IF_FAILED(verbEnum->GetCurrent(&verbElement));
|
||||
|
||||
Verb verb;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(verbElement.Get(), idAttribute, verb.id));
|
||||
|
||||
std::wstring displayName;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(verbElement.Get(), displayNameAttribute, displayName));
|
||||
|
||||
std::wstring appendMenuFlag;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(verbElement.Get(), appendMenuFlagAttribute, appendMenuFlag));
|
||||
|
||||
std::wstring oleVerbFlag;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(verbElement.Get(), oleVerbFlagAttribute, oleVerbFlag));
|
||||
|
||||
verb.verb = displayName + L"," + appendMenuFlag + L"," + oleVerbFlag;
|
||||
|
||||
exeServerClass.verbs.push_back(verb);
|
||||
RETURN_IF_FAILED(verbEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseDefaultIcon(IMsixElement * classElement, ExeServerClass & exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> defaultIconEnum;
|
||||
RETURN_IF_FAILED(classElement->GetElements(defaultIconQuery.c_str(), &defaultIconEnum));
|
||||
RETURN_IF_FAILED(defaultIconEnum->GetHasCurrent(&hasCurrent));
|
||||
if (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> defaultIconElement;
|
||||
RETURN_IF_FAILED(defaultIconEnum->GetCurrent(&defaultIconElement));
|
||||
|
||||
std::wstring path;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(defaultIconElement.Get(), pathAttribute, path));
|
||||
|
||||
std::wstring resourceIndex;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(defaultIconElement.Get(), resourceIndexAttribute, resourceIndex));
|
||||
|
||||
std::wstring resolvedPath = FilePathMappings::GetInstance().GetExecutablePath(path, m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str());
|
||||
|
||||
exeServerClass.defaultIcon = std::wstring(L"\"") + resolvedPath + std::wstring(L"\",") + resourceIndex;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::ParseToolboxBitmap(IMsixElement * classElement, ExeServerClass & exeServerClass)
|
||||
{
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> toolboxBitmapEnum;
|
||||
RETURN_IF_FAILED(classElement->GetElements(toolboxBitmapQuery.c_str(), &toolboxBitmapEnum));
|
||||
RETURN_IF_FAILED(toolboxBitmapEnum->GetHasCurrent(&hasCurrent));
|
||||
if (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> toolboxBitmapElement;
|
||||
RETURN_IF_FAILED(toolboxBitmapEnum->GetCurrent(&toolboxBitmapElement));
|
||||
|
||||
std::wstring path;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(toolboxBitmapElement.Get(), pathAttribute, path));
|
||||
|
||||
std::wstring resourceIndex;
|
||||
RETURN_IF_FAILED(GetAttributeValueFromElement(toolboxBitmapElement.Get(), resourceIndexAttribute, resourceIndex));
|
||||
|
||||
std::wstring resolvedPath = FilePathMappings::GetInstance().GetExecutablePath(path, m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str());
|
||||
|
||||
exeServerClass.toolboxBitmap = std::wstring(L"\"") + resolvedPath + std::wstring(L"\",") + resourceIndex;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ComServer::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<ComServer> localInstance(new ComServer(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,184 @@
|
|||
#pragma once
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "RegistryKey.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class ComServer : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Adds the com server registrations to the per-machine registry
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the com server registrations from the per-machine registry.
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~ComServer() {}
|
||||
private:
|
||||
struct Aspect
|
||||
{
|
||||
std::wstring type;
|
||||
std::wstring oleMiscFlag;
|
||||
};
|
||||
|
||||
struct Verb
|
||||
{
|
||||
std::wstring id;
|
||||
std::wstring verb;
|
||||
};
|
||||
|
||||
struct ExeServerClass
|
||||
{
|
||||
std::wstring id;
|
||||
std::wstring displayName;
|
||||
std::wstring enableOleDefaultHandler;
|
||||
std::wstring progId;
|
||||
std::wstring versionIndependentProgId;
|
||||
std::wstring autoConvertTo;
|
||||
std::wstring insertableObject;
|
||||
std::wstring shortDisplayName;
|
||||
std::vector<std::wstring> implementedCategories;
|
||||
std::wstring conversionReadableFormat;
|
||||
std::wstring conversionReadWritableFormat;
|
||||
std::wstring defaultFileDataFormat;
|
||||
std::vector<std::wstring> dataFormats;
|
||||
std::vector<Aspect> miscStatusAspects;
|
||||
std::wstring miscStatusOleMiscFlag;
|
||||
std::vector<Verb> verbs;
|
||||
std::wstring defaultIcon;
|
||||
std::wstring toolboxBitmap;
|
||||
};
|
||||
|
||||
struct ExeServer
|
||||
{
|
||||
std::wstring executable;
|
||||
std::wstring arguments;
|
||||
std::wstring displayName;
|
||||
std::wstring launchAndActivationPermission;
|
||||
std::vector<ExeServerClass> classes;
|
||||
};
|
||||
|
||||
struct ProgId
|
||||
{
|
||||
std::wstring id;
|
||||
std::wstring clsid;
|
||||
std::wstring currentVersion;
|
||||
};
|
||||
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
RegistryKey m_classesKey;
|
||||
|
||||
std::vector<ExeServer> m_exeServers;
|
||||
std::vector<ProgId> m_progIds;
|
||||
|
||||
/// Parses the manifest
|
||||
HRESULT ParseManifest();
|
||||
|
||||
/// Parses the manifest element to populate one com exeServer
|
||||
///
|
||||
/// @param comServerElement - the manifest element representing an comServer
|
||||
HRESULT ParseExeServerElement(IMsixElement* exeServerElement);
|
||||
|
||||
/// Parses the class element to populate one class for a com exeServer
|
||||
///
|
||||
/// @param exeServer - an ExeServer
|
||||
/// @param classElement - the manifest element representing an class
|
||||
HRESULT ParseExeServerClassElement(ExeServer& exeServer, IMsixElement* classElement);
|
||||
|
||||
/// Parses the implementedCategories element inside of a class
|
||||
///
|
||||
/// @param classElement - the manifest element representing an class
|
||||
/// @param exeServerClass - the exeServerClass whose implementedCategories will be populated
|
||||
HRESULT ParseImplementedCategories(IMsixElement * classElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the Conversion elements inside of a com exeServer class
|
||||
/// Conversion can have either Readable or ReadWritable formats
|
||||
///
|
||||
/// @param classElement - the manifest element representing an class
|
||||
/// @param formatsQuery - query to execute to find either Readable or ReadWritable formats
|
||||
/// @param formats - format string
|
||||
HRESULT ParseConversionFormats(IMsixElement * classElement, const std::wstring & formatsQuery, std::wstring & formats);
|
||||
|
||||
/// Parses the DataFormats elements inside of a com exeServer class
|
||||
///
|
||||
/// @param classElement - the manifest element representing an class
|
||||
/// @param exeServerClass - the exeServerClass whose dataFormats will be populated
|
||||
HRESULT ParseDataFormats(IMsixElement * classElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the DataFormat elements inside of a com exeServer class
|
||||
///
|
||||
/// @param dataFormatsElement - the manifest element representing the dataFormats
|
||||
/// @param exeServerClass - the exeServerClass whose dataFormats will be populated
|
||||
HRESULT ParseDataFormat(IMsixElement * dataFormatsElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the miscStatus element inside of a class
|
||||
///
|
||||
/// @param classElement - the manifest element representing an class
|
||||
/// @param exeServerClass - the exeServerClass whose miscStatus will be populated
|
||||
HRESULT ParseMiscStatus(IMsixElement * classElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the aspect elements inside a MiscStatus to fill out the appropriate field in exeServerClass
|
||||
///
|
||||
/// @param miscStatusElement - the manifest element representing an miscStatus
|
||||
/// @param exeServerClass - the exeServerClass whose miscStatusAspects will be populated
|
||||
HRESULT ParseAspects(IMsixElement * miscStatusElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the verb element inside of a class
|
||||
///
|
||||
/// @param classElement - the manifest element representing an class
|
||||
/// @param exeServerClass - the exeServerClass whose verbs will be populated
|
||||
HRESULT ParseVerbs(IMsixElement * classElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the DefaultIcon element inside of a class
|
||||
///
|
||||
/// @param classElement - the manifest element representing an class
|
||||
/// @param exeServerClass - the exeServerClass whose defaultIcons will be populated
|
||||
HRESULT ParseDefaultIcon(IMsixElement * classElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the ToolboxBitmap32 element inside of a class
|
||||
///
|
||||
/// @param classElement - the manifest element representing an class
|
||||
/// @param exeServerClass - the exeServerClass whose toolboxBitmaps will be populated
|
||||
HRESULT ParseToolboxBitmap(IMsixElement * classElement, ExeServerClass & exeServerClass);
|
||||
|
||||
/// Parses the ProgId elements of a comServer
|
||||
///
|
||||
/// @param progIdElement - the manifest element representing a progId
|
||||
HRESULT ParseProgIdElement(IMsixElement* progIdElement);
|
||||
|
||||
/// Adds the registry entries corresponding to an ExeServer
|
||||
///
|
||||
/// @param exeServer - ExeServer struct representing data from the manifest
|
||||
HRESULT ProcessExeServerForAdd(ExeServer & exeServer);
|
||||
|
||||
/// Adds the registry entries corresponding to a ProgId
|
||||
///
|
||||
/// @param progId - ProgId struct representing data from the manifest
|
||||
HRESULT ProcessProgIdForAdd(ProgId & progId);
|
||||
|
||||
/// Removes the registry entries corresponding to an ExeServer
|
||||
///
|
||||
/// @param exeServer - ExeServer struct representing data from the manifest
|
||||
HRESULT ProcessExeServerForRemove(ExeServer & exeServer);
|
||||
|
||||
/// Removes the registry entries corresponding to a ProgId
|
||||
///
|
||||
/// @param progId - ProgId struct representing data from the manifest
|
||||
HRESULT ProcessProgIdForRemove(ProgId & progId);
|
||||
|
||||
/// Adds all the registry entries associated with the servers and progIds
|
||||
HRESULT AddServersAndProgIds();
|
||||
|
||||
/// Removes all the registry entries associated with the servers and progIds
|
||||
HRESULT RemoveServersAndProgIds();
|
||||
|
||||
ComServer() {}
|
||||
ComServer(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,239 @@
|
|||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include "CommandLineInterface.hpp"
|
||||
#include "msixmgrLogger.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "..\msixmgrLib\GeneralUtil.hpp"
|
||||
#include "Util.hpp"
|
||||
#include "resource.h"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
std::map<std::wstring, Options, CaseInsensitiveLess> CommandLineInterface::s_options =
|
||||
{
|
||||
{
|
||||
L"-AddPackage",
|
||||
Options(true, IDS_STRING_HELP_OPTION_ADDPACKAGE,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& path)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Undefined)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_operationType = OperationType::Add;
|
||||
commandLineInterface->m_packageFilePath = utf8_to_utf16(path);
|
||||
return S_OK;
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-RemovePackage",
|
||||
Options(true, IDS_STRING_HELP_OPTION_REMOVEPACKAGE,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& packageFullName)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Undefined)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_operationType = OperationType::Remove;
|
||||
commandLineInterface->m_packageFullName = utf8_to_utf16(packageFullName);
|
||||
return S_OK;
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-quietUX",
|
||||
Options(false, IDS_STRING_HELP_OPTION_QUIETMODE,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string&)
|
||||
{
|
||||
commandLineInterface->m_quietMode = true;
|
||||
return S_OK;
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-FindAllPackages",
|
||||
Options(false, IDS_STRING_HELP_OPTION_FINDALLPACKAGES,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string&)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Undefined)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_operationType = OperationType::FindAllPackages;
|
||||
return S_OK;
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-FindPackage",
|
||||
Options(true, IDS_STRING_HELP_OPTION_FINDPACKAGE,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& packageFullName)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Undefined)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_operationType = OperationType::FindPackage;
|
||||
commandLineInterface->m_packageFullName = utf8_to_utf16(packageFullName);
|
||||
return S_OK;
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-Unpack",
|
||||
Options(false, IDS_STRING_HELP_OPTION_UNPACK,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& packagePath)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Undefined)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_operationType = OperationType::Unpack;
|
||||
return S_OK;
|
||||
},
|
||||
// suboptions
|
||||
{
|
||||
{
|
||||
L"-packagePath",
|
||||
Option(true, IDS_STRING_HELP_OPTION_UNPACK_PATH,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& packagePath)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Unpack)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_packageFilePath = utf8_to_utf16(packagePath);
|
||||
return S_OK;
|
||||
}),
|
||||
},
|
||||
{
|
||||
L"-destination",
|
||||
Option(true, IDS_STRING_HELP_OPTION_UNPACK_DESTINATION,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string& destination)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Unpack)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_unpackDestination = utf8_to_utf16(destination);
|
||||
return S_OK;
|
||||
}),
|
||||
},
|
||||
{
|
||||
L"-applyACLs",
|
||||
Option(false, IDS_STRING_HELP_OPTION_UNPACK_APPLYACLS,
|
||||
[&](CommandLineInterface* commandLineInterface, const std::string&)
|
||||
{
|
||||
if (commandLineInterface->m_operationType != OperationType::Unpack)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
commandLineInterface->m_applyACLs = true;
|
||||
return S_OK;
|
||||
}),
|
||||
}
|
||||
})
|
||||
},
|
||||
{
|
||||
L"-?",
|
||||
Options(false, IDS_STRING_HELP_OPTION_HELP,
|
||||
[&](CommandLineInterface*, const std::string&)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
})
|
||||
}
|
||||
};
|
||||
|
||||
std::map<std::wstring, std::wstring> CommandLineInterface::s_optionAliases =
|
||||
{
|
||||
{L"-p", L"-AddPackage"},
|
||||
{L"-x", L"-RemovePackage"},
|
||||
};
|
||||
|
||||
void CommandLineInterface::DisplayHelp()
|
||||
{
|
||||
std::wcout << GetStringResource(IDS_STRING_HELPTEXT) << std::endl;
|
||||
|
||||
for (const auto& option : CommandLineInterface::s_options)
|
||||
{
|
||||
std::wcout << L"\t" << std::left << std::setfill(L' ') << std::setw(5) <<
|
||||
option.first << L": " << GetStringResource(option.second.Help) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CommandLineInterface::Init()
|
||||
{
|
||||
for (int i = 0; i < m_argc; i++)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixUITraceLoggingProvider,
|
||||
"CommandLineArguments",
|
||||
TraceLoggingValue(i, "index"),
|
||||
TraceLoggingValue(m_argv[i], "arg"));
|
||||
}
|
||||
|
||||
if (m_argc < 2)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
|
||||
int index = 1;
|
||||
while (index < m_argc)
|
||||
{
|
||||
std::wstring optionString = utf8_to_utf16(m_argv[index]);
|
||||
auto alias = s_optionAliases.find(optionString);
|
||||
if (alias != s_optionAliases.end())
|
||||
{
|
||||
optionString = alias->second;
|
||||
}
|
||||
|
||||
auto option = s_options.find(optionString);
|
||||
if (option == s_options.end())
|
||||
{
|
||||
TraceLoggingWrite(g_MsixUITraceLoggingProvider,
|
||||
"Unknown Argument",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
|
||||
TraceLoggingValue(m_argv[index], "arg"));
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
char const *parameter = "";
|
||||
if (option->second.TakesParameter)
|
||||
{
|
||||
if (++index == m_argc)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
parameter = m_argv[index];
|
||||
}
|
||||
RETURN_IF_FAILED(option->second.DefaultCallback(this, parameter));
|
||||
|
||||
++index;
|
||||
|
||||
if (option->second.HasSuboptions)
|
||||
{
|
||||
auto suboptions = option->second.Suboptions;
|
||||
while (index < m_argc)
|
||||
{
|
||||
std::wstring optionString = utf8_to_utf16(m_argv[index]);
|
||||
auto suboption = suboptions.find(optionString);
|
||||
if (suboption == suboptions.end())
|
||||
{
|
||||
TraceLoggingWrite(g_MsixUITraceLoggingProvider,
|
||||
"Unknown Argument",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR),
|
||||
TraceLoggingValue(m_argv[index], "arg"));
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
char const *suboptionParameter = "";
|
||||
if (suboption->second.TakesParameter)
|
||||
{
|
||||
if (++index == m_argc)
|
||||
{
|
||||
return E_INVALIDARG;
|
||||
}
|
||||
parameter = m_argv[index];
|
||||
}
|
||||
RETURN_IF_FAILED(option->second.DefaultCallback(this, parameter));
|
||||
|
||||
++index;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,103 @@
|
|||
#pragma once
|
||||
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <IPackage.hpp>
|
||||
#include <AppxPackaging.hpp>
|
||||
enum OperationType
|
||||
{
|
||||
Undefined = 0,
|
||||
Add = 1,
|
||||
Remove = 2,
|
||||
FindPackage = 3,
|
||||
FindAllPackages = 4,
|
||||
Unpack = 5,
|
||||
ApplyACLs = 6
|
||||
};
|
||||
|
||||
class CommandLineInterface;
|
||||
/// Describes an option to a command that the user may specify used for the command line tool
|
||||
struct Option
|
||||
{
|
||||
using CallbackFunction = std::function<HRESULT(CommandLineInterface* commandLineInterface, const std::string& value)>;
|
||||
|
||||
Option(bool param, const UINT help, CallbackFunction callback) : Help(help), Callback(callback), TakesParameter(param)
|
||||
{}
|
||||
|
||||
bool TakesParameter;
|
||||
std::wstring Name;
|
||||
UINT Help;
|
||||
CallbackFunction Callback;
|
||||
};
|
||||
|
||||
struct Options
|
||||
{
|
||||
using CallbackFunction = std::function<HRESULT(CommandLineInterface* commandLineInterface, const std::string& value)>;
|
||||
|
||||
Options(bool takesParam, const UINT help, CallbackFunction defaultCallback) : Help(help), DefaultCallback(defaultCallback), TakesParameter(takesParam), HasSuboptions(false) {}
|
||||
Options(bool takesParam, const UINT help, CallbackFunction defaultCallback, std::map<std::wstring, Option> suboptions) : Help(help), DefaultCallback(defaultCallback), TakesParameter(takesParam), Suboptions(suboptions)
|
||||
{
|
||||
HasSuboptions = !Suboptions.empty();
|
||||
}
|
||||
|
||||
bool HasSuboptions;
|
||||
bool TakesParameter;
|
||||
std::wstring Name;
|
||||
UINT Help;
|
||||
CallbackFunction DefaultCallback;
|
||||
std::map<std::wstring, Option> Suboptions;
|
||||
};
|
||||
|
||||
struct CaseInsensitiveLess
|
||||
{
|
||||
struct CaseInsensitiveCompare
|
||||
{
|
||||
bool operator() (const wchar_t& c1, const wchar_t& c2) const
|
||||
{
|
||||
return tolower(c1) < tolower(c2);
|
||||
}
|
||||
};
|
||||
bool operator() (const std::wstring & s1, const std::wstring & s2) const
|
||||
{
|
||||
return std::lexicographical_compare(
|
||||
s1.begin(), s1.end(), // source range
|
||||
s2.begin(), s2.end(), // dest range
|
||||
CaseInsensitiveCompare()); // comparison
|
||||
}
|
||||
};
|
||||
|
||||
/// Parses the command line specified and creates a request.
|
||||
class CommandLineInterface
|
||||
{
|
||||
friend Option;
|
||||
public:
|
||||
CommandLineInterface(int argc, char * argv[]) : m_argc(argc), m_argv(argv)
|
||||
{
|
||||
m_toolName = m_argv[0];
|
||||
}
|
||||
~CommandLineInterface() {}
|
||||
|
||||
/// Displays contextual formatted help to the user used for command line tool
|
||||
void DisplayHelp();
|
||||
HRESULT Init();
|
||||
bool IsQuietMode() { return m_quietMode; }
|
||||
std::wstring GetPackageFilePathToInstall() { return m_packageFilePath; }
|
||||
std::wstring GetPackageFullName() { return m_packageFullName; }
|
||||
OperationType GetOperationType() { return m_operationType; }
|
||||
private:
|
||||
int m_argc = 0;
|
||||
char ** m_argv = nullptr;
|
||||
char * m_toolName = nullptr;
|
||||
static std::map<std::wstring, Options, CaseInsensitiveLess> s_options;
|
||||
static std::map<std::wstring, std::wstring> s_optionAliases;
|
||||
|
||||
std::wstring m_packageFilePath;
|
||||
std::wstring m_packageFullName;
|
||||
std::wstring m_unpackDestination;
|
||||
bool m_quietMode;
|
||||
bool m_applyACLs;
|
||||
|
||||
OperationType m_operationType = OperationType::Undefined;
|
||||
|
||||
CommandLineInterface() {}
|
||||
};
|
|
@ -0,0 +1,159 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
static const std::wstring uninstallKeyPath = L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall";
|
||||
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 classesKeyPath = L"SOFTWARE\\Classes";
|
||||
|
||||
static const std::wstring msixCoreProgIDPrefix = L"MsixCore";
|
||||
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 protocolValueName = L"URL Protocol";
|
||||
static const std::wstring urlProtocolPrefix = L"URL:";
|
||||
static const std::wstring commandArgument = L" \"%1\"";
|
||||
|
||||
static const std::wstring ftaCategoryNameInManifest = L"windows.fileTypeAssociation";
|
||||
static const std::wstring protocolCategoryNameInManifest = L"windows.protocol";
|
||||
static const std::wstring ComServerCategoryNameInManifest = L"windows.comServer";
|
||||
static const std::wstring comInterfaceCategoryNameInManifest = L"windows.comInterface";
|
||||
static const std::wstring startupTaskCategoryNameInManifest = L"windows.startupTask";
|
||||
|
||||
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 idAttribute = L"Id";
|
||||
static const std::wstring executableAttribute = L"Executable";
|
||||
static const std::wstring argumentsAttribute = L"Arguments";
|
||||
static const std::wstring displayNameAttribute = L"DisplayName";
|
||||
static const std::wstring launchAndActivationPermissionAttribute = L"LaunchAndActivationPermission";
|
||||
static const std::wstring progIdAttribute = L"ProgId";
|
||||
static const std::wstring versionIndependentProgIdAttribute = L"VersionIndependentProgId";
|
||||
static const std::wstring clsidAttribute = L"Clsid";
|
||||
static const std::wstring currentVersionAttribute = L"CurrentVersion";
|
||||
static const std::wstring autoConvertToAttribute = L"AutoConvertTo";
|
||||
static const std::wstring insertableObjectAttribute = L"InsertableObject";
|
||||
static const std::wstring shortDisplayNameAttribute = L"ShortDisplayName";
|
||||
static const std::wstring enableOleDefaultHandlerAttribute = L"EnableOleDefaultHandler";
|
||||
static const std::wstring formatNameAttribute = L"FormatName";
|
||||
static const std::wstring standardFormatAttribute = L"StandardFormat";
|
||||
static const std::wstring defaultFormatNameAttribute = L"DefaultFormatName";
|
||||
static const std::wstring defaultStandardFormatAttribute = L"DefaultStandardFormat";
|
||||
static const std::wstring aspectFlagAttribute = L"AspectFlag";
|
||||
static const std::wstring mediumFlagAttribute = L"MediumFlag";
|
||||
static const std::wstring directionAttribute = L"Direction";
|
||||
static const std::wstring versionNumberAttribute = L"VersionNumber";
|
||||
static const std::wstring proxyStubClsidAttribute = L"ProxyStubClsid";
|
||||
static const std::wstring proxyStubClsidForUniversalMarshaler = L"{00020424-0000-0000-C000-000000000046}";
|
||||
static const std::wstring localeIdAttribute = L"LocaleId";
|
||||
static const std::wstring libraryFlagAttribute = L"LibraryFlag";
|
||||
static const std::wstring helpDirectoryAttribute = L"HelpDirectory";
|
||||
static const std::wstring pathAttribute = L"Path";
|
||||
static const std::wstring typeAttribute = L"Type";
|
||||
static const std::wstring oleMiscFlagAttribute = L"OleMiscFlag";
|
||||
static const std::wstring appendMenuFlagAttribute = L"AppendMenuFlag";
|
||||
static const std::wstring oleVerbFlagAttribute = L"OleVerbFlag";
|
||||
static const std::wstring resourceIndexAttribute = L"ResourceIndex";
|
||||
static const std::wstring taskIdAttribute = L"TaskId";
|
||||
static const std::wstring IdAttribute = L"Id";
|
||||
|
||||
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']";
|
||||
static const std::wstring protocolQuery = L"*[local-name()='Protocol']";
|
||||
static const std::wstring displayNameQuery = L"*[local-name()='DisplayName']";
|
||||
static const std::wstring exeServerQuery = L"*[local-name()='ComServer']/*[local-name()='ExeServer']";
|
||||
static const std::wstring exeServerClassQuery = L"*[local-name()='Class']";
|
||||
static const std::wstring progIdQuery = L"*[local-name()='ComServer']/*[local-name()='ProgId']";
|
||||
static const std::wstring implementedCategoriesQuery = L"*[local-name()='ImplementedCategories']/*[local-name()='ImplementedCategory']";
|
||||
static const std::wstring readableFormatsQuery = L"*[local-name()='Conversion']/*[local-name()='Readable']/*[local-name()='Format']";
|
||||
static const std::wstring readWritableFormatsQuery = L"*[local-name()='Conversion']/*[local-name()='ReadWritable']/*[local-name()='Format']";
|
||||
static const std::wstring dataFormatsQuery = L"*[local-name()='DataFormats']";
|
||||
static const std::wstring dataFormatQuery = L"*[local-name()='DataFormat']";
|
||||
static const std::wstring interfaceQuery = L"*[local-name()='ComInterface']/*[local-name()='Interface']";
|
||||
static const std::wstring typeLibForInterfaceQuery = L"*[local-name()='TypeLib']";
|
||||
static const std::wstring typeLibQuery = L"*[local-name()='ComInterface']/*[local-name()='TypeLib']";
|
||||
static const std::wstring versionQuery = L"*[local-name()='Version']";
|
||||
static const std::wstring win32PathQuery = L"*[local-name()='Win32Path']";
|
||||
static const std::wstring win64PathQuery = L"*[local-name()='Win64Path']";
|
||||
static const std::wstring miscStatusQuery = L"*[local-name()='MiscStatus']";
|
||||
static const std::wstring aspectQuery = L"*[local-name()='Aspect']";
|
||||
static const std::wstring defaultIconQuery = L"*[local-name()='DefaultIcon']";
|
||||
static const std::wstring toolboxBitmapQuery = L"*[local-name()='ToolboxBitmap32']";
|
||||
static const std::wstring comVerbQuery = L"*[local-name()='Verbs']/*[local-name()='Verb']";
|
||||
static const std::wstring startupTaskQuery = L"*[local-name()='StartupTask']";
|
||||
static const std::wstring launchActionQuery = L"*[local-name()='AutoPlayContent']/*[local-name()='LaunchAction']";
|
||||
static const std::wstring invokeActionQuery = L"*[local-name()='AutoPlayHandler']/*[local-name()='InvokeAction']";
|
||||
static const std::wstring invokeActionContentQuery = L"*[local-name()='Content']";
|
||||
static const std::wstring invokeActionDeviceQuery = L"*[local-name()='Device']";
|
||||
|
||||
/// Constants for Firewall DEH
|
||||
static const std::wstring firewallExtensionQuery = L"/*[local-name()='Package']/*[local-name()='Extensions']/*[local-name()='Extension']/*[local-name()='FirewallRules']/*[local-name()='Rule']";
|
||||
static const std::wstring firewallRuleDirectionAttribute = L"Direction";
|
||||
static const std::wstring protocolAttribute = L"IPProtocol";
|
||||
static const std::wstring profileAttribute = L"Profile";
|
||||
static const std::wstring localPortMinAttribute = L"LocalPortMin";
|
||||
static const std::wstring localPortMaxAttribute = L"LocalPortMax";
|
||||
static const std::wstring remotePortMinAttribute = L"RemotePortMin";
|
||||
static const std::wstring remotePortMaxAttribute = L"RemotePortMax";
|
||||
static const std::wstring directionIn = L"in";
|
||||
static const std::wstring directionOut = L"out";
|
||||
|
||||
/// Constants for AutoPlay DEH
|
||||
static const std::wstring desktopAppXExtensionCategory = L"windows.autoPlayHandler";
|
||||
|
||||
static const std::wstring desktopAppXContentSubCategory = L"Windows.AutoPlayDesktopAppX.Content";
|
||||
static const std::wstring desktopAppXDeviceSubCategory = L"Windows.AutoPlayDesktopAppX.Device";
|
||||
|
||||
static const std::wstring idAttributeName = L"Verb";
|
||||
static const std::wstring actionAttributeName = L"ActionDisplayName";
|
||||
static const std::wstring providerAttributeName = L"ProviderDisplayName";
|
||||
static const std::wstring contentEventAttributeName = L"ContentEvent";
|
||||
static const std::wstring deviceEventAttributeName = L"DeviceEvent";
|
||||
static const std::wstring dropTargetHandlerAttributeName = L"DropTargetHandler";
|
||||
static const std::wstring parametersAttributeName = L"Parameters";
|
||||
static const std::wstring hwEventHandlerAttributeName = L"HWEventHandler";
|
||||
static const std::wstring InitCmdLineAttributeName = L"InitCmdLine";
|
||||
static const std::wstring dropTargetRegKeyName = L"DropTarget";
|
||||
static const std::wstring commandKeyRegName = L"command";
|
||||
static const wchar_t desktopAppXProtocolDelegateExecuteValue[] = L"{BFEC0C93-0B7D-4F2C-B09C-AFFFC4BDAE78}";
|
||||
|
||||
static const std::wstring explorerRegKeyName = L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer";
|
||||
static const std::wstring handlerKeyName = L"AutoPlayHandlers\\Handlers";
|
||||
static const std::wstring eventHandlerRootRegKeyName = L"AutoPlayHandlers\\EventHandlers";
|
||||
|
||||
static const std::wstring clsidKeyName = L"CLSID";
|
||||
static const std::wstring inprocHandlerKeyName = L"InprocHandler32";
|
||||
static const std::wstring defaultInprocHandler = L"ole32.dll";
|
||||
static const std::wstring localServerKeyName = L"LocalServer32";
|
||||
static const std::wstring progIdKeyName = L"ProgID";
|
||||
static const std::wstring versionIndependentProgIdKeyName = L"VersionIndependentProgID";
|
||||
static const std::wstring curVerKeyName = L"CurVer";
|
||||
static const std::wstring auxUserTypeKeyName = L"AuxUserType";
|
||||
static const std::wstring shortDisplayNameKeyName = L"2";
|
||||
static const std::wstring insertableObjectKeyName = L"Insertable";
|
||||
static const std::wstring autoConvertToValueName = L"AutoConvertTo";
|
||||
static const std::wstring implementedCategoriesKeyName = L"Implemented Categories";
|
||||
static const std::wstring readableKeyName = L"Conversion\\Readable\\Main";
|
||||
static const std::wstring readWritableKeyName = L"Conversion\\ReadWritable\\Main";
|
||||
static const std::wstring dataFormatsKeyName = L"DataFormats";
|
||||
static const std::wstring defaultFileKeyName = L"DefaultFile";
|
||||
static const std::wstring getSetKeyName = L"GetSet";
|
||||
static const std::wstring interfaceKeyName = L"Interface";
|
||||
static const std::wstring proxyStubClsidKeyName = L"ProxyStubClsid32";
|
||||
static const std::wstring typeLibKeyName = L"TypeLib";
|
||||
static const std::wstring versionValueName = L"Version";
|
||||
static const std::wstring win64KeyName = L"win64";
|
||||
static const std::wstring win32KeyName = L"win32";
|
||||
static const std::wstring flagsKeyName = L"Flags";
|
||||
static const std::wstring helpDirKeyName = L"HelpDir";
|
||||
static const std::wstring miscStatusKeyName = L"MiscStatus";
|
||||
static const std::wstring verbKeyName = L"Verb";
|
||||
static const std::wstring toolboxBitmapKeyName = L"ToolBoxBitmap32";
|
|
@ -0,0 +1,180 @@
|
|||
#include <Windows.h>
|
||||
#include <WinCrypt.h>
|
||||
#include <BcryptLibrary.hpp>
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <CryptoProvider.hpp>
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
CryptoProvider::CryptoProvider()
|
||||
{
|
||||
this->providerHandle = NULL;
|
||||
this->hashHandle = NULL;
|
||||
this->digest.bytes = NULL;
|
||||
this->digest.length = 0;
|
||||
}
|
||||
|
||||
CryptoProvider::~CryptoProvider()
|
||||
{
|
||||
Reset();
|
||||
}
|
||||
|
||||
void CryptoProvider::Reset()
|
||||
{
|
||||
this->digest.bytes = NULL;
|
||||
this->digest.length = 0;
|
||||
|
||||
if (NULL != this->hashHandle)
|
||||
{
|
||||
if (!SUCCEEDED(BcryptLibrary::BCryptDestroyHash(this->hashHandle)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
this->hashHandle = NULL;
|
||||
}
|
||||
|
||||
if (NULL != this->providerHandle)
|
||||
{
|
||||
if (!SUCCEEDED(BcryptLibrary::BCryptCloseAlgorithmProvider(this->providerHandle, 0)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
this->providerHandle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT CryptoProvider::Create(
|
||||
_Outptr_ CryptoProvider** provider)
|
||||
{
|
||||
RETURN_IF_FAILED(BcryptLibrary::Load());
|
||||
|
||||
std::unique_ptr<CryptoProvider> cp(new APPXCOMMON_NEW_TAG CryptoProvider());
|
||||
if (cp == nullptr)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_OUTOFMEMORY);
|
||||
}
|
||||
|
||||
*provider = cp.release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CryptoProvider::OpenProvider()
|
||||
{
|
||||
PCWSTR algorithmId = BCRYPT_SHA256_ALGORITHM;
|
||||
PCWSTR implementation = NULL;
|
||||
ULONG flags = 0;
|
||||
|
||||
BcryptLibrary::BCryptOpenAlgorithmProvider(
|
||||
&this->providerHandle,
|
||||
algorithmId,
|
||||
implementation,
|
||||
flags);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CryptoProvider::StartDigest()
|
||||
{
|
||||
BYTE* hashObject;
|
||||
ULONG hashObjectSize;
|
||||
ULONG resultSize;
|
||||
|
||||
RETURN_IF_FAILED(OpenProvider());
|
||||
|
||||
BcryptLibrary::BCryptGetProperty(
|
||||
this->providerHandle,
|
||||
BCRYPT_OBJECT_LENGTH,
|
||||
(PUCHAR)&hashObjectSize,
|
||||
sizeof(hashObjectSize),
|
||||
&resultSize,
|
||||
0);
|
||||
|
||||
if (sizeof(this->quickHashObjectBuffer) >= hashObjectSize)
|
||||
{
|
||||
hashObject = this->quickHashObjectBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
hashObject = hashObjectBuffer.data();
|
||||
}
|
||||
|
||||
BcryptLibrary::BCryptCreateHash(
|
||||
this->providerHandle,
|
||||
&this->hashHandle,
|
||||
hashObject,
|
||||
hashObjectSize,
|
||||
NULL,
|
||||
NULL,
|
||||
0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CryptoProvider::DigestData(
|
||||
_In_ const COMMON_BYTES* data)
|
||||
{
|
||||
BcryptLibrary::BCryptHashData(
|
||||
this->hashHandle,
|
||||
data->bytes,
|
||||
data->length,
|
||||
0);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT CryptoProvider::GetDigest(
|
||||
_Out_ COMMON_BYTES* digest)
|
||||
{
|
||||
BYTE* digestPtr;
|
||||
ULONG digestSize;
|
||||
|
||||
if (0 == this->digest.length)
|
||||
{
|
||||
NTSTATUS status;
|
||||
|
||||
ULONG resultSize;
|
||||
|
||||
status = BcryptLibrary::BCryptGetProperty(
|
||||
this->providerHandle,
|
||||
BCRYPT_HASH_LENGTH,
|
||||
(PUCHAR)&digestSize,
|
||||
sizeof(digestSize),
|
||||
&resultSize,
|
||||
0);
|
||||
|
||||
if (FAILED(HRESULT_FROM_NT(status)))
|
||||
{
|
||||
return HRESULT_FROM_NT(status);
|
||||
}
|
||||
|
||||
if (sizeof(this->quickDigestBuffer) >= digestSize)
|
||||
{
|
||||
digestPtr = this->quickDigestBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
digestPtr = this->digestBuffer.data();
|
||||
}
|
||||
|
||||
status = BcryptLibrary::BCryptFinishHash(
|
||||
this->hashHandle,
|
||||
digestPtr,
|
||||
digestSize,
|
||||
0);
|
||||
|
||||
if (FAILED(HRESULT_FROM_NT(status)))
|
||||
{
|
||||
return HRESULT_FROM_NT(status);
|
||||
}
|
||||
|
||||
this->digest.bytes = digestPtr;
|
||||
this->digest.length = digestSize;
|
||||
}
|
||||
|
||||
*digest = this->digest;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#define APPXCOMMON_NEW_TAG (std::nothrow)
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
struct COMMON_BYTES
|
||||
{
|
||||
ULONG length;
|
||||
__field_ecount(length) BYTE* bytes;
|
||||
};
|
||||
|
||||
// CryptoProvider objects are not thread-safe, hence should not be called from multiple threads simultaneously.
|
||||
// Usage:
|
||||
// (StartDigest DigestData* GetDigest* Reset)*
|
||||
class CryptoProvider
|
||||
{
|
||||
private:
|
||||
|
||||
BCRYPT_ALG_HANDLE providerHandle;
|
||||
BCRYPT_HASH_HANDLE hashHandle;
|
||||
std::vector<BYTE> hashObjectBuffer;
|
||||
std::vector<BYTE> digestBuffer;
|
||||
BYTE quickHashObjectBuffer[700]; // Tests shows that HMAC with 256-bit or 512-bit keys requires 600+ bytes of hash object space.
|
||||
BYTE quickDigestBuffer[64]; // accommodates up to 64-byte hashes
|
||||
COMMON_BYTES digest;
|
||||
|
||||
HRESULT OpenProvider();
|
||||
|
||||
CryptoProvider();
|
||||
|
||||
public:
|
||||
~CryptoProvider();
|
||||
|
||||
void Reset();
|
||||
|
||||
HRESULT StartDigest();
|
||||
|
||||
HRESULT DigestData(
|
||||
_In_ const COMMON_BYTES* data);
|
||||
|
||||
HRESULT GetDigest(
|
||||
_Out_ COMMON_BYTES* digest);
|
||||
|
||||
static HRESULT Create(
|
||||
_Outptr_ CryptoProvider** provider);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include "ErrorHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR ErrorHandler::HandlerName = L"ErrorHandler";
|
||||
|
||||
HRESULT ErrorHandler::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(RemovePackage(m_msixRequest->GetPackageInfo()->GetPackageFullName()));
|
||||
return m_msixRequest->GetMsixResponse()->GetHResultTextCode();
|
||||
}
|
||||
|
||||
HRESULT ErrorHandler::RemovePackage(std::wstring packageFullName)
|
||||
{
|
||||
AutoPtr<MsixRequest> removePackageRequest;
|
||||
RETURN_IF_FAILED(MsixRequest::Make(OperationType::Remove, nullptr, packageFullName,
|
||||
MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &removePackageRequest));
|
||||
|
||||
const HRESULT hrCancelRequest = removePackageRequest->ProcessRequest();
|
||||
if (FAILED(hrCancelRequest))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Failed to process cancel request",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrCancelRequest, "HR"));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ErrorHandler::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<ErrorHandler> localInstance(new ErrorHandler(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,32 @@
|
|||
#pragma once
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class ErrorHandler : IPackageHandler
|
||||
{
|
||||
public:
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Intentionally skipping implementation of ExecuteForRemoveRequest()
|
||||
/// since there is no use case as of now to handle an error in case of a remove request
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~ErrorHandler() {}
|
||||
|
||||
private:
|
||||
MsixRequest * m_msixRequest = nullptr;
|
||||
|
||||
ErrorHandler() {}
|
||||
ErrorHandler(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Prepares and sends msixrequest to uninstall the package in case cancel button was clicked during installation
|
||||
HRESULT RemovePackage(std::wstring packageFullName);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include <experimental/filesystem> // C++-standard header file name
|
||||
#include <filesystem> // Microsoft-specific implementation header file name
|
||||
|
||||
#include "Extractor.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "FootprintFiles.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include "Constants.hpp"
|
||||
|
||||
#include "RegistryDevirtualizer.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR Extractor::HandlerName = L"Extractor";
|
||||
|
||||
HRESULT Extractor::GetOutputStream(LPCWSTR path, LPCWSTR fileName, IStream** stream)
|
||||
{
|
||||
std::wstring fullFileName = path + std::wstring(L"\\") + fileName;
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(mkdirp(fullFileName)));
|
||||
RETURN_IF_FAILED(CreateStreamOnFileUTF16(fullFileName.c_str(), false, stream));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::ExtractFile(IAppxFile* file)
|
||||
{
|
||||
Text<WCHAR> fileName;
|
||||
RETURN_IF_FAILED(file->GetName(&fileName));
|
||||
|
||||
UINT64 fileSize = 0;
|
||||
RETURN_IF_FAILED(file->GetSize(&fileSize));
|
||||
|
||||
ULARGE_INTEGER fileSizeLargeInteger = { 0 };
|
||||
fileSizeLargeInteger.QuadPart = fileSize;
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"ExtractFile",
|
||||
TraceLoggingValue(fileName.Get(), "FileName"),
|
||||
TraceLoggingValue(fileSize, "FileSize"));
|
||||
|
||||
ComPtr<IStream> fileStream;
|
||||
RETURN_IF_FAILED(file->GetStream(&fileStream));
|
||||
ComPtr<IStream> outputStream;
|
||||
|
||||
auto packageDirectoryPath = m_msixRequest->GetPackageDirectoryPath();
|
||||
|
||||
RETURN_IF_FAILED(GetOutputStream(packageDirectoryPath.c_str(), fileName.Get(), &outputStream));
|
||||
RETURN_IF_FAILED(fileStream->CopyTo(outputStream.Get(), fileSizeLargeInteger, nullptr, nullptr));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::ExtractFootprintFiles()
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Extracting footprint files from the package");
|
||||
|
||||
auto packageToInstall = std::dynamic_pointer_cast<Package>(m_msixRequest->GetPackageInfo());
|
||||
|
||||
for (int i = 0; i < FootprintFilesCount; i++)
|
||||
{
|
||||
if (m_msixRequest->GetMsixResponse()->GetIsInstallCancelled())
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
|
||||
}
|
||||
|
||||
if (packageToInstall != nullptr)
|
||||
{
|
||||
ComPtr<IAppxFile> footprintFile;
|
||||
HRESULT hr = packageToInstall->GetPackageReader()->GetFootprintFile(g_footprintFilesType[i].fileType, &footprintFile);
|
||||
if (SUCCEEDED(hr) && footprintFile.Get())
|
||||
{
|
||||
RETURN_IF_FAILED(ExtractFile(footprintFile.Get()));
|
||||
}
|
||||
else if (g_footprintFilesType[i].isRequired)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Missing required Footprintfile",
|
||||
TraceLoggingValue(g_footprintFilesType[i].description, "File Description"));
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::ExtractPayloadFiles()
|
||||
{
|
||||
ComPtr<IAppxFilesEnumerator> files;
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Extracting payload files from the package");
|
||||
|
||||
auto packageToInstall = std::dynamic_pointer_cast<Package>(m_msixRequest->GetPackageInfo());
|
||||
if (packageToInstall == nullptr)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(packageToInstall->GetPackageReader()->GetPayloadFiles(&files));
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(files->GetHasCurrent(&hasCurrent));
|
||||
|
||||
unsigned int totalNumberFiles = m_msixRequest->GetPackageInfo()->GetNumberOfPayloadFiles();
|
||||
unsigned int nbrFilesExtracted = 0;
|
||||
while (hasCurrent)
|
||||
{
|
||||
if (m_msixRequest->GetMsixResponse()->GetIsInstallCancelled())
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
|
||||
}
|
||||
ComPtr<IAppxFile> file;
|
||||
RETURN_IF_FAILED(files->GetCurrent(&file));
|
||||
|
||||
RETURN_IF_FAILED(ExtractFile(file.Get()));
|
||||
|
||||
RETURN_IF_FAILED(files->MoveNext(&hasCurrent));
|
||||
++nbrFilesExtracted;
|
||||
|
||||
float progress = 100.0f * nbrFilesExtracted / totalNumberFiles;
|
||||
m_msixRequest->GetMsixResponse()->Update(InstallationStep::InstallationStepExtraction, progress);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::CreatePackageRoot()
|
||||
{
|
||||
std::wstring packagePath = FilePathMappings::GetInstance().GetMsixCoreDirectory();
|
||||
if (!CreateDirectory(packagePath.c_str(), nullptr))
|
||||
{
|
||||
DWORD lastError = GetLastError();
|
||||
if (lastError != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(lastError));
|
||||
}
|
||||
}
|
||||
|
||||
if (!CreateDirectory((FilePathMappings::GetInstance().GetMsixCoreDirectory() + m_msixRequest->GetPackageInfo()->GetPackageFullName()).c_str(), nullptr))
|
||||
{
|
||||
DWORD lastError = GetLastError();
|
||||
if (lastError != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(lastError));
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(CreatePackageRoot());
|
||||
|
||||
RETURN_IF_FAILED(ExtractPackage());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::ExecuteForRemoveRequest()
|
||||
{
|
||||
// First release manifest so we can delete the file.
|
||||
m_msixRequest->GetPackageInfo()->ReleaseManifest();
|
||||
|
||||
std::error_code error;
|
||||
auto packageDirectoryPath = m_msixRequest->GetPackageDirectoryPath();
|
||||
uintmax_t numRemoved = std::experimental::filesystem::remove_all(packageDirectoryPath, error);
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Removed directory",
|
||||
TraceLoggingValue(packageDirectoryPath.c_str(), "PackageDirectoryPath"),
|
||||
TraceLoggingValue(error.value(), "Error"),
|
||||
TraceLoggingValue(numRemoved, "NumRemoved"));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<Extractor> localInstance(new Extractor(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Extractor::ExtractPackage()
|
||||
{
|
||||
RETURN_IF_FAILED(ExtractFootprintFiles());
|
||||
RETURN_IF_FAILED(ExtractPayloadFiles());
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// Extractor extracts the filesfrom the package to the file system.
|
||||
class Extractor : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Extracts the package to the file system.
|
||||
/// Creates the package's root directory in c:\program files\msixcoreapps\<package>
|
||||
/// Extracts the files from the package to the package's root directory
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes all the files, directories written during the add.
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~Extractor() {}
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
Extractor() {}
|
||||
Extractor(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Extracts all files from a package.
|
||||
HRESULT ExtractPackage();
|
||||
|
||||
/// Writes the file from the package to disk.
|
||||
///
|
||||
/// @param file - The IAppxFile interface that represents a footprint or payload file
|
||||
/// in the package.
|
||||
HRESULT ExtractFile(IAppxFile* file);
|
||||
|
||||
/// Extracts all footprint files (i.e. manifest/blockmap/signature) from a package.
|
||||
HRESULT ExtractFootprintFiles();
|
||||
|
||||
/// Extracts all payload files from a package.
|
||||
HRESULT ExtractPayloadFiles();
|
||||
|
||||
/// Creates a writable IStream over a file with the specified name
|
||||
/// under the specified path. This function will also create intermediate
|
||||
/// subdirectories if necessary.
|
||||
///
|
||||
/// @param path - Path of the folder containing the file to be opened. This should NOT
|
||||
/// end with a slash ('\') character.
|
||||
/// @param fileName - Name, not including path, of the file to be opened
|
||||
/// @param stream - Output parameter pointing to the created instance of IStream over
|
||||
/// the specified file when this function succeeds.
|
||||
HRESULT GetOutputStream(LPCWSTR path, LPCWSTR fileName, IStream** stream);
|
||||
|
||||
/// Creates the package root directory where all the files will be installed to.
|
||||
/// This will be in c:\program files\msixcoreapps\<packagefullname>
|
||||
HRESULT CreatePackageRoot();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
#include "FilePaths.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <shlobj_core.h>
|
||||
#include <KnownFolders.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
void MsixCoreLib_GetPathChild(std::wstring &path)
|
||||
{
|
||||
while (path.front() != '\\')
|
||||
{
|
||||
path.erase(0, 1);
|
||||
}
|
||||
path.erase(0, 1);
|
||||
}
|
||||
|
||||
void MsixCoreLib_GetPathParent(std::wstring &path)
|
||||
{
|
||||
while (!path.empty() && path.back() != '\\')
|
||||
{
|
||||
if (path.length() != 1)
|
||||
{
|
||||
path.erase(path.end() - 1, path.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
path.erase(0, 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!path.empty() && path.length() != 1)
|
||||
{
|
||||
path.erase(path.end() - 1, path.end());
|
||||
}
|
||||
}
|
||||
|
||||
FilePathMappings& FilePathMappings::GetInstance()
|
||||
{
|
||||
static FilePathMappings s_selfInstance;
|
||||
return s_selfInstance;
|
||||
}
|
||||
|
||||
std::wstring FilePathMappings::GetExecutablePath(std::wstring packageExecutablePath, PCWSTR packageFullName)
|
||||
{
|
||||
// make a local copy so we can modify in place
|
||||
std::wstring executionPathWSTR = packageExecutablePath;
|
||||
|
||||
//Checks if the executable is inside the VFS
|
||||
if (executionPathWSTR.find(L"VFS") != std::wstring::npos)
|
||||
{
|
||||
MsixCoreLib_GetPathChild(executionPathWSTR);
|
||||
//Checks if the executable is in one of the known folders
|
||||
for (auto pair : m_map)
|
||||
{
|
||||
if (executionPathWSTR.find(pair.first) != std::wstring::npos)
|
||||
{
|
||||
//The executable exists in an unpacked directory
|
||||
std::wstring executablePath = pair.second;
|
||||
|
||||
MsixCoreLib_GetPathChild(executionPathWSTR);
|
||||
executablePath.push_back(L'\\');
|
||||
executablePath.append(executionPathWSTR);
|
||||
return executablePath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//The executable path exists in the root msix directory and was not devirtualized
|
||||
std::wstring executablePath = m_msixCoreDirectory;
|
||||
executablePath.append(packageFullName);
|
||||
executablePath.push_back(L'\\');
|
||||
executablePath.append(packageExecutablePath);
|
||||
return executablePath;
|
||||
}
|
||||
|
||||
HRESULT FilePathMappings::InitializePaths()
|
||||
{
|
||||
TextOle<WCHAR> systemX86Path;
|
||||
TextOle<WCHAR> systemPath;
|
||||
TextOle<WCHAR> programFilesX86Path;
|
||||
TextOle<WCHAR> programFilesCommonX86Path;
|
||||
TextOle<WCHAR> windowsPath;
|
||||
TextOle<WCHAR> commonAppDataPath;
|
||||
TextOle<WCHAR> localAppDataPath;
|
||||
TextOle<WCHAR> appDataPath;
|
||||
TextOle<WCHAR> commonProgramsPath;
|
||||
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_SystemX86, 0, NULL, &systemX86Path));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_System, 0, NULL, &systemPath));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesX86, 0, NULL, &programFilesX86Path));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommonX86, 0, NULL, &programFilesCommonX86Path));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_Windows, 0, NULL, &windowsPath));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_ProgramData, 0, NULL, &commonAppDataPath));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_RoamingAppData, 0, NULL, &appDataPath));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_CommonPrograms, 0, NULL, &commonProgramsPath));
|
||||
|
||||
std::wstring appVSystem32CatrootPath = std::wstring(systemPath.Get());
|
||||
std::wstring appVSystem32Catroot2Path = std::wstring(systemPath.Get());
|
||||
std::wstring appVSystem32DriversEtcPath = std::wstring(systemPath.Get());
|
||||
std::wstring appVSystem32DriverstorePath = std::wstring(systemPath.Get());
|
||||
std::wstring appVSystem32LogfilesPath = std::wstring(systemPath.Get());
|
||||
std::wstring appVSystem32SpoolPath = std::wstring(systemPath.Get());
|
||||
|
||||
appVSystem32CatrootPath.append(L"\\catroot");
|
||||
appVSystem32Catroot2Path.append(L"\\catroot2");
|
||||
appVSystem32DriversEtcPath.append(L"\\drivers\\etc");
|
||||
appVSystem32DriverstorePath.append(L"\\driverstore");
|
||||
appVSystem32LogfilesPath.append(L"\\logfiles");
|
||||
appVSystem32SpoolPath.append(L"\\spool");
|
||||
|
||||
std::wstring systemDrive = std::wstring(windowsPath.Get());
|
||||
MsixCoreLib_GetPathParent(systemDrive);
|
||||
m_map[L"AppVPackageDrive"] = systemDrive;
|
||||
m_map[L"SystemX86"] = std::wstring(systemX86Path.Get());
|
||||
m_map[L"System"] = std::wstring(systemPath.Get());
|
||||
m_map[L"ProgramFilesX86"] = std::wstring(programFilesX86Path.Get());
|
||||
m_map[L"ProgramFilesCommonX86"] = std::wstring(programFilesCommonX86Path.Get());
|
||||
m_map[L"Windows"] = std::wstring(windowsPath.Get());
|
||||
m_map[L"Common AppData"] = std::wstring(commonAppDataPath.Get());
|
||||
m_map[L"Common Programs"] = std::wstring(commonProgramsPath.Get());
|
||||
m_map[L"AppData"] = std::wstring(appDataPath.Get());
|
||||
m_map[L"AppVSystem32Catroot"] = appVSystem32CatrootPath;
|
||||
m_map[L"AppVSystem32Catroot2"] = appVSystem32Catroot2Path;
|
||||
m_map[L"AppVSystems32DriversEtc"] = appVSystem32DriversEtcPath;
|
||||
m_map[L"AppVSystems32Driverstore"] = appVSystem32DriverstorePath;
|
||||
m_map[L"AppVSystems32Logfiles"] = appVSystem32LogfilesPath;
|
||||
m_map[L"AppVSystems32Spool"] = appVSystem32SpoolPath;
|
||||
|
||||
#ifdef _WIN64
|
||||
TextOle<WCHAR> programFilesX64Path;
|
||||
TextOle<WCHAR> programFilesCommonX64Path;
|
||||
TextOle<WCHAR> systemX64Path;
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_System, 0, NULL, &systemX64Path));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesCommonX64, 0, NULL, &programFilesCommonX64Path));
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFilesX64, 0, NULL, &programFilesX64Path));
|
||||
m_map[L"ProgramFilesX64"] = std::wstring(programFilesX64Path.Get());
|
||||
m_map[L"ProgramFilesCommonX64"] = std::wstring(programFilesCommonX64Path.Get());
|
||||
m_map[L"SystemX64"] = std::wstring(systemX64Path.Get());
|
||||
#endif
|
||||
|
||||
TextOle<WCHAR> programFilesPath;
|
||||
RETURN_IF_FAILED(SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, NULL, &programFilesPath));
|
||||
|
||||
m_msixCoreDirectory = std::wstring(programFilesPath.Get()) + std::wstring(L"\\MsixCoreApps\\");
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#pragma once
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include "GeneralUtil.hpp"
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// FilePath Mappings maps the VFS tokens to the actual folder on disk.
|
||||
/// For instance, VFS token: Windows, actual folder: C:\windows
|
||||
class FilePathMappings
|
||||
{
|
||||
public:
|
||||
static FilePathMappings& GetInstance();
|
||||
|
||||
std::map < std::wstring, std::wstring > GetMap() { return m_map; }
|
||||
|
||||
std::wstring GetMsixCoreDirectory() { return m_msixCoreDirectory; }
|
||||
|
||||
/// Gets the resolved full path to the executable.
|
||||
/// The executable path could be the location of the exe within the package's MsixCore directory
|
||||
/// or it could point to a VFS location.
|
||||
/// @param packageExecutablePath - The path to the executable listed in the package's manifest
|
||||
/// This is relative to the package's MsixCore directory.
|
||||
/// @param packageFullName - The packageFullName
|
||||
/// @return the resolved full path to the executable
|
||||
std::wstring GetExecutablePath(std::wstring packageExecutablePath, PCWSTR packageFullName);
|
||||
HRESULT GetInitializationResult() { return m_initializationResult; }
|
||||
private:
|
||||
// Disallow creating an instance of this object
|
||||
HRESULT InitializePaths();
|
||||
FilePathMappings()
|
||||
{
|
||||
m_initializationResult = InitializePaths();
|
||||
}
|
||||
private:
|
||||
std::map < std::wstring, std::wstring > m_map;
|
||||
std::wstring m_msixCoreDirectory;
|
||||
bool m_isInitialized = false;
|
||||
HRESULT m_initializationResult = E_NOT_SET;
|
||||
};
|
||||
}
|
||||
/// Removes the first directory from a path.
|
||||
/// @param path - A path that contains at least one parent directory
|
||||
void MsixCoreLib_GetPathChild(std::wstring &path);
|
||||
|
||||
/// Removes the innermost child file from a path
|
||||
/// @param path - A file path
|
||||
void MsixCoreLib_GetPathParent(std::wstring &path);
|
|
@ -0,0 +1,378 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "FileTypeAssociation.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "RegistryKey.hpp"
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
// MSIXWindows.hpp defines NOMINMAX and undefines min and max because we want to use std::min/std::max from <algorithm>
|
||||
// GdiPlus.h requires a definiton for min and max. We can't use namespace std because c++17 defines std::byte, which conflicts with ::byte
|
||||
#define max std::max
|
||||
#define min std::min
|
||||
#include <GdiPlus.h>
|
||||
using namespace std;
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR FileTypeAssociation::HandlerName = L"FileTypeAssociation";
|
||||
|
||||
std::wstring FileTypeAssociation::CreateProgID(PCWSTR name)
|
||||
{
|
||||
std::wstring packageFullName = m_msixRequest->GetPackageInfo()->GetPackageFullName();
|
||||
std::wstring progID = msixCoreProgIDPrefix + 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,
|
||||
"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->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;
|
||||
}
|
||||
|
||||
HRESULT FileTypeAssociation::ParseManifest()
|
||||
{
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> extensionEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum));
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> extensionElement;
|
||||
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
||||
Text<wchar_t> extensionCategory;
|
||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory));
|
||||
|
||||
if (wcscmp(extensionCategory.Get(), ftaCategoryNameInManifest.c_str()) == 0)
|
||||
{
|
||||
BOOL hc_fta;
|
||||
ComPtr<IMsixElementEnumerator> ftaEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(ftaQuery.c_str(), &ftaEnum));
|
||||
RETURN_IF_FAILED(ftaEnum->GetHasCurrent(&hc_fta));
|
||||
|
||||
if (hc_fta)
|
||||
{
|
||||
ComPtr<IMsixElement> ftaElement;
|
||||
RETURN_IF_FAILED(ftaEnum->GetCurrent(&ftaElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseFtaElement(ftaElement.Get()));
|
||||
}
|
||||
}
|
||||
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FileTypeAssociation::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
for (auto fta = m_Ftas.begin(); fta != m_Ftas.end(); ++fta)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessFtaForAdd(*fta));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
/// Creates an .ico file next to the logo path which is .png by simply prepending the ICO header.
|
||||
HRESULT ConvertLogoToIcon(std::wstring logoPath, std::wstring & iconPath)
|
||||
{
|
||||
experimental::filesystem::path path(logoPath);
|
||||
iconPath = path.replace_extension(L".ico");
|
||||
|
||||
bool fileExists = false;
|
||||
RETURN_IF_FAILED(FileExists(iconPath, fileExists));
|
||||
if (fileExists)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
uintmax_t size = experimental::filesystem::file_size(logoPath);
|
||||
ifstream input(logoPath, std::ios::binary);
|
||||
ofstream output(iconPath, std::ios::binary);
|
||||
|
||||
Gdiplus::Image image(logoPath.c_str());
|
||||
|
||||
//See https://en.wikipedia.org/wiki/ICO_(file_format)
|
||||
BYTE iconHeader[] = {
|
||||
0,0, // reserved
|
||||
1,0, // 1 for .ico icon
|
||||
1,0, // 1 image in file
|
||||
static_cast<BYTE>(image.GetWidth()), // width
|
||||
static_cast<BYTE>(image.GetHeight()), // height
|
||||
0, // colors in color palette
|
||||
0, // reserved
|
||||
1,0, // color planes
|
||||
32,0, // bits per pixel
|
||||
0,0,0,0, // size of image in bytes
|
||||
0,0,0,0 }; // offset from start of file of actual image
|
||||
|
||||
// fill in size
|
||||
iconHeader[14] = static_cast<BYTE>(size % 256);
|
||||
iconHeader[15] = static_cast<BYTE>(size / 256);
|
||||
|
||||
// fill in offset from start of file, which is the size of this header
|
||||
iconHeader[18] = static_cast<BYTE>(ARRAYSIZE(iconHeader));
|
||||
|
||||
for (int i = 0; i < ARRAYSIZE(iconHeader); i++)
|
||||
{
|
||||
output << iconHeader[i];
|
||||
}
|
||||
output << input.rdbuf();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FileTypeAssociation::ProcessFtaForAdd(Fta& fta)
|
||||
{
|
||||
bool needToProcessAnyExtensions = false;
|
||||
for (auto extensionName = fta.extensions.begin(); extensionName != fta.extensions.end(); ++extensionName)
|
||||
{
|
||||
if (m_msixRequest->GetMsixResponse()->GetIsInstallCancelled())
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
|
||||
}
|
||||
|
||||
bool registryHasExtension = false;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetRegistryDevirtualizer()->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.empty())
|
||||
{
|
||||
//DefaultIcon requires an icon type image to display properly on Windows 7 -- convert logo PNG to Icon
|
||||
std::wstring iconPath;
|
||||
RETURN_IF_FAILED(ConvertLogoToIcon(fta.logo, iconPath));
|
||||
|
||||
RegistryKey defaultIconKey;
|
||||
RETURN_IF_FAILED(progIdKey.CreateSubKey(defaultIconKeyName.c_str(), KEY_WRITE, &defaultIconKey));
|
||||
RETURN_IF_FAILED(defaultIconKey.SetStringValue(L"", iconPath.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 executablePath = m_msixRequest->GetPackageDirectoryPath() + L"\\" + m_msixRequest->GetPackageInfo()->GetRelativeExecutableFilePath();
|
||||
std::wstring command = executablePath + commandArgument;
|
||||
RETURN_IF_FAILED(commandKey.SetStringValue(L"", command));
|
||||
|
||||
for (auto 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 = executablePath;
|
||||
if (!verb->parameter.empty())
|
||||
{
|
||||
verbCommand += std::wstring(L" ") + verb->parameter.c_str();
|
||||
}
|
||||
RETURN_IF_FAILED(verbCommandKey.SetStringValue(L"", verbCommand));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FileTypeAssociation::ExecuteForRemoveRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
for (auto 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 (auto extensionName = fta.extensions.begin(); extensionName != fta.extensions.end(); ++extensionName)
|
||||
{
|
||||
bool registryHasExtension = false;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetRegistryDevirtualizer()->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));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
std::wstring registryFilePath = msixRequest->GetPackageDirectoryPath() + registryDatFile;
|
||||
|
||||
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "RegistryKey.hpp"
|
||||
#include "RegistryDevirtualizer.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// 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
|
||||
{
|
||||
public:
|
||||
/// Adds the file type associations to the per-machine registry so this application can handle specific file types.
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the file type associations from the per-machine 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;
|
||||
std::vector<Fta> m_Ftas;
|
||||
|
||||
FileTypeAssociation() {}
|
||||
FileTypeAssociation(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// 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);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,299 @@
|
|||
#include <windows.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <experimental/filesystem>
|
||||
#include <netfw.h>
|
||||
|
||||
#include "FirewallRules.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include "Constants.hpp"
|
||||
|
||||
#pragma comment( lib, "ole32.lib" )
|
||||
#pragma comment( lib, "oleaut32.lib" )
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR FirewallRules::HandlerName = L"FirewallRules";
|
||||
|
||||
HRESULT FirewallRules::ExecuteForAddRequest()
|
||||
{
|
||||
for (auto firewallRule = m_firewallRules.begin(); firewallRule != m_firewallRules.end(); ++firewallRule)
|
||||
{
|
||||
RETURN_IF_FAILED(AddFirewallRules(*firewallRule));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FirewallRules::ExecuteForRemoveRequest()
|
||||
{
|
||||
for (auto firewallRule = m_firewallRules.begin(); firewallRule != m_firewallRules.end(); ++firewallRule)
|
||||
{
|
||||
//Create rule name
|
||||
std::wstring packageDisplayName = m_msixRequest->GetPackageInfo()->GetDisplayName();
|
||||
std::wstring ruleNameString = packageDisplayName.data();
|
||||
ruleNameString.append(L" (");
|
||||
ruleNameString.append(firewallRule->protocol);
|
||||
ruleNameString.append(L"-");
|
||||
ruleNameString.append(firewallRule->direction);
|
||||
ruleNameString.append(L")");
|
||||
|
||||
BSTR ruleName = SysAllocString(ruleNameString.data());
|
||||
|
||||
RETURN_IF_FAILED(RemoveFirewallRules(ruleName));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FirewallRules::RemoveFirewallRules(BSTR firewallRuleName)
|
||||
{
|
||||
ComPtr<INetFwPolicy2> pNetFwPolicy2;
|
||||
ComPtr<INetFwRules> pFwRules;
|
||||
ComPtr<INetFwRule> pFwRule;
|
||||
|
||||
// Retrieve INetFwPolicy2
|
||||
RETURN_IF_FAILED(WFCOMInitialize(&pNetFwPolicy2));
|
||||
|
||||
// Retrieve INetFwRules
|
||||
RETURN_IF_FAILED(pNetFwPolicy2->get_Rules(&pFwRules));
|
||||
|
||||
// Create a new Firewall Rule object.
|
||||
RETURN_IF_FAILED(CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwRule), (void**)&pFwRule));
|
||||
|
||||
RETURN_IF_FAILED(pFwRules->Remove(firewallRuleName));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FirewallRules::ParseManifest()
|
||||
{
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> firewallRuleEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(firewallExtensionQuery.c_str(), &firewallRuleEnum));
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(firewallRuleEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
FirewallRule firewallRule;
|
||||
ComPtr<IMsixElement> ruleElement;
|
||||
RETURN_IF_FAILED(firewallRuleEnum->GetCurrent(&ruleElement));
|
||||
|
||||
Text<wchar_t> direction;
|
||||
RETURN_IF_FAILED(ruleElement->GetAttributeValue(firewallRuleDirectionAttribute.c_str(), &direction));
|
||||
firewallRule.direction = direction.Get();
|
||||
|
||||
Text<wchar_t> protocol;
|
||||
RETURN_IF_FAILED(ruleElement->GetAttributeValue(protocolAttribute.c_str(), &protocol));
|
||||
firewallRule.protocol = protocol.Get();
|
||||
|
||||
Text<wchar_t> profile;
|
||||
RETURN_IF_FAILED(ruleElement->GetAttributeValue(profileAttribute.c_str(), &profile));
|
||||
firewallRule.profile = profile.Get();
|
||||
|
||||
Text<wchar_t> localPortMin, localPortMax, remotePortMin, remotePortMax;
|
||||
RETURN_IF_FAILED(ruleElement->GetAttributeValue(localPortMinAttribute.c_str(), &localPortMin));
|
||||
if (localPortMin.Get() != nullptr)
|
||||
{
|
||||
firewallRule.localPortMin = localPortMin.Get();
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(ruleElement->GetAttributeValue(localPortMaxAttribute.c_str(), &localPortMax));
|
||||
if (localPortMax.Get() != nullptr)
|
||||
{
|
||||
firewallRule.localPortMax = localPortMax.Get();
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(ruleElement->GetAttributeValue(remotePortMinAttribute.c_str(), &remotePortMin));
|
||||
if (remotePortMin.Get() != nullptr)
|
||||
{
|
||||
firewallRule.remotePortMin = remotePortMin.Get();
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(ruleElement->GetAttributeValue(remotePortMaxAttribute.c_str(), &remotePortMax));
|
||||
if (remotePortMax.Get() != nullptr)
|
||||
{
|
||||
firewallRule.remotePortMax = remotePortMax.Get();
|
||||
}
|
||||
|
||||
m_firewallRules.push_back(firewallRule);
|
||||
|
||||
RETURN_IF_FAILED(firewallRuleEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FirewallRules::AddFirewallRules(FirewallRule& firewallRule)
|
||||
{
|
||||
ComPtr<INetFwPolicy2> pNetFwPolicy2;
|
||||
ComPtr<INetFwRules> pFwRules;
|
||||
ComPtr<INetFwRule> pFwRule;
|
||||
|
||||
// Retrieve INetFwPolicy2
|
||||
RETURN_IF_FAILED(WFCOMInitialize(&pNetFwPolicy2));
|
||||
|
||||
// Retrieve INetFwRules
|
||||
RETURN_IF_FAILED(pNetFwPolicy2->get_Rules(&pFwRules));
|
||||
|
||||
// Create a new Firewall Rule object.
|
||||
RETURN_IF_FAILED(CoCreateInstance(__uuidof(NetFwRule), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwRule), (void**)&pFwRule));
|
||||
|
||||
// Create rule name
|
||||
std::wstring packageDisplayName = m_msixRequest->GetPackageInfo()->GetDisplayName();
|
||||
|
||||
std::wstring ruleNameString = packageDisplayName.data();
|
||||
ruleNameString.append(L" (");
|
||||
ruleNameString.append(firewallRule.protocol.c_str());
|
||||
ruleNameString.append(L"-");
|
||||
ruleNameString.append(firewallRule.direction.c_str());
|
||||
ruleNameString.append(L")");
|
||||
|
||||
BSTR ruleName = SysAllocString(ruleNameString.data());
|
||||
BSTR ruleDescription = SysAllocString(packageDisplayName.data());
|
||||
RETURN_IF_FAILED(pFwRule->put_Name(ruleName));
|
||||
RETURN_IF_FAILED(pFwRule->put_Description(ruleDescription));
|
||||
RETURN_IF_FAILED(pFwRule->put_Enabled(VARIANT_TRUE));
|
||||
|
||||
RETURN_IF_FAILED(pFwRule->put_Action(NET_FW_ACTION_ALLOW));
|
||||
|
||||
std::wstring resolvedExecutableFullPath = m_msixRequest->GetPackageDirectoryPath() + L"\\" + m_msixRequest->GetPackageInfo()->GetDisplayName();
|
||||
BSTR applicationName = SysAllocString(resolvedExecutableFullPath.data());
|
||||
RETURN_IF_FAILED(pFwRule->put_ApplicationName(applicationName));
|
||||
|
||||
//Map protocol
|
||||
RETURN_IF_FAILED(pFwRule->put_Protocol(ConvertToProtocol(firewallRule.protocol.c_str())));
|
||||
|
||||
//Local ports
|
||||
if (!firewallRule.localPortMin.empty() && !firewallRule.localPortMax.empty() )
|
||||
{
|
||||
std::wstring localPortRange = firewallRule.localPortMin.c_str();
|
||||
localPortRange.append(L"-");
|
||||
localPortRange.append(firewallRule.localPortMax.c_str());
|
||||
BSTR localPorts = SysAllocString(localPortRange.data());
|
||||
RETURN_IF_FAILED(pFwRule->put_LocalPorts(localPorts));
|
||||
}
|
||||
|
||||
//Remote ports
|
||||
if (!firewallRule.remotePortMin.empty() && !firewallRule.remotePortMax.empty())
|
||||
{
|
||||
std::wstring remotePortRange = firewallRule.remotePortMin.c_str();
|
||||
remotePortRange.append(L"-");
|
||||
remotePortRange.append(firewallRule.remotePortMax.c_str());
|
||||
BSTR remotePorts = SysAllocString(remotePortRange.data());
|
||||
RETURN_IF_FAILED(pFwRule->put_RemotePorts(remotePorts));
|
||||
}
|
||||
|
||||
//Map direction
|
||||
if (_wcsicmp(firewallRule.direction.c_str(), directionIn.c_str()) == 0)
|
||||
{
|
||||
RETURN_IF_FAILED(pFwRule->put_Direction(NET_FW_RULE_DIR_IN));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(pFwRule->put_Direction(NET_FW_RULE_DIR_OUT));
|
||||
}
|
||||
|
||||
//Map profile
|
||||
RETURN_IF_FAILED(pFwRule->put_Profiles(ConvertToProfileType(firewallRule.profile.c_str())));
|
||||
|
||||
// Populate the Firewall Rule object
|
||||
RETURN_IF_FAILED(pFwRules->Add(pFwRule.Get()));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
unsigned short FirewallRules::ConvertToProtocol(PCWSTR key)
|
||||
{
|
||||
struct ProtocolMapping
|
||||
{
|
||||
PCWSTR key;
|
||||
unsigned short value;
|
||||
};
|
||||
|
||||
ProtocolMapping protocolMap[] =
|
||||
{
|
||||
{ L"ICMPv4", 1 },
|
||||
{ L"ICMPv6", 58 },
|
||||
{ L"TCP", 6 },
|
||||
{ L"UDP", 17 },
|
||||
{ L"GRE", 47 },
|
||||
{ L"AH", 51 },
|
||||
{ L"ESP", 50 },
|
||||
{ L"EGP", 8 },
|
||||
{ L"GGP", 3 },
|
||||
{ L"HMP", 20 },
|
||||
{ L"IGMP", 2 },
|
||||
{ L"RVD", 66 },
|
||||
{ L"OSPFIGP", 89 },
|
||||
{ L"PUP", 12 },
|
||||
{ L"RDP", 27 },
|
||||
{ L"RSVP", 46 }
|
||||
};
|
||||
|
||||
for (ProtocolMapping& mapping : protocolMap)
|
||||
{
|
||||
if (_wcsicmp(mapping.key, key) == 0)
|
||||
{
|
||||
return mapping.value;
|
||||
}
|
||||
}
|
||||
return NET_FW_IP_PROTOCOL_ANY;
|
||||
}
|
||||
|
||||
NET_FW_PROFILE_TYPE2 FirewallRules::ConvertToProfileType(PCWSTR key)
|
||||
{
|
||||
struct ProfileMapping
|
||||
{
|
||||
PCWSTR key;
|
||||
NET_FW_PROFILE_TYPE2 value;
|
||||
};
|
||||
|
||||
ProfileMapping profileMap[] =
|
||||
{
|
||||
{L"domain", NET_FW_PROFILE2_DOMAIN},
|
||||
{L"private", NET_FW_PROFILE2_PRIVATE},
|
||||
{L"public", NET_FW_PROFILE2_PUBLIC},
|
||||
{L"domainAndPrivate", static_cast<NET_FW_PROFILE_TYPE2>(NET_FW_PROFILE2_DOMAIN | NET_FW_PROFILE2_PRIVATE)},
|
||||
{L"all", NET_FW_PROFILE2_ALL}
|
||||
};
|
||||
|
||||
for (ProfileMapping& mapping : profileMap)
|
||||
{
|
||||
if (_wcsicmp(mapping.key, key) == 0)
|
||||
{
|
||||
return mapping.value;
|
||||
}
|
||||
}
|
||||
return NET_FW_PROFILE2_ALL;
|
||||
}
|
||||
|
||||
HRESULT FirewallRules::WFCOMInitialize(INetFwPolicy2 ** ppNetFwPolicy2)
|
||||
{
|
||||
RETURN_IF_FAILED(CoCreateInstance(__uuidof(NetFwPolicy2), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwPolicy2), (void**)ppNetFwPolicy2));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FirewallRules::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<FirewallRules > localInstance(new FirewallRules(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include <netfw.h>
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// the firewall rule structure
|
||||
struct FirewallRule
|
||||
{
|
||||
std::wstring direction;
|
||||
std::wstring protocol;
|
||||
std::wstring profile;
|
||||
std::wstring localPortMin;
|
||||
std::wstring localPortMax;
|
||||
std::wstring remotePortMin;
|
||||
std::wstring remotePortMax;
|
||||
};
|
||||
|
||||
class FirewallRules : IPackageHandler
|
||||
{
|
||||
public:
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~FirewallRules() {}
|
||||
private:
|
||||
MsixRequest * m_msixRequest = nullptr;
|
||||
std::vector<FirewallRule> m_firewallRules;
|
||||
|
||||
FirewallRules() {}
|
||||
FirewallRules(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Parses the manifest and fills in the m_firewallRules vector
|
||||
HRESULT ParseManifest();
|
||||
|
||||
/// Adds the firewall rules from the manifest
|
||||
///
|
||||
/// @param firewallRule - the firewall rule object to add
|
||||
HRESULT AddFirewallRules(FirewallRule& firewallRule);
|
||||
|
||||
/// Removes the firewall rule passed
|
||||
///
|
||||
/// @param firewallRuleName - the name of the firewall rule to remove
|
||||
HRESULT RemoveFirewallRules(BSTR firewallRuleName);
|
||||
|
||||
HRESULT WFCOMInitialize(INetFwPolicy2** ppNetFwPolicy2);
|
||||
|
||||
/// Returns the protocol as a 'short' type to be added to the firewall rule
|
||||
///
|
||||
/// @param key - the protocol from the manifest
|
||||
unsigned short ConvertToProtocol(PCWSTR key);
|
||||
|
||||
/// Returns the profile as a 'NET_FW_PROFILE_TYPE2' type to be added to the firewall rule
|
||||
///
|
||||
/// @param key - the profile from the manifest
|
||||
NET_FW_PROFILE_TYPE2 ConvertToProfileType(PCWSTR key);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
#ifndef FOOTPRINTFILES_H
|
||||
#define FOOTPRINTFILES_H
|
||||
|
||||
#include "AppxPackaging.hpp"
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
// Describes the FootprintFilesType structure
|
||||
template<typename Type>
|
||||
struct FootprintFilesType
|
||||
{
|
||||
Type fileType;
|
||||
const char* description;
|
||||
bool isRequired;
|
||||
};
|
||||
|
||||
// Types of footprint files in an app package
|
||||
__declspec(selectany) FootprintFilesType<APPX_FOOTPRINT_FILE_TYPE> g_footprintFilesType[] =
|
||||
{
|
||||
{ APPX_FOOTPRINT_FILE_TYPE_MANIFEST, "manifest", true },
|
||||
{ APPX_FOOTPRINT_FILE_TYPE_BLOCKMAP, "block map", true },
|
||||
{ APPX_FOOTPRINT_FILE_TYPE_SIGNATURE, "digital signature", true },
|
||||
{ APPX_FOOTPRINT_FILE_TYPE_CODEINTEGRITY, "CI catalog", false }, // this is ONLY required if there exists 1+ PEs
|
||||
};
|
||||
const int FootprintFilesCount = ARRAYSIZE(g_footprintFilesType);
|
||||
#endif
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include "Package.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// Interface for a logical chunk of work done on an Msix request
|
||||
class IPackageHandler
|
||||
{
|
||||
public:
|
||||
|
||||
// Add request adds the package as per-machine. Everything is written to per-machine shared locations.
|
||||
virtual HRESULT ExecuteForAddRequest() = 0;
|
||||
|
||||
// Removes packages added by ExecuteForAddRequest.
|
||||
virtual HRESULT ExecuteForRemoveRequest() { return S_OK; }
|
||||
|
||||
virtual ~IPackageHandler() {};
|
||||
};
|
||||
|
||||
/// Function responsible for creating an instance of an IPackageHandler object
|
||||
/// @param msixRequest - the msix request object to act upon
|
||||
/// @return S_OK if CreateHandler is to not fail the deployment of the package.
|
||||
typedef HRESULT(*CreateHandler)(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "InstallComplete.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR InstallComplete::HandlerName = L"InstallComplete";
|
||||
|
||||
HRESULT InstallComplete::ExecuteForAddRequest()
|
||||
{
|
||||
auto msixResponse = m_msixRequest->GetMsixResponse();
|
||||
if (msixResponse != nullptr && !msixResponse->GetIsInstallCancelled())
|
||||
{
|
||||
msixResponse->Update(InstallationStep::InstallationStepCompleted, 100);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT InstallComplete::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<InstallComplete> localInstance(new InstallComplete(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class InstallComplete : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Send install complete message to UI
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~InstallComplete() {}
|
||||
private:
|
||||
MsixRequest * m_msixRequest = nullptr;
|
||||
|
||||
InstallComplete() {}
|
||||
InstallComplete(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,721 @@
|
|||
#include "InstallUI.hpp"
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <commctrl.h>
|
||||
#include <sys/types.h>
|
||||
#include <thread>
|
||||
#include <shldisp.h>
|
||||
#include <shlobj.h>
|
||||
#include <exdisp.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <iostream>
|
||||
#include "resource.h"
|
||||
#include <filesystem>
|
||||
|
||||
#include "Util.hpp"
|
||||
#include "msixmgrLogger.hpp"
|
||||
// MSIXWindows.hpp defines NOMINMAX and undefines min and max because we want to use std::min/std::max from <algorithm>
|
||||
// GdiPlus.h requires a definiton for min and max. We can't use namespace std because c++17 defines std::byte, which conflicts with ::byte
|
||||
#define max std::max
|
||||
#define min std::min
|
||||
#include <GdiPlus.h>
|
||||
using namespace std;
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
|
||||
static const int g_width = 500; // width of window
|
||||
static const int g_height = 400; // height of window
|
||||
|
||||
//
|
||||
// PURPOSE: This compiles the information displayed on the UI when the user selects an msix
|
||||
//
|
||||
// hWnd: the HWND of the window to draw controls
|
||||
// windowRect: the size of the window
|
||||
|
||||
HRESULT UI::DrawPackageInfo(HWND hWnd, RECT windowRect)
|
||||
{
|
||||
if (SUCCEEDED(m_loadingPackageInfoCode))
|
||||
{
|
||||
auto displayText = m_installOrUpdateText + L" " + m_displayName + L"?";
|
||||
auto messageText = L"Publisher: " + m_publisherCommonName + L"\nVersion: " + m_version;
|
||||
ChangeText(hWnd, displayText, messageText, m_logoStream.get());
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wstringstream wstringstream;
|
||||
wstringstream << L"Failed getting package information with: 0x" << std::hex << m_loadingPackageInfoCode;
|
||||
auto g_messageText = wstringstream.str();
|
||||
ChangeText(hWnd, L"Loading Package failed", g_messageText);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// FUNCTION: WndProc(HWND, UINT, WPARAM, LPARAM)
|
||||
//
|
||||
// PURPOSE: Processes messages for the main window.
|
||||
//
|
||||
// WM_PAINT - Paint the main window
|
||||
// WM_DESTROY - post a quit message and return
|
||||
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
|
||||
{
|
||||
UI* ui = (UI*)GetWindowLongPtr(hWnd, GWLP_USERDATA);
|
||||
RECT windowRect;
|
||||
GetClientRect(hWnd, &windowRect);
|
||||
switch (message)
|
||||
{
|
||||
case WM_CREATE:
|
||||
ui->CreateCheckbox(hWnd, windowRect);
|
||||
ui->InstallButton(hWnd, windowRect);
|
||||
ui->CreateLaunchButton(hWnd, windowRect, 275, 60);
|
||||
ui->CreateDisplayPercentageText(hWnd, windowRect);
|
||||
ui->CreateDisplayErrorText(hWnd, windowRect);
|
||||
break;
|
||||
case WM_PAINT:
|
||||
{
|
||||
if (ui != NULL)
|
||||
{
|
||||
ui->DrawPackageInfo(hWnd, windowRect);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_COMMAND:
|
||||
switch (LOWORD(wParam))
|
||||
{
|
||||
case IDC_INSTALLBUTTON:
|
||||
{
|
||||
g_installing = true;
|
||||
DestroyWindow(g_LaunchbuttonHWnd);
|
||||
DestroyWindow(g_buttonHWnd);
|
||||
ui->CreateCancelButton(hWnd, windowRect);
|
||||
UpdateWindow(hWnd);
|
||||
ui->CreateProgressBar(hWnd, windowRect);
|
||||
ShowWindow(g_progressHWnd, SW_SHOW); //Show progress bar only when install is clicked
|
||||
ui->ButtonClicked();
|
||||
}
|
||||
break;
|
||||
case IDC_LAUNCHCHECKBOX:
|
||||
{
|
||||
if (SendMessage(GetDlgItem(hWnd, IDC_LAUNCHCHECKBOX), BM_GETCHECK, 0, 0) == BST_CHECKED)
|
||||
{
|
||||
g_launchCheckBoxState = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_launchCheckBoxState = false;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case IDC_CANCELBUTTON:
|
||||
{
|
||||
ui->ConfirmAppCancel(hWnd);
|
||||
break;
|
||||
}
|
||||
case IDC_LAUNCHBUTTON:
|
||||
{
|
||||
ui->LaunchInstalledApp();
|
||||
ui->CloseUI();
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_INSTALLCOMPLETE_MSG:
|
||||
{
|
||||
g_installing = false; // installation complete, clicking on 'x' should not show the cancellation popup
|
||||
if (g_launchCheckBoxState)
|
||||
{
|
||||
ui->LaunchInstalledApp(); // launch app
|
||||
ui->CloseUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
RECT windowRect;
|
||||
GetClientRect(hWnd, &windowRect);
|
||||
DestroyWindow(g_CancelbuttonHWnd);
|
||||
ui->CreateLaunchButton(hWnd, windowRect, 150, 60);
|
||||
UpdateWindow(hWnd);
|
||||
ShowWindow(g_progressHWnd, SW_HIDE); //hide progress bar
|
||||
ShowWindow(g_checkboxHWnd, SW_HIDE); //hide launch check box
|
||||
ShowWindow(g_percentageTextHWnd, SW_HIDE);
|
||||
ShowWindow(g_staticPercentText, SW_HIDE);
|
||||
ShowWindow(g_LaunchbuttonHWnd, SW_SHOW);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case WM_CTLCOLORSTATIC:
|
||||
{
|
||||
HBRUSH hbr = (HBRUSH)DefWindowProc(hWnd, message, wParam, lParam);
|
||||
::DeleteObject(hbr);
|
||||
SetBkMode((HDC)wParam, TRANSPARENT);
|
||||
return (LRESULT)::GetStockObject(NULL_BRUSH);
|
||||
break;
|
||||
}
|
||||
case WM_CLOSE:
|
||||
//show popup asking if user wants to stop installation only during app installation
|
||||
if (g_installing)
|
||||
{
|
||||
ui->ConfirmAppCancel(hWnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
DestroyWindow(hWnd);
|
||||
}
|
||||
break;
|
||||
case WM_SIZE:
|
||||
case WM_SIZING:
|
||||
break;
|
||||
case WM_DESTROY:
|
||||
PostQuitMessage(0);
|
||||
exit(0);
|
||||
break;
|
||||
default:
|
||||
return DefWindowProc(hWnd, message, wParam, lParam);
|
||||
break;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void UI::ConfirmAppCancel(HWND hWnd)
|
||||
{
|
||||
const int cancelResult = MessageBox(hWnd, m_cancelPopUpMessage.c_str(), m_cancelPopUpTitle.c_str(), MB_YESNO);
|
||||
switch (cancelResult)
|
||||
{
|
||||
case IDYES:
|
||||
m_msixResponse->CancelRequest();
|
||||
break;
|
||||
case IDNO:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Avoid launching the app elevated, we need to ShellExecute from Explorer in order to launch as the currently logged in user.
|
||||
// See https://devblogs.microsoft.com/oldnewthing/?p=2643
|
||||
// ExecInExplorer.cpp sample from https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/dd940355(v=vs.85)
|
||||
HRESULT GetDesktopAutomationObject(REFIID riid, void **ppv)
|
||||
{
|
||||
ComPtr<IShellWindows> shellWindows;
|
||||
RETURN_IF_FAILED(CoCreateInstance(CLSID_ShellWindows, NULL, CLSCTX_LOCAL_SERVER, IID_PPV_ARGS(&shellWindows)));
|
||||
|
||||
HWND hwnd;
|
||||
ComPtr<IDispatch> dispatch;
|
||||
VARIANT vEmpty = {}; // VT_EMPTY
|
||||
RETURN_IF_FAILED(shellWindows->FindWindowSW(&vEmpty /*varLoc*/, &vEmpty /*varLocRoot*/, SWC_DESKTOP, (long*)&hwnd, SWFO_NEEDDISPATCH, &dispatch));
|
||||
|
||||
ComPtr<IServiceProvider> serviceProvider;
|
||||
RETURN_IF_FAILED(dispatch->QueryInterface(IID_PPV_ARGS(&serviceProvider)));
|
||||
|
||||
ComPtr<IShellBrowser> shellBrowser;
|
||||
RETURN_IF_FAILED(serviceProvider->QueryService(SID_STopLevelBrowser, &shellBrowser));
|
||||
|
||||
ComPtr<IShellView> shellView;
|
||||
RETURN_IF_FAILED(shellBrowser->QueryActiveShellView(&shellView));
|
||||
|
||||
ComPtr<IDispatch> dispatchView;
|
||||
RETURN_IF_FAILED(shellView->GetItemObject(SVGIO_BACKGROUND, IID_PPV_ARGS(&dispatchView)));
|
||||
RETURN_IF_FAILED(dispatchView->QueryInterface(riid, ppv));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ShellExecuteFromExplorer(PCWSTR pszFile)
|
||||
{
|
||||
ComPtr<IShellFolderViewDual> folderView;
|
||||
RETURN_IF_FAILED(GetDesktopAutomationObject(IID_PPV_ARGS(&folderView)));
|
||||
ComPtr<IDispatch> dispatch;
|
||||
RETURN_IF_FAILED(folderView->get_Application(&dispatch));
|
||||
|
||||
Bstr bstrfile(pszFile);
|
||||
VARIANT vtEmpty = {}; // VT_EMPTY
|
||||
ComPtr<IShellDispatch2> shellDispatch;
|
||||
RETURN_IF_FAILED(dispatch->QueryInterface(IID_PPV_ARGS(&shellDispatch)));
|
||||
RETURN_IF_FAILED(shellDispatch->ShellExecute(bstrfile, vtEmpty /*arguments*/, vtEmpty /*directory*/, vtEmpty /*operation*/, vtEmpty /*show*/));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT UI::LaunchInstalledApp()
|
||||
{
|
||||
shared_ptr<IInstalledPackage> installedPackage;
|
||||
RETURN_IF_FAILED(m_packageManager->FindPackage(m_packageInfo->GetPackageFullName(), installedPackage));
|
||||
|
||||
HRESULT hrShellExecute = ShellExecuteFromExplorer(installedPackage->GetFullExecutableFilePath().c_str());
|
||||
if (FAILED(hrShellExecute))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixUITraceLoggingProvider,
|
||||
"ShellExecute Failed",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrShellExecute, "HR"));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void StartParseFile(HWND hWnd)
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
if (result != 0)
|
||||
{
|
||||
std::cout << "Error: " << std::hex << result << " while extracting the appx package" << std::endl;
|
||||
Text<char> text;
|
||||
auto logResult = GetLogTextUTF8(MyAllocate, &text);
|
||||
if (0 == logResult)
|
||||
{
|
||||
std::cout << "LOG:" << std::endl << text.content << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "UNABLE TO GET LOG WITH HR=" << std::hex << logResult << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CommandFunc(HWND hWnd, RECT windowRect) {
|
||||
std::thread t1(StartParseFile, hWnd);
|
||||
t1.detach();
|
||||
return;
|
||||
}
|
||||
|
||||
void StartUIThread(UI* ui)
|
||||
{
|
||||
// Free the console that we started with
|
||||
FreeConsole();
|
||||
|
||||
// Register WindowClass and create the window
|
||||
HINSTANCE hInstance = GetModuleHandle(NULL);
|
||||
|
||||
std::wstring windowClass = GetStringResource(IDS_STRING_UI_TITLE);
|
||||
std::wstring title = GetStringResource(IDS_STRING_UI_TITLE);
|
||||
|
||||
WNDCLASSEX wcex;
|
||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcex.lpfnWndProc = WndProc;
|
||||
wcex.cbClsExtra = 0;
|
||||
wcex.cbWndExtra = 0;
|
||||
wcex.hInstance = hInstance;
|
||||
wcex.hIcon = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICONBIG));
|
||||
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
|
||||
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
|
||||
wcex.lpszMenuName = NULL;
|
||||
wcex.lpszClassName = windowClass.c_str();
|
||||
wcex.hIconSm = LoadIcon(wcex.hInstance, MAKEINTRESOURCE(IDI_ICONSMALL));
|
||||
|
||||
if (!RegisterClassEx(&wcex))
|
||||
{
|
||||
MessageBox(NULL, L"Call to RegisterClassEx failed!", title.c_str(), NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
Gdiplus::GdiplusStartupInput gdiplusStartupInput;
|
||||
ULONG_PTR gdiplusToken;
|
||||
Gdiplus::GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
|
||||
ui->CreateInitWindow(hInstance, SW_SHOWNORMAL, windowClass, title);
|
||||
Gdiplus::GdiplusShutdown(gdiplusToken);
|
||||
|
||||
}
|
||||
|
||||
HRESULT UI::ParseInfoFromPackage()
|
||||
{
|
||||
if (m_packageInfo == nullptr)
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case InstallUIAdd:
|
||||
{
|
||||
RETURN_IF_FAILED(m_packageManager->GetMsixPackageInfo(m_path, m_packageInfo));
|
||||
}
|
||||
break;
|
||||
case InstallUIRemove:
|
||||
{
|
||||
shared_ptr<IInstalledPackage> installedPackage;
|
||||
RETURN_IF_FAILED(m_packageManager->FindPackage(m_path, installedPackage));
|
||||
m_packageInfo = installedPackage;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Obtain publisher name
|
||||
m_publisherCommonName = m_packageInfo->GetPublisherDisplayName();
|
||||
|
||||
// Obtain version number
|
||||
m_version = m_packageInfo->GetVersion();
|
||||
|
||||
//Obtain the number of files
|
||||
m_displayName = m_packageInfo->GetDisplayName();
|
||||
m_logoStream = std::move(m_packageInfo->GetLogo());
|
||||
|
||||
//Obtain package capabilities
|
||||
m_capabilities = m_packageInfo->GetCapabilities();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT UI::ShowUI()
|
||||
{
|
||||
m_loadingPackageInfoCode = ParseInfoFromPackage();
|
||||
|
||||
std::thread thread(StartUIThread, this);
|
||||
thread.detach();
|
||||
|
||||
DWORD waitResult = WaitForSingleObject(m_closeUI, INFINITE);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void UI::PreprocessRequest()
|
||||
{
|
||||
if (FAILED(m_loadingPackageInfoCode))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
std::shared_ptr<IInstalledPackage> existingPackage;
|
||||
HRESULT hr = m_packageManager->FindPackageByFamilyName(m_packageInfo->GetPackageFamilyName(), existingPackage);
|
||||
if (existingPackage != nullptr && SUCCEEDED(hr))
|
||||
{
|
||||
if (CaseInsensitiveEquals(existingPackage->GetPackageFullName(), m_packageInfo->GetPackageFullName()))
|
||||
{
|
||||
/// Same package is already installed
|
||||
ChangeInstallButtonText(GetStringResource(IDS_STRING_REINSTALLAPP)); /// change install button text to 'reinstall'
|
||||
ShowWindow(g_LaunchbuttonHWnd, SW_SHOW); /// show launch button window
|
||||
}
|
||||
else
|
||||
{
|
||||
/// Package with same family name exists and may be an update
|
||||
m_installOrUpdateText = GetStringResource(IDS_STRING_UPDATETEXT);
|
||||
m_cancelPopUpMessage = GetStringResource(IDS_STRING_CANCEL_UPDATEPOPUP);
|
||||
ChangeInstallButtonText(GetStringResource(IDS_STRING_UPDATETEXT));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BOOL UI::CreateProgressBar(HWND parentHWnd, RECT parentRect)
|
||||
{
|
||||
int scrollHeight = GetSystemMetrics(SM_CYVSCROLL);
|
||||
|
||||
// Creates progress bar on window
|
||||
g_progressHWnd = CreateWindowEx(
|
||||
0,
|
||||
PROGRESS_CLASS,
|
||||
(LPTSTR)NULL,
|
||||
WS_CHILD,
|
||||
parentRect.left + 50, // x coord
|
||||
parentRect.bottom - scrollHeight - 125, // y coord
|
||||
parentRect.right - 100, // width
|
||||
scrollHeight, // height
|
||||
parentHWnd, // parent
|
||||
(HMENU)0,
|
||||
NULL,
|
||||
NULL
|
||||
);
|
||||
|
||||
// Set defaults for range and increments
|
||||
SendMessage(g_progressHWnd, PBM_SETRANGE, 0, MAKELPARAM(0, 100)); // set range
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL UI::CreateCheckbox(HWND parentHWnd, RECT parentRect)
|
||||
{
|
||||
g_checkboxHWnd = CreateWindowEx(
|
||||
WS_EX_LEFT, // extended window style
|
||||
L"BUTTON",
|
||||
L"Launch when ready", // text
|
||||
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_AUTOCHECKBOX, // style
|
||||
parentRect.left + 50, // x coord
|
||||
parentRect.bottom - 60, // y coord
|
||||
165, // width
|
||||
35, // height
|
||||
parentHWnd, // parent
|
||||
(HMENU)IDC_LAUNCHCHECKBOX, // menu
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
NULL);
|
||||
|
||||
//Set default checkbox state to checked
|
||||
SendMessage(g_checkboxHWnd, BM_SETCHECK, BST_CHECKED, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL UI::InstallButton(HWND parentHWnd, RECT parentRect) {
|
||||
LPVOID buttonPointer = nullptr;
|
||||
g_buttonHWnd = CreateWindowEx(
|
||||
WS_EX_LEFT, // extended window style
|
||||
L"Button",
|
||||
L"Install", // text
|
||||
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_FLAT, // style
|
||||
parentRect.right - 100 - 50, // x coord
|
||||
parentRect.bottom - 60, // y coord
|
||||
120, // width
|
||||
35, // height
|
||||
parentHWnd, // parent
|
||||
(HMENU)IDC_INSTALLBUTTON, // menu
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
buttonPointer); // pointer to button
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL UI::CreateCancelButton(HWND parentHWnd, RECT parentRect)
|
||||
{
|
||||
LPVOID buttonPointer = nullptr;
|
||||
g_CancelbuttonHWnd = CreateWindowEx(
|
||||
WS_EX_LEFT, // extended window style
|
||||
L"BUTTON",
|
||||
L"Cancel", // text
|
||||
WS_TABSTOP | WS_VISIBLE | WS_CHILD | BS_DEFPUSHBUTTON | BS_FLAT, // style
|
||||
parentRect.right - 100 - 50, // x coord
|
||||
parentRect.bottom - 60, // y coord
|
||||
120, // width
|
||||
35, // height
|
||||
parentHWnd, // parent
|
||||
(HMENU)IDC_CANCELBUTTON, // menu
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
buttonPointer); // pointer to button
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL UI::CreateLaunchButton(HWND parentHWnd, RECT parentRect, int xDiff, int yDiff)
|
||||
{
|
||||
LPVOID buttonPointer = nullptr;
|
||||
g_LaunchbuttonHWnd = CreateWindowEx(
|
||||
WS_EX_LEFT, // extended window style
|
||||
L"BUTTON",
|
||||
L"Launch", // text
|
||||
WS_TABSTOP | WS_CHILD | BS_DEFPUSHBUTTON | BS_FLAT, // style
|
||||
parentRect.right - xDiff, // x coord
|
||||
parentRect.bottom - yDiff, // y coord
|
||||
120, // width
|
||||
35, // height
|
||||
parentHWnd, // parent
|
||||
(HMENU)IDC_LAUNCHBUTTON, // menu
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
buttonPointer); // pointer to button
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL UI::CreateDisplayPercentageText(HWND parentHWnd, RECT parentRect)
|
||||
{
|
||||
int scrollHeight = GetSystemMetrics(SM_CYVSCROLL);
|
||||
|
||||
g_percentageTextHWnd = CreateWindowEx(
|
||||
WS_EX_LEFT,
|
||||
L"Static",
|
||||
L"Installing app package",
|
||||
WS_CHILD ,
|
||||
parentRect.left + 50,
|
||||
parentRect.bottom - scrollHeight - 143,
|
||||
175,
|
||||
20,
|
||||
parentHWnd,
|
||||
(HMENU)IDC_STATICPERCENTCONTROL,
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
0);
|
||||
|
||||
g_staticPercentText = CreateWindowEx(
|
||||
WS_EX_LEFT,
|
||||
L"Static",
|
||||
L"0%...",
|
||||
WS_CHILD,
|
||||
parentRect.left + 200,
|
||||
parentRect.bottom - scrollHeight - 143,
|
||||
175,
|
||||
20,
|
||||
parentHWnd,
|
||||
(HMENU)IDC_STATICPERCENTCONTROL,
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL UI::CreateDisplayErrorText(HWND parentHWnd, RECT parentRect)
|
||||
{
|
||||
g_staticErrorTextHWnd = CreateWindowEx(
|
||||
WS_EX_LEFT,
|
||||
L"Static",
|
||||
L"Reason:",
|
||||
WS_CHILD,
|
||||
parentRect.left + 50,
|
||||
parentRect.bottom - 80,
|
||||
120,
|
||||
20,
|
||||
parentHWnd,
|
||||
(HMENU)IDC_STATICERRORCONTROL,
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
0);
|
||||
|
||||
g_staticErrorDescHWnd = CreateWindowEx(
|
||||
WS_EX_LEFT,
|
||||
L"Static",
|
||||
L"",
|
||||
WS_CHILD | WS_BORDER,
|
||||
parentRect.left + 50,
|
||||
parentRect.bottom - 60,
|
||||
375,
|
||||
40,
|
||||
parentHWnd,
|
||||
(HMENU)IDC_STATICERRORCONTROL,
|
||||
reinterpret_cast<HINSTANCE>(GetWindowLongPtr(parentHWnd, GWLP_HINSTANCE)),
|
||||
0);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL UI::ChangeInstallButtonText(const std::wstring& newMessage)
|
||||
{
|
||||
SendMessage(g_buttonHWnd, WM_SETTEXT, NULL, reinterpret_cast<LPARAM>(newMessage.c_str()));
|
||||
return ShowWindow(g_buttonHWnd, SW_SHOW);
|
||||
}
|
||||
|
||||
BOOL UI::ChangeText(HWND parentHWnd, std::wstring displayName, std::wstring messageText, IStream* logoStream)
|
||||
{
|
||||
PAINTSTRUCT paint;
|
||||
HDC deviceContext = BeginPaint(parentHWnd, &paint);
|
||||
|
||||
Gdiplus::Graphics graphics(deviceContext);
|
||||
|
||||
Gdiplus::RectF layoutRect(50, 50, g_width - 144, 100);
|
||||
Gdiplus::Font displayNameFont(L"Arial", 16);
|
||||
Gdiplus::Font messageFont(L"Arial", 10);
|
||||
Gdiplus::StringFormat format;
|
||||
format.SetAlignment(Gdiplus::StringAlignmentNear);
|
||||
auto windowsTextColor = Gdiplus::Color();
|
||||
windowsTextColor.SetFromCOLORREF(GetSysColor(COLOR_WINDOWTEXT));
|
||||
Gdiplus::SolidBrush textBrush(windowsTextColor);
|
||||
|
||||
graphics.DrawString(displayName.c_str(), -1, &displayNameFont, layoutRect, &format, &textBrush);
|
||||
layoutRect.Y += 40;
|
||||
graphics.DrawString(messageText.c_str(), -1, &messageFont, layoutRect, &format, &textBrush);
|
||||
|
||||
std::wstring capabilitiesHeading = L"Capabilties:";
|
||||
layoutRect.Y += 40;
|
||||
graphics.DrawString(capabilitiesHeading.c_str(), -1, &messageFont, layoutRect, &format, &textBrush);
|
||||
|
||||
layoutRect.Y += 17;
|
||||
for (std::wstring capability : m_capabilities)
|
||||
{
|
||||
std::wstring capabilityString = L"\x2022 " + GetStringResource(IDS_RUNFULLTRUST_CAPABILITY);
|
||||
graphics.DrawString(capabilityString.c_str(), -1, &messageFont, layoutRect, &format, &textBrush);
|
||||
layoutRect.Y += 20;
|
||||
}
|
||||
|
||||
if (logoStream != nullptr)
|
||||
{
|
||||
// We shouldn't fail if the image can't be loaded, just don't show it.
|
||||
auto image = Gdiplus::Image::FromStream(logoStream, FALSE);
|
||||
if (image != nullptr)
|
||||
{
|
||||
Gdiplus::Status status = graphics.DrawImage(image, g_width - 200, 25);
|
||||
delete image;
|
||||
}
|
||||
}
|
||||
|
||||
EndPaint(parentHWnd, &paint);
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
int UI::CreateInitWindow(HINSTANCE hInstance, int nCmdShow, const std::wstring& windowClass, const std::wstring& title)
|
||||
{
|
||||
HWND hWnd = CreateWindow(
|
||||
const_cast<wchar_t*>(windowClass.c_str()),
|
||||
const_cast<wchar_t*>(title.c_str()),
|
||||
WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
g_width, // width of window
|
||||
g_height, // height of window
|
||||
NULL, // A handle to the parent or owner window of the window being created
|
||||
NULL, // a handle to a menu, or specifies a child-window identifier depending on the window style
|
||||
hInstance, // a handle to the instance o the module to be associated with the window
|
||||
NULL // Pointer to the window through the CREATESTRUCT structure
|
||||
);
|
||||
|
||||
if (!hWnd)
|
||||
{
|
||||
MessageBox(NULL, L"Call to CreateWindow failed!", title.c_str(), NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
SetHwnd(hWnd);
|
||||
SetWindowLongPtr(hWnd, GWLP_USERDATA, (LONG_PTR)this);
|
||||
PreprocessRequest();
|
||||
ShowWindow(hWnd, nCmdShow);
|
||||
UpdateWindow(hWnd);
|
||||
|
||||
// Main message loop:
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, NULL, 0, 0))
|
||||
{
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
}
|
||||
|
||||
return static_cast<int>(msg.wParam);
|
||||
}
|
||||
|
||||
void UI::ButtonClicked()
|
||||
{
|
||||
switch (m_type)
|
||||
{
|
||||
case InstallUIAdd:
|
||||
{
|
||||
m_msixResponse = m_packageManager->AddPackageAsync(m_path, DeploymentOptions::None, [this](const IMsixResponse & sender) {
|
||||
|
||||
ShowWindow(g_percentageTextHWnd, SW_SHOW);
|
||||
UpdateDisplayPercent(sender.GetPercentage());
|
||||
SendMessage(g_progressHWnd, PBM_SETPOS, (WPARAM)sender.GetPercentage(), 0);
|
||||
switch (sender.GetStatus())
|
||||
{
|
||||
case InstallationStep::InstallationStepCompleted:
|
||||
{
|
||||
SendInstallCompleteMsg();
|
||||
}
|
||||
break;
|
||||
case InstallationStep::InstallationStepError:
|
||||
{
|
||||
DisplayError(sender.GetHResultTextCode());
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void UI::SendInstallCompleteMsg()
|
||||
{
|
||||
SendMessage(GetHwnd(), WM_INSTALLCOMPLETE_MSG, NULL, NULL);
|
||||
}
|
||||
|
||||
void UI::UpdateDisplayPercent(float displayPercent)
|
||||
{
|
||||
std::wstringstream ss;
|
||||
ss << displayPercent << "%...";
|
||||
SetWindowText(g_staticPercentText, ss.str().c_str());
|
||||
ShowWindow(g_staticPercentText, SW_HIDE);
|
||||
ShowWindow(g_staticPercentText, SW_SHOW);
|
||||
}
|
||||
|
||||
void UI::DisplayError(HRESULT hr)
|
||||
{
|
||||
g_installing = false;
|
||||
ShowWindow(g_percentageTextHWnd, SW_HIDE);
|
||||
ShowWindow(g_staticPercentText, SW_HIDE);
|
||||
ShowWindow(g_progressHWnd, SW_HIDE);
|
||||
ShowWindow(g_checkboxHWnd, SW_HIDE);
|
||||
ShowWindow(g_CancelbuttonHWnd, SW_HIDE);
|
||||
|
||||
//Show Error Window
|
||||
ShowWindow(g_staticErrorTextHWnd, SW_SHOW);
|
||||
ShowWindow(g_staticErrorDescHWnd, SW_SHOW);
|
||||
std::wstringstream errorDescription;
|
||||
errorDescription << GetStringResource(IDS_STRING_ERROR_MSG) << std::hex << hr << ".";
|
||||
SetWindowText(g_staticErrorDescHWnd, errorDescription.str().c_str());
|
||||
}
|
||||
|
||||
void UI::CloseUI()
|
||||
{
|
||||
DestroyWindow(GetHwnd()); // close msix app installer
|
||||
SetEvent(m_closeUI);
|
||||
}
|
|
@ -0,0 +1,188 @@
|
|||
#pragma once
|
||||
/// Install UI Header
|
||||
/// UI Functions
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include "Util.hpp"
|
||||
#include "..\msixmgrLib\GeneralUtil.hpp"
|
||||
#include <IPackageManager.hpp>
|
||||
#include <IMsixResponse.hpp>
|
||||
#include "resource.h"
|
||||
#include <vector>
|
||||
|
||||
/// Child window identifiers
|
||||
#define IDC_LAUNCHCHECKBOX 101
|
||||
#define IDC_INSTALLBUTTON 102
|
||||
#define IDC_CANCELBUTTON 103
|
||||
#define IDC_LAUNCHBUTTON 104
|
||||
#define IDC_STATICPERCENTCONTROL 105
|
||||
#define IDC_STATICERRORCONTROL 106
|
||||
#define WM_INSTALLCOMPLETE_MSG (WM_APP+1)
|
||||
|
||||
/// Global variables
|
||||
static HWND g_buttonHWnd = NULL;
|
||||
static HWND g_checkboxHWnd = NULL;
|
||||
static HWND g_progressHWnd = NULL;
|
||||
static HWND g_CancelbuttonHWnd = NULL;
|
||||
static HWND g_LaunchbuttonHWnd = NULL;
|
||||
static HWND g_percentageTextHWnd = NULL;
|
||||
static HWND g_staticPercentText = NULL;
|
||||
static HWND g_staticErrorTextHWnd = NULL;
|
||||
static HWND g_staticErrorDescHWnd = NULL;
|
||||
static bool g_launchCheckBoxState = true; /// launch checkbox is checked by default
|
||||
static bool g_installing = false; /// variable used to indicate that app installation is in progress
|
||||
|
||||
|
||||
enum UIType { InstallUIAdd, InstallUIRemove};
|
||||
|
||||
class UI
|
||||
{
|
||||
public:
|
||||
HRESULT LaunchInstalledApp();
|
||||
void ConfirmAppCancel(HWND parentHWnd);
|
||||
|
||||
UI(_In_ MsixCoreLib::IPackageManager* packageManager, _In_ const std::wstring & path, UIType type) : m_packageManager(packageManager), m_type(type)
|
||||
{
|
||||
m_path = std::wstring(path);
|
||||
m_closeUI = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
}
|
||||
~UI() {}
|
||||
|
||||
private:
|
||||
MsixCoreLib::IPackageManager* m_packageManager = nullptr;
|
||||
std::shared_ptr<MsixCoreLib::IPackage> m_packageInfo = nullptr;
|
||||
std::wstring m_path;
|
||||
std::shared_ptr<MsixCoreLib::IMsixResponse> m_msixResponse;
|
||||
|
||||
//Parent Window Hwnd
|
||||
HWND hWnd = NULL;
|
||||
|
||||
/// Default prefix text on UI while prompting for app installation is 'Install'
|
||||
std::wstring m_installOrUpdateText = GetStringResource(IDS_STRING_INSTALLTEXT);
|
||||
|
||||
/// Message to prompt user while cancelling app installation
|
||||
std::wstring m_cancelPopUpMessage = GetStringResource(IDS_STRING_CANCEL_INSTALLPOPUP);
|
||||
|
||||
/// Popup message title while cancelling app installation
|
||||
std::wstring m_cancelPopUpTitle = GetStringResource(IDS_STRING_CANCEL_POPUP_TITLE);
|
||||
std::wstring m_displayName = L"";
|
||||
std::wstring m_publisherCommonName = L"";
|
||||
std::unique_ptr<IStream> m_logoStream;
|
||||
std::wstring m_version = L"";
|
||||
std::vector<std::wstring> m_capabilities;
|
||||
|
||||
HRESULT m_loadingPackageInfoCode = 0;
|
||||
UIType m_type;
|
||||
|
||||
HANDLE m_closeUI;
|
||||
|
||||
HRESULT ParseInfoFromPackage();
|
||||
|
||||
/// This function sets the parent window hwnd after create window
|
||||
///
|
||||
/// @param hWnd - the HWND of the parent window
|
||||
void SetHwnd(HWND hwnd)
|
||||
{
|
||||
hWnd = hwnd;
|
||||
}
|
||||
|
||||
/// This function returns the parent window hwnd
|
||||
///
|
||||
/// @return hWnd: parent window hwnd
|
||||
HWND GetHwnd()
|
||||
{
|
||||
return hWnd;
|
||||
}
|
||||
|
||||
public:
|
||||
|
||||
/// This function compiles the information displayed on the UI when the user selects an msix
|
||||
///
|
||||
/// @param hWnd - the HWND of the window to draw controls
|
||||
/// @param windowRect - the size of the window
|
||||
HRESULT DrawPackageInfo(HWND hWnd, RECT windowRect);
|
||||
|
||||
/// Creates the initial installation UI window
|
||||
///
|
||||
/// @param windowClass - the class text of the window
|
||||
/// @param windowTitle - the window title
|
||||
int CreateInitWindow(HINSTANCE hInstance, int nCmdShow, const std::wstring& windowClass, const std::wstring& title);
|
||||
|
||||
void ButtonClicked();
|
||||
|
||||
/// Creates the progress bar
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to add the progress bar to
|
||||
/// @param parentRect - the dimmensions of the parent window
|
||||
BOOL CreateProgressBar(HWND parentHWnd, RECT parentRect);
|
||||
|
||||
/// Create the lower right install button
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to add the button to
|
||||
/// @param parentRect - the specs of the parent window
|
||||
BOOL InstallButton(HWND parentHWnd, RECT parentRect);
|
||||
|
||||
/// Create the launch when ready checkbox on the bottom left
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to add the checkbox to
|
||||
/// @param parentRect - the specs of the parent window
|
||||
BOOL CreateCheckbox(HWND parentHWnd, RECT parentRect);
|
||||
|
||||
/// Create the cancel button on the bottom right corner when user clicks on install
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to add the checkbox to
|
||||
/// @param parentRect - the specs of the parent window
|
||||
BOOL CreateCancelButton(HWND parentHWnd, RECT parentRect);
|
||||
|
||||
/// Create the launch button on the botton right after app has been installed
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to add the checkbox to
|
||||
/// @param parentRect - the specs of the parent window
|
||||
/// @param xDiff - the x coordinate difference to create the button from the parent window
|
||||
/// @param yDiff - the y coordinate difference to create the button from the parent window
|
||||
BOOL CreateLaunchButton(HWND parentHWnd, RECT parentRect, int xDiff, int yDiff);
|
||||
|
||||
/// Changes the text of the lower right 'Install' button
|
||||
/// Changes text to 'Update' in case of an update, changes text to 'Reinstall' in case app is already installed on the machine
|
||||
///
|
||||
/// @param newMessage - the message to change the button to
|
||||
BOOL ChangeInstallButtonText(const std::wstring& newMessage);
|
||||
|
||||
/// Creates the text which says, 'Installing app package %' during package installation
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to add the checkbox to
|
||||
/// @param parentRect - the specs of the parent window
|
||||
BOOL CreateDisplayPercentageText(HWND parentHWnd, RECT parentRect);
|
||||
|
||||
/// Creates the text which displays error text and hresult on the UI in case of an error
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to add the checkbox to
|
||||
/// @param parentRect - the specs of the parent window
|
||||
BOOL CreateDisplayErrorText(HWND parentHWnd, RECT parentRect);
|
||||
|
||||
/// Change the text of the installation window based on the given input
|
||||
///
|
||||
/// @param parentHWnd - the HWND of the window to be changed
|
||||
/// @param windowText - the text to change the window to
|
||||
BOOL ChangeText(HWND parentHWnd, std::wstring displayText, std::wstring messageText, IStream* logoStream = nullptr);
|
||||
|
||||
/// Sends the WM_INSTALLCOMPLETE_MSG message to the main window when app installation is complete
|
||||
void SendInstallCompleteMsg();
|
||||
|
||||
/// The add operation could be an update if V1 version of the package is already installed. Show appropriate UI with respect to operation type
|
||||
/// The add operation could be an update if V1 version of the package is already installed on the machine
|
||||
/// This method checks the same and sets the button and install screen UI text to 'Update'
|
||||
///
|
||||
void PreprocessRequest();
|
||||
|
||||
/// Updates the percentage displayed on screen indicating app installation progress
|
||||
///
|
||||
/// @param displayPercent - the percentage from the msixresponse object
|
||||
void UpdateDisplayPercent(float displayPercent);
|
||||
|
||||
void DisplayError(HRESULT hr);
|
||||
|
||||
HRESULT ShowUI();
|
||||
|
||||
void CloseUI();
|
||||
};
|
|
@ -0,0 +1,242 @@
|
|||
#include "GeneralUtil.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <algorithm>
|
||||
#include <utility>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
|
||||
#include "FootprintFiles.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include <cstdio>
|
||||
#include <experimental/filesystem> // C++-standard header file name
|
||||
#include <filesystem> // Microsoft-specific implementation header file name
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
// handlers
|
||||
#include "Extractor.hpp"
|
||||
#include "StartMenuLink.hpp"
|
||||
#include "AddRemovePrograms.hpp"
|
||||
#include "PopulatePackageInfo.hpp"
|
||||
#include "Protocol.hpp"
|
||||
#include "ComInterface.hpp"
|
||||
#include "ComServer.hpp"
|
||||
#include "StartupTask.hpp"
|
||||
#include "FileTypeAssociation.hpp"
|
||||
#include "ProcessPotentialUpdate.hpp"
|
||||
#include "InstallComplete.hpp"
|
||||
#include "ErrorHandler.hpp"
|
||||
#include "ValidateTargetDeviceFamily.hpp"
|
||||
#include "PrepareDevirtualizedRegistry.hpp"
|
||||
#include "WriteDevirtualizedRegistry.hpp"
|
||||
#include "FirewallRules.hpp"
|
||||
#include "AutoPlay.hpp"
|
||||
#include "VirtualFileHandler.hpp"
|
||||
|
||||
#include "Constants.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
enum ErrorHandlingMode
|
||||
{
|
||||
IgnoreAndProcessNextHandler = 0, // ignore the error and proceed with nextHandler as if the current handler were successful
|
||||
ExecuteErrorHandler = 1, // go to errorHandler instead of nextHandler
|
||||
ReturnError = 2, // return immediately, do not process any further handlers
|
||||
};
|
||||
|
||||
struct AddHandlerInfo
|
||||
{
|
||||
CreateHandler create;
|
||||
PCWSTR nextHandler;
|
||||
ErrorHandlingMode errorMode;
|
||||
PCWSTR errorHandler;
|
||||
};
|
||||
|
||||
struct RemoveHandlerInfo
|
||||
{
|
||||
CreateHandler create;
|
||||
PCWSTR nextHandler;
|
||||
ErrorHandlingMode errorMode;
|
||||
};
|
||||
|
||||
std::map<PCWSTR, AddHandlerInfo> AddHandlers =
|
||||
{
|
||||
//HandlerName Function to create NextHandler (on success) ErrorHandlingMode ErrorHandler (when ExecuteErrorHandler)
|
||||
{PopulatePackageInfo::HandlerName, {PopulatePackageInfo::CreateHandler, ValidateTargetDeviceFamily::HandlerName, ReturnError, nullptr}},
|
||||
{ValidateTargetDeviceFamily::HandlerName, {ValidateTargetDeviceFamily::CreateHandler, ProcessPotentialUpdate::HandlerName, ReturnError, nullptr}},
|
||||
{ProcessPotentialUpdate::HandlerName, {ProcessPotentialUpdate::CreateHandler, Extractor::HandlerName, ReturnError, nullptr}},
|
||||
{Extractor::HandlerName, {Extractor::CreateHandler, PrepareDevirtualizedRegistry::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{PrepareDevirtualizedRegistry::HandlerName, {PrepareDevirtualizedRegistry::CreateHandler, VirtualFileHandler::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{VirtualFileHandler::HandlerName, {VirtualFileHandler::CreateHandler, WriteDevirtualizedRegistry::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{WriteDevirtualizedRegistry::HandlerName, {WriteDevirtualizedRegistry::CreateHandler, StartMenuLink::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{StartMenuLink::HandlerName, {StartMenuLink::CreateHandler, AddRemovePrograms::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{AddRemovePrograms::HandlerName, {AddRemovePrograms::CreateHandler, Protocol::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{Protocol::HandlerName, {Protocol::CreateHandler, ComInterface::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{ComInterface::HandlerName, {ComInterface::CreateHandler, ComServer::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{ComServer::HandlerName, {ComServer::CreateHandler, StartupTask::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{StartupTask::HandlerName, {StartupTask::CreateHandler, FileTypeAssociation::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{FileTypeAssociation::HandlerName, {FileTypeAssociation::CreateHandler, FirewallRules::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{FirewallRules::HandlerName, {FirewallRules::CreateHandler, AutoPlay::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{AutoPlay::HandlerName, {AutoPlay::CreateHandler, InstallComplete::HandlerName, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{InstallComplete::HandlerName, {InstallComplete::CreateHandler, nullptr, ExecuteErrorHandler, ErrorHandler::HandlerName}},
|
||||
{ErrorHandler::HandlerName, {ErrorHandler::CreateHandler, nullptr, ReturnError, nullptr}},
|
||||
};
|
||||
|
||||
std::map<PCWSTR, RemoveHandlerInfo> RemoveHandlers =
|
||||
{
|
||||
//HandlerName Function to create NextHandler ErrorHandling
|
||||
{PopulatePackageInfo::HandlerName, {PopulatePackageInfo::CreateHandler, StartMenuLink::HandlerName, ReturnError}},
|
||||
{StartMenuLink::HandlerName, {StartMenuLink::CreateHandler, AddRemovePrograms::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{AddRemovePrograms::HandlerName, {AddRemovePrograms::CreateHandler, PrepareDevirtualizedRegistry::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{PrepareDevirtualizedRegistry::HandlerName, {PrepareDevirtualizedRegistry::CreateHandler, Protocol::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{Protocol::HandlerName, {Protocol::CreateHandler, ComInterface::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{ComInterface::HandlerName, {ComInterface::CreateHandler, ComServer::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{ComServer::HandlerName, {ComServer::CreateHandler, StartupTask::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{StartupTask::HandlerName, {StartupTask::CreateHandler, FileTypeAssociation::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{FileTypeAssociation::HandlerName, {FileTypeAssociation::CreateHandler, FirewallRules::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{FirewallRules::HandlerName, {FirewallRules::CreateHandler, AutoPlay::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{AutoPlay::HandlerName, {AutoPlay::CreateHandler, VirtualFileHandler::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{VirtualFileHandler::HandlerName, {VirtualFileHandler::CreateHandler, WriteDevirtualizedRegistry::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{WriteDevirtualizedRegistry::HandlerName, {WriteDevirtualizedRegistry::CreateHandler, Extractor::HandlerName, IgnoreAndProcessNextHandler}},
|
||||
{Extractor::HandlerName, {Extractor::CreateHandler, nullptr, IgnoreAndProcessNextHandler}},
|
||||
};
|
||||
|
||||
HRESULT MsixRequest::Make(OperationType operationType, IStream * packageStream, std::wstring packageFullName, MSIX_VALIDATION_OPTION validationOption, MsixRequest ** outInstance)
|
||||
{
|
||||
AutoPtr<MsixRequest> instance(new MsixRequest());
|
||||
if (instance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
instance->m_operationType = operationType;
|
||||
instance->m_packageStream = packageStream;
|
||||
instance->m_packageFullName = packageFullName;
|
||||
instance->m_validationOptions = validationOption;
|
||||
RETURN_IF_FAILED(FilePathMappings::GetInstance().GetInitializationResult());
|
||||
|
||||
//Set MsixResponse
|
||||
instance->m_msixResponse = std::make_shared<MsixResponse>();
|
||||
|
||||
*outInstance = instance.Detach();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MsixRequest::ProcessRequest()
|
||||
{
|
||||
m_msixResponse->Update(InstallationStep::InstallationStepStarted, 0);
|
||||
|
||||
switch (m_operationType)
|
||||
{
|
||||
case OperationType::Add:
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessAddRequest());
|
||||
break;
|
||||
}
|
||||
case OperationType::Remove:
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessRemoveRequest());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED);
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MsixRequest::ProcessAddRequest()
|
||||
{
|
||||
PCWSTR currentHandlerName = PopulatePackageInfo::HandlerName;
|
||||
while (currentHandlerName != nullptr)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Executing handler",
|
||||
TraceLoggingValue(currentHandlerName, "HandlerName"));
|
||||
|
||||
AddHandlerInfo currentHandler = AddHandlers[currentHandlerName];
|
||||
AutoPtr<IPackageHandler> handler;
|
||||
HRESULT hr = currentHandler.create(this, &handler);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = handler->ExecuteForAddRequest();
|
||||
}
|
||||
if (FAILED(hr) && currentHandler.errorMode != IgnoreAndProcessNextHandler)
|
||||
{
|
||||
m_msixResponse->SetErrorStatus(hr, L"Failed to process add request");
|
||||
if (currentHandler.errorMode == ReturnError)
|
||||
{
|
||||
return hr;
|
||||
}
|
||||
currentHandlerName = currentHandler.errorHandler;
|
||||
}
|
||||
else
|
||||
{
|
||||
currentHandlerName = currentHandler.nextHandler;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MsixRequest::ProcessRemoveRequest()
|
||||
{
|
||||
PCWSTR currentHandlerName = PopulatePackageInfo::HandlerName;
|
||||
while (currentHandlerName != nullptr)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Executing handler",
|
||||
TraceLoggingValue(currentHandlerName, "HandlerName"));
|
||||
|
||||
RemoveHandlerInfo currentHandler = RemoveHandlers[currentHandlerName];
|
||||
AutoPtr<IPackageHandler> handler;
|
||||
HRESULT hr = currentHandler.create(this, &handler);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
hr = handler->ExecuteForRemoveRequest();
|
||||
}
|
||||
|
||||
if (FAILED(hr))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Handler failed -- if errorMode is IgnoreAndProcessNextHandler(0), this is non-fatal",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(currentHandlerName, "HandlerName"),
|
||||
TraceLoggingValue(hr, "HR"),
|
||||
TraceLoggingUInt32(currentHandler.errorMode, "ErrorMode"));
|
||||
if (currentHandler.errorMode == ReturnError)
|
||||
{
|
||||
m_msixResponse->SetErrorStatus(hr, L"Failed to process add request");
|
||||
return hr;
|
||||
}
|
||||
}
|
||||
|
||||
currentHandlerName = currentHandler.nextHandler;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void MsixRequest::SetPackageInfo(std::shared_ptr<PackageBase> packageInfo)
|
||||
{
|
||||
m_packageInfo = packageInfo;
|
||||
}
|
||||
|
||||
std::wstring MsixRequest::GetPackageDirectoryPath()
|
||||
{
|
||||
if (m_packageInfo == nullptr)
|
||||
return nullptr;
|
||||
|
||||
return FilePathMappings::GetInstance().GetMsixCoreDirectory() + m_packageInfo->GetPackageFullName();
|
||||
}
|
||||
|
||||
void MsixRequest::SetRegistryDevirtualizer(std::shared_ptr<RegistryDevirtualizer> registryDevirualizer)
|
||||
{
|
||||
m_registryDevirtualizer = registryDevirualizer;
|
||||
}
|
|
@ -0,0 +1,88 @@
|
|||
#pragma once
|
||||
#include "Package.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include "MsixResponse.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
enum OperationType
|
||||
{
|
||||
Add = 1,
|
||||
Remove = 2,
|
||||
};
|
||||
|
||||
class RegistryDevirtualizer;
|
||||
|
||||
/// MsixRequest represents what this instance of the executable will be doing and tracks the state of the current operation
|
||||
class MsixRequest
|
||||
{
|
||||
private:
|
||||
/// Should always be available via constructor
|
||||
ComPtr<IStream> m_packageStream;
|
||||
std::wstring m_packageFullName;
|
||||
MSIX_VALIDATION_OPTION m_validationOptions = MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL;
|
||||
OperationType m_operationType = Add;
|
||||
|
||||
/// Filled by PopulatePackageInfo
|
||||
std::shared_ptr<PackageBase> m_packageInfo;
|
||||
|
||||
/// MsixResponse object populated by handlers
|
||||
std::shared_ptr<MsixResponse> m_msixResponse;
|
||||
|
||||
/// Registry Devirtualizer object to access and update the virtual registry
|
||||
std::shared_ptr<RegistryDevirtualizer> m_registryDevirtualizer;
|
||||
|
||||
protected:
|
||||
MsixRequest() {}
|
||||
public:
|
||||
static HRESULT Make(OperationType operationType, IStream * packageStream, std::wstring packageFullName, MSIX_VALIDATION_OPTION validationOption, MsixRequest** outInstance);
|
||||
|
||||
/// The main function processes the request based on whichever operation type was requested and then
|
||||
/// going through the sequence of individual handlers.
|
||||
HRESULT ProcessRequest();
|
||||
|
||||
bool IsRemove()
|
||||
{
|
||||
return m_operationType == OperationType::Remove;
|
||||
}
|
||||
|
||||
bool AllowSignatureOriginUnknown()
|
||||
{
|
||||
m_validationOptions = static_cast<MSIX_VALIDATION_OPTION>(m_validationOptions | MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline MSIX_VALIDATION_OPTION GetValidationOptions() { return m_validationOptions; }
|
||||
inline IStream * GetPackageStream() { return m_packageStream.Get(); }
|
||||
inline PCWSTR GetPackageFullName() { return m_packageFullName.c_str(); }
|
||||
|
||||
/// Retrieves the msixResponse object
|
||||
///
|
||||
/// @return m_msixResponse object
|
||||
std::shared_ptr<MsixResponse> GetMsixResponse() { return m_msixResponse; }
|
||||
|
||||
/// Called by PopulatePackageInfo
|
||||
void SetPackageInfo(std::shared_ptr<PackageBase> packageInfo);
|
||||
std::wstring GetPackageDirectoryPath();
|
||||
|
||||
/// @return can return null if called before PopulatePackageInfo.
|
||||
std::shared_ptr<PackageBase> GetPackageInfo() { return m_packageInfo; }
|
||||
|
||||
std::shared_ptr<RegistryDevirtualizer> GetRegistryDevirtualizer() {
|
||||
return m_registryDevirtualizer;
|
||||
}
|
||||
|
||||
/// Sets the registry Devirtualizer object
|
||||
///
|
||||
/// @param registryDevirualizer - the registry devirtualizer object to set
|
||||
void SetRegistryDevirtualizer(std::shared_ptr<RegistryDevirtualizer> registryDevirualizer);
|
||||
|
||||
private:
|
||||
/// This handles Add operation and proceeds through each of the AddSequenceHandlers to install the package
|
||||
HRESULT ProcessAddRequest();
|
||||
|
||||
/// This handles Remove operation and proceeds through each of the RemoveSequenceHandlers to uninstall the package
|
||||
HRESULT ProcessRemoveRequest();
|
||||
|
||||
};
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
#include "GeneralUtil.hpp"
|
||||
#include "MsixResponse.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
void MsixResponse::Update(InstallationStep status, float progress)
|
||||
{
|
||||
if (m_percentage == progress && m_status == status)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
m_percentage = progress;
|
||||
m_status = status;
|
||||
if (m_callback)
|
||||
{
|
||||
m_callback(*this);
|
||||
}
|
||||
}
|
||||
|
||||
void MsixResponse::SetCallback(std::function<void(const IMsixResponse& sender)> callback)
|
||||
{
|
||||
m_callback = callback;
|
||||
}
|
||||
|
||||
void MsixResponse::SetErrorStatus(HRESULT errorCode, std::wstring errorText)
|
||||
{
|
||||
m_percentage = 0;
|
||||
m_status = InstallationStep::InstallationStepError;
|
||||
m_hresultTextCode = errorCode;
|
||||
m_textStatus = errorText;
|
||||
|
||||
if (m_callback)
|
||||
{
|
||||
m_callback(*this);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,107 @@
|
|||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
#include "IMsixResponse.hpp"
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// The response class tracks the response state of the msix deploy operation
|
||||
/// (if the operation was cancelled, progress bar updates)
|
||||
class MsixResponse: public IMsixResponse
|
||||
{
|
||||
private:
|
||||
|
||||
/// Variable used to indicate if add package request was cancelled during installation
|
||||
bool m_isInstallCancelled = false;
|
||||
|
||||
/// HResult to be populated by handlers in the msix response
|
||||
HRESULT m_hresultTextCode = 0;
|
||||
|
||||
/// Detailed text status of the msix response
|
||||
std::wstring m_textStatus;
|
||||
|
||||
/// Progress percentage of the msix deployment
|
||||
float m_percentage;
|
||||
|
||||
InstallationStep m_status = InstallationStepUnknown;
|
||||
|
||||
/// Callback to indicate how the progress of the installation
|
||||
std::function<void(const IMsixResponse&)> m_callback;
|
||||
|
||||
public:
|
||||
|
||||
/// Method used to return the status of the cancel button
|
||||
///
|
||||
/// @return variable indicating the state of cancel button
|
||||
bool GetIsInstallCancelled()
|
||||
{
|
||||
return m_isInstallCancelled;
|
||||
}
|
||||
|
||||
/// Method used to set the status of the cancel button if 'Cancel' is clicked
|
||||
virtual void CancelRequest()
|
||||
{
|
||||
m_isInstallCancelled = true;
|
||||
}
|
||||
|
||||
|
||||
/// Get the Hresult value in an msix response
|
||||
///
|
||||
/// @return Hresult code as set in the response
|
||||
virtual inline HRESULT GetHResultTextCode() const
|
||||
{
|
||||
return m_hresultTextCode;
|
||||
}
|
||||
|
||||
/// Sets the detailed text status in the msix response object
|
||||
///
|
||||
/// @param textStatus - the textStatus to be set
|
||||
inline void SetTextStatus(std::wstring textStatus)
|
||||
{
|
||||
m_textStatus = textStatus;
|
||||
}
|
||||
|
||||
/// Returns the detailed text status as set in the msix response object
|
||||
///
|
||||
/// @return textStatus in the msix response
|
||||
virtual inline std::wstring GetTextStatus() const
|
||||
{
|
||||
return m_textStatus;
|
||||
}
|
||||
|
||||
/// Update response and send a callback to the UI
|
||||
///
|
||||
/// @param status - current step of the installation
|
||||
/// @param percentage - installation progresss percentage
|
||||
void Update(InstallationStep status, float percentage);
|
||||
|
||||
/// Update response to indicate an error status
|
||||
///
|
||||
/// @param errorCode - code associated to the error
|
||||
/// @param errorText - description of the error
|
||||
void SetErrorStatus(HRESULT errorCode, std::wstring errorText);
|
||||
|
||||
/// Set a callback for the UI
|
||||
///
|
||||
/// @param callback - callback called when status or percentage change
|
||||
virtual void SetCallback(std::function<void(const IMsixResponse&)> callback);
|
||||
|
||||
/// Get the installation progress percentage
|
||||
///
|
||||
/// @return the percentage (from 0 to 100)
|
||||
virtual inline float GetPercentage() const
|
||||
{
|
||||
return m_percentage;
|
||||
}
|
||||
|
||||
/// Get the installation status
|
||||
///
|
||||
/// @return the installation status
|
||||
virtual inline InstallationStep GetStatus() const
|
||||
{
|
||||
return m_status;
|
||||
}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,252 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include "Package.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <fstream>
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
//
|
||||
// Gets the stream of a file.
|
||||
//
|
||||
// Parameters:
|
||||
// package - The package reader for the app package.
|
||||
// name - Name of the file.
|
||||
// stream - The stream for the file.
|
||||
//
|
||||
|
||||
HRESULT GetStreamFromFile(IAppxPackageReader* package, LPCWCHAR name, IStream** stream)
|
||||
{
|
||||
*stream = nullptr;
|
||||
|
||||
ComPtr<IAppxFilesEnumerator> files;
|
||||
RETURN_IF_FAILED(package->GetPayloadFiles(&files));
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(files->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IAppxFile> file;
|
||||
RETURN_IF_FAILED(files->GetCurrent(&file));
|
||||
Text<WCHAR> fileName;
|
||||
file->GetName(&fileName);
|
||||
if (wcscmp(fileName.Get(), name) == 0)
|
||||
{
|
||||
RETURN_IF_FAILED(file->GetStream(stream));
|
||||
return S_OK;
|
||||
}
|
||||
RETURN_IF_FAILED(files->MoveNext(&hasCurrent));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::wstring MsixCoreLib::PackageBase::GetResolvedExecutableFilePath()
|
||||
{
|
||||
return FilePathMappings::GetInstance().GetExecutablePath(m_relativeExecutableFilePath, m_packageFullName.c_str());
|
||||
}
|
||||
|
||||
std::wstring PackageBase::GetVersion()
|
||||
{
|
||||
return ConvertVersionToString(m_version);
|
||||
}
|
||||
|
||||
HRESULT PackageBase::ParseManifest(IMsixElement* element)
|
||||
{
|
||||
BOOL hc = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> applicationElementEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(
|
||||
L"/*[local-name()='Package']/*[local-name()='Applications']/*[local-name()='Application']",
|
||||
&applicationElementEnum));
|
||||
RETURN_IF_FAILED(applicationElementEnum->GetHasCurrent(&hc));
|
||||
|
||||
if (!hc)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"No Application Found",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR));
|
||||
return E_NOT_SET;
|
||||
}
|
||||
|
||||
ComPtr<IMsixElement> applicationElement;
|
||||
RETURN_IF_FAILED(applicationElementEnum->GetCurrent(&applicationElement));
|
||||
|
||||
Text<wchar_t> executablePath;
|
||||
Text<wchar_t> applicationId;
|
||||
RETURN_IF_FAILED(applicationElement->GetAttributeValue(L"Executable", &executablePath));
|
||||
RETURN_IF_FAILED(applicationElement->GetAttributeValue(L"Id", &applicationId));
|
||||
m_relativeExecutableFilePath = executablePath.Get();
|
||||
m_applicationId = applicationId.Get();
|
||||
|
||||
ComPtr<IMsixElementEnumerator> visualElementsEnum;
|
||||
RETURN_IF_FAILED(applicationElement->GetElements(L"*[local-name()='VisualElements']", &visualElementsEnum));
|
||||
RETURN_IF_FAILED(visualElementsEnum->GetHasCurrent(&hc));
|
||||
if (!hc)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"No DisplayName Found",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR));
|
||||
return E_NOT_SET;
|
||||
}
|
||||
|
||||
ComPtr<IMsixElement> visualElementsElement;
|
||||
RETURN_IF_FAILED(visualElementsEnum->GetCurrent(&visualElementsElement));
|
||||
|
||||
Text<wchar_t> displayName;
|
||||
RETURN_IF_FAILED(visualElementsElement->GetAttributeValue(L"DisplayName", &displayName));
|
||||
m_displayName = displayName.Get();
|
||||
|
||||
Text<WCHAR> logo;
|
||||
RETURN_IF_FAILED(visualElementsElement->GetAttributeValue(L"Square150x150Logo", &logo));
|
||||
m_relativeLogoPath = logo.Get();
|
||||
return S_OK;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PackageBase::ParseManifestCapabilities(IMsixElement* element)
|
||||
{
|
||||
ComPtr<IMsixElementEnumerator> capabilitiesEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(
|
||||
L"/*[local-name()='Package']/*[local-name()='Capabilities']/*[local-name()='Capability']",
|
||||
&capabilitiesEnum));
|
||||
|
||||
BOOL hc = FALSE;
|
||||
RETURN_IF_FAILED(capabilitiesEnum->GetHasCurrent(&hc));
|
||||
|
||||
while (hc)
|
||||
{
|
||||
ComPtr<IMsixElement> capabilityElement;
|
||||
RETURN_IF_FAILED(capabilitiesEnum->GetCurrent(&capabilityElement));
|
||||
|
||||
Text<wchar_t> capabilityName;
|
||||
RETURN_IF_FAILED(capabilityElement->GetAttributeValue(L"Name", &capabilityName));
|
||||
m_capabilities.push_back(capabilityName.Get());
|
||||
|
||||
RETURN_IF_FAILED(capabilitiesEnum->MoveNext(&hc));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Package::MakeFromPackageReader(IAppxPackageReader * packageReader, std::shared_ptr<Package> * packageInfo)
|
||||
{
|
||||
std::shared_ptr<Package> instance = std::make_shared<Package>();
|
||||
if (instance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
instance->m_packageReader = packageReader;
|
||||
|
||||
ComPtr<IAppxManifestReader> manifestReader;
|
||||
RETURN_IF_FAILED(packageReader->GetManifest(&manifestReader));
|
||||
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader.Get()));
|
||||
|
||||
// Get the number of payload files
|
||||
DWORD numberOfPayloadFiles = 0;
|
||||
ComPtr<IAppxFilesEnumerator> fileEnum;
|
||||
RETURN_IF_FAILED(packageReader->GetPayloadFiles(&fileEnum));
|
||||
|
||||
BOOL hc = FALSE;
|
||||
RETURN_IF_FAILED(fileEnum->GetHasCurrent(&hc));
|
||||
while (hc)
|
||||
{
|
||||
numberOfPayloadFiles++;
|
||||
RETURN_IF_FAILED(fileEnum->MoveNext(&hc));
|
||||
}
|
||||
instance->m_numberOfPayloadFiles = numberOfPayloadFiles;
|
||||
|
||||
*packageInfo = instance;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PackageBase::SetManifestReader(IAppxManifestReader * manifestReader)
|
||||
{
|
||||
m_manifestReader = manifestReader;
|
||||
|
||||
// Also fill other fields that come from the manifest reader
|
||||
ComPtr<IAppxManifestPackageId> manifestId;
|
||||
RETURN_IF_FAILED(manifestReader->GetPackageId(&manifestId));
|
||||
|
||||
Text<WCHAR> publisher;
|
||||
RETURN_IF_FAILED(manifestId->GetPublisher(&publisher));
|
||||
m_publisher = publisher.Get();
|
||||
|
||||
m_publisherName = m_publisher.substr(m_publisher.find_first_of(L"=") + 1,
|
||||
m_publisher.find_first_of(L",") - m_publisher.find_first_of(L"=") - 1);
|
||||
|
||||
RETURN_IF_FAILED(manifestId->GetVersion(&m_version));
|
||||
|
||||
Text<WCHAR> packageFullName;
|
||||
RETURN_IF_FAILED(manifestId->GetPackageFullName(&packageFullName));
|
||||
m_packageFullName = packageFullName.Get();
|
||||
m_packageFamilyName = GetFamilyNameFromFullName(m_packageFullName);
|
||||
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(manifestReader->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
RETURN_IF_FAILED(ParseManifest(element.Get()));
|
||||
|
||||
RETURN_IF_FAILED(ParseManifestCapabilities(element.Get()));
|
||||
|
||||
Text<WCHAR> packageFamilyName;
|
||||
RETURN_IF_FAILED(manifestId->GetPackageFamilyName(&packageFamilyName));
|
||||
if (!m_applicationId.empty() && packageFamilyName.Get() != NULL)
|
||||
{
|
||||
m_appUserModelId = std::wstring(packageFamilyName.Get()) + L"!" + m_applicationId;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::wstring MsixCoreLib::Package::GetResolvedExecutableFilePath()
|
||||
{
|
||||
return FilePathMappings::GetInstance().GetExecutablePath(m_relativeExecutableFilePath, m_packageFullName.c_str());
|
||||
}
|
||||
|
||||
std::unique_ptr<IStream> Package::GetLogo()
|
||||
{
|
||||
if (m_packageReader.Get() == nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IStream * logoStream;
|
||||
if (GetStreamFromFile(m_packageReader.Get(), m_relativeLogoPath.data(), &logoStream) == S_OK)
|
||||
{
|
||||
return std::unique_ptr<IStream>(logoStream);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<IStream> InstalledPackage::GetLogo()
|
||||
{
|
||||
auto iconPath = m_packageDirectoryPath + m_relativeLogoPath;
|
||||
IStream* stream;
|
||||
if (SUCCEEDED(CreateStreamOnFileUTF16(iconPath.c_str(), true, &stream)))
|
||||
{
|
||||
return std::unique_ptr<IStream>(stream);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
HRESULT InstalledPackage::MakeFromManifestReader(const std::wstring & directoryPath, IAppxManifestReader * manifestReader, std::shared_ptr<InstalledPackage> * packageInfo)
|
||||
{
|
||||
std::shared_ptr<InstalledPackage> instance = std::make_shared<InstalledPackage>();
|
||||
if (instance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(instance->SetManifestReader(manifestReader));
|
||||
instance->m_packageDirectoryPath = directoryPath + L"\\";
|
||||
|
||||
*packageInfo = instance;
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,160 @@
|
|||
#pragma once
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "AppxPackaging.hpp"
|
||||
#include "MSIXWindows.hpp"
|
||||
#include "IPackage.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
|
||||
class PackageBase
|
||||
{
|
||||
protected:
|
||||
std::wstring m_packageFullName;
|
||||
std::wstring m_packageFamilyName;
|
||||
std::wstring m_relativeExecutableFilePath;
|
||||
std::wstring m_displayName;
|
||||
std::wstring m_appUserModelId;
|
||||
std::wstring m_applicationId;
|
||||
unsigned long long m_version = 0;
|
||||
std::wstring m_publisher;
|
||||
std::wstring m_publisherName;
|
||||
std::wstring m_relativeLogoPath;
|
||||
std::wstring m_packageDirectoryPath;
|
||||
std::vector<std::wstring> m_capabilities;
|
||||
|
||||
ComPtr<IAppxManifestReader> m_manifestReader;
|
||||
unsigned int m_numberOfPayloadFiles = 0;
|
||||
public:
|
||||
std::wstring GetPackageFullName() { return m_packageFullName; }
|
||||
std::wstring GetPackageFamilyName() { return m_packageFamilyName; }
|
||||
std::wstring GetRelativeExecutableFilePath() { return m_relativeExecutableFilePath; }
|
||||
std::wstring GetResolvedExecutableFilePath();
|
||||
std::wstring GetDisplayName() { return m_displayName; }
|
||||
std::wstring GetId() { return m_appUserModelId; }
|
||||
unsigned long long GetVersionNumber() { return m_version; }
|
||||
std::wstring GetVersion();
|
||||
std::wstring GetPublisher() { return m_publisher; }
|
||||
std::wstring GetPublisherDisplayName() { return m_publisherName; }
|
||||
std::wstring GetApplicationId() { return m_applicationId; }
|
||||
std::wstring GetRelativeLogoPath() {
|
||||
return m_relativeLogoPath;
|
||||
}
|
||||
std::wstring GetPackageDirectoryPath() {
|
||||
m_packageDirectoryPath = FilePathMappings::GetInstance().GetMsixCoreDirectory() + m_packageFullName + L"\\";
|
||||
return m_packageDirectoryPath;
|
||||
}
|
||||
|
||||
std::vector<std::wstring> GetCapabilities()
|
||||
{
|
||||
return m_capabilities;
|
||||
}
|
||||
|
||||
protected:
|
||||
PackageBase() {}
|
||||
/// Sets the manifest reader, and other fields derived from the manifest
|
||||
/// Specifically, packageFullName, packageDirectoryPath, executableFilePath, displayname, version and publisher.
|
||||
///
|
||||
/// @param manifestReader - manifestReader to set
|
||||
HRESULT SetManifestReader(IAppxManifestReader* manifestReader);
|
||||
/// Parse the manifest file
|
||||
///
|
||||
/// @param element - the root node of the manifest file
|
||||
HRESULT ParseManifest(IMsixElement * element);
|
||||
|
||||
/// Parse the manifest file for capabilities listed
|
||||
///
|
||||
/// @param element - the root node of the manifest file
|
||||
HRESULT ParseManifestCapabilities(IMsixElement * element);
|
||||
public:
|
||||
virtual ~PackageBase()
|
||||
{
|
||||
ReleaseManifest();
|
||||
}
|
||||
unsigned int GetNumberOfPayloadFiles() { return m_numberOfPayloadFiles; }
|
||||
|
||||
// Getters
|
||||
IAppxManifestReader * GetManifestReader() { return m_manifestReader.Get(); }
|
||||
/// This is meant only to be called when deleting the manifest file; the reader needs to first be released so it can be deleted
|
||||
void ReleaseManifest() {
|
||||
m_manifestReader.Release();
|
||||
}
|
||||
};
|
||||
|
||||
class Package : public PackageBase, public IPackage
|
||||
{
|
||||
public:
|
||||
std::wstring GetPackageFullName() { return m_packageFullName; }
|
||||
std::wstring GetPackageFamilyName() { return m_packageFamilyName; }
|
||||
std::wstring GetRelativeExecutableFilePath() { return m_relativeExecutableFilePath; }
|
||||
std::wstring GetResolvedExecutableFilePath();
|
||||
std::wstring GetDisplayName() { return m_displayName; }
|
||||
std::wstring GetId() { return m_appUserModelId; }
|
||||
unsigned long long GetVersionNumber() { return m_version; }
|
||||
std::wstring GetVersion() { return PackageBase::GetVersion(); }
|
||||
std::wstring GetPublisher() { return m_publisher; }
|
||||
std::wstring GetPublisherDisplayName() { return m_publisherName; }
|
||||
std::unique_ptr<IStream> GetLogo();
|
||||
std::wstring GetApplicationId() { return m_applicationId; }
|
||||
|
||||
std::vector<std::wstring> GetCapabilities()
|
||||
{
|
||||
return m_capabilities;
|
||||
}
|
||||
|
||||
IAppxPackageReader * GetPackageReader() { return m_packageReader.Get(); }
|
||||
|
||||
/// Create a Package using the package reader. This is intended for Add scenarios where
|
||||
/// the actual .msix package file is given.
|
||||
static HRESULT MakeFromPackageReader(IAppxPackageReader* packageReader, std::shared_ptr<Package>* packageInfo);
|
||||
|
||||
virtual ~Package()
|
||||
{
|
||||
m_packageReader.Release();
|
||||
}
|
||||
Package() : PackageBase(), IPackage(){}
|
||||
|
||||
private:
|
||||
/// PackageReader and payloadFiles are available on Add, but not Remove because it's created off the original package itself which is no longer available once it's been installed.
|
||||
ComPtr<IAppxPackageReader> m_packageReader;
|
||||
};
|
||||
|
||||
class InstalledPackage : public PackageBase, public IInstalledPackage
|
||||
{
|
||||
public:
|
||||
std::wstring GetPackageFullName() { return m_packageFullName; }
|
||||
std::wstring GetPackageFamilyName() { return m_packageFamilyName; }
|
||||
std::wstring GetRelativeExecutableFilePath() { return m_relativeExecutableFilePath; }
|
||||
std::wstring GetDisplayName() { return m_displayName; }
|
||||
std::wstring GetId() { return m_appUserModelId; }
|
||||
unsigned long long GetVersionNumber() { return m_version; }
|
||||
std::wstring GetVersion() { return PackageBase::GetVersion(); }
|
||||
std::wstring GetPublisher() { return m_publisher; }
|
||||
std::wstring GetPublisherDisplayName() { return m_publisherName; }
|
||||
std::wstring GetApplicationId() { return m_applicationId; }
|
||||
|
||||
std::unique_ptr<IStream> GetLogo();
|
||||
|
||||
virtual std::wstring GetFullExecutableFilePath()
|
||||
{
|
||||
return m_packageDirectoryPath + m_relativeExecutableFilePath;
|
||||
}
|
||||
virtual std::wstring GetInstalledLocation()
|
||||
{
|
||||
return m_packageDirectoryPath;
|
||||
}
|
||||
std::vector<std::wstring> GetCapabilities()
|
||||
{
|
||||
return m_capabilities;
|
||||
}
|
||||
|
||||
/// Create a InstalledPackage 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(const std::wstring & directoryPath, IAppxManifestReader* manifestReader, std::shared_ptr<InstalledPackage>* packageInfo);
|
||||
InstalledPackage() :PackageBase(), IInstalledPackage() {}
|
||||
private:
|
||||
std::wstring m_packageDirectoryPath;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include "PopulatePackageInfo.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include <experimental/filesystem> // C++-standard header file name
|
||||
#include "Constants.hpp"
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
using namespace std;
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR PopulatePackageInfo::HandlerName = L"PopulatePackageInfo";
|
||||
|
||||
HRESULT PopulatePackageInfo::GetPackageInfoFromPackage(IStream * packageStream, MSIX_VALIDATION_OPTION validationOption, std::shared_ptr<Package> * packageInfo)
|
||||
{
|
||||
// On Win32 platforms CoCreateAppxFactory defaults to CoTaskMemAlloc/CoTaskMemFree
|
||||
// On non-Win32 platforms CoCreateAppxFactory will return 0x80070032 (e.g. HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED))
|
||||
// So on all platforms, it's always safe to call CoCreateAppxFactoryWithHeap, just be sure to bring your own heap!
|
||||
ComPtr<IAppxFactory> appxFactory;
|
||||
RETURN_IF_FAILED(CoCreateAppxFactoryWithHeap(MyAllocate, MyFree, validationOption, &appxFactory));
|
||||
|
||||
// Create a new package reader using the factory.
|
||||
ComPtr<IAppxPackageReader> packageReader;
|
||||
RETURN_IF_FAILED(appxFactory->CreatePackageReader(packageStream, &packageReader));
|
||||
RETURN_IF_FAILED(Package::MakeFromPackageReader(packageReader.Get(), packageInfo));
|
||||
packageReader.Release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PopulatePackageInfo::GetPackageInfoFromManifest(const std::wstring & directoryPath, MSIX_VALIDATION_OPTION validationOption, std::shared_ptr<InstalledPackage> * packageInfo)
|
||||
{
|
||||
std::wstring manifestPath = directoryPath + manifestFile;
|
||||
|
||||
// On Win32 platforms CoCreateAppxFactory defaults to CoTaskMemAlloc/CoTaskMemFree
|
||||
// On non-Win32 platforms CoCreateAppxFactory will return 0x80070032 (e.g. HRESULT_FROM_WIN32(ERROR_NOT_SUPPORTED))
|
||||
// So on all platforms, it's always safe to call CoCreateAppxFactoryWithHeap, just be sure to bring your own heap!
|
||||
ComPtr<IStream> inputStream;
|
||||
RETURN_IF_FAILED(CreateStreamOnFileUTF16(manifestPath.c_str(), /*forRead*/ true, &inputStream));
|
||||
|
||||
// Create a new package reader using the factory.
|
||||
ComPtr<IAppxFactory> appxFactory;
|
||||
RETURN_IF_FAILED(CoCreateAppxFactoryWithHeap(MyAllocate, MyFree, validationOption, &appxFactory));
|
||||
|
||||
ComPtr<IAppxManifestReader> manifestReader;
|
||||
RETURN_IF_FAILED(appxFactory->CreateManifestReader(inputStream.Get(), &manifestReader));
|
||||
|
||||
RETURN_IF_FAILED(InstalledPackage::MakeFromManifestReader(directoryPath, manifestReader.Get(), packageInfo));
|
||||
manifestReader.Release();
|
||||
inputStream.Release();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
|
||||
HRESULT PopulatePackageInfo::ExecuteForAddRequest()
|
||||
{
|
||||
|
||||
std::shared_ptr<Package> packageInfo;
|
||||
RETURN_IF_FAILED(PopulatePackageInfo::GetPackageInfoFromPackage(m_msixRequest->GetPackageStream(), m_msixRequest->GetValidationOptions(), &packageInfo));
|
||||
|
||||
if (packageInfo == nullptr)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
|
||||
m_msixRequest->SetPackageInfo(packageInfo);
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"PackageInfo",
|
||||
TraceLoggingValue(packageInfo->GetPackageFullName().c_str(), "PackageFullName"),
|
||||
TraceLoggingValue(packageInfo->GetNumberOfPayloadFiles(), "NumberOfPayloadFiles"),
|
||||
TraceLoggingValue(m_msixRequest->GetPackageDirectoryPath().c_str(), "ExecutableFilePath"),
|
||||
TraceLoggingValue(packageInfo->GetDisplayName().c_str(), "DisplayName"));
|
||||
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PopulatePackageInfo::ExecuteForRemoveRequest()
|
||||
{
|
||||
auto packageDirectoryPath = FilePathMappings::GetInstance().GetMsixCoreDirectory() + m_msixRequest->GetPackageFullName();
|
||||
|
||||
std::shared_ptr<InstalledPackage> package;
|
||||
RETURN_IF_FAILED(GetPackageInfoFromManifest(packageDirectoryPath, MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_ALLOWSIGNATUREORIGINUNKNOWN, &package));
|
||||
|
||||
if (package == nullptr)
|
||||
{
|
||||
return E_FAIL;
|
||||
}
|
||||
m_msixRequest->SetPackageInfo(package);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PopulatePackageInfo::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<PopulatePackageInfo> localInstance(new PopulatePackageInfo(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "Package.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
#include <string>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class PopulatePackageInfo : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Opens the package to grab the package reader, manifest reader and read the info out of the manifest
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Opens the manifest from the filesystem to create a manifest reader and read the info out of the manifest
|
||||
/// @return E_NOT_SET when the package cannot be found
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~PopulatePackageInfo() {}
|
||||
|
||||
static HRESULT GetPackageInfoFromManifest(const std::wstring & directoryPath, MSIX_VALIDATION_OPTION validationOption, std::shared_ptr<InstalledPackage> * packageInfo);
|
||||
static HRESULT GetPackageInfoFromPackage(IStream * packageStream, MSIX_VALIDATION_OPTION validationOption, std::shared_ptr<Package> * packageInfo);
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
PopulatePackageInfo() {}
|
||||
PopulatePackageInfo(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
};
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <experimental/filesystem> // C++-standard header file name
|
||||
|
||||
#include "PrepareDevirtualizedRegistry.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include "RegistryDevirtualizer.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR PrepareDevirtualizedRegistry::HandlerName = L"PrepareDevirtualizedRegistry";
|
||||
|
||||
HRESULT PrepareDevirtualizedRegistry::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(ExtractRegistry());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PrepareDevirtualizedRegistry::ExtractRegistry()
|
||||
{
|
||||
std::wstring registryFilePath = m_msixRequest->GetPackageDirectoryPath() + registryDatFile;
|
||||
|
||||
std::shared_ptr<RegistryDevirtualizer> registryDevirtualizer;
|
||||
RETURN_IF_FAILED(RegistryDevirtualizer::Create(registryFilePath, m_msixRequest, ®istryDevirtualizer));
|
||||
m_msixRequest->SetRegistryDevirtualizer(registryDevirtualizer);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PrepareDevirtualizedRegistry::ExecuteForRemoveRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(ExtractRegistry());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PrepareDevirtualizedRegistry::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<PrepareDevirtualizedRegistry > localInstance(new PrepareDevirtualizedRegistry(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class PrepareDevirtualizedRegistry : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Mount the registry.dat file and set it in the msixRequest so future handlers can access the information within
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Mount the registry.dat file and set it in the msixRequest so future handlers can access the information within
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~PrepareDevirtualizedRegistry() {}
|
||||
private:
|
||||
MsixRequest * m_msixRequest = nullptr;
|
||||
|
||||
PrepareDevirtualizedRegistry() {}
|
||||
PrepareDevirtualizedRegistry(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Mount the registry.dat file and set it in the msixRequest so future handlers can access the information within
|
||||
HRESULT ExtractRegistry();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
#include "ProcessPotentialUpdate.hpp"
|
||||
#include <filesystem>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
const PCWSTR ProcessPotentialUpdate::HandlerName = L"ProcessPotentialUpdate";
|
||||
|
||||
HRESULT ProcessPotentialUpdate::ExecuteForAddRequest()
|
||||
{
|
||||
/// This design chooses the simplest solution of removing the existing package in the family before proceeding with the install
|
||||
/// This is currently good enough for our requirements; it leverages existing removal codepaths.
|
||||
/// An alternate, more complicated design would have each handler to expose a new Update verb (e.g. ExecuteForUpdate that takes in the old package)
|
||||
/// and each handler would have the opportunity to reason between the old and new packages to perform more efficient updating.
|
||||
std::wstring currentPackageFamilyName = m_msixRequest->GetPackageInfo()->GetPackageFamilyName();
|
||||
|
||||
for (auto& p : std::experimental::filesystem::directory_iterator(FilePathMappings::GetInstance().GetMsixCoreDirectory()))
|
||||
{
|
||||
if (std::experimental::filesystem::is_directory(p.path()))
|
||||
{
|
||||
std::wstring installedPackageFamilyName = GetFamilyNameFromFullName(p.path().filename());
|
||||
if (CaseInsensitiveEquals(currentPackageFamilyName, installedPackageFamilyName)
|
||||
&& !CaseInsensitiveEquals(m_msixRequest->GetPackageInfo()->GetPackageFullName(), p.path().filename()))
|
||||
{
|
||||
RETURN_IF_FAILED(RemovePackage(p.path().filename()));
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Not an update, nothing to do.");
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ProcessPotentialUpdate::RemovePackage(std::wstring packageFullName)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Found an update to an existing package, removing package",
|
||||
TraceLoggingValue(packageFullName.c_str(), "PackageToBeRemoved"));
|
||||
|
||||
AutoPtr<MsixRequest> localRequest;
|
||||
RETURN_IF_FAILED(MsixRequest::Make(OperationType::Remove, nullptr, packageFullName, MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &localRequest));
|
||||
|
||||
const HRESULT hrProcessRequest = localRequest->ProcessRequest();
|
||||
if (FAILED(hrProcessRequest))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Failed to remove package",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrProcessRequest, "HR"));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ProcessPotentialUpdate::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<ProcessPotentialUpdate> localInstance(new ProcessPotentialUpdate(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// Determines if the incoming add request is actually an update to an existing package.
|
||||
/// If it is, it'll remove the outdated package
|
||||
class ProcessPotentialUpdate : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Determines if the incoming add request is actually an update to an existing package.
|
||||
/// If it is, it'll remove the outdated package. This handler is only run on an AddRequest.
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~ProcessPotentialUpdate() {}
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
ProcessPotentialUpdate() {}
|
||||
ProcessPotentialUpdate(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Synchronously removes the outdated package before allowing the current request to proceed
|
||||
HRESULT RemovePackage(std::wstring packageFullName);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,246 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "Protocol.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR Protocol::HandlerName = L"Protocol";
|
||||
|
||||
HRESULT Protocol::ParseProtocolElement(IMsixElement* protocolElement)
|
||||
{
|
||||
Text<wchar_t> protocolName;
|
||||
RETURN_IF_FAILED(protocolElement->GetAttributeValue(nameAttribute.c_str(), &protocolName));
|
||||
|
||||
ProtocolData protocol;
|
||||
protocol.name = protocolName.Get();
|
||||
|
||||
Text<wchar_t> parameters;
|
||||
RETURN_IF_FAILED(protocolElement->GetAttributeValue(parametersAttribute.c_str(), ¶meters));
|
||||
if (parameters.Get())
|
||||
{
|
||||
protocol.parameters = parameters.Get();
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Parsing Protocol",
|
||||
TraceLoggingValue(protocolName.Get(), "Name"));
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
ComPtr<IMsixElementEnumerator> logoEnum;
|
||||
RETURN_IF_FAILED(protocolElement->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));
|
||||
|
||||
protocol.logo = m_msixRequest->GetPackageDirectoryPath() + std::wstring(L"\\") + logoPath.Get();
|
||||
}
|
||||
|
||||
ComPtr<IMsixElementEnumerator> displayNameEnum;
|
||||
RETURN_IF_FAILED(protocolElement->GetElements(displayNameQuery.c_str(), &displayNameEnum));
|
||||
RETURN_IF_FAILED(displayNameEnum->GetHasCurrent(&hasCurrent));
|
||||
if (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> displayNameElement;
|
||||
RETURN_IF_FAILED(displayNameEnum->GetCurrent(&displayNameElement));
|
||||
|
||||
Text<wchar_t> displayName;
|
||||
RETURN_IF_FAILED(displayNameElement->GetText(&displayName));
|
||||
|
||||
protocol.displayName = urlProtocolPrefix + displayName.Get();
|
||||
}
|
||||
else
|
||||
{
|
||||
protocol.displayName = urlProtocolPrefix + protocol.name;
|
||||
}
|
||||
|
||||
m_protocols.push_back(protocol);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Protocol::ParseManifest()
|
||||
{
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> extensionEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum));
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
if (m_msixRequest->GetMsixResponse()->GetIsInstallCancelled())
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
|
||||
}
|
||||
|
||||
ComPtr<IMsixElement> extensionElement;
|
||||
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
||||
Text<wchar_t> extensionCategory;
|
||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory));
|
||||
|
||||
if (wcscmp(extensionCategory.Get(), protocolCategoryNameInManifest.c_str()) == 0)
|
||||
{
|
||||
BOOL hc_protocol;
|
||||
ComPtr<IMsixElementEnumerator> protocolEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(protocolQuery.c_str(), &protocolEnum));
|
||||
RETURN_IF_FAILED(protocolEnum->GetHasCurrent(&hc_protocol));
|
||||
|
||||
if (hc_protocol)
|
||||
{
|
||||
ComPtr<IMsixElement> protocolElement;
|
||||
RETURN_IF_FAILED(protocolEnum->GetCurrent(&protocolElement));
|
||||
|
||||
RETURN_IF_FAILED(ParseProtocolElement(protocolElement.Get()));
|
||||
}
|
||||
}
|
||||
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Protocol::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
for (auto protocol = m_protocols.begin(); protocol != m_protocols.end(); ++protocol)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessProtocolForAdd(*protocol));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Protocol::ProcessProtocolForAdd(ProtocolData& protocol)
|
||||
{
|
||||
RegistryKey protocolKey;
|
||||
RETURN_IF_FAILED(m_classesKey.CreateSubKey(protocol.name.c_str(), KEY_WRITE, &protocolKey));
|
||||
|
||||
RETURN_IF_FAILED(protocolKey.SetValue(protocolValueName.c_str(), nullptr, 0, REG_SZ));
|
||||
RETURN_IF_FAILED(protocolKey.SetStringValue(L"", protocol.displayName));
|
||||
|
||||
if (protocol.logo.c_str() != nullptr)
|
||||
{
|
||||
RegistryKey defaultIconKey;
|
||||
RETURN_IF_FAILED(protocolKey.CreateSubKey(defaultIconKeyName.c_str(), KEY_WRITE, &defaultIconKey));
|
||||
RETURN_IF_FAILED(defaultIconKey.SetStringValue(L"", protocol.logo.c_str()));
|
||||
}
|
||||
|
||||
RegistryKey shellKey;
|
||||
RETURN_IF_FAILED(protocolKey.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()->GetResolvedExecutableFilePath();
|
||||
if (protocol.parameters.c_str() != nullptr)
|
||||
{
|
||||
command += std::wstring(L" ") + protocol.parameters;
|
||||
}
|
||||
else
|
||||
{
|
||||
command += commandArgument;
|
||||
}
|
||||
RETURN_IF_FAILED(commandKey.SetStringValue(L"", command));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Protocol::ExecuteForRemoveRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_classesKey.Open(HKEY_LOCAL_MACHINE, classesKeyPath.c_str(), KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
for (auto protocol = m_protocols.begin(); protocol != m_protocols.end(); ++protocol)
|
||||
{
|
||||
RETURN_IF_FAILED(ProcessProtocolForRemove(*protocol));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool Protocol::IsCurrentlyAssociatedWithPackage(PCWSTR name)
|
||||
{
|
||||
std::wstring keyPath = name + std::wstring(L"\\") + shellKeyName + std::wstring(L"\\") + openKeyName + std::wstring(L"\\") + commandKeyName;
|
||||
|
||||
RegistryKey protocolExeKey;
|
||||
HRESULT hrOpenKey = m_classesKey.OpenSubKey(keyPath.c_str(), KEY_READ, &protocolExeKey);
|
||||
if (FAILED(hrOpenKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to open protocol key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrOpenKey, "HR"),
|
||||
TraceLoggingValue(keyPath.c_str(), "Protocol key path"));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::wstring executablePath = m_msixRequest->GetPackageInfo()->GetResolvedExecutableFilePath();
|
||||
std::wstring currentlyAssociatedExe;
|
||||
if (SUCCEEDED(protocolExeKey.GetStringValue(L"", currentlyAssociatedExe)))
|
||||
{
|
||||
if (wcsncmp(currentlyAssociatedExe.c_str(), executablePath.c_str(), executablePath.size()) != 0)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Protocol is no longer associated with this package, not modifying this protocol",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
|
||||
TraceLoggingValue(currentlyAssociatedExe.c_str(), "Current exe"));
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT Protocol::ProcessProtocolForRemove(ProtocolData& protocol)
|
||||
{
|
||||
if (IsCurrentlyAssociatedWithPackage(protocol.name.c_str()))
|
||||
{
|
||||
HRESULT hrDeleteKey = m_classesKey.DeleteTree(protocol.name.c_str());
|
||||
if (FAILED(hrDeleteKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to delete protocol extension",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrDeleteKey, "HR"),
|
||||
TraceLoggingValue(protocol.name.c_str(), "Protocol"));
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT Protocol::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<Protocol> localInstance(new Protocol(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,64 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "RegistryKey.hpp"
|
||||
#include <vector>
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
|
||||
/// Data structs to be filled in from the information in the manifest
|
||||
struct ProtocolData
|
||||
{
|
||||
std::wstring name; // name of the URI scheme, such as "mailto"
|
||||
std::wstring parameters; // parameters to be passed into the executable when invoked
|
||||
std::wstring displayName; // friendly name to be displayed to users
|
||||
std::wstring logo; // filepath to logo file
|
||||
};
|
||||
|
||||
class Protocol : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Adds the protocol registrations to the per-machine registry
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the protocol registrations from the per-machine registry.
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~Protocol() {}
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
RegistryKey m_classesKey;
|
||||
std::vector<ProtocolData> m_protocols;
|
||||
|
||||
Protocol() {}
|
||||
Protocol(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Parse one protocol element, containing one ProtocolData to be added to the m_protocols vector.
|
||||
///
|
||||
/// @param protocolElement - the IMsixElement representing the uap:Protocol element from the manifest
|
||||
HRESULT ParseProtocolElement(IMsixElement * protocolElement);
|
||||
|
||||
/// Parses the manifest and fills out the m_protocols vector of ProtocolData containing data from the manifest
|
||||
HRESULT ParseManifest();
|
||||
|
||||
/// Adds the protocol data to the system registry
|
||||
///
|
||||
/// @param protocol - the protocol data to be added
|
||||
HRESULT ProcessProtocolForAdd(ProtocolData & protocol);
|
||||
|
||||
/// Removes the protocol data from the system registry
|
||||
///
|
||||
/// @param protocol - the protocol data to be removed
|
||||
HRESULT ProcessProtocolForRemove(ProtocolData& protocol);
|
||||
|
||||
/// Determines if the protocol is currently associated with the package
|
||||
///
|
||||
/// @param name - the name of the protocol
|
||||
bool IsCurrentlyAssociatedWithPackage(PCWSTR name);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,481 @@
|
|||
#include "RegistryDevirtualizer.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include <vector>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
struct VirtualRegistryMapping
|
||||
{
|
||||
PCWSTR virtualKey;
|
||||
HKEY actualRootHive;
|
||||
PCWSTR actualSubkey;
|
||||
};
|
||||
|
||||
VirtualRegistryMapping mappings[] =
|
||||
{
|
||||
//virtualKey from registry.dat, root hive, Subkey off of root hive
|
||||
{L"USER\\.DEFAULT", HKEY_USERS, L".DEFAULT"},
|
||||
{L"USER\\[{AppVCurrentUserSID}]_CLASSES", HKEY_CURRENT_USER, L"Software\\Classes"},
|
||||
{L"USER\\[{AppVCurrentUserSID}]", HKEY_CURRENT_USER, L""},
|
||||
{L"MACHINE", HKEY_LOCAL_MACHINE, L""},
|
||||
};
|
||||
|
||||
HRESULT RegistryDevirtualizer::Run(_In_ bool remove)
|
||||
{
|
||||
if (!m_hiveFileNameExists)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::wstring rootPath = m_loadedHiveKeyName + L"\\Registry";
|
||||
RETURN_IF_FAILED(m_rootKey.Open(HKEY_USERS, rootPath.c_str(), KEY_READ));
|
||||
|
||||
for (auto mapping : mappings)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Processing mapping",
|
||||
TraceLoggingValue(mapping.virtualKey, "Virtual Key"));
|
||||
|
||||
RegistryKey virtualKey;
|
||||
HRESULT hrOpenVirtualKey = m_rootKey.OpenSubKey(mapping.virtualKey, KEY_READ, &virtualKey);
|
||||
if (hrOpenVirtualKey == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Skipping mapping because it does not exist",
|
||||
TraceLoggingValue(mapping.virtualKey, "Virtual Key"));
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(hrOpenVirtualKey);
|
||||
}
|
||||
|
||||
RegistryKey realKey;
|
||||
RETURN_IF_FAILED(realKey.Open(mapping.actualRootHive, mapping.actualSubkey, KEY_READ | KEY_WRITE | WRITE_DAC));
|
||||
|
||||
if (remove)
|
||||
{
|
||||
RETURN_IF_FAILED(RemoveDevirtualizeRegistryTree(&virtualKey, &realKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(CopyAndDevirtualizeRegistryTree(&virtualKey, &realKey));
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::HasFTA(std::wstring ftaName, bool & hasFTA)
|
||||
{
|
||||
hasFTA = false;
|
||||
if (!m_hiveFileNameExists)
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
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[] =
|
||||
{
|
||||
uninstallKeySubPath, // uninstall key will be written by MsixInstaller itself, no need to copy from package
|
||||
};
|
||||
|
||||
for (auto excludeKey : excludeKeys)
|
||||
{
|
||||
if (realKey->GetPath().find(excludeKey) != std::string::npos)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::CopyAndDevirtualizeRegistryTree(RegistryKey* virtualKey, RegistryKey* realKey)
|
||||
{
|
||||
if (IsExcludeKey(realKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Key is in exclusion list: not creating anything further",
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(RegistryKey::EnumKeyAndDoActionForAllSubkeys(virtualKey,
|
||||
[&](PCWSTR subKeyName, RegistryKey*, bool*) -> HRESULT
|
||||
{
|
||||
RegistryKey sourceSubKey;
|
||||
RETURN_IF_FAILED(virtualKey->OpenSubKey(subKeyName, KEY_READ, &sourceSubKey));
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Creating subkey",
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"),
|
||||
TraceLoggingValue(subKeyName, "SubKey"));
|
||||
|
||||
RegistryKey destinationSubKey;
|
||||
HRESULT hrCreateKey = HRESULT_FROM_WIN32(realKey->CreateSubKey(subKeyName, KEY_READ | KEY_WRITE | WRITE_DAC, &destinationSubKey));
|
||||
if (hrCreateKey == HRESULT_FROM_WIN32(ERROR_CHILD_MUST_BE_VOLATILE))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Skipping attempted write to volatile key",
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"),
|
||||
TraceLoggingValue(subKeyName, "SubKey"));
|
||||
return S_OK;
|
||||
}
|
||||
else if (FAILED(hrCreateKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to Create key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"),
|
||||
TraceLoggingValue(subKeyName, "SubKey"),
|
||||
TraceLoggingValue(hrCreateKey, "HR"));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(CopyAndDevirtualizeRegistryTree(&sourceSubKey, &destinationSubKey));
|
||||
return S_OK;
|
||||
}));
|
||||
|
||||
DWORD valuesCount = 0;
|
||||
DWORD valueNameMaxLength = 0;
|
||||
DWORD valueDataMaxLength = 0;
|
||||
RETURN_IF_FAILED(virtualKey->GetValuesInfo(&valuesCount, &valueNameMaxLength, &valueDataMaxLength));
|
||||
|
||||
if (valuesCount == 0)
|
||||
{
|
||||
// no values -- nothing more to devirtualize, return early.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
DWORD valueType = REG_NONE;
|
||||
std::vector<wchar_t> valueNameBuffer(valueNameMaxLength + 1);
|
||||
std::vector<BYTE> valueDataBuffer((valueDataMaxLength + 1));
|
||||
|
||||
// for each value of this key, devirtualize it and write it to the real key.
|
||||
for (DWORD i = 0; i < valuesCount; i++)
|
||||
{
|
||||
DWORD nameLength = static_cast<DWORD>(valueNameBuffer.size());
|
||||
DWORD dataLength = static_cast<DWORD>(valueDataBuffer.size());
|
||||
valueType = REG_NONE;
|
||||
|
||||
RETURN_IF_FAILED(virtualKey->EnumValue(i, &valueNameBuffer[0], &nameLength, &valueType,
|
||||
reinterpret_cast<LPBYTE>(&valueDataBuffer[0]), &dataLength));
|
||||
|
||||
std::wstring valueNameString(valueNameBuffer.begin(), valueNameBuffer.end());
|
||||
RETURN_IF_FAILED(DevirtualizeValue(realKey, valueNameString.c_str(), valueDataBuffer, dataLength, valueType));
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::DetokenizeData(std::wstring& data)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Detokenizing string",
|
||||
TraceLoggingValue(data.c_str(), "data"));
|
||||
|
||||
std::wstring::size_type beginToken = data.find(L"[{");
|
||||
std::wstring::size_type endToken = data.find(L"}]");
|
||||
while (beginToken != std::wstring::npos && endToken != std::wstring::npos)
|
||||
{
|
||||
bool foundToken = false;
|
||||
// get the contents of what's in between the braces, i.e. [{token}]
|
||||
std::wstring token = data.substr(beginToken + 2, (endToken - beginToken - 2)); // +2 to skip over [{ characters, -2 to omit }] characters
|
||||
|
||||
std::map<std::wstring, std::wstring> map = FilePathMappings::GetInstance().GetMap();
|
||||
for (auto& pair : map)
|
||||
{
|
||||
if (token.find(pair.first) != std::wstring::npos)
|
||||
{
|
||||
// replace the entire braces [{token}] with what it represents
|
||||
data.replace(beginToken, endToken + 2 - beginToken, pair.second); // +2 to include the }] characters
|
||||
foundToken = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!foundToken)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Fail to find token",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(token.c_str(), "token"));
|
||||
break;
|
||||
}
|
||||
|
||||
beginToken = data.find(L"[{");
|
||||
endToken = data.find(L"}]");
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Detokenizing result:",
|
||||
TraceLoggingValue(data.c_str(), "data"));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::DevirtualizeValue(RegistryKey* realKey, PCWSTR name, std::vector<BYTE>& dataBuffer, DWORD dataLength, DWORD type)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Devirtualizing value info",
|
||||
TraceLoggingValue(name, "value name"),
|
||||
TraceLoggingValue(type, "valueType"));
|
||||
|
||||
DWORD actualType = (type & 0xff);
|
||||
if (actualType == REG_NONE)
|
||||
{
|
||||
actualType = REG_SZ;
|
||||
}
|
||||
|
||||
if (actualType == REG_SZ)
|
||||
{
|
||||
std::wstring dataWString = reinterpret_cast<PWSTR>(&dataBuffer[0]);
|
||||
RETURN_IF_FAILED(DetokenizeData(dataWString));
|
||||
RETURN_IF_FAILED(realKey->SetStringValue(name, dataWString));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(realKey->SetValue(name, reinterpret_cast<LPBYTE>(&dataBuffer[0]), dataLength, actualType));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RemoveSubKeyIfEmpty(RegistryKey* realKey, PCWSTR subKeyName)
|
||||
{
|
||||
// if there are no values left, try to delete the subkey
|
||||
RegistryKey subKey;
|
||||
RETURN_IF_FAILED(realKey->OpenSubKey(subKeyName, KEY_READ | KEY_WRITE | WRITE_DAC, &subKey));
|
||||
|
||||
DWORD valuesCount = 0;
|
||||
DWORD valueNameMaxLength = 0;
|
||||
DWORD valueDataMaxLength = 0;
|
||||
RETURN_IF_FAILED(subKey.GetValuesInfo(&valuesCount, &valueNameMaxLength, &valueDataMaxLength));
|
||||
|
||||
subKey.Close();
|
||||
if (valuesCount == 0)
|
||||
{
|
||||
HRESULT hrDeleteKey = realKey->DeleteSubKey(subKeyName);
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Attempt to delete subkey",
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"),
|
||||
TraceLoggingValue(subKeyName, "Subkey"),
|
||||
TraceLoggingValue(hrDeleteKey, "HR"));
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Key not empty -- not deleting subkey",
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"),
|
||||
TraceLoggingValue(subKeyName, "Subkey"),
|
||||
TraceLoggingValue(valuesCount, "Remaining value count"));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::RemoveDevirtualizeRegistryTree(RegistryKey * virtualKey, RegistryKey * realKey)
|
||||
{
|
||||
if (IsExcludeKey(realKey))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Key is in exclusion list: not processing further",
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(RegistryKey::EnumKeyAndDoActionForAllSubkeys(virtualKey,
|
||||
[&](PCWSTR subKeyName, RegistryKey*, bool*) -> HRESULT
|
||||
{
|
||||
RegistryKey sourceSubKey;
|
||||
RETURN_IF_FAILED(virtualKey->OpenSubKey(subKeyName, KEY_READ, &sourceSubKey));
|
||||
|
||||
RegistryKey destinationSubKey;
|
||||
HRESULT hrCreateKey = HRESULT_FROM_WIN32(realKey->OpenSubKey(subKeyName, KEY_READ | KEY_WRITE | WRITE_DAC, &destinationSubKey));
|
||||
if (hrCreateKey == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Key not found -- nothing to delete",
|
||||
TraceLoggingValue(realKey->GetPath().c_str(), "Realkey"),
|
||||
TraceLoggingValue(subKeyName, "SubKey"));
|
||||
return S_OK;
|
||||
}
|
||||
RETURN_IF_FAILED(hrCreateKey);
|
||||
|
||||
RETURN_IF_FAILED(RemoveDevirtualizeRegistryTree(&sourceSubKey, &destinationSubKey));
|
||||
|
||||
destinationSubKey.Close();
|
||||
RETURN_IF_FAILED(RemoveSubKeyIfEmpty(realKey, subKeyName));
|
||||
return S_OK;
|
||||
}));
|
||||
|
||||
DWORD valuesCount = 0;
|
||||
DWORD valueNameMaxLength = 0;
|
||||
DWORD valueDataMaxLength = 0;
|
||||
RETURN_IF_FAILED(virtualKey->GetValuesInfo(&valuesCount, &valueNameMaxLength, &valueDataMaxLength));
|
||||
|
||||
if (valuesCount > 0)
|
||||
{
|
||||
DWORD valueType = REG_NONE;
|
||||
std::vector<wchar_t> valueNameBuffer(valueNameMaxLength + 1);
|
||||
std::vector<BYTE> valueDataBuffer((valueDataMaxLength + 1));
|
||||
|
||||
// for each value of this key, devirtualize it and delete it from the real key.
|
||||
for (DWORD i = 0; i < valuesCount; i++)
|
||||
{
|
||||
DWORD nameLength = static_cast<DWORD>(valueNameBuffer.size());
|
||||
DWORD dataLength = static_cast<DWORD>(valueDataBuffer.size());
|
||||
valueType = REG_NONE;
|
||||
|
||||
RETURN_IF_FAILED(virtualKey->EnumValue(i, &valueNameBuffer[0], &nameLength, &valueType,
|
||||
reinterpret_cast<LPBYTE>(&valueDataBuffer[0]), &dataLength));
|
||||
|
||||
std::wstring valueNameString(valueNameBuffer.begin(), valueNameBuffer.end());
|
||||
RETURN_IF_FAILED(realKey->DeleteValue(valueNameString.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::Create(std::wstring hiveFileName, MsixRequest* msixRequest, std::shared_ptr<RegistryDevirtualizer> * instance)
|
||||
{
|
||||
bool registryFileExists = false;
|
||||
RETURN_IF_FAILED(FileExists(hiveFileName, registryFileExists));
|
||||
|
||||
std::shared_ptr<RegistryDevirtualizer> localInstance(new RegistryDevirtualizer(hiveFileName, msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
RETURN_IF_FAILED(E_OUTOFMEMORY);
|
||||
}
|
||||
|
||||
if (registryFileExists)
|
||||
{
|
||||
localInstance->m_hiveFileNameExists = true;
|
||||
HANDLE userToken = nullptr;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES, &userToken))
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
LUID seRestoreLuid;
|
||||
|
||||
if (!LookupPrivilegeValue(
|
||||
NULL, // lookup privilege on local system
|
||||
SE_RESTORE_NAME, // privilege to lookup
|
||||
&seRestoreLuid)) // receives LUID of privilege
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
LUID seBackupLuid;
|
||||
|
||||
if (!LookupPrivilegeValue(
|
||||
NULL, // lookup privilege on local system
|
||||
SE_BACKUP_NAME, // privilege to lookup
|
||||
&seBackupLuid)) // receives LUID of privilege
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
PTOKEN_PRIVILEGES pTokenPrivileges = NULL;
|
||||
|
||||
// be sure we allocate enought space for 2 LUID_AND_ATTRIBUTES
|
||||
// by default TOKEN_PRIVILEGES allocates space for only 1 LUID_AND_ATTRIBUTES.
|
||||
pTokenPrivileges = (PTOKEN_PRIVILEGES)LocalAlloc(LMEM_FIXED, sizeof(TOKEN_PRIVILEGES) + (sizeof(LUID_AND_ATTRIBUTES) * 2));
|
||||
pTokenPrivileges->PrivilegeCount = 2;
|
||||
pTokenPrivileges->Privileges[0].Luid = seRestoreLuid;
|
||||
pTokenPrivileges->Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
pTokenPrivileges->Privileges[1].Luid = seBackupLuid;
|
||||
pTokenPrivileges->Privileges[1].Attributes = SE_PRIVILEGE_ENABLED;
|
||||
|
||||
auto success = AdjustTokenPrivileges(userToken, FALSE, pTokenPrivileges, sizeof(TOKEN_PRIVILEGES), NULL, NULL);
|
||||
LocalFree(pTokenPrivileges);
|
||||
pTokenPrivileges = NULL;
|
||||
if (!success)
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(CreateTempKeyName(localInstance->m_loadedHiveKeyName));
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(RegLoadKey(HKEY_USERS, localInstance->m_loadedHiveKeyName.c_str(), localInstance->m_registryHiveFileName.c_str())));
|
||||
}
|
||||
|
||||
*instance = localInstance;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::CreateTempKeyName(std::wstring &tempName)
|
||||
{
|
||||
GUID guid;
|
||||
RETURN_IF_FAILED(CoCreateGuid(&guid));
|
||||
|
||||
WCHAR stringGuid[40] = {};
|
||||
StringFromGUID2(guid, stringGuid, ARRAYSIZE(stringGuid));
|
||||
|
||||
tempName = stringGuid;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryDevirtualizer::UnloadMountedHive()
|
||||
{
|
||||
if (m_hiveFileNameExists)
|
||||
{
|
||||
m_rootKey.Close();
|
||||
|
||||
LSTATUS status = RegUnLoadKey(HKEY_USERS, m_loadedHiveKeyName.c_str());
|
||||
|
||||
if (status == ERROR_SUCCESS)
|
||||
{
|
||||
m_hiveFileNameExists = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Failed to unload key",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(status, "Error LSTATUS"),
|
||||
TraceLoggingValue(m_loadedHiveKeyName.c_str(), "Hive key name"));
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RegistryDevirtualizer::~RegistryDevirtualizer()
|
||||
{
|
||||
UnloadMountedHive();
|
||||
}
|
|
@ -0,0 +1,94 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
#include "RegistryKey.hpp"
|
||||
#include <vector>
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// Handles the conversion of the information stored in Registry.dat
|
||||
/// to the currently running OS's registry, which is referred to here as "actual registry"
|
||||
/// The information stored in Registry.dat is referred to here as "virtual registry" or "virtualized registry"
|
||||
class RegistryDevirtualizer
|
||||
{
|
||||
public:
|
||||
|
||||
/// Add or remove the entries contained in Registry.dat
|
||||
///
|
||||
/// @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_ std::shared_ptr<RegistryDevirtualizer>* instance);
|
||||
|
||||
/// Creates a GUID string as a temporary registry key's name
|
||||
/// The Registry.dat hive will be loaded under this name to avoid conflict with existing keys or other installs
|
||||
///
|
||||
/// @param tempName - string containing the name
|
||||
static HRESULT CreateTempKeyName(std::wstring & tempName);
|
||||
|
||||
HRESULT UnloadMountedHive();
|
||||
|
||||
~RegistryDevirtualizer();
|
||||
|
||||
private:
|
||||
/// Determines if the registry key in question should not be written to the actual registry.
|
||||
/// Certain keys should not be written because they will be written by this installers.
|
||||
/// For instance, AddRemovePrograms will write an uninstall key,
|
||||
/// so we should not write the uninstall key from the virtualized registry
|
||||
///
|
||||
/// @param realKey - registry key on the actual OS
|
||||
/// @return true if the key should be excluded.
|
||||
bool IsExcludeKey(RegistryKey * realKey);
|
||||
|
||||
/// Recursively copy the registry tree from the loaded Registry.dat to the actual registry.
|
||||
///
|
||||
/// @param virtualKey - registry key from the loaded Registry.dat to copy from
|
||||
/// @param realKey - registry key in the actual registry to write to
|
||||
HRESULT CopyAndDevirtualizeRegistryTree(RegistryKey* virtualKey, RegistryKey* realKey);
|
||||
|
||||
/// Replaces the tokens with their real counterparts
|
||||
/// For instance, [{ProgramFilesX86}]\notepad\notepad.exe becomes c:\program files (x86)\notepad\notepad.exe
|
||||
/// The tokens are based on the FilePathMappings
|
||||
///
|
||||
/// @param data - the string to detokenize; this is the registry data to be written
|
||||
HRESULT DetokenizeData(std::wstring & data);
|
||||
|
||||
/// Writes the value to the actual key after detokenizing it if necessary
|
||||
///
|
||||
/// @param realKey - registry key from actual registry to write the value to
|
||||
/// @param name - registry value name, can be null for (default)
|
||||
/// @param dataBuffer - registry data
|
||||
/// @param dataLength - length of registry data
|
||||
/// @param type - registry data type, which can have AppV flags.
|
||||
HRESULT DevirtualizeValue(RegistryKey * realKey, PCWSTR name, std::vector<BYTE>& dataBuffer, DWORD dataLength, DWORD type);
|
||||
|
||||
/// Recursively removes the data contained in the loaded Registry.dat from the actual registry.
|
||||
/// Remove counterpart of @see CopyAndDevirtualizeRegistryTree
|
||||
///
|
||||
/// @param virtualKey - registry key from the loaded Registry.dat as a template of what keys to delete
|
||||
/// @param realKey - registry key in the actual registry to delete keys from
|
||||
HRESULT RemoveDevirtualizeRegistryTree(RegistryKey* virtualKey, RegistryKey* realKey);
|
||||
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
RegistryDevirtualizer() {}
|
||||
RegistryDevirtualizer(_In_ std::wstring hiveFileName, _In_ MsixRequest* msixRequest)
|
||||
: m_registryHiveFileName(hiveFileName),
|
||||
m_msixRequest(msixRequest),
|
||||
m_rootKey(nullptr) {}
|
||||
|
||||
std::wstring m_loadedHiveKeyName;
|
||||
std::wstring m_registryHiveFileName;
|
||||
|
||||
RegistryKey m_rootKey;
|
||||
bool m_hiveFileNameExists = false;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,157 @@
|
|||
#include "RegistryKey.hpp"
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
HRESULT RegistryKey::Open(
|
||||
_In_ const HKEY hkey,
|
||||
_In_opt_ PCWSTR subkey,
|
||||
_In_ const REGSAM sam)
|
||||
{
|
||||
Close();
|
||||
if (subkey != nullptr)
|
||||
{
|
||||
m_path = subkey;
|
||||
}
|
||||
return HRESULT_FROM_WIN32(RegOpenKeyExW(hkey, subkey, 0, sam, AddressOfHkey()));
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::OpenSubKey(
|
||||
_In_opt_ PCWSTR subkey,
|
||||
_Inout_ RegistryKey * regkey)
|
||||
{
|
||||
return OpenSubKey(subkey, 0 /*sam*/, regkey);
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::OpenSubKey(
|
||||
_In_opt_ PCWSTR subkey,
|
||||
_In_ const REGSAM sam,
|
||||
_Inout_ RegistryKey * regkey)
|
||||
{
|
||||
regkey->Close();
|
||||
regkey->SetPath(m_path + L"\\" + subkey);
|
||||
return HRESULT_FROM_WIN32(RegOpenKeyExW(this->m_hkey, subkey, 0, sam, regkey->AddressOfHkey()));
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::CreateSubKey(PCWSTR subkey, const REGSAM sam, RegistryKey * regkey)
|
||||
{
|
||||
regkey->Close();
|
||||
regkey->SetPath(m_path + L"\\" + subkey);
|
||||
return HRESULT_FROM_WIN32(RegCreateKeyExW(this->m_hkey, subkey, 0 /*reserved*/, nullptr /*class*/, 0 /* options */, sam,
|
||||
nullptr /*securityAttributes*/, regkey->AddressOfHkey(), nullptr /*disposition*/));
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::EnumValue(
|
||||
_In_ const DWORD index,
|
||||
_Out_writes_to_(*valueNameLength, *valueNameLength) PWSTR valueName,
|
||||
_Out_ DWORD * valueNameLength,
|
||||
_Out_ DWORD * valueType,
|
||||
_Out_writes_to_(*dataLength, *dataLength) BYTE * data,
|
||||
_Out_ DWORD * dataLength)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(RegEnumValueW(this->m_hkey, index, valueName, valueNameLength, nullptr/*reserved*/, valueType, data, dataLength));
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::GetInfo(
|
||||
_Out_opt_ DWORD * subKeysCount,
|
||||
_Out_opt_ DWORD * maxSubKeyLength,
|
||||
_Out_opt_ DWORD * valuesCount,
|
||||
_Out_opt_ DWORD * maxValueNameLength,
|
||||
_Out_opt_ DWORD * maxValueSize,
|
||||
_Out_opt_ DWORD * securityDescriptorSize,
|
||||
_Out_opt_ FILETIME * lastModified)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(RegQueryInfoKeyW(this->m_hkey, nullptr /*class*/, nullptr/*cchClass*/, nullptr /*reserved*/,
|
||||
subKeysCount, maxSubKeyLength,
|
||||
nullptr /*maxClass*/,
|
||||
valuesCount, maxValueNameLength,
|
||||
maxValueSize, securityDescriptorSize,
|
||||
lastModified));
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::GetValuesInfo(
|
||||
_Out_ DWORD * count,
|
||||
_Out_opt_ DWORD * maxNameLength,
|
||||
_Out_opt_ DWORD * maxValueSize)
|
||||
{
|
||||
return GetInfo(nullptr/*subkeysCount*/, nullptr /*maxSubKeyLength*/,
|
||||
count, maxNameLength, maxValueSize,
|
||||
nullptr /*securityDescriptorSize*/, nullptr /*lastModified*/);
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::GetStringValue(PCWSTR name, std::wstring & value)
|
||||
{
|
||||
WCHAR buffer[MAX_PATH];
|
||||
DWORD bufferSize = ARRAYSIZE(buffer);
|
||||
DWORD type = 0;
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(RegQueryValueExW(this->m_hkey, name, nullptr /*reserved*/, &type, reinterpret_cast<BYTE *>(buffer), &bufferSize)));
|
||||
if (type != REG_SZ)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INVALID_DATATYPE);
|
||||
}
|
||||
|
||||
value = buffer;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::SetValue(
|
||||
_In_opt_ PCWSTR name,
|
||||
_In_reads_bytes_opt_(valueSize) const void * value,
|
||||
_In_ const DWORD valueSize,
|
||||
_In_ const DWORD type)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(RegSetValueExW(this->m_hkey, name, 0, type, static_cast<const BYTE*>(value), valueSize));
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::SetStringValue(PCWSTR name, const std::wstring& value)
|
||||
{
|
||||
if (value.empty())
|
||||
{
|
||||
// Documentation for RegSetValueEx indicates that REG_SZ type the string must be null-terminated-- we can't just write nullptr here.
|
||||
return SetValue(name, L"", sizeof(WCHAR), REG_SZ);
|
||||
}
|
||||
return SetValue(name, value.c_str(), static_cast<DWORD>(value.size() * sizeof(WCHAR)), REG_SZ);
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::DeleteValue(_In_opt_ PCWSTR name)
|
||||
{
|
||||
HRESULT hr = HRESULT_FROM_WIN32(RegDeleteValueW(this->m_hkey, name));
|
||||
if (hr == HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
return hr;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::KeyExists(PCWSTR subkey, bool& exists)
|
||||
{
|
||||
RegistryKey target;
|
||||
LONG rc = RegOpenKeyExW(this->m_hkey, subkey, 0, KEY_READ, target.AddressOfHkey());
|
||||
if (rc == ERROR_SUCCESS)
|
||||
{
|
||||
exists = true;
|
||||
return S_OK;
|
||||
}
|
||||
else if ((rc == ERROR_FILE_NOT_FOUND) || (rc == ERROR_PATH_NOT_FOUND))
|
||||
{
|
||||
exists = false;
|
||||
return S_OK;
|
||||
}
|
||||
return HRESULT_FROM_WIN32(rc);
|
||||
}
|
||||
|
||||
HRESULT RegistryKey::SetUInt32Value(
|
||||
_In_opt_ PCWSTR name,
|
||||
_In_ UINT32 value)
|
||||
{
|
||||
return SetValue(name, &value, static_cast<DWORD>(sizeof(value)), REG_DWORD);
|
||||
}
|
|
@ -0,0 +1,169 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// Encapsulates and lifetimes the HKEY used by the RegOpenKey et al APIs.
|
||||
class RegistryKey
|
||||
{
|
||||
public:
|
||||
RegistryKey() = default;
|
||||
|
||||
RegistryKey(HKEY hkey) : m_hkey(hkey) {}
|
||||
|
||||
inline ~RegistryKey()
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
inline void Close()
|
||||
{
|
||||
if ((m_hkey != nullptr) && (m_hkey != INVALID_HANDLE_VALUE))
|
||||
{
|
||||
RegCloseKey(m_hkey);
|
||||
}
|
||||
m_hkey = nullptr;
|
||||
m_path.clear();
|
||||
}
|
||||
|
||||
inline HKEY Detach()
|
||||
{
|
||||
HKEY old = m_hkey;
|
||||
m_hkey = nullptr;
|
||||
m_path.clear();
|
||||
return old;
|
||||
}
|
||||
|
||||
inline std::wstring GetPath() { return m_path; }
|
||||
inline void SetPath(std::wstring path)
|
||||
{
|
||||
m_path = path;
|
||||
}
|
||||
|
||||
inline RegistryKey* operator&()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
inline HKEY* AddressOfHkey()
|
||||
{
|
||||
return &m_hkey;
|
||||
}
|
||||
|
||||
inline operator const HKEY() const
|
||||
{
|
||||
return m_hkey;
|
||||
}
|
||||
|
||||
inline operator HKEY()
|
||||
{
|
||||
return m_hkey;
|
||||
}
|
||||
|
||||
HRESULT Open(
|
||||
_In_ const HKEY hkey,
|
||||
_In_opt_ PCWSTR subkey,
|
||||
_In_ const REGSAM sam);
|
||||
|
||||
HRESULT OpenSubKey(
|
||||
_In_opt_ PCWSTR subkey,
|
||||
_Inout_ RegistryKey * regkey);
|
||||
|
||||
HRESULT OpenSubKey(
|
||||
_In_opt_ PCWSTR subkey,
|
||||
_In_ const REGSAM sam,
|
||||
_Inout_ RegistryKey * regkey);
|
||||
|
||||
HRESULT CreateSubKey(
|
||||
_In_opt_ PCWSTR subkey,
|
||||
_In_ const REGSAM sam,
|
||||
_Inout_ RegistryKey * regkey);
|
||||
|
||||
HRESULT GetInfo(
|
||||
_Out_opt_ DWORD * subKeysCount,
|
||||
_Out_opt_ DWORD * maxSubKeyLength,
|
||||
_Out_opt_ DWORD * valuesCount,
|
||||
_Out_opt_ DWORD * maxValueNameLength,
|
||||
_Out_opt_ DWORD * maxValueSize,
|
||||
_Out_opt_ DWORD * securityDescriptorSize,
|
||||
_Out_opt_ FILETIME * lastModified);
|
||||
|
||||
HRESULT GetValuesInfo(
|
||||
_Out_ DWORD * count,
|
||||
_Out_opt_ DWORD * maxNameLength = nullptr,
|
||||
_Out_opt_ DWORD * maxValueSize = nullptr);
|
||||
|
||||
HRESULT GetStringValue(
|
||||
_In_ PCWSTR name,
|
||||
_Out_ std::wstring& value);
|
||||
|
||||
HRESULT EnumValue(
|
||||
_In_ const DWORD index,
|
||||
_Out_writes_to_(*valueNameLength, *valueNameLength) PWSTR valueName,
|
||||
_Out_ DWORD * valueNameLength,
|
||||
_Out_ DWORD * valueType,
|
||||
_Out_writes_to_(*dataLength, *dataLength) BYTE * data,
|
||||
_Out_ DWORD * dataLength);
|
||||
|
||||
HRESULT SetValue(
|
||||
_In_opt_ PCWSTR name,
|
||||
_In_reads_bytes_opt_(valueSize) const void * value,
|
||||
_In_ const DWORD valueSize,
|
||||
_In_ const DWORD type);
|
||||
|
||||
HRESULT SetStringValue(
|
||||
_In_opt_ PCWSTR name,
|
||||
_In_opt_ const std::wstring& value);
|
||||
|
||||
HRESULT DeleteValue(
|
||||
_In_opt_ PCWSTR name);
|
||||
|
||||
HRESULT DeleteSubKey(
|
||||
_In_ PCWSTR subkey);
|
||||
|
||||
HRESULT DeleteTree(
|
||||
_In_ PCWSTR subkey);
|
||||
|
||||
HRESULT KeyExists(
|
||||
_In_ PCWSTR subkey,
|
||||
_Out_ bool& exists);
|
||||
|
||||
HRESULT SetUInt32Value(
|
||||
_In_opt_ PCWSTR name,
|
||||
_In_ UINT32 value);
|
||||
|
||||
template<typename TAction> static HRESULT EnumKeyAndDoActionForAllSubkeys(
|
||||
_In_ RegistryKey* registryKey,
|
||||
_In_ TAction subkeyActionFunction)
|
||||
{
|
||||
DWORD subKeySize = MAX_PATH + 1;
|
||||
WCHAR subKey[MAX_PATH + 1] = L"";
|
||||
|
||||
DWORD index = 0;
|
||||
LONG errorCode = RegEnumKeyEx(*registryKey, index, subKey, &subKeySize, NULL, NULL, NULL, NULL);
|
||||
while (errorCode == ERROR_SUCCESS)
|
||||
{
|
||||
bool shouldStopEnumerating = false;
|
||||
RETURN_IF_FAILED(subkeyActionFunction(subKey, registryKey, &shouldStopEnumerating));
|
||||
if (shouldStopEnumerating)
|
||||
{
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
subKeySize = MAX_PATH + 1;
|
||||
errorCode = RegEnumKeyEx(*registryKey, index, subKey, &subKeySize, NULL, NULL, NULL, NULL);
|
||||
}
|
||||
if (errorCode != ERROR_NO_MORE_ITEMS)
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(errorCode));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::wstring m_path;
|
||||
HKEY m_hkey = nullptr;
|
||||
};
|
||||
}
|
|
@ -0,0 +1,86 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
#include <propvarutil.h>
|
||||
#include <propkey.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "StartMenuLink.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR StartMenuLink::HandlerName = L"StartMenuLink";
|
||||
|
||||
HRESULT StartMenuLink::CreateLink(PCWSTR targetFilePath, PCWSTR linkFilePath, PCWSTR description, PCWSTR appUserModelId)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Creating Link",
|
||||
TraceLoggingValue(targetFilePath, "TargetFilePath"),
|
||||
TraceLoggingValue(linkFilePath, "LinkFilePath"),
|
||||
TraceLoggingValue(appUserModelId, "AppUserModelId"));
|
||||
|
||||
ComPtr<IShellLink> shellLink;
|
||||
RETURN_IF_FAILED(CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_IShellLink, reinterpret_cast<LPVOID*>(&shellLink)));
|
||||
RETURN_IF_FAILED(shellLink->SetPath(targetFilePath));
|
||||
RETURN_IF_FAILED(shellLink->SetArguments(L""));
|
||||
|
||||
if (appUserModelId != NULL && appUserModelId[0] != 0)
|
||||
{
|
||||
ComPtr<IPropertyStore> propertyStore;
|
||||
PROPVARIANT appIdPropVar;
|
||||
RETURN_IF_FAILED(shellLink->QueryInterface(IID_IPropertyStore, reinterpret_cast<LPVOID*>(&propertyStore)));
|
||||
RETURN_IF_FAILED(InitPropVariantFromString(appUserModelId, &appIdPropVar));
|
||||
RETURN_IF_FAILED(propertyStore->SetValue(PKEY_AppUserModel_ID, appIdPropVar));
|
||||
RETURN_IF_FAILED(propertyStore->Commit());
|
||||
PropVariantClear(&appIdPropVar);
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(shellLink->SetDescription(description));
|
||||
|
||||
ComPtr<IPersistFile> persistFile;
|
||||
RETURN_IF_FAILED(shellLink->QueryInterface(IID_IPersistFile, reinterpret_cast<LPVOID*>(&persistFile)));
|
||||
RETURN_IF_FAILED(persistFile->Save(linkFilePath, TRUE));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartMenuLink::ExecuteForAddRequest()
|
||||
{
|
||||
if (m_msixRequest->GetMsixResponse()->GetIsInstallCancelled())
|
||||
{
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_USEREXIT);
|
||||
}
|
||||
auto packageInfo = m_msixRequest->GetPackageInfo();
|
||||
|
||||
std::wstring filePath = FilePathMappings::GetInstance().GetMap()[L"Common Programs"] + L"\\" + packageInfo->GetDisplayName() + L".lnk";
|
||||
std::wstring resolvedExecutableFullPath = packageInfo->GetResolvedExecutableFilePath();
|
||||
std::wstring appUserModelId = m_msixRequest->GetPackageInfo()->GetId();
|
||||
RETURN_IF_FAILED(CreateLink(resolvedExecutableFullPath.c_str(), filePath.c_str(), L"", appUserModelId.c_str()));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartMenuLink::ExecuteForRemoveRequest()
|
||||
{
|
||||
auto packageInfo = m_msixRequest->GetPackageInfo();
|
||||
|
||||
std::wstring filePath = FilePathMappings::GetInstance().GetMap()[L"Common Programs"] + L"\\" + packageInfo->GetDisplayName() + L".lnk";
|
||||
|
||||
RETURN_IF_FAILED(DeleteFile(filePath.c_str()));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartMenuLink::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<StartMenuLink> localInstance(new StartMenuLink(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// Handles adding/removing the shortcut in the start menu to launch the application
|
||||
class StartMenuLink : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Creates the start menu shortcut in the per-machine CSIDL_COMMON_PROGRAM to the executable for the application
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the start menu link
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~StartMenuLink() {}
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
StartMenuLink() {}
|
||||
StartMenuLink(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Creates the Shell link for the start menu shortcut.
|
||||
/// Uses the Shell's IShellLink and IPersistFile interfaces
|
||||
/// to create and store a shortcut to the specified object.
|
||||
/// @param targetFilePath - full file path to the application exe
|
||||
/// @param linkFilePath - full file path to the shortcut .lnk file
|
||||
/// @param description - description of the Shell link, stored in the Comment field of the link properties.
|
||||
/// @param appUserModelId- Application User Model ID, needed to display toasts.
|
||||
HRESULT CreateLink(PCWSTR targetFilePath, PCWSTR linkFilePath, PCWSTR description, PCWSTR appUserModelId = NULL);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,241 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "StartupTask.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <taskschd.h>
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR StartupTask::HandlerName = L"StartupTask";
|
||||
|
||||
const PCWSTR StartupTask::TaskDefinitionXmlPrefix =
|
||||
L"<Task xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\
|
||||
<Principals>\
|
||||
<Principal id=\"Author\">\
|
||||
<GroupId>S-1-1-0</GroupId>\
|
||||
<RunLevel>LeastPrivilege</RunLevel>\
|
||||
</Principal>\
|
||||
</Principals>\
|
||||
<Triggers>\
|
||||
<LogonTrigger>\
|
||||
<Enabled>true</Enabled>\
|
||||
</LogonTrigger>\
|
||||
</Triggers>\
|
||||
<Settings>\
|
||||
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\
|
||||
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>\
|
||||
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>\
|
||||
<AllowHardTerminate>true</AllowHardTerminate>\
|
||||
<StartWhenAvailable>false</StartWhenAvailable>\
|
||||
<RunOnlyIfNetworkAvailable>false</RunOnlyIfNetworkAvailable>\
|
||||
<AllowStartOnDemand>true</AllowStartOnDemand>\
|
||||
<Enabled>true</Enabled>\
|
||||
<Hidden>false</Hidden>\
|
||||
<RunOnlyIfIdle>false</RunOnlyIfIdle>\
|
||||
<WakeToRun>false</WakeToRun>\
|
||||
<Priority>7</Priority>\
|
||||
<UseUnifiedSchedulingEngine>true</UseUnifiedSchedulingEngine>\
|
||||
</Settings>\
|
||||
<Actions Context=\"Author\">\
|
||||
<Exec>\
|
||||
<Command>\"";
|
||||
const PCWSTR StartupTask::TaskDefinitionXmlPostfix =
|
||||
L"\"</Command>\
|
||||
</Exec>\
|
||||
</Actions>\
|
||||
</Task>";
|
||||
|
||||
const PCWSTR windowsTaskFolderName = L"\\Microsoft\\Windows";
|
||||
const PCWSTR taskFolderName = L"MsixCore";
|
||||
|
||||
std::wstring StartupTask::CreateTaskXml(std::wstring& executable)
|
||||
{
|
||||
return std::wstring(TaskDefinitionXmlPrefix) +
|
||||
executable +
|
||||
TaskDefinitionXmlPostfix;
|
||||
}
|
||||
|
||||
HRESULT StartupTask::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(CreateScheduledTasks());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartupTask::CreateScheduledTasks()
|
||||
{
|
||||
if (m_tasks.empty())
|
||||
{
|
||||
// Startup tasks are not required, if there are none, nothing to do.
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
AutoCoInitialize coInit;
|
||||
RETURN_IF_FAILED(coInit.Initialize(COINIT_MULTITHREADED));
|
||||
{
|
||||
// New scope so that ComPtrs are released prior to calling CoUninitialize
|
||||
|
||||
VARIANT variantNull;
|
||||
ZeroMemory(&variantNull, sizeof(VARIANT));
|
||||
variantNull.vt = VT_NULL;
|
||||
|
||||
ComPtr<ITaskService> taskService;
|
||||
RETURN_IF_FAILED(CoCreateInstance(CLSID_TaskScheduler,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_ITaskService,
|
||||
reinterpret_cast<void**>(&taskService)));
|
||||
RETURN_IF_FAILED(taskService->Connect(variantNull /*serverName*/, variantNull /*user*/, variantNull /*domain*/, variantNull /*password*/));
|
||||
|
||||
Bstr rootFolderPathBstr(windowsTaskFolderName);
|
||||
ComPtr<ITaskFolder> rootFolder;
|
||||
RETURN_IF_FAILED(taskService->GetFolder(rootFolderPathBstr, &rootFolder));
|
||||
|
||||
Bstr taskFolderBstr(taskFolderName);
|
||||
ComPtr<ITaskFolder> msixCoreFolder;
|
||||
HRESULT hrCreateFolder = rootFolder->CreateFolder(taskFolderBstr, variantNull /*sddl*/, &msixCoreFolder);
|
||||
if (hrCreateFolder == HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS))
|
||||
{
|
||||
RETURN_IF_FAILED(rootFolder->GetFolder(taskFolderBstr, &msixCoreFolder));
|
||||
}
|
||||
else
|
||||
{
|
||||
RETURN_IF_FAILED(hrCreateFolder);
|
||||
}
|
||||
|
||||
ComPtr<ITaskDefinition> taskDefinition;
|
||||
RETURN_IF_FAILED(taskService->NewTask(0, &taskDefinition));
|
||||
|
||||
for (auto& task : m_tasks)
|
||||
{
|
||||
std::wstring taskDefinitionXml = CreateTaskXml(task.executable);
|
||||
Bstr taskDefinitionXmlBstr(taskDefinitionXml);
|
||||
RETURN_IF_FAILED(taskDefinition->put_XmlText(taskDefinitionXmlBstr));
|
||||
|
||||
Bstr taskNameBstr(task.name);
|
||||
ComPtr<IRegisteredTask> registeredTask;
|
||||
RETURN_IF_FAILED(msixCoreFolder->RegisterTaskDefinition(taskNameBstr, taskDefinition.Get(), TASK_CREATE_OR_UPDATE,
|
||||
variantNull /*userId*/, variantNull /*password*/, TASK_LOGON_GROUP, variantNull /*sddl*/, ®isteredTask));
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartupTask::ExecuteForRemoveRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(DeleteScheduledTasks())
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartupTask::DeleteScheduledTasks()
|
||||
{
|
||||
if (m_tasks.empty())
|
||||
{
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
AutoCoInitialize coInit;
|
||||
RETURN_IF_FAILED(coInit.Initialize(COINIT_MULTITHREADED));
|
||||
{
|
||||
// New scope so that ComPtrs are released prior to calling CoUninitialize
|
||||
|
||||
VARIANT variantNull;
|
||||
ZeroMemory(&variantNull, sizeof(VARIANT));
|
||||
variantNull.vt = VT_NULL;
|
||||
|
||||
ComPtr<ITaskService> taskService;
|
||||
RETURN_IF_FAILED(CoCreateInstance(CLSID_TaskScheduler,
|
||||
NULL,
|
||||
CLSCTX_INPROC_SERVER,
|
||||
IID_ITaskService,
|
||||
reinterpret_cast<void**>(&taskService)));
|
||||
RETURN_IF_FAILED(taskService->Connect(variantNull, variantNull, variantNull, variantNull));
|
||||
|
||||
Bstr rootFolderPathBstr(windowsTaskFolderName);
|
||||
ComPtr<ITaskFolder> rootFolder;
|
||||
RETURN_IF_FAILED(taskService->GetFolder(rootFolderPathBstr, &rootFolder));
|
||||
|
||||
Bstr taskFolderBstr(taskFolderName);
|
||||
ComPtr<ITaskFolder> msixCoreFolder;
|
||||
RETURN_IF_FAILED(rootFolder->GetFolder(taskFolderBstr, &msixCoreFolder));
|
||||
|
||||
for (auto& task : m_tasks)
|
||||
{
|
||||
Bstr taskNameBstr(task.name);
|
||||
ComPtr<IRegisteredTask> registeredTask;
|
||||
RETURN_IF_FAILED(msixCoreFolder->DeleteTask(taskNameBstr, 0 /*flags*/));
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartupTask::ParseManifest()
|
||||
{
|
||||
std::wstring currentPackageFamilyName = GetFamilyNameFromFullName(m_msixRequest->GetPackageInfo()->GetPackageFullName());
|
||||
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(m_msixRequest->GetPackageInfo()->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> extensionEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(extensionQuery.c_str(), &extensionEnum));
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(extensionEnum->GetHasCurrent(&hasCurrent));
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> extensionElement;
|
||||
RETURN_IF_FAILED(extensionEnum->GetCurrent(&extensionElement));
|
||||
Text<wchar_t> extensionCategory;
|
||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(categoryAttribute.c_str(), &extensionCategory));
|
||||
|
||||
if (wcscmp(extensionCategory.Get(), startupTaskCategoryNameInManifest.c_str()) == 0)
|
||||
{
|
||||
Text<wchar_t> executable;
|
||||
RETURN_IF_FAILED(extensionElement->GetAttributeValue(executableAttribute.c_str(), &executable));
|
||||
|
||||
ComPtr<IMsixElementEnumerator> startupTaskEnum;
|
||||
RETURN_IF_FAILED(extensionElement->GetElements(startupTaskQuery.c_str(), &startupTaskEnum));
|
||||
BOOL taskHasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(startupTaskEnum->GetHasCurrent(&taskHasCurrent));
|
||||
if (taskHasCurrent)
|
||||
{
|
||||
ComPtr<IMsixElement> startupTaskElement;
|
||||
RETURN_IF_FAILED(startupTaskEnum->GetCurrent(&startupTaskElement));
|
||||
|
||||
Text<wchar_t> taskId;
|
||||
RETURN_IF_FAILED(startupTaskElement->GetAttributeValue(taskIdAttribute.c_str(), &taskId));
|
||||
|
||||
Task task;
|
||||
task.executable = FilePathMappings::GetInstance().GetExecutablePath(executable.Get(), m_msixRequest->GetPackageInfo()->GetPackageFullName().c_str());
|
||||
task.name = currentPackageFamilyName + L" " + taskId.Get();
|
||||
|
||||
m_tasks.push_back(task);
|
||||
}
|
||||
}
|
||||
RETURN_IF_FAILED(extensionEnum->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT StartupTask::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<StartupTask> localInstance(new StartupTask(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(localInstance->ParseManifest());
|
||||
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
#pragma once
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "RegistryKey.hpp"
|
||||
#include <vector>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
struct Task
|
||||
{
|
||||
std::wstring name;
|
||||
std::wstring executable;
|
||||
};
|
||||
|
||||
class StartupTask : IPackageHandler
|
||||
{
|
||||
public:
|
||||
|
||||
/// Using task scheduler APIs, creates a scheduled task to run the executable listed in the windows.startupTask extension when the user logs on.
|
||||
/// This diverges from the Windows 10 handling of startupTask, as there is built-in OS support to run the tasks on logon without using task scheduler.
|
||||
/// Windows 7 and 8 and some versions of windows 10 do not have OS support to understand startupTasks in the same way, so we're using task scheduler instead.
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the scheduled tasks added
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~StartupTask() {}
|
||||
private:
|
||||
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
std::vector<Task> m_tasks;
|
||||
|
||||
/// Parses the manifest to find the executable to run as part of startup task
|
||||
HRESULT ParseManifest();
|
||||
|
||||
StartupTask() {}
|
||||
StartupTask(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
static const PCWSTR TaskDefinitionXmlPrefix;
|
||||
static const PCWSTR TaskDefinitionXmlMiddle;
|
||||
static const PCWSTR TaskDefinitionXmlPostfix;
|
||||
static const PCWSTR TaskDefinitionXmlGroupIdForAllUsers;
|
||||
|
||||
std::wstring CreateTaskXml(std::wstring& executable);
|
||||
HRESULT CreateScheduledTasks();
|
||||
HRESULT DeleteScheduledTasks();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#include "Util.hpp"
|
||||
#include <string>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
|
||||
std::wstring GetStringResource(UINT resourceId)
|
||||
{
|
||||
HMODULE instance = GetModuleHandle(nullptr);
|
||||
|
||||
WCHAR buffer[MAX_PATH] = L"";
|
||||
int loadStringRet = LoadStringW(instance, resourceId, buffer, ARRAYSIZE(buffer));
|
||||
if (loadStringRet <= 0)
|
||||
{
|
||||
return std::wstring(L"Failed to load string resource");
|
||||
}
|
||||
|
||||
std::wstring stringResource(buffer);
|
||||
|
||||
return stringResource;
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "AppxPackaging.hpp"
|
||||
#include <winmeta.h>
|
||||
#include <string.h>
|
||||
|
||||
/// Helper to get string resource
|
||||
///
|
||||
/// @param resourceId - resource ID, these should be listed in resource.h
|
||||
/// @return string for the resource, resolved from the stringtable defined in msixmgr.rc
|
||||
std::wstring GetStringResource(UINT resourceId);
|
||||
|
|
@ -0,0 +1,180 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "FilePaths.hpp"
|
||||
#include "ValidateTargetDeviceFamily.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <VersionHelpers.h>
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR ValidateTargetDeviceFamily::HandlerName = L"ValidateTargetDeviceFamily";
|
||||
|
||||
HRESULT ValidateTargetDeviceFamily::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(ParseAndValidateTargetDeviceFamilyFromPackage());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT ValidateTargetDeviceFamily::ParseAndValidateTargetDeviceFamilyFromPackage()
|
||||
{
|
||||
auto packageInfo = m_msixRequest->GetPackageInfo();
|
||||
|
||||
ComPtr<IMsixDocumentElement> domElement;
|
||||
RETURN_IF_FAILED(packageInfo->GetManifestReader()->QueryInterface(UuidOfImpl<IMsixDocumentElement>::iid, reinterpret_cast<void**>(&domElement)));
|
||||
|
||||
ComPtr<IMsixElement> element;
|
||||
RETURN_IF_FAILED(domElement->GetDocumentElement(&element));
|
||||
|
||||
/// Obtain the TargetDeviceFamily tag info
|
||||
ComPtr<IMsixElementEnumerator> dependencyEnum;
|
||||
RETURN_IF_FAILED(element->GetElements(
|
||||
L"/*[local-name()='Package']/*[local-name()='Dependencies']/*[local-name()='TargetDeviceFamily']",
|
||||
&dependencyEnum));
|
||||
|
||||
BOOL hc = FALSE;
|
||||
RETURN_IF_FAILED(dependencyEnum->GetHasCurrent(&hc));
|
||||
if (!hc)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"No Target device family Found",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR));
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_REJECTED);
|
||||
}
|
||||
|
||||
while (hc)
|
||||
{
|
||||
ComPtr<IMsixElement> dependencyElement;
|
||||
RETURN_IF_FAILED(dependencyEnum->GetCurrent(&dependencyElement));
|
||||
|
||||
Text<wchar_t> targetDeviceFamilyName;
|
||||
RETURN_IF_FAILED(dependencyElement->GetAttributeValue(L"Name", &targetDeviceFamilyName));
|
||||
m_targetDeviceFamilyName = targetDeviceFamilyName.Get();
|
||||
|
||||
Text<wchar_t> minVersion;
|
||||
RETURN_IF_FAILED(dependencyElement->GetAttributeValue(L"MinVersion", &minVersion));
|
||||
std::wstring manifestMinVersion = minVersion.Get();
|
||||
|
||||
/// Major version
|
||||
size_t start = 0;
|
||||
size_t end = manifestMinVersion.find_first_of(L'.');
|
||||
m_majorVersion = manifestMinVersion.substr(start, end - start);
|
||||
|
||||
/// Minor version
|
||||
manifestMinVersion.replace(start, end - start + 1, L"");
|
||||
end = manifestMinVersion.find_first_of(L'.');
|
||||
m_minorVersion = manifestMinVersion.substr(start, end - start);
|
||||
|
||||
/// Build number
|
||||
manifestMinVersion.replace(start, end - start + 1, L"");
|
||||
end = manifestMinVersion.find_first_of(L'.');
|
||||
m_buildNumber = manifestMinVersion.substr(start, end - start);
|
||||
|
||||
/// Return if any one of the target device families are compatible with OS
|
||||
if (IsTargetDeviceFamilyNameCompatibleWithOS() && IsManifestVersionCompatibleWithOS())
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Target device family name and manifest min version are compatible with OS",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
|
||||
TraceLoggingValue(m_targetDeviceFamilyName.c_str(), "TargetDeviceFamilyName"),
|
||||
TraceLoggingValue(minVersion.Get(), "ManifestMinVersion"));
|
||||
return S_OK;
|
||||
}
|
||||
else
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Target device family name and manifest min version are not compatible with OS",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_INFO),
|
||||
TraceLoggingValue(m_targetDeviceFamilyName.c_str(), "TargetDeviceFamilyName"),
|
||||
TraceLoggingValue(minVersion.Get(), "ManifestMinVersion"));
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(dependencyEnum->MoveNext(&hc));
|
||||
}
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Target device family name or manifest min version are not compatible with the OS",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR));
|
||||
return HRESULT_FROM_WIN32(ERROR_INSTALL_REJECTED);
|
||||
}
|
||||
|
||||
bool ValidateTargetDeviceFamily::IsTargetDeviceFamilyNameCompatibleWithOS()
|
||||
{
|
||||
if (IsWindowsProductTypeServer()) /// Server OS
|
||||
{
|
||||
if (m_targetDeviceFamilyName == L"MsixCore.Server" || m_targetDeviceFamilyName == L"MsixCore.Desktop")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (IsWindowsProductTypeDesktop()) /// Desktop OS
|
||||
{
|
||||
if (m_targetDeviceFamilyName == L"MsixCore.Desktop")
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ValidateTargetDeviceFamily::IsManifestVersionCompatibleWithOS()
|
||||
{
|
||||
OSVERSIONINFOEX osvi;
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
int op = VER_GREATER_EQUAL;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
osvi.dwMajorVersion = std::stoi(m_majorVersion.c_str());
|
||||
osvi.dwMinorVersion = std::stoi(m_minorVersion.c_str());
|
||||
osvi.dwBuildNumber = std::stoi(m_buildNumber.c_str());
|
||||
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, op);
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, op);
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, op);
|
||||
|
||||
return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, dwlConditionMask);
|
||||
}
|
||||
|
||||
bool ValidateTargetDeviceFamily::IsWindowsProductTypeDesktop()
|
||||
{
|
||||
OSVERSIONINFOEX osvi;
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
osvi.wProductType = VER_NT_WORKSTATION;
|
||||
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
|
||||
|
||||
return VerifyVersionInfo(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
|
||||
}
|
||||
|
||||
bool ValidateTargetDeviceFamily::IsWindowsProductTypeServer()
|
||||
{
|
||||
OSVERSIONINFOEX osvi;
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
osvi.wProductType = VER_NT_SERVER;
|
||||
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_PRODUCT_TYPE, VER_EQUAL);
|
||||
|
||||
return VerifyVersionInfo(&osvi, VER_PRODUCT_TYPE, dwlConditionMask);
|
||||
}
|
||||
|
||||
HRESULT ValidateTargetDeviceFamily::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<ValidateTargetDeviceFamily> localInstance(new ValidateTargetDeviceFamily(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class ValidateTargetDeviceFamily : IPackageHandler
|
||||
{
|
||||
public:
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~ValidateTargetDeviceFamily() {}
|
||||
private:
|
||||
MsixRequest * m_msixRequest = nullptr;
|
||||
|
||||
/// the target device family name in the manifest
|
||||
std::wstring m_targetDeviceFamilyName;
|
||||
|
||||
/// the parsed major version from the minversion manifest string
|
||||
std::wstring m_majorVersion;
|
||||
|
||||
/// the parsed minor version from the minversion manifest string
|
||||
std::wstring m_minorVersion;
|
||||
|
||||
/// the parsed build number from the minversion manifest string
|
||||
std::wstring m_buildNumber;
|
||||
|
||||
ValidateTargetDeviceFamily() {}
|
||||
ValidateTargetDeviceFamily(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// This function parses the target device family fields from the app manifest
|
||||
HRESULT ParseAndValidateTargetDeviceFamilyFromPackage();
|
||||
|
||||
/// This function returns true if the target device family name is compatible with the operating system
|
||||
/// Desktop OS is compatible with MsixCore.Desktop and MsixCore.Server, Server OS is compatible with MsixCore.Server
|
||||
bool IsTargetDeviceFamilyNameCompatibleWithOS();
|
||||
|
||||
/// This function returns true if the manifest version is compatible with the operating system
|
||||
bool IsManifestVersionCompatibleWithOS();
|
||||
|
||||
/// This function returns true for Desktop OS
|
||||
///
|
||||
/// @return true in case of desktop OS
|
||||
bool IsWindowsProductTypeDesktop();
|
||||
|
||||
/// This function returns true for Server OS
|
||||
///
|
||||
/// @return true in case of Server OS
|
||||
bool IsWindowsProductTypeServer();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,378 @@
|
|||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include <experimental/filesystem> // C++-standard header file name
|
||||
#include <filesystem> // Microsoft-specific implementation header file name
|
||||
|
||||
#include "VirtualFileHandler.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "FootprintFiles.hpp"
|
||||
#include "FilePaths.hpp"
|
||||
#include "Constants.hpp"
|
||||
|
||||
#include "RegistryDevirtualizer.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR VirtualFileHandler::HandlerName = L"VirtualFileHandler";
|
||||
|
||||
HRESULT VirtualFileHandler::ExecuteForAddRequest()
|
||||
{
|
||||
auto vfsDirectoryPath = m_msixRequest->GetPackageDirectoryPath() + L"\\VFS";
|
||||
|
||||
for (auto& p : std::experimental::filesystem::recursive_directory_iterator(vfsDirectoryPath))
|
||||
{
|
||||
if (std::experimental::filesystem::is_regular_file(p.path()))
|
||||
{
|
||||
RETURN_IF_FAILED(CopyVfsFileToLocal(p.path()));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::RemoveVfsFiles()
|
||||
{
|
||||
std::wstring blockMapPath = m_msixRequest->GetPackageDirectoryPath() + blockMapFile;
|
||||
ComPtr<IStream> stream;
|
||||
RETURN_IF_FAILED(CreateStreamOnFileUTF16(blockMapPath.c_str(), true /*forRead*/, &stream));
|
||||
|
||||
ComPtr<IAppxFactory> appxFactory;
|
||||
RETURN_IF_FAILED(CoCreateAppxFactoryWithHeap(MyAllocate, MyFree, m_msixRequest->GetValidationOptions(), &appxFactory));
|
||||
|
||||
ComPtr<IAppxBlockMapReader> blockMapReader;
|
||||
RETURN_IF_FAILED(appxFactory->CreateBlockMapReader(stream.Get(), &blockMapReader));
|
||||
|
||||
ComPtr<IAppxBlockMapFilesEnumerator> files;
|
||||
RETURN_IF_FAILED(blockMapReader->GetFiles(&files));
|
||||
|
||||
BOOL hasCurrent = FALSE;
|
||||
RETURN_IF_FAILED(files->GetHasCurrent(&hasCurrent));
|
||||
|
||||
while (hasCurrent)
|
||||
{
|
||||
ComPtr<IAppxBlockMapFile> file;
|
||||
RETURN_IF_FAILED(files->GetCurrent(&file));
|
||||
|
||||
//if it's a VFS file, delete it from the local location
|
||||
Text<WCHAR> name;
|
||||
RETURN_IF_FAILED(file->GetName(&name));
|
||||
std::wstring nameStr = name.Get();
|
||||
if (nameStr.find(L"VFS") != std::wstring::npos)
|
||||
{
|
||||
RETURN_IF_FAILED(RemoveVfsFile(nameStr));
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(files->MoveNext(&hasCurrent));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::ExecuteForRemoveRequest()
|
||||
{
|
||||
HRESULT hrRemoveVfsFiles = RemoveVfsFiles();
|
||||
if (FAILED(hrRemoveVfsFiles))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to remove VFS files",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrRemoveVfsFiles, "HR"));
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<VirtualFileHandler> localInstance(new VirtualFileHandler(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT GetFileVersion(std::wstring file, _Out_ UINT64& version, _Out_ bool& isUnversioned)
|
||||
{
|
||||
isUnversioned = true;
|
||||
DWORD size = GetFileVersionInfoSize(file.c_str(), nullptr);
|
||||
if (size == 0)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error == ERROR_RESOURCE_DATA_NOT_FOUND || error == ERROR_RESOURCE_TYPE_NOT_FOUND)
|
||||
{
|
||||
// Does not have version info, isUnversioned = true was set earlier.
|
||||
version = 0;
|
||||
return S_OK;
|
||||
}
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(error));
|
||||
}
|
||||
|
||||
std::unique_ptr<BYTE[]> versionInfo(new BYTE[size]);
|
||||
if (!GetFileVersionInfo(file.c_str(), 0, size, versionInfo.get()))
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
VS_FIXEDFILEINFO* fileInfo = nullptr;
|
||||
UINT fileInfoLength = 0;
|
||||
if (!VerQueryValue(versionInfo.get(), TEXT("\\"), (LPVOID*)&fileInfo, &fileInfoLength))
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
version = ((UINT64)(fileInfo->dwFileVersionMS) << 32) + fileInfo->dwFileVersionLS;
|
||||
isUnversioned = false;
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT IsFileModified(std::wstring file, _Out_ bool& isModified)
|
||||
{
|
||||
isModified = false;
|
||||
// Since we follow MSI file rules, the file is considered modified by MSI if modified date is more than 2 seconds later than creation date
|
||||
const int TwoSecondsInFileTimeIncrements = 20000000; // FILETIME is in 100 nanosecond increments
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA strData;
|
||||
if (!GetFileAttributesEx(file.c_str(), GetFileExInfoStandard, (LPVOID)&strData))
|
||||
{
|
||||
RETURN_IF_FAILED(HRESULT_FROM_WIN32(GetLastError()));
|
||||
}
|
||||
|
||||
if ((strData.ftLastWriteTime.dwHighDateTime > strData.ftCreationTime.dwHighDateTime))
|
||||
{
|
||||
// This copies the MSI bug where as long as LastWrite dwHighDateTime is greater, it's considered modified, even if
|
||||
// the Create/LastWrite are within 2 seconds of one another (by virtue of creation dwLowDateTime being near UINT_MAX)
|
||||
isModified = true;
|
||||
}
|
||||
else if (strData.ftLastWriteTime.dwHighDateTime == strData.ftCreationTime.dwHighDateTime)
|
||||
{
|
||||
if (strData.ftLastWriteTime.dwLowDateTime > strData.ftCreationTime.dwLowDateTime &&
|
||||
strData.ftLastWriteTime.dwLowDateTime - strData.ftCreationTime.dwLowDateTime > TwoSecondsInFileTimeIncrements)
|
||||
{
|
||||
isModified = true;
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::NeedToCopyFile(std::wstring sourceFullPath, std::wstring targetFullPath, _Out_ bool &needToCopyFile)
|
||||
{
|
||||
needToCopyFile = false;
|
||||
|
||||
bool targetFileExists = false;
|
||||
RETURN_IF_FAILED(FileExists(targetFullPath, targetFileExists));
|
||||
|
||||
if (!targetFileExists)
|
||||
{
|
||||
needToCopyFile = true;
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Need to copy file because target doesn't exist",
|
||||
TraceLoggingValue(targetFullPath.c_str(), "TargetFullPath"));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
// Whether we overwrite existing files or keep the existing file as-is follows MSI file versioning rules
|
||||
UINT64 targetFileVersion = 0;
|
||||
bool targetFileIsUnversioned = false;
|
||||
bool targetFileIsModified = false;
|
||||
RETURN_IF_FAILED(GetFileVersion(targetFullPath, targetFileVersion, targetFileIsUnversioned));
|
||||
if (targetFileIsUnversioned)
|
||||
{
|
||||
RETURN_IF_FAILED(IsFileModified(targetFullPath, targetFileIsModified));
|
||||
}
|
||||
|
||||
UINT64 sourceFileVersion = 0;
|
||||
bool sourceFileIsUnversioned = false;
|
||||
RETURN_IF_FAILED(GetFileVersion(sourceFullPath, sourceFileVersion, sourceFileIsUnversioned));
|
||||
|
||||
std::wstring targetVersionString = ConvertVersionToString(targetFileVersion);
|
||||
std::wstring sourceVersionString = ConvertVersionToString(sourceFileVersion);
|
||||
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Target Exists, file versioning information",
|
||||
TraceLoggingValue(targetFullPath.c_str(), "TargetFullPath"),
|
||||
TraceLoggingValue(targetVersionString.c_str(), "TargetFileVersion"),
|
||||
TraceLoggingValue(targetFileIsUnversioned, "TargetFileIsUnversioned"),
|
||||
TraceLoggingValue(targetFileIsModified, "TargetFileIsModified"),
|
||||
TraceLoggingValue(sourceFullPath.c_str(), "SourceFullPath"),
|
||||
TraceLoggingValue(sourceVersionString.c_str(), "SourceFileVersion"),
|
||||
TraceLoggingValue(sourceFileIsUnversioned, "SourceFileIsUnversioned"));
|
||||
|
||||
if (targetFileIsUnversioned && !sourceFileIsUnversioned)
|
||||
{
|
||||
// Versioned file should overwrite unversioned file
|
||||
needToCopyFile = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (targetFileIsUnversioned && !targetFileIsModified)
|
||||
{
|
||||
// Unversioned file is treated as userData; we do not want to delete user customizations if they made changes to the file
|
||||
// Existing file is unversioned and unmodified -- this is treated as unmodified user data so we can overwrite it
|
||||
needToCopyFile = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
if (targetFileVersion < sourceFileVersion)
|
||||
{
|
||||
// Higher version wins
|
||||
needToCopyFile = true;
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::CopyVfsFileIfNecessary(std::wstring sourceFullPath, std::wstring targetFullPath)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"CopyVfsFile",
|
||||
TraceLoggingValue(sourceFullPath.c_str(), "Source"),
|
||||
TraceLoggingValue(targetFullPath.c_str(), "Target"));
|
||||
|
||||
bool needToCopyFile = false;
|
||||
RETURN_IF_FAILED(NeedToCopyFile(sourceFullPath, targetFullPath, needToCopyFile));
|
||||
|
||||
if (needToCopyFile)
|
||||
{
|
||||
HRESULT hrMkdir = HRESULT_FROM_WIN32(mkdirp(targetFullPath));
|
||||
if (FAILED(hrMkdir))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to create directory for copying file",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(targetFullPath.c_str(), "FullPath"),
|
||||
TraceLoggingValue(hrMkdir, "HR"));
|
||||
}
|
||||
else if (!CopyFile(sourceFullPath.c_str(), targetFullPath.c_str(), FALSE /*failIfExists*/))
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to Copy file",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(targetFullPath.c_str(), "FullPath"),
|
||||
TraceLoggingValue(error, "error"));
|
||||
}
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::RemoveVfsFile(std::wstring fileName)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"RemoveVfsFile",
|
||||
TraceLoggingValue(fileName.c_str(), "FileName"));
|
||||
|
||||
std::wstring fullPath;
|
||||
if (FAILED(ConvertVfsNameToFullPath(fileName, fullPath)))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Could not find VFS mapping",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(fileName.c_str(), "fileName"));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
bool success = DeleteFile(fullPath.c_str());
|
||||
if (!success)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to Delete file",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(fullPath.c_str(), "FullPath"),
|
||||
TraceLoggingValue(GetLastError(), "error"));
|
||||
}
|
||||
|
||||
while (success)
|
||||
{
|
||||
MsixCoreLib_GetPathParent(fullPath);
|
||||
|
||||
// instead of checking if the directory is empty, just try to delete it.
|
||||
// if it's not empty it'll fail with expected error code that we can ignore
|
||||
success = RemoveDirectory(fullPath.c_str());
|
||||
if (!success)
|
||||
{
|
||||
DWORD error = GetLastError();
|
||||
if (error != ERROR_DIR_NOT_EMPTY)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to Delete directory",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(fullPath.c_str(), "FullPath"),
|
||||
TraceLoggingValue(GetLastError(), "error"));
|
||||
}
|
||||
}
|
||||
// if we're successfull in deleting the directory, try to delete the containing directory too, in case it's now empty
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::ConvertVfsNameToFullPath(std::wstring fileName, std::wstring& targetFullPath)
|
||||
{
|
||||
//Convert filename "VFS\FirstDir\...\file.ext" to remainingFilePath to "FirstDir\...\file.ext"
|
||||
std::wstring remainingFilePath = fileName;
|
||||
MsixCoreLib_GetPathChild(remainingFilePath); // remove the VFS directory
|
||||
RETURN_IF_FAILED(ConvertRemainingPathToFullPath(remainingFilePath, targetFullPath));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::ConvertVfsFullPathToFullPath(std::wstring sourceFullPath, std::wstring& targetFullPath)
|
||||
{
|
||||
//Convert sourceFullPath "c:\program files\MsixCoreApps\<package>\VFS\FirstDir\...\file.ext" to remainingFilePath to "FirstDir\...\file.ext"
|
||||
std::wstring vfsDirectoryPath = m_msixRequest->GetPackageDirectoryPath() + L"\\VFS\\";
|
||||
std::wstring remainingFilePath = sourceFullPath.substr(vfsDirectoryPath.size(), sourceFullPath.size());
|
||||
RETURN_IF_FAILED(ConvertRemainingPathToFullPath(remainingFilePath, targetFullPath));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::ConvertRemainingPathToFullPath(std::wstring& remainingFilePath, std::wstring& targetFullPath)
|
||||
{
|
||||
std::map<std::wstring, std::wstring> map = FilePathMappings::GetInstance().GetMap();
|
||||
for (auto& pair : map)
|
||||
{
|
||||
if (remainingFilePath.find(pair.first) != std::wstring::npos)
|
||||
{
|
||||
MsixCoreLib_GetPathChild(remainingFilePath); // remove the FirstDir directory.
|
||||
|
||||
// Pre-pend the VFS target directory to obtain the full path for the target location
|
||||
targetFullPath = pair.second + std::wstring(L"\\") + remainingFilePath;
|
||||
|
||||
//Stop looping through the list
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
|
||||
return E_NOT_SET;
|
||||
}
|
||||
|
||||
HRESULT VirtualFileHandler::CopyVfsFileToLocal(std::wstring sourceFullPath)
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"CopyVfsFileToLocal",
|
||||
TraceLoggingValue(sourceFullPath.c_str(), "FileName"));
|
||||
|
||||
std::wstring targetFullPath;
|
||||
if (FAILED(ConvertVfsFullPathToFullPath(sourceFullPath, targetFullPath)))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Could not find VFS mapping",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(sourceFullPath.c_str(), "SourceFullPath"));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
RETURN_IF_FAILED(CopyVfsFileIfNecessary(sourceFullPath, targetFullPath));
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// VirtualFileHandler handles the files in the VFS directory--on install determining whether it needs to copy the files to the de-virtualized location
|
||||
/// and updating the SharedDLL count in the registry for any shared files that may require it.
|
||||
/// Sequencing requirements: this assumes the registry.dat is already mounted, and obviously that all the files to have been extracted.
|
||||
class VirtualFileHandler : IPackageHandler
|
||||
{
|
||||
public:
|
||||
/// Copies over the VFS files from the package root directory to the actual file system location if necessary
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
/// Removes the VFS files that were copied over, if they're no longer referenced.
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
~VirtualFileHandler() {}
|
||||
private:
|
||||
MsixRequest* m_msixRequest = nullptr;
|
||||
|
||||
VirtualFileHandler() {}
|
||||
VirtualFileHandler(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
|
||||
/// Copies a VFS file from the package root to its resolved location.
|
||||
/// for example VFS\ProgramFilesx86\Notepadplusplus\notepadplusplus.exe would get copied over
|
||||
/// to c:\program files (x86)\Notepadplusplus\notepadplusplus.exe
|
||||
/// The mapping between the VFS path and the resolved location is obtained through FilePathMappings::GetMap
|
||||
///
|
||||
/// @param nameStr - A full path of the file in the VFS directory
|
||||
HRESULT CopyVfsFileToLocal(std::wstring sourceFullPath);
|
||||
|
||||
/// Determines if a file needs to be copied.
|
||||
/// If a file already exists in the target location, the highest version file will be retained
|
||||
/// This follows MSI versioning rules.
|
||||
///
|
||||
/// @param sourceFullPath - The source file
|
||||
/// @param targetFullPath - The target location to copy to
|
||||
/// @param needToCopyFile - whether we need to copy the file
|
||||
HRESULT NeedToCopyFile(std::wstring sourceFullPath, std::wstring targetFullPath, bool & needToCopyFile);
|
||||
|
||||
/// Determine whether we need to copy a VFS file from the package root to its resolved location
|
||||
///
|
||||
/// @param sourceFullPath - The source file
|
||||
/// @param targetFullPath - The target location to copy to
|
||||
HRESULT CopyVfsFileIfNecessary(std::wstring sourceFullPath, std::wstring targetFullPath);
|
||||
|
||||
/// Removes a VFS file from the resolved location
|
||||
/// This needs to first resolve the VFS file to the real location and then delete it
|
||||
/// It also removes the folder if this is the last file in that folder
|
||||
///
|
||||
/// @param fileName - the VFS file name
|
||||
HRESULT RemoveVfsFile(std::wstring fileName);
|
||||
|
||||
/// Resolves the VFS file (relative to the package root) to the real location
|
||||
///
|
||||
/// @param fileName - the VFS file name relative to the package root, i.e. VFS\FirstDir\...\File.ext
|
||||
/// @param targetFullPath - the real location full path
|
||||
/// @return E_NOT_SET if the VFS name cannot be found in the mapping.
|
||||
HRESULT ConvertVfsNameToFullPath(std::wstring sourceFullPath, std::wstring &targetFullPath);
|
||||
|
||||
/// Resolves the full file path of a VFS file to the real location
|
||||
///
|
||||
/// @param sourceFullPath - the full path of the VFS file, i.e. C:\Program Files\MsixCoreApps\<package>\VFS\FirstDir\...\File.ext
|
||||
/// @param targetFullPath - the real location full path
|
||||
/// @return E_NOT_SET if the VFS name cannot be found in the mapping.
|
||||
HRESULT ConvertVfsFullPathToFullPath(std::wstring sourceFullPath, std::wstring & targetFullPath);
|
||||
|
||||
/// Helper used by both ConvertVfsNameToFullPath and ConvertVfsFullPathToFullPath to convert the normalized remainingFilePath
|
||||
/// into the real location.
|
||||
///
|
||||
/// @param remainingFilePath - the remainder of the path inside the VFS directory, i.e. FirstDir\...\File.ext
|
||||
/// @param targetFullPath - the real location full path
|
||||
/// @return E_NOT_SET if the VFS name cannot be found in the mapping.
|
||||
HRESULT ConvertRemainingPathToFullPath(std::wstring& remainingFilePath, std::wstring& targetFullPath);
|
||||
|
||||
/// Removes all VFS files in the package
|
||||
HRESULT RemoveVfsFiles();
|
||||
};
|
||||
}
|
|
@ -0,0 +1,113 @@
|
|||
#include "Windows10Redirector.hpp"
|
||||
#include <winrt/Windows.Foundation.h>
|
||||
#include <winrt/Windows.Management.Deployment.h>
|
||||
|
||||
HRESULT MsixCoreLib::Windows10Redirector::AddPackageWithProgress(const std::wstring & packageFilePath, std::shared_ptr<MsixResponse>& msixResponse)
|
||||
{
|
||||
winrt::init_apartment();
|
||||
|
||||
winrt::Windows::Foundation::Uri packageUri{ packageFilePath };
|
||||
winrt::Windows::Management::Deployment::PackageManager packageManager;
|
||||
|
||||
auto deploymentOperation{ packageManager.AddPackageAsync(packageUri, nullptr, winrt::Windows::Management::Deployment::DeploymentOptions::None) };
|
||||
deploymentOperation.Progress([&](auto const& /*sender */, winrt::Windows::Management::Deployment::DeploymentProgress progress)
|
||||
{
|
||||
if (progress.percentage == 100)
|
||||
{
|
||||
msixResponse->Update(InstallationStep::InstallationStepCompleted, (float)progress.percentage);
|
||||
}
|
||||
else
|
||||
{
|
||||
msixResponse->Update(InstallationStep::InstallationStepExtraction, (float)progress.percentage);
|
||||
}
|
||||
});
|
||||
|
||||
deploymentOperation.get();
|
||||
if (deploymentOperation.Status() == winrt::Windows::Foundation::AsyncStatus::Error)
|
||||
{
|
||||
auto deploymentResult{ deploymentOperation.GetResults() };
|
||||
msixResponse->SetErrorStatus(deploymentOperation.ErrorCode(), deploymentResult.ErrorText().c_str());
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MsixCoreLib::Windows10Redirector::AddPackage(const std::wstring & packageFilePath)
|
||||
{
|
||||
winrt::init_apartment();
|
||||
|
||||
winrt::Windows::Foundation::Uri packageUri{ packageFilePath };
|
||||
winrt::Windows::Management::Deployment::PackageManager packageManager;
|
||||
|
||||
auto deploymentOperation{ packageManager.AddPackageAsync(packageUri, nullptr, winrt::Windows::Management::Deployment::DeploymentOptions::None) };
|
||||
deploymentOperation.get();
|
||||
if (deploymentOperation.Status() == winrt::Windows::Foundation::AsyncStatus::Error)
|
||||
{
|
||||
auto deploymentResult{ deploymentOperation.GetResults() };
|
||||
return deploymentOperation.ErrorCode();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MsixCoreLib::Windows10Redirector::RemovePackage(const std::wstring & packageFullName)
|
||||
{
|
||||
winrt::init_apartment();
|
||||
|
||||
winrt::Windows::Management::Deployment::PackageManager packageManager;
|
||||
|
||||
auto deploymentOperation{ packageManager.RemovePackageAsync(packageFullName) };
|
||||
deploymentOperation.get();
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT MsixCoreLib::Windows10Redirector::ConvertIStreamToPackagePath(IStream * packageStream, TCHAR tempPackagePath[])
|
||||
{
|
||||
TCHAR tempPathBuffer[MAX_PATH];
|
||||
|
||||
if (!GetTempPath(MAX_PATH, tempPathBuffer))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
if (!GetTempFileName(tempPathBuffer, TEXT("MSIX"), 0, tempPackagePath))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
HANDLE tempFileHandle = INVALID_HANDLE_VALUE;
|
||||
tempFileHandle = CreateFile((LPTSTR)tempPackagePath, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
||||
if (tempFileHandle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
ULONG dwBytesRead = 0;
|
||||
DWORD dwBytesWritten = 0;
|
||||
|
||||
const int numOfBytes = 2048;
|
||||
BYTE buffer[numOfBytes];
|
||||
HRESULT hr;
|
||||
|
||||
while ((hr = packageStream->Read(buffer, numOfBytes, &dwBytesRead)) == S_OK)
|
||||
{
|
||||
if (dwBytesRead > 0)
|
||||
{
|
||||
if (!WriteFile(tempFileHandle, buffer, dwBytesRead, &dwBytesWritten, NULL))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!CloseHandle(tempFileHandle))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "MsixResponse.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
/// These are functions that redirect on windows 10 using the winRT Windows::Management::Deployment::PackageManager APIs
|
||||
/// These will attempt to delayload apisets only available on windows 10, so caller should check that the OS is windows 10 before calling.
|
||||
namespace Windows10Redirector
|
||||
{
|
||||
HRESULT AddPackageWithProgress(const std::wstring & packageFilePath, std::shared_ptr<MsixResponse>& msixResponse);
|
||||
HRESULT AddPackage(const std::wstring & packageFilePath);
|
||||
HRESULT RemovePackage(const std::wstring & packageFullName);
|
||||
HRESULT ConvertIStreamToPackagePath(_In_ IStream * packageStream, _Out_ TCHAR tempPackagePath[]);
|
||||
};
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
|
||||
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
|
||||
<application>
|
||||
<!-- Windows 10 -->
|
||||
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
|
||||
<!-- Windows 8.1 -->
|
||||
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
|
||||
<!-- Windows Vista -->
|
||||
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/>
|
||||
<!-- Windows 7 -->
|
||||
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
|
||||
<!-- Windows 8 -->
|
||||
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
|
||||
</application>
|
||||
</compatibility>
|
||||
</assembly>
|
|
@ -0,0 +1,48 @@
|
|||
#include <windows.h>
|
||||
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include "WriteDevirtualizedRegistry.hpp"
|
||||
#include "GeneralUtil.hpp"
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include "RegistryDevirtualizer.hpp"
|
||||
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
const PCWSTR WriteDevirtualizedRegistry::HandlerName = L"WriteDevirtualizedRegistry";
|
||||
|
||||
HRESULT WriteDevirtualizedRegistry::ExecuteForAddRequest()
|
||||
{
|
||||
RETURN_IF_FAILED(m_msixRequest->GetRegistryDevirtualizer()->Run(false));
|
||||
RETURN_IF_FAILED(m_msixRequest->GetRegistryDevirtualizer()->UnloadMountedHive());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WriteDevirtualizedRegistry::ExecuteForRemoveRequest()
|
||||
{
|
||||
const HRESULT hrRemoveRegistry = m_msixRequest->GetRegistryDevirtualizer()->Run(true);
|
||||
if (FAILED(hrRemoveRegistry))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Unable to remove registry",
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_WARNING),
|
||||
TraceLoggingValue(hrRemoveRegistry, "HR"));
|
||||
}
|
||||
RETURN_IF_FAILED(m_msixRequest->GetRegistryDevirtualizer()->UnloadMountedHive());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT WriteDevirtualizedRegistry::CreateHandler(MsixRequest * msixRequest, IPackageHandler ** instance)
|
||||
{
|
||||
std::unique_ptr<WriteDevirtualizedRegistry > localInstance(new WriteDevirtualizedRegistry(msixRequest));
|
||||
if (localInstance == nullptr)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
*instance = localInstance.release();
|
||||
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#pragma once
|
||||
|
||||
#include "GeneralUtil.hpp"
|
||||
#include "IPackageHandler.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
class WriteDevirtualizedRegistry : IPackageHandler
|
||||
{
|
||||
public:
|
||||
HRESULT ExecuteForAddRequest();
|
||||
|
||||
HRESULT ExecuteForRemoveRequest();
|
||||
|
||||
static const PCWSTR HandlerName;
|
||||
static HRESULT CreateHandler(_In_ MsixRequest* msixRequest, _Out_ IPackageHandler** instance);
|
||||
private:
|
||||
MsixRequest * m_msixRequest = nullptr;
|
||||
|
||||
WriteDevirtualizedRegistry() {}
|
||||
WriteDevirtualizedRegistry(_In_ MsixRequest* msixRequest) : m_msixRequest(msixRequest) {}
|
||||
};
|
||||
}
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 7.4 KiB |
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 894 B |
|
@ -0,0 +1,154 @@
|
|||
// msixmgr.cpp :
|
||||
// The main entry point for msixmgr.exe. This application manages various facets of msix packages, including
|
||||
// a working preview for the MSIX/APPX installer for Windows 7 SP1 and higher OS versions
|
||||
#include "MSIXWindows.hpp"
|
||||
#include <shlobj_core.h>
|
||||
#include <CommCtrl.h>
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <vector>
|
||||
#include <TraceLoggingProvider.h>
|
||||
#include "InstallUI.hpp"
|
||||
#include "CommandLineInterface.hpp"
|
||||
#include "msixmgrLogger.hpp"
|
||||
#include "Util.hpp"
|
||||
#include "..\msixmgrLib\GeneralUtil.hpp"
|
||||
#include "resource.h"
|
||||
#include <VersionHelpers.h>
|
||||
|
||||
#include <msixmgrActions.hpp>
|
||||
using namespace std;
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_MsixTraceLoggingProvider);
|
||||
|
||||
int main(int argc, char * argv[])
|
||||
{
|
||||
// Register the providers
|
||||
TraceLoggingRegister(g_MsixUITraceLoggingProvider);
|
||||
TraceLoggingRegister(g_MsixTraceLoggingProvider);
|
||||
|
||||
HRESULT hrCoInitialize = CoInitializeEx(NULL, COINIT_MULTITHREADED);
|
||||
if (FAILED(hrCoInitialize))
|
||||
{
|
||||
std::wcout << GetStringResource(IDS_STRING_FAILED_COM_INITIALIZATION) << " " << std::hex << hrCoInitialize << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
CommandLineInterface cli(argc, argv);
|
||||
|
||||
const HRESULT hrCreateRequest = cli.Init();
|
||||
if (SUCCEEDED(hrCreateRequest))
|
||||
{
|
||||
switch (cli.GetOperationType())
|
||||
{
|
||||
case OperationType::Add:
|
||||
{
|
||||
AutoPtr<IPackageManager> packageManager;
|
||||
RETURN_IF_FAILED(MsixCoreLib_CreatePackageManager(&packageManager));
|
||||
|
||||
if (cli.IsQuietMode())
|
||||
{
|
||||
HRESULT hrAddPackage = packageManager->AddPackage(cli.GetPackageFilePathToInstall(), DeploymentOptions::None);
|
||||
if (FAILED(hrAddPackage))
|
||||
{
|
||||
std::wcout << GetStringResource(IDS_STRING_FAILED_REQUEST) << " " << std::hex << hrAddPackage << std::endl;
|
||||
return hrAddPackage;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (IsWindows10RS3OrLater())
|
||||
{
|
||||
const int bufSize = 1024;
|
||||
wchar_t path[bufSize];
|
||||
if (!GetFullPathNameW(cli.GetPackageFilePathToInstall().c_str(), bufSize, path, nullptr))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
|
||||
std::wstring protocol = std::wstring(L"ms-appinstaller:?source=");
|
||||
protocol.append(path);
|
||||
|
||||
ShellExecuteW(nullptr, L"Open", protocol.c_str(), nullptr, nullptr, SW_SHOWNORMAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto ui = new UI(packageManager, cli.GetPackageFilePathToInstall(), UIType::InstallUIAdd);
|
||||
ui->ShowUI();
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationType::Remove:
|
||||
{
|
||||
AutoPtr<IPackageManager> packageManager;
|
||||
RETURN_IF_FAILED(MsixCoreLib_CreatePackageManager(&packageManager));
|
||||
|
||||
auto packageFullName = cli.GetPackageFullName();
|
||||
HRESULT hrRemovePackage = packageManager->RemovePackage(packageFullName);
|
||||
if (FAILED(hrRemovePackage))
|
||||
{
|
||||
std::wcout << GetStringResource(IDS_STRING_FAILED_REQUEST) << " " << std::hex << hrRemovePackage << std::endl;
|
||||
return hrRemovePackage;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OperationType::FindPackage:
|
||||
{
|
||||
AutoPtr<IPackageManager> packageManager;
|
||||
RETURN_IF_FAILED(MsixCoreLib_CreatePackageManager(&packageManager));
|
||||
|
||||
shared_ptr<IInstalledPackage> packageInfo;
|
||||
HRESULT hr = packageManager->FindPackage(cli.GetPackageFullName(), packageInfo);
|
||||
if (packageInfo == NULL || FAILED(hr))
|
||||
{
|
||||
std::wcout << std::endl;
|
||||
std::wcout << L"No packages found " << hr << std::endl;
|
||||
std::wcout << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::wcout << std::endl;
|
||||
std::wcout << L"PackageFullName: " << packageInfo->GetPackageFullName().c_str() << std::endl;
|
||||
std::wcout << L"DisplayName: " << packageInfo->GetDisplayName().c_str() << std::endl;
|
||||
|
||||
std::wcout << L"DirectoryPath: " << packageInfo->GetInstalledLocation().c_str() << std::endl;
|
||||
std::wcout << std::endl;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
case OperationType::FindAllPackages:
|
||||
{
|
||||
AutoPtr<IPackageManager> packageManager;
|
||||
RETURN_IF_FAILED(MsixCoreLib_CreatePackageManager(&packageManager));
|
||||
|
||||
std::unique_ptr<std::vector<std::shared_ptr<IInstalledPackage>>> packages;
|
||||
RETURN_IF_FAILED(packageManager->FindPackages(packages));
|
||||
|
||||
unsigned int numPackages = 0;
|
||||
for (auto& package : *packages)
|
||||
{
|
||||
std::wcout << package->GetPackageFullName() << std::endl;
|
||||
numPackages++;
|
||||
}
|
||||
|
||||
std::cout << numPackages << " Package(s) found" << std::endl;
|
||||
return S_OK;
|
||||
}
|
||||
default:
|
||||
return E_NOT_SET;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cli.DisplayHelp();
|
||||
}
|
||||
|
||||
// Stop TraceLogging and unregister the providers
|
||||
TraceLoggingUnregister(g_MsixUITraceLoggingProvider);
|
||||
TraceLoggingUnregister(g_MsixTraceLoggingProvider);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "winres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (United States) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""winres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Icon
|
||||
//
|
||||
|
||||
// Icon with lowest ID value placed first to ensure application icon
|
||||
// remains consistent on all systems.
|
||||
IDI_ICONBIG ICON "icon1.ico"
|
||||
|
||||
IDI_ICONSMALL ICON "icon2.ico"
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING_FAILED_REQUEST "Failed to process request"
|
||||
IDS_STRING_FAILED_COM_INITIALIZATION "Failed to initialize com"
|
||||
IDS_STRING_HELP_OPTION_ADDPACKAGE
|
||||
"Add the package at the specified file path."
|
||||
IDS_STRING_HELP_OPTION_REMOVEPACKAGE
|
||||
"Remove the package with the specified package full name."
|
||||
IDS_STRING_HELP_OPTION_QUIETMODE "Quiet mode - no user interaction."
|
||||
IDS_STRING_HELP_OPTION_FINDALLPACKAGES "Find all installed packages."
|
||||
IDS_STRING_HELP_OPTION_FINDPACKAGE
|
||||
"Find package with the specific package full name."
|
||||
IDS_STRING_HELP_OPTION_HELP "Display this help text."
|
||||
IDS_STRING_HELPTEXT " Usage:\n ------\n \tmsixmgr.exe [options]\n\n Description:\n-----------\n \tInstalls an msix package, removes an msix package or queries for msix packages\n\n Options:\n -------\n"
|
||||
IDS_STRING_UI_CANCEL "Cancel"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_STRING_UI_CLOSE "Close"
|
||||
IDS_STRING_UI_TITLE "MSIX Installer"
|
||||
IDS_STRING_UI_INSTALL_COMPLETE "Installation Completed"
|
||||
IDS_STRING_UI_UNINSTALL_COMPLETE "Uninstallation Completed"
|
||||
IDS_STRING_UI_COMPLETION_MESSAGE "You may close this installation window"
|
||||
IDS_STRING_INSTALLTEXT "Install"
|
||||
IDS_STRING_UPDATETEXT "Update"
|
||||
IDS_STRING_CANCEL_INSTALLPOPUP
|
||||
"Are you sure you want to cancel the install?"
|
||||
IDS_STRING_CANCEL_POPUP_TITLE "Cancel App install"
|
||||
IDS_STRING_CANCEL_UPDATEPOPUP
|
||||
"Clicking on Yes will uninstall the app. Are you sure you want to cancel the install?"
|
||||
IDS_STRING_REINSTALLAPP "Reinstall"
|
||||
IDS_RUNFULLTRUST_CAPABILITY "Uses all system resources"
|
||||
IDS_STRING_ERROR_MSG "App installation failed with error message: An internal error occurred with error 0x"
|
||||
IDS_STRING_HELP_OPTION_UNPACK "Unpack package - Extract package contents to folder"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_PATH "Path to package to unpack"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_DESTINATION "Path to location where package should be unpacked to"
|
||||
IDS_STRING_HELP_OPTION_UNPACK_APPLYACLS "Apply ACLs - apply ACLs to the folder created by unpack"
|
||||
END
|
||||
|
||||
#endif // English (United States) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
|
@ -0,0 +1,229 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>15.0</VCProjectVersion>
|
||||
<ProjectGuid>{AAD31763-5E65-47FD-958A-08E35AB83025}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>msixmgr</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.17134.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>Application</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v141</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>No</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>No</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>No</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>No</LinkIncremental>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\msixmgrLib\inc;..\..\..\.vs\src\msix;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\Debug;..\..\..\.vs\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
|
||||
<AdditionalOptions>comctl32.lib %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;msi.lib;comctl32.lib;gdiplus.lib;version.lib;taskschd.lib;windowsapp_downlevel.lib;windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>api-ms-win-core-winrt-string-l1-1-0.dll;api-ms-win-core-winrt-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-1.dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D "..\..\..\.vs\bin\msix.dll" "$(OutDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\msixmgrLib\inc;..\..\..\.vs\src\msix;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\x64\Debug;..\..\..\.vs\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
|
||||
<AdditionalOptions>comctl32.lib %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;msi.lib;comctl32.lib;gdiplus.lib;version.lib;taskschd.lib;windowsapp_downlevel.lib;windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>api-ms-win-core-winrt-string-l1-1-0.dll;api-ms-win-core-winrt-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-1.dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D "..\..\..\.vs\bin\msix.dll" "$(OutDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
<CustomBuildStep>
|
||||
<Command>
|
||||
</Command>
|
||||
</CustomBuildStep>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\msixmgrLib\inc;..\..\..\.vs\src\msix;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<AdditionalOptions>/await</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\Release;..\..\..\.vs\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
|
||||
<AdditionalOptions>comctl32.lib %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;msi.lib;comctl32.lib;gdiplus.lib;version.lib;taskschd.lib;windowsapp_downlevel.lib;windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>api-ms-win-core-winrt-string-l1-1-0.dll;api-ms-win-core-winrt-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-1.dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D "..\..\..\.vs\bin\msix.dll" "$(OutDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>false</ConformanceMode>
|
||||
<AdditionalIncludeDirectories>..\msixmgrLib\inc;..\..\..\.vs\src\msix;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
|
||||
<LanguageStandard>stdcpp17</LanguageStandard>
|
||||
<AdditionalOptions>/await</AdditionalOptions>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<AdditionalLibraryDirectories>..\x64\Release;..\..\..\.vs\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
<UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
|
||||
<AdditionalOptions>comctl32.lib %(AdditionalOptions)</AdditionalOptions>
|
||||
<AdditionalDependencies>msixmgrlib.lib;msix.lib;msi.lib;comctl32.lib;gdiplus.lib;version.lib;taskschd.lib;windowsapp_downlevel.lib;windowsapp.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<DelayLoadDLLs>api-ms-win-core-winrt-string-l1-1-0.dll;api-ms-win-core-winrt-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-0.dll;api-ms-win-core-winrt-error-l1-1-1.dll</DelayLoadDLLs>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D "..\..\..\.vs\bin\msix.dll" "$(OutDir)"</Command>
|
||||
</PostBuildEvent>
|
||||
<Manifest>
|
||||
<AdditionalManifestFiles>WindowsCompatibility.manifest %(AdditionalManifestFiles)</AdditionalManifestFiles>
|
||||
</Manifest>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CommandLineInterface.hpp" />
|
||||
<ClInclude Include="InstallUI.hpp" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="Util.hpp" />
|
||||
<ClInclude Include="msixmgrLogger.hpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="InstallUI.cpp" />
|
||||
<ClCompile Include="CommandLineInterface.cpp" />
|
||||
<ClCompile Include="Util.cpp" />
|
||||
<ClCompile Include="msixmgr.cpp" />
|
||||
<ClCompile Include="msixmgrLogger.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="msixmgr.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="icon1.ico" />
|
||||
<Image Include="icon2.ico" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="WindowsCompatibility.manifest" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
|
@ -0,0 +1,67 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="CommandLineInterface.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="InstallUI.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="msixmgrLogger.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Util.hpp">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="InstallUI.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CommandLineInterface.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msixmgrLogger.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="msixmgr.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Util.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="msixmgr.rc">
|
||||
<Filter>Resource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Image Include="icon1.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
<Image Include="icon2.ico">
|
||||
<Filter>Resource Files</Filter>
|
||||
</Image>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Manifest Include="WindowsCompatibility.manifest" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -0,0 +1,13 @@
|
|||
#include "msixmgrLogger.hpp"
|
||||
// Define the GUID to use in TraceLoggingProviderRegister
|
||||
// {db5b779e-2dcf-41bc-ab0e-40a6e02f1438}
|
||||
// One way to enable:
|
||||
// logman create trace <nameoftrace> -p "{db5b779e-2dcf-41bc-ab0e-40a6e02f1438}" -o <filename>
|
||||
// i.e. logman create trace MsixTrace -p "{db5b779e-2dcf-41bc-ab0e-40a6e02f1438}" -o c:\msixtrace.etl
|
||||
// logman start MsixTrace
|
||||
// logman stop MsixTrace
|
||||
// tracerpt.exe, Windows Performance Analyzer or other tools can be used to view the etl file.
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_MsixUITraceLoggingProvider,
|
||||
"MsixInstallerUITraceLoggingProvider",
|
||||
(0xdb5b779e, 0x2dcf, 0x41bc, 0xab, 0x0e, 0x40, 0xa6, 0xe0, 0x2f, 0x14, 0x38));
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <TraceLoggingProvider.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_MsixUITraceLoggingProvider);
|
||||
|
||||
|
||||
// Definition of function to return error if failed
|
||||
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define RETURN_IF_FAILED(a) \
|
||||
{ \
|
||||
HRESULT __hr = a; \
|
||||
if (FAILED(__hr)) \
|
||||
{ \
|
||||
TraceLoggingWrite(g_MsixUITraceLoggingProvider, \
|
||||
"RETURN_IF_FAILED", \
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR), \
|
||||
TraceLoggingValue(#a, "Code"), \
|
||||
TraceLoggingHResult(__hr, "HR"), \
|
||||
TraceLoggingUInt32(__LINE__, "Line"), \
|
||||
TraceLoggingValue(__FILENAME__, "Filename")); \
|
||||
return __hr; \
|
||||
} \
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by msixmgr.rc
|
||||
//
|
||||
#define IDS_STRING_FAILED_REQUEST 102
|
||||
#define IDS_STRING_FAILED_COM_INITIALIZATION 103
|
||||
#define IDI_ICONBIG 103
|
||||
#define IDS_STRING_HELP_OPTION_ADDPACKAGE 104
|
||||
#define IDI_ICON2 104
|
||||
#define IDI_ICONSMALL 104
|
||||
#define IDS_STRING_HELP_OPTION_REMOVEPACKAGE 105
|
||||
#define IDS_STRING_HELP_OPTION_QUIETMODE 106
|
||||
#define IDS_STRING_HELP_OPTION_FINDALLPACKAGES 107
|
||||
#define IDS_STRING_HELP_OPTION_FINDPACKAGE 108
|
||||
#define IDS_STRING_HELP_OPTION_HELP 109
|
||||
#define IDS_STRING_HELPTEXT 110
|
||||
#define IDS_STRING_UI_CANCEL 111
|
||||
#define IDS_STRING_UI_CLOSE 112
|
||||
#define IDS_STRING_UI_TITLE 113
|
||||
#define IDS_STRING_UI_INSTALL_COMPLETE 114
|
||||
#define IDS_STRING_UI_UNINSTALL_COMPLETE 115
|
||||
#define IDS_STRING_UI_COMPLETION_MESSAGE 116
|
||||
#define IDS_STRING_INSTALLTEXT 117
|
||||
#define IDS_STRING_UPDATETEXT 118
|
||||
#define IDS_STRING_CANCEL_INSTALLPOPUP 119
|
||||
#define IDS_STRING_CANCEL_POPUP_TITLE 120
|
||||
#define IDS_STRING_CANCEL_UPDATEPOPUP 121
|
||||
#define IDS_STRING_REINSTALLAPP 122
|
||||
#define IDS_RUNFULLTRUST_CAPABILITY 123
|
||||
#define IDS_STRING_ERROR_MSG 124
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK 125
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_PATH 126
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_DESTINATION 127
|
||||
#define IDS_STRING_HELP_OPTION_UNPACK_APPLYACLS 128
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 122
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
|
@ -0,0 +1,191 @@
|
|||
#include "GeneralUtil.hpp"
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <string>
|
||||
#include <codecvt>
|
||||
#include <iostream>
|
||||
#include <sddl.h>
|
||||
#include <memory>
|
||||
#pragma warning(disable : 4996)
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
std::string utf16_to_utf8(const std::wstring& utf16string)
|
||||
{
|
||||
auto converted = std::wstring_convert<std::codecvt_utf8<wchar_t>>{}.to_bytes(utf16string.data());
|
||||
std::string result(converted.begin(), converted.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring utf8_to_utf16(const std::string& utf8string)
|
||||
{
|
||||
// see https://connect.microsoft.com/VisualStudio/feedback/details/1403302/unresolved-external-when-using-codecvt-utf8
|
||||
auto converted = std::wstring_convert<std::codecvt_utf8_utf16<unsigned short>, unsigned short>{}.from_bytes(utf8string.data());
|
||||
std::wstring result(converted.begin(), converted.end());
|
||||
return result;
|
||||
}
|
||||
|
||||
std::wstring ConvertVersionToString(UINT64 version)
|
||||
{
|
||||
return std::to_wstring((version >> 0x30) & 0xFFFF) + L"."
|
||||
+ std::to_wstring((version >> 0x20) & 0xFFFF) + L"."
|
||||
+ std::to_wstring((version >> 0x10) & 0xFFFF) + L"."
|
||||
+ std::to_wstring((version) & 0xFFFF);
|
||||
}
|
||||
|
||||
std::wstring GetFamilyNameFromFullName(const std::wstring& fullName)
|
||||
{
|
||||
return fullName.substr(0, fullName.find(L"_")) + fullName.substr(fullName.find_last_of(L"_"));
|
||||
}
|
||||
|
||||
bool CaseInsensitiveEquals(const std::wstring& left, const std::wstring& right)
|
||||
{
|
||||
return (_wcsicmp(left.c_str(), right.c_str()) == 0);
|
||||
}
|
||||
|
||||
HRESULT GetAttributeValueFromElement(IMsixElement * element, const std::wstring attributeName, std::wstring & attributeValue)
|
||||
{
|
||||
Text<wchar_t> textValue;
|
||||
RETURN_IF_FAILED(element->GetAttributeValue(attributeName.c_str(), &textValue));
|
||||
if (textValue.Get() != nullptr)
|
||||
{
|
||||
attributeValue = textValue.Get();
|
||||
}
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
std::wstring GuidFromManifestId(std::wstring id)
|
||||
{
|
||||
return L"{" + id + L"}";
|
||||
}
|
||||
|
||||
HRESULT FileExists(std::wstring file, _Out_ bool &exists)
|
||||
{
|
||||
DWORD fileAttributes = GetFileAttributesW(file.c_str());
|
||||
if (fileAttributes == INVALID_FILE_ATTRIBUTES)
|
||||
{
|
||||
DWORD lastError = GetLastError();
|
||||
if ((lastError == ERROR_FILE_NOT_FOUND) || (lastError == ERROR_PATH_NOT_FOUND))
|
||||
{
|
||||
exists = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
return HRESULT_FROM_WIN32(lastError);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
exists = true;
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT GetCurrentUserSidString(std::wstring & userSidString)
|
||||
{
|
||||
HANDLE token;
|
||||
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &token))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
DWORD tokenSize = 0;
|
||||
GetTokenInformation(token, TokenUser, nullptr /*token buffer */, tokenSize, &tokenSize);
|
||||
DWORD lastError = GetLastError();
|
||||
if (lastError != ERROR_INSUFFICIENT_BUFFER)
|
||||
{
|
||||
return HRESULT_FROM_WIN32(lastError);
|
||||
}
|
||||
|
||||
std::unique_ptr<BYTE[]> tokenBuffer(new BYTE[tokenSize]);
|
||||
if (!tokenBuffer)
|
||||
{
|
||||
return E_OUTOFMEMORY;
|
||||
}
|
||||
|
||||
if (!GetTokenInformation(token, TokenUser, tokenBuffer.get(), tokenSize, &tokenSize))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
auto pTokenUser = reinterpret_cast<PTOKEN_USER>(tokenBuffer.get());
|
||||
|
||||
PWSTR stringSid = nullptr;
|
||||
if (!ConvertSidToStringSid(pTokenUser->User.Sid, &stringSid))
|
||||
{
|
||||
return HRESULT_FROM_WIN32(GetLastError());
|
||||
}
|
||||
userSidString = stringSid;
|
||||
LocalFree(stringSid);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
BOOL IsWindows10RS3OrLater()
|
||||
{
|
||||
OSVERSIONINFOEX osvi;
|
||||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||||
|
||||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||||
osvi.dwMajorVersion = 10;
|
||||
osvi.dwMinorVersion = 0;
|
||||
osvi.dwBuildNumber = 16299;
|
||||
|
||||
DWORDLONG dwlConditionMask = 0;
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_MAJORVERSION, VER_GREATER_EQUAL);
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
|
||||
VER_SET_CONDITION(dwlConditionMask, VER_BUILDNUMBER, VER_GREATER_EQUAL);
|
||||
|
||||
return VerifyVersionInfo(&osvi, VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER, dwlConditionMask);
|
||||
}
|
||||
|
||||
|
||||
/// Replaces all oldchars in input with newchar
|
||||
///
|
||||
/// @param input - The input string that contains the characters to be changed
|
||||
/// @param oldchar - Old character that are to be replaced
|
||||
/// @param newchar - New character that replaces oldchar
|
||||
void replace(std::wstring& input, const wchar_t oldchar, const wchar_t newchar)
|
||||
{
|
||||
std::size_t found = input.find_first_of(oldchar);
|
||||
while (found != std::string::npos)
|
||||
{
|
||||
input[found] = newchar;
|
||||
found = input.find_first_of(oldchar, found + 1);
|
||||
}
|
||||
}
|
||||
|
||||
/// Makes a directory, including all parent directories based on the inputted filepath
|
||||
///
|
||||
/// @param utf16Path - The filepath to create a directory in utf16
|
||||
int mkdirp(std::wstring& utf16Path)
|
||||
{
|
||||
replace(utf16Path, L'/', L'\\');
|
||||
for (std::size_t i = 3; i < utf16Path.size(); i++) // 3 skips past c:
|
||||
{
|
||||
if (utf16Path[i] == L'\0')
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (utf16Path[i] == L'\\')
|
||||
{
|
||||
// Temporarily set string to terminate at the '\' character
|
||||
// to obtain name of the subdirectory to create
|
||||
utf16Path[i] = L'\0';
|
||||
|
||||
if (!CreateDirectoryW(utf16Path.c_str(), nullptr))
|
||||
{
|
||||
int lastError = static_cast<int>(GetLastError());
|
||||
|
||||
// It is normal for CreateDirectory to fail if the subdirectory
|
||||
// already exists. Other errors should not be ignored.
|
||||
if (lastError != ERROR_ALREADY_EXISTS)
|
||||
{
|
||||
return lastError;
|
||||
}
|
||||
}
|
||||
// Restore original string
|
||||
utf16Path[i] = L'\\';
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,324 @@
|
|||
#pragma once
|
||||
#include <windows.h>
|
||||
#include "AppxPackaging.hpp"
|
||||
#include <winmeta.h>
|
||||
#include <string.h>
|
||||
|
||||
namespace MsixCoreLib
|
||||
{
|
||||
//
|
||||
// Converts a wstring from utf16 to utf8
|
||||
//
|
||||
// Parameters:
|
||||
// utf16string - A utf16 wstring
|
||||
//
|
||||
std::string utf16_to_utf8(const std::wstring& utf16string);
|
||||
|
||||
/// Converts a string from utf8 to utf16
|
||||
///
|
||||
/// @param utf8string - A utf8 string
|
||||
/// @return utf16 string
|
||||
std::wstring utf8_to_utf16(const std::string& utf8string);
|
||||
|
||||
// Helper to convert version number to a version number string
|
||||
std::wstring ConvertVersionToString(UINT64 version);
|
||||
|
||||
//
|
||||
// A designated memory allocator
|
||||
//
|
||||
// Parameters:
|
||||
// cb - The size of memory
|
||||
//
|
||||
inline LPVOID STDMETHODCALLTYPE MyAllocate(SIZE_T cb) { return std::malloc(cb); }
|
||||
|
||||
//
|
||||
// A designated memory freeing method
|
||||
//
|
||||
// Parameters:
|
||||
// pv - A pointer to the file to release
|
||||
//
|
||||
inline void STDMETHODCALLTYPE MyFree(LPVOID pv) { std::free(pv); }
|
||||
|
||||
/// Converts a packageFullName (i.e. examplePackageName_1.0.0.0_x64_resourceId_8wekyb3d8bbwe)
|
||||
/// into a packageFamilyName (i.e. examplePackageName_8wekyb3d8bbwe)
|
||||
///
|
||||
/// @param fullName - the packageFullName, assumed to be properly formatted and not validated.
|
||||
/// @return packageFamilyName for the packageFullName
|
||||
std::wstring GetFamilyNameFromFullName(const std::wstring& fullName);
|
||||
|
||||
/// Determines if two strings are case-insensitive equals
|
||||
///
|
||||
/// @param left - one of the two strings
|
||||
/// @param right - the other of the two strings
|
||||
/// @return true if the strings equal, false otherwise
|
||||
bool CaseInsensitiveEquals(const std::wstring& left, const std::wstring& right);
|
||||
|
||||
/// Returns the current user sid as a string.
|
||||
///
|
||||
/// @param userSidString - current user sid stirng
|
||||
HRESULT GetCurrentUserSidString(std::wstring & userSidString);
|
||||
|
||||
/// Determines if the currently running OS is Windows 10 RS3 or later.
|
||||
/// For this to work correctly, the calling exe needs to have compatibility manifest to allow it to detect windows 10 versions
|
||||
/// https://docs.microsoft.com/en-us/windows/desktop/SysInfo/targeting-your-application-at-windows-8-1
|
||||
BOOL IsWindows10RS3OrLater();
|
||||
|
||||
/// Replaces all oldchars in input with newchar
|
||||
///
|
||||
/// @param input - The input string that contains the characters to be changed
|
||||
/// @param oldchar - Old character that are to be replaced
|
||||
/// @param newchar - New character that replaces oldchar
|
||||
void replace(std::wstring& input, const wchar_t oldchar, const wchar_t newchar);
|
||||
|
||||
/// Makes a directory, including all parent directories based on the inputted filepath
|
||||
///
|
||||
/// @param utf16Path - The filepath to create a directory in utf16
|
||||
int mkdirp(std::wstring& utf16Path);
|
||||
|
||||
//
|
||||
// Stripped down ComPtr class provided for those platforms that do not already have a ComPtr class.
|
||||
//
|
||||
template <class T>
|
||||
class ComPtr
|
||||
{
|
||||
public:
|
||||
// default ctor
|
||||
ComPtr() = default;
|
||||
ComPtr(T* ptr) : m_ptr(ptr) { InternalAddRef(); }
|
||||
|
||||
~ComPtr() { InternalRelease(); }
|
||||
inline T* operator->() const { return m_ptr; }
|
||||
inline T* Get() const { return m_ptr; }
|
||||
|
||||
inline T** operator&()
|
||||
{
|
||||
InternalRelease();
|
||||
return &m_ptr;
|
||||
}
|
||||
|
||||
inline T* operator=(__in_opt T* ptr) throw()
|
||||
{
|
||||
InternalRelease();
|
||||
m_ptr = ptr;
|
||||
InternalAddRef();
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
void Release() { InternalRelease(); }
|
||||
|
||||
protected:
|
||||
T * m_ptr = nullptr;
|
||||
|
||||
inline void InternalAddRef() { if (m_ptr) { m_ptr->AddRef(); } }
|
||||
inline void InternalRelease()
|
||||
{
|
||||
T* temp = m_ptr;
|
||||
if (temp)
|
||||
{
|
||||
m_ptr = nullptr;
|
||||
temp->Release();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <class T>
|
||||
class AutoPtr
|
||||
{
|
||||
public:
|
||||
AutoPtr() = default;
|
||||
AutoPtr(T * ptr) : m_ptr(ptr) {}
|
||||
|
||||
~AutoPtr() { delete m_ptr; }
|
||||
void Free() { delete m_ptr; m_ptr = 0; }
|
||||
|
||||
inline T* Detach()
|
||||
{
|
||||
T* old = m_ptr;
|
||||
m_ptr = 0;
|
||||
return old;
|
||||
}
|
||||
|
||||
inline operator const T* () const
|
||||
{
|
||||
return (T*)m_ptr;
|
||||
}
|
||||
|
||||
inline operator T* ()
|
||||
{
|
||||
return (T*)m_ptr;
|
||||
}
|
||||
|
||||
inline const T& operator*() const
|
||||
{
|
||||
return *m_ptr;
|
||||
}
|
||||
inline T& operator*()
|
||||
{
|
||||
return *m_ptr;
|
||||
}
|
||||
|
||||
inline const T* Get() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
inline T* Get()
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
inline T** operator&()
|
||||
{
|
||||
return &m_ptr;
|
||||
}
|
||||
|
||||
inline T** AddressOf()
|
||||
{
|
||||
return &m_ptr;
|
||||
}
|
||||
|
||||
inline T** FreeAndAddressOf()
|
||||
{
|
||||
Free();
|
||||
return &m_ptr;
|
||||
}
|
||||
|
||||
inline const T* operator->() const
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
inline T* operator->()
|
||||
{
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
inline T* operator=(T* ptr)
|
||||
{
|
||||
if (m_ptr != ptr)
|
||||
{
|
||||
delete m_ptr;
|
||||
m_ptr = ptr;
|
||||
}
|
||||
return m_ptr;
|
||||
}
|
||||
|
||||
protected:
|
||||
T* m_ptr = nullptr;
|
||||
};
|
||||
|
||||
//
|
||||
// Helper class to free string buffers obtained from the packaging APIs.
|
||||
//
|
||||
template<typename T>
|
||||
class Text
|
||||
{
|
||||
public:
|
||||
T** operator&() { return &content; }
|
||||
~Text() { Cleanup(); }
|
||||
T* Get() { return content; }
|
||||
|
||||
T* content = nullptr;
|
||||
protected:
|
||||
void Cleanup() { if (content) { MyFree(content); content = nullptr; } }
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Helper class to free string buffers created using OLE memory allocator.
|
||||
//
|
||||
template<typename T>
|
||||
class TextOle
|
||||
{
|
||||
public:
|
||||
T** operator&() { return &content; }
|
||||
~TextOle() { Cleanup(); }
|
||||
T* Get() { return content; }
|
||||
|
||||
T* content = nullptr;
|
||||
protected:
|
||||
void Cleanup() { if (content) { CoTaskMemFree(content); content = nullptr; } }
|
||||
};
|
||||
|
||||
|
||||
HRESULT GetAttributeValueFromElement(IMsixElement* element, std::wstring attributeName, std::wstring& attributeValue);
|
||||
/// The manifest ID is missing the curly braces;
|
||||
/// This adds the curly braces to convert it into a proper Guid form.
|
||||
std::wstring GuidFromManifestId(std::wstring id);
|
||||
|
||||
HRESULT FileExists(std::wstring file, _Out_ bool &exists);
|
||||
|
||||
class AutoCoInitialize
|
||||
{
|
||||
public:
|
||||
inline AutoCoInitialize()
|
||||
: hr(CO_E_NOTINITIALIZED)
|
||||
{
|
||||
}
|
||||
|
||||
inline ~AutoCoInitialize()
|
||||
{
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
Uninitialize();
|
||||
}
|
||||
}
|
||||
|
||||
inline HRESULT Initialize(
|
||||
_In_ DWORD threadingModel = COINIT_MULTITHREADED)
|
||||
{
|
||||
HRESULT hr = CoInitializeEx(NULL, threadingModel);
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
this->hr = hr;
|
||||
}
|
||||
else if (hr == RPC_E_CHANGED_MODE)
|
||||
{
|
||||
// Thread was already initialized with a different apartment model, don't need to initialize again.
|
||||
// But leave this->hr as a FAILED value as we didn't successfully initialize COM so we better not uninitialize it later
|
||||
return S_FALSE;
|
||||
}
|
||||
|
||||
return hr;
|
||||
}
|
||||
|
||||
inline void Uninitialize()
|
||||
{
|
||||
if (SUCCEEDED(hr))
|
||||
{
|
||||
CoUninitialize();
|
||||
this->hr = CO_E_NOTINITIALIZED;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
// Actions Not Supported
|
||||
AutoCoInitialize(_In_ const AutoCoInitialize&);
|
||||
AutoCoInitialize& operator=(_In_ const AutoCoInitialize&);
|
||||
|
||||
private:
|
||||
HRESULT hr;
|
||||
};
|
||||
|
||||
class Bstr
|
||||
{
|
||||
BSTR m_bstr;
|
||||
public:
|
||||
operator BSTR() && = delete;
|
||||
operator BSTR() & { return m_bstr; }
|
||||
Bstr() { m_bstr = nullptr; }
|
||||
Bstr(std::wstring text)
|
||||
{
|
||||
m_bstr = ::SysAllocStringLen(text.c_str(), static_cast<UINT>(text.length()));
|
||||
}
|
||||
~Bstr() { ::SysFreeString(m_bstr); }
|
||||
|
||||
BSTR* AddressOf()
|
||||
{
|
||||
::SysFreeString(m_bstr);
|
||||
return &m_bstr;
|
||||
}
|
||||
|
||||
BSTR& Get() { return m_bstr; }
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,14 @@
|
|||
#include "msixmgrLogger.hpp"
|
||||
|
||||
// Define the GUID to use in TraceLoggingProviderRegister
|
||||
// {033321d3-d599-48e0-868d-c59f15901637}
|
||||
// One way to enable:
|
||||
// logman create trace <nameoftrace> -p "{033321d3-d599-48e0-868d-c59f15901637}" -o <filename>
|
||||
// i.e. logman create trace MsixTrace -p "{033321d3-d599-48e0-868d-c59f15901637}" -o c:\msixtrace.etl
|
||||
// logman start MsixTrace
|
||||
// logman stop MsixTrace
|
||||
// tracerpt.exe, Windows Performance Analyzer or other tools can be used to view the etl file.
|
||||
TRACELOGGING_DEFINE_PROVIDER(
|
||||
g_MsixTraceLoggingProvider,
|
||||
"MsixTraceLoggingProvider",
|
||||
(0x033321d3, 0xd599, 0x48e0, 0x86, 0x8d, 0xc5, 0x9f, 0x15, 0x90, 0x16, 0x37));
|
|
@ -0,0 +1,23 @@
|
|||
#pragma once
|
||||
#include <windows.h>
|
||||
#include <TraceLoggingProvider.h>
|
||||
TRACELOGGING_DECLARE_PROVIDER(g_MsixTraceLoggingProvider);
|
||||
|
||||
|
||||
// Definition of function to return error if failed
|
||||
#define __FILENAME__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
|
||||
#define RETURN_IF_FAILED(a) \
|
||||
{ \
|
||||
HRESULT __hr = a; \
|
||||
if (FAILED(__hr)) \
|
||||
{ \
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider, \
|
||||
"RETURN_IF_FAILED", \
|
||||
TraceLoggingLevel(WINEVENT_LEVEL_ERROR), \
|
||||
TraceLoggingValue(#a, "Code"), \
|
||||
TraceLoggingHResult(__hr, "HR"), \
|
||||
TraceLoggingUInt32(__LINE__, "Line"), \
|
||||
TraceLoggingValue(__FILENAME__, "Filename")); \
|
||||
return __hr; \
|
||||
} \
|
||||
}
|
|
@ -0,0 +1,245 @@
|
|||
#include "PackageManager.hpp"
|
||||
#include "MsixRequest.hpp"
|
||||
#include "Constants.hpp"
|
||||
#include "PopulatePackageInfo.hpp"
|
||||
#include "MsixTraceLoggingProvider.hpp"
|
||||
#include <experimental/filesystem>
|
||||
#include <thread>
|
||||
#include "Windows10Redirector.hpp"
|
||||
|
||||
using namespace std;
|
||||
using namespace MsixCoreLib;
|
||||
|
||||
PackageManager::PackageManager()
|
||||
{
|
||||
}
|
||||
|
||||
shared_ptr<IMsixResponse> PackageManager::AddPackageAsync(const wstring & packageFilePath, DeploymentOptions options, function<void(const IMsixResponse&)> callback)
|
||||
{
|
||||
if (IsWindows10RS3OrLater())
|
||||
{
|
||||
auto msixResponse = std::make_shared<MsixResponse>();
|
||||
msixResponse->SetCallback(callback);
|
||||
|
||||
auto t = thread([&](shared_ptr<MsixResponse> response) {
|
||||
Windows10Redirector::AddPackageWithProgress(packageFilePath, response);
|
||||
}, msixResponse);
|
||||
t.detach();
|
||||
|
||||
return msixResponse;
|
||||
}
|
||||
|
||||
ComPtr<IStream> packageStream;
|
||||
if (FAILED(CreateStreamOnFileUTF16(packageFilePath.c_str(), /*forRead */ true, &packageStream)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return AddPackageAsync(packageStream.Get(), options, callback);
|
||||
}
|
||||
|
||||
shared_ptr<IMsixResponse> PackageManager::AddPackageAsync(IStream * packageStream, DeploymentOptions options, function<void(const IMsixResponse&)> callback)
|
||||
{
|
||||
if (IsWindows10RS3OrLater())
|
||||
{
|
||||
auto msixResponse = std::make_shared<MsixResponse>();
|
||||
msixResponse->SetCallback(callback);
|
||||
|
||||
TCHAR tempPackagePath[MAX_PATH];
|
||||
if (FAILED(Windows10Redirector::ConvertIStreamToPackagePath(packageStream, tempPackagePath)))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto t = thread([&](shared_ptr<MsixResponse> response) {
|
||||
Windows10Redirector::AddPackageWithProgress(tempPackagePath, response);
|
||||
}, msixResponse);
|
||||
t.detach();
|
||||
|
||||
return msixResponse;
|
||||
}
|
||||
|
||||
MsixRequest * impl;
|
||||
HRESULT hr = (MsixRequest::Make(OperationType::Add, packageStream, L"", MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &impl));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
impl->GetMsixResponse()->SetCallback(callback);
|
||||
}
|
||||
|
||||
auto t = thread([&](MsixRequest* msixRequest) {
|
||||
msixRequest->ProcessRequest();
|
||||
delete msixRequest;
|
||||
msixRequest = nullptr;
|
||||
}, impl);
|
||||
t.detach();
|
||||
return impl->GetMsixResponse();
|
||||
}
|
||||
|
||||
HRESULT PackageManager::AddPackage(const wstring & packageFilePath, DeploymentOptions options)
|
||||
{
|
||||
if (IsWindows10RS3OrLater())
|
||||
{
|
||||
RETURN_IF_FAILED(Windows10Redirector::AddPackage(packageFilePath));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ComPtr<IStream> packageStream;
|
||||
auto res = CreateStreamOnFileUTF16(packageFilePath.c_str(), /*forRead */ true, &packageStream);
|
||||
if (FAILED(res))
|
||||
{
|
||||
return res;
|
||||
}
|
||||
|
||||
return AddPackage(packageStream.Get(), options);
|
||||
}
|
||||
|
||||
HRESULT PackageManager::AddPackage(IStream * packageStream, DeploymentOptions options)
|
||||
{
|
||||
if (IsWindows10RS3OrLater())
|
||||
{
|
||||
TCHAR tempPackagePath[MAX_PATH];
|
||||
RETURN_IF_FAILED(Windows10Redirector::ConvertIStreamToPackagePath(packageStream, tempPackagePath));
|
||||
RETURN_IF_FAILED(Windows10Redirector::AddPackage(tempPackagePath));
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
AutoPtr<MsixRequest> impl;
|
||||
RETURN_IF_FAILED(MsixRequest::Make(OperationType::Add, packageStream, L"", MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &impl));
|
||||
|
||||
RETURN_IF_FAILED(impl->ProcessRequest());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
shared_ptr<IMsixResponse> PackageManager::RemovePackageAsync(const wstring & packageFullName, function<void(const IMsixResponse&)> callback)
|
||||
{
|
||||
MsixRequest* impl;
|
||||
HRESULT hr = (MsixRequest::Make(OperationType::Remove, nullptr, packageFullName, MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &impl));
|
||||
if (FAILED(hr))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (callback != nullptr)
|
||||
{
|
||||
impl->GetMsixResponse()->SetCallback(callback);
|
||||
}
|
||||
|
||||
auto t = thread([&](MsixRequest* msixRequest) {
|
||||
msixRequest->ProcessRequest();
|
||||
delete msixRequest;
|
||||
msixRequest = nullptr;
|
||||
}, impl);
|
||||
t.detach();
|
||||
return impl->GetMsixResponse();
|
||||
}
|
||||
|
||||
HRESULT PackageManager::RemovePackage(const wstring & packageFullName)
|
||||
{
|
||||
if (IsWindows10RS3OrLater())
|
||||
{
|
||||
RETURN_IF_FAILED(Windows10Redirector::RemovePackage(packageFullName));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
AutoPtr<MsixRequest> impl;
|
||||
RETURN_IF_FAILED(MsixRequest::Make(OperationType::Remove, nullptr, packageFullName, MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &impl));
|
||||
|
||||
RETURN_IF_FAILED(impl->ProcessRequest());
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PackageManager::GetPackageInfo(const wstring & directoryPath, shared_ptr<IInstalledPackage> & installedPackage)
|
||||
{
|
||||
std::shared_ptr<InstalledPackage> packageInfo;
|
||||
RETURN_IF_FAILED(PopulatePackageInfo::GetPackageInfoFromManifest(directoryPath.c_str(), MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &packageInfo));
|
||||
|
||||
installedPackage = std::dynamic_pointer_cast<IInstalledPackage>(packageInfo);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PackageManager::FindPackage(const wstring & packageFullName, shared_ptr<IInstalledPackage>& installedPackage)
|
||||
{
|
||||
auto filemapping = FilePathMappings::GetInstance();
|
||||
RETURN_IF_FAILED(filemapping.GetInitializationResult());
|
||||
|
||||
wstring msixCoreDirectory = filemapping.GetMsixCoreDirectory();
|
||||
wstring packageDirectoryPath = msixCoreDirectory + packageFullName;
|
||||
RETURN_IF_FAILED(GetPackageInfo(packageDirectoryPath, installedPackage));
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PackageManager::FindPackageByFamilyName(const wstring & packageFamilyName, shared_ptr<IInstalledPackage>& installedPackage)
|
||||
{
|
||||
auto filemapping = FilePathMappings::GetInstance();
|
||||
RETURN_IF_FAILED(filemapping.GetInitializationResult());
|
||||
auto msixCoreDirectory = filemapping.GetMsixCoreDirectory();
|
||||
|
||||
for (auto& p : experimental::filesystem::directory_iterator(msixCoreDirectory))
|
||||
{
|
||||
if (experimental::filesystem::is_directory(p.path()))
|
||||
{
|
||||
auto installedAppFamilyName = GetFamilyNameFromFullName(p.path().filename());
|
||||
if (CaseInsensitiveEquals(installedAppFamilyName, packageFamilyName))
|
||||
{
|
||||
wstring packageDirectoryPath = msixCoreDirectory + std::wstring(p.path().filename());
|
||||
RETURN_IF_FAILED(GetPackageInfo(packageDirectoryPath, installedPackage));
|
||||
return S_OK;
|
||||
}
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PackageManager::FindPackages(unique_ptr<vector<shared_ptr<IInstalledPackage>>> & installedPackages)
|
||||
{
|
||||
auto packages = std::make_unique<std::vector<shared_ptr<IInstalledPackage>>>();
|
||||
|
||||
auto filemapping = FilePathMappings::GetInstance();
|
||||
RETURN_IF_FAILED(filemapping.GetInitializationResult());
|
||||
wstring msixCoreDirectory = filemapping.GetMsixCoreDirectory();
|
||||
|
||||
std::vector<std::wstring> packageFullNames;
|
||||
for (auto& p : experimental::filesystem::directory_iterator(msixCoreDirectory))
|
||||
{
|
||||
if (experimental::filesystem::is_directory(p.path()))
|
||||
{
|
||||
wstring packageDirectoryPath = msixCoreDirectory + std::wstring(p.path().filename());
|
||||
shared_ptr<IInstalledPackage> packageInfo;
|
||||
const HRESULT hrGetPackageInfo = GetPackageInfo(packageDirectoryPath, packageInfo);
|
||||
if (FAILED(hrGetPackageInfo))
|
||||
{
|
||||
TraceLoggingWrite(g_MsixTraceLoggingProvider,
|
||||
"Error getting package info from directory",
|
||||
TraceLoggingValue(packageDirectoryPath.c_str(), "Directory"),
|
||||
TraceLoggingValue(hrGetPackageInfo, "HR"));
|
||||
}
|
||||
else
|
||||
{
|
||||
packages->push_back(packageInfo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
installedPackages.swap(packages);
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT PackageManager::GetMsixPackageInfo(const wstring & msixFullPath, shared_ptr<IPackage> & package)
|
||||
{
|
||||
auto filemapping = FilePathMappings::GetInstance();
|
||||
RETURN_IF_FAILED(filemapping.GetInitializationResult());
|
||||
|
||||
shared_ptr<Package> packageInfo;
|
||||
ComPtr<IStream> packageStream;
|
||||
RETURN_IF_FAILED(CreateStreamOnFileUTF16(msixFullPath.c_str(), /*forRead */ true, &packageStream));
|
||||
RETURN_IF_FAILED(PopulatePackageInfo::GetPackageInfoFromPackage(packageStream.Get(), MSIX_VALIDATION_OPTION::MSIX_VALIDATION_OPTION_FULL, &packageInfo));
|
||||
|
||||
package = dynamic_pointer_cast<IPackage>(packageInfo);
|
||||
return S_OK;
|
||||
}
|
|
@ -0,0 +1,25 @@
|
|||
#pragma once
|
||||
#include "inc/IPackageManager.hpp"
|
||||
#include "inc/IPackage.hpp"
|
||||
|
||||
namespace MsixCoreLib {
|
||||
|
||||
class PackageManager :
|
||||
public IPackageManager
|
||||
{
|
||||
public:
|
||||
PackageManager();
|
||||
std::shared_ptr<IMsixResponse> AddPackageAsync(const std::wstring & packageFilePath, DeploymentOptions options, std::function<void(const IMsixResponse&)> callback = nullptr) override;
|
||||
std::shared_ptr<IMsixResponse> AddPackageAsync(IStream * packageStream, DeploymentOptions options, std::function<void(const IMsixResponse&)> callback = nullptr) override;
|
||||
HRESULT AddPackage(const std::wstring & packageFilePath, DeploymentOptions options) override;
|
||||
HRESULT AddPackage(IStream * packageStream, DeploymentOptions options) override;
|
||||
std::shared_ptr<IMsixResponse> RemovePackageAsync(const std::wstring & packageFullName, std::function<void(const IMsixResponse&)> callback = nullptr) override;
|
||||
HRESULT RemovePackage(const std::wstring & packageFullName) override;
|
||||
HRESULT FindPackage(const std::wstring & packageFullName, std::shared_ptr<IInstalledPackage> & installedPackage) override;
|
||||
HRESULT FindPackageByFamilyName(const std::wstring & packageFamilyName, std::shared_ptr<IInstalledPackage> & installedPackage) override;
|
||||
HRESULT FindPackages(std::unique_ptr<std::vector<std::shared_ptr<IInstalledPackage>>> & installedPackages) override;
|
||||
HRESULT GetMsixPackageInfo(const std::wstring & msixFullPath, std::shared_ptr<IPackage> & package) override;
|
||||
private:
|
||||
HRESULT GetPackageInfo(const std::wstring & directoryPath, std::shared_ptr<IInstalledPackage> & installedPackage);
|
||||
};
|
||||
}
|
Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше
Загрузка…
Ссылка в новой задаче