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.

1878 lines
36 KiB

  1. /*++
  2. Copyright (c) 1994-2001 Microsoft Corporation
  3. Module Name :
  4. supdlgs.cpp
  5. Abstract:
  6. Supporting dialogs
  7. Author:
  8. Ronald Meijer (ronaldm)
  9. Sergei Antonov (sergeia)
  10. Project:
  11. Internet Services Manager
  12. Revision History:
  13. --*/
  14. //
  15. // Include Files
  16. //
  17. #include "stdafx.h"
  18. #include <iiscnfgp.h>
  19. #include <winsock2.h>
  20. #include "common.h"
  21. #include "InetMgrApp.h"
  22. #include "supdlgs.h"
  23. extern CInetmgrApp theApp;
  24. #ifdef _DEBUG
  25. #undef THIS_FILE
  26. static char BASED_CODE THIS_FILE[] = __FILE__;
  27. #endif
  28. #define new DEBUG_NEW
  29. //
  30. // Registry key name for this dialog
  31. //
  32. const TCHAR g_szRegKey[] = _T("Advanced");
  33. //
  34. // Site Security Listbox Column Definitions
  35. //
  36. // Note: IDS_IP_ADDRESS_SUBNET_MASK is overridden
  37. // in w3scfg
  38. //
  39. static const ODL_COLUMN_DEF g_aColumns[] =
  40. {
  41. // ===============================================
  42. // Weight Label
  43. // ===============================================
  44. { 4, IDS_ACCESS, },
  45. { 15, IDS_IP_ADDRESS_SUBNET_MASK, },
  46. };
  47. #define NUM_COLUMNS (sizeof(g_aColumns) / sizeof(g_aColumns[0]))
  48. //
  49. // CUserAccountDlg dialog
  50. //
  51. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  52. CUserAccountDlg::CUserAccountDlg(
  53. IN LPCTSTR lpstrServer,
  54. IN LPCTSTR lpstrUserName,
  55. IN LPCTSTR lpstrPassword,
  56. IN CWnd * pParent OPTIONAL
  57. )
  58. /*++
  59. Routine Description:
  60. Constructor for bringing up the user account dialog
  61. Arguments:
  62. LPCTSTR lpstrServer : Server
  63. LPCTSTR lpstrUserName : Starting Username
  64. LPCTSTR lpstrPassword : Starting Password
  65. CWnd * pParent : Parent window handle
  66. Return Value:
  67. N/A
  68. --*/
  69. : m_strUserName(lpstrUserName),
  70. m_strServer(lpstrServer),
  71. m_fDelegation(FALSE),
  72. CDialog(CUserAccountDlg::IDD, pParent)
  73. {
  74. //{{AFX_DATA_INIT(CUserAccountDlg)
  75. m_strPassword = lpstrPassword;
  76. //}}AFX_DATA_INIT
  77. }
  78. void
  79. CUserAccountDlg::DoDataExchange(
  80. IN CDataExchange * pDX
  81. )
  82. {
  83. CDialog::DoDataExchange(pDX);
  84. //{{AFX_DATA_MAP(CUserAccountDlg)
  85. DDX_Control(pDX, IDC_EDIT_USERNAME, m_edit_UserName);
  86. DDX_Control(pDX, IDC_EDIT_PASSWORD, m_edit_Password);
  87. DDX_Control(pDX, IDC_DELEGATION, m_chk_Delegation);
  88. DDX_Check(pDX, IDC_DELEGATION, m_fDelegation);
  89. //}}AFX_DATA_MAP
  90. DDX_Text(pDX, IDC_EDIT_USERNAME, m_strUserName);
  91. if (!m_fDelegation)
  92. {
  93. if (pDX->m_bSaveAndValidate)
  94. {
  95. DDV_MinMaxChars(pDX, m_strUserName, 1, UNLEN);
  96. }
  97. //DDX_Password(pDX, IDC_EDIT_PASSWORD, m_strPassword, g_lpszDummyPassword);
  98. DDX_Password_SecuredString(pDX, IDC_EDIT_PASSWORD, m_strPassword, g_lpszDummyPassword);
  99. if (pDX->m_bSaveAndValidate)
  100. {
  101. //DDV_MaxCharsBalloon(pDX, m_strPassword, PWLEN);
  102. DDV_MaxCharsBalloon_SecuredString(pDX, m_strPassword, PWLEN);
  103. }
  104. }
  105. }
  106. //
  107. // Message Map
  108. //
  109. BEGIN_MESSAGE_MAP(CUserAccountDlg, CDialog)
  110. //{{AFX_MSG_MAP(CUserAccountDlg)
  111. ON_BN_CLICKED(IDC_BUTTON_BROWSE_USERS, OnButtonBrowseUsers)
  112. ON_BN_CLICKED(IDC_BUTTON_CHECK_PASSWORD, OnButtonCheckPassword)
  113. ON_EN_CHANGE(IDC_EDIT_USERNAME, OnChangeEditUsername)
  114. ON_BN_CLICKED(IDC_DELEGATION, OnCheckDelegation)
  115. ON_BN_CLICKED(IDOK, OnOK)
  116. ON_BN_CLICKED(ID_HELP, OnHelp)
  117. //}}AFX_MSG_MAP
  118. END_MESSAGE_MAP()
  119. //
  120. // Message Handlers
  121. //
  122. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  123. BOOL
  124. CUserAccountDlg::OnInitDialog()
  125. {
  126. m_fDelegation = m_strUserName.IsEmpty();
  127. BOOL bRes = CDialog::OnInitDialog();
  128. OnCheckDelegation();
  129. return bRes;
  130. }
  131. void
  132. CUserAccountDlg::OnOK()
  133. {
  134. CDialog::OnOK();
  135. if (m_fDelegation)
  136. {
  137. m_strUserName.Empty();
  138. m_strPassword.Empty();
  139. }
  140. }
  141. void
  142. CUserAccountDlg::OnButtonBrowseUsers()
  143. {
  144. CString str;
  145. if (GetIUsrAccount(m_strServer, this, str))
  146. {
  147. //
  148. // If a name was selected, blank
  149. // out the password
  150. //
  151. m_edit_UserName.SetWindowText(str);
  152. m_edit_Password.SetFocus();
  153. }
  154. }
  155. void
  156. CUserAccountDlg::OnCheckDelegation()
  157. {
  158. BOOL bEnable = BST_CHECKED != m_chk_Delegation.GetCheck();
  159. m_edit_UserName.EnableWindow(bEnable);
  160. m_edit_Password.EnableWindow(bEnable);
  161. GetDlgItem(IDC_BUTTON_BROWSE_USERS)->EnableWindow(bEnable);
  162. }
  163. void
  164. CUserAccountDlg::OnChangeEditUsername()
  165. {
  166. m_edit_Password.SetWindowText(_T(""));
  167. }
  168. void
  169. CUserAccountDlg::OnButtonCheckPassword()
  170. {
  171. if (!UpdateData(TRUE))
  172. {
  173. return;
  174. }
  175. CString csTempPassword;
  176. m_strPassword.CopyTo(csTempPassword);
  177. CError err(CComAuthInfo::VerifyUserPassword(m_strUserName, csTempPassword));
  178. if (!err.MessageBoxOnFailure(m_hWnd))
  179. {
  180. DoHelpMessageBox(m_hWnd,IDS_PASSWORD_OK, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0);
  181. }
  182. }
  183. void
  184. CUserAccountDlg::OnHelp()
  185. {
  186. WinHelpDebug(0x20000 + CUserAccountDlg::IDD);
  187. ::WinHelp(m_hWnd, theApp.m_pszHelpFilePath, HELP_CONTEXT, 0x20000 + CUserAccountDlg::IDD);
  188. }
  189. //
  190. // Text dialog that warns of clear text violation
  191. //
  192. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  193. CClearTxtDlg::CClearTxtDlg(
  194. IN CWnd * pParent OPTIONAL
  195. )
  196. /*++
  197. Routine Description:
  198. Clear text dialog constructor
  199. Arguments:
  200. CWnd * pParent : Optional parent window
  201. Return Value:
  202. N/A
  203. --*/
  204. : CDialog(CClearTxtDlg::IDD, pParent)
  205. {
  206. //{{AFX_DATA_INIT(CClearTxtDlg)
  207. //}}AFX_DATA_INIT
  208. }
  209. void
  210. CClearTxtDlg::DoDataExchange(
  211. IN CDataExchange * pDX
  212. )
  213. /*++
  214. Routine Description:
  215. Initialise/Store control data
  216. Arguments:
  217. CDataExchange * pDX - DDX/DDV control structure
  218. Return Value:
  219. None
  220. --*/
  221. {
  222. CDialog::DoDataExchange(pDX);
  223. //{{AFX_DATA_MAP(CClearTxtDlg)
  224. //}}AFX_DATA_MAP
  225. }
  226. //
  227. // Message Map
  228. //
  229. BEGIN_MESSAGE_MAP(CClearTxtDlg, CDialog)
  230. //{{AFX_MSG_MAP(CClearTxtDlg)
  231. //}}AFX_MSG_MAP
  232. END_MESSAGE_MAP()
  233. //
  234. // Message Handlers
  235. //
  236. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  237. BOOL
  238. CClearTxtDlg::OnInitDialog()
  239. /*++
  240. Routine Description:
  241. WM_INITDIALOG handler. Initialize the dialog.
  242. Arguments:
  243. None.
  244. Return Value:
  245. TRUE if no focus is to be set automatically, FALSE if the focus
  246. is already set.
  247. --*/
  248. {
  249. CDialog::OnInitDialog();
  250. (GetDlgItem(IDCANCEL))->SetFocus();
  251. CenterWindow();
  252. MessageBeep(MB_ICONEXCLAMATION);
  253. return FALSE;
  254. }
  255. IMPLEMENT_DYNAMIC(CIPAccessDescriptorListBox, CHeaderListBox);
  256. //
  257. // Bitmap indices
  258. //
  259. enum
  260. {
  261. BMPID_GRANTED = 0,
  262. BMPID_DENIED,
  263. BMPID_SINGLE,
  264. BMPID_MULTIPLE,
  265. //
  266. // Don't move this one
  267. //
  268. BMPID_TOTAL
  269. };
  270. const int CIPAccessDescriptorListBox::nBitmaps = BMPID_TOTAL;
  271. CIPAccessDescriptorListBox::CIPAccessDescriptorListBox(
  272. IN BOOL fDomainsAllowed
  273. )
  274. /*++
  275. Routine Description:
  276. Constructor
  277. Arguments:
  278. fDomainsAllowed : TRUE if domain names are legal.
  279. Return Value:
  280. N/A
  281. --*/
  282. : m_fDomainsAllowed(fDomainsAllowed),
  283. CHeaderListBox(HLS_STRETCH, g_szRegKey)
  284. {
  285. m_strGranted.LoadString(IDS_GRANTED);
  286. m_strDenied.LoadString(IDS_DENIED);
  287. m_strFormat.LoadString(IDS_FMT_SECURITY);
  288. }
  289. void
  290. CIPAccessDescriptorListBox::DrawItemEx(
  291. IN CRMCListBoxDrawStruct & ds
  292. )
  293. /*++
  294. Routine Description:
  295. Draw item in the listbox
  296. Arguments:
  297. CRMCListBoxDrawStruct & ds : Draw item structure
  298. Return Value:
  299. None
  300. --*/
  301. {
  302. CIPAccessDescriptor * p = (CIPAccessDescriptor *)ds.m_ItemData;
  303. ASSERT_READ_PTR(p);
  304. //
  305. // Display Granted/Denied with appropriate bitmap
  306. //
  307. DrawBitmap(ds, 0, p->HasAccess() ? BMPID_GRANTED : BMPID_DENIED);
  308. ColumnText(ds, 0, TRUE, p->HasAccess() ? m_strGranted : m_strDenied);
  309. //
  310. // Display IP Address with multiple/single bitmap
  311. //
  312. DrawBitmap(ds, 1, p->IsSingle() ? BMPID_SINGLE : BMPID_MULTIPLE);
  313. if (p->IsDomainName())
  314. {
  315. ColumnText(ds, 1, TRUE, p->QueryDomainName());
  316. }
  317. else if (p->IsSingle())
  318. {
  319. //
  320. // Display only ip address
  321. //
  322. ColumnText(ds, 1, TRUE, p->QueryIPAddress());
  323. }
  324. else
  325. {
  326. //
  327. // Display ip address/subnet mask
  328. //
  329. CString str, strIP, strMask;
  330. str.Format(
  331. m_strFormat,
  332. (LPCTSTR)p->QueryIPAddress().QueryIPAddress(strIP),
  333. (LPCTSTR)p->QuerySubnetMask().QueryIPAddress(strMask)
  334. );
  335. ColumnText(ds, 1, TRUE, str);
  336. }
  337. }
  338. /* virtual */
  339. BOOL
  340. CIPAccessDescriptorListBox::Initialize()
  341. /*++
  342. Routine Description:
  343. Initialize the listbox. Insert the columns as requested, and lay
  344. them out appropriately
  345. Arguments:
  346. None
  347. Return Value:
  348. TRUE for succesful initialisation, FALSE otherwise
  349. --*/
  350. {
  351. if (!CHeaderListBox::Initialize())
  352. {
  353. return FALSE;
  354. }
  355. //
  356. // Build all columns
  357. //
  358. HINSTANCE hInst = AfxGetResourceHandle();
  359. for (int nCol = 0; nCol < NUM_COLUMNS; ++nCol)
  360. {
  361. InsertColumn(nCol, g_aColumns[nCol].nWeight, g_aColumns[nCol].nLabelID, hInst);
  362. }
  363. //
  364. // Try to set the widths from the stored registry value,
  365. // otherwise distribute according to column weights specified
  366. //
  367. // if (!SetWidthsFromReg())
  368. // {
  369. DistributeColumns();
  370. // }
  371. return TRUE;
  372. }
  373. //
  374. // CAccessEntryListBox - a listbox of user SIDs
  375. //
  376. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  377. IMPLEMENT_DYNAMIC(CAccessEntryListBox, CRMCListBox);
  378. const int CAccessEntryListBox::nBitmaps = 8;
  379. void
  380. CAccessEntryListBox::DrawItemEx(
  381. IN CRMCListBoxDrawStruct & ds
  382. )
  383. /*++
  384. Routine Description:
  385. Draw item in the listbox
  386. Arguments:
  387. CRMCListBoxDrawStruct & ds : Input data structure
  388. Return Value:
  389. N/A
  390. --*/
  391. {
  392. CAccessEntry * pEntry = (CAccessEntry *)ds.m_ItemData;
  393. ASSERT_READ_PTR(pEntry);
  394. ASSERT(pEntry->IsSIDResolved());
  395. DrawBitmap(ds, 0, pEntry->QueryPictureID());
  396. ColumnText(ds, 0, TRUE, pEntry->QueryUserName());
  397. }
  398. void
  399. CAccessEntryListBox::FillAccessListBox(
  400. IN CObListPlus & obl
  401. )
  402. /*++
  403. Routine Description:
  404. Fill a listbox with entries from the oblist.
  405. Entries will not be shown if the deleted flag is set, or if
  406. their access mask does not fit with the requested access mask.
  407. Arguments:
  408. CObListPlus & obl : List of access entries
  409. Return Value:
  410. None.
  411. --*/
  412. {
  413. CObListIter obli(obl);
  414. CAccessEntry * pAccessEntry;
  415. //
  416. // Remember the selection.
  417. //
  418. CAccessEntry * pSelEntry = GetSelectedItem();
  419. SetRedraw(FALSE);
  420. ResetContent();
  421. int cItems = 0;
  422. for ( /**/ ; pAccessEntry = (CAccessEntry *)obli.Next(); ++cItems)
  423. {
  424. if (pAccessEntry->IsVisible() && !pAccessEntry->IsDeleted())
  425. {
  426. AddItem(pAccessEntry);
  427. }
  428. }
  429. SetRedraw(TRUE);
  430. SelectItem(pSelEntry);
  431. }
  432. void
  433. CAccessEntryListBox::ResolveAccessList(
  434. IN CObListPlus &obl
  435. )
  436. /*++
  437. Routine Description:
  438. For each member of the list, resolve the SID into a username.
  439. Arguments:
  440. CObListPlus & obl : List of access entries
  441. Return Value:
  442. None.
  443. --*/
  444. {
  445. CObListIter obli(obl);
  446. CAccessEntry * pAccessEntry;
  447. while (pAccessEntry = (CAccessEntry *)obli.Next())
  448. {
  449. if (!pAccessEntry->IsSIDResolved())
  450. {
  451. pAccessEntry->ResolveSID();
  452. }
  453. }
  454. }
  455. BOOL
  456. CAccessEntryListBox::AddUserPermissions(
  457. IN LPCTSTR lpstrServer,
  458. IN CObListPlus &oblSID,
  459. IN CAccessEntry * newUser,
  460. IN ACCESS_MASK accPermissions
  461. )
  462. /*++
  463. Routine Description:
  464. Add a user to the service list. The return value is
  465. what would be its listbox index.
  466. Arguments:
  467. LPCTSTR lpstrServer : Server name
  468. CObListPlus &oblSID : List of SIDs
  469. CAccessEntry * newUser : User details from user browser
  470. ACCESS_MASK accPermissions : Access permissions
  471. Return Value:
  472. TRUE for success, FALSE for failure.
  473. --*/
  474. {
  475. CAccessEntry * pAccessEntry;
  476. //
  477. // Look it up in the list to see if it exists already
  478. //
  479. CObListIter obli(oblSID);
  480. while(pAccessEntry = (CAccessEntry *)obli.Next())
  481. {
  482. if (*pAccessEntry == newUser->GetSid())
  483. {
  484. TRACEEOLID("Found existing account -- adding permissions");
  485. if (pAccessEntry->IsDeleted())
  486. {
  487. //
  488. // Back again..
  489. //
  490. pAccessEntry->FlagForDeletion(FALSE);
  491. }
  492. break;
  493. }
  494. }
  495. if (pAccessEntry == NULL)
  496. {
  497. //
  498. // I am creating new entry here to be sure that I could delete
  499. // it from the input array. The SID is copied to new entry.
  500. //
  501. pAccessEntry = new CAccessEntry(*newUser);
  502. if (pAccessEntry)
  503. {
  504. pAccessEntry->MarkEntryAsNew();
  505. oblSID.AddTail(pAccessEntry);
  506. }
  507. else
  508. {
  509. return FALSE;
  510. }
  511. }
  512. pAccessEntry->AddPermissions(accPermissions);
  513. return TRUE;
  514. }
  515. /*
  516. BOOL
  517. CAccessEntryListBox::AddUserPermissions(
  518. IN LPCTSTR lpstrServer,
  519. IN CObListPlus &oblSID,
  520. IN LPUSERDETAILS pusdtNewUser,
  521. IN ACCESS_MASK accPermissions
  522. )
  523. /*++
  524. Routine Description:
  525. Add a user to the service list. The return value is
  526. the what would be its listbox index.
  527. Arguments:
  528. LPCTSTR lpstrServer : Server name
  529. CObListPlus &oblSID : List of SIDs
  530. LPUSERDETAILS pusdtNewUser : User details from user browser
  531. ACCESS_MASK accPermissions : Access permissions
  532. Return Value:
  533. TRUE for success, FALSE for failure.
  534. --/
  535. {
  536. //
  537. // Look it up in the list to see if it exists already
  538. //
  539. CObListIter obli(oblSID);
  540. CAccessEntry * pAccessEntry;
  541. while(pAccessEntry = (CAccessEntry *)obli.Next())
  542. {
  543. if (*pAccessEntry == pusdtNewUser->psidUser)
  544. {
  545. TRACEEOLID("Found existing account -- adding permissions");
  546. if (pAccessEntry->IsDeleted())
  547. {
  548. //
  549. // Back again..
  550. //
  551. pAccessEntry->FlagForDeletion(FALSE);
  552. }
  553. break;
  554. }
  555. }
  556. if (pAccessEntry == NULL)
  557. {
  558. TRACEEOLID("This account did not yet exist -- adding new one");
  559. pAccessEntry = new CAccessEntry(accPermissions,
  560. pusdtNewUser->psidUser,
  561. lpstrServer,
  562. TRUE
  563. );
  564. if (!pAccessEntry)
  565. {
  566. TRACEEOLID("AddUserPermissions: OOM");
  567. return FALSE;
  568. }
  569. pAccessEntry->MarkEntryAsNew();
  570. oblSID.AddTail(pAccessEntry);
  571. }
  572. else
  573. {
  574. pAccessEntry->AddPermissions(accPermissions);
  575. }
  576. return TRUE;
  577. }
  578. */
  579. BOOL
  580. CAccessEntryListBox::AddToAccessList(
  581. IN CWnd * pWnd,
  582. IN LPCTSTR lpstrServer,
  583. OUT CObListPlus & obl
  584. )
  585. /*++
  586. Routine Description:
  587. Bring up the Add Users and Groups dialogs from netui.
  588. Arguments:
  589. CWnd * pWnd : Parent window
  590. LPCTSTR lpstrServer : Server that owns the accounts
  591. CObListPlus & obl : Returns the list of selected users.
  592. Return Value:
  593. TRUE if anything was added, FALSE otherwise.
  594. --*/
  595. {
  596. CGetUsers usrBrowser(lpstrServer, TRUE);
  597. BOOL bRes = usrBrowser.GetUsers(pWnd->GetSafeHwnd());
  598. UINT cItems = 0;
  599. if (bRes)
  600. {
  601. //
  602. // Specify access mask for an operator
  603. //
  604. ACCESS_MASK accPermissions =
  605. (MD_ACR_READ | MD_ACR_WRITE | MD_ACR_ENUM_KEYS);
  606. for (int i = 0; i < usrBrowser.GetSize(); i++)
  607. {
  608. if (!AddUserPermissions(lpstrServer, obl, usrBrowser[i], accPermissions))
  609. {
  610. bRes = FALSE;
  611. break;
  612. }
  613. cItems++;
  614. }
  615. }
  616. if (cItems > 0)
  617. {
  618. FillAccessListBox(obl);
  619. }
  620. return bRes;
  621. }
  622. CDnsNameDlg::CDnsNameDlg(
  623. IN CIPAddressCtrl * pIpControl OPTIONAL,
  624. IN CWnd * pParent OPTIONAL
  625. )
  626. /*++
  627. Routine Description:
  628. Construct with optional associated IP address control
  629. Arguments:
  630. CWndIpAddress * pIpControl : Associated IP control
  631. CWnd * pParent : Pointer to parent window
  632. Return Value:
  633. N/A
  634. --*/
  635. : m_pIpControl(pIpControl),
  636. m_dwIPValue(0L),
  637. CDialog(CDnsNameDlg::IDD, pParent)
  638. {
  639. #if 0 // Keep class wizard happy
  640. //{{AFX_DATA_INIT(CDnsNameDlg)
  641. //}}AFX_DATA_INIT
  642. #endif // 0
  643. if (m_pIpControl)
  644. {
  645. m_pIpControl->GetAddress(m_dwIPValue);
  646. }
  647. }
  648. CDnsNameDlg::CDnsNameDlg(
  649. IN DWORD dwIPValue,
  650. IN CWnd * pParent OPTIONAL
  651. )
  652. /*++
  653. Routine Description:
  654. Construct with associated IP value
  655. Arguments:
  656. DWORD dwIPValue : IP Value
  657. CWnd * pParent : Pointer to parent window
  658. Return Value:
  659. N/A
  660. --*/
  661. : m_pIpControl(NULL),
  662. m_dwIPValue(dwIPValue),
  663. CDialog(CDnsNameDlg::IDD, pParent)
  664. {
  665. }
  666. void
  667. CDnsNameDlg::DoDataExchange(
  668. IN CDataExchange * pDX
  669. )
  670. /*++
  671. Routine Description:
  672. Initialise/Store control data
  673. Arguments:
  674. CDataExchange * pDX - DDX/DDV control structure
  675. Return Value:
  676. None
  677. --*/
  678. {
  679. CDialog::DoDataExchange(pDX);
  680. //{{AFX_DATA_MAP(CDnsNameDlg)
  681. DDX_Control(pDX, IDC_EDIT_DNS_NAME, m_edit_DNSName);
  682. DDX_Control(pDX, IDOK, m_button_OK);
  683. //}}AFX_DATA_MAP
  684. }
  685. //
  686. // Message Map
  687. //
  688. BEGIN_MESSAGE_MAP(CDnsNameDlg, CDialog)
  689. //{{AFX_MSG_MAP(CDnsNameDlg)
  690. ON_EN_CHANGE(IDC_EDIT_DNS_NAME, OnChangeEditDnsName)
  691. //}}AFX_MSG_MAP
  692. END_MESSAGE_MAP()
  693. DWORD
  694. CDnsNameDlg::FillIpControlFromName()
  695. /*++
  696. Routine Description:
  697. Do a DNS lookup from the hostname in the edit control, and place
  698. the ip value in the ip control if we have one.
  699. Arguments:
  700. None
  701. Return Value:
  702. Error return code
  703. --*/
  704. {
  705. CString str;
  706. DWORD err = 0;
  707. HOSTENT * pHostent = NULL;
  708. m_edit_DNSName.GetWindowText(str);
  709. BeginWaitCursor();
  710. #ifdef _UNICODE
  711. CHAR * pAnsi = AllocAnsiString(str);
  712. if (pAnsi == NULL)
  713. {
  714. return ERROR_NOT_ENOUGH_MEMORY;
  715. }
  716. pHostent = ::gethostbyname(pAnsi);
  717. #else
  718. pHostent = ::gethostbyname((LPCTSTR)str);
  719. #endif // _UNICODE;
  720. if (pHostent != NULL)
  721. {
  722. //
  723. // Got a valid lookup. Convert the value to host order,
  724. // optionally set the value in the associated ip control
  725. //
  726. m_dwIPValue = ::ntohl(*((u_long *)pHostent->h_addr_list[0]));
  727. if (m_pIpControl)
  728. {
  729. m_pIpControl->SetAddress(m_dwIPValue);
  730. }
  731. }
  732. else
  733. {
  734. err = ::WSAGetLastError();
  735. if (err == WSANO_DATA)
  736. err = WSAHOST_NOT_FOUND;
  737. }
  738. EndWaitCursor();
  739. #ifdef _UNICODE
  740. FreeMem(pAnsi);
  741. #endif // _UNICODE
  742. return err;
  743. }
  744. DWORD
  745. CDnsNameDlg::FillNameFromIpValue()
  746. /*++
  747. Routine Description:
  748. Given the ip value, fill, do a reverse lookup, and fill the name in
  749. the edit control.
  750. Arguments:
  751. None
  752. Return Value:
  753. Error return code
  754. --*/
  755. {
  756. DWORD err = ERROR_SUCCESS;
  757. if (m_dwIPValue == 0L)
  758. {
  759. //
  760. // Don't bother filling this
  761. // one in -- not an error, though
  762. //
  763. return err;
  764. }
  765. //
  766. // Call the Winsock API to get host name and alias information.
  767. //
  768. u_long ulAddrInNetOrder = ::htonl((u_long)m_dwIPValue);
  769. BeginWaitCursor();
  770. HOSTENT * pHostInfo = ::gethostbyaddr(
  771. (CHAR *)&ulAddrInNetOrder,
  772. sizeof ulAddrInNetOrder,
  773. PF_INET
  774. );
  775. EndWaitCursor();
  776. if (pHostInfo == NULL)
  777. {
  778. return ::WSAGetLastError();
  779. }
  780. try
  781. {
  782. CString str(pHostInfo->h_name);
  783. m_edit_DNSName.SetWindowText(str);
  784. }
  785. catch(CException * e)
  786. {
  787. err = ::GetLastError();
  788. e->Delete();
  789. }
  790. return err;
  791. }
  792. //
  793. // Message Handlers
  794. //
  795. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  796. void
  797. CDnsNameDlg::OnOK()
  798. /*++
  799. Routine Description:
  800. Attempt to resolve the the IP address in response to the OK button
  801. being pressed. Don't dismiss the dialog if the name is not
  802. resolvable.
  803. Arguments:
  804. None
  805. Return Value:
  806. None
  807. --*/
  808. {
  809. CError err(FillIpControlFromName());
  810. if (err.Failed())
  811. {
  812. UINT errId = 0;
  813. if (err.Win32Error() == WSAHOST_NOT_FOUND)
  814. {
  815. errId = IDS_WSAHOST_NOT_FOUND;
  816. }
  817. if (errId == 0)
  818. {
  819. err.MessageBoxOnFailure(m_hWnd);
  820. }
  821. else
  822. {
  823. DoHelpMessageBox(m_hWnd,errId, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0);
  824. }
  825. //
  826. // Failed, don't dismiss the dialog
  827. //
  828. return;
  829. }
  830. //
  831. // Dismiss the dialog
  832. //
  833. CDialog::OnOK();
  834. }
  835. void
  836. CDnsNameDlg::OnChangeEditDnsName()
  837. /*++
  838. Routine Description:
  839. Enable/disable the ok button depending on the contents of the edit control.
  840. Arguments:
  841. None
  842. Return Value:
  843. None
  844. --*/
  845. {
  846. m_button_OK.EnableWindow(m_edit_DNSName.GetWindowTextLength() > 0);
  847. }
  848. BOOL
  849. CDnsNameDlg::OnInitDialog()
  850. /*++
  851. Routine Description:
  852. WM_INITDIALOG handler. Initialize the dialog.
  853. Arguments:
  854. None.
  855. Return Value:
  856. TRUE if focus is to be set automatically, FALSE if the focus
  857. is already set.
  858. --*/
  859. {
  860. CDialog::OnInitDialog();
  861. //
  862. // If an address has been pre-set do a reverse lookup
  863. //
  864. if (m_dwIPValue)
  865. {
  866. CError err(FillNameFromIpValue());
  867. err.MessageBoxOnFailure(m_hWnd);
  868. }
  869. OnChangeEditDnsName();
  870. return TRUE;
  871. }
  872. //
  873. // IP Access Dialog
  874. //
  875. CIPAccessDlg::CIPAccessDlg(
  876. IN BOOL fDenyAccessMode,
  877. IN OUT CIPAccessDescriptor *& pAccess,
  878. IN CObListPlus * poblAccessList OPTIONAL,
  879. IN CWnd * pParent OPTIONAL,
  880. IN BOOL fAllowDomains
  881. )
  882. /*++
  883. Routine Description:
  884. Constructor for the access descriptor editor dialog. If constructed
  885. with a NULL access descriptor pointer, the access descriptor will
  886. be allocated, otherwise the dialog will edit the existing one in
  887. place.
  888. Arguments:
  889. BOOL fDenyAccessMode : If TRUE, we're denying access, if FALSE,
  890. we're granting access.
  891. CIPAccessDescriptor *& pAccess : Object being edited, or NULL to allocate
  892. a new access descriptor
  893. CObListPlus * poblAccessList : List of already existing entries to check
  894. for duplicates, or NULL
  895. CWnd * pParent, : Pointer to parent window or NULL
  896. BOOL fAllowDomains : If TRUE, domain names are valid, otherwise
  897. they will not be available
  898. Return Value:
  899. N/A
  900. --*/
  901. : CDialog(CIPAccessDlg::IDD, pParent),
  902. m_pAccess(pAccess),
  903. m_poblAccessList(poblAccessList),
  904. m_fNew(pAccess == NULL),
  905. m_fDenyAccessMode(fDenyAccessMode),
  906. m_fAllowDomains(fAllowDomains)
  907. {
  908. #if 0 // Keep Class Wizard happy
  909. //{{AFX_DATA_INIT(CIPAccessDlg)
  910. m_nStyle = RADIO_SINGLE;
  911. //}}AFX_DATA_INIT
  912. #endif // 0
  913. if (m_pAccess == NULL)
  914. {
  915. //
  916. // Allocate new one
  917. //
  918. m_pAccess = new CIPAccessDescriptor;
  919. if (m_pAccess)
  920. {
  921. m_pAccess->GrantAccess(!m_fDenyAccessMode);
  922. }
  923. }
  924. if (m_pAccess == NULL)
  925. {
  926. TRACEEOLID("Invalid access object -- possible memory failure");
  927. return;
  928. }
  929. if (m_pAccess->IsDomainName())
  930. {
  931. m_nStyle = RADIO_DOMAIN;
  932. }
  933. else
  934. {
  935. m_nStyle = m_pAccess->IsSingle() ? RADIO_SINGLE : RADIO_MULTIPLE;
  936. }
  937. //
  938. // We can only look at granted items when
  939. // deny by default is on and vice versa
  940. //
  941. ASSERT(m_pAccess->HasAccess() == !m_fDenyAccessMode);
  942. //
  943. // Load static strings
  944. //
  945. VERIFY(m_bstrIPAddress.LoadString(IDS_PROMPT_IP_ADDRESS));
  946. VERIFY(m_bstrNetworkID.LoadString(IDS_PROMPT_NETWORK_ID));
  947. VERIFY(m_bstrDomainName.LoadString(IDS_PROMPT_DOMAIN));
  948. }
  949. void
  950. CIPAccessDlg::DoDataExchange(
  951. IN CDataExchange * pDX
  952. )
  953. /*++
  954. Routine Description:
  955. Initialise/Store control data
  956. Arguments:
  957. CDataExchange * pDX - DDX/DDV control structure
  958. Return Value:
  959. None
  960. --*/
  961. {
  962. CDialog::DoDataExchange(pDX);
  963. //{{AFX_DATA_MAP(CIPAccessDlg)
  964. DDX_Control(pDX, IDOK, m_button_OK);
  965. DDX_Control(pDX, IDC_EDIT_DOMAIN, m_edit_Domain);
  966. DDX_Control(pDX, IDC_STATIC_IP_ADDRESS, m_static_IpAddress);
  967. DDX_Control(pDX, IDC_STATIC_SUBNET_MASK, m_static_SubnetMask);
  968. DDX_Control(pDX, IDC_BUTTON_DNS, m_button_DNS);
  969. DDX_Radio(pDX, IDC_RADIO_SINGLE, m_nStyle);
  970. //}}AFX_DATA_MAP
  971. DDX_Control(pDX, IDC_RADIO_DOMAIN, m_radio_Domain);
  972. DDX_Control(pDX, IDC_IPA_IPADDRESS, m_ipa_IPAddress);
  973. DDX_Control(pDX, IDC_IPA_SUBNET_MASK, m_ipa_SubnetMask);
  974. }
  975. //
  976. // Message Map
  977. //
  978. BEGIN_MESSAGE_MAP(CIPAccessDlg, CDialog)
  979. //{{AFX_MSG_MAP(CIPAccessDlg)
  980. ON_BN_CLICKED(IDC_RADIO_MULTIPLE, OnRadioMultiple)
  981. ON_BN_CLICKED(IDC_RADIO_SINGLE, OnRadioSingle)
  982. ON_BN_CLICKED(IDC_RADIO_DOMAIN, OnRadioDomain)
  983. ON_BN_CLICKED(IDC_BUTTON_DNS, OnButtonDns)
  984. //}}AFX_MSG_MAP
  985. ON_EN_CHANGE(IDC_IPA_IPADDRESS, OnItemChanged)
  986. ON_EN_CHANGE(IDC_IPA_SUBNET_MASK, OnItemChanged)
  987. ON_EN_CHANGE(IDC_EDIT_DOMAIN, OnItemChanged)
  988. END_MESSAGE_MAP()
  989. void
  990. CIPAccessDlg::SetControlStates(
  991. IN int nStyle
  992. )
  993. /*++
  994. Routine Description:
  995. Show/hide controls depending on the type of access descriptor we're
  996. editing.
  997. Arguments:
  998. int nStyle : Radio button style
  999. Return Value:
  1000. None
  1001. --*/
  1002. {
  1003. m_nStyle = nStyle;
  1004. ActivateControl(m_ipa_IPAddress, m_nStyle != RADIO_DOMAIN);
  1005. ActivateControl(m_static_SubnetMask, m_nStyle == RADIO_MULTIPLE);
  1006. ActivateControl(m_ipa_SubnetMask, m_nStyle == RADIO_MULTIPLE);
  1007. ActivateControl(m_button_DNS, m_nStyle == RADIO_SINGLE);
  1008. ActivateControl(m_edit_Domain, m_nStyle == RADIO_DOMAIN);
  1009. //
  1010. // Change the prompt over the editbox/ip address box to explain
  1011. // what's supposed to be edited.
  1012. //
  1013. switch(m_nStyle)
  1014. {
  1015. case RADIO_SINGLE:
  1016. m_static_IpAddress.SetWindowText(m_bstrIPAddress);
  1017. break;
  1018. case RADIO_MULTIPLE:
  1019. m_static_IpAddress.SetWindowText(m_bstrNetworkID);
  1020. break;
  1021. case RADIO_DOMAIN:
  1022. ASSERT(m_fAllowDomains);
  1023. m_static_IpAddress.SetWindowText(m_bstrDomainName);
  1024. break;
  1025. }
  1026. }
  1027. //
  1028. // Message Handlers
  1029. //
  1030. // <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
  1031. BOOL
  1032. CIPAccessDlg::OnInitDialog()
  1033. /*++
  1034. Routine Description:
  1035. WM_INITDIALOG handler. Initialize the dialog.
  1036. Arguments:
  1037. None.
  1038. Return Value:
  1039. TRUE if no focus is to be set automatically, FALSE if the focus
  1040. is already set.
  1041. --*/
  1042. {
  1043. CDialog::OnInitDialog();
  1044. ASSERT_READ_PTR(m_pAccess);
  1045. if (m_pAccess == NULL)
  1046. {
  1047. CError err(ERROR_NOT_ENOUGH_MEMORY);
  1048. TRACEEOLID("access descriptor is NULL -- aborting dialog");
  1049. err.MessageBox(m_hWnd);
  1050. EndDialog(IDCANCEL);
  1051. return FALSE;
  1052. }
  1053. //
  1054. // Domain selection not always available
  1055. //
  1056. ASSERT(!(!m_fAllowDomains && m_pAccess->IsDomainName()));
  1057. ActivateControl(m_radio_Domain, m_fAllowDomains);
  1058. //
  1059. // Use an appropriate title for the dialog depending on
  1060. // whether we're editing a 'grant' item or a 'deny' item
  1061. //
  1062. CComBSTR bstrTitle;
  1063. VERIFY(bstrTitle.LoadString(m_fDenyAccessMode ? IDS_DENY : IDS_GRANT));
  1064. SetWindowText(bstrTitle);
  1065. //
  1066. // Set fields to be edited
  1067. //
  1068. if (m_pAccess->IsDomainName())
  1069. {
  1070. m_edit_Domain.SetWindowText(m_pAccess->QueryDomainName());
  1071. }
  1072. else
  1073. {
  1074. DWORD dwIP = m_pAccess->QueryIPAddress();
  1075. if (dwIP != 0L)
  1076. {
  1077. m_ipa_IPAddress.SetAddress(m_pAccess->QueryIPAddress());
  1078. }
  1079. if (!m_pAccess->IsSingle())
  1080. {
  1081. m_ipa_SubnetMask.SetAddress(m_pAccess->QuerySubnetMask());
  1082. }
  1083. }
  1084. //
  1085. // Configure the dialog appropriately
  1086. //
  1087. SetControlStates(m_nStyle);
  1088. //
  1089. // No changes made yet
  1090. //
  1091. m_button_OK.EnableWindow(FALSE);
  1092. return TRUE;
  1093. }
  1094. void
  1095. CIPAccessDlg::OnRadioSingle()
  1096. /*++
  1097. Routine Description:
  1098. 'Single' radio button has been pressed. Change dialog style
  1099. appropriately.
  1100. Arguments:
  1101. None
  1102. Return Value:
  1103. None
  1104. --*/
  1105. {
  1106. SetControlStates(RADIO_SINGLE);
  1107. OnItemChanged();
  1108. }
  1109. void
  1110. CIPAccessDlg::OnRadioMultiple()
  1111. /*++
  1112. Routine Description:
  1113. 'Multiple' radio button has been pressed. Change dialog style
  1114. appropriately.
  1115. Arguments:
  1116. None
  1117. Return Value:
  1118. None
  1119. --*/
  1120. {
  1121. SetControlStates(RADIO_MULTIPLE);
  1122. OnItemChanged();
  1123. }
  1124. void
  1125. CIPAccessDlg::OnRadioDomain()
  1126. /*++
  1127. Routine Description:
  1128. 'Domain' radio button has been pressed. Change dialog style
  1129. appropriately. If this the first time domain has been pressed,
  1130. put up a warning about the performance implications of using
  1131. domain filtering.
  1132. Arguments:
  1133. None
  1134. Return Value:
  1135. None
  1136. --*/
  1137. {
  1138. ASSERT(m_fAllowDomains);
  1139. static BOOL fShownWarning = FALSE;
  1140. if (!fShownWarning)
  1141. {
  1142. fShownWarning = TRUE;
  1143. DoHelpMessageBox(m_hWnd,IDS_DOMAIN_PERF, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0);
  1144. }
  1145. SetControlStates(RADIO_DOMAIN);
  1146. OnItemChanged();
  1147. }
  1148. void
  1149. CIPAccessDlg::OnItemChanged()
  1150. /*++
  1151. Routine Description:
  1152. Control data has changed. Check to see if sufficient data have been
  1153. entered given the type of access descriptor being edited, and enable
  1154. or disable the OK button based on that result.
  1155. Arguments:
  1156. None
  1157. Return Value:
  1158. None
  1159. --*/
  1160. {
  1161. DWORD dwIP;
  1162. DWORD dwMask;
  1163. BOOL fOK = FALSE;
  1164. CString strDomain;
  1165. switch(m_nStyle)
  1166. {
  1167. case RADIO_DOMAIN:
  1168. m_edit_Domain.GetWindowText(strDomain);
  1169. fOK = !strDomain.IsEmpty();
  1170. break;
  1171. case RADIO_SINGLE:
  1172. m_ipa_IPAddress.GetAddress(dwIP);
  1173. fOK = (dwIP != 0L);
  1174. break;
  1175. case RADIO_MULTIPLE:
  1176. m_ipa_IPAddress.GetAddress(dwIP);
  1177. m_ipa_SubnetMask.GetAddress(dwMask);
  1178. fOK = (dwIP != 0L && dwMask != 0L);
  1179. break;
  1180. }
  1181. m_button_OK.EnableWindow(fOK);
  1182. }
  1183. void
  1184. CIPAccessDlg::OnButtonDns()
  1185. /*++
  1186. Routine Description:
  1187. 'DNS' Button was pressed. Bring up the DNS name resolver dialog
  1188. which will set the value in the associated IP address control.
  1189. Arguments:
  1190. None
  1191. Return Value:
  1192. None
  1193. --*/
  1194. {
  1195. //
  1196. // Ask for a DNS name to resolve to an IP address. The ip address
  1197. // control is passed along to the dns name dialog which will manage
  1198. // the ip addresses in it automatically.
  1199. //
  1200. CDnsNameDlg dlg(&m_ipa_IPAddress);
  1201. dlg.DoModal();
  1202. }
  1203. void
  1204. CIPAccessDlg::OnCancel()
  1205. /*++
  1206. Routine Description:
  1207. IDCANCEL handler. If we had allocated the access descriptor, throw it
  1208. away now.
  1209. Arguments:
  1210. None
  1211. Return Value:
  1212. None
  1213. --*/
  1214. {
  1215. if (m_fNew && m_pAccess != NULL)
  1216. {
  1217. delete m_pAccess;
  1218. m_pAccess = NULL;
  1219. }
  1220. CDialog::OnCancel();
  1221. }
  1222. void
  1223. CIPAccessDlg::OnOK()
  1224. /*++
  1225. Routine Description:
  1226. Handler for IDOK. Save control data to the access descriptor object
  1227. being edited. If we have a list of access descriptors, check for
  1228. duplicates.
  1229. Arguments:
  1230. None
  1231. Return Value:
  1232. None
  1233. --*/
  1234. {
  1235. //
  1236. // Must have been allocated by now.
  1237. //
  1238. ASSERT_READ_PTR(m_pAccess);
  1239. UpdateData(TRUE);
  1240. if (m_nStyle == RADIO_DOMAIN)
  1241. {
  1242. CString strDomain;
  1243. m_edit_Domain.GetWindowText(strDomain);
  1244. //
  1245. // Ensure that wildcards are used only in the first char
  1246. // of the name, or not at all.
  1247. //
  1248. int nWildCard;
  1249. if ((nWildCard = strDomain.ReverseFind(_T('*'))) != -1)
  1250. {
  1251. if (nWildCard != 0
  1252. || strDomain.GetLength() < 3
  1253. || strDomain[1] != _T('.'))
  1254. {
  1255. //
  1256. // Don't dismiss
  1257. //
  1258. m_edit_Domain.SetFocus();
  1259. m_edit_Domain.SetSel(0,-1);
  1260. EditShowBalloon(m_edit_Domain.m_hWnd, IDS_INVALID_DOMAIN_NAME);
  1261. return;
  1262. }
  1263. }
  1264. m_pAccess->SetValues(!m_fDenyAccessMode, strDomain);
  1265. }
  1266. else
  1267. {
  1268. DWORD dwIP;
  1269. m_ipa_IPAddress.GetAddress(dwIP);
  1270. //
  1271. // Filter out bogus ip addresses
  1272. //
  1273. if (dwIP == 0L || dwIP == (DWORD)-1L)
  1274. {
  1275. //
  1276. // Don't dismiss the dialog
  1277. //
  1278. m_ipa_IPAddress.SetFocus();
  1279. // EditShowBalloon(m_ipa_IPAddress.m_hWnd, IDS_IP_INVALID);
  1280. DoHelpMessageBox(m_hWnd,IDS_IP_INVALID, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0);
  1281. return;
  1282. }
  1283. if (m_nStyle == RADIO_SINGLE)
  1284. {
  1285. m_pAccess->SetValues(!m_fDenyAccessMode, dwIP);
  1286. }
  1287. else // Multiple
  1288. {
  1289. DWORD dwMask;
  1290. m_ipa_SubnetMask.GetAddress(dwMask);
  1291. m_pAccess->SetValues(!m_fDenyAccessMode, dwIP, dwMask);
  1292. }
  1293. }
  1294. //
  1295. // Check for duplicates in the list
  1296. //
  1297. if (m_poblAccessList)
  1298. {
  1299. if (m_pAccess->DuplicateInList(*m_poblAccessList))
  1300. {
  1301. //
  1302. // Found duplicate; don't dismiss the dialog
  1303. //
  1304. // EditShowBalloon(m_ipa_IPAddress.m_hWnd, IDS_DUPLICATE_ENTRY);
  1305. DoHelpMessageBox(m_hWnd,IDS_DUPLICATE_ENTRY, MB_APPLMODAL | MB_OK | MB_ICONINFORMATION, 0);
  1306. return;
  1307. }
  1308. }
  1309. //
  1310. // Everything ok -- dismiss the dialog.
  1311. //
  1312. EndDialog(IDOK);
  1313. }
  1314. void CALLBACK MsgBoxCallback(LPHELPINFO lpHelpInfo)
  1315. {
  1316. WinHelpDebug(lpHelpInfo->dwContextId);
  1317. AfxGetApp()->WinHelp(lpHelpInfo->dwContextId);
  1318. }
  1319. // this version accepts a text string
  1320. UINT IisMessageBox(HWND hWnd, LPCTSTR szText, UINT nType, UINT nIDHelp = 0)
  1321. {
  1322. MSGBOXPARAMS mbp;
  1323. memset(&mbp, 0, sizeof mbp);
  1324. mbp.cbSize = sizeof MSGBOXPARAMS;
  1325. mbp.hwndOwner = hWnd;
  1326. mbp.hInstance = AfxGetInstanceHandle();
  1327. mbp.lpszText = szText;
  1328. // if you wanted to specify a different caption, here is where you do it
  1329. CString cap;
  1330. cap.LoadString(IDS_APP_NAME);
  1331. mbp.lpszCaption = cap;
  1332. // if Help ID is not 0, then add a help button
  1333. if (nIDHelp != 0)
  1334. {
  1335. mbp.dwStyle = nType | MB_HELP;
  1336. }
  1337. else
  1338. {
  1339. mbp.dwStyle = nType;
  1340. }
  1341. // mbp.lpszIcon = ; // note, you could provide your own custom ICON here!
  1342. mbp.dwContextHelpId = nIDHelp;
  1343. mbp.lpfnMsgBoxCallback = &MsgBoxCallback;
  1344. // mbp.dwLanguageId = 0x0409;
  1345. return ::MessageBoxIndirect(&mbp);
  1346. }
  1347. // this version accepts a resource string identifier
  1348. UINT IisMessageBox(HWND hWnd, UINT nIDText, UINT nType, UINT nIDHelp = 0)
  1349. {
  1350. CString s;
  1351. s.LoadString(nIDText);
  1352. return IisMessageBox(hWnd, s, nType, nIDHelp);
  1353. }