#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "resource.h" //#define SNAPSHOT_TEST #ifdef SNAPSHOT_TEST #define TESTMSG(x) \ WriteToConsole((x)) #else #define TESTMSG(x) #endif //SNAPSHOT_TEST // // Default warning state for warning user check button // #define DEFAULTWARNINGSTATE BST_CHECKED #define TITLEWARNINGLEN 32 // // Name of the executable // LPWSTR g_lpszProgramName = NULL; // // Enum for all of the actions. // enum { ACTION_SHUTDOWN = 0, ACTION_RESTART = 1, ACTION_LOGOFF, ACTION_STANDBY, ACTION_DISCONNECT, ACTION_ABORT }; // // Resource IDs for actions. // DWORD g_dwActions[] = { IDS_ACTION_SHUTDOWN, IDS_ACTION_RESTART, IDS_ACTION_LOGOFF //IDS_ACTION_STANDBY, //IDS_ACTION_DISCONNECT, //IDS_ACTION_ABORT }; // // Number of actions and the action strings loaded from resource. // const int g_nActions = sizeof(g_dwActions) / sizeof(DWORD); WCHAR g_lppszActions[g_nActions][MAX_PATH]; LPWSTR g_lpszNewComputers = NULL; WCHAR g_lpszDefaultDomain[MAX_PATH] = L""; WCHAR g_lpszLocalComputerName[MAX_PATH] = L""; WCHAR g_lpszTitleWarning[TITLEWARNINGLEN]; BOOL g_bAssumeShutdown = FALSE; struct _PROVIDER{ LPWSTR szName; DWORD dwLen; }; typedef struct _SHUTDOWNREASON { DWORD dwCode; WCHAR lpName[MAX_REASON_NAME_LEN + 1]; WCHAR lpDesc[MAX_REASON_DESC_LEN + 1]; } SHUTDOWNREASON, *PSHUTDOWNREASON; PSHUTDOWNREASON g_lpReasons = NULL; DWORD g_dwReasons = 0; DWORD g_dwReasonSelect; DWORD g_dwActionSelect; typedef struct _SHUTDOWNCACHEDHWNDS { HWND hwndShutdownDialog; HWND hwndListSelectComputers; HWND hwndEditComment; HWND hwndStaticDesc; HWND hwndEditTimeout; HWND hwndButtonWarning; HWND hwndComboAction; HWND hwndComboOption; HWND hwndBtnAdd; HWND hwndBtnRemove; HWND hwndBtnBrowse; HWND hwndChkPlanned; HWND hwndButtonOK; } SHUTDOWNCACHEDHWNDS, *PSHUTDOWNCACHEDHWNDS; SHUTDOWNCACHEDHWNDS g_wins; HMODULE g_hDllInstance = NULL; typedef BOOL (*REASONBUILDPROC)(REASONDATA *, BOOL, BOOL); typedef VOID (*REASONDESTROYPROC)(REASONDATA *); BOOL GetNetworkComputers(HWND hwndList, HWND hwndProgress, LPCWSTR lpDomain); BOOL GetComputerNameFromPath(LPWSTR szPath, LPWSTR szName); VOID AdjustWindowState(); INT_PTR CALLBACK Shutdown_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK AddNew_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); INT_PTR CALLBACK Browse_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); BOOL Shutdown_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); BOOL AddNew_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); BOOL Browse_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify); BOOL Shutdown_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); BOOL Browse_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam); typedef void (*PSetThreadUILanguage)(DWORD); // // Check whether a string is all white spaces. // BOOL IsEmpty(LPCWSTR lpCWSTR) { if(!lpCWSTR) return TRUE; while(*lpCWSTR && (*lpCWSTR == '\n' || *lpCWSTR == '\t' || *lpCWSTR == '\r' || *lpCWSTR == ' ')) lpCWSTR++; if(*lpCWSTR) return FALSE; return TRUE; } // Write the string to console VOID WriteToConsole( LPWSTR pszMsg ) { HANDLE hConsole = GetStdHandle( STD_OUTPUT_HANDLE ); if ( !pszMsg || !*pszMsg ) return; DWORD dwStrLen = lstrlenW( pszMsg ); LPSTR pszAMsg = NULL; DWORD dwBytesWritten = 0; DWORD dwMode = 0; if ( (GetFileType ( hConsole ) & FILE_TYPE_CHAR ) && GetConsoleMode( hConsole, &dwMode ) ) { WriteConsoleW( hConsole, pszMsg, dwStrLen, &dwBytesWritten, 0 ); return; } // console redirect to a file. if ( !(pszAMsg = (LPSTR)LocalAlloc(LMEM_FIXED, (dwStrLen + 1) * sizeof(WCHAR) ) ) ) { return; } if (WideCharToMultiByte(GetConsoleOutputCP(), 0, pszMsg, -1, pszAMsg, dwStrLen * sizeof(WCHAR), NULL, NULL) != 0 && hConsole) { WriteFile( hConsole, pszAMsg, lstrlenA(pszAMsg), &dwBytesWritten, NULL ); } LocalFree( pszAMsg ); } // Report error. VOID report_error( DWORD error_code ) { LPVOID msgBuf = 0; FormatMessageW( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, error_code, MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language reinterpret_cast< wchar_t* >( &msgBuf ), 0, NULL); //fwprintf( stderr, L"%s : %s\n", g_lpszProgramName, reinterpret_cast< wchar_t* >( msgBuf )); WriteToConsole( reinterpret_cast (msgBuf) ); LocalFree( msgBuf ); } BOOL parse_reason_code( LPCWSTR arg, LPDWORD lpdwReason ) { // Code consists of flags:major:minor int major = 0; int minor = 0; const int state_start = 0; const int state_flags = 0; const int state_major = 1; const int state_minor = 2; const int state_null = 3; const int state_done = 4; for( int i = 0, state = state_start; state != state_done; ++i ) { switch( state ) { case state_flags : // Expecting flags switch( arg[ i ] ) { case L'U' : case L'u' : *lpdwReason |= 0x40000000; // SHTDN_REASON_FLAG_USER_DEFINED break; case L'P' : case L'p' : *lpdwReason |= 0x80000000; // SHTDN_REASON_FLAG_PLANNED break; case L':' : state = state_major; break; case 0 : // End of string (use default major and minor). state = state_done; break; default : return FALSE; } break; case state_major : // Expecting major if( arg[ i ] >= L'0' && arg[ i ] <= L'9' ) { major = major * 10 + arg[ i ] - L'0'; } else { // Make sure we only have 8 bits if( major > 0xff ) return FALSE; *lpdwReason |= major << 16; if( arg[ i ] == 0 ) { // use default minor reason. state = state_done; } if( arg[ i ] == L':' ) { state = state_minor; } else return FALSE; } break; case state_minor : // Expecting minor reason // Expecting major if( arg[ i ] >= L'0' && arg[ i ] <= L'9' ) { minor = minor * 10 + arg[ i ] - L'0'; } else { // Make sure we only have 8 bits if( minor > 0xffff ) return FALSE; *lpdwReason = ( *lpdwReason & 0xffff0000 ) | minor; if( arg[ i ] == 0 ) { return state_done; } if( arg[ i ] == L':' ) { state = state_null; } else return FALSE; } break; case state_null : // Expecting end of string if( arg[ i ] != 0 ) return FALSE; state = state_done; default : return FALSE; } } return TRUE; } // Parses an integer if it is in decimal notation. // Returns FALSE if it is malformed. BOOL parse_int( const wchar_t* arg, LPDWORD lpdwInt ) { *lpdwInt = 0; while( *arg ) { if( *arg >= L'0' && *arg <= L'9' ) { *lpdwInt = *lpdwInt * 10 + int( *arg++ - L'0' ); } else { return FALSE; } } return TRUE; } // Parse options. // Returns FALSE if the option strings are malformed. This causes the usage to be printed. BOOL parse_options( int argc, wchar_t *argv[], LPBOOL lpfLogoff, LPBOOL lpfForce, LPBOOL lpfReboot, LPBOOL lpfAbort, LPWSTR *ppServerName, LPWSTR *ppMessage, LPDWORD lpdwTimeout, LPDWORD lpdwReason ) { BOOL fShutdown = FALSE; *lpfLogoff = FALSE; *lpfForce = FALSE; *lpfReboot = FALSE; *lpfAbort = FALSE; *ppServerName = NULL; *ppMessage = NULL; *lpdwTimeout = 30; *lpdwReason = 0xFF; // // Set default reason to be planned // *lpdwReason |= 0x80000000; // SHTDN_REASON_FLAG_PLANNED for( int i = 1; i < argc; ++i ) { wchar_t* arg = argv[ i ]; switch( arg[ 0 ] ) { case L'/' : case L'-' : switch( arg[ 1 ] ) { case L'L' : case L'l' : *lpfLogoff = TRUE; if (arg[2] != 0) return FALSE; break; case L'S' : case L's' : // // Use server name if supplied (i.e. do nothing here) // fShutdown = TRUE; if( arg[ 2 ] != 0 ) return FALSE; break; case L'F' : case L'f' : *lpfForce = TRUE; if( arg[ 2 ] != 0 ) return FALSE; break; case L'R' : case L'r' : *lpfReboot = TRUE; if( arg[ 2 ] != 0 ) return FALSE; break; case L'A' : case L'a' : *lpfAbort = TRUE; if( arg[ 2 ] != 0 ) return FALSE; break; case L'T' : case L't' : // // Next arg should be number of seconds // if (++i == argc) { return FALSE; } arg = argv[i]; if( arg[ 0 ] < L'0' || arg[ 0 ] > L'9' ) return FALSE; if( !parse_int( arg, lpdwTimeout )) return FALSE; break; case L'Y' : case L'y' : // Ignore this option. break; case L'D' : case L'd' : // // Next arg should be reason code // if (++i == argc) { return FALSE; } arg = argv[i]; // //If reason code is given, we clear the planned bit. // *lpdwReason &= ~(0x80000000); // SHTDN_REASON_FLAG_PLANNED if( !parse_reason_code( arg, lpdwReason )) { return FALSE; } break; case L'C' : case L'c' : // // Next arg should be shutdown message. Make // sure only one is specified. // if (++i == argc || *ppMessage) { return FALSE; } arg = argv[i]; *ppMessage = arg; break; case L'M' : case L'm' : // // Next arg should be machine name. Make // sure only one is specified. // if (++i == argc || *ppServerName) { return FALSE; } arg = argv[i]; if (arg[0] == L'\\' && arg[1] == L'\\') { *ppServerName = arg + 2; } else { *ppServerName = arg; } break; case L'H' : case L'h' : case L'?' : default : return FALSE; } break; default : // // Junk // return FALSE; } } // // Default is to logoff // if (!fShutdown && !*lpfReboot && !*lpfAbort) { *lpfLogoff = TRUE; } // // Check for mutually exclusive options // if (*lpfAbort && (*lpfLogoff || fShutdown || *lpfReboot || *lpfForce)) { return FALSE; } if (*lpfLogoff && (*ppServerName || fShutdown || *lpfReboot)) { return FALSE; } if (fShutdown && *lpfReboot) { return FALSE; } return TRUE; } // Print out usage help string. VOID usage( VOID ) { HMODULE hModule = GetModuleHandle( NULL ); int buf_len = MAX_PATH; int new_len = 0; LPWSTR buf = NULL; LPWSTR msg = NULL; if( hModule == NULL ) { report_error( GetLastError() ); return; } buf = (LPWSTR) LocalAlloc(LMEM_FIXED, buf_len * sizeof(WCHAR)); if (buf == NULL) { report_error( GetLastError() ); return; } new_len = LoadStringW( hModule, IDS_USAGE, buf, buf_len ); // // Since LoadString doesn't tell you how much data you should have // if the buffer's too small, retry until we succeed. // while( new_len + 1 == buf_len ) { LocalFree(buf); buf_len *= 2; buf = (LPWSTR) LocalAlloc(LMEM_FIXED, buf_len * sizeof(WCHAR)); if (buf == NULL) { report_error( GetLastError() ); return; } new_len = LoadStringW( hModule, IDS_USAGE, buf, buf_len ); } if( 0 == new_len ) { report_error( GetLastError() ); LocalFree(buf); return; } //fwprintf( stderr, buf, g_lpszProgramName ); new_len = lstrlenW( buf ) + lstrlenW( g_lpszProgramName ) + 1; if ( msg = (LPWSTR)LocalAlloc(LMEM_FIXED, new_len * sizeof(WCHAR) ) ) { swprintf(msg, buf, g_lpszProgramName ); WriteToConsole( msg ); } else { report_error( GetLastError() ); } LocalFree(buf); LocalFree(msg); } // We need shutdown privileges enabled to be able to shut down our machines. BOOL enable_privileges( LPCWSTR lpServerName, BOOL fLogoff ) { NTSTATUS Status = STATUS_SUCCESS; NTSTATUS Status1 = STATUS_SUCCESS; BOOLEAN fWasEnabled; if (fLogoff) { // // No privileges to get // return TRUE; } // // We will always enable both privileges so // it can work for telnet sessions. // Status = RtlAdjustPrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &fWasEnabled); Status1 = RtlAdjustPrivilege(SE_REMOTE_SHUTDOWN_PRIVILEGE, TRUE, FALSE, &fWasEnabled); if (!NT_SUCCESS(Status) || !NT_SUCCESS(Status1)) { report_error(RtlNtStatusToDosError(Status)); report_error(RtlNtStatusToDosError(Status1)); return FALSE; } return TRUE; } VOID __cdecl wmain( int argc, wchar_t *argv[] ) { BOOL fLogoff; BOOL fForce; BOOL fReboot; BOOL fAbort; LPWSTR lpServerName; LPWSTR lpMessage; DWORD dwTimeout; DWORD dwReason; INT_PTR hResult; HINSTANCE hInstance = LoadLibrary( L"kernel32.dll" ); if ( hInstance ) { PSetThreadUILanguage SetThreadUILang = (PSetThreadUILanguage)GetProcAddress( hInstance, "SetThreadUILanguage" ); if ( SetThreadUILang ) (*SetThreadUILang)( 0 ); FreeLibrary( hInstance ); } // We use the program name for reporting errors. g_lpszProgramName = argv[ 0 ]; // // Userdomain is used as the default domain. // GetEnvironmentVariableW(L"USERDOMAIN", g_lpszDefaultDomain, MAX_PATH); GetEnvironmentVariableW(L"COMPUTERNAME", g_lpszLocalComputerName, MAX_PATH); // // if there is no arguments, we will display help. // if(argc == 1) { usage(); return; } // // If the first argument is -i or /i, we pop up UI. // if(wcsncmp(argv[1], L"-i", 2) == 0 || wcsncmp(argv[1], L"/i", 2) == 0 || wcsncmp(argv[1], L"-I", 2) == 0 || wcsncmp(argv[1], L"/I", 2) == 0) { g_hDllInstance = GetModuleHandle(NULL); if(g_hDllInstance) { hResult = DialogBoxParam(g_hDllInstance, MAKEINTRESOURCE(IDD_DIALOGSHUTDOWN), NULL, Shutdown_DialogProc, NULL); if(g_lpReasons) LocalFree((HLOCAL)g_lpReasons); } return; } // Parse the options. if( !parse_options( argc, argv, &fLogoff, &fForce, &fReboot, &fAbort, &lpServerName, &lpMessage, &dwTimeout, &dwReason )) { usage(); return; } // Get all privileges so that we can shutdown the machine. if( !enable_privileges( lpServerName, fLogoff )) { TESTMSG(L"enable_privileges failed\n"); return; } // Do the work. if( fAbort ) { if( !AbortSystemShutdownW( lpServerName )) { report_error( GetLastError( )); } } else if (fLogoff) { if (!ExitWindowsEx(fForce ? EWX_LOGOFF : (EWX_LOGOFF | EWX_FORCE), 0)) { report_error(GetLastError()); } } else { // Do the normal form. if( !InitiateSystemShutdownExW( lpServerName, lpMessage, dwTimeout, fForce, fReboot, dwReason )) { TESTMSG(L"InitiateSystemShutdownExW failed\n"); report_error( GetLastError( )); } } } // // Get the computers in the spesified domain and populate the list box // BOOL GetNetworkComputers(HWND hwndList, HWND hwndProgress, LPCWSTR lpDomain) { LPSERVER_INFO_101 pBuf = NULL; LPSERVER_INFO_101 pTmpBuf; DWORD dwLevel = 101; DWORD dwPrefMaxLen = -1; DWORD dwEntriesRead = 0; DWORD dwTotalEntries = 0; DWORD dwTotalCount = 0; DWORD dwServerType = SV_TYPE_SERVER; // all servers DWORD dwResumeHandle = 0; NET_API_STATUS nStatus; LPWSTR pszServerName = NULL; WCHAR lpWSTR[MAX_PATH]; DWORD i; WCHAR er[MAX_PATH]; WCHAR wsz[MAX_PATH]; LoadStringW(g_hDllInstance, IDS_GETCOMPUTERNAMEWAIT, er, MAX_PATH); LoadStringW(g_hDllInstance, IDS_GETCOMPUTERNAMEPROGRESS, wsz, MAX_PATH); SetWindowTextW(hwndProgress, er); nStatus = NetServerEnum(NULL, dwLevel, (LPBYTE *) &pBuf, dwPrefMaxLen, &dwEntriesRead, &dwTotalEntries, dwServerType, lpDomain, &dwResumeHandle); // // If the call succeeds, // if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA)) { if ((pTmpBuf = pBuf) != NULL) { // // Loop through the entries and // add each computer name to list. // for (i = 0; i < dwEntriesRead; i++) { if (pTmpBuf == NULL) { break; } wcscpy(lpWSTR, pTmpBuf->sv101_name); if(i+1 > 0 && (i+1) % 500 == 0){ DWORD percentage = ((i+1)*100)/dwEntriesRead; swprintf(er, L"%d%% %s(%d)...", percentage, wsz, i+1); SetWindowTextW(hwndProgress, er); } // // We don't add dups. // if(LB_ERR == ListBox_FindString(hwndList, -1, lpWSTR)) ListBox_AddString(hwndList, lpWSTR); pTmpBuf++; dwTotalCount++; } // // Display a warning if all available entries were // not enumerated, print the number actually // enumerated, and the total number available. // if (nStatus == ERROR_MORE_DATA) { LoadStringW(g_hDllInstance, IDS_GETCOMPUTERNAMEMOREDATA, er, MAX_PATH); SetWindowTextW(hwndProgress, er); } LoadStringW(g_hDllInstance, IDS_GETCOMPUTERNAMECOMPLETE, wsz, MAX_PATH); swprintf(er, L"%s %d)", wsz, dwTotalCount); SetWindowTextW(hwndProgress, er); } } else { LoadStringW(g_hDllInstance, IDS_GETCOMPUTERNAMEERROR, wsz, MAX_PATH); swprintf(er, L"%s: %d", wsz, nStatus); SetWindowTextW(hwndProgress, er); } // // Free the allocated buffer. // if (pBuf != NULL) NetApiBufferFree(pBuf); return TRUE; } // // Get computername from ADSI path // Here we only handle WinNT, LDAP, NWCOMPAT, and NDS. // BOOL GetComputerNameFromPath(LPWSTR szPath, LPWSTR szName) { static _PROVIDER p[] = { {L"LDAP://", 7}, {L"WinNT://", 8}, {L"NWCOMPAT://", 11}, {L"NDS://", 6} }; static UINT np = sizeof(p)/sizeof(_PROVIDER); LPWSTR lpsz = NULL; if(!szPath || !szName) return FALSE; for(UINT i = 0; i < np; i++) { if(wcsncmp(szPath, p[i].szName, p[i].dwLen) == 0) { switch(i) { case 0: // LDAP lpsz = wcsstr(szPath, L"CN="); if(!lpsz) return FALSE; lpsz += 3; while(*lpsz && *lpsz != ',') *szName++ = *lpsz++; *szName = 0; return TRUE; case 1: // WinNT case 2: // NWCOMPAT lpsz = szPath + p[i].dwLen; // // skip domain or provider path // while(*lpsz && *lpsz != '/') lpsz++; lpsz++; while(*lpsz && *lpsz != '/') *szName++ = *lpsz++; *szName = 0; return TRUE; case 3: // NDS lpsz = wcsstr(szPath, L"CN="); if(!lpsz) return FALSE; lpsz += 3; while(*lpsz && *lpsz != '/') *szName++ = *lpsz++; *szName = 0; return TRUE; default: return FALSE; } } } return FALSE; } // // A centralized place for adjusting window states. // VOID AdjustWindowState() { if(g_dwActionSelect == ACTION_SHUTDOWN || g_dwActionSelect == ACTION_RESTART) { EnableWindow(g_wins.hwndButtonWarning, TRUE); if (IsDlgButtonChecked(g_wins.hwndShutdownDialog, IDC_CHECKWARNING) == BST_CHECKED) EnableWindow(g_wins.hwndEditTimeout, TRUE); else EnableWindow(g_wins.hwndEditTimeout, FALSE); EnableWindow(g_wins.hwndEditComment, TRUE); if(g_bAssumeShutdown) { EnableWindow(g_wins.hwndComboOption, FALSE); EnableWindow(g_wins.hwndChkPlanned, FALSE); EnableWindow(g_wins.hwndButtonOK, TRUE); } else { EnableWindow(g_wins.hwndComboOption, TRUE); EnableWindow(g_wins.hwndChkPlanned, TRUE); if(g_dwReasonSelect != -1 && (g_lpReasons[g_dwReasonSelect].dwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED)) { if(Edit_GetTextLength(g_wins.hwndEditComment) > 0) EnableWindow(g_wins.hwndButtonOK, TRUE); else EnableWindow(g_wins.hwndButtonOK, FALSE); } else { EnableWindow(g_wins.hwndButtonOK, TRUE); } } EnableWindow(g_wins.hwndBtnAdd, TRUE); EnableWindow(g_wins.hwndBtnBrowse, TRUE); EnableWindow(g_wins.hwndBtnRemove, TRUE); EnableWindow(g_wins.hwndListSelectComputers, TRUE); } else { EnableWindow(g_wins.hwndChkPlanned, FALSE); EnableWindow(g_wins.hwndButtonWarning, FALSE); EnableWindow(g_wins.hwndBtnAdd, FALSE); EnableWindow(g_wins.hwndBtnBrowse, FALSE); EnableWindow(g_wins.hwndBtnRemove, FALSE); EnableWindow(g_wins.hwndComboOption, FALSE); EnableWindow(g_wins.hwndEditComment, FALSE); EnableWindow(g_wins.hwndEditTimeout, FALSE); EnableWindow(g_wins.hwndListSelectComputers, FALSE); EnableWindow(g_wins.hwndButtonOK, TRUE); } } // // Init dialog handler for the shutdown dialog. // BOOL Shutdown_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { HMODULE hUser32; REASONBUILDPROC buildProc; REASONDESTROYPROC DestroyProc; WCHAR lpReasonName[MAX_PATH]; REASONDATA Reasons; int i; // // Init all of the dialog items so we dont have to find // them everytime we need them. // g_wins.hwndShutdownDialog = hwnd; g_wins.hwndButtonWarning = GetDlgItem(hwnd, IDC_CHECKWARNING); g_wins.hwndComboAction = GetDlgItem(hwnd, IDC_COMBOACTION); g_wins.hwndComboOption = GetDlgItem(hwnd, IDC_COMBOOPTION); g_wins.hwndEditComment = GetDlgItem(hwnd, IDC_EDITCOMMENT); g_wins.hwndStaticDesc = GetDlgItem(hwnd, IDC_STATICDESCRIPTION); g_wins.hwndEditTimeout = GetDlgItem(hwnd, IDC_EDITTIMEOUT); g_wins.hwndListSelectComputers = GetDlgItem(hwnd, IDC_LISTSELECTEDCOMPUTERS); g_wins.hwndBtnAdd = GetDlgItem(hwnd, IDC_BUTTONADDNEW); g_wins.hwndBtnBrowse = GetDlgItem(hwnd, IDC_BUTTONBROWSE); g_wins.hwndBtnRemove = GetDlgItem(hwnd, IDC_BUTTONREMOVE); g_wins.hwndChkPlanned = GetDlgItem(hwnd, IDC_CHECK_PLANNED); g_wins.hwndButtonOK = GetDlgItem(hwnd, IDOK); if(g_wins.hwndButtonWarning == NULL || g_wins.hwndComboAction == NULL || g_wins.hwndComboOption == NULL || g_wins.hwndEditComment == NULL || g_wins.hwndStaticDesc == NULL || g_wins.hwndEditTimeout == NULL || g_wins.hwndListSelectComputers == NULL || g_wins.hwndBtnAdd == NULL || g_wins.hwndBtnBrowse == NULL || g_wins.hwndBtnRemove == NULL || g_wins.hwndChkPlanned == NULL) { report_error( GetLastError( )); EndDialog(hwnd, (int)-1); return FALSE; } LoadString(g_hDllInstance, IDS_DIALOGTITLEWARNING, g_lpszTitleWarning, TITLEWARNINGLEN); // // Default timeout is set to 20 seconds. // Edit_SetText(g_wins.hwndEditTimeout, L"20"); if(! CheckDlgButton(hwnd, IDC_CHECKWARNING, DEFAULTWARNINGSTATE)) { report_error( GetLastError( )); EndDialog(hwnd, (int)-1); return FALSE; } // // The for loop will load all of the actions into action combo. // in the meantime we save them for later use. // for(i = 0; i < g_nActions; i++) { LoadString(g_hDllInstance, g_dwActions[i], g_lppszActions[i], MAX_PATH - 1); ComboBox_AddString(g_wins.hwndComboAction, g_lppszActions[i]); if(g_dwActions[i] == IDS_ACTION_SHUTDOWN) { ComboBox_SelectString(g_wins.hwndComboAction, -1, g_lppszActions[i]); g_dwActionSelect = ACTION_SHUTDOWN; } } hUser32 = LoadLibraryW(L"user32.dll"); if(hUser32 != NULL) { // // We are using the user32.dll to get and destroy the reasons. // The reasons are added to the option combo and also cached for later use. // buildProc = (REASONBUILDPROC)GetProcAddress(hUser32, "BuildReasonArray"); DestroyProc = (REASONDESTROYPROC)GetProcAddress(hUser32, "DestroyReasons"); if(!buildProc || !DestroyProc) { FreeLibrary(hUser32); hUser32 = NULL; g_bAssumeShutdown = TRUE; } } else { g_bAssumeShutdown = TRUE; } // // If we dont have BuildReasonArray() and DestroyReasons() is user32.dll (pre whistler) // we will disable he option combo box. // if(!g_bAssumeShutdown) { if(!(*buildProc)(&Reasons, TRUE, FALSE)) { report_error( GetLastError( )); FreeLibrary(hUser32); EndDialog(hwnd, (int)-1); return FALSE; } else { int iOption; int iFirst = -1; DWORD dwCheckState = 0x0; // // Alloc space for reasons. // g_lpReasons = (PSHUTDOWNREASON)LocalAlloc(LMEM_FIXED, Reasons.cReasons * sizeof(SHUTDOWNREASON)); if(!g_lpReasons) { report_error( GetLastError( )); FreeLibrary(hUser32); EndDialog(hwnd, (int)-1); return FALSE; } // // Set the default to be planned. // CheckDlgButton(hwnd, IDC_CHECK_PLANNED, BST_CHECKED); if (IsDlgButtonChecked(hwnd, IDC_CHECK_PLANNED) == BST_CHECKED) dwCheckState = SHTDN_REASON_FLAG_PLANNED; // // Init this guy number of reasons. // g_dwReasons = Reasons.cReasons; // // Now populate the combo according the current check state. // for (iOption = 0; iOption < (int)Reasons.cReasons; iOption++) { wcscpy(g_lpReasons[iOption].lpName, Reasons.rgReasons[iOption]->szName); wcscpy(g_lpReasons[iOption].lpDesc, Reasons.rgReasons[iOption]->szDesc); g_lpReasons[iOption].dwCode = Reasons.rgReasons[iOption]->dwCode; if(((Reasons.rgReasons[iOption]->dwCode) & SHTDN_REASON_FLAG_PLANNED) == dwCheckState) { if(iFirst == -1) iFirst = iOption; ComboBox_AddString(g_wins.hwndComboOption, g_lpReasons[iOption].lpName); } } // // do a default selection (the first one), and set the description. // g_dwReasonSelect = iFirst; if(g_dwReasons > 0 && iFirst != -1) { ComboBox_SelectString(g_wins.hwndComboOption, -1, g_lpReasons[iFirst].lpName); SetWindowTextW(g_wins.hwndStaticDesc, g_lpReasons[iFirst].lpDesc); } else { return FALSE; } // // Setup the comment box. // We must fix the maximum characters. // SendMessage( g_wins.hwndEditComment, EM_LIMITTEXT, (WPARAM)MAX_REASON_COMMENT_LEN, (LPARAM)0 ); (*DestroyProc)(&Reasons); FreeLibrary(hUser32); } } AdjustWindowState(); return TRUE; } // // Init dialog handler for browse dialog // BOOL Browse_OnInitDialog(HWND hwnd, HWND hwndFocus, LPARAM lParam) { HWND hwndDomain = NULL; int cItems = 1024; int lpItems[1024]; int cActualItems; WCHAR lpDomain[MAX_PATH]; hwndDomain = GetDlgItem(hwnd, IDC_EDITDOMAIN); if(!hwndDomain) return FALSE; Edit_SetText(hwndDomain, g_lpszDefaultDomain);; return TRUE; } // // winproc for shutdown dialog // INT_PTR CALLBACK Shutdown_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_INITDIALOG, Shutdown_OnInitDialog); HANDLE_MSG(hwnd, WM_COMMAND, Shutdown_OnCommand); case (WM_SYSCOMMAND): return (Shutdown_OnCommand(hwnd, (int)(LOWORD(wParam)), (HWND)(lParam), (UINT)HIWORD(wParam)), 0L); } return FALSE; } // // winproc for Browse dialog // INT_PTR CALLBACK Browse_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_INITDIALOG, Browse_OnInitDialog); HANDLE_MSG(hwnd, WM_COMMAND, Browse_OnCommand); } return FALSE; } // // winproc for AddNew dialog // INT_PTR CALLBACK AddNew_DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) { switch (uMsg) { HANDLE_MSG(hwnd, WM_COMMAND, AddNew_OnCommand); } return FALSE; } // // Command handler for the shutdown dialog. // BOOL Shutdown_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { BOOL fHandled = FALSE; DWORD dwDlgResult = 0; HINSTANCE h; switch (id) { case IDCANCEL: if (codeNotify == BN_CLICKED) { EndDialog(hwnd, (int) dwDlgResult); } fHandled = TRUE; break; case SC_CLOSE: EndDialog(hwnd, (int) dwDlgResult); fHandled = TRUE; break; case IDC_BUTTONREMOVE: if (codeNotify == BN_CLICKED) { int cItems = 1024; int lpItems[1024]; int cActualItems; WCHAR lpServerName[MAX_PATH]; // // Get the number of selected items. If there is any remove them one by one. // cActualItems = ListBox_GetSelItems(g_wins.hwndListSelectComputers, cItems, lpItems); if(cActualItems > 0) { int i; for(i = cActualItems-1; i >= 0; i--) { ListBox_DeleteString(g_wins.hwndListSelectComputers, lpItems[i]); } } fHandled = TRUE; } break; case IDC_CHECK_PLANNED: if (codeNotify == BN_CLICKED) { int iOption; int iFirst = -1; DWORD dwCheckState = 0x0; // // Get check button state. // if (IsDlgButtonChecked(hwnd, IDC_CHECK_PLANNED) == BST_CHECKED) dwCheckState = SHTDN_REASON_FLAG_PLANNED; // // Remove all items from combo // while (ComboBox_GetCount(g_wins.hwndComboOption)) ComboBox_DeleteString(g_wins.hwndComboOption, 0); // // Now populate the combo according the current check state. // for (iOption = 0; iOption < (int)g_dwReasons; iOption++) { if(((g_lpReasons[iOption].dwCode) & SHTDN_REASON_FLAG_PLANNED) == dwCheckState) { ComboBox_AddString(g_wins.hwndComboOption, g_lpReasons[iOption].lpName); if(iFirst == -1) iFirst = iOption; } } // // do a default selection (the first one), and set the description. // if(iFirst != -1) { ComboBox_SelectString(g_wins.hwndComboOption, -1, g_lpReasons[iFirst].lpName); SetWindowTextW(g_wins.hwndStaticDesc, g_lpReasons[iFirst].lpDesc); } g_dwReasonSelect = iFirst; AdjustWindowState(); fHandled = TRUE; } break; case IDC_EDITCOMMENT: if( codeNotify == EN_CHANGE) { if(g_dwReasonSelect != -1 && (g_lpReasons[g_dwReasonSelect].dwCode & SHTDN_REASON_FLAG_COMMENT_REQUIRED)) { if(Edit_GetTextLength(g_wins.hwndEditComment) > 0) EnableWindow(g_wins.hwndButtonOK, TRUE); else EnableWindow(g_wins.hwndButtonOK, FALSE); } else { EnableWindow(g_wins.hwndButtonOK, TRUE); } fHandled = TRUE; } break; case IDC_BUTTONADDNEW: if (codeNotify == BN_CLICKED) { WCHAR lpServerName[MAX_PATH]; LPWSTR lpBuffer; DWORD dwIndex = 0; INT_PTR hResult; // // Will pop up the addnew dialog. User can type in computer names seperated // by white space. After click on OK, we will parse the computer names and // add them to the selected computer list. No duplicates will be added. // hResult = DialogBoxParam(g_hDllInstance, MAKEINTRESOURCE(IDD_DIALOG_ADDNEW), hwnd, AddNew_DialogProc, NULL); if(g_lpszNewComputers) { lpBuffer = g_lpszNewComputers; while(*lpBuffer) { lpServerName[dwIndex] = '\0'; while(*lpBuffer && *lpBuffer != '\t' && *lpBuffer != '\n' && *lpBuffer != '\r' && *lpBuffer != ' ') lpServerName[dwIndex++] = *lpBuffer++; lpServerName[dwIndex] = '\0'; if(dwIndex > 0 && LB_ERR == ListBox_FindString(g_wins.hwndListSelectComputers, -1, lpServerName)) ListBox_AddString(g_wins.hwndListSelectComputers, lpServerName); dwIndex = 0; while(*lpBuffer && (*lpBuffer == '\t' || *lpBuffer == '\n' || *lpBuffer == '\r' || *lpBuffer == ' ')) lpBuffer++; } LocalFree((HLOCAL)g_lpszNewComputers); g_lpszNewComputers = NULL; } fHandled = TRUE; } break; case IDOK: // // Here we gather all of the information and do the action. // if (codeNotify == BN_CLICKED) { int cItems = 1024; int lpItems[1024]; int cActualItems; BOOL fLogoff = FALSE; BOOL fAbort = FALSE; BOOL fForce = FALSE; BOOL fReboot = FALSE; BOOL fDisconnect = FALSE; BOOL fStandby = FALSE; DWORD dwTimeout = 0; DWORD dwReasonCode = 0; WCHAR lpServerName[MAX_PATH]; WCHAR lpMsg[MAX_REASON_COMMENT_LEN] = L""; DWORD dwCnt = 0; DWORD dwActionCode = g_dwActionSelect; WCHAR lpNoPrivilage[MAX_PATH]; WCHAR lpNotSupported[MAX_PATH]; WCHAR lpRes[MAX_PATH * 2]; WCHAR lpFailed[MAX_PATH]; WCHAR lpSuccess[MAX_PATH]; // // The default reason code is 0 and default comment is L"". // if(IsDlgButtonChecked(hwnd, IDC_CHECKWARNING)) { fForce = FALSE; lpServerName[0] = '\0'; GetWindowText(g_wins.hwndEditTimeout, lpServerName, MAX_PATH); if(lstrlen(lpServerName) == 0) dwTimeout = 0; else dwTimeout = _wtoi(lpServerName); } else { fForce = TRUE; } LoadString(g_hDllInstance, IDS_CANNOTGETPRIVILAGE, lpNoPrivilage, MAX_PATH); LoadString(g_hDllInstance, IDS_ACTIONNOTSUPPORTED, lpNotSupported, MAX_PATH); LoadString(g_hDllInstance, IDS_FAILED, lpFailed, MAX_PATH); LoadString(g_hDllInstance, IDS_SUCCEEDED, lpSuccess, MAX_PATH); GetWindowText(g_wins.hwndEditComment, lpMsg, MAX_REASON_COMMENT_LEN); if(dwActionCode == ACTION_LOGOFF) { fLogoff = TRUE; } else if (dwActionCode == ACTION_RESTART) { fReboot = TRUE; } #if 0 else if (dwActionCode == ACTION_ABORT) fAbort = TRUE; else if (dwActionCode == ACTION_STANDBY) { fStandby = TRUE; //lstrcat(lpNotSupported, lpServerName); //MessageBox(hwnd, lpNotSupported, NULL, 0); break; } else if (dwActionCode == ACTION_DISCONNECT) { fDisconnect = TRUE; //lstrcat(lpNotSupported, lpServerName); //MessageBox(hwnd, lpNotSupported, g_lpszTitleWarning, 0); break; } #endif //0 // // Logoff is only for the local computer. // Everything else will ingored. // if(fLogoff) { if (!ExitWindowsEx(fForce ? EWX_LOGOFF : (EWX_LOGOFF | EWX_FORCE), 0)) { report_error(GetLastError()); } EndDialog(hwnd, (int) dwDlgResult); break; } if(! g_bAssumeShutdown) { dwReasonCode = g_lpReasons[g_dwReasonSelect].dwCode; } dwCnt = ListBox_GetCount(g_wins.hwndListSelectComputers); if(dwCnt > 0) { DWORD i; for(i = 0; i < dwCnt; i++) { ListBox_GetText(g_wins.hwndListSelectComputers, i, lpServerName); // // Get all privileges so that we can shutdown the machine. // if( !enable_privileges(lpServerName, fLogoff)) { report_error( GetLastError( )); wcscpy(lpRes, lpNoPrivilage); wcscat(lpRes, L": "); wcscat(lpRes, lpServerName); wcscat(lpRes, L"\n"); WriteToConsole(lpRes); continue; } // // Do the work. // if( fAbort ) { if( !AbortSystemShutdown( lpServerName )) { report_error( GetLastError( )); } } else { // // Do the normal form. // if( !InitiateSystemShutdownEx( lpServerName, lpMsg, dwTimeout, fForce, fReboot, dwReasonCode )) { report_error( GetLastError( )); wcscpy(lpRes, lpFailed); wcscat(lpRes, L": "); wcscat(lpRes, lpServerName); wcscat(lpRes, L"\n"); WriteToConsole(lpRes); } else { wcscpy(lpRes, lpSuccess); wcscat(lpRes, L": "); wcscat(lpRes, lpServerName); wcscat(lpRes, L"\n"); WriteToConsole(lpRes); } } } } else { // // We will keep the dialog up in case user forget to add computers. // LoadStringW(g_hDllInstance, IDS_EMPTYLISTMSG, lpMsg, MAX_REASON_COMMENT_LEN); MessageBoxW(hwnd, lpMsg, g_lpszTitleWarning, 0); break; } EndDialog(hwnd, (int) dwDlgResult); } break; case IDC_CHECKWARNING: // // The checkbutton state decides the state of the timeout edit box. // if (codeNotify == BN_CLICKED) { if(BST_CHECKED == IsDlgButtonChecked(hwnd, IDC_CHECKWARNING)) { EnableWindow(g_wins.hwndEditTimeout, TRUE); } else { EnableWindow(g_wins.hwndEditTimeout, FALSE); } fHandled = TRUE; } break; case IDC_BUTTONBROWSE: // // Simply pop up the browse dialog. That dialog will be responsible // for adding the user selection to the selected computer list. // if (codeNotify == BN_CLICKED) { //DialogBoxParam(g_hDllInstance, MAKEINTRESOURCE(IDD_DIALOG_BROWSE), hwnd, Browse_DialogProc, NULL); HRESULT hr; ICommonQuery* pcq; OPENQUERYWINDOW oqw = { 0 }; DSQUERYINITPARAMS dqip = { 0 }; IDataObject *pdo; FORMATETC fmte = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; FORMATETC fmte2 = {(CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSQUERYPARAMS), NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL}; STGMEDIUM medium = { TYMED_NULL, NULL, NULL }; DSOBJECTNAMES *pdon; DSQUERYPARAMS *pdqp; CoInitialize(NULL); // // Windows 2000: Always use IID_ICommonQueryW explicitly. IID_ICommonQueryA is not supported. // hr = CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER, IID_ICommonQuery, (void**)&pcq); if (FAILED(hr)) { // // if failed we fall back on our browse dialog. // CoUninitialize(); DbgPrint("Cannot create ICommonQuery, fallback on simple browse.\n"); DialogBoxParam(g_hDllInstance, MAKEINTRESOURCE(IDD_DIALOG_BROWSE), hwnd, Browse_DialogProc, NULL); break; } // // Initialize the OPENQUERYWINDOW structure to indicate // we want to do a Directory Service // Query, the default form is printers and the search // should start once the window is initialized. // oqw.cbStruct = sizeof(oqw); oqw.dwFlags = OQWF_OKCANCEL|OQWF_DEFAULTFORM|OQWF_HIDEMENUS|OQWF_REMOVEFORMS; oqw.clsidHandler = CLSID_DsQuery; oqw.pHandlerParameters = &dqip; oqw.clsidDefaultForm = CLSID_DsFindComputer; // // Now initialize the handler specific parameters, // in this case, the File/Save menu item is removed // dqip.cbStruct = sizeof(dqip); dqip.dwFlags = DSQPF_NOSAVE; // // Call OpenQueryWindow, it will block until // the Query Window is dismissed, // hr = pcq->OpenQueryWindow(hwnd, &oqw, &pdo); if (FAILED(hr)) { // // if failed we fall back on our browse dialog. // pcq->Release(); CoUninitialize(); DialogBoxParam(g_hDllInstance, MAKEINTRESOURCE(IDD_DIALOG_BROWSE), hwnd, Browse_DialogProc, NULL); break; } if (!pdo) { // // if cancelled,nothing needs to be done. // pcq->Release(); CoUninitialize(); break; } // // Get the CFSTR_DSOBJECTNAMES data. For each selected, the data // includes the object class and an ADsPath to the selected object. // hr = pdo->GetData(&fmte, &medium); if(! FAILED(hr)) { pdon = (DSOBJECTNAMES*)GlobalLock(medium.hGlobal); if ( pdon ) { WCHAR szName[MAX_PATH]; LPWSTR lpsz = NULL; UINT i; for (i = 0; i < pdon->cItems; i++) { if(!GetComputerNameFromPath((LPWSTR) ((PBYTE) pdon + pdon->aObjects[i].offsetName), szName)) continue; // // We don't add dups. // if(LB_ERR == ListBox_FindString(g_wins.hwndListSelectComputers, -1, szName)) { ListBox_AddString(g_wins.hwndListSelectComputers, szName); } } GlobalUnlock(medium.hGlobal); } else { DbgPrint("GlobalLock on medium failed.\n"); } ReleaseStgMedium(&medium); } else { DbgPrint("pdo->GetData failed: 0x%x\n", hr); } // // Release resources. // pdo->Release(); pcq->Release(); CoUninitialize(); fHandled = TRUE; } break; case IDC_COMBOOPTION: // // Here is where you select shutdown reasons. // if (codeNotify == CBN_SELCHANGE) { WCHAR name[MAX_REASON_NAME_LEN + 1]; DWORD dwIndex; GetWindowText(g_wins.hwndComboOption, (LPWSTR)&name, MAX_REASON_NAME_LEN); for(dwIndex = 0; dwIndex < g_dwReasons; dwIndex++) { if(lstrcmp(name, g_lpReasons[dwIndex].lpName) == 0) { SetWindowTextW(g_wins.hwndStaticDesc, g_lpReasons[dwIndex].lpDesc); g_dwReasonSelect = dwIndex; AdjustWindowState(); break; } } fHandled = TRUE; } break; case IDC_COMBOACTION: // // Select user action here. // according to the action. some item will be disabled or enabled. // if (codeNotify == CBN_SELCHANGE) { WCHAR name[MAX_PATH]; DWORD dwIndex; GetWindowText(g_wins.hwndComboAction, (LPWSTR)&name, MAX_PATH); for(dwIndex = 0; dwIndex < g_nActions; dwIndex++) { if(lstrcmp(name, g_lppszActions[dwIndex]) == 0) { g_dwActionSelect = dwIndex; AdjustWindowState(); break; } } fHandled = TRUE; } break; } return fHandled; } // // Command handler for the addnew dialog. // It simply copy the text into a allocated buffer when OK is clicked. // BOOL AddNew_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { BOOL fHandled = FALSE; DWORD dwDlgResult = 0; HINSTANCE h; switch (id) { case IDOK: if (codeNotify == BN_CLICKED) { HWND hwndEdit; DWORD dwTextlen = 0; hwndEdit = GetDlgItem(hwnd, IDC_EDIT_ADDCOMPUTERS_COMPUTERS); if(hwndEdit != NULL) { dwTextlen = Edit_GetTextLength(hwndEdit); if(dwTextlen) { g_lpszNewComputers = (LPWSTR)LocalAlloc(LMEM_FIXED, sizeof(TCHAR) * (dwTextlen + 1)); if(g_lpszNewComputers){ Edit_GetText(hwndEdit, g_lpszNewComputers, dwTextlen + 1); } } } EndDialog(hwnd, (int) dwDlgResult); } break; case IDCANCEL: if (codeNotify == BN_CLICKED) { EndDialog(hwnd, (int) dwDlgResult); } break; } return fHandled; } // //Command handler for the browse dialog. // BOOL Browse_OnCommand(HWND hwnd, int id, HWND hwndCtl, UINT codeNotify) { BOOL fHandled = FALSE; DWORD dwDlgResult = 0; HINSTANCE h; switch (id) { case IDOK: // // Get selected computer names and add them to // selected computer list. No dups. // if (codeNotify == BN_CLICKED) { int cItems = 1024; int lpItems[1024]; int cActualItems; HWND hwndFromList; WCHAR lpServerName[MAX_PATH]; hwndFromList = GetDlgItem(hwnd, IDC_LISTNETWORKCOMPUTERS); assert(hwndFromList != NULL); cActualItems = ListBox_GetSelItems(hwndFromList, cItems, lpItems); if(cActualItems > 0) { int i; for(i = 0; i < cActualItems; i++) { ListBox_GetText(hwndFromList, lpItems[i], lpServerName); if(IsEmpty(lpServerName)) continue; if(LB_ERR == ListBox_FindString(g_wins.hwndListSelectComputers, -1, lpServerName)) ListBox_AddString(g_wins.hwndListSelectComputers, lpServerName); } } EndDialog(hwnd, (int) dwDlgResult); fHandled = TRUE; } break; case IDCANCEL: if (codeNotify == BN_CLICKED) { EndDialog(hwnd, (int)0); fHandled = TRUE; } break; case IDC_BUTTON_REFRESH: // // List the computers in the specified domain. // if (codeNotify == BN_CLICKED) { WCHAR domain[MAX_PATH]; HWND hwndDomain; HWND hwndComputers; HWND hwndProgress; hwndDomain = GetDlgItem(hwnd, IDC_EDITDOMAIN); hwndComputers = GetDlgItem(hwnd, IDC_LISTNETWORKCOMPUTERS); hwndProgress = GetDlgItem(hwnd, IDC_STATIC_PROGRESS); assert(hwndDomain != NULL && hwndComputers != NULL && hwndProgress != NULL); while(ComboBox_DeleteString(hwndComputers, 0)); lstrcpy(domain, g_lpszDefaultDomain); Edit_GetText(hwndDomain, domain, MAX_PATH); GetNetworkComputers(hwndComputers, hwndProgress, domain); fHandled = TRUE; } break; } return fHandled; }