Leaked source code of windows server 2003
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.

1141 lines
39 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1997-2002.
  5. //
  6. // File: Dialogs.cpp
  7. //
  8. // Contents:
  9. //
  10. //----------------------------------------------------------------------------
  11. /////////////////////////////////////////////////////////////////////
  12. // Dialogs.cpp
  13. //
  14. // DlgProc for Send Console Message Snapin.
  15. //
  16. // HISTORY
  17. // 4-Aug-97 t-danm Creation.
  18. // 13 Feb 2001 bryanwal Use object picker instead of add recipients
  19. // dialog
  20. /////////////////////////////////////////////////////////////////////
  21. #include "stdafx.h"
  22. #include <strsafe.h>
  23. #include <objsel.h>
  24. #include "debug.h"
  25. #include "util.h"
  26. #include "dialogs.h"
  27. #include "resource.h"
  28. #include <htmlhelp.h> //<mmc.h>
  29. #if 1
  30. #define ThreadTrace0(sz) Trace0(sz)
  31. #define ThreadTrace1(sz, p1) Trace1(sz, p1)
  32. #else
  33. #define ThreadTrace0(sz)
  34. #define ThreadTrace1(sz, p1)
  35. #endif
  36. const PCWSTR CONTEXT_HELP_FILE = L"sendcmsg.hlp";
  37. const PCWSTR HTML_HELP_FILE = L"sendcmsg.chm";
  38. // Register clipboard formats used by the Send Console Message
  39. UINT g_cfSendConsoleMessageText = ::RegisterClipboardFormat(_T("mmc.sendcmsg.MessageText"));
  40. UINT g_cfSendConsoleMessageRecipients = ::RegisterClipboardFormat(_T("mmc.sendcmsg.MessageRecipients"));
  41. enum
  42. {
  43. iImageComputer = 0, // Generic image of a computer
  44. iImageComputerOK,
  45. iImageComputerError
  46. };
  47. // Maximum length of a recipient (machine name)
  48. const int cchRECIPIENT_NAME_MAX = MAX_PATH;
  49. enum
  50. {
  51. COL_NAME = 0,
  52. COL_RESULT,
  53. NUM_COLS // must be last
  54. };
  55. /////////////////////////////////////////////////////////////////////
  56. ///////////////////////////////////////////////////////////////////////////////
  57. ///////////////////////////////////////////////////////////////////////////////
  58. // Generic Computer Picker
  59. ///////////////////////////////////////////////////////////////////////////////
  60. //+--------------------------------------------------------------------------
  61. //
  62. // Function: InitObjectPickerForComputers
  63. //
  64. // Synopsis: Call IDsObjectPicker::Initialize with arguments that will
  65. // set it to allow the user to pick a single computer object.
  66. //
  67. // Arguments: [pDsObjectPicker] - object picker interface instance
  68. //
  69. // Returns: Result of calling IDsObjectPicker::Initialize.
  70. //
  71. // History: 10-14-1998 DavidMun Created
  72. //
  73. //---------------------------------------------------------------------------
  74. HRESULT InitObjectPickerForComputers(IDsObjectPicker *pDsObjectPicker)
  75. {
  76. if ( !pDsObjectPicker )
  77. return E_POINTER;
  78. //
  79. // Prepare to initialize the object picker.
  80. // Set up the array of scope initializer structures.
  81. //
  82. static const int SCOPE_INIT_COUNT = 2;
  83. DSOP_SCOPE_INIT_INFO aScopeInit[SCOPE_INIT_COUNT];
  84. ZeroMemory(aScopeInit, sizeof(aScopeInit));
  85. //
  86. // 127399: JonN 10/30/00 JOINED_DOMAIN should be starting scope
  87. //
  88. aScopeInit[0].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  89. aScopeInit[0].flType = DSOP_SCOPE_TYPE_UPLEVEL_JOINED_DOMAIN
  90. | DSOP_SCOPE_TYPE_DOWNLEVEL_JOINED_DOMAIN;
  91. aScopeInit[0].flScope = DSOP_SCOPE_FLAG_STARTING_SCOPE;
  92. aScopeInit[0].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_COMPUTERS;
  93. aScopeInit[0].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS;
  94. aScopeInit[1].cbSize = sizeof(DSOP_SCOPE_INIT_INFO);
  95. aScopeInit[1].flType = DSOP_SCOPE_TYPE_ENTERPRISE_DOMAIN
  96. | DSOP_SCOPE_TYPE_GLOBAL_CATALOG
  97. | DSOP_SCOPE_TYPE_EXTERNAL_UPLEVEL_DOMAIN
  98. | DSOP_SCOPE_TYPE_EXTERNAL_DOWNLEVEL_DOMAIN
  99. | DSOP_SCOPE_TYPE_WORKGROUP
  100. | DSOP_SCOPE_TYPE_USER_ENTERED_UPLEVEL_SCOPE
  101. | DSOP_SCOPE_TYPE_USER_ENTERED_DOWNLEVEL_SCOPE;
  102. aScopeInit[1].FilterFlags.Uplevel.flBothModes = DSOP_FILTER_COMPUTERS;
  103. aScopeInit[1].FilterFlags.flDownlevel = DSOP_DOWNLEVEL_FILTER_COMPUTERS;
  104. //
  105. // Put the scope init array into the object picker init array
  106. //
  107. DSOP_INIT_INFO initInfo;
  108. ZeroMemory(&initInfo, sizeof(initInfo));
  109. initInfo.cbSize = sizeof(initInfo);
  110. initInfo.pwzTargetComputer = NULL; // NULL == local machine
  111. initInfo.cDsScopeInfos = SCOPE_INIT_COUNT;
  112. initInfo.aDsScopeInfos = aScopeInit;
  113. initInfo.cAttributesToFetch = 1;
  114. static PCWSTR pwszDnsHostName = L"dNSHostName";
  115. initInfo.apwzAttributeNames = &pwszDnsHostName;
  116. //
  117. // Note object picker makes its own copy of initInfo. Also note
  118. // that Initialize may be called multiple times, last call wins.
  119. //
  120. return pDsObjectPicker->Initialize(&initInfo);
  121. }
  122. //+--------------------------------------------------------------------------
  123. //
  124. // Function: ProcessSelectedObjects
  125. //
  126. // Synopsis: Retrieve the list of selected items from the data object
  127. // created by the object picker and print out each one.
  128. //
  129. // Arguments: [pdo] - data object returned by object picker
  130. //
  131. // History: 10-14-1998 DavidMun Created
  132. //
  133. //---------------------------------------------------------------------------
  134. HRESULT ProcessSelectedObjects(IDataObject *pdo, PWSTR computerName, int cchLen)
  135. {
  136. Assert (pdo && computerName);
  137. if ( !pdo || !computerName)
  138. return E_POINTER;
  139. HRESULT hr = S_OK;
  140. static UINT g_cfDsObjectPicker =
  141. RegisterClipboardFormat(CFSTR_DSOP_DS_SELECTION_LIST);
  142. STGMEDIUM stgmedium =
  143. {
  144. TYMED_HGLOBAL,
  145. NULL,
  146. NULL
  147. };
  148. FORMATETC formatetc =
  149. {
  150. (CLIPFORMAT)g_cfDsObjectPicker,
  151. NULL,
  152. DVASPECT_CONTENT,
  153. -1,
  154. TYMED_HGLOBAL
  155. };
  156. bool fGotStgMedium = false;
  157. do
  158. {
  159. hr = pdo->GetData(&formatetc, &stgmedium);
  160. if ( SUCCEEDED (hr) )
  161. {
  162. fGotStgMedium = true;
  163. PDS_SELECTION_LIST pDsSelList =
  164. (PDS_SELECTION_LIST) GlobalLock(stgmedium.hGlobal);
  165. if (!pDsSelList)
  166. {
  167. hr = HRESULT_FROM_WIN32 (GetLastError());
  168. break;
  169. }
  170. Assert (1 == pDsSelList->cItems);
  171. if ( 1 == pDsSelList->cItems )
  172. {
  173. PDS_SELECTION psel = &(pDsSelList->aDsSelection[0]);
  174. VARIANT* pvarDnsName = &(psel->pvarFetchedAttributes[0]);
  175. if ( NULL == pvarDnsName
  176. || VT_BSTR != pvarDnsName->vt
  177. || NULL == pvarDnsName->bstrVal
  178. || L'\0' == (pvarDnsName->bstrVal)[0] )
  179. {
  180. // security review 3/1/2002 BryanWal
  181. // ISSUE - possible non-null termination - convert to strsafe
  182. // NTRAID# Bug9 560859 security: SendCMsg: possible non-null termination of computer name
  183. wcsncpy (computerName, psel->pwzName, cchLen);
  184. }
  185. else
  186. {
  187. // security review 3/1/2002 BryanWal
  188. // ISSUE - possible non-null termination - convert to strsafe
  189. // NTRAID# Bug9 560859 security: SendCMsg: possible non-null termination of computer name
  190. wcsncpy (computerName, pvarDnsName->bstrVal, cchLen);
  191. }
  192. }
  193. else
  194. hr = E_UNEXPECTED;
  195. GlobalUnlock(stgmedium.hGlobal);
  196. }
  197. } while (0);
  198. if (fGotStgMedium)
  199. {
  200. ReleaseStgMedium(&stgmedium);
  201. }
  202. return hr;
  203. }
  204. ///////////////////////////////////////////////////////////////////////////////
  205. // Generic method for launching a single-select computer picker
  206. //
  207. // Paremeters:
  208. // hwndParent (IN) - window handle of parent window
  209. // computerName (OUT) - computer name returned
  210. //
  211. // Returns S_OK if everything succeeded, S_FALSE if user pressed "Cancel"
  212. //
  213. //////////////////////////////////////////////////////////////////////////////
  214. HRESULT ComputerNameFromObjectPicker (HWND hwndParent, PWSTR computerName, int cchLen)
  215. {
  216. Assert (computerName);
  217. if ( !computerName )
  218. return E_POINTER;
  219. //
  220. // Create an instance of the object picker. The implementation in
  221. // objsel.dll is apartment model.
  222. //
  223. CComPtr<IDsObjectPicker> spDsObjectPicker;
  224. // security review 3/1/2002 BryanWal ok
  225. HRESULT hr = CoCreateInstance(CLSID_DsObjectPicker,
  226. NULL,
  227. CLSCTX_INPROC_SERVER,
  228. IID_IDsObjectPicker,
  229. (void **) &spDsObjectPicker);
  230. if ( SUCCEEDED (hr) )
  231. {
  232. Assert(!!spDsObjectPicker);
  233. //
  234. // Initialize the object picker to choose computers
  235. //
  236. hr = InitObjectPickerForComputers(spDsObjectPicker);
  237. if ( SUCCEEDED (hr) )
  238. {
  239. //
  240. // Now pick a computer
  241. //
  242. CComPtr<IDataObject> spDataObject;
  243. hr = spDsObjectPicker->InvokeDialog(hwndParent, &spDataObject);
  244. if ( S_OK == hr )
  245. {
  246. Assert(!!spDataObject);
  247. hr = ProcessSelectedObjects(spDataObject, computerName, cchLen);
  248. }
  249. }
  250. }
  251. return hr;
  252. }
  253. /////////////////////////////////////////////////////////////////////
  254. /////////////////////////////////////////////////////////////////////
  255. CSendConsoleMessageDlg::CSendConsoleMessageDlg()
  256. : m_cRefCount (0),
  257. m_hImageList (0),
  258. m_hdlg (0),
  259. m_hwndEditMessageText (0),
  260. m_hwndListviewRecipients (0)
  261. {
  262. m_DispatchInfo.pargbItemStatus = NULL;
  263. // security review 3/1/2002 BryanWal
  264. // ISSUE - can raise a STATUS_NO_MEMORY exception. consider pre-allocating at DLL_PROCESS_ATTACH
  265. // NTRAID Bug9 565939 SendCMsg: InitializeCriticalSection throws uncaught exception
  266. InitializeCriticalSection(OUT &m_DispatchInfo.cs);
  267. }
  268. CSendConsoleMessageDlg::~CSendConsoleMessageDlg()
  269. {
  270. ThreadTrace0("Destroying CSendConsoleMessageDlg object.\n");
  271. Assert(m_hdlg == NULL);
  272. delete m_DispatchInfo.pargbItemStatus;
  273. DeleteCriticalSection(IN &m_DispatchInfo.cs);
  274. }
  275. /////////////////////////////////////////////////////////////////////
  276. void CSendConsoleMessageDlg::AddRef()
  277. {
  278. // ISSUE - use interlocked increment
  279. // security review 3/1/2002 BryanWal
  280. // NTRAID# Bug9 561315 Security: SendCMsg: Replace critical sections with interlocked_increment
  281. EnterCriticalSection(INOUT &m_DispatchInfo.cs);
  282. Assert(m_cRefCount >= 0);
  283. Assert(HIWORD(m_cRefCount) == 0);
  284. m_cRefCount++;
  285. LeaveCriticalSection(INOUT &m_DispatchInfo.cs);
  286. }
  287. /////////////////////////////////////////////////////////////////////
  288. void CSendConsoleMessageDlg::Release()
  289. {
  290. // Security Review 3/1/2002 BryanWal ok
  291. EnterCriticalSection(INOUT &m_DispatchInfo.cs);
  292. Assert(HIWORD(m_cRefCount) == 0);
  293. m_cRefCount--;
  294. BOOL fDeleteObject = (m_cRefCount <= 0);
  295. if (m_hdlg != NULL)
  296. {
  297. Assert(IsWindow(m_hdlg));
  298. // Cause the UI to refresh
  299. PostMessage(m_hdlg, WM_COMMAND, MAKEWPARAM(IDC_EDIT_MESSAGE_TEXT, EN_CHANGE), 0);
  300. }
  301. LeaveCriticalSection(INOUT &m_DispatchInfo.cs);
  302. if (fDeleteObject)
  303. delete this;
  304. }
  305. /////////////////////////////////////////////////////////////////////
  306. void CSendConsoleMessageDlg::OnInitDialog(HWND hdlg, IDataObject * pDataObject)
  307. {
  308. Assert(IsWindow(hdlg));
  309. Assert(pDataObject != NULL);
  310. if ( !IsWindow (hdlg) || ! pDataObject )
  311. return;
  312. m_hdlg = hdlg;
  313. m_hwndEditMessageText = GetDlgItem(m_hdlg, IDC_EDIT_MESSAGE_TEXT);
  314. m_hwndListviewRecipients = GetDlgItem(m_hdlg, IDC_LIST_RECIPIENTS);
  315. Assert(::IsWindow(m_hwndEditMessageText));
  316. Assert(::IsWindow(m_hwndListviewRecipients));
  317. WCHAR * pawszMessage = NULL;
  318. (void) HrExtractDataAlloc(IN pDataObject, g_cfSendConsoleMessageText, OUT (PVOID *)&pawszMessage);
  319. // Set the initial message text
  320. if ( pawszMessage )
  321. {
  322. SetWindowTextW(m_hwndEditMessageText, pawszMessage);
  323. GlobalFree(pawszMessage);
  324. }
  325. SendMessage(m_hwndEditMessageText, EM_SETSEL, 0, 0);
  326. SetFocus(m_hwndEditMessageText);
  327. Assert(m_hImageList == NULL);
  328. m_hImageList = ImageList_LoadImage(
  329. g_hInstance,
  330. MAKEINTRESOURCE(IDB_BITMAP_COMPUTER),
  331. 16, 3, RGB(255, 0, 255),
  332. IMAGE_BITMAP, 0);
  333. Report(m_hImageList != NULL);
  334. ListView_SetImageList(m_hwndListviewRecipients, m_hImageList, LVSIL_SMALL);
  335. // Set up columns in list view
  336. int colWidths[NUM_COLS] = {200, 200};
  337. LVCOLUMN lvColumn;
  338. WCHAR szColumnText[128];
  339. ::ZeroMemory (&lvColumn, sizeof (lvColumn));
  340. lvColumn.mask = LVCF_WIDTH | LVCF_TEXT;
  341. lvColumn.cx = colWidths[COL_NAME];
  342. CchLoadString (IDS_RECIPIENT, OUT szColumnText, LENGTH(szColumnText));
  343. lvColumn.pszText = szColumnText;
  344. int nCol = ListView_InsertColumn (m_hwndListviewRecipients, COL_NAME, &lvColumn);
  345. Assert (-1 != nCol);
  346. lvColumn.cx = colWidths[COL_RESULT];
  347. CchLoadString (IDS_MESSAGE_STATUS, OUT szColumnText, LENGTH(szColumnText));
  348. lvColumn.pszText = szColumnText;
  349. nCol = ListView_InsertColumn (m_hwndListviewRecipients, COL_RESULT, &lvColumn);
  350. Assert (-1 != nCol);
  351. if ( -1 != nCol )
  352. {
  353. // Make column fill remaining space
  354. ListView_SetColumnWidth (m_hwndListviewRecipients, COL_RESULT,
  355. LVSCW_AUTOSIZE_USEHEADER);
  356. }
  357. // Get the list of recipients
  358. WCHAR * pagrwszRecipients = NULL;
  359. (void)HrExtractDataAlloc(IN pDataObject, g_cfSendConsoleMessageRecipients, OUT (PVOID *)&pagrwszRecipients);
  360. if (pagrwszRecipients == NULL)
  361. {
  362. UpdateUI();
  363. return;
  364. }
  365. // Add the recipients to the listview
  366. const WCHAR * pszRecipient = pagrwszRecipients;
  367. while (*pszRecipient != '\0')
  368. {
  369. // Strip off leading "\\" if present.
  370. // security review 3/1/2002 BryanWal ok
  371. if ( !_wcsnicmp (pszRecipient, L"\\\\", 2) )
  372. {
  373. pszRecipient+= 2;
  374. }
  375. AddRecipient(pszRecipient);
  376. while(*pszRecipient++ != '\0')
  377. ; // Skip until the next string
  378. } // while
  379. // NTRAID# 213370 [SENDCMSG] Accessibility - Main dialog tab stop on
  380. // Recipients listview has no visible focus indicator until object is
  381. // selected
  382. int nIndex = ListView_GetTopIndex (m_hwndListviewRecipients);
  383. ListView_SetItemState (m_hwndListviewRecipients, nIndex, LVIS_FOCUSED,
  384. LVIS_FOCUSED);
  385. GlobalFree(pagrwszRecipients);
  386. UpdateUI();
  387. } // CSendConsoleMessageDlg::OnInitDialog()
  388. /////////////////////////////////////////////////////////////////////
  389. void CSendConsoleMessageDlg::OnOK()
  390. {
  391. Assert(m_cRefCount == 1 && "There is already another thread running.");
  392. m_DispatchInfo.status = e_statusDlgInit;
  393. m_DispatchInfo.cErrors = 0;
  394. delete m_DispatchInfo.pargbItemStatus;
  395. m_DispatchInfo.pargbItemStatus = NULL;
  396. (void)DoDialogBox(IDD_DISPATCH_MESSAGES, m_hdlg,
  397. DlgProcDispatchMessageToRecipients, (LPARAM)this);
  398. if (m_DispatchInfo.cErrors == 0 && m_DispatchInfo.status == e_statusDlgCompleted)
  399. {
  400. // No problems dispatching the message to recipients
  401. EndDialog(m_hdlg, TRUE); // Close the dialog
  402. return;
  403. }
  404. Assert(IsWindow(m_hwndListviewRecipients));
  405. ListView_UnselectAllItems(m_hwndListviewRecipients);
  406. if (m_DispatchInfo.cErrors > 0)
  407. {
  408. DoMessageBox(m_hdlg, IDS_ERR_CANNOT_SEND_TO_ALL_RECIPIENTS);
  409. }
  410. // We did not finished the job, so display the status to the UI
  411. if (m_DispatchInfo.pargbItemStatus == NULL)
  412. {
  413. // The progress was unable to allocate memory for the status
  414. Trace0("CSendConsoleMessageDlg::OnOK() - Out of memory.\n");
  415. return;
  416. }
  417. // Remove all the successful items, leaving only the failed targets and
  418. // the unsent targets (in the event the user pressed Cancel).
  419. int iItem = ListView_GetItemCount (m_hwndListviewRecipients);
  420. iItem--;
  421. const BYTE * pb = m_DispatchInfo.pargbItemStatus + iItem;
  422. for (; iItem >= 0 && pb >= m_DispatchInfo.pargbItemStatus;
  423. pb--, iItem--)
  424. {
  425. if ( *pb == iImageComputerOK )
  426. VERIFY (ListView_DeleteItem (m_hwndListviewRecipients, iItem));
  427. }
  428. } // CSendConsoleMessageDlg::OnOK()
  429. /////////////////////////////////////////////////////////////////////
  430. void CSendConsoleMessageDlg::DispatchMessageToRecipients()
  431. {
  432. const size_t FORMAT_BUF_LEN = 128;
  433. const int cRecipients = ListView_GetItemCount(m_hwndListviewRecipients);
  434. WCHAR szT[FORMAT_BUF_LEN + cchRECIPIENT_NAME_MAX];
  435. WCHAR szFmtStaticRecipient[FORMAT_BUF_LEN]; // "Sending console message to %s..."
  436. WCHAR szFmtStaticMessageOf[FORMAT_BUF_LEN]; // "Sending message %d of %d."
  437. WCHAR szFmtStaticTotalErrors[FORMAT_BUF_LEN]; // "Total errors encountered\t%d."
  438. GetWindowText(m_DispatchInfo.hctlStaticRecipient, OUT szFmtStaticRecipient, LENGTH(szFmtStaticRecipient));
  439. GetWindowText(m_DispatchInfo.hctlStaticMessageOf, szFmtStaticMessageOf, LENGTH(szFmtStaticMessageOf));
  440. GetWindowText(m_DispatchInfo.hctlStaticErrors, OUT szFmtStaticTotalErrors, LENGTH(szFmtStaticTotalErrors));
  441. SendMessage(m_DispatchInfo.hctlProgressBar, PBM_SETRANGE, 0, MAKELPARAM(0, cRecipients));
  442. //
  443. // Set the image of each recipient to normal computer
  444. //
  445. ListView_UnselectAllItems(m_hwndListviewRecipients);
  446. for (int i = 0; i < cRecipients; i++)
  447. {
  448. ListView_SetItemImage(m_hwndListviewRecipients, i, iImageComputer);
  449. ListView_SetItemText(m_hwndListviewRecipients, i, COL_RESULT, L"");
  450. }
  451. UpdateUI(); // Update the other UI controls (especially OK button)
  452. //
  453. // Get the text from the edit control
  454. //
  455. int cchMessage = GetWindowTextLength(m_hwndEditMessageText) + 1;
  456. WCHAR * pawszMessage = new WCHAR[cchMessage];
  457. if (pawszMessage != NULL)
  458. {
  459. // security review 3/1/2002 BryanWal ok - cchMessage includes null terminator
  460. GetWindowTextW(m_hwndEditMessageText, OUT pawszMessage, cchMessage);
  461. }
  462. else
  463. {
  464. cchMessage = 0;
  465. Trace0("Unable to allocate memory for message.\n");
  466. }
  467. WCHAR wszRecipient[cchRECIPIENT_NAME_MAX];
  468. LV_ITEMW lvItem;
  469. ::ZeroMemory (&lvItem, sizeof(lvItem));
  470. lvItem.iItem = 0;
  471. lvItem.iSubItem = 0;
  472. lvItem.pszText = wszRecipient;
  473. lvItem.cchTextMax = LENGTH(wszRecipient);
  474. Assert(m_DispatchInfo.pargbItemStatus == NULL && "Memory Leak");
  475. m_DispatchInfo.pargbItemStatus = new BYTE[cRecipients+1];
  476. if (m_DispatchInfo.pargbItemStatus != NULL)
  477. {
  478. // security review 3/1/2002 BryanWal ok
  479. memset(OUT m_DispatchInfo.pargbItemStatus, iImageComputer, cRecipients+1);
  480. }
  481. else
  482. {
  483. Trace0("Unable to allocate memory for listview item status.\n");
  484. }
  485. Assert(m_DispatchInfo.status == e_statusDlgInit);
  486. m_DispatchInfo.status = e_statusDlgDispatching; // Allow the user to cancel the dialog
  487. WCHAR szFailure[128];
  488. CchLoadString(IDS_MESSAGE_COULD_NOT_BE_SENT, OUT szFailure,
  489. LENGTH(szFailure));
  490. for (i = 0; i < cRecipients; i++)
  491. {
  492. ThreadTrace1("Sending message to recipient %d.\n", i + 1);
  493. // security review 3/1/2002 BryanWal ok
  494. EnterCriticalSection(INOUT &m_DispatchInfo.cs);
  495. if (m_DispatchInfo.status == e_statusUserCancel)
  496. {
  497. ThreadTrace0("DispatchMessageToRecipients() - Aborting loop @1...\n");
  498. LeaveCriticalSection(INOUT &m_DispatchInfo.cs);
  499. break;
  500. }
  501. ListView_SelectItem(m_hwndListviewRecipients, i);
  502. ListView_EnsureVisible(m_hwndListviewRecipients, i, FALSE);
  503. lvItem.iItem = i;
  504. wszRecipient[0] = '\0';
  505. // Get the recipient name
  506. SendMessage(m_hwndListviewRecipients, LVM_GETITEMTEXTW, i, OUT (LPARAM)&lvItem);
  507. if (m_DispatchInfo.pargbItemStatus != NULL)
  508. m_DispatchInfo.pargbItemStatus[i] = iImageComputerError;
  509. // security review 3/1/2002 BryanWal
  510. // Issue: convert to strsafe - possible buffer overflow because of static allocation
  511. HRESULT hr = ::StringCchPrintf (OUT szT, sizeof (szT)/sizeof (szT[0]), szFmtStaticRecipient, wszRecipient);
  512. Assert (SUCCEEDED (hr));
  513. if ( FAILED (hr) )
  514. continue;
  515. SetWindowTextW(m_DispatchInfo.hctlStaticRecipient, szT);
  516. // security review 3/1/2002 BryanWal
  517. // Issue: convert to strsafe - possible buffer overflow because of static allocation
  518. hr = ::StringCchPrintf (OUT szT, sizeof (szT)/sizeof (szT[0]), szFmtStaticMessageOf, i + 1, cRecipients);
  519. Assert (SUCCEEDED (hr));
  520. if ( FAILED (hr) )
  521. continue;
  522. SetWindowText(m_DispatchInfo.hctlStaticMessageOf, szT);
  523. switch ( m_DispatchInfo.cErrors )
  524. {
  525. case 0:
  526. break;
  527. case 1:
  528. ::ShowWindow (m_DispatchInfo.hctlStaticErrors, SW_SHOW);
  529. {
  530. WCHAR sz1NotSet[128];
  531. CchLoadString(IDS_1_RECIPIENT_NOT_CONTACTED, OUT sz1NotSet,
  532. LENGTH(sz1NotSet));
  533. SetWindowText(m_DispatchInfo.hctlStaticErrors, sz1NotSet);
  534. }
  535. break;
  536. default:
  537. // security review 3/1/2002 BryanWal
  538. // ISSUE - convert to strsafe - possible buffer overflow because of static allocation
  539. hr = ::StringCchPrintf (OUT szT, sizeof (szT)/sizeof (szT[0]), szFmtStaticTotalErrors, m_DispatchInfo.cErrors);
  540. Assert (SUCCEEDED (hr));
  541. if ( SUCCEEDED (hr) )
  542. SetWindowText(m_DispatchInfo.hctlStaticErrors, szT);
  543. break;
  544. }
  545. LeaveCriticalSection(INOUT &m_DispatchInfo.cs);
  546. // Send the message to the recipient (ie, computer)
  547. NET_API_STATUS err;
  548. err = ::NetMessageBufferSend(
  549. NULL,
  550. wszRecipient,
  551. NULL,
  552. (BYTE *)pawszMessage,
  553. cchMessage * sizeof(WCHAR));
  554. int iImage = iImageComputerOK;
  555. if (err != ERROR_SUCCESS)
  556. {
  557. Trace3("Error sending message to recipient %ws. err=%d (0x%X).\n", wszRecipient, err, err);
  558. m_DispatchInfo.cErrors++;
  559. iImage = iImageComputerError;
  560. }
  561. if (m_DispatchInfo.pargbItemStatus != NULL)
  562. m_DispatchInfo.pargbItemStatus[i] = (BYTE)iImage;
  563. // security review 3/1/2002 BryanWal - ok - nothing in here throws an exception
  564. EnterCriticalSection(INOUT &m_DispatchInfo.cs);
  565. if (m_DispatchInfo.status == e_statusUserCancel)
  566. {
  567. ThreadTrace0("DispatchMessageToRecipients() - Aborting loop @2...\n");
  568. LeaveCriticalSection(INOUT &m_DispatchInfo.cs);
  569. break;
  570. }
  571. //
  572. // Update the listview
  573. //
  574. ListView_UnselectItem(m_hwndListviewRecipients, i);
  575. ListView_SetItemImage(m_hwndListviewRecipients, i, iImage);
  576. if ( iImage == iImageComputerError )
  577. ListView_SetItemText(m_hwndListviewRecipients, i, COL_RESULT,
  578. szFailure);
  579. //
  580. // Update the progress dialog
  581. //
  582. SendMessage(m_DispatchInfo.hctlProgressBar, PBM_SETPOS, i + 1, 0);
  583. LeaveCriticalSection(INOUT &m_DispatchInfo.cs);
  584. } // for
  585. delete [] pawszMessage;
  586. Sleep(500);
  587. // security review 3/1/2002 BryanWal ok
  588. EnterCriticalSection(INOUT &m_DispatchInfo.cs);
  589. if (m_DispatchInfo.status != e_statusUserCancel)
  590. {
  591. // We are done dispatching the message to all the recipients
  592. // and the user did not canceled the operation.
  593. m_DispatchInfo.status = e_statusDlgCompleted;
  594. Assert(IsWindow(m_DispatchInfo.hdlg));
  595. EndDialog(m_DispatchInfo.hdlg, TRUE); // Gracefully close the dialog
  596. }
  597. LeaveCriticalSection(INOUT &m_DispatchInfo.cs);
  598. } // CSendConsoleMessageDlg::DispatchMessageToRecipients()
  599. /////////////////////////////////////////////////////////////////////
  600. // Add a recipient to the listview control
  601. //
  602. // Return the index of the inserted item.
  603. //
  604. int CSendConsoleMessageDlg::AddRecipient(
  605. PCWSTR pszRecipient, // IN: Machine name
  606. BOOL fSelectItem) // TRUE => Select the item that is inserted
  607. {
  608. Assert(pszRecipient != NULL);
  609. // NTRAID# 498210 [Send Console Message] User can add the same computer to
  610. // the Recipients listbox multiple times, sending multiple messages
  611. LVFINDINFO lvfi;
  612. ::ZeroMemory (&lvfi, sizeof (lvfi));
  613. lvfi.flags = LVFI_STRING;
  614. lvfi.psz = const_cast<WCHAR *>(pszRecipient);
  615. if ( -1 == ListView_FindItem (m_hwndListviewRecipients, -1, &lvfi) )
  616. {
  617. LV_ITEM lvItem;
  618. ::ZeroMemory (&lvItem, sizeof(lvItem));
  619. lvItem.mask = LVIF_TEXT | LVIF_IMAGE;
  620. lvItem.iSubItem = 0;
  621. lvItem.iImage = iImageComputer;
  622. lvItem.pszText = const_cast<WCHAR *>(pszRecipient);
  623. if (fSelectItem)
  624. {
  625. lvItem.mask = LVIF_TEXT | LVIF_IMAGE |LVIF_STATE;
  626. lvItem.state = LVIS_SELECTED;
  627. }
  628. return ListView_InsertItem(m_hwndListviewRecipients, IN &lvItem);
  629. }
  630. else
  631. return -1;
  632. } // CSendConsoleMessageDlg::AddRecipient()
  633. /////////////////////////////////////////////////////////////////////
  634. LRESULT CSendConsoleMessageDlg::OnNotify(NMHDR * pNmHdr)
  635. {
  636. Assert(pNmHdr != NULL);
  637. switch (pNmHdr->code)
  638. {
  639. case LVN_ENDLABELEDIT:
  640. {
  641. WCHAR * pszText = ((LV_DISPINFO *)pNmHdr)->item.pszText;
  642. if (pszText == NULL)
  643. break; // User canceled editing
  644. // HACK: Modifying a string which I'm not sure where it is allocated
  645. (void)FTrimString(INOUT pszText);
  646. // Check out if there is already another recipient
  647. int iItem = ListView_FindString(m_hwndListviewRecipients, pszText);
  648. if (iItem >= 0)
  649. {
  650. ListView_SelectItem(m_hwndListviewRecipients, iItem);
  651. DoMessageBox(m_hdlg, IDS_RECIPIENT_ALREADY_EXISTS);
  652. break;
  653. }
  654. // Otherwise accept the changes
  655. SetWindowLongPtr(m_hdlg, DWLP_MSGRESULT, TRUE);
  656. return TRUE;
  657. }
  658. case LVN_ITEMCHANGED: // Selection changed
  659. UpdateUI();
  660. break;
  661. case LVN_KEYDOWN:
  662. switch (((LV_KEYDOWN *)pNmHdr)->wVKey)
  663. {
  664. case VK_INSERT:
  665. SendMessage(m_hdlg, WM_COMMAND, IDC_BUTTON_ADD_RECIPIENT, 0);
  666. break;
  667. case VK_DELETE:
  668. SendMessage(m_hdlg, WM_COMMAND, IDC_BUTTON_REMOVE_RECIPIENT, 0);
  669. break;
  670. } // switch
  671. break;
  672. case NM_CLICK:
  673. UpdateUI();
  674. break;
  675. case NM_DBLCLK:
  676. UpdateUI();
  677. break;
  678. } // switch
  679. return 0;
  680. } // CSendConsoleMessageDlg::OnNotify()
  681. /////////////////////////////////////////////////////////////////////
  682. void CSendConsoleMessageDlg::EnableDlgItem(INT nIdDlgItem, BOOL fEnable)
  683. {
  684. Assert(::IsWindow(::GetDlgItem(m_hdlg, nIdDlgItem)));
  685. ::EnableWindow(::GetDlgItem(m_hdlg, nIdDlgItem), fEnable);
  686. }
  687. /////////////////////////////////////////////////////////////////////
  688. void CSendConsoleMessageDlg::UpdateUI()
  689. {
  690. Assert(m_cRefCount > 0);
  691. int cchMessage = GetWindowTextLength(m_hwndEditMessageText);
  692. int cItems = ListView_GetItemCount(m_hwndListviewRecipients);
  693. EnableDlgItem(IDOK, (cchMessage > 0) && (cItems > 0) && (m_cRefCount == 1));
  694. int iItemSelected = ListView_GetSelectedItem(m_hwndListviewRecipients);
  695. EnableDlgItem(IDC_BUTTON_REMOVE_RECIPIENT, iItemSelected >= 0);
  696. UpdateWindow(m_hwndListviewRecipients);
  697. } // CSendConsoleMessageDlg::UpdateUI()
  698. /////////////////////////////////////////////////////////////////////
  699. // Dialog proc for the Send Console Message snapin.
  700. //
  701. // USAGE
  702. // DoDialogBox(IDD_SEND_CONSOLE_MESSAGE, ::GetActiveWindow(), CSendConsoleMessageDlg::DlgProc);
  703. //
  704. INT_PTR CALLBACK CSendConsoleMessageDlg::DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  705. {
  706. CSendConsoleMessageDlg * pThis;
  707. pThis = (CSendConsoleMessageDlg *)::GetWindowLongPtr(hdlg, GWLP_USERDATA);
  708. switch (uMsg)
  709. {
  710. case WM_INITDIALOG:
  711. Assert(pThis == NULL);
  712. if (pThis != NULL)
  713. return FALSE;
  714. pThis = new CSendConsoleMessageDlg;
  715. if (pThis == NULL)
  716. {
  717. Trace0("Unable to allocate CSendConsoleMessageDlg object.\n");
  718. return -1;
  719. }
  720. SetWindowLongPtr(hdlg, GWLP_USERDATA, (LONG_PTR)pThis);
  721. pThis->AddRef();
  722. pThis->OnInitDialog(hdlg, (IDataObject *)lParam);
  723. SendDlgItemMessage (hdlg, IDC_EDIT_MESSAGE_TEXT, EM_LIMITTEXT, 885, 0);
  724. return FALSE;
  725. case WM_NCDESTROY:
  726. ThreadTrace0("CSendConsoleMessageDlg::DlgProc() - WM_NCDESTROY.\n");
  727. // security review 3/1/2002 BryanWal
  728. EnterCriticalSection(INOUT &pThis->m_DispatchInfo.cs);
  729. pThis->m_hdlg = NULL;
  730. LeaveCriticalSection(INOUT &pThis->m_DispatchInfo.cs);
  731. pThis->Release();
  732. break;
  733. case WM_COMMAND:
  734. switch (LOWORD(wParam))
  735. {
  736. case IDOK:
  737. if (HIWORD(wParam) == BN_CLICKED)
  738. {
  739. Assert((HWND)lParam == GetDlgItem(hdlg, IDOK));
  740. pThis->OnOK();
  741. }
  742. break;
  743. case IDCANCEL:
  744. if (HIWORD(wParam) == BN_CLICKED)
  745. {
  746. Assert((HWND)lParam == GetDlgItem(hdlg, IDCANCEL));
  747. EndDialog(hdlg, FALSE);
  748. }
  749. break;
  750. case IDC_EDIT_MESSAGE_TEXT:
  751. if (HIWORD(wParam) == EN_CHANGE)
  752. pThis->UpdateUI();
  753. break;
  754. case IDC_BUTTON_ADD_RECIPIENT:
  755. {
  756. WCHAR szComputerName[MAX_PATH];
  757. // S_FALSE means user pressed "Cancel"
  758. if ( S_OK == ComputerNameFromObjectPicker (hdlg,
  759. szComputerName, MAX_PATH) )
  760. {
  761. pThis->AddRecipient (szComputerName, TRUE);
  762. }
  763. pThis->UpdateUI();
  764. }
  765. break;
  766. case IDC_BUTTON_REMOVE_RECIPIENT:
  767. while (TRUE)
  768. {
  769. // Remove all the selected recipients
  770. int iItem = ListView_GetSelectedItem(pThis->m_hwndListviewRecipients);
  771. if (iItem < 0)
  772. break;
  773. ListView_DeleteItem(pThis->m_hwndListviewRecipients, iItem);
  774. }
  775. ::SetFocus(pThis->m_hwndListviewRecipients);
  776. pThis->UpdateUI();
  777. break;
  778. case IDC_BUTTON_ADVANCED:
  779. (void)DoDialogBox(IDD_ADVANCED_MESSAGE_OPTIONS, hdlg, CSendMessageAdvancedOptionsDlg::DlgProc);
  780. break;
  781. } // switch
  782. break;
  783. case WM_NOTIFY:
  784. return pThis->OnNotify((NMHDR *)lParam);
  785. case WM_HELP:
  786. return pThis->OnHelp (lParam, IDD_SEND_CONSOLE_MESSAGE);
  787. default:
  788. return FALSE;
  789. } // switch
  790. return TRUE;
  791. } // CSendConsoleMessageDlg::DlgProc()
  792. /////////////////////////////////////////////////////////////////////
  793. // DlgProcDispatchMessageToRecipients()
  794. //
  795. // Private dialog to indicate the progress while a background
  796. // thread dispatches a message to each recipient.
  797. //
  798. INT_PTR CALLBACK CSendConsoleMessageDlg::DlgProcDispatchMessageToRecipients(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  799. {
  800. CSendConsoleMessageDlg * pThis = (CSendConsoleMessageDlg *)::GetWindowLongPtr(hdlg, GWLP_USERDATA);
  801. switch (uMsg)
  802. {
  803. case WM_INITDIALOG:
  804. Assert(pThis == NULL);
  805. if (pThis != NULL)
  806. return FALSE;
  807. pThis = (CSendConsoleMessageDlg *)lParam;
  808. SetWindowLongPtr(hdlg, GWLP_USERDATA, (LONG_PTR)pThis);
  809. Assert(pThis != NULL);
  810. Assert(pThis->m_DispatchInfo.status == e_statusDlgInit);
  811. pThis->m_DispatchInfo.hdlg = hdlg;
  812. pThis->m_DispatchInfo.hctlStaticRecipient = GetDlgItem(hdlg, IDC_STATIC_RECIPIENT);
  813. pThis->m_DispatchInfo.hctlStaticMessageOf = GetDlgItem(hdlg, IDC_STATIC_MESSAGE_OF);
  814. pThis->m_DispatchInfo.hctlStaticErrors = GetDlgItem(hdlg, IDC_STATIC_ERRORS_ENCOUNTERED);
  815. pThis->m_DispatchInfo.hctlProgressBar = GetDlgItem(hdlg, IDC_PROGRESS_MESSAGES);
  816. {
  817. DWORD dwThreadId;
  818. HANDLE hThread = ::CreateThread(NULL, 0,
  819. (LPTHREAD_START_ROUTINE)ThreadProcDispatchMessageToRecipients, pThis, 0, OUT &dwThreadId);
  820. Report(hThread != NULL && "Unable to create thread");
  821. if (hThread != NULL)
  822. {
  823. VERIFY( ::CloseHandle(hThread) );
  824. }
  825. else
  826. {
  827. Trace0("Unable to create thread.\n");
  828. // Prevent a potential deadlock
  829. pThis->m_DispatchInfo.status = e_statusUserCancel; // Pretend the user clicked on cancel
  830. EndDialog(hdlg, FALSE);
  831. }
  832. }
  833. break;
  834. case WM_DESTROY:
  835. // Those variables are set to NULL just in case
  836. pThis->m_DispatchInfo.hdlg = NULL;
  837. pThis->m_DispatchInfo.hctlStaticRecipient = NULL;
  838. pThis->m_DispatchInfo.hctlStaticMessageOf = NULL;
  839. pThis->m_DispatchInfo.hctlStaticErrors = NULL;
  840. pThis->m_DispatchInfo.hctlProgressBar = NULL;
  841. break;
  842. case WM_COMMAND:
  843. if (wParam == IDCANCEL)
  844. {
  845. Trace0("INFO: WM_COMMAND: IDCANCEL: User canceled operation.\n");
  846. BOOL fEndDialog = FALSE;
  847. if (TryEnterCriticalSection(INOUT &pThis->m_DispatchInfo.cs))
  848. {
  849. if (pThis->m_DispatchInfo.status != e_statusDlgInit)
  850. {
  851. pThis->m_DispatchInfo.status = e_statusUserCancel;
  852. fEndDialog = TRUE;
  853. }
  854. LeaveCriticalSection(INOUT &pThis->m_DispatchInfo.cs);
  855. }
  856. if (fEndDialog)
  857. {
  858. EndDialog(hdlg, FALSE);
  859. }
  860. else
  861. {
  862. ThreadTrace0("Critical section already in use. Try again...\n");
  863. PostMessage(hdlg, WM_COMMAND, IDCANCEL, 0);
  864. Sleep(100);
  865. } // if...else
  866. } // if
  867. break;
  868. case WM_HELP:
  869. return pThis->OnHelp (lParam, IDD_DISPATCH_MESSAGES);
  870. default:
  871. return FALSE;
  872. } // switch
  873. return TRUE;
  874. } // CSendConsoleMessageDlg::DlgProcDispatchMessageToRecipients()
  875. /////////////////////////////////////////////////////////////////////
  876. DWORD CSendConsoleMessageDlg::ThreadProcDispatchMessageToRecipients(CSendConsoleMessageDlg * pThis)
  877. {
  878. Assert(pThis != NULL);
  879. pThis->AddRef();
  880. Assert(pThis->m_cRefCount > 1);
  881. pThis->DispatchMessageToRecipients();
  882. pThis->Release();
  883. return 0;
  884. } // CSendConsoleMessageDlg::ThreadProcDispatchMessageToRecipients()
  885. #define IDH_EDIT_MESSAGE_TEXT 900
  886. #define IDH_LIST_RECIPIENTS 901
  887. #define IDH_BUTTON_ADD_RECIPIENT 903
  888. #define IDH_BUTTON_REMOVE_RECIPIENT 904
  889. const DWORD g_aHelpIDs_IDD_SEND_CONSOLE_MESSAGE[]=
  890. {
  891. IDC_EDIT_MESSAGE_TEXT, IDH_EDIT_MESSAGE_TEXT,
  892. IDOK, IDOK,
  893. IDC_LIST_RECIPIENTS, IDH_LIST_RECIPIENTS,
  894. IDC_BUTTON_ADD_RECIPIENT, IDH_BUTTON_ADD_RECIPIENT,
  895. IDC_BUTTON_REMOVE_RECIPIENT, IDH_BUTTON_REMOVE_RECIPIENT,
  896. 0, 0
  897. };
  898. BOOL CSendConsoleMessageDlg::OnHelp(LPARAM lParam, int nDlgIDD)
  899. {
  900. const LPHELPINFO pHelpInfo = (LPHELPINFO)lParam;
  901. if (pHelpInfo && pHelpInfo->iContextType == HELPINFO_WINDOW)
  902. {
  903. switch (nDlgIDD)
  904. {
  905. case IDD_SEND_CONSOLE_MESSAGE:
  906. DoSendConsoleMessageContextHelp ((HWND) pHelpInfo->hItemHandle);
  907. break;
  908. }
  909. }
  910. else
  911. HtmlHelpW (NULL, HTML_HELP_FILE, HH_DISPLAY_TOPIC, 0);
  912. return TRUE;
  913. }
  914. void CSendConsoleMessageDlg::DoSendConsoleMessageContextHelp (HWND hWndControl)
  915. {
  916. switch (::GetDlgCtrlID (hWndControl))
  917. {
  918. case IDCANCEL:
  919. case IDC_BUTTON_ADVANCED:
  920. break;
  921. default:
  922. // Display context help for a control
  923. if ( !::WinHelp (
  924. hWndControl,
  925. CONTEXT_HELP_FILE,
  926. HELP_WM_HELP,
  927. (DWORD_PTR) g_aHelpIDs_IDD_SEND_CONSOLE_MESSAGE) )
  928. {
  929. Trace1 ("WinHelp () failed: 0x%x\n", GetLastError ());
  930. }
  931. break;
  932. }
  933. }
  934. /////////////////////////////////////////////////////////////////////
  935. /////////////////////////////////////////////////////////////////////
  936. void CSendMessageAdvancedOptionsDlg::OnInitDialog(HWND hdlg)
  937. {
  938. m_hdlg = hdlg;
  939. m_fSendAutomatedMessage = FALSE;
  940. CheckDlgButton(m_hdlg, IDC_CHECK_SEND_AUTOMATED_MESSAGE, m_fSendAutomatedMessage);
  941. UpdateUI();
  942. }
  943. /////////////////////////////////////////////////////////////////////
  944. void CSendMessageAdvancedOptionsDlg::UpdateUI()
  945. {
  946. static const UINT rgid[] =
  947. {
  948. IDC_STATIC_RESOURCE_NAME,
  949. IDC_EDIT_RESOURCE_NAME,
  950. IDC_STATIC_SHUTDOWN_OCCURS,
  951. IDC_EDIT_SHUTDOWN_OCCURS,
  952. //IDC_SPIN_SHUTDOWN_OCCURS,
  953. IDC_STATIC_SHUTDOWN_OCCURS_UNIT,
  954. IDC_STATIC_RESEND,
  955. IDC_EDIT_RESEND,
  956. //IDC_SPIN_RESEND,
  957. IDC_STATIC_RESEND_UNIT,
  958. IDC_STATIC_RESOURCE_BACK_ONLINE,
  959. IDC_EDIT_RESOURCE_BACK_ONLINE,
  960. };
  961. for (int i = 0; i < LENGTH(rgid); i++)
  962. {
  963. EnableWindow(GetDlgItem(m_hdlg, rgid[i]), m_fSendAutomatedMessage);
  964. }
  965. } // CSendMessageAdvancedOptionsDlg::UpdateUI()
  966. /////////////////////////////////////////////////////////////////////
  967. INT_PTR CALLBACK CSendMessageAdvancedOptionsDlg::DlgProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPARAM /*lParam*/)
  968. {
  969. CSendMessageAdvancedOptionsDlg * pThis;
  970. pThis = (CSendMessageAdvancedOptionsDlg *)GetWindowLongPtr(hdlg, GWLP_USERDATA);
  971. switch (uMsg)
  972. {
  973. case WM_INITDIALOG:
  974. Assert(pThis == NULL);
  975. pThis = new CSendMessageAdvancedOptionsDlg;
  976. if (pThis == NULL)
  977. return -1;
  978. SetWindowLongPtr(hdlg, GWLP_USERDATA, (LONG_PTR)pThis);
  979. pThis->OnInitDialog(hdlg);
  980. break;
  981. case WM_COMMAND:
  982. switch (wParam)
  983. {
  984. case IDOK:
  985. EndDialog(hdlg, TRUE);
  986. break;
  987. case IDCANCEL:
  988. EndDialog(hdlg, FALSE);
  989. break;
  990. case IDC_CHECK_SEND_AUTOMATED_MESSAGE:
  991. pThis->m_fSendAutomatedMessage = IsDlgButtonChecked(hdlg, IDC_CHECK_SEND_AUTOMATED_MESSAGE);
  992. pThis->UpdateUI();
  993. break;
  994. } // switch
  995. break;
  996. default:
  997. return FALSE;
  998. } // switch
  999. return TRUE;
  1000. } // CSendMessageAdvancedOptionsDlg::DlgProc()
  1001. BOOL CSendMessageAdvancedOptionsDlg::OnHelp(LPARAM /*lParam*/)
  1002. {
  1003. HtmlHelpW (NULL, HTML_HELP_FILE, HH_DISPLAY_TOPIC, 0);
  1004. return TRUE;
  1005. }