Source code of Windows XP (NT5)
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.

1905 lines
49 KiB

  1. //+----------------------------------------------------------------------------
  2. //
  3. // DS Administration MMC snapin.
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1999
  7. //
  8. // File: queryui.cpp
  9. //
  10. //--------------------------------------------------------------------------
  11. #include "stdafx.h"
  12. #include "resource.h"
  13. #include "queryui.h"
  14. #include "dssnap.h"
  15. #include "uiutil.h"
  16. #include <cmnquery.h> // DSFind
  17. #include <dsquery.h> // DSFind
  18. #include <dsclient.h> // BrowseForContainer
  19. #include <dsqueryp.h> // COLUMNINFO and QueryParamsAddQueryString, QueryParamsAlloc helpers
  20. #include <cmnquryp.h> // CQFF_ISNEVERLISTED
  21. #include <lmaccess.h> // UF_ACCOUNTDISABLE and UF_DONT_EXPIRE_PASSWD
  22. #include <ntldap.h> // LDAP_MATCHING_RULE_BIT_AND_W
  23. #define DSQF_LAST_LOGON_QUERY 0x00000001
  24. #define DSQF_NON_EXPIRING_PWD_QUERY 0x00000004
  25. //
  26. // Used to set the maximum text length on fields in the new query dialog
  27. //
  28. #define MAX_QUERY_NAME_LENGTH 259
  29. #define MAX_QUERY_DESC_LENGTH 1024
  30. typedef struct
  31. {
  32. UINT nDisplayStringID;
  33. PWSTR pszFormatString;
  34. } QUERYSTRINGS, * PQUERYSTRINGS;
  35. QUERYSTRINGS g_pQueryStrings[] = {
  36. { IDS_STARTSWITH, L"(%s=%s*)" },
  37. { IDS_ENDSWITH, L"(%s=*%s)" },
  38. { IDS_ISEXACTLY, L"(%s=%s)" },
  39. { IDS_ISNOT, L"(!%s=%s)" },
  40. { IDS_PRESENT, L"(%s=%s*)" }, // NOTE: the second string needs to be NULL here
  41. { IDS_NOTPRESENT, L"(!%s=%s*)" }, // NOTE: the second string needs to be NULL here
  42. { 0, NULL }
  43. };
  44. static const CString g_szUserAccountCtrlQuery = L"(userAccountControl:" + CString(LDAP_MATCHING_RULE_BIT_AND_W) + L":=%u)";
  45. /*-----------------------------------------------------------------------------
  46. / QueryParamsAlloc
  47. / ----------------
  48. / Construct a block we can pass to the DS query handler which contains
  49. / all the parameters for the query.
  50. /
  51. / In:
  52. / ppDsQueryParams -> receives the parameter block
  53. / pQuery -> LDAP query string to be used
  54. / iColumns = number of columns
  55. / pColumnInfo -> column info structure to use
  56. /
  57. / Out:
  58. / HRESULT
  59. /----------------------------------------------------------------------------*/
  60. HRESULT QueryParamsAlloc(LPDSQUERYPARAMS* ppDsQueryParams, LPWSTR pQuery, LONG iColumns, LPCOLUMNINFO aColumnInfo)
  61. {
  62. HRESULT hr = S_OK;
  63. LPDSQUERYPARAMS pDsQueryParams = NULL;
  64. size_t cbStruct;
  65. LONG i;
  66. ASSERT(!*ppDsQueryParams);
  67. TRACE(L"QueryParamsAlloc");
  68. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  69. if ( !pQuery || !iColumns || !ppDsQueryParams )
  70. {
  71. return E_INVALIDARG;
  72. }
  73. //
  74. // Compute the size of the structure we need to be using
  75. //
  76. cbStruct = sizeof(DSQUERYPARAMS) + (sizeof(DSCOLUMN)*iColumns);
  77. cbStruct += (wcslen(pQuery) + 1) * sizeof(WCHAR);
  78. for (i = 0; i < iColumns; i++)
  79. {
  80. if (aColumnInfo[i].pPropertyName)
  81. {
  82. cbStruct += (wcslen(aColumnInfo[i].pPropertyName) + 1) * sizeof(WCHAR);
  83. }
  84. }
  85. pDsQueryParams = (LPDSQUERYPARAMS)CoTaskMemAlloc(cbStruct);
  86. if (!pDsQueryParams)
  87. {
  88. return E_OUTOFMEMORY;
  89. }
  90. //
  91. // Structure allocated so lets fill it with data
  92. //
  93. pDsQueryParams->cbStruct = static_cast<DWORD>(cbStruct);
  94. pDsQueryParams->dwFlags = 0;
  95. pDsQueryParams->hInstance = _Module.m_hInst;
  96. pDsQueryParams->iColumns = iColumns;
  97. pDsQueryParams->dwReserved = 0;
  98. cbStruct = sizeof(DSQUERYPARAMS) + (sizeof(DSCOLUMN)*iColumns);
  99. pDsQueryParams->offsetQuery = static_cast<LONG>(cbStruct);
  100. CopyMemory(&(((LPBYTE)pDsQueryParams)[cbStruct]), pQuery, (wcslen(pQuery) + 1) * sizeof(WCHAR));
  101. cbStruct += (wcslen(pQuery) + 1) * sizeof(WCHAR);
  102. for ( i = 0 ; i < iColumns ; i++ )
  103. {
  104. pDsQueryParams->aColumns[i].dwFlags = 0;
  105. pDsQueryParams->aColumns[i].fmt = aColumnInfo[i].fmt;
  106. pDsQueryParams->aColumns[i].cx = aColumnInfo[i].cx;
  107. pDsQueryParams->aColumns[i].idsName = aColumnInfo[i].idsName;
  108. pDsQueryParams->aColumns[i].dwReserved = 0;
  109. if ( aColumnInfo[i].pPropertyName )
  110. {
  111. pDsQueryParams->aColumns[i].offsetProperty = static_cast<LONG>(cbStruct);
  112. CopyMemory(&(((LPBYTE)pDsQueryParams)[cbStruct]), aColumnInfo[i].pPropertyName, (wcslen(aColumnInfo[i].pPropertyName) + 1) * sizeof(WCHAR));
  113. cbStruct += (wcslen(aColumnInfo[i].pPropertyName) + 1) * sizeof(WCHAR);
  114. }
  115. else
  116. {
  117. pDsQueryParams->aColumns[i].offsetProperty = aColumnInfo[i].iPropertyIndex;
  118. }
  119. }
  120. *ppDsQueryParams = pDsQueryParams;
  121. return hr;
  122. }
  123. /*-----------------------------------------------------------------------------
  124. / QueryParamsAddQueryString
  125. / -------------------------
  126. / Given an existing DS query block appened the given LDAP query string into
  127. / it. We assume that the query block has been allocated by IMalloc (or CoTaskMemAlloc).
  128. /
  129. / In:
  130. / ppDsQueryParams -> receives the parameter block
  131. / pQuery -> LDAP query string to be appended
  132. /
  133. / Out:
  134. / HRESULT
  135. /----------------------------------------------------------------------------*/
  136. HRESULT QueryParamsAddQueryString(LPDSQUERYPARAMS* ppDsQueryParams, LPWSTR pQuery)
  137. {
  138. HRESULT hr = S_OK;
  139. LPWSTR pOriginalQuery = NULL;
  140. LPDSQUERYPARAMS pDsQuery = *ppDsQueryParams;
  141. size_t cbQuery;
  142. LONG i;
  143. LPVOID pv;
  144. ASSERT(*ppDsQueryParams);
  145. TRACE(_T("QueryParamsAddQueryString"));
  146. if ( pQuery )
  147. {
  148. if (!pDsQuery)
  149. {
  150. return E_INVALIDARG;
  151. }
  152. // Work out the size of the bits we are adding, take a copy of the
  153. // query string and finally re-alloc the query block (which may cause it
  154. // to move).
  155. cbQuery = ((wcslen(pQuery) + 1) * sizeof(WCHAR));
  156. TRACE(_T("DSQUERYPARAMS being resized by %d bytes"));
  157. i = static_cast<LONG>((wcslen((LPWSTR)ByteOffset(pDsQuery, pDsQuery->offsetQuery)) + 1) * sizeof(WCHAR));
  158. pOriginalQuery = (WCHAR*) new BYTE[i];
  159. if (!pOriginalQuery)
  160. {
  161. return E_OUTOFMEMORY;
  162. }
  163. lstrcpyW(pOriginalQuery, (LPWSTR)ByteOffset(pDsQuery, pDsQuery->offsetQuery));
  164. pv = CoTaskMemRealloc(*ppDsQueryParams, pDsQuery->cbStruct+cbQuery);
  165. if ( pv == NULL )
  166. {
  167. delete[] pOriginalQuery;
  168. pOriginalQuery = 0;
  169. return E_OUTOFMEMORY;
  170. }
  171. *ppDsQueryParams = (LPDSQUERYPARAMS) pv;
  172. pDsQuery = *ppDsQueryParams; // if may have moved
  173. // Now move everything above the query string up, and fix all the
  174. // offsets that reference those items (probably the property table),
  175. // finally adjust the size to reflect the change
  176. MoveMemory(ByteOffset(pDsQuery, pDsQuery->offsetQuery+cbQuery),
  177. ByteOffset(pDsQuery, pDsQuery->offsetQuery),
  178. (pDsQuery->cbStruct - pDsQuery->offsetQuery));
  179. for ( i = 0 ; i < pDsQuery->iColumns ; i++ )
  180. {
  181. if ( pDsQuery->aColumns[i].offsetProperty > pDsQuery->offsetQuery )
  182. {
  183. pDsQuery->aColumns[i].offsetProperty += static_cast<LONG>(cbQuery);
  184. }
  185. }
  186. wcscpy((LPWSTR)ByteOffset(pDsQuery, pDsQuery->offsetQuery), pOriginalQuery);
  187. wcscat((LPWSTR)ByteOffset(pDsQuery, pDsQuery->offsetQuery), pQuery);
  188. pDsQuery->cbStruct += static_cast<DWORD>(cbQuery);
  189. delete[] pOriginalQuery;
  190. pOriginalQuery = 0;
  191. }
  192. return hr;
  193. }
  194. ///////////////////////////////////////////////////////////////////////////////
  195. // AddQueryUnitWithModifier
  196. HRESULT AddQueryUnitWithModifier(UINT nModifierStringID,
  197. PCWSTR pszAttrName,
  198. PCWSTR pszValue,
  199. CString& szFilter)
  200. {
  201. HRESULT hr = S_OK;
  202. ASSERT(pszAttrName != NULL);
  203. if (pszAttrName == NULL)
  204. {
  205. return E_INVALIDARG;
  206. }
  207. CString szNewFilter;
  208. PQUERYSTRINGS pQueryStrings = g_pQueryStrings;
  209. PWSTR pszFormatString = NULL;
  210. while (pQueryStrings->nDisplayStringID != 0)
  211. {
  212. if (nModifierStringID == pQueryStrings->nDisplayStringID)
  213. {
  214. pszFormatString = pQueryStrings->pszFormatString;
  215. break;
  216. }
  217. pQueryStrings++;
  218. }
  219. if (pszFormatString != NULL)
  220. {
  221. szNewFilter.Format(pszFormatString, pszAttrName, pszValue);
  222. szFilter += szNewFilter;
  223. }
  224. else
  225. {
  226. hr = E_INVALIDARG;
  227. }
  228. return hr;
  229. }
  230. ///////////////////////////////////////////////////////////////////////////////
  231. // CQueryPageBase
  232. BEGIN_MESSAGE_MAP(CQueryPageBase, CHelpDialog)
  233. END_MESSAGE_MAP()
  234. ///////////////////////////////////////////////////////////////////////////////
  235. // CStdQueryPage
  236. #define FILTER_PREFIX_USER L"(objectCategory=person)(objectClass=user)"
  237. #define FILTER_PREFIX_COMPUTER L"(objectCategory=computer)"
  238. #define FILTER_PREFIX_GROUP L"(objectCategory=group)"
  239. #define ATTR_COL_NAME L"name"
  240. #define ATTR_COL_DESC L"description"
  241. COLUMNINFO UserColumn[] =
  242. {
  243. { 0, 40, IDS_QUERY_COL_NAME, 0, ATTR_COL_NAME },
  244. { 0, 40, IDS_QUERY_COL_DESC, 0, ATTR_COL_DESC }
  245. };
  246. int cUserColumns = 2;
  247. BEGIN_MESSAGE_MAP(CStdQueryPage, CQueryPageBase)
  248. ON_CBN_SELCHANGE(IDC_NAME_COMBO, OnNameComboChange)
  249. ON_CBN_SELCHANGE(IDC_DESCRIPTION_COMBO, OnDescriptionComboChange)
  250. END_MESSAGE_MAP()
  251. void CStdQueryPage::DoContextHelp(HWND hWndControl)
  252. {
  253. if (hWndControl)
  254. {
  255. ::WinHelp(hWndControl,
  256. DSADMIN_CONTEXT_HELP_FILE,
  257. HELP_WM_HELP,
  258. (DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_QUERY_STD_PAGE);
  259. }
  260. }
  261. BOOL CStdQueryPage::OnInitDialog()
  262. {
  263. CHelpDialog::OnInitDialog();
  264. PQUERYSTRINGS pQueryStrings = g_pQueryStrings;
  265. ASSERT(pQueryStrings != NULL);
  266. //
  267. // Fill in the combo boxes
  268. //
  269. while (pQueryStrings->nDisplayStringID != 0)
  270. {
  271. CString szComboString;
  272. VERIFY(szComboString.LoadString(pQueryStrings->nDisplayStringID));
  273. //
  274. // Fill in the Name combo
  275. //
  276. LRESULT lRes = SendDlgItemMessage(IDC_NAME_COMBO, CB_ADDSTRING, 0, (LPARAM)(PCWSTR)szComboString);
  277. if (lRes != CB_ERR)
  278. {
  279. lRes = SendDlgItemMessage(IDC_NAME_COMBO, CB_SETITEMDATA, (WPARAM)lRes, (LPARAM)pQueryStrings->nDisplayStringID);
  280. ASSERT(lRes != CB_ERR);
  281. }
  282. //
  283. // Fill in the Description combo
  284. //
  285. lRes = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_ADDSTRING, 0, (LPARAM)(PCWSTR)szComboString);
  286. if (lRes != CB_ERR)
  287. {
  288. lRes = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_SETITEMDATA, (WPARAM)lRes, (LPARAM)pQueryStrings->nDisplayStringID);
  289. ASSERT(lRes != CB_ERR);
  290. }
  291. pQueryStrings++;
  292. }
  293. //
  294. // Insert an empty so that there is a way to undo changes
  295. //
  296. LRESULT lBlankName = SendDlgItemMessage(IDC_NAME_COMBO, CB_ADDSTRING, 0, (LPARAM)L"");
  297. if (lBlankName != CB_ERR)
  298. {
  299. SendDlgItemMessage(IDC_NAME_COMBO, CB_SETITEMDATA, (WPARAM)lBlankName, (LPARAM)0);
  300. }
  301. //
  302. // Insert an empty so that there is a way to undo changes
  303. //
  304. LRESULT lBlankDesc = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_ADDSTRING, 0, (LPARAM)L"");
  305. if (lBlankDesc != CB_ERR)
  306. {
  307. SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_SETITEMDATA, (WPARAM)lBlankDesc, (LPARAM)0);
  308. }
  309. //
  310. // Force the UI to enable and disable controls related to the combo boxes
  311. //
  312. OnNameComboChange();
  313. OnDescriptionComboChange();
  314. return FALSE;
  315. }
  316. void CStdQueryPage::OnNameComboChange()
  317. {
  318. LRESULT lRes = SendDlgItemMessage(IDC_NAME_COMBO, CB_GETCURSEL, 0, 0);
  319. if (lRes != CB_ERR)
  320. {
  321. LRESULT lData = SendDlgItemMessage(IDC_NAME_COMBO, CB_GETITEMDATA, lRes, 0);
  322. if (lData != CB_ERR)
  323. {
  324. if (lData == IDS_PRESENT || lData == IDS_NOTPRESENT || lData == 0)
  325. {
  326. GetDlgItem(IDC_NAME_EDIT)->EnableWindow(FALSE);
  327. SetDlgItemText(IDC_NAME_EDIT, L"");
  328. }
  329. else
  330. {
  331. GetDlgItem(IDC_NAME_EDIT)->EnableWindow(TRUE);
  332. }
  333. }
  334. }
  335. else
  336. {
  337. GetDlgItem(IDC_NAME_EDIT)->EnableWindow(FALSE);
  338. }
  339. }
  340. void CStdQueryPage::OnDescriptionComboChange()
  341. {
  342. LRESULT lRes = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_GETCURSEL, 0, 0);
  343. if (lRes != CB_ERR)
  344. {
  345. LRESULT lData = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_GETITEMDATA, lRes, 0);
  346. if (lData != CB_ERR)
  347. {
  348. if (lData == IDS_PRESENT || lData == IDS_NOTPRESENT || lData == 0)
  349. {
  350. GetDlgItem(IDC_DESCRIPTION_EDIT)->EnableWindow(FALSE);
  351. SetDlgItemText(IDC_DESCRIPTION_EDIT, L"");
  352. }
  353. else
  354. {
  355. GetDlgItem(IDC_DESCRIPTION_EDIT)->EnableWindow(TRUE);
  356. }
  357. }
  358. }
  359. else
  360. {
  361. GetDlgItem(IDC_DESCRIPTION_EDIT)->EnableWindow(FALSE);
  362. }
  363. }
  364. void CStdQueryPage::Init()
  365. {
  366. //
  367. // Clear all controls
  368. //
  369. SetDlgItemText(IDC_NAME_EDIT, L"");
  370. SetDlgItemText(IDC_DESCRIPTION_EDIT, L"");
  371. //
  372. // Reselect the blank string in the combo boxes
  373. //
  374. LRESULT lRes = SendDlgItemMessage(IDC_NAME_COMBO, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)L"");
  375. if (lRes != CB_ERR)
  376. {
  377. SendDlgItemMessage(IDC_NAME_COMBO, CB_SETCURSEL, lRes, 0);
  378. }
  379. lRes = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_FINDSTRINGEXACT, (WPARAM)-1, (LPARAM)L"");
  380. if (lRes != CB_ERR)
  381. {
  382. SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_SETCURSEL, lRes, 0);
  383. }
  384. }
  385. HRESULT CStdQueryPage::GetQueryParams(LPDSQUERYPARAMS* ppDsQueryParams)
  386. {
  387. HRESULT hr = S_OK;
  388. //
  389. // Build the filter string here
  390. //
  391. CString szFilter;
  392. CString szName;
  393. CString szDescription;
  394. GetDlgItemText(IDC_NAME_EDIT, szName);
  395. GetDlgItemText(IDC_DESCRIPTION_EDIT, szDescription);
  396. //
  397. // Get the selection of the modifier combo
  398. //
  399. LRESULT lSel = SendDlgItemMessage(IDC_NAME_COMBO, CB_GETCURSEL, 0, 0);
  400. if (lSel != CB_ERR)
  401. {
  402. //
  403. // Retrieve the associated string ID
  404. //
  405. LRESULT lData = SendDlgItemMessage(IDC_NAME_COMBO, CB_GETITEMDATA, lSel, 0);
  406. if (lData != CB_ERR)
  407. {
  408. if (!szName.IsEmpty() || lData == IDS_PRESENT || lData == IDS_NOTPRESENT)
  409. {
  410. AddQueryUnitWithModifier(static_cast<UINT>(lData),
  411. ATTR_COL_NAME,
  412. szName,
  413. szFilter);
  414. }
  415. }
  416. }
  417. //
  418. // Get the selection of the modifier combo
  419. //
  420. lSel = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_GETCURSEL, 0, 0);
  421. if (lSel != CB_ERR)
  422. {
  423. //
  424. // Retrieve the associated string ID
  425. //
  426. LRESULT lData = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_GETITEMDATA, lSel, 0);
  427. if (lData != CB_ERR)
  428. {
  429. if (!szDescription.IsEmpty() || lData == IDS_PRESENT || lData == IDS_NOTPRESENT)
  430. {
  431. AddQueryUnitWithModifier(static_cast<UINT>(lData),
  432. ATTR_COL_DESC,
  433. szDescription,
  434. szFilter);
  435. }
  436. }
  437. }
  438. if (!szFilter.IsEmpty())
  439. {
  440. szFilter = m_szFilterPrefix + szFilter;
  441. hr = BuildQueryParams(ppDsQueryParams, (LPWSTR)(LPCWSTR)szFilter);
  442. }
  443. return hr;
  444. }
  445. HRESULT CStdQueryPage::BuildQueryParams(LPDSQUERYPARAMS* ppDsQueryParams, LPWSTR pQuery)
  446. {
  447. ASSERT(pQuery);
  448. if(*ppDsQueryParams)
  449. {
  450. return QueryParamsAddQueryString(ppDsQueryParams, pQuery);
  451. }
  452. return QueryParamsAlloc(ppDsQueryParams, pQuery, cUserColumns, UserColumn);
  453. }
  454. HRESULT CStdQueryPage::Persist(IPersistQuery* pPersistQuery, BOOL fRead)
  455. {
  456. HRESULT hr = S_OK;
  457. if (pPersistQuery == NULL)
  458. {
  459. ASSERT(FALSE);
  460. return E_INVALIDARG;
  461. }
  462. if (fRead)
  463. {
  464. //
  465. // Read the Name combo value
  466. //
  467. int iData = 0;
  468. hr = pPersistQuery->ReadInt(m_szFilterPrefix, L"NameCombo", &iData);
  469. if (FAILED(hr))
  470. {
  471. TRACE(_T("Failed to read int \"NameCombo\" from stream: 0x%x\n"), hr);
  472. ASSERT(FALSE);
  473. return hr;
  474. }
  475. //
  476. // Select the appropriate list box item
  477. //
  478. SelectComboAssociatedWithData(IDC_NAME_COMBO, iData);
  479. if (iData != 0 && iData != IDS_PRESENT && iData != IDS_NOTPRESENT)
  480. {
  481. //
  482. // Read the name edit value
  483. //
  484. WCHAR szBuf[MAX_PATH] = {0};
  485. hr = pPersistQuery->ReadString(m_szFilterPrefix, L"NameEdit", szBuf, MAX_PATH);
  486. if (FAILED(hr))
  487. {
  488. TRACE(_T("Failed to read string \"NameEdit\" from stream: 0x%x\n"), hr);
  489. ASSERT(FALSE);
  490. return hr;
  491. }
  492. if (szBuf != NULL)
  493. {
  494. SetDlgItemText(IDC_NAME_EDIT, szBuf);
  495. }
  496. }
  497. else
  498. {
  499. GetDlgItem(IDC_NAME_EDIT)->EnableWindow(FALSE);
  500. }
  501. //
  502. // Read the Description combo value
  503. //
  504. iData = 0;
  505. hr = pPersistQuery->ReadInt(m_szFilterPrefix, L"DescCombo", &iData);
  506. if (FAILED(hr))
  507. {
  508. TRACE(_T("Failed to read int \"DescCombo\" from stream: 0x%x\n"), hr);
  509. ASSERT(FALSE);
  510. return hr;
  511. }
  512. //
  513. // Select the appropriate list box item
  514. //
  515. SelectComboAssociatedWithData(IDC_DESCRIPTION_COMBO, iData);
  516. if (iData != 0 && iData != IDS_PRESENT && iData != IDS_NOTPRESENT)
  517. {
  518. //
  519. // Read the name edit value
  520. //
  521. WCHAR szBuf[MAX_PATH] = {0};
  522. hr = pPersistQuery->ReadString(m_szFilterPrefix, L"DescEdit", szBuf, MAX_PATH);
  523. if (FAILED(hr))
  524. {
  525. TRACE(_T("Failed to read string \"DescEdit\" from stream: 0x%x\n"), hr);
  526. ASSERT(FALSE);
  527. return hr;
  528. }
  529. if (szBuf != NULL)
  530. {
  531. SetDlgItemText(IDC_DESCRIPTION_EDIT, szBuf);
  532. }
  533. }
  534. else
  535. {
  536. GetDlgItem(IDC_DESCRIPTION_EDIT)->EnableWindow(FALSE);
  537. }
  538. OnNameComboChange();
  539. OnDescriptionComboChange();
  540. }
  541. else // write
  542. {
  543. //
  544. // Write out the name info
  545. //
  546. LRESULT lSel = SendDlgItemMessage(IDC_NAME_COMBO, CB_GETCURSEL, 0, 0);
  547. if (lSel != CB_ERR)
  548. {
  549. //
  550. // Retrieve the associated string ID
  551. //
  552. LRESULT lData = SendDlgItemMessage(IDC_NAME_COMBO, CB_GETITEMDATA, lSel, 0);
  553. if (lData != CB_ERR)
  554. {
  555. hr = pPersistQuery->WriteInt(m_szFilterPrefix, L"NameCombo", static_cast<int>(lData));
  556. if (FAILED(hr))
  557. {
  558. ASSERT(FALSE);
  559. return hr;
  560. }
  561. if (lData != 0 && lData != IDS_PRESENT && lData != IDS_NOTPRESENT)
  562. {
  563. CString szName;
  564. GetDlgItemText(IDC_NAME_EDIT, szName);
  565. hr = pPersistQuery->WriteString(m_szFilterPrefix, L"NameEdit", szName);
  566. if (FAILED(hr))
  567. {
  568. ASSERT(FALSE);
  569. return hr;
  570. }
  571. }
  572. }
  573. }
  574. else
  575. {
  576. //
  577. // If there hasn't been a selection, write in the empty string value
  578. //
  579. hr = pPersistQuery->WriteInt(m_szFilterPrefix, L"NameCombo", 0);
  580. }
  581. //
  582. // Write out the description info
  583. //
  584. lSel = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_GETCURSEL, 0, 0);
  585. if (lSel != CB_ERR)
  586. {
  587. //
  588. // Retrieve the associated string ID
  589. //
  590. LRESULT lData = SendDlgItemMessage(IDC_DESCRIPTION_COMBO, CB_GETITEMDATA, lSel, 0);
  591. if (lData != CB_ERR)
  592. {
  593. hr = pPersistQuery->WriteInt(m_szFilterPrefix, L"DescCombo", static_cast<int>(lData));
  594. if (FAILED(hr))
  595. {
  596. ASSERT(FALSE);
  597. return hr;
  598. }
  599. if (lData != 0 && lData != IDS_PRESENT && lData != IDS_NOTPRESENT)
  600. {
  601. CString szDescription;
  602. GetDlgItemText(IDC_DESCRIPTION_EDIT, szDescription);
  603. hr = pPersistQuery->WriteString(m_szFilterPrefix, L"DescEdit", szDescription);
  604. if (FAILED(hr))
  605. {
  606. ASSERT(FALSE);
  607. return hr;
  608. }
  609. }
  610. }
  611. }
  612. else
  613. {
  614. //
  615. // If there hasn't been a selection, write in the empty string value
  616. //
  617. hr = pPersistQuery->WriteInt(m_szFilterPrefix, L"DescCombo", 0);
  618. }
  619. }
  620. return hr;
  621. }
  622. void CStdQueryPage::SelectComboAssociatedWithData(UINT nCtrlID, LRESULT lData)
  623. {
  624. //
  625. // Selects the item with the associated data in a combo box
  626. //
  627. LRESULT lRes = SendDlgItemMessage(nCtrlID, CB_GETCOUNT, 0, 0);
  628. if (lRes != CB_ERR)
  629. {
  630. for (int idx = 0; idx < static_cast<int>(lRes); idx++)
  631. {
  632. LRESULT lRetData = SendDlgItemMessage(nCtrlID, CB_GETITEMDATA, (WPARAM)idx, 0);
  633. if (lRetData != CB_ERR)
  634. {
  635. if (lRetData == lData)
  636. {
  637. SendDlgItemMessage(nCtrlID, CB_SETCURSEL, (WPARAM)idx, 0);
  638. break;
  639. }
  640. }
  641. }
  642. }
  643. }
  644. ///////////////////////////////////////////////////////////////////////////////
  645. // CUserComputerQueryPage
  646. BEGIN_MESSAGE_MAP(CUserComputerQueryPage, CStdQueryPage)
  647. ON_WM_HELPINFO()
  648. END_MESSAGE_MAP()
  649. BOOL CUserComputerQueryPage::OnInitDialog()
  650. {
  651. return CStdQueryPage::OnInitDialog();
  652. }
  653. void CUserComputerQueryPage::DoContextHelp(HWND hWndControl)
  654. {
  655. if (hWndControl)
  656. {
  657. ::WinHelp(hWndControl,
  658. DSADMIN_CONTEXT_HELP_FILE,
  659. HELP_WM_HELP,
  660. (DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_QUERY_USER_PAGE);
  661. }
  662. }
  663. void CUserComputerQueryPage::Init()
  664. {
  665. //
  666. // Clear all controls
  667. //
  668. CStdQueryPage::Init();
  669. }
  670. HRESULT CUserComputerQueryPage::GetQueryParams(LPDSQUERYPARAMS* ppDsQueryParams)
  671. {
  672. HRESULT hr = S_OK;
  673. //
  674. // Build the filter string here
  675. //
  676. hr = CStdQueryPage::GetQueryParams(ppDsQueryParams);
  677. CString szFilter;
  678. BOOL bDisabledAccounts = FALSE;
  679. //
  680. // Get disabled accounts check
  681. //
  682. LRESULT lRes = SendDlgItemMessage(IDC_DISABLED_ACCOUNTS_CHECK, BM_GETCHECK, 0, 0);
  683. if (lRes == BST_CHECKED)
  684. {
  685. bDisabledAccounts = TRUE;
  686. }
  687. if (bDisabledAccounts)
  688. {
  689. szFilter.Format(g_szUserAccountCtrlQuery, UF_ACCOUNTDISABLE);
  690. szFilter = m_szFilterPrefix + szFilter;
  691. hr = BuildQueryParams(ppDsQueryParams, (LPWSTR)(LPCWSTR)szFilter);
  692. }
  693. return hr;
  694. }
  695. HRESULT CUserComputerQueryPage::Persist(IPersistQuery* pPersistQuery, BOOL fRead)
  696. {
  697. HRESULT hr = CStdQueryPage::Persist(pPersistQuery, fRead);
  698. if (FAILED(hr))
  699. {
  700. return hr;
  701. }
  702. if (pPersistQuery == NULL)
  703. {
  704. ASSERT(FALSE);
  705. return E_INVALIDARG;
  706. }
  707. if (fRead)
  708. {
  709. //
  710. // Read disabled accounts flag
  711. //
  712. int iData = 0;
  713. hr = pPersistQuery->ReadInt(m_szFilterPrefix, L"DisableCheck", &iData);
  714. if (FAILED(hr))
  715. {
  716. TRACE(_T("Failed to read int \"DisableCheck\" from stream: 0x%x\n"), hr);
  717. ASSERT(FALSE);
  718. return hr;
  719. }
  720. SendDlgItemMessage(IDC_DISABLED_ACCOUNTS_CHECK, BM_SETCHECK, (iData > 0) ? BST_CHECKED : BST_UNCHECKED, 0);
  721. }
  722. else
  723. {
  724. //
  725. // Write disabled accounts flag
  726. //
  727. LRESULT lRes = SendDlgItemMessage(IDC_DISABLED_ACCOUNTS_CHECK, BM_GETCHECK, 0, 0);
  728. if (lRes != -1)
  729. {
  730. int iRes = (lRes == BST_CHECKED) ? 1 : 0;
  731. hr = pPersistQuery->WriteInt(m_szFilterPrefix, L"DisableCheck", iRes);
  732. if (FAILED(hr))
  733. {
  734. ASSERT(FALSE);
  735. return hr;
  736. }
  737. }
  738. }
  739. return hr;
  740. }
  741. ///////////////////////////////////////////////////////////////////////////////
  742. // CUserQueryPage
  743. BEGIN_MESSAGE_MAP(CUserQueryPage, CStdQueryPage)
  744. END_MESSAGE_MAP()
  745. BOOL CUserQueryPage::OnInitDialog()
  746. {
  747. return CUserComputerQueryPage::OnInitDialog();
  748. }
  749. void CUserQueryPage::DoContextHelp(HWND hWndControl)
  750. {
  751. if (hWndControl)
  752. {
  753. ::WinHelp(hWndControl,
  754. DSADMIN_CONTEXT_HELP_FILE,
  755. HELP_WM_HELP,
  756. (DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_QUERY_USER_PAGE);
  757. }
  758. }
  759. void CUserQueryPage::Init()
  760. {
  761. //
  762. // Clear all controls
  763. //
  764. CUserComputerQueryPage::Init();
  765. }
  766. HRESULT CUserQueryPage::GetQueryParams(LPDSQUERYPARAMS* ppDsQueryParams)
  767. {
  768. HRESULT hr = S_OK;
  769. //
  770. // Build the filter string here
  771. //
  772. hr = CUserComputerQueryPage::GetQueryParams(ppDsQueryParams);
  773. CString szFilter;
  774. BOOL bNonExpPwds = FALSE;
  775. BOOL bLastLogon = FALSE;
  776. DWORD dwLastLogonData = 0;
  777. //
  778. // Get non expiring password check
  779. //
  780. LRESULT lRes = SendDlgItemMessage(IDC_NON_EXPIRING_PWD_CHECK, BM_GETCHECK, 0, 0);
  781. if (lRes == BST_CHECKED)
  782. {
  783. bNonExpPwds = TRUE;
  784. }
  785. //
  786. // Get stale acccounts check
  787. //
  788. lRes = SendDlgItemMessage(IDC_LASTLOGON_COMBO, CB_GETCURSEL, 0, 0);
  789. if (lRes == CB_ERR)
  790. {
  791. lRes = m_lLogonSelection;
  792. }
  793. if (lRes != -1 || lRes != CB_ERR)
  794. {
  795. LRESULT lTextLen = SendDlgItemMessage(IDC_LASTLOGON_COMBO, CB_GETLBTEXTLEN, (WPARAM)lRes, 0);
  796. if (lTextLen != CB_ERR)
  797. {
  798. if (lTextLen > 0)
  799. {
  800. bLastLogon = TRUE;
  801. WCHAR* pszData = new WCHAR[lTextLen + 1];
  802. if (pszData != NULL)
  803. {
  804. LRESULT lData = SendDlgItemMessage(IDC_LASTLOGON_COMBO, CB_GETLBTEXT, (WPARAM)lRes, (LPARAM)pszData);
  805. if (lData != CB_ERR)
  806. {
  807. dwLastLogonData = static_cast<DWORD>(_wtol(pszData));
  808. }
  809. delete[] pszData;
  810. pszData = NULL;
  811. }
  812. }
  813. }
  814. }
  815. if (bNonExpPwds || bLastLogon)
  816. {
  817. if (bNonExpPwds)
  818. {
  819. szFilter.Format(g_szUserAccountCtrlQuery, UF_DONT_EXPIRE_PASSWD);
  820. }
  821. szFilter = m_szFilterPrefix + szFilter;
  822. hr = BuildQueryParams(ppDsQueryParams, (LPWSTR)(LPCWSTR)szFilter);
  823. if (SUCCEEDED(hr))
  824. {
  825. if (bLastLogon)
  826. {
  827. (*ppDsQueryParams)->dwFlags |= DSQF_LAST_LOGON_QUERY;
  828. (*ppDsQueryParams)->dwReserved = dwLastLogonData;
  829. }
  830. }
  831. }
  832. return hr;
  833. }
  834. HRESULT CUserQueryPage::Persist(IPersistQuery* pPersistQuery, BOOL fRead)
  835. {
  836. HRESULT hr = CUserComputerQueryPage::Persist(pPersistQuery, fRead);
  837. if (FAILED(hr))
  838. {
  839. return hr;
  840. }
  841. if (pPersistQuery == NULL)
  842. {
  843. ASSERT(FALSE);
  844. return E_INVALIDARG;
  845. }
  846. if (fRead)
  847. {
  848. //
  849. // Read non expiring pwds flag
  850. //
  851. int iData = 0;
  852. hr = pPersistQuery->ReadInt(m_szFilterPrefix, L"NonExpPwdCheck", &iData);
  853. if (FAILED(hr))
  854. {
  855. TRACE(_T("Failed to read int \"NonExpPwdCheck\" from stream: 0x%x\n"), hr);
  856. ASSERT(FALSE);
  857. return hr;
  858. }
  859. SendDlgItemMessage(IDC_NON_EXPIRING_PWD_CHECK, BM_SETCHECK, (iData > 0) ? BST_CHECKED : BST_UNCHECKED, 0);
  860. iData = 0;
  861. hr = pPersistQuery->ReadInt(m_szFilterPrefix, L"LastLogonCombo", &iData);
  862. if (FAILED(hr))
  863. {
  864. TRACE(_T("Failed to read int \"LastLogonCombo\" from stream: 0x%x\n"), hr);
  865. ASSERT(FALSE);
  866. return hr;
  867. }
  868. SendDlgItemMessage(IDC_LASTLOGON_COMBO, CB_SETCURSEL, (WPARAM)iData, 0);
  869. m_lLogonSelection = iData;
  870. }
  871. else
  872. {
  873. //
  874. // Write non expiring pwd flag
  875. //
  876. LRESULT lRes = SendDlgItemMessage(IDC_NON_EXPIRING_PWD_CHECK, BM_GETCHECK, 0, 0);
  877. if (lRes != -1)
  878. {
  879. int iRes = (lRes == BST_CHECKED) ? 1 : 0;
  880. hr = pPersistQuery->WriteInt(m_szFilterPrefix, L"NonExpPwdCheck", iRes);
  881. if (FAILED(hr))
  882. {
  883. ASSERT(FALSE);
  884. return hr;
  885. }
  886. }
  887. //
  888. // Write last logon combo index
  889. //
  890. lRes = SendDlgItemMessage(IDC_LASTLOGON_COMBO, CB_GETCURSEL, 0, 0);
  891. if (lRes == CB_ERR)
  892. {
  893. if (m_lLogonSelection != -1)
  894. {
  895. lRes = m_lLogonSelection;
  896. }
  897. else
  898. {
  899. lRes = 0;
  900. }
  901. }
  902. hr = pPersistQuery->WriteInt(m_szFilterPrefix, L"LastLogonCombo", static_cast<int>(lRes));
  903. if (FAILED(hr))
  904. {
  905. ASSERT(FALSE);
  906. return hr;
  907. }
  908. }
  909. return hr;
  910. }
  911. ///////////////////////////////////////////////////////////////////////////////
  912. // CQueryFormBase
  913. HRESULT PageProc(LPCQPAGE pPage, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  914. INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
  915. HRESULT GetQueryParams(HWND hWnd, LPDSQUERYPARAMS* ppDsQueryParams);
  916. STDMETHODIMP CQueryFormBase::Initialize(HKEY)
  917. {
  918. // This method is called to initialize the query form object, it is called before
  919. // any pages are added. hkForm should be ignored, in the future however it
  920. // will be a way to persist form state.
  921. HRESULT hr = S_OK;
  922. return hr;
  923. }
  924. STDMETHODIMP CQueryFormBase::AddForms(LPCQADDFORMSPROC pAddFormsProc, LPARAM lParam)
  925. {
  926. CQFORM cqf;
  927. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  928. // This method is called to allow the form handler to register its query form(s),
  929. // each form is identifiered by a CLSID and registered via the pAddFormProc. Here
  930. // we are going to register a test form.
  931. // When registering a form which is only applicable to a specific task, eg. Find a Domain
  932. // object, it is advised that the form be marked as hidden (CQFF_ISNEVERLISTED) which
  933. // will cause it not to appear in the form picker control. Then when the
  934. // client wants to use this form, they specify the form identifier and ask for the
  935. // picker control to be hidden.
  936. if ( !pAddFormsProc )
  937. {
  938. return E_INVALIDARG;
  939. }
  940. cqf.cbStruct = sizeof(cqf);
  941. cqf.dwFlags = CQFF_NOGLOBALPAGES;
  942. cqf.clsid = CLSID_DSAdminQueryUIForm;
  943. cqf.hIcon = NULL;
  944. CString title;
  945. title.LoadString(IDS_QUERY_TITLE_SAVEDQUERYFORM);
  946. cqf.pszTitle = (LPCTSTR)title;
  947. return pAddFormsProc(lParam, &cqf);
  948. }
  949. STDMETHODIMP CQueryFormBase::AddPages(LPCQADDPAGESPROC pAddPagesProc, LPARAM lParam)
  950. {
  951. HRESULT hr = S_OK;
  952. CQPAGE cqp;
  953. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  954. // AddPages is called after AddForms, it allows us to add the pages for the
  955. // forms we have registered. Each page is presented on a seperate tab within
  956. // the dialog. A form is a dialog with a DlgProc and a PageProc.
  957. //
  958. // When registering a page the entire structure passed to the callback is copied,
  959. // the amount of data to be copied is defined by the cbStruct field, therefore
  960. // a page implementation can grow this structure to store extra information. When
  961. // the page dialog is constructed via CreateDialog the CQPAGE strucuture is passed
  962. // as the create param.
  963. if ( !pAddPagesProc )
  964. return E_INVALIDARG;
  965. cqp.cbStruct = sizeof(cqp);
  966. cqp.dwFlags = 0x0;
  967. cqp.pPageProc = PageProc;
  968. cqp.hInstance = _Module.GetModuleInstance();
  969. cqp.pDlgProc = DlgProc;
  970. //
  971. // Add the user page
  972. //
  973. cqp.idPageName = IDS_QUERY_TITLE_USERPAGE;
  974. cqp.idPageTemplate = IDD_QUERY_USER_PAGE;
  975. cqp.lParam = (LPARAM)new CUserQueryPage(FILTER_PREFIX_USER);
  976. hr = pAddPagesProc(lParam, CLSID_DSAdminQueryUIForm, &cqp);
  977. //
  978. // Add the computer page (this is just a std page)
  979. //
  980. cqp.idPageName = IDS_QUERY_TITLE_COMPUTER_PAGE;
  981. cqp.idPageTemplate = IDD_QUERY_COMPUTER_PAGE;
  982. cqp.lParam = (LPARAM)new CUserComputerQueryPage(IDD_QUERY_COMPUTER_PAGE, FILTER_PREFIX_COMPUTER);
  983. hr = pAddPagesProc(lParam, CLSID_DSAdminQueryUIForm, &cqp);
  984. //
  985. // Add the group page (this is just a std page)
  986. //
  987. cqp.idPageName = IDS_QUERY_TITLE_GROUP_PAGE;
  988. cqp.idPageTemplate = IDD_QUERY_STD_PAGE;
  989. cqp.lParam = (LPARAM)new CStdQueryPage(IDD_QUERY_STD_PAGE, FILTER_PREFIX_GROUP);
  990. hr = pAddPagesProc(lParam, CLSID_DSAdminQueryUIForm, &cqp);
  991. //
  992. // Add more pages here if needed
  993. //
  994. return hr;
  995. }
  996. /*---------------------------------------------------------------------------*/
  997. // The PageProc is used to perform general house keeping and communicate between
  998. // the frame and the page.
  999. //
  1000. // All un-handled, or unknown reasons should result in an E_NOIMPL response
  1001. // from the proc.
  1002. //
  1003. // In:
  1004. // pPage -> CQPAGE structure (copied from the original passed to pAddPagesProc)
  1005. // hwnd = handle of the dialog for the page
  1006. // uMsg, wParam, lParam = message parameters for this event
  1007. //
  1008. // Out:
  1009. // HRESULT
  1010. //
  1011. // uMsg reasons:
  1012. // ------------
  1013. // CQPM_INIIIALIZE
  1014. // CQPM_RELEASE
  1015. // These are issued as a result of the page being declared or freed, they
  1016. // allow the caller to AddRef, Release or perform basic initialization
  1017. // of the form object.
  1018. //
  1019. // CQPM_ENABLE
  1020. // Enable is when the query form needs to enable or disable the controls
  1021. // on its page. wParam contains TRUE/FALSE indicating the state that
  1022. // is required.
  1023. //
  1024. // CQPM_GETPARAMETERS
  1025. // To collect the parameters for the query each page on the active form
  1026. // receives this event. lParam is an LPVOID* which is set to point to the
  1027. // parameter block to pass to the handler, if the pointer is non-NULL
  1028. // on entry the form needs to appened its query information to it. The
  1029. // parameter block is handler specific.
  1030. //
  1031. // Returning S_FALSE from this event causes the query to be canceled.
  1032. //
  1033. // CQPM_CLEARFORM
  1034. // When the page window is created for the first time, or the user clicks
  1035. // the clear search the page receives a CQPM_CLEARFORM notification, at
  1036. // which point it needs to clear out the edit controls it has and
  1037. // return to a default state.
  1038. //
  1039. // CQPM_PERSIST:
  1040. // When loading of saving a query, each page is called with an IPersistQuery
  1041. // interface which allows them to read or write the configuration information
  1042. // to save or restore their state. lParam is a pointer to the IPersistQuery object,
  1043. // and wParam is TRUE/FALSE indicating read or write accordingly.
  1044. HRESULT PageProc(LPCQPAGE pQueryPage, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1045. {
  1046. HRESULT hr = S_OK;
  1047. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  1048. CQueryPageBase* pDialog = (CQueryPageBase*)pQueryPage->lParam;
  1049. ASSERT(pDialog);
  1050. switch ( uMsg )
  1051. {
  1052. // Initialize so AddRef the object we are associated with so that
  1053. // we don't get unloaded.
  1054. case CQPM_INITIALIZE:
  1055. break;
  1056. // Changed from qform sample to detach the hwnd, and delete the CDialog
  1057. // ensure correct destruction etc.
  1058. case CQPM_RELEASE:
  1059. pDialog->Detach();
  1060. SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM)0);
  1061. delete pDialog;
  1062. break;
  1063. // Enable so fix the state of our two controls within the window.
  1064. case CQPM_ENABLE:
  1065. SetFocus(GetDlgItem(hwnd, IDC_NAME_COMBO));
  1066. break;
  1067. // Fill out the parameter structure to return to the caller, this is
  1068. // handler specific. In our case we constructure a query of the CN
  1069. // and objectClass properties, and we show a columns displaying both
  1070. // of these. For further information about the DSQUERYPARAMs structure
  1071. // see dsquery.h
  1072. case CQPM_GETPARAMETERS:
  1073. hr = pDialog->GetQueryParams((LPDSQUERYPARAMS*)lParam);
  1074. break;
  1075. // Clear form, therefore set the window text for these two controls
  1076. // to zero.
  1077. case CQPM_CLEARFORM:
  1078. hr = pDialog->ClearForm();
  1079. break;
  1080. // persistance is not currently supported by this form.
  1081. case CQPM_PERSIST:
  1082. {
  1083. BOOL fRead = (BOOL)wParam;
  1084. IPersistQuery* pPersistQuery = (IPersistQuery*)lParam;
  1085. if ( !pPersistQuery )
  1086. {
  1087. return E_INVALIDARG;
  1088. }
  1089. hr = pDialog->Persist(pPersistQuery, fRead);
  1090. break;
  1091. }
  1092. default:
  1093. hr = E_NOTIMPL;
  1094. break;
  1095. }
  1096. return hr;
  1097. }
  1098. /*---------------------------------------------------------------------------*/
  1099. // The DlgProc is a standard Win32 dialog proc associated with the form
  1100. // window.
  1101. INT_PTR CALLBACK DlgProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1102. {
  1103. LPCQPAGE pQueryPage;
  1104. CQueryPageBase* pDialog;
  1105. AFX_MANAGE_STATE(AfxGetStaticModuleState());
  1106. if ( uMsg == WM_INITDIALOG )
  1107. {
  1108. // changed from qForm sample to save CDialog pointer
  1109. // in the DWL_USER field of the dialog box instance.
  1110. pQueryPage = (LPCQPAGE)lParam;
  1111. pDialog = (CQueryPageBase*)pQueryPage->lParam;
  1112. pDialog->Attach(hwnd);
  1113. SetWindowLongPtr(hwnd, DWLP_USER, (LPARAM)pDialog);
  1114. return pDialog->OnInitDialog();
  1115. }
  1116. else
  1117. {
  1118. // CDialog pointer is stored in DWL_USER
  1119. // dialog structure, note however that in some cases this will
  1120. // be NULL as it is set on WM_INITDIALOG.
  1121. pDialog = (CQueryPageBase*)GetWindowLongPtr(hwnd, DWLP_USER);
  1122. }
  1123. if(!pDialog)
  1124. {
  1125. return FALSE;
  1126. }
  1127. else
  1128. {
  1129. return AfxCallWndProc(pDialog, hwnd, uMsg, wParam, lParam);
  1130. }
  1131. }
  1132. ///////////////////////////////////////////////////////////////////////
  1133. // CQueryDialog
  1134. CQueryDialog::CQueryDialog(CSavedQueryNode* pQueryNode,
  1135. CFavoritesNode* pFavNode,
  1136. CDSComponentData* pComponentData,
  1137. BOOL bNewQuery,
  1138. BOOL bImportQuery)
  1139. : CHelpDialog(IDD_CREATE_NEW_QUERY)
  1140. {
  1141. m_bInit = FALSE;
  1142. m_bNewQuery = bNewQuery;
  1143. m_bImportQuery = bImportQuery;
  1144. m_pComponentData = pComponentData;
  1145. m_pQueryNode = pQueryNode;
  1146. m_pFavNode = pFavNode;
  1147. m_szName = pQueryNode->GetName();
  1148. m_szOriginalName = pQueryNode->GetName();
  1149. m_szDescription = pQueryNode->GetDesc();
  1150. m_szQueryRoot = pQueryNode->GetRootPath();
  1151. m_szQueryFilter = pQueryNode->GetQueryString();
  1152. m_bMultiLevel = !pQueryNode->IsOneLevel();
  1153. m_bLastLogonFilter = pQueryNode->IsFilterLastLogon();
  1154. m_dwLastLogonData = pQueryNode->GetLastLogonDays();
  1155. m_pPersistQueryImpl = pQueryNode->GetQueryPersist();
  1156. if (m_pPersistQueryImpl != NULL)
  1157. {
  1158. m_pPersistQueryImpl->AddRef();
  1159. }
  1160. else
  1161. {
  1162. //
  1163. // Create the IPersistQuery object
  1164. //
  1165. CComObject<CDSAdminPersistQueryFilterImpl>::CreateInstance(&m_pPersistQueryImpl);
  1166. ASSERT(m_pPersistQueryImpl != NULL);
  1167. //
  1168. // created with zero refcount,need to AddRef() to one
  1169. //
  1170. m_pPersistQueryImpl->AddRef();
  1171. }
  1172. }
  1173. CQueryDialog::~CQueryDialog()
  1174. {
  1175. if (m_pPersistQueryImpl != NULL)
  1176. {
  1177. //
  1178. // go to refcount of zero, to destroy object
  1179. //
  1180. m_pPersistQueryImpl->Release();
  1181. }
  1182. }
  1183. BEGIN_MESSAGE_MAP(CQueryDialog, CHelpDialog)
  1184. ON_BN_CLICKED(IDC_BROWSE_BUTTON, OnBrowse)
  1185. ON_BN_CLICKED(IDC_EDIT_BUTTON, OnEditQuery)
  1186. ON_BN_CLICKED(IDC_MULTI_LEVEL_CHECK, OnMultiLevelChange)
  1187. ON_EN_CHANGE(IDC_NAME_EDIT, OnNameChange)
  1188. ON_EN_CHANGE(IDC_DESCRIPTION_EDIT, OnDescriptionChange)
  1189. ON_NOTIFY_EX(TTN_NEEDTEXT, 0, OnNeedToolTipText)
  1190. END_MESSAGE_MAP()
  1191. void CQueryDialog::DoContextHelp(HWND hWndControl)
  1192. {
  1193. if (hWndControl)
  1194. {
  1195. ::WinHelp(hWndControl,
  1196. DSADMIN_CONTEXT_HELP_FILE,
  1197. HELP_WM_HELP,
  1198. (DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_CREATE_NEW_QUERY);
  1199. }
  1200. }
  1201. BOOL CQueryDialog::OnInitDialog()
  1202. {
  1203. CHelpDialog::OnInitDialog();
  1204. if (m_pQueryNode == NULL)
  1205. {
  1206. ASSERT(FALSE);
  1207. EndDialog(IDCANCEL);
  1208. }
  1209. //
  1210. // Change the title for editing queries
  1211. //
  1212. if (!m_bNewQuery)
  1213. {
  1214. CString szTitle;
  1215. VERIFY(szTitle.LoadString(IDS_SAVED_QUERIES_EDIT_TITLE));
  1216. SetWindowText(szTitle);
  1217. }
  1218. //
  1219. // Initialize the controls with data
  1220. //
  1221. SetDlgItemText(IDC_NAME_EDIT, m_szName);
  1222. SendDlgItemMessage(IDC_NAME_EDIT, EM_SETLIMITTEXT, MAX_QUERY_NAME_LENGTH, 0);
  1223. SetDlgItemText(IDC_DESCRIPTION_EDIT, m_szDescription);
  1224. SendDlgItemMessage(IDC_DESCRIPTION_EDIT, EM_SETLIMITTEXT, MAX_QUERY_DESC_LENGTH, 0);
  1225. SendDlgItemMessage(IDC_MULTI_LEVEL_CHECK, BM_SETCHECK, (m_bMultiLevel) ? BST_CHECKED : BST_UNCHECKED, 0);
  1226. SetQueryFilterDisplay();
  1227. EnableToolTips(TRUE);
  1228. SetQueryRoot(m_szQueryRoot);
  1229. SetDirty();
  1230. m_bInit = TRUE;
  1231. return TRUE;
  1232. }
  1233. void CQueryDialog::SetDirty(BOOL bDirty)
  1234. {
  1235. if (m_bInit || m_bImportQuery)
  1236. {
  1237. m_szName.TrimLeft();
  1238. m_szName.TrimRight();
  1239. if (m_szName.IsEmpty() ||
  1240. m_szQueryRoot.IsEmpty() ||
  1241. m_szQueryFilter.IsEmpty())
  1242. {
  1243. m_bDirty = FALSE;
  1244. }
  1245. else
  1246. {
  1247. m_bDirty = bDirty;
  1248. }
  1249. GetDlgItem(IDOK)->EnableWindow(m_bDirty);
  1250. }
  1251. }
  1252. void CQueryDialog::OnOK()
  1253. {
  1254. if (m_bDirty)
  1255. {
  1256. if (m_pQueryNode != NULL)
  1257. {
  1258. GetDlgItemText(IDC_NAME_EDIT, m_szName);
  1259. GetDlgItemText(IDC_DESCRIPTION_EDIT, m_szDescription);
  1260. LRESULT lRes = SendDlgItemMessage(IDC_MULTI_LEVEL_CHECK, BM_GETCHECK, 0, 0);
  1261. if (lRes == BST_CHECKED)
  1262. {
  1263. m_bMultiLevel = TRUE;
  1264. }
  1265. else
  1266. {
  1267. m_bMultiLevel = FALSE;
  1268. }
  1269. //
  1270. // Trim white space
  1271. //
  1272. m_szName.TrimLeft();
  1273. m_szName.TrimRight();
  1274. if (wcscmp(m_szOriginalName, m_szName) != 0 || m_bImportQuery)
  1275. {
  1276. CUINode* pDupNode = NULL;
  1277. if (!m_pFavNode->IsUniqueName(m_szName, &pDupNode))
  1278. {
  1279. CString szFormatMsg;
  1280. VERIFY(szFormatMsg.LoadString(IDS_ERRMSG_NOT_UNIQUE_QUERY_NAME));
  1281. CString szErrMsg;
  1282. szErrMsg.Format(szFormatMsg, m_szName);
  1283. CString szTitle;
  1284. VERIFY(szTitle.LoadString(IDS_DSSNAPINNAME));
  1285. MessageBox(szErrMsg, szTitle, MB_OK | MB_ICONSTOP);
  1286. //
  1287. // Set the focus to the name field and select all the text
  1288. //
  1289. GetDlgItem(IDC_NAME_EDIT)->SetFocus();
  1290. SendDlgItemMessage(IDC_NAME_EDIT, EM_SETSEL, 0, -1);
  1291. return;
  1292. }
  1293. }
  1294. if (m_bLastLogonFilter)
  1295. {
  1296. m_pQueryNode->SetLastLogonQuery(m_dwLastLogonData);
  1297. }
  1298. m_pQueryNode->SetQueryString(m_szQueryFilter);
  1299. m_pQueryNode->SetName(m_szName);
  1300. m_pQueryNode->SetDesc(m_szDescription);
  1301. m_pQueryNode->SetRootPath(m_szQueryRoot);
  1302. m_pQueryNode->SetOneLevel((m_bMultiLevel == BST_CHECKED) ? FALSE : TRUE);
  1303. m_pQueryNode->SetQueryPersist(m_pPersistQueryImpl);
  1304. }
  1305. }
  1306. CHelpDialog::OnOK();
  1307. }
  1308. BOOL CQueryDialog::OnNeedToolTipText(UINT, NMHDR* pTTTStruct, LRESULT* /*ignored*/)
  1309. {
  1310. BOOL bRes = FALSE;
  1311. TOOLTIPTEXT* pTTText = reinterpret_cast<TOOLTIPTEXT*>(pTTTStruct);
  1312. if (pTTText != NULL)
  1313. {
  1314. if (pTTText->uFlags & TTF_IDISHWND)
  1315. {
  1316. UINT nCtrlID = ::GetDlgCtrlID((HWND)pTTText->hdr.idFrom);
  1317. if (nCtrlID == IDC_ROOT_EDIT)
  1318. {
  1319. pTTText->lpszText = (LPWSTR)(LPCWSTR)m_szQueryRoot;
  1320. bRes = TRUE;
  1321. }
  1322. }
  1323. }
  1324. return bRes;
  1325. }
  1326. void CQueryDialog::OnEditQuery()
  1327. {
  1328. CLIPFORMAT cfDsQueryParams = (CLIPFORMAT)::RegisterClipboardFormat(CFSTR_DSQUERYPARAMS);
  1329. //
  1330. // create a query object
  1331. //
  1332. HRESULT hr;
  1333. CComPtr<ICommonQuery> spCommonQuery;
  1334. hr = ::CoCreateInstance(CLSID_CommonQuery, NULL, CLSCTX_INPROC_SERVER,
  1335. IID_ICommonQuery, (PVOID *)&spCommonQuery);
  1336. if (FAILED(hr))
  1337. {
  1338. ReportMessageEx(GetSafeHwnd(), IDS_ERRMSG_NO_DSQUERYUI);
  1339. return;
  1340. }
  1341. //
  1342. // setup structs to make the query
  1343. //
  1344. DSQUERYINITPARAMS dqip;
  1345. OPENQUERYWINDOW oqw;
  1346. ZeroMemory(&dqip, sizeof(DSQUERYINITPARAMS));
  1347. ZeroMemory(&oqw, sizeof(OPENQUERYWINDOW));
  1348. dqip.cbStruct = sizeof(dqip);
  1349. dqip.dwFlags = DSQPF_NOSAVE | DSQPF_SHOWHIDDENOBJECTS |
  1350. DSQPF_ENABLEADMINFEATURES;
  1351. dqip.pDefaultScope = NULL;
  1352. CString szServerName = m_pComponentData->GetBasePathsInfo()->GetServerName();
  1353. if (!szServerName.IsEmpty())
  1354. {
  1355. dqip.dwFlags |= DSQPF_HASCREDENTIALS;
  1356. dqip.pServer = (PWSTR)(PCWSTR)szServerName;
  1357. }
  1358. oqw.cbStruct = sizeof(oqw);
  1359. oqw.dwFlags = OQWF_OKCANCEL | OQWF_DEFAULTFORM | OQWF_SHOWOPTIONAL | /*OQWF_REMOVEFORMS |*/
  1360. OQWF_REMOVESCOPES | OQWF_SAVEQUERYONOK | OQWF_HIDEMENUS | OQWF_HIDESEARCHUI;
  1361. if (!m_pPersistQueryImpl->IsEmpty())
  1362. {
  1363. oqw.dwFlags |= OQWF_LOADQUERY;
  1364. }
  1365. oqw.clsidHandler = CLSID_DsQuery;
  1366. oqw.pHandlerParameters = &dqip;
  1367. oqw.clsidDefaultForm = CLSID_DSAdminQueryUIForm;
  1368. //
  1369. // set the IPersistQuery pointer (smart pointer)
  1370. //
  1371. CComPtr<IPersistQuery> spIPersistQuery;
  1372. hr = m_pPersistQueryImpl->QueryInterface(IID_IPersistQuery, (void**)&spIPersistQuery);
  1373. if (FAILED(hr))
  1374. {
  1375. int iRes = ReportMessageEx(GetSafeHwnd(), IDS_ERRMSG_NO_PERSIST_QUERYUI, MB_OKCANCEL | MB_ICONINFORMATION);
  1376. if (iRes == IDCANCEL)
  1377. {
  1378. return;
  1379. }
  1380. }
  1381. //
  1382. // now smart pointer has refcount=1 for it lifetime
  1383. //
  1384. oqw.pPersistQuery = spIPersistQuery;
  1385. //
  1386. // Get the HWND of the current dialog
  1387. //
  1388. HWND hWnd = GetSafeHwnd();
  1389. //
  1390. // make the call to get the query displayed
  1391. //
  1392. CComPtr<IDataObject> spQueryResultDataObject;
  1393. hr = spCommonQuery->OpenQueryWindow(hWnd, &oqw, &spQueryResultDataObject);
  1394. if (SUCCEEDED(hr) && spQueryResultDataObject != NULL)
  1395. {
  1396. //
  1397. // retrieve the query string from the data object
  1398. //
  1399. FORMATETC fmte = {cfDsQueryParams, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  1400. STGMEDIUM medium = {TYMED_NULL, NULL, NULL};
  1401. hr = spQueryResultDataObject->GetData(&fmte, &medium);
  1402. if (SUCCEEDED(hr)) // we have data
  1403. {
  1404. //
  1405. // get the query string
  1406. //
  1407. LPDSQUERYPARAMS pDsQueryParams = (LPDSQUERYPARAMS)medium.hGlobal;
  1408. LPWSTR pwszFilter = (LPWSTR)ByteOffset(pDsQueryParams, pDsQueryParams->offsetQuery);
  1409. CString szTempFilter = pwszFilter;
  1410. //
  1411. // Check to see if we received a "special" query string
  1412. //
  1413. if (pDsQueryParams->dwFlags & DSQF_LAST_LOGON_QUERY)
  1414. {
  1415. m_bLastLogonFilter = TRUE;
  1416. m_dwLastLogonData = pDsQueryParams->dwReserved;
  1417. LARGE_INTEGER li;
  1418. GetCurrentTimeStampMinusInterval(m_dwLastLogonData, &li);
  1419. CString szTimeStamp;
  1420. litow(li, szTimeStamp);
  1421. szTempFilter.Format(L"%s(lastLogonTimestamp<=%s)", szTempFilter, szTimeStamp);
  1422. }
  1423. else
  1424. {
  1425. m_bLastLogonFilter = FALSE;
  1426. m_dwLastLogonData = 0;
  1427. }
  1428. ::ReleaseStgMedium(&medium);
  1429. // REVIEW_MARCOC: this is a hack waiting for Diz to fix it...
  1430. // the query string should be a well formed expression. Period
  1431. // the query string is in the form (<foo>)(<bar>)...
  1432. // if more of one token, need to wrap as (& (<foo>)(<bar>)...)
  1433. PWSTR pChar = (LPWSTR)(LPCWSTR)szTempFilter;
  1434. int nLeftPar = 0;
  1435. while (*pChar != NULL)
  1436. {
  1437. if (*pChar == TEXT('('))
  1438. {
  1439. nLeftPar++;
  1440. if (nLeftPar > 1)
  1441. break;
  1442. }
  1443. pChar++;
  1444. }
  1445. if (nLeftPar > 1)
  1446. {
  1447. m_szQueryFilter.Format(_T("(&%s)"), (LPCWSTR)szTempFilter);
  1448. }
  1449. else
  1450. {
  1451. m_szQueryFilter = szTempFilter;
  1452. }
  1453. SetDirty();
  1454. }
  1455. else
  1456. {
  1457. //
  1458. // The user removed all query data from DSQUERYUI
  1459. //
  1460. //
  1461. // Remove filter data
  1462. //
  1463. m_szQueryFilter = L"";
  1464. m_bLastLogonFilter = FALSE;
  1465. m_dwLastLogonData = 0;
  1466. SetDirty();
  1467. }
  1468. }
  1469. SetQueryFilterDisplay();
  1470. return;
  1471. }
  1472. void CQueryDialog::SetQueryFilterDisplay()
  1473. {
  1474. if (m_bLastLogonFilter)
  1475. {
  1476. CString szTemp;
  1477. szTemp.LoadString(IDS_HIDE_LASTLOGON_QUERY);
  1478. SetDlgItemText(IDC_QUERY_STRING_EDIT, szTemp);
  1479. }
  1480. else
  1481. {
  1482. SetDlgItemText(IDC_QUERY_STRING_EDIT, m_szQueryFilter);
  1483. }
  1484. }
  1485. void CQueryDialog::OnBrowse()
  1486. {
  1487. DWORD result;
  1488. CString szBrowseTitle;
  1489. VERIFY(szBrowseTitle.LoadString(IDS_QUERY_BROWSE_TITLE));
  1490. CString szBrowseCaption;
  1491. VERIFY(szBrowseCaption.LoadString(IDS_QUERY_BROWSE_CAPTION));
  1492. WCHAR szPath[2 * MAX_PATH+1];
  1493. //
  1494. // Get the root of the console
  1495. CString szDNC = m_pComponentData->GetBasePathsInfo()->GetDefaultRootNamingContext();
  1496. CString szRootPath;
  1497. m_pComponentData->GetBasePathsInfo()->ComposeADsIPath(szRootPath, szDNC);
  1498. DSBROWSEINFO dsbi;
  1499. ::ZeroMemory( &dsbi, sizeof(dsbi) );
  1500. dsbi.hwndOwner = GetSafeHwnd();
  1501. dsbi.cbStruct = sizeof (DSBROWSEINFO);
  1502. dsbi.pszCaption = (LPWSTR)((LPCWSTR)szBrowseTitle);
  1503. dsbi.pszTitle = (LPWSTR)((LPCWSTR)szBrowseCaption);
  1504. dsbi.pszRoot = szRootPath;
  1505. dsbi.pszPath = szPath;
  1506. dsbi.cchPath = ((2 * MAX_PATH + 1) / sizeof(WCHAR));
  1507. dsbi.dwFlags = DSBI_INCLUDEHIDDEN | DSBI_RETURN_FORMAT;
  1508. dsbi.pfnCallback = NULL;
  1509. dsbi.lParam = 0;
  1510. dsbi.dwReturnFormat = ADS_FORMAT_X500;
  1511. result = DsBrowseForContainer( &dsbi );
  1512. if ( result == IDOK )
  1513. {
  1514. //
  1515. // returns -1, 0, IDOK or IDCANCEL
  1516. // get path from BROWSEINFO struct, put in text edit field
  1517. //
  1518. TRACE(_T("returned from DS Browse successfully with:\n %s\n"),
  1519. dsbi.pszPath);
  1520. CPathCracker pathCracker;
  1521. HRESULT hr = pathCracker.Set(dsbi.pszPath, ADS_SETTYPE_FULL);
  1522. if (SUCCEEDED(hr))
  1523. {
  1524. hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
  1525. if (SUCCEEDED(hr))
  1526. {
  1527. CComBSTR bstrDN;
  1528. hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN, &bstrDN);
  1529. if (SUCCEEDED(hr))
  1530. {
  1531. SetQueryRoot(bstrDN);
  1532. SetDirty();
  1533. }
  1534. }
  1535. }
  1536. }
  1537. }
  1538. void CQueryDialog::SetQueryRoot(PCWSTR pszPath)
  1539. {
  1540. m_szQueryRoot = pszPath;
  1541. CPathCracker pathCracker;
  1542. HRESULT hr = pathCracker.Set((LPWSTR)(LPCWSTR)m_szQueryRoot, ADS_SETTYPE_DN);
  1543. if (SUCCEEDED(hr))
  1544. {
  1545. hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  1546. if (SUCCEEDED(hr))
  1547. {
  1548. CComBSTR bstrDisplayPath;
  1549. hr = pathCracker.GetElement(0, &bstrDisplayPath);
  1550. if (SUCCEEDED(hr))
  1551. {
  1552. CString szDisplayString;
  1553. szDisplayString.Format(L"...\\%s", bstrDisplayPath);
  1554. SetDlgItemText(IDC_ROOT_EDIT, szDisplayString);
  1555. }
  1556. else
  1557. {
  1558. SetDlgItemText(IDC_ROOT_EDIT, m_szQueryRoot);
  1559. }
  1560. }
  1561. else
  1562. {
  1563. SetDlgItemText(IDC_ROOT_EDIT, m_szQueryRoot);
  1564. }
  1565. }
  1566. else
  1567. {
  1568. SetDlgItemText(IDC_ROOT_EDIT, m_szQueryRoot);
  1569. }
  1570. }
  1571. void CQueryDialog::OnMultiLevelChange()
  1572. {
  1573. SetDirty();
  1574. }
  1575. void CQueryDialog::OnNameChange()
  1576. {
  1577. GetDlgItemText(IDC_NAME_EDIT, m_szName);
  1578. if (m_szName.IsEmpty())
  1579. {
  1580. SetDirty(FALSE);
  1581. }
  1582. else
  1583. {
  1584. SetDirty();
  1585. }
  1586. }
  1587. void CQueryDialog::OnDescriptionChange()
  1588. {
  1589. SetDirty();
  1590. }
  1591. ///////////////////////////////////////////////////////////////////////////
  1592. // CFavoritesNodePropertyPage
  1593. BEGIN_MESSAGE_MAP(CFavoritesNodePropertyPage, CHelpPropertyPage)
  1594. ON_EN_CHANGE(IDC_DESCRIPTION_EDIT, OnDescriptionChange)
  1595. END_MESSAGE_MAP()
  1596. void CFavoritesNodePropertyPage::DoContextHelp(HWND hWndControl)
  1597. {
  1598. if (hWndControl)
  1599. {
  1600. ::WinHelp(hWndControl,
  1601. DSADMIN_CONTEXT_HELP_FILE,
  1602. HELP_WM_HELP,
  1603. (DWORD_PTR)(LPTSTR)g_aHelpIDs_IDD_FAVORITES_PROPERTY_PAGE);
  1604. }
  1605. }
  1606. BOOL CFavoritesNodePropertyPage::OnInitDialog()
  1607. {
  1608. CHelpPropertyPage::OnInitDialog();
  1609. m_szOldDescription = m_pFavNode->GetDesc();
  1610. SetDlgItemText(IDC_CN, m_pFavNode->GetName());
  1611. SetDlgItemText(IDC_DESCRIPTION_EDIT, m_szOldDescription);
  1612. return FALSE;
  1613. }
  1614. void CFavoritesNodePropertyPage::OnDescriptionChange()
  1615. {
  1616. CString szNewDescription;
  1617. GetDlgItemText(IDC_DESCRIPTION_EDIT, szNewDescription);
  1618. if (szNewDescription == m_szOldDescription)
  1619. {
  1620. SetModified(FALSE);
  1621. }
  1622. else
  1623. {
  1624. SetModified(TRUE);
  1625. }
  1626. }
  1627. BOOL CFavoritesNodePropertyPage::OnApply()
  1628. {
  1629. BOOL bRet = TRUE;
  1630. CString szNewDescription;
  1631. GetDlgItemText(IDC_DESCRIPTION_EDIT, szNewDescription);
  1632. if (szNewDescription == m_szOldDescription)
  1633. {
  1634. return TRUE;
  1635. }
  1636. else
  1637. {
  1638. m_pFavNode->SetDesc(szNewDescription);
  1639. if (m_lNotifyHandle != NULL && m_pDataObject != NULL)
  1640. {
  1641. MMCPropertyChangeNotify(m_lNotifyHandle, (LPARAM)m_pDataObject);
  1642. }
  1643. }
  1644. m_szOldDescription = szNewDescription;
  1645. return bRet;
  1646. }