c++mfcpaintshellexecutewaitforsingleobject

ShellExecuteEx and WaitForSingleObject paint corruption to window under


Using the following code to update my application with an external exe file, I get paint corruption (not update or refresing) to window under -which is the main app and the caller- when I move this window. It seems that under Windows 7 works fine but under window XP I have this problem.

void CMainFrame::OnBtnUpdateApp() {

    SHELLEXECUTEINFO    lpExecInfo;
    DWORD               dwExitCode;
    HINSTANCE           hProcess = 0;
    BOOL                bResult;



    ZeroMemory(&lpExecInfo,sizeof(lpExecInfo));
    lpExecInfo.cbSize = sizeof(SHELLEXECUTEINFO);
    lpExecInfo.fMask = SEE_MASK_NOCLOSEPROCESS;
    lpExecInfo.hwnd = GetSafeHwnd();
    lpExecInfo.lpVerb = _T("open");
    lpExecInfo.lpFile = _T("Update.exe");
    lpExecInfo.lpParameters = _T("");
    lpExecInfo.lpDirectory = _T("");
    lpExecInfo.nShow = SW_SHOWNORMAL;
    lpExecInfo.hInstApp = NULL;
    lpExecInfo.hProcess = hProcess;

    bResult = ShellExecuteEx(&lpExecInfo);

    if(bResult) { 


         WaitForSingleObject( lpExecInfo.hProcess, INFINITE );

         if (!GetExitCodeProcess(lpExecInfo.hProcess, &dwExitCode)) {
                //failed to terminate normally   
         }

         CloseHandle(lpExecInfo.hProcess);

    } else {

        //failed to execute the exe file
    }


}

What seems to be wrong here ?


