//                                          
// Driver Verifier UI
// Copyright (c) Microsoft Corporation, 1999
//
//
//
// module: SlowDlg.cpp
// author: DMihai
// created: 11/1/00
//
// Description:
//

#include "stdafx.h"
#include "verifier.h"

#include "SlowDlg.h"
#include "VrfUtil.h"
#include "VGlobal.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/////////////////////////////////////////////////////////////////////////////
// CSlowProgressDlg dialog


CSlowProgressDlg::CSlowProgressDlg( CWnd* pParent /*=NULL*/ )
	: CDialog(CSlowProgressDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CSlowProgressDlg)
		// NOTE: the ClassWizard will add member initialization here
	//}}AFX_DATA_INIT

    m_hWorkerThread = NULL;

    //
    // Create the event used for killing the worker thread
    //

    m_hKillThreadEvent = CreateEvent(
        NULL,
        TRUE,
        FALSE,
        NULL );
}

CSlowProgressDlg::~CSlowProgressDlg()
{
    if( NULL != m_hKillThreadEvent )
    {
        CloseHandle( m_hKillThreadEvent );
    }
}

void CSlowProgressDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CSlowProgressDlg)
	DDX_Control(pDX, IDC_UNSIGNED_PROGRESS, m_ProgressCtl);
	DDX_Control(pDX, IDC_UNSIGNED_STATIC, m_CurrentActionStatic);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CSlowProgressDlg, CDialog)
	//{{AFX_MSG_MAP(CSlowProgressDlg)
	ON_BN_CLICKED(IDC_UNSIGNED_CANCEL_BUTTON, OnCancelButton)
	ON_WM_SHOWWINDOW()
	ON_WM_HELPINFO()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CSlowProgressDlg::LoadDriverDataWorkerThread( PVOID p )
{
    CSlowProgressDlg *pThis;
    
    pThis = (CSlowProgressDlg *)p;
    
    //
    // Cannot ASSERT_VALID for a CWnd from a thread that didn't create the window in MFC...
    //
    
    ASSERT( NULL != pThis );
    
    //
    // Load all the drivers information (name, version, etc.)
    // if we haven't don that already
    //

    g_NewVerifierSettings.m_DriversSet.LoadAllDriversData( pThis->m_hKillThreadEvent,
                                                           pThis->m_ProgressCtl );

    //
    // Done - hide the "slow progress" dialog and press the wizard "next" button
    //

    pThis->ShowWindow( SW_HIDE );

    if( g_NewVerifierSettings.m_DriversSet.m_bDriverDataInitialized )
    {
        AfxGetMainWnd()->PostMessage(PSM_PRESSBUTTON, (WPARAM)PSBTN_NEXT, 0) ;
    }

    return 0;
}

/////////////////////////////////////////////////////////////////////////////
DWORD WINAPI CSlowProgressDlg::SearchUnsignedDriversWorkerThread( PVOID p )
{
    CSlowProgressDlg *pThis;
    
    pThis = (CSlowProgressDlg *)p;
    
    //
    // Cannot ASSERT_VALID for a CWnd from a thread that didn't create the window in MFC...
    //
    
    ASSERT( NULL != pThis );
   
    //
    // Find out the unsigned drivers if we didn't do that already
    //

    g_NewVerifierSettings.m_DriversSet.FindUnsignedDrivers( pThis->m_hKillThreadEvent,
                                                                  pThis->m_ProgressCtl );

    //
    // Done - hide the "slow progress" dialog and press the wizard "next" button
    //

    pThis->ShowWindow( SW_HIDE );

    if( g_NewVerifierSettings.m_DriversSet.m_bUnsignedDriverDataInitialized )
    {
        AfxGetMainWnd()->PostMessage(PSM_PRESSBUTTON, (WPARAM)PSBTN_NEXT, 0) ;
    }

    return 0;
}

