Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code

Unified Diff: installer/src/installer-lib/process.h

Issue 5675960980471808: Updated installer with custom action (Closed)
Patch Set: Created March 8, 2014, 5:06 a.m.
Use n/p to move between diff chunks; N/P to move between comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: installer/src/installer-lib/process.h
===================================================================
--- a/installer/src/installer-lib/process.h
+++ b/installer/src/installer-lib/process.h
@@ -1,8 +1,27 @@
-#include <windows.h>
+/**
+ * \file process.h
+ */
+
+#ifndef PROCESS_H
+#define PROCESS_H
+
+#include <vector>
+#include <set>
+#include <algorithm>
+#include <memory>
+
+#include <Windows.h>
#include <TlHelp32.h>
-#include <vector>
-
+//-------------------------------------------------------
+// Windows_Handle
+//-------------------------------------------------------
+/**
+ * A handle to some Windows platform resource.
+ *
+ * Note, this is not the same as a Windows Installer handle (MSIHANDLE).
+ * The two handles have different underlying types and use different functions to close.
+ */
class Windows_Handle
{
public:
@@ -19,7 +38,31 @@
~Windows_Handle() ;
/**
- * Copy constructor declared but not defined.
+ * Conversion operator to underlying HANDLE.
+ */
+ operator HANDLE() const { return handle ; } ;
+
+ /**
+ * Raw handle assignment.
+ *
+ * This is equivalent to destroying the old object and constructing a new one in its place.
+ * In C++11 this would be handled by the move constructor on an rvalue reference.
+ */
+ void operator=( HANDLE h ) ;
+
+private:
+ /**
+ * \invariant The handle is an open handle to some system resource.
+ */
+ HANDLE handle ;
+
+ /**
+ * Validation function for the handle. Invoked at both construction and assignment.
+ */
+ void validate_handle() ;
+
+ /**
+ * Copy constructor declared private and not defined.
*
* \par Implementation
* Add "= delete" for C++11.
@@ -27,88 +70,488 @@
Windows_Handle( const Windows_Handle & ) ;
/**
- * Copy assignment declared but not defined.
+ * Copy assignment declared private and not defined.
*
* \par Implementation
* Add "= delete" for C++11.
*/
Windows_Handle operator=( const Windows_Handle & ) ;
- /**
- * Conversion operator to underlying HANDLE.
- */
- operator HANDLE() const { return handle ; } ;
-
-private:
- /**
- * \invariant The handle is an open handle to some system resource.
- */
- HANDLE handle ;
};
+//-------------------------------------------------------
+// exe_name_set: case-insensitive wide-string set
+//-------------------------------------------------------
+int wcscmpi( const wchar_t * s1, const wchar_t * s2 ) ;
+
+struct exe_name
+{
+ /**
+ * Pointer to wide-character string, which is supposed to be null-terminated.
+ */
+ const wchar_t * name ;
+
+ exe_name( const wchar_t * name ) : name( name ) {} ;
+} ;
+
+template <>
+struct std::less< exe_name >
+ : std::binary_function< exe_name, exe_name, bool >
+{
+ bool operator()( const exe_name & a, const exe_name & b ) const
+ {
+ return wcscmpi( a.name, b.name ) < 0 ;
+ }
+} ;
+
+struct exe_name_set
+ : public std::set< exe_name >
+{
+ exe_name_set( const wchar_t * exe_name_list[], size_t n_exe_names )
+ {
+ for ( unsigned int j = 0 ; j < n_exe_names ; ++ j )
+ {
+ insert( exe_name( exe_name_list[ j ] ) ) ;
+ }
+ }
+} ;
+
+//-------------------------------------------------------
+//-------------------------------------------------------
/**
- * \tparam Predicate A unary predicate type, either function pointer or function object. Ordinarily inferred from the parameter.
+ * Filter by process name. Comparison is case-insensitive.
*/
-template< class T >
-class Process_List
+class process_by_any_exe_name_CI
+ : public std::unary_function< PROCESSENTRY32W, bool >
{
+ const exe_name_set & names ;
+public:
+ bool operator()( const PROCESSENTRY32W & ) ;
+ process_by_any_exe_name_CI( const exe_name_set & names )
+ : names( names )
+ {}
+} ;
+
+//-------------------------------------------------------
+// Process utility functions.
+//-------------------------------------------------------
+/**
+ * A promiscuous filter admits everything.
+ */
+struct every_process
+ : public std::unary_function< PROCESSENTRY32W, bool >
+{
+ bool operator()( const PROCESSENTRY32W & ) { return true ; } ;
+} ;
+
+/**
+ * Filter by process name. Comparison is case-insensitive.
+ */
+class process_by_name_CI
+ : public std::unary_function< PROCESSENTRY32W, bool >
+{
+ const wchar_t * name ;
+ const size_t length ;
+ process_by_name_CI() ;
+public:
+ bool operator()( const PROCESSENTRY32W & ) ;
+ process_by_name_CI( const wchar_t * name ) ;
+} ;
+
+/**
+ * Extractor that copies the entire process structure.
+ */
+struct copy_all
+ : public std::unary_function< PROCESSENTRY32W, PROCESSENTRY32W >
+{
+ PROCESSENTRY32W operator()( const PROCESSENTRY32W & process ) { return process ; }
+} ;
+
+/**
+ * Extractor that copies only the PID.
+ */
+struct copy_PID
+ : public std::unary_function< PROCESSENTRY32W, DWORD >
+{
+ inline DWORD operator()( const PROCESSENTRY32W & process ) { return process.th32ProcessID ; }
+} ;
+
+/**
+ * Case-insensitive wide-character C-style string comparison, fixed-length
+ */
+int wcsncmpi( const wchar_t * a, const wchar_t * b, unsigned int length ) ;
+
+/**
+ * Retrieve the process ID that created a window.
+ *
+ * Wrapper around GetWindowThreadProcessId.
+ * Converts an error return from the system call into an exception.
+ * The system call can also retrieve the creating thread; we ignore it.
+ *
+ * \param window
+ * Handle of the window
+ * \return
+ * ID of the process that created the argument window
+ *
+ * \sa
+ * MSDN [GetWindowThreadProcessId function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms633522%28v=vs.85%29.aspx)
+ */
+DWORD creator_process( HWND window ) ;
+
+//-------------------------------------------------------
+// Snapshot
+//-------------------------------------------------------
+/**
+ * A snapshot of all the processes running on the system.
+ *
+ * Unfortunately, we cannot provide standard iterator for this class.
+ * Standard iterators must be copy-constructible, which entails the possibility of multiple, coexisting iteration states.
+ * The iteration behavior provided by Process32First and Process32Next relies upon state held within the snapshot itself.
+ * Thus, there can be only one iterator at a time for the snapshot.
+ * The two requirements are not simultaneously satisfiable.
+ *
+ * As a substitute for a standard iterator, we provide a few functions mimicking the pattern of standard iterators.
+ * This class acts as its own iterator.
+ * The pointer returned is either one to the member variable "process" or else 0.
+ *
+ * \par Implementation
+ *
+ * - MSDN [CreateToolhelp32Snapshot function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms682489%28v=vs.85%29.aspx)
+ * - MSDN [Process32First function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms684834%28v=vs.85%29.aspx)
+ * - MSDN [Process32Next function](http://msdn.microsoft.com/en-us/library/windows/desktop/ms684836%28v=vs.85%29.aspx)
+ * - MSDN [PROCESSENTRY32 structure](http://msdn.microsoft.com/en-us/library/windows/desktop/ms684839%28v=vs.85%29.aspx)
+ */
+class Snapshot
+{
+ /**
+ * Handle to the process snapshot.
+ */
+ Windows_Handle handle ;
+
+ /**
+ * Buffer for reading a single process entry out of the snapshot.
+ */
+ PROCESSENTRY32W process;
+
+ /**
+ * Copy constructor declared private and not defined.
+ *
+ * \par Implementation
+ * Add "= delete" for C++11.
+ */
+ Snapshot( const Snapshot & ) ;
+
+ /**
+ * Copy assignment declared private and not defined.
+ *
+ * \par Implementation
+ * Add "= delete" for C++11.
+ */
+ Snapshot operator=( const Snapshot & ) ;
+
+
public:
/**
- * \tparam Predicate Function pointer or function object. Generally inferred from the argument.
- * \param admit A selection filter predicate.
- * A process appears in the list only if the predicate returns true.
- * The use of this predicate is analogous to that in std::copy_if.
- * \tparam Converter Function pointer or function object. Generally inferred from the argument.
- * \param convert A conversion function that takes a PROCESSENTRY32W as input argument and returns an element of type T.
+ * Default constructor takes the snapshot.
*/
- template< class Predicate, class Converter >
- Process_List( Predicate admit, Converter convert );
+ Snapshot() ;
/**
+ * Reconstruct the current instance with a new system snapshot.
*/
- ~Process_List() {};
+ void refresh() ;
/**
- * This class is principally a way of initializing a vector by filtering and extracting a process list.
- * There's no point in keeping the underlying vector private.
+ * Return a pointer to the first process in the snapshot.
*/
- std::vector< T > v;
-};
+ PROCESSENTRY32W * begin() ;
+
+ /**
+ * The end pointer is an alias for the null pointer.
+ */
+ inline PROCESSENTRY32W * end() const { return 0 ; }
+
+ /**
+ * Return a pointer to the next process in the snapshot.
+ * begin() must have been called first.
+ */
+ PROCESSENTRY32W * next() ;
+
+ /**
+ * Type definition for pointer to underlying structure.
+ */
+ typedef PROCESSENTRY32W * Pointer ;
+} ;
+
+//-------------------------------------------------------
+// initialize_process_list
+//-------------------------------------------------------
+/**
+ * \tparam T The type into which a PROCESSENTRY32W struture is extracted.
+ * \tparam Admittance Function type for argument 'admit'
+ * \tparam Extractor Function type for argument 'extract'
+ * \param admit A unary predicate function class that determines what's included
+ * A process appears in the list only if the predicate returns true.
+ * The use of this predicate is analogous to that in std::copy_if.
+ * \param convert A conversion function that takes a PROCESSENTRY32W as input argument and returns an element of type T.
+ */
+template< class T, class Admittance, class Extractor >
+void initialize_process_list( std::vector< T > & v, Snapshot & snap, Admittance admit = Admittance(), Extractor extract = Extractor() )
+{
+ Snapshot::Pointer p = snap.begin() ;
+ while ( p != snap.end() )
+ {
+ if ( admit( * p ) )
+ {
+ /*
+ * We don't have C++11 emplace_back, which can construct the element in place.
+ * Instead, we copy the return value of the converter.
+ */
+ v.push_back( extract( * p ) );
+ }
+ p = snap.next() ;
+ }
+} ;
+
+//-------------------------------------------------------
+// initialize_process_set
+//-------------------------------------------------------
+/**
+ * \tparam T The type into which a PROCESSENTRY32W struture is extracted.
+ * \tparam Admittance Function type for argument 'admit'
+ * \tparam Extractor Function type for argument 'extract'
+ * \param admit A unary predicate function class that determines what's included
+ * A process appears in the list only if the predicate returns true.
+ * The use of this predicate is analogous to that in std::copy_if.
+ * \param convert A conversion function that takes a PROCESSENTRY32W as input argument and returns an element of type T.
+ */
+template< class T, class Admittance, class Extractor >
+void initialize_process_set( std::set< T > & set, Snapshot & snap, Admittance admit = Admittance(), Extractor extract = Extractor() )
+{
+ Snapshot::Pointer p = snap.begin() ;
+ while ( p != snap.end() )
+ {
+ if ( admit( * p ) )
+ {
+ set.insert( extract( * p ) );
+ }
+ p = snap.next() ;
+ }
+} ;
+
+//-------------------------------------------------------
+// enumerate_windows
+//-------------------------------------------------------
/**
- * \par Implementation
- *
- * CreateToolhelp32Snapshot function http://msdn.microsoft.com/en-us/library/windows/desktop/ms682489%28v=vs.85%29.aspx
- * Process32First function http://msdn.microsoft.com/en-us/library/windows/desktop/ms684834%28v=vs.85%29.aspx
- * Process32Next function http://msdn.microsoft.com/en-us/library/windows/desktop/ms684836%28v=vs.85%29.aspx
- * PROCESSENTRY32 structure http://msdn.microsoft.com/en-us/library/windows/desktop/ms684839%28v=vs.85%29.aspx
+ * States of a window enumeration.
*/
-template< class T > template< class Predicate, class Converter >
-Process_List< T >::Process_List( Predicate admit, Converter convert )
+typedef enum
{
- /*
- * Take a snapshot only of all processes on the system, and not all the other data available through the CreateToolhelp32Snapshot.
- * Second argument is ignored for flag TH32CS_SNAPPROCESS.
- */
- Windows_Handle handle( ::CreateToolhelp32Snapshot( TH32CS_SNAPPROCESS, 0 ) );
- // Size initialization required for Process32FirstW to succeed.
- PROCESSENTRY32W process;
- process.dwSize = sizeof( PROCESSENTRY32W );
- /*
- * Process32FirstW and Process32NextW iterate through the process snapshot.
- */
- BOOL have_process = ::Process32FirstW( handle, & process );
- while ( have_process )
+ started, ///< The iteration is currently running
+ normal, ///< Iteration terminated without error.
+ early, ///< Callback returned false and terminated iteration early.
+ exception, ///< Callback threw an exception and thereby terminated iteration.
+ error ///< Callback always return true but EnumWindows failed.
+} enumerate_windows_state ;
+
+/**
+ * Data to perform a window enumeration, shared between the main function and the callback function.
+ */
+template< class F >
+struct ew_data
+{
+ F & f ;
+
+ enumerate_windows_state status ;
+
+ std::unique_ptr< std::exception > ee ;
+
+ ew_data( F & f )
+ : f( f ), status( started )
+ {}
+} ;
+
+/**
+ * Callback function for EnumWindows.
+ */
+template< class F >
+BOOL CALLBACK enumeration_callback( HWND window, LPARAM x )
+{
+ // LPARAM is always the same size as a pointer
+ ew_data< F > * data = reinterpret_cast< ew_data< F > * >( x ) ;
+ try
{
- if ( admit( process ) )
+ bool r = data -> f( window ) ;
+ if ( ! r )
+ {
+ data -> status = early ;
+ }
+ return r ;
+ }
+ catch ( std::exception e )
+ {
+ data -> ee = std::unique_ptr< std::exception >( new( std::nothrow ) std::exception( e ) ) ;
+ data -> status = exception ;
+ return FALSE ;
+ }
+ catch ( ... )
+ {
+ data -> status = exception ;
+ return FALSE ;
+ }
+ return TRUE ;
+}
+
+/**
+ * Enumerate windows, applying a function to each one.
+ */
+template< class F >
+bool enumerate_windows( F f )
+{
+ ew_data< F > data( f ) ;
+ BOOL x( ::EnumWindows( enumeration_callback< F >, reinterpret_cast< LPARAM >( & data ) ) ) ;
+ bool r ;
+ if ( data.status != started )
+ {
+ // Assert status was changed within the callback
+ if ( data.status == exception )
{
/*
- * We don't have C++11 emplace_back, which can construct the element in place.
- * Instead, we copy the return value of the converter.
+ * The callback threw an exception of some sort.
+ * We forward it to the extent we are able.
*/
- v.push_back( convert( process ) );
+ if ( data.ee )
+ {
+ throw * data.ee ;
+ }
+ else
+ {
+ throw std::runtime_error( "Unknown exception thrown in callback function." ) ;
+ }
}
- have_process = ::Process32NextW( handle, & process );
+ r = false ;
}
+ else
+ {
+ if ( x )
+ {
+ data.status = normal ;
+ r = true ;
+ }
+ else
+ {
+ // Assert EnumWindows failed
+ data.status = error ;
+ r = false ;
+ }
+ }
+ return r ;
}
+//-------------------------------------------------------
+// Process_Closer
+//-------------------------------------------------------
+class Process_Closer
+{
+ /**
+ * Set of process identifiers matching one of the executable names.
+ */
+ std::set< DWORD > pid_set ;
+
+ /**
+ * Set of executable names by which to filter.
+ *
+ * The argument of the filter constructor is a set by reference.
+ * Since it does not make a copy for itself, we define it as a class member to provide its allocation.
+ */
+ exe_name_set exe_names ;
+
+ /**
+ * Filter function object matches on any of the exe names specified in the constructor.
+ */
+ process_by_any_exe_name_CI filter ;
+
+ /**
+ * Copy function object copies just the process ID.
+ */
+ copy_PID copy ;
+
+ /**
+ * Snapshot of running processes.
+ */
+ Snapshot & snapshot ;
+
+ void update()
+ {
+ initialize_process_set( pid_set, snapshot, filter, copy ) ;
+ } ;
+
+ template< class F >
+ class only_our_processes
+ {
+ Process_Closer & self ;
+
+ F f ;
+
+ public:
+ only_our_processes( Process_Closer & self, F f )
+ : f( f ), self( self )
+ {}
+
+ bool operator()( HWND window )
+ {
+ bool b ;
+ try
+ {
+ b = self.contains( creator_process( window ) ) ;
+ }
+ catch ( ... )
+ {
+ // ignore window handles that are no longer valid
+ return true ;
+ }
+ if ( ! b )
+ {
+ // Assert the process that created the window is not in our pid_set
+ return true ;
+ }
+ return f( window ) ;
+ }
+ } ;
+
+public:
+ Process_Closer( Snapshot & snapshot, const wchar_t * exe_name_list[], size_t n_exe_names )
+ : snapshot( snapshot ), exe_names( exe_name_list, n_exe_names ), filter( exe_names )
+ {
+ update() ;
+ }
+
+ /**
+ * Refresh our state to match the snapshot state.
+ */
+ void refresh()
+ {
+ pid_set.clear() ;
+ update() ;
+ }
+
+ bool is_running() { return ! pid_set.empty() ; } ;
+
+ bool contains( DWORD pid ) const { return pid_set.find( pid ) != pid_set.end() ; } ;
+
+ template< class F >
+ bool iterate_our_windows( F f )
+ {
+ only_our_processes< F > g( * this, f ) ;
+ return enumerate_windows( g ) ;
+ }
+
+ /*
+ * Shut down every process in the pid_set.
+ */
+ bool shut_down() ;
+
+} ;
+
+#endif

Powered by Google App Engine
This is Rietveld