Solution

  • You're not processing any window messages during WaitForSingleObject.

    Re the difference between Windows XP and Windows 7, the Desktop Window Manager technology in Windows 7 was introduced with Windows Vista, and was not available in Windows XP. Essentially it provides a layer of indirection between each app's painting actions and the result on screen.

    A reasonable way to launch and wait for a program is to disable the main window the window's user interface parts and then poll the program's exit status in a peek-message loop.


    Example, except that it uses CreateProcess (I coded it up without remembering to check the question, and now it's pretty late in the evening, but better with imperfect help than no help, I think):

    #include <windows.h>    // UNICODE, NOMINMAX, STRICT, WIN32_LEAN_AND_MEAN
    #include <windowsx.h>   // Message cracker macros, e.g. HANDLE_WM_DESTROY
    
    #include <assert.h>
    #include <stdexcept>
    #include <string>
    using namespace std;
    
    auto hopefully( bool const condition ) -> bool { return condition; }
    auto fail( string const& s ) -> bool { throw runtime_error( s ); }
    
    struct Window_class_id
    {
        ATOM value;
        auto as_pointer() const -> wchar_t const* { return MAKEINTATOM( value ); }
    };
    
    auto get_message( MSG& m )
        -> bool
    {
        int const result = GetMessage( &m, 0, 0, 0 );
        hopefully( result != -1 )
            || fail( "GetMessage failed" );
        return !!result;
    }
    
    auto peek_message( MSG& m )
        -> bool
    {
        int const result = PeekMessage( &m, 0, 0, 0, TRUE );
        hopefully( result != -1 )
            || fail( "PeekMessage failed" );
        return !!result;
    }
    
    void empty_message_queue()
    {
        MSG m;
        while( peek_message( m ) )
        {
            TranslateMessage( &m );
            DispatchMessage( &m );
        }
    }
    
    auto dispatch_messages()
        -> DWORD            // Exit code from WM_QUIT
    {
        MSG msg;
        while( get_message( msg ) )
        {
            TranslateMessage( &msg );
            DispatchMessage( &msg );
        }
        assert( msg.message == WM_QUIT );
        return msg.wParam;
    }
    
    auto run( wchar_t const command[] )
        -> HANDLE
    {
        wstring commandline = command;
        hopefully( commandline.length() > 0 )
            || fail( "run: Empty command line" );
        STARTUPINFO in_params = { sizeof( STARTUPINFO ) };
        PROCESS_INFORMATION out_params = {};
    
        bool const success = !!CreateProcess(
            nullptr,            // app name
            &commandline[0],
            nullptr,            // process attributes
            nullptr,            // thread attributes
            false,              // inherit handles
            0,                  // creation flags
            nullptr,            // environment block
            nullptr,            // current directory
            &in_params,         // startup info
            &out_params         // process info
            );
        hopefully( success )
            || fail( "run: CreateProcess failed" );
        CloseHandle( out_params.hThread );
        return out_params.hProcess;
    }
    
    namespace main_window
    {
        namespace command_id {
            int const run_fun = 101;
        }  // namespace command
    
        namespace run_button {
            int const id     = command_id::run_fun;
        }  // namespace run_button
    
        namespace command {
            void run_fun( HWND const window )
            {
                EnableWindow( GetDlgItem( window, run_button::id ), false );
                UpdateWindow( window );
                empty_message_queue();
                HANDLE const process = run( L"notepad" );
                for( ;; )
                {
                    DWORD const result = WaitForSingleObject( process, 100 );
                    if( result == WAIT_OBJECT_0 )
                    {
                        break;
                    }
                    empty_message_queue();
                }
                CloseHandle( process );
                EnableWindow( GetDlgItem( window, run_button::id ), true );
            }
        }  // namespace command
    
       void on_command( HWND const window, int const id )
        {
            switch( id )
            {
            case command_id::run_fun:   return command::run_fun( window );
            }
        }
    
        void on_wm_command(
            HWND const      window,
            int const       control_or_command_id,
            HWND const      control,
            UINT const      notification_code
            )
        {
            if( control == 0 )
            {
                int const command_id = control_or_command_id;
                on_command( window, command_id );
            }
            else
            {
                int const control_id = control_or_command_id;
                switch( control_id )
                {
                case run_button::id:
                    if( notification_code == BN_CLICKED )
                    {
                        int const command_id = control_id;
                        on_command( window, command_id );
                    }
                }
            }
        }
    
        auto on_wm_create( HWND const window, CREATESTRUCT const* const p_params )
            -> bool     // `true` if creation succeeded.
        {
            (void) p_params;
            HWND const button_handle = CreateWindow(
                L"button", L"Run the fun", WS_CHILD | WS_VISIBLE,
                10, 10, 120, 26,
                window,             // parent
                reinterpret_cast<HMENU>( run_button::id ),
                GetModuleHandle( 0 ),
                0           // lpParam
                );
            return (button_handle != 0);
        }
    
        void on_wm_destroy( HWND const window )
        {
            (void) window;
            PostQuitMessage( 0 );
        }
    
        auto CALLBACK message_handler(
            HWND const          window,
            UINT const          message_id,
            WPARAM const        word_param,
            LPARAM const        long_param
            )
            -> LRESULT
        {
            switch( message_id )
            {
                case WM_COMMAND:    return HANDLE_WM_COMMAND(
                    window, word_param, long_param, on_wm_command );
                case WM_CREATE:     return HANDLE_WM_CREATE(
                    window, word_param, long_param, on_wm_create );
                case WM_DESTROY:    return HANDLE_WM_DESTROY(
                    window, word_param, long_param, on_wm_destroy );
            }
            return DefWindowProc( window, message_id, word_param, long_param );
        }
    }  // namespace main_window
    
    auto register_window_class()
        -> Window_class_id
    {
        WNDCLASS params = {};
        params.style            = CS_DBLCLKS;
        params.lpfnWndProc      = main_window::message_handler;
        params.hInstance        = GetModuleHandle( 0 );
        params.hIcon            = LoadIcon( 0, IDI_APPLICATION );
        params.hCursor          = LoadCursor( 0, IDC_ARROW );
        params.hbrBackground    = reinterpret_cast<HBRUSH>( COLOR_WINDOW );
        params.lpszClassName    = L"MainWindow_class";
    
        ATOM const id = RegisterClass( &params );
        hopefully( id != 0 )
            || fail( "RegisterClass failed" );
        return {id};
    }
    
    auto create_window( Window_class_id const& class_id )
        -> HWND
    {
        HWND const handle = CreateWindow(
            class_id.as_pointer(),
            L"Fun run",
            WS_OVERLAPPEDWINDOW,
            CW_USEDEFAULT, CW_USEDEFAULT, 380, 221,     // x, y, w, h
            0, 0,       // parent, menu
            GetModuleHandle( 0 ),
            0           // lpParam
            );
        hopefully( handle != 0 )
            || fail( "CreateWindow failed" );
        return handle;
    }
    
    void cpp_main()
    {
        Window_class_id const class_id = register_window_class();
        HWND const window = create_window( class_id );
        ShowWindow( window, SW_SHOWDEFAULT );
        int const exit_code = static_cast<int>( dispatch_messages() );
        hopefully( exit_code == 0 )
            || fail( "WM_QUIT indicated failure" );
    }
    
    auto main() -> int
    {
        try{ cpp_main(); } catch( ... ) { return E_FAIL; }
        return 0;
    }