// ************************************************************************** // // Status.C // // Microsoft Confidential // Copyright (c) Microsoft Corporation 1999-2001 // All rights reserved // // This application implements a common status dialog for factory functions. // // 04/01/2001 Stephen Lodwick // Project started // // // *************************************************************************/ // // Includes // #include "factoryp.h" typedef struct _STATUSWINDOWS { HWND hStatusWindow; LPSTATUSNODE lpCurrent; struct _STATUSWINDOWS*lpNext; } STATUSWINDOWS, *LPSTATUSWINDOWS, **LPLPSTATUSWINDOWS; typedef struct _DIALOGPARAM { LPSTATUSWINDOW lpswParam; LPSTATUSNODE lpsnParam; HWND hStatusWindow; HANDLE hEvent; } DIALOGPARAM, *LPDIALOGPARAM, **LPLPDIALOGPARAM; // // Internal Function Prototype(s): // LRESULT CALLBACK StatusDlgProc(HWND, UINT, WPARAM, LPARAM); DWORD StatusDisplayList(HWND, LPSTATUSNODE); BOOL StatusAddWindow(HWND, LPLPSTATUSWINDOWS, LPSTATUSNODE, BOOL); VOID StatusCreateDialogThread(LPDIALOGPARAM); // // Internal Define(s): // #define FIRST_SPACING 7 // Spacing (pixel) between the title and the first app #define LINE_SPACING 7 // Spacing (pixel) between additional applcations #define WM_FINISHED (WM_USER + 0x0001) // User defined message to indicate the dialog should be destroyed #define WM_PROGRESS (WM_USER + 0x0002) // User defined message to indicate that we are progressing to the next app /*++ Routine: StatusAddNode Routine Description: This routine takes a string and adds that string onto the end of our linked list. Arguments: lpszNodeText - Pointer to string of text that you would like to add to list lplpsnHead - Pointer to a LPSTATUSNODE list Return Value: TRUE - Node was added to list FALSE - If the Node was not added to list --*/ BOOL StatusAddNode( LPTSTR lpszNodeText, // Text that you would like to add to the current list LPLPSTATUSNODE lplpsnHead // List that we will be adding status node to ) { LPSTATUSNODE lpsnTemp = NULL; if ( lpszNodeText && *lpszNodeText == NULLCHR ) return FALSE; // Go to the end of the list // while ( lplpsnHead && *lplpsnHead ) lplpsnHead=&((*lplpsnHead)->lpNext); if ( (lpsnTemp = (LPSTATUSNODE)MALLOC(sizeof(STATUSNODE)) ) && lpszNodeText ) { lstrcpyn(lpsnTemp->szStatusText, lpszNodeText, AS( lpsnTemp->szStatusText ) ); lpsnTemp->lpNext = NULL; // Make sure the previous node points to the new node // if ( lplpsnHead ) *lplpsnHead = lpsnTemp; return TRUE; } else return FALSE; } /*++ Routine: StatusIncrement Routine Description: This routine increments the status to the next node in the list Arguments: hStatusDialog - Handle of the StatusDialog bLastResult - Result of the last node (whether it succeeded or failed) Return Value: TRUE - Message sent to status dialog FALSE - Message was not sent to status dialog --*/ BOOL StatusIncrement( HWND hStatusDialog, BOOL bLastResult ) { // We must have a valid handle to the status dialog // if ( IsWindow(hStatusDialog) ) { // Send a message to the dialog proc to go to the next caption // SendMessage(hStatusDialog, WM_PROGRESS,(WPARAM) NULL,(LPARAM) bLastResult); return TRUE; } return FALSE; } /*++ Routine: StatusEndDialog Routine Description: This routine sends a message to a status dialog to end. Arguments: hStatusDialog - Handle of the StatusDialog Return Value: TRUE - Message sent to status dialog FALSE - Message was not sent to status dialog --*/ BOOL StatusEndDialog( HWND hStatusDialog // handle to status dialog ) { // We must have a valid handle to the status dialog // if ( IsWindow(hStatusDialog) ) { // Send a message to the dialog proc to end the dialog // SendMessage(hStatusDialog, WM_FINISHED,(WPARAM) NULL,(LPARAM) NULL); return TRUE; } return FALSE; } /*++ Routine: StatusCreateDialogThread Routine Description: This routine creates the dialog for the status window and processes until the window is ended. Arguments: lpDialog - Pointer to structure that contains information to create thread/dialog Return Value: None. --*/ VOID StatusCreateDialogThread(LPDIALOGPARAM lpDialog) { MSG msg; HWND hWnd; HANDLE hEvent = lpDialog->hEvent; hWnd = CreateDialogParam(g_hInstance, MAKEINTRESOURCE(IDD_RUN), NULL, StatusDlgProc, (LPARAM) lpDialog); while (GetMessage(&msg, hWnd, 0, 0) != 0) { TranslateMessage(&msg); DispatchMessage(&msg); } // Event has not been been signaled, null out the hwnd and signal event // if ( WaitForSingleObject(hEvent, 0) == WAIT_TIMEOUT ) { // Reset the status window handle to null, and set the event // lpDialog->hStatusWindow = NULL; SetEvent(lpDialog->hEvent); } else { // Close the event handle // CloseHandle(hEvent); } } /*++ Routine: StatusCreateDialog Routine Description: Main function that creates the status dialog Arguments: lpswStatus - Pointer to structure that contains information about Status Window lpsnStatus - Pointer to struction that contains head node for status labels Return Value: HWND - Handle to window that was created, NULL if window was not created. --*/ HWND StatusCreateDialog( LPSTATUSWINDOW lpswStatus, // structure that contains information about the window LPSTATUSNODE lpsnStatus // head node for status text ) { DIALOGPARAM dpStatus; DWORD dwThread; HANDLE hThread; HANDLE hEvent; // Determine if we have the required structures to continue, an hInstance, and some status text // if ( !lpswStatus || !lpsnStatus || !lpsnStatus->szStatusText[0]) return NULL; // Zero out memory // ZeroMemory(&dpStatus, sizeof(dpStatus)); // Create an event, non-signaled to determine if dialog is initialized // if ( hEvent = CreateEvent(NULL, TRUE, FALSE, NULL) ) { // Need a single variable for the dialog box parameter // dpStatus.lpswParam = lpswStatus; dpStatus.lpsnParam = lpsnStatus; dpStatus.hStatusWindow = NULL; dpStatus.hEvent = hEvent; // Create the thread to initialize the dialog // if (hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE) StatusCreateDialogThread, (LPVOID) &dpStatus, 0, &dwThread) ) { MSG msg; // Wait for the event from WM_INITDIALOG // WaitForSingleObject(hEvent, INFINITE); // Close the handle to the thread // CloseHandle(hThread); } // Reset and close the event only if we failed to create window, otherwise, StatusCreateDialogThread will close handle // if ( !dpStatus.hStatusWindow ) CloseHandle(hEvent); } // Return the handle to the StatusDialog // return ( dpStatus.hStatusWindow ); } /*++ Routine: StatusDlgProc Routine Description: Main dialog procedure for StatusCreateDialog. Arguments: hWnd - Handle to window uMsg - Message being sent to window uParam - Upper parameter being sent lParam - Lower parameter being sent Return Value: LRESULT - Result of message that was processed --*/ LRESULT CALLBACK StatusDlgProc(HWND hWnd, UINT uMsg, WPARAM uParam, LPARAM lParam) { static HFONT hNormFont = NULL; static HFONT hBoldFont = NULL; static HANDLE hIconSuccess = NULL; static HANDLE hIconError = NULL; static LPSTATUSWINDOWS lpStatusWindows; switch (uMsg) { case WM_INITDIALOG: { LPSTATUSWINDOW lpswWindow = NULL; LPSTATUSNODE lpHead = NULL, lpWindowHead = NULL; HANDLE hEvent = NULL; if ( lParam ) { // Determine the window, and current node // lpswWindow = ((LPDIALOGPARAM)lParam)->lpswParam; lpHead = ((LPDIALOGPARAM)lParam)->lpsnParam; hEvent = ((LPDIALOGPARAM)lParam)->hEvent; // Check to make sure that we have pointers to the required structures // if ( !lpswWindow || !lpHead || !hEvent) { PostMessage(hWnd, WM_FINISHED,(WPARAM) NULL,(LPARAM) NULL); return FALSE; } // Copy the list that was passed in, to our own buffer // while ( lpHead ) { StatusAddNode(lpHead->szStatusText, &lpWindowHead); lpHead = lpHead->lpNext; } // Add this window to our list of windows // StatusAddWindow(hWnd, &lpStatusWindows, lpWindowHead, FALSE); // Set the status window if there is one specified // if (lpswWindow->szWindowText[0] ) SetWindowText(hWnd, lpswWindow->szWindowText); // Set the status window description if there is one specified. // if ( lpswWindow->lpszDescription ) { SetDlgItemText(hWnd, IDC_TITLE, lpswWindow->lpszDescription); } // Set the status window description if there is one specified. // if ( lpswWindow->hMainIcon ) { SendDlgItemMessage(hWnd, IDC_STATUS_ICON, STM_SETICON, (WPARAM) lpswWindow->hMainIcon, 0L); } // Display the list // StatusDisplayList(hWnd, lpWindowHead); // Move the window if a position was given // if ( lpswWindow->X || lpswWindow->Y ) { // See if either coordinate is relative to the other // side of the screen. // if ( ( lpswWindow->X < 0 ) || ( lpswWindow->Y < 0 ) ) { RECT rc; // Need to get the size of our work area on the main monitor. // if ( SystemParametersInfo(SPI_GETWORKAREA, 0, &rc, 0) ) { RECT rcHwnd; POINT point; // This code will get the current window size/position, // set the point structure to the upper left coordinate // for the window if we wanted the lower right part of // window to touch the lower right part of the desktop. // GetWindowRect(hWnd, &rcHwnd); point.x = rc.right - (rcHwnd.right - rcHwnd.left); point.y = rc.bottom - (rcHwnd.bottom - rcHwnd.top); MapWindowPoints(NULL, hWnd, &point, 1); // Now if they passed in negative coordinates, add those // to our point structure so the window moves away from // the bottom or right part of the screen by the number // of units they specifed. // if ( lpswWindow->X < 0 ) { lpswWindow->X += point.x; } if ( lpswWindow->Y < 0 ) { lpswWindow->Y += point.y; } } } // Now move the window. // SetWindowPos(hWnd, 0, lpswWindow->X, lpswWindow->Y, 0, 0, SWP_NOSIZE | SWP_NOZORDER); } // Build the new fonts // if(!hNormFont) hNormFont = GetFont((HWND) GetDlgItem(hWnd, IDC_TITLE), NULL, 0, FW_NORMAL, FALSE); if(!hBoldFont) hBoldFont = GetFont((HWND) GetDlgItem(hWnd, IDC_TITLE), NULL, 0, FW_BOLD, FALSE); // Bold the first item in the list // if ( lpWindowHead && hBoldFont) SendMessage((HWND)lpWindowHead->hLabelWin, WM_SETFONT, (WPARAM) hBoldFont, MAKELPARAM(TRUE, 0)); // Now make sure we show the window now. // ShowWindow(hWnd, SW_SHOW); ((LPDIALOGPARAM)lParam)->hStatusWindow = hWnd; // Set event stating that dialog is initialized // SetEvent(hEvent); } else PostMessage(hWnd, WM_FINISHED,(WPARAM) NULL,(LPARAM) NULL); } break; case WM_PROGRESS: { // Progress to the next item, if there are no items, end the dialog // LPSTATUSWINDOWS lpswNext = lpStatusWindows; BOOL bFound = FALSE; LPSTATUSNODE lpsnTemp = NULL; // Locate the current node given a window handle // while ( lpswNext && !bFound) { if ( lpswNext->hStatusWindow == hWnd ) bFound = TRUE; else lpswNext = lpswNext->lpNext; } // If there is a current node, lets unbold it, increment and bold the next item // if ( bFound && lpswNext && lpswNext->lpCurrent ) { if ( hNormFont ) SendMessage((HWND)lpswNext->lpCurrent->hLabelWin, WM_SETFONT, (WPARAM) hNormFont, MAKELPARAM(TRUE, 0)); if ( !hIconSuccess ) hIconSuccess = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_STATUSSUCCESS), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); if ( !hIconError ) hIconError = LoadImage(g_hInstance, MAKEINTRESOURCE(IDI_STATUSERROR), IMAGE_ICON, 16, 16, LR_DEFAULTCOLOR); if ( hIconSuccess && hIconError ) SendMessage(lpswNext->lpCurrent->hIconWin,STM_SETIMAGE, (WPARAM)IMAGE_ICON, (BOOL)lParam ? (LPARAM)hIconSuccess : (LPARAM)hIconError ); // Increment to the next node in the list // lpsnTemp = lpswNext->lpCurrent; lpswNext->lpCurrent = lpswNext->lpCurrent->lpNext; // Free this memory since we allocated it // FREE(lpsnTemp); // If there is not a current node, end the dialog, otherwise, bold the item // if ( lpswNext->lpCurrent && hBoldFont) SendMessage(lpswNext->lpCurrent->hLabelWin, WM_SETFONT, (WPARAM) hBoldFont, MAKELPARAM(TRUE, 0)); } } break; case WM_FINISHED: { // Destroy the dialog // if ( hWnd ) { // If there are status windows, let's remove the one that we are ending // if ( lpStatusWindows ) StatusAddWindow(hWnd, &lpStatusWindows, NULL, TRUE); // Check to see if there are any windows left, if not, do some clean up // if ( !lpStatusWindows ) { // Delete fonts static to dlgproc // if (hNormFont) { DeleteObject(hNormFont); hNormFont = NULL; } if (hBoldFont) { DeleteObject(hBoldFont); hBoldFont = NULL; } } // Quit and end the dialog // EndDialog(hWnd, 1); } return FALSE; } break; default: return FALSE; } return FALSE; } /*++ Routine: StatusDisplayList Routine Description: Function that displays list to user interface Arguments: hWnd - Handle to Status window lpsnList - List to head Status Node Return Value: DWORD - Number of entries that were added to the dialog --*/ DWORD StatusDisplayList(HWND hWnd, LPSTATUSNODE lpsnList) { LPSTATUSNODE lpsnTempList = lpsnList; HWND hWndLabel, hWndIcon; RECT Rect, WindowRect; POINT Point; DWORD dwEntries = 0; HFONT hNormFont = NULL; LPTSTR lpTempRunName; HDC hdc; SIZE size = { 0, 0 }; LONG nWidestControl; GetWindowRect(GetDlgItem(hWnd, IDC_TITLE), &Rect); Rect.right -= Rect.left; // The width of the control. Rect.bottom -= Rect.top; // The height of the control. Point.x = Rect.left; Point.y = Rect.top; nWidestControl = Rect.right; MapWindowPoints(NULL, hWnd, &Point, 1); Point.y += FIRST_SPACING; while(lpsnTempList) { // Calculate the point of the first label window // Point.y += Rect.bottom + LINE_SPACING; // Get the size of the text in pixels // if (hdc = GetWindowDC(hWnd)) { GetTextExtentPoint32(hdc, lpsnTempList->szStatusText, lstrlen(lpsnTempList->szStatusText), &size); if (size.cx > nWidestControl) nWidestControl = size.cx; ReleaseDC(hWnd, hdc); } // Create the label window // hWndIcon = CreateWindow(_T("STATIC"), _T(""), WS_CHILD | WS_VISIBLE | SS_ICON | SS_CENTERIMAGE, Point.x - 16, Point.y - 2, 16, 16, hWnd, NULL, g_hInstance, NULL); hWndLabel = CreateWindow(_T("STATIC"), _T(""), WS_CHILD | WS_VISIBLE | SS_CENTERIMAGE, Point.x, Point.y, (Rect.right > size.cx ? Rect.right : size.cx) , Rect.bottom, hWnd, NULL, g_hInstance, NULL); // If the font is NULL, get a font // if ( hNormFont == NULL ) hNormFont = (HFONT) SendDlgItemMessage(hWnd, IDC_TITLE, WM_GETFONT, 0, 0L); // Set the font to the same font as the title // SendMessage(hWndLabel, WM_SETFONT, (WPARAM) hNormFont, MAKELPARAM(FALSE, 0)); // Set the Window text to the name of the program // SetWindowText(hWndLabel, lpsnTempList->szStatusText); // Set the hWndTemp (created from CreateWindow) in the list // lpsnTempList->hLabelWin = hWndLabel; lpsnTempList->hIconWin = hWndIcon; // Goto the next item in the list // lpsnTempList = lpsnTempList->lpNext; // Increment for each of the applications in the list // dwEntries++; } GetWindowRect(hWnd, &WindowRect); WindowRect.right = WindowRect.right - WindowRect.left + nWidestControl - Rect.right; // The width of the winodw. WindowRect.bottom -= WindowRect.top; // The height of the window. // Resize the dialog to fit the label text. // SetWindowPos(hWnd, 0, 0, 0, WindowRect.right, WindowRect.bottom + ((Rect.bottom + LINE_SPACING)*dwEntries), SWP_NOMOVE | SWP_SHOWWINDOW | SWP_NOZORDER ); return dwEntries; } /*++ =============================================================================== Routine Description: StatusAddWindow Adds a new window to the list. This function is used internally for the status dialog. Arguments: hStatusWindow - Handle to Status Window lplpswHead - Head of the Windows list lpsnHead - Current head node for Status Window bRemove - Indicates wheter we add or remove the window from the list Return Value: None =============================================================================== --*/ BOOL StatusAddWindow( HWND hStatusWindow, // Text that you would like to add to the current list LPLPSTATUSWINDOWS lplpswHead, // List that we will be adding status window to LPSTATUSNODE lpsnHead, // Head of the STATUSNODE structure BOOL bRemove // Indicates if we are removing the window from our list ) { LPLPSTATUSWINDOWS lplpswNext = lplpswHead; LPSTATUSWINDOWS lpswTemp = NULL; LPSTATUSNODE lpsnTemp = NULL; BOOL bFound = FALSE; // If there is no status window we have nothing to add // if ( !hStatusWindow ) return FALSE; // Attempt to find the window passed in, if not, we are at the end of the list // while ( *lplpswNext && !bFound) { if ( (*lplpswNext)->hStatusWindow == hStatusWindow ) bFound = TRUE; else lplpswNext=&((*lplpswNext)->lpNext); } // If we are adding it and we didn't find it in the list go ahead an add the new node // if ( !bRemove && !bFound) { if ( lpswTemp = (LPSTATUSWINDOWS)MALLOC(sizeof(STATUSWINDOWS)) ) { lpswTemp->hStatusWindow = hStatusWindow; lpswTemp->lpNext = NULL; lpswTemp->lpCurrent = lpsnHead; // Make sure the previous node points to the new node // *lplpswNext = lpswTemp; } else return FALSE; } else if ( bRemove && bFound && *lplpswNext) { // If there are nodes left in for the window, lets clean them up // if ( (*lplpswNext)->lpCurrent ) StatusDeleteNodes((*lplpswNext)->lpCurrent); // Free the window node // lpswTemp = (*lplpswNext); (*lplpswNext) = (*lplpswNext)->lpNext; FREE(lpswTemp); } else { return FALSE; } return TRUE; } /*++ =============================================================================== Routine Description: VOID StatusDeleteNodes Deletes all the Nodes in a STATUSNODE list Arguments: lpsnHead - current head of the list Return Value: None =============================================================================== --*/ VOID StatusDeleteNodes( LPSTATUSNODE lpsnHead ) { LPSTATUSNODE lpsnTemp = NULL; while ( lpsnHead ) { lpsnTemp = lpsnHead; lpsnHead = lpsnHead->lpNext; FREE(lpsnTemp); } }