Skip to content
Browse files

Initial commit

  • Loading branch information...
0 parents commit 36d8ee2ea5c887549b410a74f18c3c610ca46e1c @gix committed Nov 12, 2011
Showing with 1,180 additions and 0 deletions.
  1. +9 −0 .gitignore
  2. +21 −0 LICENSE
  3. +39 −0 ReadMe.txt
  4. +12 −0 src/KillHandle.props
  5. +97 −0 src/KillHandle.rc
  6. +26 −0 src/KillHandle.sln
  7. +140 −0 src/KillHandle.vcxproj
  8. +32 −0 src/KillHandle.vcxproj.filters
  9. +789 −0 src/Main.cpp
  10. +15 −0 src/Resource.h
9 .gitignore
@@ -0,0 +1,9 @@
+bin/
+obj/
+ipch/
+GeneratedFiles/
+*.user
+*.sdf
+*.opensdf
+*.suo
+*.aps
21 LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) Nico Rieck
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
39 ReadMe.txt
@@ -0,0 +1,39 @@
+KillHandle
+----------
+
+Synopsis
+
+ KillHandle <file-path> [<process-name>]
+
+Description
+
+ Visual Studio 2010 sometimes keeps file handles to compilation targets
+ (especially static libraries) open which prevents further build attempts
+ of the affected project.
+
+ KillHandle, when used as a pre-build step, works around that bug by
+ killing the (presumably leaked) file handles from the first devenv.exe
+ process it finds.
+
+ Related Connect-Entry:
+ <http://connect.microsoft.com/VisualStudio/feedback/details/551819/vs2010-locks-static-library-after-debug-session>
+
+Options
+
+ <file-path>
+ The file path of handles to kill (e.g. "C:\FooLib\Debug\FooLib.lib").
+ KillHandle only runs when this specifies the path to an actual file
+ system object (that can be opened by CreateFile).
+
+ <process-name>
+ The name of the process to inspect. Defaults to "devenv.exe".
+
+Usage
+
+ Copy KillHandle.exe and KillHandle.props to the location of your choice
+ and add the property sheet to the affected projects or manually add
+ it as Pre-Build Event.
+
+Bugs
+
+ Please report bugs to <https://github.com/gix/KillHandle/issues>.
12 src/KillHandle.props
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <ImportGroup Label="PropertySheets" />
+ <PropertyGroup Label="UserMacros" />
+ <PropertyGroup />
+ <ItemDefinitionGroup>
+ <PreBuildEvent>
+ <Command>$(MSBuildThisFileDirectory)$(MSBuildThisFileName).exe "$(TargetPath)"</Command>
+ </PreBuildEvent>
+ </ItemDefinitionGroup>
+ <ItemGroup />
+</Project>
97 src/KillHandle.rc
@@ -0,0 +1,97 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "Resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.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)
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,0
+ PRODUCTVERSION 1,0,0,0
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x40004L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0"
+ BEGIN
+ VALUE "FileDescription", "KillHandle"
+ VALUE "FileVersion", "1.0.0.0"
+ VALUE "InternalName", "KillHandle.exe"
+ VALUE "LegalCopyright", "Copyright (c) 2011 Nico Rieck"
+ VALUE "OriginalFilename", "KillHandle.exe"
+ VALUE "ProductName", "KillHandle"
+ VALUE "ProductVersion", "1.0.0.0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "Resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (United States) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
26 src/KillHandle.sln
@@ -0,0 +1,26 @@
+
+Microsoft Visual Studio Solution File, Format Version 11.00
+# Visual Studio 2010
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "KillHandle", "KillHandle.vcxproj", "{CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Debug|x64 = Debug|x64
+ Release|Win32 = Release|Win32
+ Release|x64 = Release|x64
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Debug|Win32.Build.0 = Debug|Win32
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Debug|x64.ActiveCfg = Debug|x64
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Debug|x64.Build.0 = Debug|x64
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Release|Win32.ActiveCfg = Release|Win32
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Release|Win32.Build.0 = Release|Win32
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Release|x64.ActiveCfg = Release|x64
+ {CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}.Release|x64.Build.0 = Release|x64
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
140 src/KillHandle.vcxproj
@@ -0,0 +1,140 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="4.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="Debug|x64">
+ <Configuration>Debug</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|Win32">
+ <Configuration>Release</Configuration>
+ <Platform>Win32</Platform>
+ </ProjectConfiguration>
+ <ProjectConfiguration Include="Release|x64">
+ <Configuration>Release</Configuration>
+ <Platform>x64</Platform>
+ </ProjectConfiguration>
+ </ItemGroup>
+ <PropertyGroup Label="Globals">
+ <ProjectGuid>{CDA43A81-51DE-407E-8E3A-F25FF5BB11AF}</ProjectGuid>
+ <RootNamespace>KillHandle</RootNamespace>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>true</UseDebugLibraries>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+ <ConfigurationType>Application</ConfigurationType>
+ <UseDebugLibraries>false</UseDebugLibraries>
+ <WholeProgramOptimization>true</WholeProgramOptimization>
+ <CharacterSet>Unicode</CharacterSet>
+ </PropertyGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+ <ImportGroup Label="ExtensionSettings">
+ </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 Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+ <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 Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+ <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'">
+ <OutDir>$(SolutionDir)bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <OutDir>$(SolutionDir)bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <OutDir>$(SolutionDir)bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <OutDir>$(SolutionDir)bin\$(Platform)\$(Configuration)\</OutDir>
+ <IntDir>obj\$(Platform)\$(Configuration)\</IntDir>
+ </PropertyGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>Shlwapi.lib;psapi.lib;Mpr.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>Disabled</Optimization>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <AdditionalDependencies>Shlwapi.lib;psapi.lib;Mpr.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>Shlwapi.lib;psapi.lib;Mpr.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+ <ClCompile>
+ <WarningLevel>Level4</WarningLevel>
+ <Optimization>MaxSpeed</Optimization>
+ <FunctionLevelLinking>true</FunctionLevelLinking>
+ <IntrinsicFunctions>true</IntrinsicFunctions>
+ </ClCompile>
+ <Link>
+ <GenerateDebugInformation>true</GenerateDebugInformation>
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>
+ <OptimizeReferences>true</OptimizeReferences>
+ <AdditionalDependencies>Shlwapi.lib;psapi.lib;Mpr.lib;%(AdditionalDependencies)</AdditionalDependencies>
+ </Link>
+ </ItemDefinitionGroup>
+ <ItemGroup>
+ <ClCompile Include="Main.cpp" />
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Resource.h" />
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="KillHandle.rc" />
+ </ItemGroup>
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+ <ImportGroup Label="ExtensionTargets">
+ </ImportGroup>
+</Project>
32 src/KillHandle.vcxproj.filters
@@ -0,0 +1,32 @@
+<?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;hpp;hxx;hm;inl;inc;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>
+ <ClCompile Include="Main.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ResourceCompile Include="KillHandle.rc">
+ <Filter>Resource Files</Filter>
+ </ResourceCompile>
+ </ItemGroup>
+ <ItemGroup>
+ <ClInclude Include="Resource.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
+ </ItemGroup>
+</Project>
789 src/Main.cpp
@@ -0,0 +1,789 @@
+#ifndef UNICODE
+#define UNICODE
+#endif
+
+#include <windows.h>
+#include <Psapi.h>
+#include <vector>
+#include <Shlwapi.h>
+#include <strsafe.h>
+
+#include <memory>
+#include <array>
+#include <exception>
+#include <stdexcept>
+#include <tuple>
+#include <type_traits>
+#include <cstdio>
+#include <cstdlib>
+#include <cstdint>
+
+#pragma warning(push)
+#pragma warning(disable: 4995) // <string> is not <Strsafe.h>-safe
+#include <string>
+#pragma warning(pop)
+
+#pragma warning(3: 4365)
+
+#define NT_SUCCESS(x) ((x) >= 0)
+#define STATUS_INFO_LENGTH_MISMATCH 0xc0000004
+
+#define SystemHandleInformation 16
+#define ObjectBasicInformation 0
+#define ObjectNameInformation 1
+#define ObjectTypeInformation 2
+
+typedef NTSTATUS (NTAPI *_NtQuerySystemInformation)(
+ ULONG SystemInformationClass,
+ PVOID SystemInformation,
+ ULONG SystemInformationLength,
+ PULONG ReturnLength
+ );
+typedef NTSTATUS (NTAPI *_NtDuplicateObject)(
+ HANDLE SourceProcessHandle,
+ HANDLE SourceHandle,
+ HANDLE TargetProcessHandle,
+ PHANDLE TargetHandle,
+ ACCESS_MASK DesiredAccess,
+ ULONG Attributes,
+ ULONG Options
+ );
+typedef NTSTATUS (NTAPI *_NtQueryObject)(
+ HANDLE ObjectHandle,
+ ULONG ObjectInformationClass,
+ PVOID ObjectInformation,
+ ULONG ObjectInformationLength,
+ PULONG ReturnLength
+ );
+
+typedef struct _UNICODE_STRING
+{
+ USHORT Length;
+ USHORT MaximumLength;
+ PWSTR Buffer;
+} UNICODE_STRING, *PUNICODE_STRING;
+
+typedef struct _SYSTEM_HANDLE_TABLE_ENTRY_INFO
+{
+ ULONG ProcessId;
+ BYTE ObjectTypeNumber;
+ BYTE Flags;
+ USHORT Handle;
+ PVOID Object;
+ ACCESS_MASK GrantedAccess;
+} SYSTEM_HANDLE_TABLE_ENTRY_INFO, *PSYSTEM_HANDLE_TABLE_ENTRY_INFO;
+
+typedef struct _SYSTEM_HANDLE_INFORMATION
+{
+ ULONG HandleCount;
+ SYSTEM_HANDLE_TABLE_ENTRY_INFO Handles[1];
+} SYSTEM_HANDLE_INFORMATION, *PSYSTEM_HANDLE_INFORMATION;
+
+typedef enum _POOL_TYPE
+{
+ NonPagedPool,
+ PagedPool,
+ NonPagedPoolMustSucceed,
+ DontUseThisType,
+ NonPagedPoolCacheAligned,
+ PagedPoolCacheAligned,
+ NonPagedPoolCacheAlignedMustS
+} POOL_TYPE, *PPOOL_TYPE;
+
+typedef struct _OBJECT_TYPE_INFORMATION
+{
+ UNICODE_STRING Name;
+ ULONG TotalNumberOfObjects;
+ ULONG TotalNumberOfHandles;
+ ULONG TotalPagedPoolUsage;
+ ULONG TotalNonPagedPoolUsage;
+ ULONG TotalNamePoolUsage;
+ ULONG TotalHandleTableUsage;
+ ULONG HighWaterNumberOfObjects;
+ ULONG HighWaterNumberOfHandles;
+ ULONG HighWaterPagedPoolUsage;
+ ULONG HighWaterNonPagedPoolUsage;
+ ULONG HighWaterNamePoolUsage;
+ ULONG HighWaterHandleTableUsage;
+ ULONG InvalidAttributes;
+ GENERIC_MAPPING GenericMapping;
+ ULONG ValidAccess;
+ BOOLEAN SecurityRequired;
+ BOOLEAN MaintainHandleCount;
+ USHORT MaintainTypeList;
+ POOL_TYPE PoolType;
+ ULONG PagedPoolUsage;
+ ULONG NonPagedPoolUsage;
+} OBJECT_TYPE_INFORMATION, *POBJECT_TYPE_INFORMATION;
+
+_NtQuerySystemInformation NtQuerySystemInformation = 0;
+_NtDuplicateObject NtDuplicateObject = 0;
+_NtQueryObject NtQueryObject = 0;
+
+template<typename To, typename From>
+To checked_cast(From const& from);
+
+template<>
+int checked_cast<int, size_t>(size_t const& from)
+{
+ if (from > INT_MAX) throw std::invalid_argument("cast would result in over-/underflow");
+ return static_cast<int>(from);
+}
+
+template<>
+unsigned long checked_cast<unsigned long, size_t>(size_t const& from)
+{
+ if (from > ULONG_MAX) throw std::invalid_argument("cast would result in over-/underflow");
+ return static_cast<unsigned long>(from);
+}
+
+template<>
+unsigned int checked_cast<unsigned int, int>(int const& from)
+{
+ if (from < 0) throw std::invalid_argument("cast would result in over-/underflow");
+ return static_cast<unsigned int>(from);
+}
+
+template<typename T>
+T* Malloc(size_t count)
+{
+ return reinterpret_cast<T*>(malloc(count * sizeof(T)));
+}
+
+template<typename T>
+T* RawMalloc(size_t size)
+{
+ return reinterpret_cast<T*>(malloc(size));
+}
+
+template<typename T>
+T* ReAlloc(T* ptr, size_t count)
+{
+ return reinterpret_cast<T*>(realloc(ptr, count * sizeof(T)));
+}
+
+template<typename T>
+T* RawReAlloc(T* ptr, size_t size)
+{
+ return reinterpret_cast<T*>(realloc(ptr, size));
+}
+
+template<>
+void* RawReAlloc(void* ptr, size_t size)
+{
+ return realloc(ptr, size);
+}
+
+class handle_ptr
+ : public std::unique_ptr<void, BOOL (WINAPI *)(HANDLE)>
+{
+public:
+ handle_ptr(HANDLE handle = NULL)
+ : std::unique_ptr<void, BOOL (WINAPI *)(HANDLE)>(handle, CloseHandle)
+ { }
+
+ operator HANDLE() { return get(); }
+ bool operator ==(HANDLE h) { return h == get(); }
+};
+
+class local_free_ptr
+ : public std::unique_ptr<void, HLOCAL (__stdcall *)(HLOCAL)>
+{
+public:
+ local_free_ptr(HLOCAL handle = NULL)
+ : std::unique_ptr<void, HLOCAL (__stdcall *)(HLOCAL)>(handle, LocalFree)
+ { }
+};
+
+template<typename T = void>
+class malloc_ptr
+ : public std::unique_ptr<T, void (*)(void*)>
+{
+ typedef std::unique_ptr<T, void (*)(void*)> base;
+public:
+ malloc_ptr(T* ptr = nullptr)
+ : base(ptr, std::free)
+ {
+ EnsureAlloc();
+ }
+
+ malloc_ptr(size_t countOrSize, bool useSize = false)
+ : base(useSize ? RawMalloc<T>(countOrSize) : Malloc<T>(countOrSize), std::free)
+ {
+ EnsureAlloc();
+ }
+
+ void realloc(size_t countOrSize, bool useSize = false)
+ {
+ reset(useSize ? RawReAlloc(release(), countOrSize) : ReAlloc(release(), countOrSize));
+ EnsureAlloc();
+ }
+
+private:
+ void EnsureAlloc()
+ {
+ if (get() == nullptr)
+ throw std::bad_alloc();
+ }
+};
+
+template<>
+malloc_ptr<void>::malloc_ptr(size_t size, bool /*useSize*/)
+ : malloc_ptr<void>::base(RawMalloc<void>(size), std::free)
+{
+ EnsureAlloc();
+}
+
+template<>
+void malloc_ptr<void>::realloc(size_t size, bool /*useSize*/)
+{
+ reset(RawReAlloc(release(), size));
+ EnsureAlloc();
+}
+
+class NamedHandle
+{
+public:
+ NamedHandle(std::wstring const& name, HANDLE handle)
+ : Name(name), Handle(handle) { }
+ std::wstring Name;
+ HANDLE Handle;
+};
+
+typedef std::vector<NamedHandle> HandleList;
+typedef std::vector<DWORD> ProcessIdList;
+
+template<typename T>
+T GetLibraryProcAddress(wchar_t const* libraryName, char const* procName)
+{
+ return reinterpret_cast<T>(GetProcAddress(GetModuleHandleW(libraryName), procName));
+}
+
+bool EndsWith(std::wstring const& string, std::wstring const& ending)
+{
+ if (string.length() < ending.length())
+ return false;
+
+ return StrCmpNIW(
+ &string[string.length() - ending.length()],
+ ending.c_str(),
+ checked_cast<int>(ending.length())
+ ) == 0;
+}
+
+bool EqualsI(std::wstring const& left, std::wstring const& right)
+{
+ if (left.length() != right.length())
+ return false;
+
+ return StrCmpNIW(left.c_str(), right.c_str(), checked_cast<int>(left.length())) == 0;
+}
+
+bool LoadProcs()
+{
+ NtQuerySystemInformation = GetLibraryProcAddress<_NtQuerySystemInformation>(L"ntdll.dll", "NtQuerySystemInformation");
+ NtDuplicateObject = GetLibraryProcAddress<_NtDuplicateObject>(L"ntdll.dll", "NtDuplicateObject");
+ NtQueryObject = GetLibraryProcAddress<_NtQueryObject>(L"ntdll.dll", "NtQueryObject");
+
+ return
+ NtQuerySystemInformation != nullptr &&
+ NtDuplicateObject != nullptr &&
+ NtQueryObject != nullptr;
+}
+
+namespace StringCvt
+{
+ size_t wcslen_max(wchar_t const* ptr, size_t max)
+ {
+ size_t len = 0;
+ while (len < max && ptr[len] != 0)
+ ++len;
+ return len;
+ }
+
+ size_t ConvertWideToCodepage(
+ unsigned codepage, char* out, size_t outSize, wchar_t const* source, size_t sourceSize)
+ {
+ if (outSize == 0)
+ return 0;
+
+ std::memset(out, 0, outSize * sizeof(out[0]));
+ ::WideCharToMultiByte(codepage, 0, source, checked_cast<int>(sourceSize), out, checked_cast<int>(outSize), "?", NULL);
+ out[outSize-1] = 0;
+ return strlen(out);
+ }
+
+ size_t EstimateWideToCodepage(unsigned codepage, wchar_t const* source, size_t sourceSize)
+ {
+ return static_cast<size_t>(::WideCharToMultiByte(codepage, 0, source, checked_cast<int>(wcslen_max(source, sourceSize)), nullptr, 0, "?", NULL) + 1);
+ }
+
+ class AnsiFromWide {
+ public:
+ AnsiFromWide() {}
+ AnsiFromWide(AnsiFromWide const& source) : buffer(source.buffer) { }
+ AnsiFromWide(wchar_t const* source, size_t sourceSize = ~0)
+ {
+ Convert(source, sourceSize);
+ }
+
+ void Convert(wchar_t const* source, size_t sourceSize = ~0)
+ {
+ size_t size = EstimateWideToCodepage(CP_ACP, source, sourceSize);
+ buffer.resize(size);
+ ConvertWideToCodepage(CP_ACP, buffer.data(), size, source, sourceSize);
+ }
+
+ operator char const*() const { return GetPtr(); }
+ char const* GetPtr() const { return buffer.data(); }
+ size_t Length() const { return strlen(GetPtr()); }
+
+ private:
+ std::vector<char> buffer;
+ };
+}
+
+void FormatError(
+ DWORD errorCode,
+ HMODULE module,
+ std::wstring const* customMessage,
+ std::string* errorMessageOut)
+{
+ DWORD flags = FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
+ if (module != NULL)
+ flags |= FORMAT_MESSAGE_FROM_HMODULE;
+
+ wchar_t* messageBuffer = nullptr;
+ local_free_ptr messageBufferGuard;
+ DWORD result = ::FormatMessageW(
+ flags,
+ module,
+ errorCode,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<LPWSTR>(&messageBuffer),
+ 0,
+ NULL);
+
+ std::wstring errorMessage;
+ if (result == 0) {
+ DWORD err = ::GetLastError();
+ result = ::FormatMessageW(
+ FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,
+ err,
+ MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+ reinterpret_cast<LPWSTR>(&messageBuffer),
+ 0,
+ NULL);
+
+ if (result == 0) {
+ messageBufferGuard.reset();
+ messageBuffer = L"Unable to format error code. Additionally, an error occured formatting the error code of FormatMessage.";
+ } else {
+ messageBufferGuard.reset(messageBuffer);
+ }
+ } else {
+ messageBufferGuard.reset(messageBuffer);
+ }
+
+ SIZE_T size = (wcslen(messageBuffer) + 40) * sizeof(wchar_t);
+ local_free_ptr msgWithCodeBuffer(::LocalAlloc(LMEM_ZEROINIT, size));
+ ::StringCchPrintfW(
+ reinterpret_cast<LPWSTR>(msgWithCodeBuffer.get()),
+ ::LocalSize(msgWithCodeBuffer.get()) / sizeof(wchar_t),
+ L"[0x%X] %s",
+ errorCode,
+ messageBuffer);
+
+ wchar_t* msgWithCode = reinterpret_cast<wchar_t*>(msgWithCodeBuffer.get());
+ size_t len = wcslen(msgWithCode) - 1;
+ while (len > 0 && msgWithCode[len] == '\n' || msgWithCode[len] == '\r')
+ --len;
+ msgWithCode[len + 1] = 0;
+
+ if (customMessage != nullptr && customMessage->size() > 0)
+ *errorMessageOut = StringCvt::AnsiFromWide((*customMessage + msgWithCode).c_str());
+ else
+ *errorMessageOut = StringCvt::AnsiFromWide(msgWithCode);
+}
+
+class NtException : public virtual std::exception
+{
+public:
+ NtException(NTSTATUS status);
+ NtException(NTSTATUS status, std::wstring const& message);
+
+ virtual char const* what() const { return message.c_str(); }
+
+private:
+ NTSTATUS status;
+ std::string message;
+};
+
+NtException::NtException(NTSTATUS status)
+ : status(status)
+{
+ FormatError(static_cast<DWORD>(status), GetModuleHandleW(L"ntdll.dll"), nullptr, &this->message);
+}
+
+NtException::NtException(NTSTATUS status, std::wstring const& message)
+ : status(status)
+{
+ FormatError(static_cast<DWORD>(status), GetModuleHandleW(L"ntdll.dll"), &message, &this->message);
+}
+
+class Win32Exception : public virtual std::exception
+{
+public:
+ Win32Exception();
+ Win32Exception(DWORD error);
+ Win32Exception(std::wstring const& message);
+ Win32Exception(DWORD error, std::wstring const& message);
+
+ virtual char const* what() const { return message.c_str(); }
+
+private:
+ DWORD error;
+ std::string message;
+};
+
+Win32Exception::Win32Exception()
+ : error(GetLastError())
+{
+ FormatError(error, NULL, nullptr, &this->message);
+}
+
+Win32Exception::Win32Exception(DWORD error)
+ : error(error)
+{
+ FormatError(error, NULL, nullptr, &this->message);
+}
+
+Win32Exception::Win32Exception(std::wstring const& message)
+ : error(GetLastError())
+{
+ FormatError(error, NULL, &message, &this->message);
+}
+
+Win32Exception::Win32Exception(DWORD error, std::wstring const& message)
+ : error(error)
+{
+ FormatError(error, NULL, &message, &this->message);
+}
+
+std::wstring QueryHandleType(HANDLE handle)
+{
+ malloc_ptr<OBJECT_TYPE_INFORMATION> objectTypeInfo(0x1000, true);
+ NTSTATUS status = NtQueryObject(handle, ObjectTypeInformation, objectTypeInfo.get(), 0x1000, NULL);
+ if (!NT_SUCCESS(status))
+ throw NtException(status, L"Could not query type of handle via NtQueryObject.");
+
+ return std::wstring(objectTypeInfo->Name.Buffer, checked_cast<unsigned int>(objectTypeInfo->Name.Length / 2));
+}
+
+std::wstring QueryHandleName(HANDLE handle)
+{
+ malloc_ptr<> objectNameInfo(0x1000);
+ ULONG returnLength = 0;
+ NTSTATUS status = NtQueryObject(
+ handle,
+ ObjectNameInformation,
+ objectNameInfo.get(),
+ 0x1000,
+ &returnLength);
+
+ if (!NT_SUCCESS(status)) {
+ if (status != STATUS_INFO_LENGTH_MISMATCH)
+ throw NtException(status);
+
+ // Reallocate the buffer and try again.
+ objectNameInfo.realloc(returnLength, true);
+ status = NtQueryObject(
+ handle,
+ ObjectNameInformation,
+ objectNameInfo.get(),
+ returnLength,
+ NULL);
+ }
+
+ if (!NT_SUCCESS(status))
+ throw NtException(status, L"Could not query name of handle via NtQueryObject.");
+
+ UNICODE_STRING objectName = *reinterpret_cast<UNICODE_STRING*>(objectNameInfo.get());
+ if (objectName.Length == 0)
+ return std::wstring(L"<unnamed>");
+
+ return std::wstring(objectName.Buffer, checked_cast<unsigned int>(objectName.Length / 2));
+}
+
+// Translate path with device name to drive letters.
+std::wstring NativePathToDosPath(std::wstring nativePath)
+{
+ int const BufferSize = 512;
+ wchar_t temp[BufferSize];
+ temp[0] = '\0';
+
+ if (!GetLogicalDriveStringsW(BufferSize - 1, temp))
+ throw std::exception();
+
+ wchar_t name[MAX_PATH];
+ wchar_t drive[3] = L" :";
+ bool found = false;
+ wchar_t* p = temp;
+
+ do {
+ // Copy the drive letter to the template string
+ *drive = *p;
+
+ // Look up each device name
+ if (QueryDosDevice(drive, name, MAX_PATH)) {
+ size_t nameLength = wcslen(name);
+
+ if (nameLength < MAX_PATH) {
+ found =
+ _wcsnicmp(nativePath.c_str(), name, nameLength) == 0 &&
+ nativePath[nameLength] == L'\\';
+
+ if (found) {
+ // Replace device path with DOS path
+ wchar_t tempFile[MAX_PATH];
+ StringCchPrintfW(
+ tempFile,
+ MAX_PATH,
+ L"%s%s",
+ drive,
+ nativePath.c_str() + nameLength);
+ std::wstring dosPath(tempFile);
+ return dosPath;
+ }
+ }
+ }
+
+ // Go to the next NULL character.
+ while (*p++)
+ ;
+ } while (!found && *p); // end of string
+
+ return std::wstring();
+}
+
+bool TryGetProcessName(DWORD const processId, std::wstring& processName)
+{
+ handle_ptr process(OpenProcess(
+ PROCESS_QUERY_INFORMATION | PROCESS_VM_READ,
+ FALSE,
+ processId));
+
+ if (process == NULL)
+ return false;
+
+ HMODULE baseModule;
+ DWORD bytesNeeded;
+ if (!EnumProcessModules(process, &baseModule, sizeof(baseModule), &bytesNeeded))
+ return false;
+
+ wchar_t nameBuffer[MAX_PATH] = L"<unknown>";
+ GetModuleBaseNameW(process, baseModule, nameBuffer, _countof(nameBuffer));
+
+ processName.assign(nameBuffer);
+ return true;
+}
+
+template<typename T, typename A>
+inline size_t DataSizeOf(std::vector<T, A> const& vec)
+{
+ return sizeof(vec[0]) * vec.size();
+}
+
+ProcessIdList GetProcessIds()
+{
+ ProcessIdList processIds(1024);
+ size_t processCount = 0;
+
+ for (;;) {
+ DWORD returnedBytes;
+ DWORD bytes = checked_cast<DWORD>(DataSizeOf(processIds));
+ if (!EnumProcesses(processIds.data(), bytes, &returnedBytes))
+ break;
+ if (returnedBytes < bytes) {
+ processCount = returnedBytes / sizeof(DWORD);
+ break;
+ }
+
+ processIds.resize(processIds.size() * 2);
+ }
+
+ processIds.resize(processCount);
+ processIds.shrink_to_fit();
+ return processIds;
+}
+
+DWORD FindProcessByName(std::wstring const& name)
+{
+ ProcessIdList processIds(GetProcessIds());
+
+ for (auto it = processIds.begin(), end = processIds.end(); it != end; ++it) {
+ std::wstring processName;
+ if (TryGetProcessName(*it, processName) && EndsWith(processName, name))
+ return *it;
+ }
+
+ return 0;
+}
+
+HandleList FindHandles(DWORD processId, HANDLE processHandle, std::wstring fileName)
+{
+ NTSTATUS status;
+ HandleList handles;
+
+ ULONG handleInfoSize = 0x10000;
+ malloc_ptr<SYSTEM_HANDLE_INFORMATION> handleInfo(handleInfoSize, true);
+
+ while ((status = NtQuerySystemInformation(
+ SystemHandleInformation,
+ handleInfo.get(),
+ handleInfoSize,
+ NULL
+ )) == STATUS_INFO_LENGTH_MISMATCH) {
+ handleInfo.realloc(handleInfoSize *= 2, true);
+ }
+
+ if (!NT_SUCCESS(status))
+ throw NtException(status, L"NtQuerySystemInformation failed.");
+
+ for (ULONG i = 0; i < handleInfo->HandleCount; ++i) {
+ SYSTEM_HANDLE_TABLE_ENTRY_INFO handle = handleInfo->Handles[i];
+ HANDLE dupHandle = NULL;
+
+ if (handle.ProcessId != processId)
+ continue;
+ if (handle.GrantedAccess == 0x0012019F)
+ continue;
+
+ // Duplicate the handle so we can query it.
+ status = NtDuplicateObject(
+ processHandle, reinterpret_cast<HANDLE>(handle.Handle), GetCurrentProcess(),
+ &dupHandle, 0, 0, 0);
+ if (!NT_SUCCESS(status))
+ continue;
+
+ handle_ptr h(dupHandle);
+
+ std::wstring type = QueryHandleType(h);
+ if (type.compare(L"File") != 0)
+ continue;
+
+ std::wstring name = QueryHandleName(h);
+ if (!EqualsI(name, fileName))
+ continue;
+
+ handles.push_back(
+ NamedHandle(NativePathToDosPath(name), reinterpret_cast<HANDLE>(handle.Handle)));
+ }
+
+ return handles;
+}
+
+// Try to get the device path for the input path. Succeeds only if the target
+// file exists.
+bool TryGetDevicePath(std::wstring inputPath, std::wstring& devicePath)
+{
+ handle_ptr targetHandle(CreateFileW(
+ inputPath.c_str(),
+ 0,
+ FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+ nullptr,
+ OPEN_EXISTING,
+ FILE_ATTRIBUTE_NORMAL,
+ NULL));
+
+ if (targetHandle == NULL)
+ return false;
+
+ wchar_t buffer[MAX_PATH] = { 0 };
+ if (!GetFinalPathNameByHandle(
+ targetHandle.get(),
+ buffer,
+ MAX_PATH,
+ FILE_NAME_NORMALIZED | VOLUME_NAME_NT)) {
+ // Do not fail here so we can try with the non-modified path.
+ devicePath = inputPath;
+ } else {
+ devicePath = std::wstring(buffer);
+ }
+
+ return true;
+}
+
+void KillHandle(HANDLE processHandle, NamedHandle handle)
+{
+ if (!DuplicateHandle(processHandle, handle.Handle, NULL, nullptr, 0, FALSE, DUPLICATE_CLOSE_SOURCE)) {
+ fwprintf(
+ stderr,
+ L"KillHandle: Could not close handle 0x%08IX in process 0x%08IX (%s)!\n",
+ handle.Handle,
+ processHandle,
+ handle.Name.c_str());
+ return;
+ }
+
+ wprintf(
+ L"KillHandle: Killed handle 0x%08IX in process 0x%08IX (%s)\n",
+ handle.Handle,
+ processHandle,
+ handle.Name.c_str());
+}
+
+int wmain(int argc, wchar_t* argv[])
+{
+ std::wstring const defaultProcessName(L"devenv.exe");
+
+ if (argc < 2) {
+ fwprintf(
+ stderr,
+ L"KillHandle <file-path> [<process-name = \"%s\">]",
+ argv[0],
+ defaultProcessName.c_str());
+ return 1;
+ }
+
+ std::wstring targetFileName;
+ if (!TryGetDevicePath(argv[1], targetFileName))
+ // Nothing to do
+ return 0;
+
+ std::wstring processName(argc >= 3 ? argv[2] : defaultProcessName);
+
+ try {
+ DWORD pid = FindProcessByName(processName);
+ if (pid == 0) {
+ fwprintf(stderr, L"KillHandle: <%s> process not found.\n", processName.c_str());
+ return 1;
+ }
+
+ if (!LoadProcs()) {
+ fwprintf(stderr, L"KillHandle: Could not load ntdll.dll functions.\n");
+ return 1;
+ }
+
+ handle_ptr processHandle(OpenProcess(PROCESS_DUP_HANDLE, FALSE, pid));
+ if (processHandle == NULL) {
+ fwprintf(stderr, L"KillHandle: Could not open process %s:%lu.\n", processName.c_str(), pid);
+ return 1;
+ }
+
+ HandleList handles = FindHandles(pid, processHandle.get(), targetFileName);
+ std::for_each(
+ handles.begin(),
+ handles.end(),
+ [&](NamedHandle& handle) { KillHandle(processHandle.get(), handle); });
+ } catch (NtException const& ex) {
+ fprintf(stderr, "KillHandle: NT-call failed: %s\n", ex.what());
+ return 1;
+ } catch (std::exception const& ex) {
+ fprintf(stderr, "KillHandle: Caught exception: %s\n", ex.what());
+ return 1;
+ }
+
+ return 0;
+}
15 src/Resource.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by KillHandle.rc
+//
+
+// Next default values for new objects
+//
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE 101
+#define _APS_NEXT_COMMAND_VALUE 40001
+#define _APS_NEXT_CONTROL_VALUE 1001
+#define _APS_NEXT_SYMED_VALUE 101
+#endif
+#endif

0 comments on commit 36d8ee2

Please sign in to comment.
Something went wrong with that request. Please try again.