//+--------------------------------------------------------------------------- // // Microsoft Windows // Copyright (C) Microsoft Corporation, 1997-2002. // // File: Dialogs.cpp // // Contents: // //---------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////// // Dialogs.cpp // // DlgProc for Send Console Message Snapin. // // HISTORY // 4-Aug-97 t-danm Creation. // 13 Feb 2001 bryanwal Use object picker instead of add recipients // dialog ///////////////////////////////////////////////////////////////////// #include "stdafx.h" #include #include #include "debug.h" #include "util.h" #include "dialogs.h" #include "resource.h" #include // #if 1 #define ThreadTrace0(sz) Trace0(sz) #define ThreadTrace1(sz, p1) Trace1(sz, p1) #else #define ThreadTrace0(sz) #define ThreadTrace1(sz, p1) #endif const PCWSTR CONTEXT_HELP_FILE = L"sendcmsg.hlp"; const PCWSTR HTML_HELP_FILE = L"sendcmsg.chm"; // Register clipboard formats used by the Send Console Message UINT g_cfSendConsoleMessageText = ::RegisterClipboardFormat(_T("mmc.sendcmsg.MessageText")); UINT g_cfSendConsoleMessageRecipients = ::RegisterClipboardFormat(_T("mmc.sendcmsg.MessageRecipients")); enum { iImageComputer = 0, // Generic image of a computer iImageComputerOK, iImageComputerError }; // Maximum length of a recipient (machine name) const int cchRECIPIENT_NAME_MAX = MAX_PATH; enum { COL_NAME = 0, COL_RESULT, NUM_COLS // must be last }; ///////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// /////////////////////////////////////////////////////////////////////////////// // Generic Computer Picker /////////////////////////////////////////////////////////////////////////////// //+-------------------------------------------------------------------------- // // Function: InitObjectPickerForComputers // // Synopsis: Call IDsObjectPicker::Initialize with arguments that will // set it to allow the user to pick a single computer object. // // Arguments: [pDsObjectPicker] - object picker interface instance // // Returns: Result of calling IDsObjectPicker::Initialize. // // History: 10-14-1998 DavidMun Created // //--------------------------------------------------------------------------- HRESULT InitObjectPickerForComputers(IDsObjectPicker *pDsObjectPicker) { if ( !pDsObjectPicker ) return E_POINTER; // // Prepare to initialize the object picker. // Set up the array of scope initializer structures. // static const int SCOPE_INIT_COUNT = 2; DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT]; ZeroMemory(aScopeInit, sizeof(aScopeInit)); // // 127399: JonN 10/30/00 JOINED_DOMAIN should be starting scope // aScopeInit[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); aScopeInit[0].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN | DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN; aScopeInit[0].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE; aScopeInit[0].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_COMPUTERS; aScopeInit[0].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS; aScopeInit[1].cbSize = sizeof(DSOP_SCOPE_INIT_INFO); aScopeInit[1].flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN | DSOP_SCOPE_TYPE_GLOBAL_CATALOG | DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN | DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN | DSOP_SCOPE_TYPE_WORKGROUP | DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE | DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE; aScopeInit[1].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_COMPUTERS; aScopeInit[1].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS; // // Put the scope init array into the object picker init array // DSOP_INIT_INFO initInfo; ZeroMemory(&initInfo, sizeof(initInfo)); initInfo.cbSize = sizeof(initInfo); initInfo.pwzTargetComputer = NULL; // NULL == local machine initInfo.cDsScopeInfos = SCOPE_INIT_COUNT; initInfo.aDsScopeInfos = aScopeInit; initInfo.cAttributesToFetch = 1; static PCWSTR pwszDnsHostName = L"dNSHostName"; initInfo.apwzAttributeNames = &pwszDnsHostName; // // Note object picker makes its own copy of initInfo. Also note // that Initialize may be called multiple times, last call wins. // return pDsObjectPicker->Initialize(&initInfo); } //+-------------------------------------------------------------------------- // // Function: ProcessSelectedObjects // // Synopsis: Retrieve the list of selected items from the data object // created by the object picker and print out each one. // // Arguments: [pdo] - data object returned by object picker // // History: 10-14-1998 DavidMun Created // //--------------------------------------------------------------------------- HRESULT ProcessSelectedObjects(IDataObject *pdo, PWSTR computerName, int cchLen) { Assert (pdo && computerName); if ( !pdo || !computerName) return E_POINTER; HRESULT hr = S_OK; static UINT g_cfDsObjectPicker = RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST); STGMEDIUM stgmedium = { TYMED_HGLOBAL, NULL, NULL }; FORMATETC formatetc = { (CLIPFORMAT)g_cfDsObjectPicker, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; bool fGotStgMedium = false; do { hr = pdo->GetData(&formatetc, &stgmedium); if ( SUCCEEDED (hr) ) { fGotStgMedium = true; PDS_SELECTION_LIST pDsSelList = (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal); if (!pDsSelList) { hr = HRESULT_FROM_WIN32 (GetLastError()); break; } Assert (1 == pDsSelList->cItems); if ( 1 == pDsSelList->cItems ) { PDS_SELECTION psel = &(pDsSelList->aDsSelection[0]); VARIANT* pvarDnsName = &(psel->pvarFetchedAttributes[0]); if ( NULL == pvarDnsName || VT_BSTR != pvarDnsName->vt || NULL == pvarDnsName->bstrVal || L'\0' == (pvarDnsName->bstrVal)[0] ) { // security review 3/1/2002 BryanWal // ISSUE - possible non-null termination - convert to strsafe // NTRAID# Bug9 560859 security: SendCMsg: possible non-null termination of computer name wcsncpy (computerName, psel->pwzName, cchLen); } else { // security review 3/1/2002 BryanWal // ISSUE - possible non-null termination - convert to strsafe // NTRAID# Bug9 560859 security: SendCMsg: possible non-null termination of computer name wcsncpy (computerName, pvarDnsName->bstrVal, cchLen); } } else hr = E_UNEXPECTED; GlobalUnlock(stgmedium.hGlobal); } } while (0); if (fGotStgMedium) { ReleaseStgMedium(&stgmedium); } return hr; } /////////////////////////////////////////////////////////////////////////////// // Generic method for launching a single-select computer picker // // Paremeters: // hwndParent (IN) - window handle of parent window // computerName (OUT) - computer name returned // // Returns S_OK if everything succeeded, S_FALSE if user pressed "Cancel" // ////////////////////////////////////////////////////////////////////////////// HRESULT ComputerNameFromObjectPicker (HWND hwndParent, PWSTR computerName, int cchLen) { Assert (computerName); if ( !computerName ) return E_POINTER; // // Create an instance of the object picker. The implementation in // objsel.dll is apartment model. // CComPtr spDsObjectPicker; // security review 3/1/2002 BryanWal ok HRESULT hr = CoCreateInstance(CLSID_DsObjectPicker, NULL, CLSCTX_INPROC_SERVER, IID_IDsObjectPicker, (void **) &spDsObjectPicker); if ( SUCCEEDED (hr) ) { Assert(!!spDsObjectPicker); // // Initialize the object picker to choose computers // hr = InitObjectPickerForComputers(spDsObjectPicker); if ( SUCCEEDED (hr) ) { // // Now pick a computer // CComPtr spDataObject; hr = spDsObjectPicker->InvokeDialog(hwndParent, &spDataObject); if ( S_OK == hr ) { Assert(!!spDataObject); hr = ProcessSelectedObjects(spDataObject, computerName, cchLen); } } } return hr; } ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// CSendConsoleMessageDlg::CSendConsoleMessageDlg() : m_cRefCount (0), m_hImageList (0), m_hdlg (0), m_hwndEditMessageText (0), m_hwndListviewRecipients (0) { m_DispatchInfo.pargbItemStatus = NULL; // security review 3/1/2002 BryanWal // ISSUE - can raise a STATUS_NO_MEMORY exception. consider pre-allocating at DLL_PROCESS_ATTACH // NTRAID Bug9 565939 SendCMsg: InitializeCriticalSection throws uncaught exception InitializeCriticalSection(OUT &m_DispatchInfo.cs); } CSendConsoleMessageDlg::~CSendConsoleMessageDlg() { ThreadTrace0("Destroying CSendConsoleMessageDlg object.\n"); Assert(m_hdlg == NULL); delete m_DispatchInfo.pargbItemStatus; DeleteCriticalSection(IN &m_DispatchInfo.cs); } ///////////////////////////////////////////////////////////////////// void CSendConsoleMessageDlg::AddRef() { // ISSUE - use interlocked increment // security review 3/1/2002 BryanWal // NTRAID# Bug9 561315 Security: SendCMsg: Replace critical sections with interlocked_increment EnterCriticalSection(INOUT &m_DispatchInfo.cs); Assert(m_cRefCount >= 0); Assert(HIWORD(m_cRefCount) == 0); m_cRefCount++; LeaveCriticalSection(INOUT &m_DispatchInfo.cs); } ///////////////////////////////////////////////////////////////////// void CSendConsoleMessageDlg::Release() { // Security Review 3/1/2002 BryanWal ok EnterCriticalSection(INOUT &m_DispatchInfo.cs); Assert(HIWORD(m_cRefCount) == 0); m_cRefCount--; BOOL fDeleteObject = (m_cRefCount <= 0); if (m_hdlg != NULL) { Assert(IsWindow(m_hdlg)); // Cause the UI to refresh PostMessage(m_hdlg, WM_COMMAND, MAKEWPARAM(IDC_EDIT_MESSAGE_TEXT, EN_CHANGE), 0); } LeaveCriticalSection(INOUT &m_DispatchInfo.cs); if (fDeleteObject) delete this; } ///////////////////////////////////////////////////////////////////// void CSendConsoleMessageDlg::OnInitDialog(HWND hdlg, IDataObject * pDataObject) { Assert(IsWindow(hdlg)); Assert(pDataObject != NULL); if ( !IsWindow (hdlg) || ! pDataObject ) return; m_hdlg = hdlg; m_hwndEditMessageText = GetDlgItem(m_hdlg, IDC_EDIT_MESSAGE_TEXT); m_hwndListviewRecipients = GetDlgItem(m_hdlg, IDC_LIST_RECIPIENTS); Assert(::IsWindow(m_hwndEditMessageText)); Assert(::IsWindow(m_hwndListviewRecipients)); WCHAR * pawszMessage = NULL; (void) HrExtractDataAlloc(IN pDataObject, g_cfSendConsoleMessageText, OUT (PVOID *)&pawszMessage); // Set the initial message text if ( pawszMessage ) { SetWindowTextW(m_hwndEditMessageText, pawszMessage); GlobalFree(pawszMessage); } SendMessage(m_hwndEditMessageText, EM_SETSEL, 0, 0); SetFocus(m_hwndEditMessageText); Assert(m_hImageList == NULL); m_hImageList = ImageList_LoadImage( g_hInstance, MAKEINTRESOURCE(IDB_BITMAP_COMPUTER), 16, 3, RGB(255, 0, 255), IMAGE_BITMAP, 0); Report(m_hImageList != NULL); ListView_SetImageList(m_hwndListviewRecipients, m_hImageList, LVSIL_SMALL); // Set up columns in list view int colWidths[NUM_COLS] = {200, 200}; LVCOLUMN lvColumn; WCHAR szColumnText[128]; ::ZeroMemory (&lvColumn, sizeof (lvColumn)); lvColumn.mask = LVCF_WIDTH | LVCF_TEXT; lvColumn.cx = colWidths[COL_NAME]; CchLoadString (IDS_RECIPIENT, OUT szColumnText, LENGTH(szColumnText)); lvColumn.pszText = szColumnText; int nCol = ListView_InsertColumn (m_hwndListviewRecipients, COL_NAME, &lvColumn); Assert (-1 != nCol); lvColumn.cx = colWidths[COL_RESULT]; CchLoadString (IDS_MESSAGE_STATUS, OUT szColumnText, LENGTH(szColumnText)); lvColumn.pszText = szColumnText; nCol = ListView_InsertColumn (m_hwndListviewRecipients, COL_RESULT, &lvColumn); Assert (-1 != nCol); if ( -1 != nCol ) { // Make column fill remaining space ListView_SetColumnWidth (m_hwndListviewRecipients, COL_RESULT, LVSCW_AUTOSIZE_USEHEADER); } // Get the list of recipients WCHAR * pagrwszRecipients = NULL; (void)HrExtractDataAlloc(IN pDataObject, g_cfSendConsoleMessageRecipients, OUT (PVOID *)&pagrwszRecipients); if (pagrwszRecipients == NULL) { UpdateUI(); return; } // Add the recipients to the listview const WCHAR * pszRecipient = pagrwszRecipients; while (*pszRecipient != '\0') { // Strip off leading "\\" if present. // security review 3/1/2002 BryanWal ok if ( !_wcsnicmp (pszRecipient, L"\\\\", 2) ) { pszRecipient+= 2; } AddRecipient(pszRecipient); while(*pszRecipient++ != '\0') ; // Skip until the next string } // while // NTRAID# 213370 [SENDCMSG] Accessibility - Main dialog tab stop on // Recipients listview has no visible focus indicator until object is // selected int nIndex = ListView_GetTopIndex (m_hwndListviewRecipients); ListView_SetItemState (m_hwndListviewRecipients, nIndex, LVIS_FOCUSED, LVIS_FOCUSED); GlobalFree(pagrwszRecipients); UpdateUI(); } // CSendConsoleMessageDlg::OnInitDialog() ///////////////////////////////////////////////////////////////////// void CSendConsoleMessageDlg::OnOK() { Assert(m_cRefCount == 1 && "There is already another thread running."); m_DispatchInfo.status = e_statusDlgInit; m_DispatchInfo.cErrors = 0; delete m_DispatchInfo.pargbItemStatus; m_DispatchInfo.pargbItemStatus = NULL; (void)DoDialogBox(IDD_DISPATCH_MESSAGES, m_hdlg, DlgProcDispatchMessageToRecipients, (LPARAM)this); if (m_DispatchInfo.cErrors == 0 && m_DispatchInfo.status == e_statusDlgCompleted) { // No problems dispatching the message to recipients EndDialog(m_hdlg, TRUE); // Close the dialog return; } Assert(IsWindow(m_hwndListviewRecipients)); ListView_UnselectAllItems(m_hwndListviewRecipients); if (m_DispatchInfo.cErrors > 0) { DoMessageBox(m_hdlg, IDS_ERR_CANNOT_SEND_TO_ALL_RECIPIENTS); } // We did not finished the job, so display the status to the UI if (m_DispatchInfo.pargbItemStatus == NULL) { // The progress was unable to allocate memory for the status Trace0("CSendConsoleMessageDlg::OnOK() - Out of memory.\n"); return; } // Remove all the successful items, leaving only the failed targets and // the unsent targets (in the event the user pressed Cancel). int iItem = ListView_GetItemCount (m_hwndListviewRecipients); iItem--; const BYTE * pb = m_DispatchInfo.pargbItemStatus + iItem; for (; iItem >= 0 && pb >= m_DispatchInfo.pargbItemStatus; pb--, iItem--) { if ( *pb == iImageComputerOK ) VERIFY (ListView_DeleteItem (m_hwndListviewRecipients, iItem)); } } // CSendConsoleMessageDlg::OnOK() ///////////////////////////////////////////////////////////////////// void CSendConsoleMessageDlg::DispatchMessageToRecipients() { const size_t FORMAT_BUF_LEN = 128; const int cRecipients = ListView_GetItemCount(m_hwndListviewRecipients); WCHAR szT[FORMAT_BUF_LEN + cchRECIPIENT_NAME_MAX]; WCHAR szFmtStaticRecipient[FORMAT_BUF_LEN]; // "Sending console message to %s..." WCHAR szFmtStaticMessageOf[FORMAT_BUF_LEN]; // "Sending message %d of %d." WCHAR szFmtStaticTotalErrors[FORMAT_BUF_LEN]; // "Total errors encountered\t%d." GetWindowText(m_DispatchInfo.hctlStaticRecipient, OUT szFmtStaticRecipient, LENGTH(szFmtStaticRecipient)); GetWindowText(m_DispatchInfo.hctlStaticMessageOf, szFmtStaticMessageOf, LENGTH(szFmtStaticMessageOf)); GetWindowText(m_DispatchInfo.hctlStaticErrors, OUT szFmtStaticTotalErrors, LENGTH(szFmtStaticTotalErrors)); SendMessage(m_DispatchInfo.hctlProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, cRecipients)); // // Set the image of each recipient to normal computer // ListView_UnselectAllItems(m_hwndListviewRecipients); for (int i = 0; i < cRecipients; i++) { ListView_SetItemImage(m_hwndListviewRecipients, i, iImageComputer); ListView_SetItemText(m_hwndListviewRecipients, i, COL_RESULT, L""); } UpdateUI(); // Update the other UI controls (especially OK button) // // Get the text from the edit control // int cchMessage = GetWindowTextLength(m_hwndEditMessageText) + 1; WCHAR * pawszMessage = new WCHAR[cchMessage]; if (pawszMessage != NULL) { // security review 3/1/2002 BryanWal ok - cchMessage includes null terminator GetWindowTextW(m_hwndEditMessageText, OUT pawszMessage, cchMessage); } else { cchMessage = 0; Trace0("Unable to allocate memory for message.\n"); } WCHAR wszRecipient[cchRECIPIENT_NAME_MAX]; LV_ITEMW lvItem; ::ZeroMemory (&lvItem, sizeof(lvItem)); lvItem.iItem = 0; lvItem.iSubItem = 0; lvItem.pszText = wszRecipient; lvItem.cchTextMax = LENGTH(wszRecipient); Assert(m_DispatchInfo.pargbItemStatus == NULL && "Memory Leak"); m_DispatchInfo.pargbItemStatus = new BYTE[cRecipients+1]; if (m_DispatchInfo.pargbItemStatus != NULL) { // security review 3/1/2002 BryanWal ok memset(OUT m_DispatchInfo.pargbItemStatus, iImageComputer, cRecipients+1); } else { Trace0("Unable to allocate memory for listview item status.\n"); } Assert(m_DispatchInfo.status == e_statusDlgInit); m_DispatchInfo.status = e_statusDlgDispatching; // Allow the user to cancel the dialog WCHAR szFailure[128]; CchLoadString(IDS_MESSAGE_COULD_NOT_BE_SENT, OUT szFailure, LENGTH(szFailure)); for (i = 0; i < cRecipients; i++) { ThreadTrace1("Sending message to recipient %d.\n", i + 1); // security review 3/1/2002 BryanWal ok EnterCriticalSection(INOUT &m_DispatchInfo.cs); if (m_DispatchInfo.status == e_statusUserCancel) { ThreadTrace0("DispatchMessageToRecipients() - Aborting loop @1...\n"); LeaveCriticalSection(INOUT &m_DispatchInfo.cs); break; } ListView_SelectItem(m_hwndListviewRecipients, i); ListView_EnsureVisible(m_hwndListviewRecipients, i, FALSE); lvItem.iItem = i; wszRecipient[0] = '\0'; // Get the recipient name SendMessage(m_hwndListviewRecipients, LVM_GETITEMTEXTW, i, OUT (LPARAM)&lvItem); if (m_DispatchInfo.pargbItemStatus != NULL) m_DispatchInfo.pargbItemStatus[i] = iImageComputerError; // security review 3/1/2002 BryanWal // Issue: convert to strsafe - possible buffer overflow because of static allocation HRESULT hr = ::StringCchPrintf (OUT szT, sizeof (szT)/sizeof (szT[0]), szFmtStaticRecipient, wszRecipient); Assert (SUCCEEDED (hr)); if ( FAILED (hr) ) continue; SetWindowTextW(m_DispatchInfo.hctlStaticRecipient, szT); // security review 3/1/2002 BryanWal // Issue: convert to strsafe - possible buffer overflow because of static allocation hr = ::StringCchPrintf (OUT szT, sizeof (szT)/sizeof (szT[0]), szFmtStaticMessageOf, i + 1, cRecipients); Assert (SUCCEEDED (hr)); if ( FAILED (hr) ) continue; SetWindowText(m_DispatchInfo.hctlStaticMessageOf, szT); switch ( m_DispatchInfo.cErrors ) { case 0: break; case 1: ::ShowWindow (m_DispatchInfo.hctlStaticErrors, SW_SHOW); { WCHAR sz1NotSet[128]; CchLoadString(IDS_1_RECIPIENT_NOT_CONTACTED, OUT sz1NotSet, LENGTH(sz1NotSet)); SetWindowText(m_DispatchInfo.hctlStaticErrors, sz1NotSet); } break; default: // security review 3/1/2002 BryanWal // ISSUE - convert to strsafe - possible buffer overflow because of static allocation hr = ::StringCchPrintf (OUT szT, sizeof (szT)/sizeof (szT[0]), szFmtStaticTotalErrors, m_DispatchInfo.cErrors); Assert (SUCCEEDED (hr)); if ( SUCCEEDED (hr) ) SetWindowText(m_DispatchInfo.hctlStaticErrors, szT); break; } LeaveCriticalSection(INOUT &m_DispatchInfo.cs); // Send the message to the recipient (ie, computer) NET_API_STATUS err; err = ::NetMessageBufferSend( NULL, wszRecipient, NULL, (BYTE *)pawszMessage, cchMessage * sizeof(WCHAR)); int iImage = iImageComputerOK; if (err != ERROR_SUCCESS) { Trace3("Error sending message to recipient %ws. err=%d (0x%X).\n", wszRecipient, err, err); m_DispatchInfo.cErrors++; iImage = iImageComputerError; } if (m_DispatchInfo.pargbItemStatus != NULL) m_DispatchInfo.pargbItemStatus[i] = (BYTE)iImage; // security review 3/1/2002 BryanWal - ok - nothing in here throws an exception EnterCriticalSection(INOUT &m_DispatchInfo.cs); if (m_DispatchInfo.status == e_statusUserCancel) { ThreadTrace0("DispatchMessageToRecipients() - Aborting loop @2...\n"); LeaveCriticalSection(INOUT &m_DispatchInfo.cs); break; } // // Update the listview // ListView_UnselectItem(m_hwndListviewRecipients, i); ListView_SetItemImage(m_hwndListviewRecipients, i, iImage); if ( iImage == iImageComputerError ) ListView_SetItemText(m_hwndListviewRecipients, i, COL_RESULT, szFailure); // // Update the progress dialog // SendMessage(m_DispatchInfo.hctlProgressBar, PBM_SETPOS, i + 1, 0); LeaveCriticalSection(INOUT &m_DispatchInfo.cs); } // for delete [] pawszMessage; Sleep(500); // security review 3/1/2002 BryanWal ok EnterCriticalSection(INOUT &m_DispatchInfo.cs); if (m_DispatchInfo.status != e_statusUserCancel) { // We are done dispatching the message to all the recipients // and the user did not canceled the operation. m_DispatchInfo.status = e_statusDlgCompleted; Assert(IsWindow(m_DispatchInfo.hdlg)); EndDialog(m_DispatchInfo.hdlg, TRUE); // Gracefully close the dialog } LeaveCriticalSection(INOUT &m_DispatchInfo.cs); } // CSendConsoleMessageDlg::DispatchMessageToRecipients() ///////////////////////////////////////////////////////////////////// // Add a recipient to the listview control // // Return the index of the inserted item. // int CSendConsoleMessageDlg::AddRecipient( PCWSTR pszRecipient, // IN: Machine name BOOL fSelectItem) // TRUE => Select the item that is inserted { Assert(pszRecipient != NULL); // NTRAID# 498210 [Send Console Message] User can add the same computer to // the Recipients listbox multiple times, sending multiple messages LVFINDINFO lvfi; ::ZeroMemory (&lvfi, sizeof (lvfi)); lvfi.flags = LVFI_STRING; lvfi.psz = const_cast(pszRecipient); if ( -1 == ListView_FindItem (m_hwndListviewRecipients, -1, &lvfi) ) { LV_ITEM lvItem; ::ZeroMemory (&lvItem, sizeof(lvItem)); lvItem.mask = LVIF_TEXT | LVIF_IMAGE; lvItem.iSubItem = 0; lvItem.iImage = iImageComputer; lvItem.pszText = const_cast(pszRecipient); if (fSelectItem) { lvItem.mask = LVIF_TEXT | LVIF_IMAGE |LVIF_STATE; lvItem.state = LVIS_SELECTED; } return ListView_InsertItem(m_hwndListviewRecipients, IN &lvItem); } else return -1; } // CSendConsoleMessageDlg::AddRecipient() ///////////////////////////////////////////////////////////////////// LRESULT CSendConsoleMessageDlg::OnNotify(NMHDR * pNmHdr) { Assert(pNmHdr != NULL); switch (pNmHdr->code) { case LVN_ENDLABELEDIT: { WCHAR * pszText = ((LV_DISPINFO *)pNmHdr)->item.pszText; if (pszText == NULL) break; // User canceled editing // HACK: Modifying a string which I'm not sure where it is allocated (void)FTrimString(INOUT pszText); // Check out if there is already another recipient int iItem = ListView_FindString(m_hwndListviewRecipients, pszText); if (iItem >= 0) { ListView_SelectItem(m_hwndListviewRecipients, iItem); DoMessageBox(m_hdlg, IDS_RECIPIENT_ALREADY_EXISTS); break; } // Otherwise accept the changes SetWindowLongPtr(m_hdlg, DWLP_MSGRESULT, TRUE); return TRUE; } case LVN_ITEMCHANGED: // Selection changed UpdateUI(); break; case LVN_KEYDOWN: switch (((LV_KEYDOWN *)pNmHdr)->wVKey) { case VK_INSERT: SendMessage(m_hdlg, WM_COMMAND, IDC_BUTTON_ADD_RECIPIENT, 0); break; case VK_DELETE: SendMessage(m_hdlg, WM_COMMAND, IDC_BUTTON_REMOVE_RECIPIENT, 0); break; } // switch break; case NM_CLICK: UpdateUI(); break; case NM_DBLCLK: UpdateUI(); break; } // switch return 0; } // CSendConsoleMessageDlg::OnNotify() ///////////////////////////////////////////////////////////////////// void CSendConsoleMessageDlg::EnableDlgItem(INT nIdDlgItem, BOOL fEnable) { Assert(::IsWindow(::GetDlgItem(m_hdlg, nIdDlgItem))); ::EnableWindow(::GetDlgItem(m_hdlg, nIdDlgItem), fEnable); } ///////////////////////////////////////////////////////////////////// void CSendConsoleMessageDlg::UpdateUI() { Assert(m_cRefCount > 0); int cchMessage = GetWindowTextLength(m_hwndEditMessageText); int cItems = ListView_GetItemCount(m_hwndListviewRecipients); EnableDlgItem(IDOK, (cchMessage > 0) && (cItems > 0) && (m_cRefCount == 1)); int iItemSelected = ListView_GetSelectedItem(m_hwndListviewRecipients); EnableDlgItem(IDC_BUTTON_REMOVE_RECIPIENT, iItemSelected >= 0); UpdateWindow(m_hwndListviewRecipients); } // CSendConsoleMessageDlg::UpdateUI() ///////////////////////////////////////////////////////////////////// // Dialog proc for the Send Console Message snapin. // // USAGE // DoDialogBox(IDD_SEND_CONSOLE_MESSAGE, ::GetActiveWindow(), CSendConsoleMessageDlg::DlgProc); // INT_PTR CALLBACK CSendConsoleMessageDlg::DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { CSendConsoleMessageDlg * pThis; pThis = (CSendConsoleMessageDlg *)::GetWindowLongPtr(hdlg, GWLP_USERDATA); switch (uMsg) { case WM_INITDIALOG: Assert(pThis == NULL); if (pThis != NULL) return FALSE; pThis = new CSendConsoleMessageDlg; if (pThis == NULL) { Trace0("Unable to allocate CSendConsoleMessageDlg object.\n"); return -1; } SetWindowLongPtr(hdlg, GWLP_USERDATA, (LONG_PTR)pThis); pThis->AddRef(); pThis->OnInitDialog(hdlg, (IDataObject *)lParam); SendDlgItemMessage (hdlg, IDC_EDIT_MESSAGE_TEXT, EM_LIMITTEXT, 885, 0); return FALSE; case WM_NCDESTROY: ThreadTrace0("CSendConsoleMessageDlg::DlgProc() - WM_NCDESTROY.\n"); // security review 3/1/2002 BryanWal EnterCriticalSection(INOUT &pThis->m_DispatchInfo.cs); pThis->m_hdlg = NULL; LeaveCriticalSection(INOUT &pThis->m_DispatchInfo.cs); pThis->Release(); break; case WM_COMMAND: switch (LOWORD(wParam)) { case IDOK: if (HIWORD(wParam) == BN_CLICKED) { Assert((HWND)lParam == GetDlgItem(hdlg, IDOK)); pThis->OnOK(); } break; case IDCANCEL: if (HIWORD(wParam) == BN_CLICKED) { Assert((HWND)lParam == GetDlgItem(hdlg, IDCANCEL)); EndDialog(hdlg, FALSE); } break; case IDC_EDIT_MESSAGE_TEXT: if (HIWORD(wParam) == EN_CHANGE) pThis->UpdateUI(); break; case IDC_BUTTON_ADD_RECIPIENT: { WCHAR szComputerName[MAX_PATH]; // S_FALSE means user pressed "Cancel" if ( S_OK == ComputerNameFromObjectPicker (hdlg, szComputerName, MAX_PATH) ) { pThis->AddRecipient (szComputerName, TRUE); } pThis->UpdateUI(); } break; case IDC_BUTTON_REMOVE_RECIPIENT: while (TRUE) { // Remove all the selected recipients int iItem = ListView_GetSelectedItem(pThis->m_hwndListviewRecipients); if (iItem < 0) break; ListView_DeleteItem(pThis->m_hwndListviewRecipients, iItem); } ::SetFocus(pThis->m_hwndListviewRecipients); pThis->UpdateUI(); break; case IDC_BUTTON_ADVANCED: (void)DoDialogBox(IDD_ADVANCED_MESSAGE_OPTIONS, hdlg, CSendMessageAdvancedOptionsDlg::DlgProc); break; } // switch break; case WM_NOTIFY: return pThis->OnNotify((NMHDR *)lParam); case WM_HELP: return pThis->OnHelp (lParam, IDD_SEND_CONSOLE_MESSAGE); default: return FALSE; } // switch return TRUE; } // CSendConsoleMessageDlg::DlgProc() ///////////////////////////////////////////////////////////////////// // DlgProcDispatchMessageToRecipients() // // Private dialog to indicate the progress while a background // thread dispatches a message to each recipient. // INT_PTR CALLBACK CSendConsoleMessageDlg::DlgProcDispatchMessageToRecipients(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam) { CSendConsoleMessageDlg * pThis = (CSendConsoleMessageDlg *)::GetWindowLongPtr(hdlg, GWLP_USERDATA); switch (uMsg) { case WM_INITDIALOG: Assert(pThis == NULL); if (pThis != NULL) return FALSE; pThis = (CSendConsoleMessageDlg *)lParam; SetWindowLongPtr(hdlg, GWLP_USERDATA, (LONG_PTR)pThis); Assert(pThis != NULL); Assert(pThis->m_DispatchInfo.status == e_statusDlgInit); pThis->m_DispatchInfo.hdlg = hdlg; pThis->m_DispatchInfo.hctlStaticRecipient = GetDlgItem(hdlg, IDC_STATIC_RECIPIENT); pThis->m_DispatchInfo.hctlStaticMessageOf = GetDlgItem(hdlg, IDC_STATIC_MESSAGE_OF); pThis->m_DispatchInfo.hctlStaticErrors = GetDlgItem(hdlg, IDC_STATIC_ERRORS_ENCOUNTERED); pThis->m_DispatchInfo.hctlProgressBar = GetDlgItem(hdlg, IDC_PROGRESS_MESSAGES); { DWORD dwThreadId; HANDLE hThread = ::CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProcDispatchMessageToRecipients, pThis, 0, OUT &dwThreadId); Report(hThread != NULL && "Unable to create thread"); if (hThread != NULL) { VERIFY( ::CloseHandle(hThread) ); } else { Trace0("Unable to create thread.\n"); // Prevent a potential deadlock pThis->m_DispatchInfo.status = e_statusUserCancel; // Pretend the user clicked on cancel EndDialog(hdlg, FALSE); } } break; case WM_DESTROY: // Those variables are set to NULL just in case pThis->m_DispatchInfo.hdlg = NULL; pThis->m_DispatchInfo.hctlStaticRecipient = NULL; pThis->m_DispatchInfo.hctlStaticMessageOf = NULL; pThis->m_DispatchInfo.hctlStaticErrors = NULL; pThis->m_DispatchInfo.hctlProgressBar = NULL; break; case WM_COMMAND: if (wParam == IDCANCEL) { Trace0("INFO: WM_COMMAND: IDCANCEL: User canceled operation.\n"); BOOL fEndDialog = FALSE; if (TryEnterCriticalSection(INOUT &pThis->m_DispatchInfo.cs)) { if (pThis->m_DispatchInfo.status != e_statusDlgInit) { pThis->m_DispatchInfo.status = e_statusUserCancel; fEndDialog = TRUE; } LeaveCriticalSection(INOUT &pThis->m_DispatchInfo.cs); } if (fEndDialog) { EndDialog(hdlg, FALSE); } else { ThreadTrace0("Critical section already in use. Try again...\n"); PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0); Sleep(100); } // if...else } // if break; case WM_HELP: return pThis->OnHelp (lParam, IDD_DISPATCH_MESSAGES); default: return FALSE; } // switch return TRUE; } // CSendConsoleMessageDlg::DlgProcDispatchMessageToRecipients() ///////////////////////////////////////////////////////////////////// DWORD CSendConsoleMessageDlg::ThreadProcDispatchMessageToRecipients(CSendConsoleMessageDlg * pThis) { Assert(pThis != NULL); pThis->AddRef(); Assert(pThis->m_cRefCount > 1); pThis->DispatchMessageToRecipients(); pThis->Release(); return 0; } // CSendConsoleMessageDlg::ThreadProcDispatchMessageToRecipients() #define IDH_EDIT_MESSAGE_TEXT 900 #define IDH_LIST_RECIPIENTS 901 #define IDH_BUTTON_ADD_RECIPIENT 903 #define IDH_BUTTON_REMOVE_RECIPIENT 904 const DWORD g_aHelpIDs_IDD_SEND_CONSOLE_MESSAGE[]= { IDC_EDIT_MESSAGE_TEXT, IDH_EDIT_MESSAGE_TEXT, IDOK, IDOK, IDC_LIST_RECIPIENTS, IDH_LIST_RECIPIENTS, IDC_BUTTON_ADD_RECIPIENT, IDH_BUTTON_ADD_RECIPIENT, IDC_BUTTON_REMOVE_RECIPIENT, IDH_BUTTON_REMOVE_RECIPIENT, 0, 0 }; BOOL CSendConsoleMessageDlg::OnHelp(LPARAM lParam, int nDlgIDD) { const LPHELPINFO pHelpInfo = (LPHELPINFO)lParam; if (pHelpInfo && pHelpInfo->iContextType == HELPINFO_WINDOW) { switch (nDlgIDD) { case IDD_SEND_CONSOLE_MESSAGE: DoSendConsoleMessageContextHelp ((HWND) pHelpInfo->hItemHandle); break; } } else HtmlHelpW (NULL, HTML_HELP_FILE, HH_DISPLAY_TOPIC, 0); return TRUE; } void CSendConsoleMessageDlg::DoSendConsoleMessageContextHelp (HWND hWndControl) { switch (::GetDlgCtrlID (hWndControl)) { case IDCANCEL: case IDC_BUTTON_ADVANCED: break; default: // Display context help for a control if ( !::WinHelp ( hWndControl, CONTEXT_HELP_FILE, HELP_WM_HELP, (DWORD_PTR) g_aHelpIDs_IDD_SEND_CONSOLE_MESSAGE) ) { Trace1 ("WinHelp () failed: 0x%x\n", GetLastError ()); } break; } } ///////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////// void CSendMessageAdvancedOptionsDlg::OnInitDialog(HWND hdlg) { m_hdlg = hdlg; m_fSendAutomatedMessage = FALSE; CheckDlgButton(m_hdlg, IDC_CHECK_SEND_AUTOMATED_MESSAGE, m_fSendAutomatedMessage); UpdateUI(); } ///////////////////////////////////////////////////////////////////// void CSendMessageAdvancedOptionsDlg::UpdateUI() { static const UINT rgid[] = { IDC_STATIC_RESOURCE_NAME, IDC_EDIT_RESOURCE_NAME, IDC_STATIC_SHUTDOWN_OCCURS, IDC_EDIT_SHUTDOWN_OCCURS, //IDC_SPIN_SHUTDOWN_OCCURS, IDC_STATIC_SHUTDOWN_OCCURS_UNIT, IDC_STATIC_RESEND, IDC_EDIT_RESEND, //IDC_SPIN_RESEND, IDC_STATIC_RESEND_UNIT, IDC_STATIC_RESOURCE_BACK_ONLINE, IDC_EDIT_RESOURCE_BACK_ONLINE, }; for (int i = 0; i < LENGTH(rgid); i++) { EnableWindow(GetDlgItem(m_hdlg, rgid[i]), m_fSendAutomatedMessage); } } // CSendMessageAdvancedOptionsDlg::UpdateUI() ///////////////////////////////////////////////////////////////////// INT_PTR CALLBACK CSendMessageAdvancedOptionsDlg::DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM /*lParam*/) { CSendMessageAdvancedOptionsDlg * pThis; pThis = (CSendMessageAdvancedOptionsDlg *)GetWindowLongPtr(hdlg, GWLP_USERDATA); switch (uMsg) { case WM_INITDIALOG: Assert(pThis == NULL); pThis = new CSendMessageAdvancedOptionsDlg; if (pThis == NULL) return -1; SetWindowLongPtr(hdlg, GWLP_USERDATA, (LONG_PTR)pThis); pThis->OnInitDialog(hdlg); break; case WM_COMMAND: switch (wParam) { case IDOK: EndDialog(hdlg, TRUE); break; case IDCANCEL: EndDialog(hdlg, FALSE); break; case IDC_CHECK_SEND_AUTOMATED_MESSAGE: pThis->m_fSendAutomatedMessage = IsDlgButtonChecked(hdlg, IDC_CHECK_SEND_AUTOMATED_MESSAGE); pThis->UpdateUI(); break; } // switch break; default: return FALSE; } // switch return TRUE; } // CSendMessageAdvancedOptionsDlg::DlgProc() BOOL CSendMessageAdvancedOptionsDlg::OnHelp(LPARAM /*lParam*/) { HtmlHelpW (NULL, HTML_HELP_FILE, HH_DISPLAY_TOPIC, 0); return TRUE; }