/////////////////////////////////////////////////////////////////////////////
BOOL CSlowProgressDlg::StartWorkerThread( LPTHREAD_START_ROUTINE pThreadStart,
                                          ULONG uMessageResourceId )
{
    DWORD dwThreadId;
    CString strWorkMessage;

    //
    // Load a description of the current "work item"
    // and show it to the user
    //

    VERIFY( strWorkMessage.LoadString( uMessageResourceId ) );
    m_CurrentActionStatic.SetWindowText( strWorkMessage );
    m_CurrentActionStatic.RedrawWindow();

    //
    // Kill a possible currently running worker thread
    //

    KillWorkerThread();

    ASSERT( NULL == m_hWorkerThread );
    
    //
    // Make sure the "kill thread" event is not signaled
    //

    if( NULL != m_hKillThreadEvent )
    {
        ResetEvent( m_hKillThreadEvent );
    }

    //
    // Create the new worker thread
    //

    m_hWorkerThread = CreateThread( 
        NULL,
        0,
        pThreadStart,
        this,
        0,
        &dwThreadId );

    if( NULL == m_hWorkerThread )
    {
        //
        // Could not create the worker thread - bail out
        //

        VrfErrorResourceFormat( IDS_NOT_ENOUGH_MEMORY );

        PostMessage( WM_COMMAND,
                     IDC_UNSIGNED_CANCEL_BUTTON );

        return FALSE;
    }

    return TRUE;
}

/////////////////////////////////////////////////////////////////////////////
VOID CSlowProgressDlg::KillWorkerThread()
{
    DWORD dwWaitResult;
    MSG msg;

    if( NULL != m_hWorkerThread )
    {
        if( NULL != m_hKillThreadEvent )
        {
            //
            // Ask the worker thread to die asap
            //

            SetEvent( m_hKillThreadEvent );
        }

        //
        // Wait forever for a decent death from the worker thread.
        //
        // We cannot TerminateThread on our worker thread because
        // it could be killed while holding locks (e.g. the heap lock)
        // and that would deadlock our whole process.
        //

        while( m_hWorkerThread != NULL )
        {
            dwWaitResult = MsgWaitForMultipleObjects( 1,
                                                      &m_hWorkerThread, 
                                                      FALSE,
                                                      INFINITE,
                                                      QS_ALLINPUT );

            ASSERT( NULL != WAIT_FAILED );

            if( WAIT_OBJECT_0 != dwWaitResult )
            {
                //
                // Our thread didn't exit but we have some messages to dispatch.
                //

                while( PeekMessage( &msg, NULL, NULL, NULL, PM_REMOVE ) )
                {
                    TranslateMessage( &msg );
                    DispatchMessage( &msg );
                }

                //
                // During the DispatchMessage above we could process another 
                // click of the Cancel button or the Back button of the wizard. 
                // The KillWorkerThread recursive call will wait until the worker 
                // thread dies then will sets m_hWorkerThread to NULL. 
                // So we need to check for m_hWorkerThread != NULL before each new
                // MsgWaitForMultipleObjects.
                //
            }
            else
            {
                //
                // The worker thread finished execution.
                //

                break;
            }
        }

        if( m_hWorkerThread != NULL )
        {
            //
            // Close the thread handle
            //

            CloseHandle( m_hWorkerThread );

            m_hWorkerThread = NULL;
        }
    }
}

/////////////////////////////////////////////////////////////////////////////
// CSlowProgressDlg message handlers

void CSlowProgressDlg::OnCancelButton() 
{
    KillWorkerThread();

    ShowWindow( SW_HIDE );
}

/////////////////////////////////////////////////////////////////////////////
void CSlowProgressDlg::OnShowWindow(BOOL bShow, UINT nStatus) 
{
    CDialog::OnShowWindow(bShow, nStatus);
	
    if( TRUE == bShow )
    {
        CenterWindow();
    }
}

/////////////////////////////////////////////////////////////////////////////
BOOL CSlowProgressDlg::OnHelpInfo(HELPINFO* pHelpInfo) 
{
    return TRUE;
}