summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2018-08-16 22:06:56 +0200
committerOdd Stranne <odd@mullvad.net>2018-08-29 12:14:24 +0200
commite0fbb5dfda63ebb054a47aaf88a270ab1334a68a (patch)
treef89aae227ff12068e03842b849f5afff52eb0db0
parentac290fef91b3fb7fe35cffada330c56436d223ed (diff)
downloadmullvadvpn-e0fbb5dfda63ebb054a47aaf88a270ab1334a68a.tar.xz
mullvadvpn-e0fbb5dfda63ebb054a47aaf88a270ab1334a68a.zip
Add uninstaller plugin for extended system cleaning
-rw-r--r--windows/nsis-plugins/nsis-plugins.sln9
-rw-r--r--windows/nsis-plugins/src/cleanup/cleaningops.cpp269
-rw-r--r--windows/nsis-plugins/src/cleanup/cleaningops.h13
-rw-r--r--windows/nsis-plugins/src/cleanup/cleanup.cpp81
-rw-r--r--windows/nsis-plugins/src/cleanup/cleanup.def6
-rw-r--r--windows/nsis-plugins/src/cleanup/cleanup.vcxproj124
-rw-r--r--windows/nsis-plugins/src/cleanup/cleanup.vcxproj.filters17
-rw-r--r--windows/nsis-plugins/src/cleanup/dllmain.cpp11
-rw-r--r--windows/nsis-plugins/src/cleanup/stdafx.cpp8
-rw-r--r--windows/nsis-plugins/src/cleanup/stdafx.h16
-rw-r--r--windows/nsis-plugins/src/cleanup/targetver.h12
11 files changed, 566 insertions, 0 deletions
diff --git a/windows/nsis-plugins/nsis-plugins.sln b/windows/nsis-plugins/nsis-plugins.sln
index 3b3e7a7f62..0f2f5533f2 100644
--- a/windows/nsis-plugins/nsis-plugins.sln
+++ b/windows/nsis-plugins/nsis-plugins.sln
@@ -7,6 +7,11 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "driverlogic", "src\driverlo
{B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
EndProjectSection
EndProject
+Project("{54BCC44A-7EAA-4BB3-8CF5-564137999875}") = "cleanup", "src\cleanup\cleanup.vcxproj", "{47B5C1C1-67D7-4544-9037-8E7F44C1E5BD}"
+ ProjectSection(ProjectDependencies) = postProject
+ {B52E2D10-A94A-4605-914A-2DCEF6A757EF} = {B52E2D10-A94A-4605-914A-2DCEF6A757EF}
+ EndProjectSection
+EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "libcommon", "..\windows-libraries\src\libcommon\libcommon.vcxproj", "{B52E2D10-A94A-4605-914A-2DCEF6A757EF}"
EndProject
Global
@@ -19,6 +24,10 @@ Global
{AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}.Debug|x86.Build.0 = Debug|Win32
{AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}.Release|x86.ActiveCfg = Release|Win32
{AABA9AB7-A7D0-4BB5-A1FA-92F566023E0D}.Release|x86.Build.0 = Release|Win32
+ {47B5C1C1-67D7-4544-9037-8E7F44C1E5BD}.Debug|x86.ActiveCfg = Debug|Win32
+ {47B5C1C1-67D7-4544-9037-8E7F44C1E5BD}.Debug|x86.Build.0 = Debug|Win32
+ {47B5C1C1-67D7-4544-9037-8E7F44C1E5BD}.Release|x86.ActiveCfg = Release|Win32
+ {47B5C1C1-67D7-4544-9037-8E7F44C1E5BD}.Release|x86.Build.0 = Release|Win32
{B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.ActiveCfg = Debug|Win32
{B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Debug|x86.Build.0 = Debug|Win32
{B52E2D10-A94A-4605-914A-2DCEF6A757EF}.Release|x86.ActiveCfg = Release|Win32
diff --git a/windows/nsis-plugins/src/cleanup/cleaningops.cpp b/windows/nsis-plugins/src/cleanup/cleaningops.cpp
new file mode 100644
index 0000000000..dd14b9035c
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/cleaningops.cpp
@@ -0,0 +1,269 @@
+#include "stdafx.h"
+#include "cleaningops.h"
+#include <libcommon/filesystem.h>
+#include <libcommon/fileenumerator.h>
+#include <libcommon/string.h>
+#include <libcommon/memory.h>
+#include <libcommon/security.h>
+#include <libcommon/process.h>
+#include <experimental/filesystem>
+#include <utility>
+#include <functional>
+#include <processthreadsapi.h>
+
+namespace
+{
+
+//
+// Returns range in lhs that is also present in rhs.
+// Equalence/equivalence determined by 'comp'.
+//
+// Returns pair<lhsBegin, lhsBegin> if there is no mirrored range.
+//
+template<typename ForwardIterator>
+std::pair<ForwardIterator, ForwardIterator>
+mirrored_range
+(
+ ForwardIterator lhsBegin, ForwardIterator lhsEnd,
+ ForwardIterator rhsBegin, ForwardIterator rhsEnd,
+ std::function<bool(const typename ForwardIterator::value_type &, const typename ForwardIterator::value_type &)> comp
+)
+{
+ ForwardIterator begin = lhsBegin;
+
+ while (lhsBegin != lhsEnd
+ && rhsBegin != rhsEnd)
+ {
+ if (false == comp(*lhsBegin, *rhsBegin))
+ {
+ break;
+ }
+
+ ++lhsBegin;
+ ++rhsBegin;
+ }
+
+ return std::make_pair(begin, lhsBegin);
+}
+
+std::wstring ConstructLocalAppDataPath(const std::wstring &base, const std::wstring &user,
+ const std::pair<std::vector<std::wstring>::iterator, std::vector<std::wstring>::iterator> &tokens)
+{
+ auto result = common::fs::MakePath(base, user);
+
+ std::for_each(tokens.first, tokens.second, [&](const std::wstring &token)
+ {
+ result = common::fs::MakePath(result, token);
+ });
+
+ return result;
+}
+
+std::wstring GetSystemUserLocalAppData()
+{
+ common::security::AdjustCurrentProcessTokenPrivilege(L"SeDebugPrivilege");
+
+ common::memory::ScopeDestructor sd;
+
+ sd += []
+ {
+ common::security::AdjustCurrentProcessTokenPrivilege(L"SeDebugPrivilege", false);
+ };
+
+ auto systemDir = common::fs::GetKnownFolderPath(FOLDERID_System, KF_FLAG_DEFAULT, NULL);
+ auto lsassPid = common::process::GetProcessIdFromName(common::fs::MakePath(systemDir, L"lsass.exe"));
+
+ auto processHandle = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, lsassPid);
+
+ if (nullptr == processHandle)
+ {
+ throw std::runtime_error("Failed to access the \"LSASS\" process");
+ }
+
+ HANDLE processToken;
+
+ auto status = OpenProcessToken(processHandle, TOKEN_READ | TOKEN_IMPERSONATE | TOKEN_DUPLICATE, &processToken);
+
+ CloseHandle(processHandle);
+
+ if (FALSE == status)
+ {
+ throw std::runtime_error("Failed to acquire process token for the \"LSASS\" process");
+ }
+
+ sd += [&]()
+ {
+ CloseHandle(processToken);
+ };
+
+ return common::fs::GetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, processToken);
+}
+
+} // anonymous namespace
+
+namespace cleaningops
+{
+
+void RemoveLogsCacheCurrentUser()
+{
+ auto localAppData = common::fs::GetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr);
+
+ auto appdir = common::fs::MakePath(localAppData, L"Mullvad VPN");
+
+ std::experimental::filesystem::remove_all(appdir);
+}
+
+void RemoveLogsCacheOtherUsers()
+{
+ //
+ // Determine relative path to "local app data" from home directory.
+ //
+ // Beware, the local app data path may be overriden from its default location
+ // as a node somewhere beneath the home directory.
+ //
+
+ auto localAppData = common::fs::GetKnownFolderPath(FOLDERID_LocalAppData, KF_FLAG_DEFAULT, nullptr);
+ auto homeDir = common::fs::GetKnownFolderPath(FOLDERID_Profile, KF_FLAG_DEFAULT, nullptr);
+
+ //
+ // Tokenize to get rid of slashes pointing in different directions.
+ //
+ auto localAppDataTokens = common::string::Tokenize(localAppData, L"\\/");
+ auto homeDirTokens = common::string::Tokenize(homeDir, L"\\/");
+
+ auto mirror = mirrored_range
+ (
+ localAppDataTokens.begin(), localAppDataTokens.end(),
+ homeDirTokens.begin(), homeDirTokens.end(),
+ [](const std::wstring &lhs, const std::wstring &rhs)
+ {
+ return 0 == _wcsicmp(lhs.c_str(), rhs.c_str());
+ }
+ );
+
+ auto equalTokensCount = (size_t)std::distance(mirror.first, mirror.second);
+
+ //
+ // Abort if "local app data" is not beneath home dir.
+ //
+ if (equalTokensCount < homeDirTokens.size())
+ {
+ return;
+ }
+
+ auto relativeLocalAppData = std::make_pair(std::next(localAppDataTokens.begin(), equalTokensCount), localAppDataTokens.end());
+ auto currentUser = *homeDirTokens.rbegin();
+
+ //
+ // Find all other users and construct the most plausible path for their
+ // respective "local app data" dirs.
+ //
+
+ auto parentHomeDir = common::fs::GetKnownFolderPath(FOLDERID_UserProfiles, KF_FLAG_DEFAULT, nullptr);
+
+ common::fs::FileEnumerator files(parentHomeDir);
+
+ files.addFilter(std::make_unique<common::fs::FilterDirectories>());
+ files.addFilter(std::make_unique<common::fs::FilterNotRelativeDirs>());
+
+ auto notNamedSet = std::make_unique<common::fs::FilterNotNamedSet>();
+
+ notNamedSet->addObject(std::wstring(currentUser));
+ notNamedSet->addObject(L"All Users"); // Redirects to 'c:\programdata'.
+ notNamedSet->addObject(L"Public"); // Shared documents, not an actual user or user template.
+
+ files.addFilter(std::move(notNamedSet));
+
+ WIN32_FIND_DATAW file;
+
+ while (files.next(file))
+ {
+ auto userLocalAppData = ConstructLocalAppDataPath(files.getDirectory(), file.cFileName, relativeLocalAppData);
+
+ std::error_code dummy;
+ std::experimental::filesystem::remove_all(common::fs::MakePath(userLocalAppData, L"Mullvad VPN"), dummy);
+ }
+}
+
+void RemoveLogsServiceUser()
+{
+ auto programData = common::fs::GetKnownFolderPath(FOLDERID_ProgramData, KF_FLAG_DEFAULT, nullptr);
+
+ auto appdir = common::fs::MakePath(programData, L"Mullvad VPN");
+
+ std::experimental::filesystem::remove_all(appdir);
+}
+
+void RemoveCacheServiceUser()
+{
+ auto localAppData = GetSystemUserLocalAppData();
+ auto mullvadAppData = common::fs::MakePath(localAppData, L"Mullvad VPN");
+
+ common::fs::ScopedNativeFileSystem nativeFileSystem;
+
+ common::security::AddAdminToObjectDacl(mullvadAppData, SE_FILE_OBJECT);
+
+ {
+ common::fs::FileEnumerator files(mullvadAppData);
+
+ auto notNamedSet = std::make_unique<common::fs::FilterNotNamedSet>();
+
+ notNamedSet->addObject(L"account-history.json");
+ notNamedSet->addObject(L"settings.json");
+
+ files.addFilter(std::move(notNamedSet));
+ files.addFilter(std::make_unique<common::fs::FilterFiles>());
+
+ WIN32_FIND_DATAW file;
+
+ while (files.next(file))
+ {
+ std::error_code dummy;
+ std::experimental::filesystem::remove(common::fs::MakePath(files.getDirectory(), file.cFileName), dummy);
+ }
+ }
+
+ //
+ // This fails unless the directory is empty.
+ // Which is what we want, since removing cache and settings files are separate operations.
+ //
+ RemoveDirectoryW(std::wstring(L"\\\\?\\").append(mullvadAppData).c_str());
+}
+
+void RemoveSettingsServiceUser()
+{
+ auto localAppData = GetSystemUserLocalAppData();
+ auto mullvadAppData = common::fs::MakePath(localAppData, L"Mullvad VPN");
+
+ common::fs::ScopedNativeFileSystem nativeFileSystem;
+
+ common::security::AddAdminToObjectDacl(mullvadAppData, SE_FILE_OBJECT);
+
+ {
+ common::fs::FileEnumerator files(mullvadAppData);
+
+ auto filter = std::make_unique<common::fs::FilterNamedSet>();
+
+ filter->addObject(L"account-history.json");
+ filter->addObject(L"settings.json");
+
+ files.addFilter(std::move(filter));
+ files.addFilter(std::make_unique<common::fs::FilterFiles>());
+
+ WIN32_FIND_DATAW file;
+
+ while (files.next(file))
+ {
+ std::error_code dummy;
+ std::experimental::filesystem::remove(common::fs::MakePath(files.getDirectory(), file.cFileName), dummy);
+ }
+ }
+
+ //
+ // This fails unless the directory is empty.
+ // Which is what we want, since removing cache and settings files are separate operations.
+ //
+ RemoveDirectoryW(std::wstring(L"\\\\?\\").append(mullvadAppData).c_str());
+}
+
+}
diff --git a/windows/nsis-plugins/src/cleanup/cleaningops.h b/windows/nsis-plugins/src/cleanup/cleaningops.h
new file mode 100644
index 0000000000..219d4333de
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/cleaningops.h
@@ -0,0 +1,13 @@
+#pragma once
+
+namespace cleaningops
+{
+
+void RemoveLogsCacheCurrentUser();
+void RemoveLogsCacheOtherUsers();
+void RemoveLogsServiceUser();
+void RemoveCacheServiceUser();
+
+void RemoveSettingsServiceUser();
+
+}
diff --git a/windows/nsis-plugins/src/cleanup/cleanup.cpp b/windows/nsis-plugins/src/cleanup/cleanup.cpp
new file mode 100644
index 0000000000..c7890d60b7
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/cleanup.cpp
@@ -0,0 +1,81 @@
+#include <stdafx.h>
+#include "cleaningops.h"
+#include <windows.h>
+#include <nsis/pluginapi.h>
+#include <functional>
+#include <vector>
+
+enum class RemoveLogsAndCacheStatus
+{
+ GENERAL_ERROR = 0,
+ SUCCESS
+};
+
+void __declspec(dllexport) NSISCALL RemoveLogsAndCache
+(
+ HWND hwndParent,
+ int string_size,
+ LPTSTR variables,
+ stack_t **stacktop,
+ extra_parameters *extra,
+ ...
+)
+{
+ EXDLL_INIT();
+
+ std::vector<std::function<void()> > functions =
+ {
+ cleaningops::RemoveLogsCacheCurrentUser,
+ cleaningops::RemoveLogsCacheOtherUsers,
+ cleaningops::RemoveLogsServiceUser,
+ cleaningops::RemoveCacheServiceUser,
+ };
+
+ bool success = true;
+
+ //
+ // Invoke all functions and take note of any failure.
+ //
+ for (const auto &function : functions)
+ {
+ try
+ {
+ function();
+ }
+ catch (...)
+ {
+ success = false;
+ }
+ }
+
+ pushint(success ? RemoveLogsAndCacheStatus::SUCCESS : RemoveLogsAndCacheStatus::GENERAL_ERROR);
+}
+
+enum class RemoveSettingsStatus
+{
+ GENERAL_ERROR = 0,
+ SUCCESS
+};
+
+void __declspec(dllexport) NSISCALL RemoveSettings
+(
+ HWND hwndParent,
+ int string_size,
+ LPTSTR variables,
+ stack_t **stacktop,
+ extra_parameters *extra,
+ ...
+)
+{
+ EXDLL_INIT();
+
+ try
+ {
+ cleaningops::RemoveSettingsServiceUser();
+ pushint(RemoveSettingsStatus::SUCCESS);
+ }
+ catch (...)
+ {
+ pushint(RemoveSettingsStatus::GENERAL_ERROR);
+ }
+}
diff --git a/windows/nsis-plugins/src/cleanup/cleanup.def b/windows/nsis-plugins/src/cleanup/cleanup.def
new file mode 100644
index 0000000000..590d311b7a
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/cleanup.def
@@ -0,0 +1,6 @@
+LIBRARY cleanup
+
+EXPORTS
+
+RemoveLogsAndCache
+RemoveSettings
diff --git a/windows/nsis-plugins/src/cleanup/cleanup.vcxproj b/windows/nsis-plugins/src/cleanup/cleanup.vcxproj
new file mode 100644
index 0000000000..8ceab43448
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/cleanup.vcxproj
@@ -0,0 +1,124 @@
+<?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>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <VCProjectVersion>15.0</VCProjectVersion>
+ <ProjectGuid>{47B5C1C1-67D7-4544-9037-8E7F44C1E5BD}</ProjectGuid>
+ <Keyword>Win32Proj</Keyword>
+ <RootNamespace>cleanup</RootNamespace>
+ <WindowsTargetPlatformVersion>10.0.16299.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>
+ <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>
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <LinkIncremental>true</LinkIncremental>
+ <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <LinkIncremental>false</LinkIncremental>
+ <OutDir>$(SolutionDir)bin\$(Platform)-$(Configuration)\</OutDir>
+ <IntDir>$(SolutionDir)bin\temp\$(Platform)-$(Configuration)\$(ProjectName)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <PrecompiledHeader>Use</PrecompiledHeader>
+ <WarningLevel>Level3</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ <SDLCheck>true</SDLCheck>
+ <PreprocessorDefinitions>WIN32;_DEBUG;CLEANUP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/nsis/;$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;pluginapi-x86-unicode.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>
+ <IgnoreSpecificDefaultLibraries>libc.lib</IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>cleanup.def</ModuleDefinitionFile>
+ </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;CLEANUP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+ <ConformanceMode>true</ConformanceMode>
+ <AdditionalIncludeDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/;$(ProjectDir)../../../windows-libraries/src/</AdditionalIncludeDirectories>
+ <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+ <LanguageStandard>stdcpplatest</LanguageStandard>
+ </ClCompile>
+ <Link>
+ <SubSystem>Windows</SubSystem>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
+ <AdditionalLibraryDirectories>$(ProjectDir)../../../../dist-assets/binaries/windows/nsis/;$(SolutionDir)bin\$(Platform)-$(Configuration)\</AdditionalLibraryDirectories>
+ <AdditionalDependencies>libcommon.lib;pluginapi-x86-unicode.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>
+ <IgnoreSpecificDefaultLibraries>libc.lib</IgnoreSpecificDefaultLibraries>
+ <ModuleDefinitionFile>cleanup.def</ModuleDefinitionFile>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClInclude Include="cleaningops.h" />
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="cleaningops.cpp" />
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="cleanup.cpp" />
+ <ClCompile Include="stdafx.cpp">
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
+ <PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="cleanup.def" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project> \ No newline at end of file
diff --git a/windows/nsis-plugins/src/cleanup/cleanup.vcxproj.filters b/windows/nsis-plugins/src/cleanup/cleanup.vcxproj.filters
new file mode 100644
index 0000000000..43d93655d9
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/cleanup.vcxproj.filters
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ItemGroup>
+ <ClInclude Include="stdafx.h" />
+ <ClInclude Include="targetver.h" />
+ <ClInclude Include="cleaningops.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClCompile Include="dllmain.cpp" />
+ <ClCompile Include="cleanup.cpp" />
+ <ClCompile Include="stdafx.cpp" />
+ <ClCompile Include="cleaningops.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="cleanup.def" />
+ </ItemGroup>
+</Project> \ No newline at end of file
diff --git a/windows/nsis-plugins/src/cleanup/dllmain.cpp b/windows/nsis-plugins/src/cleanup/dllmain.cpp
new file mode 100644
index 0000000000..f11c65c519
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/dllmain.cpp
@@ -0,0 +1,11 @@
+#include "stdafx.h"
+#include <windows.h>
+
+BOOL APIENTRY DllMain(HMODULE, DWORD, LPVOID)
+{
+ //
+ // Avoid doing work in DllMain since the loader lock is held
+ //
+
+ return TRUE;
+}
diff --git a/windows/nsis-plugins/src/cleanup/stdafx.cpp b/windows/nsis-plugins/src/cleanup/stdafx.cpp
new file mode 100644
index 0000000000..3b6341d106
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/stdafx.cpp
@@ -0,0 +1,8 @@
+// stdafx.cpp : source file that includes just the standard includes
+// driverlogic.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
+
+// TODO: reference any additional headers you need in STDAFX.H
+// and not in this file
diff --git a/windows/nsis-plugins/src/cleanup/stdafx.h b/windows/nsis-plugins/src/cleanup/stdafx.h
new file mode 100644
index 0000000000..f3a07375c7
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/stdafx.h
@@ -0,0 +1,16 @@
+// 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>
+
+
+
+// TODO: reference additional headers your program requires here
diff --git a/windows/nsis-plugins/src/cleanup/targetver.h b/windows/nsis-plugins/src/cleanup/targetver.h
new file mode 100644
index 0000000000..ae4a5c032c
--- /dev/null
+++ b/windows/nsis-plugins/src/cleanup/targetver.h
@@ -0,0 +1,12 @@
+#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 <WinSDKVer.h>
+
+#define _WIN32_WINNT _WIN32_WINNT_WIN7
+
+#include <SDKDDKVer.h>