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

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

Issue 6202981292703744: Whole installer (Closed)
Patch Set: Created June 24, 2014, 7:27 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.cpp
===================================================================
new file mode 100644
--- /dev/null
+++ b/installer/src/installer-lib/process.cpp
@@ -0,0 +1,301 @@
+#include <stdexcept>
+#include <functional>
+#include <wctype.h>
+// <thread> is C++11, but implemented in VS2012
+#include <thread>
+
+#include "installer-lib.h"
+#include "process.h"
+
+//-------------------------------------------------------
+//-------------------------------------------------------
+bool process_by_any_exe_with_any_module::operator()( const PROCESSENTRY32W & process )
+{
+ if (processNames.find(process.szExeFile) != processNames.end())
+ {
+ if (moduleNames.empty())
+ return true;
+
+ Module_Snapshot ms(process.th32ProcessID);
+ const MODULEENTRY32W* me = ms.first();
+ while (me != 0)
+ {
+ if (moduleNames.find(me->szModule) != moduleNames.end())
+ {
+ return true;
+ }
+ me = ms.next();
+ }
+ }
+ return false;
+}
+
+//-------------------------------------------------------
+// creator_process
+//-------------------------------------------------------
+DWORD creator_process( HWND window )
+{
+ DWORD pid ;
+ DWORD r = GetWindowThreadProcessId( window, & pid ) ;
+ if ( r == 0 )
+ {
+ // Assert GetWindowThreadProcessId returned an error
+ // If the window handle is invalid, we end up here.
+ throw windows_api_error( "GetWindowThreadProcessId", r ) ;
+ }
+ return pid ;
+}
+
+//-------------------------------------------------------
+// send_message, send_endsession_messages
+//-------------------------------------------------------
+/**
+* Default process exit wait time (per message) 5000 ms
+*
+* 5 seconds is time that the system will wait before it considers a process non-responsive.
+*/
+static const unsigned int timeout = 5000 ; // milliseconds
+
+/**
+* An function object to process the results of sending window messages in send_message.
+*
+* We are using send_message within a system iteration over windows.
+* The system has its own convention for continuing/breaking the iteration.
+* This convention is assured consistently in send_message, which also provides default behavior.
+* This class provides the base for any variation from the default behavior.
+*/
+struct message_accumulator
+ : public std::binary_function< DWORD_PTR, bool, bool >
+{
+ virtual result_type operator()( first_argument_type result, second_argument_type return_value ) = 0 ;
+ virtual ~message_accumulator() {} ;
+} ;
+
+/**
+* Iteration action to send a message to a window and accumulate results.
+*
+* An error sending the message is not a failure for the function a whole.
+* The goal is to close the process, and if the window is no longer present, then the process may have already closed.
+* Therefore, we're ignoring both the return value and the result.
+*/
+class send_message
+{
+ UINT message ; ///< Message type for windows message
+ WPARAM p1 ; ///< Generic parameter 1 for windows message
+ LPARAM p2 ; ///< Generic parameter 2 for windows message
+ message_accumulator * f ; ///< Processor for results of sending the message.
+
+public:
+ /**
+ * Full contructor gathers message parameters and a message accumulator.
+ */
+ send_message( UINT message, WPARAM p1, LPARAM p2, message_accumulator & f )
+ : message( message ), p1( p1 ), p2( p2 ), f( & f )
+ {}
+
+ /**
+ * Abbreviated contructor gathers only message parameters.
+ * The message accumulator is absent.
+ */
+ send_message( UINT message, WPARAM p1, LPARAM p2 )
+ : message( message ), p1( p1 ), p2( p2 ), f( 0 )
+ {}
+
+ /*
+ * Enumeration function applied to each window.
+ */
+ bool operator()( HWND window )
+ {
+ DWORD_PTR result ;
+ LRESULT rv = SendMessageTimeoutW( window, message, p1, p2, SMTO_BLOCK, timeout, & result ) ;
+ /*
+ * If we have no message accumulator, the default behavior is to iterate everything.
+ * If we do have one, we delegate to it the decision whether to break or to continue.
+ */
+ if ( ! f )
+ {
+ return true ;
+ }
+ return ( * f )( result, (rv != 0) ) ;
+ }
+} ;
+
+/**
+* Send WM_QUERYENDSESSION and WM_ENDSESSION to a window.
+*
+* This window processor tries to shut down each application individually.
+* The alternative, gathering all the query results first and only then ending sessions, cannot be done with a single window enumeration.
+*/
+class send_endsession_messages
+{
+public:
+ /*
+ * Enumeration function applied to each window.
+ */
+ bool operator()( HWND window )
+ {
+ DWORD_PTR result ;
+ if ( ! SendMessageTimeoutW( window, WM_QUERYENDSESSION, 0, ENDSESSION_CLOSEAPP, SMTO_BLOCK, timeout, & result ) )
+ {
+ // Assert sending the message failed
+ // Ignore failure, just as with send_message().
+ return true ;
+ }
+ // Assert result is FALSE if the process has refused notice that it should shut down.
+ if ( ! result )
+ {
+ /*
+ * Returning false terminates iteration over windows.
+ * Since this process is refusing to shut down, we can't close all the processes and the operation fails.
+ */
+ return false ;
+ }
+ SendMessageTimeoutW( window, WM_ENDSESSION, 0, ENDSESSION_CLOSEAPP, SMTO_BLOCK, timeout, 0 ) ;
+ return true ;
+ }
+} ;
+
+/**
+* Accumulator for query-endsession message.
+*
+* Implements a conditional-conjunction of the query results.
+* All answers must be true in order for this result to be true,
+* and the calculation is terminated at the first answer 'false'.
+* As usual, errors sending messages are ignored.
+*/
+struct endsession_accumulator :
+ public message_accumulator
+{
+ bool permit_end_session ; ///< Accumulator variable yields final result.
+
+ /**
+ * Enumeration function applied to each window.
+ */
+ bool operator()( DWORD_PTR result, bool return_value )
+ {
+ if ( ( ! return_value ) || result )
+ {
+ // 1. If the result is true, then the process will permit WM_ENDSESSION
+ // 2. An error sending the message counts as "no new information"
+ return true ;
+ }
+ // The first false is the result of the calculation.
+ // The second false means to terminate enumeration early.
+ permit_end_session = false ;
+ return false ;
+ }
+
+ /**
+ * Ordinary constructor.
+ */
+ endsession_accumulator()
+ : permit_end_session( true )
+ {}
+} ;
+
+//-------------------------------------------------------
+// Process_Closer
+//-------------------------------------------------------
+/**
+* Shut down all the processes in the pid_list.
+*
+* The method used here uses blocking system calls to send messages to target processes.
+* Message processing delays, therefore, are sequential and the total delay is their sum.
+* Windows has non-blocking message calls available, and using a multi-threaded implementation would shorten that delay.
+* The code, hwoever, is significantly simpler without multi-threading.
+* The present use of this method is not closing dozens of applications, so delay performance is not critical.
+*
+* \return
+* The negation of is_running.
+* If is_running() was true at the beginning, then this function will have run refresh() before returning.
+*
+* \sa
+* - MSDN [WM_QUERYENDSESSION message](http://msdn.microsoft.com/en-us/library/windows/desktop/aa376890%28v=vs.85%29.aspx)
+* - MSDN [WM_ENDSESSION message](http://msdn.microsoft.com/en-us/library/windows/desktop/aa376889%28v=vs.85%29.aspx)
+*/
+bool Process_Closer::shut_down()
+{
+ /*
+ * If we're not running, we don't need to shut down.
+ */
+ if ( ! is_running() )
+ {
+ return true ;
+ }
+
+ /*
+ * Shutting down is a structure as an escalating series of attempts to shut down.
+ * After each one, we wait to see if the shut down has completed.
+ * Even though we're using a blocking call to send messages, applications need not block before exiting.
+ * Internet Explorer, in particular, does not.
+ *
+ * Note that termination occurs inside the default case within the switch statement
+ */
+ for ( unsigned int stage = 1 ; ; ++ stage )
+ {
+ // Assert is_running()
+ switch( stage )
+ {
+ case 1 :
+ /*
+ * Send WM_QUERYENDSESSION to every admissible window.
+ * Send WM_ENDSESSION if all processes are ready to shut down.
+ * We try this technique first, since this allows an application to restore its application state when it starts up again.
+ */
+ {
+ endsession_accumulator acc ;
+ send_message m1( WM_QUERYENDSESSION, 0, ENDSESSION_CLOSEAPP, acc ) ;
+ iterate_our_windows( m1 ) ;
+
+ if ( acc.permit_end_session )
+ {
+ send_message m2( WM_ENDSESSION, 0, 0 ) ;
+ iterate_our_windows( m2 ) ;
+ }
+ }
+ break ;
+
+ case 2 :
+ {
+ /*
+ * Send WM_QUERYENDSESSION and WM_ENDSESSION to every admissible window singly, not accumulating results.
+ */
+ send_endsession_messages m ;
+ iterate_our_windows( m ) ;
+ }
+ break ;
+
+ case 3 :
+ {
+ /*
+ * Send WM_CLOSE to every admissible window.
+ */
+ send_message m( WM_CLOSE, 0, 0 ) ;
+ iterate_our_windows( m ) ;
+ }
+ break ;
+
+ default :
+ /*
+ * We're out of ways to try to shut down.
+ */
+ return false ;
+ }
+
+ /*
+ * Wait loop.
+ */
+ for ( unsigned int j = 0 ; j < 50 ; ++ j )
+ {
+ std::this_thread::sleep_for( std::chrono::milliseconds( 100 ) ) ;
+ refresh() ;
+ if ( ! is_running() )
+ {
+ return true ;
+ }
+ }
+ // Assert is_running()
+ }
+ // No control path leaves the for-loop.
+} ;
+

Powered by Google App Engine
This is Rietveld