You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
460 lines
12 KiB
460 lines
12 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: A simple app which looks for the HL2 wise installer and ticks the progress bar due |
|
// to a bug with installing more than 2GB of data using the current ver of the windows installer |
|
// |
|
//=============================================================================// |
|
|
|
#include <windows.h> |
|
#include <stdarg.h> |
|
#include <stdio.h> |
|
#include <commctrl.h> |
|
#include <stdlib.h> |
|
|
|
|
|
#define FIND_WINDOW_TEXT_PROGRESSDIALOG "vHackWiseProgressDialog092304" |
|
#define FIND_WINDOW_TEXT_CHANGEDISKDIALOG "vHackWiseProgressDialogChangeCD092304" |
|
#define FIND_WINDOW_TEXT_CANCELDIALOG "vHackWiseProgressDialogCancel092304" |
|
|
|
static char szAppName[] = "vWiseProgressBarHackWndClass"; |
|
|
|
// The full bar is this many ticks (which are about 100 msec apart, so 30 seconds to walk bar |
|
#define PROGRESS_TICKS 75 |
|
#define PROGRESS_WAIT_TICKS 20 |
|
|
|
// After this long, if we didn't find the setup dialog, exit the application |
|
#define SEARCH_TIMEOUT_SECONDS 60 |
|
|
|
#define WISE_PROGRESS_BAR_WINDOW_CLASS "msctls_progress32" |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Globals |
|
//----------------------------------------------------------------------------- |
|
struct Globals_t |
|
{ |
|
|
|
DWORD m_nLastThink; |
|
DWORD m_nStartTick; |
|
|
|
bool m_bFoundWindow; |
|
HWND m_hProgressBar; |
|
HWND m_hDialog; |
|
|
|
UINT m_nTickCounter; |
|
}; |
|
|
|
static Globals_t g; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
struct FindParams_t |
|
{ |
|
HWND wnd; |
|
char searchtext[ 512 ]; |
|
}; |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : hwnd - |
|
// lParam - |
|
// Output : BOOL CALLBACK |
|
//----------------------------------------------------------------------------- |
|
BOOL CALLBACK EnumChildrenLookingForSpecialControl(HWND hwnd,LPARAM lParam) |
|
{ |
|
FindParams_t *p = ( FindParams_t *)lParam; |
|
|
|
char buf[ 512 ]; |
|
|
|
GetWindowText( hwnd, buf, sizeof( buf ) ); |
|
if ( !stricmp( buf, p->searchtext ) ) |
|
{ |
|
p->wnd = hwnd; |
|
return FALSE; |
|
} |
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : hwnd - |
|
// lParam - |
|
// Output : BOOL CALLBACK |
|
//----------------------------------------------------------------------------- |
|
BOOL CALLBACK EnumChildWindowsProc(HWND hwnd, LPARAM lParam) |
|
{ |
|
// Now search for the special hidden text control inside a top level window |
|
|
|
FindParams_t *p = ( FindParams_t *)lParam; |
|
|
|
FindParams_t special; |
|
memset( &special, 0, sizeof( special ) ); |
|
strcpy( special.searchtext, p->searchtext ); |
|
|
|
EnumChildWindows( hwnd, EnumChildrenLookingForSpecialControl, (LPARAM)&special ); |
|
if ( special.wnd != NULL ) |
|
{ |
|
p->wnd = hwnd; |
|
return FALSE; |
|
} |
|
|
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : root - |
|
// *text - |
|
// Output : HWND |
|
//----------------------------------------------------------------------------- |
|
HWND FindWindowHavingChildWithSpecifiedText( HWND root, char const *text ) |
|
{ |
|
FindParams_t params; |
|
memset( ¶ms, 0, sizeof( params ) ); |
|
|
|
strncpy( params.searchtext, text, sizeof( params.searchtext ) - 1 ); |
|
|
|
EnumChildWindows( root, EnumChildWindowsProc, (LPARAM)¶ms ); |
|
|
|
return params.wnd; |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : *text - |
|
// Output : HWND |
|
//----------------------------------------------------------------------------- |
|
HWND FindTopLevelWindowHavingChildWithSpecifiedText( char const *text ) |
|
{ |
|
return FindWindowHavingChildWithSpecifiedText( GetDesktopWindow(), text ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Search for window of class WISE_PROGRESS_BAR_WINDOW_CLASS |
|
// Input : hwnd - |
|
// lParam - |
|
// Output : BOOL CALLBACK |
|
//----------------------------------------------------------------------------- |
|
BOOL CALLBACK EnumFindProgressBarInDialog( HWND hwnd,LPARAM lParam ) |
|
{ |
|
char buf[100]; |
|
|
|
GetClassName( hwnd, buf, sizeof( buf ) ); |
|
if ( !stricmp( buf, WISE_PROGRESS_BAR_WINDOW_CLASS ) ) |
|
{ |
|
*(HWND*)lParam = hwnd; |
|
return FALSE; |
|
} |
|
return TRUE; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Hides the window |
|
// Input : visible - |
|
//----------------------------------------------------------------------------- |
|
void ShowProgressBar( bool visible ) |
|
{ |
|
if ( !g.m_hProgressBar ) |
|
return; |
|
|
|
DWORD style = GetWindowLong( g.m_hProgressBar, GWL_STYLE ); |
|
if ( visible ) |
|
{ |
|
style |= WS_VISIBLE; |
|
} |
|
else |
|
{ |
|
style &= ~WS_VISIBLE; |
|
} |
|
|
|
SetWindowLong( g.m_hProgressBar, GWL_STYLE, style ); |
|
InvalidateRect( g.m_hDialog, NULL, TRUE ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Search for the progress dialog |
|
//----------------------------------------------------------------------------- |
|
void SearchForWindow() |
|
{ |
|
HWND hProgressDialog = FindTopLevelWindowHavingChildWithSpecifiedText( FIND_WINDOW_TEXT_PROGRESSDIALOG ); |
|
if ( !hProgressDialog ) |
|
{ |
|
return; |
|
} |
|
|
|
HWND hWndChild = NULL; |
|
|
|
EnumChildWindows( hProgressDialog, EnumFindProgressBarInDialog, (LPARAM)&hWndChild ); |
|
if ( !hWndChild ) |
|
return; |
|
|
|
g.m_bFoundWindow = true; |
|
g.m_hProgressBar = hWndChild; |
|
g.m_hDialog = hProgressDialog; |
|
|
|
// Hide the progress bar on the dialog since we'll be drawing our own |
|
ShowProgressBar( false ); |
|
} |
|
|
|
void DrawProgressBar( HDC dc, bool showTicks, float frac ) |
|
{ |
|
// Get progress bar rectangle |
|
RECT rc; |
|
GetClientRect( g.m_hProgressBar, &rc ); |
|
|
|
//InflateRect( &rc, 2, 2 ); |
|
|
|
int w = rc.right - rc.left; |
|
int h = rc.bottom - rc.top; |
|
|
|
HDC dcMemory = CreateCompatibleDC( dc ); |
|
HBITMAP bmMemory = CreateCompatibleBitmap( dc, w, h ); |
|
HBITMAP bmOld = (HBITMAP)SelectObject( dcMemory, bmMemory ); |
|
|
|
{ |
|
|
|
HBRUSH clearColor = CreateSolidBrush( GetSysColor( COLOR_BTNFACE ) ); |
|
|
|
FillRect( dcMemory, &rc, clearColor ); |
|
|
|
// Create blue tick brush |
|
HBRUSH br = CreateSolidBrush( RGB( 2, 62, 134 ) ); |
|
// Create background brush of same color as dialog background |
|
HBRUSH bg = CreateSolidBrush( RGB( 153, 175, 199) ); |
|
|
|
// Create a black / shadow colored pen to frame the progress bar |
|
HPEN blackpen; |
|
blackpen = CreatePen( PS_SOLID, 1, GetSysColor( COLOR_BTNSHADOW ) ); |
|
|
|
// Select items into dcMemory |
|
HPEN oldPen = (HPEN)SelectObject( dcMemory, blackpen ); |
|
HBRUSH oldBrush = (HBRUSH)SelectObject( dcMemory, bg ); |
|
|
|
rc.bottom = rc.top + 15; |
|
RoundRect( dcMemory, rc.left, rc.top, rc.right, rc.bottom, 5, 5 ); |
|
|
|
// Inset by one unit |
|
InflateRect( &rc, -1, -1 ); |
|
|
|
if ( showTicks ) |
|
{ |
|
HRGN clipRegion = (HRGN)CreateRectRgn( rc.left+1, rc.top, rc.right-1, rc.bottom );; |
|
|
|
SelectClipRgn( dcMemory, clipRegion ); |
|
|
|
int numblocks = 8; |
|
int blockwidth = 6; |
|
int blockgap = 2; |
|
|
|
int size = numblocks * ( blockwidth + blockgap ); |
|
|
|
// Determine width of progress bar work area |
|
int width = rc.right - rc.left + 2 * size; |
|
|
|
// Compute right edge of progress bar |
|
RECT rcProgress = rc; |
|
rcProgress.right = rcProgress.left - size + ( int )( frac * width + 0.5f ); |
|
rcProgress.left = rcProgress.right - size; |
|
|
|
for ( int block = 0; block < numblocks; ++block ) |
|
{ |
|
RECT rcBlock; |
|
rcBlock.left = rcProgress.left + block * ( blockwidth + blockgap ); |
|
rcBlock.right = rcBlock.left + blockwidth; |
|
rcBlock.top = rcProgress.top + 1; |
|
rcBlock.bottom = rcProgress.bottom - 1; |
|
|
|
// Fill in progress bar |
|
FillRect( dcMemory, &rcBlock, br ); |
|
} |
|
|
|
SelectClipRgn( dcMemory, NULL ); |
|
DeleteObject( clipRegion ); |
|
} |
|
|
|
// Restore GDI states |
|
SelectObject( dcMemory, oldBrush ); |
|
SelectObject( dcMemory, oldPen ); |
|
|
|
DeleteObject( blackpen ); |
|
|
|
DeleteObject( bg ); |
|
DeleteObject( br ); |
|
DeleteObject( clearColor ); |
|
} |
|
|
|
POINT pt; |
|
pt.x = pt.y = 0; |
|
|
|
// Convert top left of progress bar to screen space |
|
ClientToScreen( g.m_hProgressBar, &pt ); |
|
// and then back to dialog relative space |
|
ScreenToClient( g.m_hDialog, &pt ); |
|
|
|
// Offset the progress bar rect to the right position in the dialog |
|
OffsetRect( &rc, pt.x, pt.y ); |
|
|
|
BitBlt( dc, rc.left, rc.top, w, h, dcMemory, 0, 0, SRCCOPY ); |
|
|
|
SelectObject( dcMemory, bmOld ); |
|
DeleteObject( bmMemory ); |
|
|
|
DeleteObject( dcMemory ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void UpdateProgress() |
|
{ |
|
// If the "Insert next CD" or "Exit Setup" dialogs are showing, stop advancing the progress bar |
|
HWND hSwapDiskDialog = FindWindowHavingChildWithSpecifiedText( GetDesktopWindow(), FIND_WINDOW_TEXT_CHANGEDISKDIALOG ); |
|
HWND hCancelDialog = FindWindowHavingChildWithSpecifiedText( GetDesktopWindow(), FIND_WINDOW_TEXT_CANCELDIALOG ); |
|
if ( !hSwapDiskDialog && !hCancelDialog ) |
|
{ |
|
g.m_nTickCounter++; |
|
} |
|
|
|
if ( !g.m_hProgressBar || !g.m_hDialog ) |
|
return; |
|
|
|
int remainder = ( g.m_nTickCounter % PROGRESS_TICKS ); |
|
|
|
bool showTicks = ( remainder <= PROGRESS_WAIT_TICKS ) ? false : true; |
|
|
|
int currentTick = max( 0, remainder - PROGRESS_WAIT_TICKS ); |
|
int totalTicks = PROGRESS_TICKS - PROGRESS_WAIT_TICKS; |
|
|
|
float frac = ( float )( currentTick % totalTicks ) / ( float )( totalTicks - 1 ); |
|
|
|
HDC dc = GetDC( g.m_hDialog ); |
|
{ |
|
// Draw the progress bar |
|
DrawProgressBar( dc, showTicks, frac ); |
|
} |
|
ReleaseDC( g.m_hDialog, dc ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Either searches for window or updates progress |
|
// The app will quit if the dialog is found and then goes away |
|
// The app wil also quit if the dialog was not found after waiting 60 seconds |
|
//----------------------------------------------------------------------------- |
|
void Think() |
|
{ |
|
// Only think once every 100 msec |
|
DWORD curTick = GetTickCount(); |
|
if ( curTick - g.m_nLastThink < 50 ) |
|
{ |
|
return; |
|
} |
|
|
|
g.m_nLastThink = curTick; |
|
|
|
// Haven't found window yet, keep searching |
|
if ( !g.m_bFoundWindow ) |
|
{ |
|
SearchForWindow(); |
|
|
|
// Wise never got going..., abort this app... |
|
if ( ( curTick - g.m_nStartTick ) > ( SEARCH_TIMEOUT_SECONDS * 1000 ) ) |
|
{ |
|
PostQuitMessage( 0 ); |
|
} |
|
} |
|
else |
|
{ |
|
// Only redraw progress once every 100 msec |
|
UpdateProgress(); |
|
|
|
// If the progress dialog does away, exit this app immediately |
|
if ( !IsWindow( g.m_hDialog ) ) |
|
{ |
|
PostQuitMessage( 0 ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Main entry point |
|
// Input : hInstance - |
|
// hPrevInstance - |
|
// nCmdShow - |
|
// Output : int APIENTRY |
|
//----------------------------------------------------------------------------- |
|
int APIENTRY WinMain(HINSTANCE hInstance, |
|
HINSTANCE hPrevInstance, |
|
LPSTR lpCmdLine, |
|
int nCmdShow) |
|
{ |
|
|
|
HWND hwnd ; |
|
WNDCLASS wndclass ; |
|
|
|
wndclass.style = CS_HREDRAW | CS_VREDRAW ; |
|
wndclass.lpfnWndProc = DefWindowProc; |
|
wndclass.cbClsExtra = 0 ; |
|
wndclass.cbWndExtra = 0 ; |
|
wndclass.hInstance = hInstance ; |
|
wndclass.hIcon = NULL; |
|
wndclass.hCursor = NULL; |
|
wndclass.hbrBackground = NULL; |
|
wndclass.lpszMenuName = NULL ; |
|
wndclass.lpszClassName = szAppName ; |
|
|
|
if ( !RegisterClass (&wndclass) ) |
|
{ |
|
return 0 ; |
|
} |
|
|
|
hwnd = CreateWindow |
|
( |
|
szAppName, |
|
TEXT (""), |
|
WS_OVERLAPPEDWINDOW, |
|
CW_USEDEFAULT, CW_USEDEFAULT, |
|
CW_USEDEFAULT, CW_USEDEFAULT, |
|
NULL, NULL, hInstance, NULL |
|
); |
|
|
|
if (!hwnd) |
|
return 0 ; |
|
|
|
ShowWindow( hwnd, SW_HIDE ) ; |
|
|
|
// Remember when we started |
|
g.m_nStartTick = GetTickCount(); |
|
|
|
bool done = false; |
|
while ( 1 ) |
|
{ |
|
MSG msg; |
|
|
|
while ( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) ) |
|
{ |
|
if ( msg.message == WM_QUIT ) |
|
{ |
|
done = true; |
|
break; |
|
} |
|
|
|
TranslateMessage (&msg) ; |
|
DispatchMessage (&msg) ; |
|
} |
|
|
|
if ( done ) |
|
break; |
|
|
|
Think(); |
|
Sleep( 20 ); |
|
} |
|
|
|
// Restore progress bar as needed |
|
ShowProgressBar( true ); |
|
|
|
return 0; |
|
} |
|
|
|
|
|
|
|
|