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.

1655 lines
50 KiB

  1. #include "pch.h"
  2. #include "helpids.h"
  3. #pragma hdrstop
  4. /*-----------------------------------------------------------------------------
  5. / Constants and other helpers
  6. /----------------------------------------------------------------------------*/
  7. //
  8. // Page size used for paging the result sets (better performance)
  9. //
  10. #define PAGE_SIZE 128
  11. WCHAR c_szQueryNormal[] = L"(&(objectClass=*)(!showInAdvancedViewOnly=TRUE))";
  12. WCHAR c_szQueryAll[] = L"(objectClass=*)";
  13. WCHAR c_szShowInAdvancedViewOnly[] = L"showInAdvancedViewOnly";
  14. WCHAR c_szObjectClass[] = L"objectClass";
  15. WCHAR c_szADsPath[] = L"ADsPath";
  16. WCHAR c_szName[] = L"name";
  17. WCHAR c_szRDN[] = L"rdn";
  18. WCHAR c_szLDAPPrefix[] = L"LDAP://";
  19. /*-----------------------------------------------------------------------------
  20. / CBrowseDlg class definition
  21. /----------------------------------------------------------------------------*/
  22. class CBrowseDlg
  23. {
  24. private:
  25. // a UNICODE version of the structure
  26. DSBROWSEINFOW _bi;
  27. // an IADsPathname object for usto use
  28. IADsPathname* _pPathCracker;
  29. // server being referenced (cracked out of the pszRoot path);
  30. LPWSTR _pServer;
  31. // browse information (initialized during startup)
  32. WCHAR _szFilter[INTERNET_MAX_URL_LENGTH];
  33. WCHAR _szNameAttribute[MAX_PATH];
  34. public:
  35. CBrowseDlg(PDSBROWSEINFOW pbi);
  36. ~CBrowseDlg();
  37. private:
  38. HRESULT _GetPathCracker(void);
  39. LPCWSTR _GetSelectedPath(HWND hDlg) const;
  40. LPCWSTR GetSelectedObjectClass(HWND hDlg) const;
  41. int _SetSelectedPath(HWND hDlg, LPCWSTR pszADsPath);
  42. HRESULT _BuildNodeString(LPCWSTR pszADsPath, LPCWSTR pszClass, LPWSTR *ppszResult);
  43. HRESULT _ExpandNode(LPWSTR pszRootPath, HWND hwndTree, HTREEITEM hParentItem);
  44. HRESULT _ExpandNode(IADs *pRootObject, HWND hwndTree, HTREEITEM hParentItem);
  45. HRESULT _EnumerateNode(IADsContainer *pDsContainer, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded);
  46. HRESULT _SearchNode(IDirectorySearch *pDsSearch, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded);
  47. HRESULT _AddTreeNode(IADs *pDsObject, LPCWSTR pObjectPath, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem);
  48. HRESULT _AddTreeNode(LPCWSTR pszPath, LPCWSTR pszClass, LPCWSTR pszName, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem);
  49. HRESULT _AddTreeNode(LPDOMAINDESC pDomainDesc, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem);
  50. BOOL _OnInitDlg(HWND hDlg);
  51. BOOL _OnNotify(HWND hDlg, int idCtrl, LPNMHDR pnmh);
  52. void _OnOK(HWND hDlg);
  53. BOOL _DlgProc(HWND, UINT, WPARAM, LPARAM);
  54. static INT_PTR CALLBACK s_DlgProc(HWND, UINT, WPARAM, LPARAM);
  55. friend STDMETHODIMP_(int) DsBrowseForContainerW(PDSBROWSEINFOW pbi);
  56. };
  57. /*-----------------------------------------------------------------------------
  58. / Helper function to create an IADsPathname "path cracker" object
  59. /----------------------------------------------------------------------------*/
  60. HRESULT
  61. CreatePathCracker(IADsPathname **ppPath)
  62. {
  63. HRESULT hr;
  64. TraceEnter(TRACE_BROWSE, "CreatePathCracker");
  65. TraceAssert(ppPath != NULL);
  66. hr = CoCreateInstance(CLSID_Pathname,
  67. NULL,
  68. CLSCTX_INPROC_SERVER,
  69. IID_IADsPathname,
  70. (LPVOID*)ppPath);
  71. TraceLeaveResult(hr);
  72. }
  73. /*-----------------------------------------------------------------------------
  74. / DsBrowseForContainer API implementation
  75. /----------------------------------------------------------------------------*/
  76. STDMETHODIMP_(int)
  77. DsBrowseForContainerA(PDSBROWSEINFOA pbi)
  78. {
  79. int nResult = -1;
  80. DSBROWSEINFOW bi = {0};
  81. USES_CONVERSION;
  82. TraceEnter(TRACE_BROWSE, "DsBrowseForContainerA");
  83. if (pbi == NULL ||
  84. IsBadReadPtr(pbi, SIZEOF(DWORD)) ||
  85. pbi->cbStruct < FIELD_OFFSET(DSBROWSEINFOA, dwReturnFormat) ||
  86. IsBadReadPtr(pbi, pbi->cbStruct) ||
  87. pbi->pszPath == NULL ||
  88. pbi->cchPath == 0 ||
  89. IsBadWritePtr(pbi->pszPath, pbi->cchPath))
  90. {
  91. SetLastError(ERROR_INVALID_PARAMETER);
  92. TraceLeaveValue(nResult);
  93. }
  94. CopyMemory(&bi, pbi, min(SIZEOF(bi), pbi->cbStruct));
  95. // Thunk the ANSI strings to UNICODE as required, then
  96. // call the UNICODE version of this API
  97. if (pbi->pszTitle)
  98. bi.pszTitle = A2CW(pbi->pszTitle);
  99. if (pbi->pszCaption)
  100. bi.pszCaption = A2CW(pbi->pszCaption);
  101. nResult = DsBrowseForContainerW(&bi);
  102. TraceLeaveValue(nResult);
  103. }
  104. STDMETHODIMP_(int)
  105. DsBrowseForContainerW(PDSBROWSEINFOW pbi)
  106. {
  107. HRESULT hresCoInit;
  108. int nResult = -1;
  109. TraceEnter(TRACE_BROWSE, "DsBrowseForContainerW");
  110. hresCoInit = CoInitialize(NULL);
  111. FailGracefully(hresCoInit, "Failed when calling CoInitialize");
  112. if (pbi == NULL ||
  113. IsBadReadPtr(pbi, SIZEOF(DWORD)) ||
  114. pbi->cbStruct < FIELD_OFFSET(DSBROWSEINFOW, dwReturnFormat) ||
  115. IsBadReadPtr(pbi, pbi->cbStruct) ||
  116. pbi->pszPath == NULL ||
  117. pbi->cchPath == 0 ||
  118. IsBadWritePtr(pbi->pszPath, pbi->cchPath * SIZEOF(WCHAR)))
  119. {
  120. SetLastError(ERROR_INVALID_PARAMETER);
  121. goto exit_gracefully;
  122. }
  123. if (pbi->pszRoot == NULL)
  124. pbi->pszRoot = L"LDAP:";
  125. if (pbi->pszRoot != NULL)
  126. {
  127. CBrowseDlg dlg(pbi);
  128. nResult = (int)DialogBoxParam(GLOBAL_HINSTANCE,
  129. MAKEINTRESOURCE(IDD_DSBROWSEFORCONTAINER),
  130. pbi->hwndOwner,
  131. CBrowseDlg::s_DlgProc,
  132. (LPARAM)&dlg);
  133. }
  134. exit_gracefully:
  135. if (SUCCEEDED(hresCoInit))
  136. CoUninitialize();
  137. TraceLeaveValue(nResult);
  138. }
  139. /*-----------------------------------------------------------------------------
  140. / CBrowseDlg class implementation
  141. /----------------------------------------------------------------------------*/
  142. CBrowseDlg::CBrowseDlg(PDSBROWSEINFOW pbi) :
  143. _pPathCracker(NULL),
  144. _pServer(NULL)
  145. {
  146. USES_CONVERSION;
  147. TraceEnter(TRACE_BROWSE, "CBrowseDlg::CBrowseDlg");
  148. TraceAssert(pbi != NULL && !IsBadReadPtr(pbi, pbi->cbStruct));
  149. TraceAssert(pbi->pszPath != NULL &&
  150. pbi->cchPath != 0 &&
  151. !IsBadWritePtr(pbi->pszPath, pbi->cchPath * SIZEOF(WCHAR)));
  152. CopyMemory(&_bi, pbi, min(SIZEOF(_bi), pbi->cbStruct));
  153. //
  154. // initialize the fitler we are using
  155. //
  156. StrCpyW(_szFilter, (_bi.dwFlags & DSBI_INCLUDEHIDDEN) ? c_szQueryAll:c_szQueryNormal);
  157. Trace(TEXT("_szFilter: %s"), W2T(_szFilter));
  158. _szNameAttribute[0] = L'\0';
  159. TraceLeaveVoid();
  160. }
  161. CBrowseDlg::~CBrowseDlg()
  162. {
  163. LocalFreeStringW(&_pServer);
  164. DoRelease(_pPathCracker);
  165. }
  166. HRESULT
  167. CBrowseDlg::_GetPathCracker(void)
  168. {
  169. HRESULT hr = S_OK;
  170. if (_pPathCracker == NULL)
  171. hr = CreatePathCracker(&_pPathCracker);
  172. if (SUCCEEDED(hr) && _pPathCracker)
  173. _pPathCracker->SetDisplayType(ADS_DISPLAY_FULL); // ensure we are set to full
  174. return hr;
  175. }
  176. LPCWSTR CBrowseDlg::_GetSelectedPath(HWND hDlg) const
  177. {
  178. LPCWSTR pszResult = NULL;
  179. HWND hwndTree;
  180. HTREEITEM hti = NULL;
  181. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_GetSelectedPath");
  182. TraceAssert(hDlg != NULL);
  183. hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
  184. if (hwndTree != NULL)
  185. {
  186. hti = TreeView_GetSelection(hwndTree);
  187. }
  188. if (hti != NULL)
  189. {
  190. TV_ITEM tvi;
  191. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  192. tvi.hItem = hti;
  193. if (TreeView_GetItem(hwndTree, &tvi))
  194. {
  195. pszResult = (LPCWSTR)tvi.lParam;
  196. }
  197. }
  198. TraceLeaveValue(pszResult);
  199. }
  200. LPCWSTR CBrowseDlg::GetSelectedObjectClass(HWND hDlg) const
  201. {
  202. LPCWSTR pszResult = NULL;
  203. HWND hwndTree;
  204. HTREEITEM hti = NULL;
  205. TraceEnter(TRACE_BROWSE, "CBrowseDlg::GetSelectedObjectClass");
  206. TraceAssert(hDlg != NULL);
  207. hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
  208. if (hwndTree != NULL)
  209. hti = TreeView_GetSelection(hwndTree);
  210. if (hti != NULL)
  211. {
  212. TV_ITEM tvi;
  213. tvi.mask = TVIF_PARAM | TVIF_HANDLE;
  214. tvi.hItem = hti;
  215. if (TreeView_GetItem(hwndTree, &tvi))
  216. {
  217. LPCWSTR pszPathAndClass = (LPWSTR)tvi.lParam;
  218. pszResult = (LPCWSTR)ByteOffset(tvi.lParam, StringByteSizeW(pszPathAndClass));
  219. }
  220. }
  221. TraceLeaveValue(pszResult);
  222. }
  223. int CBrowseDlg::_SetSelectedPath(HWND hDlg, LPCWSTR pszADsPath)
  224. {
  225. int nResult = -1;
  226. HRESULT hr;
  227. BSTR bstrPath = NULL;
  228. UINT nPathLength;
  229. HWND hwndTree;
  230. HTREEITEM hItem;
  231. TV_ITEM tvi;
  232. tvi.mask = TVIF_HANDLE | TVIF_PARAM;
  233. USES_CONVERSION;
  234. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_SetSelectedPath");
  235. // Run the path through a path cracker to get a known format
  236. hr = _GetPathCracker();
  237. FailGracefully(hr, "Unable to create ADsPathname object");
  238. hr = _pPathCracker->Set((LPWSTR)pszADsPath, ADS_SETTYPE_FULL);
  239. FailGracefully(hr, "Unable to parse path");
  240. hr = _pPathCracker->Retrieve(ADS_FORMAT_WINDOWS, &bstrPath);
  241. FailGracefully(hr, "Unable to build ADS Windows path");
  242. nPathLength = lstrlenW(bstrPath);
  243. Trace(TEXT("bstrPath: %s (%d)"), W2T(bstrPath), nPathLength);
  244. hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
  245. hItem = TreeView_GetChild(hwndTree, NULL);
  246. while (hItem != NULL)
  247. {
  248. LPWSTR pszCompare;
  249. UINT nCompareLength;
  250. tvi.hItem = hItem;
  251. TreeView_GetItem(hwndTree, &tvi);
  252. pszCompare = (LPWSTR)tvi.lParam;
  253. nCompareLength = lstrlenW(pszCompare);
  254. Trace(TEXT("Comparing against: %s"), W2T(pszCompare));
  255. // Does bstrPath contain pszCompare?
  256. if (2 == CompareStringW(LOCALE_SYSTEM_DEFAULT,
  257. NORM_IGNORECASE,
  258. bstrPath,
  259. min(nCompareLength, nPathLength),
  260. pszCompare,
  261. nCompareLength))
  262. {
  263. TraceMsg("Current item, contains bstrPath");
  264. TreeView_SelectItem(hwndTree, hItem);
  265. if (nCompareLength == nPathLength)
  266. {
  267. TraceMsg("... and it was an exact match");
  268. TreeView_Expand(hwndTree, hItem, TVE_EXPAND);
  269. nResult = 0;
  270. break;
  271. }
  272. else
  273. {
  274. TraceMsg("... checking children for match");
  275. TreeView_Expand(hwndTree, hItem, TVE_EXPAND);
  276. hItem = TreeView_GetChild(hwndTree, hItem);
  277. }
  278. }
  279. else
  280. {
  281. TraceMsg("Checking sibling as no match found");
  282. hItem = TreeView_GetNextSibling(hwndTree, hItem);
  283. }
  284. }
  285. exit_gracefully:
  286. if (bstrPath != NULL)
  287. SysFreeString(bstrPath);
  288. TraceLeaveValue(nResult);
  289. }
  290. HRESULT CBrowseDlg::_BuildNodeString(LPCWSTR pszADsPath, LPCWSTR pszClass, LPWSTR *ppszResult)
  291. {
  292. HRESULT hr = S_OK;
  293. INT cbPath, cbClass;
  294. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_BuildNodeString");
  295. TraceAssert(pszADsPath != NULL);
  296. TraceAssert(pszClass != NULL);
  297. TraceAssert(ppszResult != NULL);
  298. cbPath = StringByteSizeW(pszADsPath);
  299. cbClass = StringByteSizeW(pszClass);
  300. *ppszResult = (LPWSTR)LocalAlloc(LPTR, cbPath+cbClass);
  301. if (!*ppszResult)
  302. ExitGracefully(hr, E_OUTOFMEMORY, "Failed to allocate node string");
  303. CopyMemory(*ppszResult, pszADsPath, cbPath);
  304. CopyMemory(ByteOffset(*ppszResult, cbPath), pszClass, cbClass);
  305. exit_gracefully:
  306. TraceLeaveResult(hr);
  307. }
  308. HRESULT CBrowseDlg::_ExpandNode(LPWSTR pszRootPath, HWND hwndTree, HTREEITEM hParentItem)
  309. {
  310. HRESULT hr = S_OK;
  311. IADs *pRootObject = NULL;
  312. HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  313. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_ExpandNode");
  314. TraceAssert(pszRootPath != NULL);
  315. TraceAssert(hwndTree != NULL);
  316. Trace(TEXT("Scope is: %s"), pszRootPath);
  317. hr = ADsOpenObject(pszRootPath,
  318. (_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pUserName:NULL,
  319. (_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pPassword:NULL,
  320. (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION,
  321. IID_IADs, (LPVOID*)&pRootObject);
  322. if (SUCCEEDED(hr))
  323. {
  324. hr = _ExpandNode(pRootObject, hwndTree, hParentItem);
  325. DoRelease(pRootObject);
  326. }
  327. SetCursor(hcur);
  328. TraceLeaveResult(hr);
  329. }
  330. HRESULT CBrowseDlg::_ExpandNode(IADs *pRootObject, HWND hwndTree, HTREEITEM hParentItem)
  331. {
  332. HRESULT hr = S_OK;
  333. CLASSCACHEGETINFO ccgi = { 0 };
  334. LPCLASSCACHEENTRY pCacheEntry = NULL;
  335. IADsContainer *pDsContainer = NULL;
  336. IDirectorySearch *pDsSearch = NULL;
  337. BOOL fIsContainer = TRUE;
  338. ULONG cAdded = 0;
  339. TV_ITEM tvi;
  340. BSTR bstrADsPath = NULL;
  341. BSTR bstrClass = NULL;
  342. HCURSOR hcur = SetCursor(LoadCursor(NULL, IDC_WAIT));
  343. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_ExpandNode");
  344. TraceAssert(pRootObject != NULL);
  345. TraceAssert(hwndTree != NULL);
  346. //
  347. // The IDirectorySearch method is better, but doesn't work for all objects
  348. // (e.g. "LDAP:") so try enumerating if IDirectorySearch isn't supported.
  349. //
  350. hr = pRootObject->get_Class(&bstrClass);
  351. FailGracefully(hr, "Failed to get class from object");
  352. hr = pRootObject->get_ADsPath(&bstrADsPath);
  353. FailGracefully(hr, "Failed to get class from object");
  354. ccgi.dwFlags = CLASSCACHE_CONTAINER|CLASSCACHE_TREATASLEAF|CLASSCACHE_DSAVAILABLE;
  355. ccgi.pPath = bstrADsPath;
  356. ccgi.pObjectClass = bstrClass;
  357. ccgi.pServer = _pServer;
  358. if (_bi.dwFlags & DSBI_HASCREDENTIALS)
  359. {
  360. TraceMsg("Passing credential information to ClassCache API");
  361. ccgi.pUserName = (LPWSTR)_bi.pUserName;
  362. ccgi.pPassword = (LPWSTR)_bi.pPassword;
  363. }
  364. if (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE)
  365. {
  366. TraceMsg("Setting the CLASSCACHE_SIMPLEAUTHENTICATE flag");
  367. ccgi.dwFlags |= CLASSCACHE_SIMPLEAUTHENTICATE;
  368. }
  369. if (SUCCEEDED(ClassCache_GetClassInfo(&ccgi, &pCacheEntry)))
  370. {
  371. fIsContainer = _IsClassContainer(pCacheEntry, _bi.dwFlags & DSBI_IGNORETREATASLEAF);
  372. ClassCache_ReleaseClassInfo(&pCacheEntry);
  373. }
  374. if (fIsContainer)
  375. {
  376. if (SUCCEEDED(pRootObject->QueryInterface(IID_IDirectorySearch, (LPVOID*)&pDsSearch)))
  377. {
  378. hr = _SearchNode(pDsSearch, hwndTree, hParentItem, &cAdded);
  379. DoRelease(pDsSearch);
  380. }
  381. else if (SUCCEEDED(pRootObject->QueryInterface(IID_IADsContainer, (LPVOID*)&pDsContainer)))
  382. {
  383. hr = _EnumerateNode(pDsContainer, hwndTree, hParentItem, &cAdded);
  384. DoRelease(pDsContainer);
  385. }
  386. else
  387. {
  388. TraceMsg("No IDsSearch, no IDsContainer - were screwed");
  389. }
  390. }
  391. // If we did not add anything we should update this item to let
  392. // the user know something happened.
  393. if (cAdded == 0)
  394. {
  395. tvi.mask = TVIF_CHILDREN | TVIF_HANDLE;
  396. tvi.hItem = hParentItem;
  397. tvi.cChildren = 0;
  398. TreeView_SetItem(hwndTree, &tvi);
  399. }
  400. exit_gracefully:
  401. SysFreeString(bstrADsPath);
  402. SysFreeString(bstrClass);
  403. TraceLeaveResult(hr);
  404. }
  405. HRESULT CBrowseDlg::_EnumerateNode(IADsContainer *pDsContainer, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded)
  406. {
  407. HRESULT hr = S_OK;
  408. IEnumVARIANT *pEnum = NULL;
  409. VARIANT aVariant[8];
  410. ULONG cAdded = 0;
  411. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_EnumerateNode");
  412. TraceAssert(pDsContainer != NULL);
  413. TraceAssert(hwndTree != NULL);
  414. hr = ADsBuildEnumerator(pDsContainer, &pEnum);
  415. FailGracefully(hr, "Unable to build container enumerator object");
  416. //
  417. // Enumerate the given container
  418. //
  419. for (;;)
  420. {
  421. ULONG cFetched = 0;
  422. ULONG i;
  423. //
  424. // Get a bunch of child containers and add them to the tree.
  425. //
  426. ADsEnumerateNext(pEnum, ARRAYSIZE(aVariant), aVariant, &cFetched);
  427. if (cFetched == 0)
  428. break;
  429. for (i = 0; i < cFetched; i++)
  430. {
  431. IADs *pDsObject;
  432. TraceAssert(V_VT(&aVariant[i]) == VT_DISPATCH);
  433. TraceAssert(V_DISPATCH(&aVariant[i]));
  434. if (SUCCEEDED(V_DISPATCH(&aVariant[i])->QueryInterface(IID_IADs,
  435. (LPVOID*)&pDsObject)))
  436. {
  437. hr = _AddTreeNode(pDsObject, NULL, hwndTree, hParentItem, NULL);
  438. if (SUCCEEDED(hr))
  439. cAdded++;
  440. DoRelease(pDsObject);
  441. }
  442. VariantClear(&aVariant[i]);
  443. }
  444. }
  445. hr = S_OK;
  446. exit_gracefully:
  447. DoRelease(pEnum);
  448. if (pdwAdded != NULL)
  449. *pdwAdded = cAdded;
  450. TraceLeaveResult(hr);
  451. }
  452. HRESULT CBrowseDlg::_SearchNode(IDirectorySearch *pDsSearch, HWND hwndTree, HTREEITEM hParentItem, LPDWORD pdwAdded)
  453. {
  454. HRESULT hr = S_OK;
  455. ULONG cAdded = 0;
  456. ADS_SEARCH_HANDLE hSearch = NULL;
  457. ADS_SEARCH_COLUMN column;
  458. ADS_SEARCHPREF_INFO prefInfo[3];
  459. LPWSTR pszName = NULL;
  460. LPWSTR pszADsPath = NULL;
  461. LPWSTR pszObjectClass = NULL;
  462. BSTR bstrName;
  463. LPWSTR aProperties[] = { _szNameAttribute, c_szObjectClass, c_szADsPath, c_szName, c_szRDN };
  464. LPWSTR *pProperties = aProperties;
  465. INT cProperties = ARRAYSIZE(aProperties);
  466. USES_CONVERSION;
  467. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_SearchNode");
  468. TraceAssert(pDsSearch != NULL);
  469. TraceAssert(hwndTree != NULL);
  470. // Set the query prefernece to single level scope, and async retrevial
  471. // rather than waiting for all objects
  472. prefInfo[0].dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
  473. prefInfo[0].vValue.dwType = ADSTYPE_INTEGER;
  474. prefInfo[0].vValue.Integer = ADS_SCOPE_ONELEVEL;
  475. prefInfo[1].dwSearchPref = ADS_SEARCHPREF_ASYNCHRONOUS;
  476. prefInfo[1].vValue.dwType = ADSTYPE_BOOLEAN;
  477. prefInfo[1].vValue.Boolean = TRUE;
  478. prefInfo[2].dwSearchPref = ADS_SEARCHPREF_PAGESIZE; // paged results
  479. prefInfo[2].vValue.dwType = ADSTYPE_INTEGER;
  480. prefInfo[2].vValue.Integer = PAGE_SIZE;
  481. hr = pDsSearch->SetSearchPreference(prefInfo, ARRAYSIZE(prefInfo));
  482. FailGracefully(hr, "Failed to set search preferences");
  483. if (!_szNameAttribute[0])
  484. {
  485. TraceMsg("_szNameAttribute is NULL, so removing from query attributes");
  486. pProperties++;
  487. cProperties--;
  488. }
  489. hr = pDsSearch->ExecuteSearch(_szFilter, pProperties, cProperties, &hSearch);
  490. FailGracefully(hr, "Failed in ExecuteSearch");
  491. for (;;)
  492. {
  493. CLASSCACHEENTRY cce;
  494. LocalFreeStringW(&pszObjectClass);
  495. LocalFreeStringW(&pszADsPath);
  496. LocalFreeStringW(&pszName);
  497. ADsSetLastError(ERROR_SUCCESS, NULL, NULL); // clear the ADSI previous errror
  498. hr = pDsSearch->GetNextRow(hSearch);
  499. FailGracefully(hr, "Failed in GetNextRow");
  500. if (hr == S_ADS_NOMORE_ROWS)
  501. {
  502. DWORD dwError;
  503. WCHAR wszError[64], wszName[64];
  504. hr = ADsGetLastError(&dwError, wszError, ARRAYSIZE(wszError), wszName, ARRAYSIZE(wszName));
  505. if (SUCCEEDED(hr) && (dwError != ERROR_MORE_DATA))
  506. {
  507. break;
  508. }
  509. }
  510. //
  511. // Get the columns for each of the properties we are interested in, if
  512. // we failed to get any of the base properties for the object then lets
  513. // just skip this entry as we cannot build a valid IDLIST for it. The
  514. // properties that we request should be present on all objects.
  515. //
  516. if (FAILED(pDsSearch->GetColumn(hSearch, c_szObjectClass, &column)))
  517. {
  518. TraceMsg("Failed to get objectClass from search");
  519. continue;
  520. }
  521. hr = ObjectClassFromSearchColumn(&column, &pszObjectClass);
  522. pDsSearch->FreeColumn(&column);
  523. FailGracefully(hr, "Failed to get the object class from the property");
  524. if (FAILED(pDsSearch->GetColumn(hSearch, c_szADsPath, &column)))
  525. {
  526. TraceMsg("Failed to get ADsPath from search");
  527. continue;
  528. }
  529. hr = StringFromSearchColumn(&column, &pszADsPath);
  530. pDsSearch->FreeColumn(&column);
  531. FailGracefully(hr, "Failed to convert the ADsPath column to a string");
  532. //
  533. // Try and read the name attribute from the query results, if that fails
  534. // then lets pass the ADsPath into the pathname API and get the
  535. // LEAF name (RDN) back.
  536. //
  537. if (_szNameAttribute[0])
  538. {
  539. TraceMsg("Name attribute specified, therefore trying to decode that");
  540. hr = pDsSearch->GetColumn(hSearch, _szNameAttribute, &column);
  541. }
  542. if (!_szNameAttribute[0] || FAILED(hr))
  543. {
  544. TraceMsg("Either _szNameAttribute == NULL, or failed to read it");
  545. hr = pDsSearch->GetColumn(hSearch, c_szName, &column);
  546. if (FAILED(hr))
  547. {
  548. TraceMsg("'name' not returned as a column, so checking for RDN");
  549. hr = pDsSearch->GetColumn(hSearch, c_szRDN, &column);
  550. }
  551. }
  552. if (SUCCEEDED(hr))
  553. {
  554. //
  555. // so that succeeded and we have a search column that we can decode, so lets
  556. // do so and put that value into a string
  557. //
  558. hr = StringFromSearchColumn(&column, &pszName);
  559. pDsSearch->FreeColumn(&column);
  560. FailGracefully(hr, "Failed to convert the name column to a string");
  561. }
  562. else
  563. {
  564. BSTR bstrName;
  565. //
  566. // so now we attempt to use the path cracker as the string doesn't exist
  567. //
  568. TraceMsg("Failed to get the name, rdn etc, so using the path cracker");
  569. hr = _GetPathCracker();
  570. FailGracefully(hr, "Unable to create ADsPathname object");
  571. hr = _pPathCracker->Set((LPWSTR)pszADsPath, ADS_SETTYPE_FULL);
  572. FailGracefully(hr, "Unable to parse path");
  573. hr = _pPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  574. FailGracefully(hr, "Failed to set the display type for this value");
  575. hr = _pPathCracker->Retrieve(ADS_FORMAT_X500_DN, &bstrName);
  576. FailGracefully(hr, "Unable to retrieve requested path format");
  577. hr = LocalAllocStringW(&pszName, bstrName);
  578. SysFreeString(bstrName);
  579. FailGracefully(hr, "Failed to alloc clone of the name");
  580. }
  581. Trace(TEXT("class %s, name %s, ADsPath %s"), W2T(pszObjectClass), W2T(pszName), W2T(pszADsPath));
  582. hr = _AddTreeNode(pszADsPath, pszObjectClass, pszName, hwndTree, hParentItem, NULL);
  583. if (SUCCEEDED(hr))
  584. cAdded++;
  585. }
  586. hr = S_OK;
  587. exit_gracefully:
  588. if (hSearch != NULL)
  589. pDsSearch->CloseSearchHandle(hSearch);
  590. LocalFreeStringW(&pszObjectClass);
  591. LocalFreeStringW(&pszADsPath);
  592. LocalFreeStringW(&pszName);
  593. if (pdwAdded != NULL)
  594. *pdwAdded = cAdded;
  595. TraceLeaveResult(hr);
  596. }
  597. HRESULT CBrowseDlg::_AddTreeNode(IADs *pDsObject, LPCWSTR pObjectPath, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem)
  598. {
  599. HRESULT hr = E_FAIL;
  600. BSTR bstrClass = NULL;
  601. BSTR bstrPath = NULL;
  602. BSTR bstrName = NULL;
  603. VARIANT var = {0};
  604. LPWSTR pszName = NULL;
  605. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_AddTreeNode");
  606. TraceAssert(pDsObject != NULL);
  607. TraceAssert(hwndTree != NULL);
  608. // Do we want to include hidden objects?
  609. if (!(_bi.dwFlags & DSBI_INCLUDEHIDDEN))
  610. {
  611. if (SUCCEEDED(pDsObject->Get(c_szShowInAdvancedViewOnly, &var)))
  612. {
  613. TraceAssert(V_VT(&var) == VT_BOOL);
  614. if (!V_BOOL(&var))
  615. ExitGracefully(hr, E_FAIL, "Hidden node");
  616. VariantClear(&var);
  617. }
  618. }
  619. // Get the path and class name
  620. if (!pObjectPath)
  621. pDsObject->get_ADsPath(&bstrPath);
  622. pDsObject->get_Class(&bstrClass);
  623. // Try to get the name property, if that fails then try RDN (for X5 connectivity)
  624. if (SUCCEEDED(pDsObject->Get(c_szName, &var))
  625. || SUCCEEDED(pDsObject->Get(c_szRDN, &var))
  626. || (_szNameAttribute[0] && SUCCEEDED(pDsObject->Get(_szNameAttribute, &var))))
  627. {
  628. TraceAssert(V_VT(&var) == VT_BSTR);
  629. pszName = V_BSTR(&var);
  630. }
  631. else if (SUCCEEDED(pDsObject->get_Name(&bstrName)))
  632. {
  633. pszName = bstrName;
  634. }
  635. if (pszName != NULL)
  636. {
  637. hr = _AddTreeNode(pObjectPath ? pObjectPath : bstrPath,
  638. bstrClass, pszName, hwndTree, hParentItem, phitem);
  639. }
  640. exit_gracefully:
  641. if (bstrPath)
  642. SysFreeString(bstrPath);
  643. SysFreeString(bstrClass);
  644. SysFreeString(bstrName);
  645. VariantClear(&var);
  646. TraceLeaveResult(hr);
  647. }
  648. HRESULT CBrowseDlg::_AddTreeNode(LPCWSTR pszPath, LPCWSTR pszClass, LPCWSTR pszName, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem)
  649. {
  650. HRESULT hr;
  651. BSTR bstrWinPath = NULL;
  652. CLASSCACHEGETINFO ccgi = { 0 };
  653. LPCLASSCACHEENTRY pCacheEntry = NULL;
  654. TV_INSERTSTRUCT tvi;
  655. BSTR bstrName = NULL;
  656. BOOL fIsContainer = TRUE;
  657. long nElements;
  658. INT iResult = 0;
  659. HTREEITEM hitem = NULL;
  660. WCHAR szIconLocation[MAX_PATH];
  661. INT iIconResID = 0;
  662. USES_CONVERSION;
  663. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_AddTreeNode");
  664. TraceAssert(hwndTree != NULL);
  665. if (!pszPath || !*pszPath || !pszClass || !*pszClass)
  666. ExitGracefully(hr, E_INVALIDARG, "Missing required string parameter");
  667. hr = _GetPathCracker();
  668. FailGracefully(hr, "Failed to get the path cracker");
  669. hr = _pPathCracker->Set((LPWSTR)pszPath, ADS_SETTYPE_FULL);
  670. FailGracefully(hr, "Failed to set the path into the cracker");
  671. //
  672. // we can get the name from the cracker
  673. //
  674. if (!pszName || !*pszName)
  675. {
  676. _pPathCracker->SetDisplayType(ADS_DISPLAY_VALUE_ONLY); // value only pls.
  677. hr = _pPathCracker->Retrieve(ADS_FORMAT_LEAF, &bstrName);
  678. FailGracefully(hr, "Failed to get leaf name");
  679. Trace(TEXT("bstrName: is %s"), W2CT(bstrName));
  680. pszName = bstrName;
  681. }
  682. tvi.hParent = hParentItem;
  683. tvi.hInsertAfter = TVI_SORT;
  684. tvi.item.mask = TVIF_CHILDREN | TVIF_IMAGE | TVIF_SELECTEDIMAGE |
  685. TVIF_STATE | TVIF_TEXT | TVIF_PARAM;
  686. tvi.item.state = 0;
  687. tvi.item.stateMask = (UINT)-1;
  688. tvi.item.cchTextMax = 0;
  689. tvi.item.cChildren = 1;
  690. tvi.item.pszText = (LPTSTR)W2CT(pszName);
  691. tvi.item.iImage = 0;
  692. tvi.item.iSelectedImage = 0;
  693. tvi.item.lParam = 0;
  694. //
  695. // See if this object is a container, and get its image indexes
  696. // from the class cache.
  697. //
  698. ccgi.dwFlags = CLASSCACHE_CONTAINER|CLASSCACHE_TREATASLEAF|CLASSCACHE_ICONS|CLASSCACHE_DSAVAILABLE;
  699. ccgi.pPath = (LPWSTR)pszPath;
  700. ccgi.pObjectClass = (LPWSTR)pszClass;
  701. ccgi.pServer = _pServer;
  702. if (_bi.dwFlags & DSBI_HASCREDENTIALS)
  703. {
  704. TraceMsg("Passing credential information to ClassCache API");
  705. ccgi.pUserName = (LPWSTR)_bi.pUserName;
  706. ccgi.pPassword = (LPWSTR)_bi.pPassword;
  707. }
  708. if (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE)
  709. {
  710. TraceMsg("Setting the CLASSCACHE_SIMPLEAUTHENTICATE flag");
  711. ccgi.dwFlags |= CLASSCACHE_SIMPLEAUTHENTICATE;
  712. }
  713. if (SUCCEEDED(ClassCache_GetClassInfo(&ccgi, &pCacheEntry)))
  714. {
  715. fIsContainer = _IsClassContainer(pCacheEntry, _bi.dwFlags & DSBI_IGNORETREATASLEAF);
  716. _GetIconLocation(pCacheEntry, DSGIF_DEFAULTISCONTAINER|DSGIF_GETDEFAULTICON, szIconLocation, ARRAYSIZE(szIconLocation), &iIconResID);
  717. ClassCache_ReleaseClassInfo(&pCacheEntry);
  718. }
  719. if (!fIsContainer)
  720. ExitGracefully(hr, E_FAIL, "Not a container");
  721. //
  722. // If we have a callback function then call it taking a note of
  723. // the changes the caller wants to make to the node we are adding
  724. // to the tree, internally the LPARAM of this item still points at
  725. // the ADsPath/Class structure, but the display information has
  726. // been suitably modified.
  727. //
  728. if (_bi.pfnCallback)
  729. {
  730. DSBITEM dsbItem;
  731. dsbItem.cbStruct = SIZEOF(dsbItem);
  732. dsbItem.pszADsPath = pszPath;
  733. dsbItem.pszClass = pszClass;
  734. dsbItem.dwMask = DSBF_STATE | DSBF_DISPLAYNAME;
  735. dsbItem.dwState = 0;
  736. dsbItem.dwStateMask = DSBS_ROOT;
  737. StrCpyN(dsbItem.szDisplayName, W2CT(pszName), ARRAYSIZE(dsbItem.szDisplayName));
  738. StrCpyN(dsbItem.szIconLocation, W2CT(szIconLocation), ARRAYSIZE(dsbItem.szIconLocation));
  739. dsbItem.iIconResID = iIconResID;
  740. if (_bi.dwFlags & DSBI_CHECKBOXES)
  741. {
  742. // handle the checked case properly;
  743. dsbItem.dwStateMask |= DSBS_CHECKED;
  744. }
  745. if ((hParentItem == TVI_ROOT) || !hParentItem)
  746. {
  747. dsbItem.dwState |= DSBS_ROOT;
  748. }
  749. iResult = _bi.pfnCallback(GetParent(hwndTree), DSBM_QUERYINSERT, (LPARAM)&dsbItem, _bi.lParam);
  750. #ifdef UNICODE
  751. //
  752. // For the UNICODE version of this call we must check to see if the
  753. // caller handled it, if not then we will attempt to send an ANSI version
  754. // and thunk the parameters accordingly.
  755. //
  756. if (!iResult)
  757. {
  758. DSBITEMA dsbItemA;
  759. CopyMemory(&dsbItemA, &dsbItem, SIZEOF(dsbItemA));
  760. WideCharToMultiByte(CP_ACP, 0,
  761. pszName, -1,
  762. dsbItemA.szDisplayName, ARRAYSIZE(dsbItemA.szDisplayName),
  763. NULL, NULL);
  764. WideCharToMultiByte(CP_ACP, 0,
  765. dsbItem.szIconLocation, -1,
  766. dsbItemA.szIconLocation, ARRAYSIZE(dsbItemA.szIconLocation),
  767. NULL, NULL);
  768. iResult = _bi.pfnCallback(GetParent(hwndTree), DSBM_QUERYINSERTA, (LPARAM)&dsbItemA, _bi.lParam);
  769. if (iResult)
  770. {
  771. TraceMsg("ANSI DSBM_QUERYINSERT was successful");
  772. Assert(SIZEOF(DSBITEMA) == SIZEOF(DSBITEMW));
  773. CopyMemory(&dsbItem, &dsbItemA, SIZEOF(dsbItem));
  774. MultiByteToWideChar(CP_ACP, 0,
  775. dsbItemA.szDisplayName, -1,
  776. dsbItem.szDisplayName, ARRAYSIZE(dsbItem.szDisplayName));
  777. MultiByteToWideChar(CP_ACP, 0,
  778. dsbItemA.szIconLocation, -1,
  779. dsbItem.szIconLocation, ARRAYSIZE(dsbItem.szIconLocation));
  780. }
  781. }
  782. #endif
  783. //
  784. // iResult == TRUE then the user has modified the structure and we
  785. // should attempt to apply the changes they have made to the
  786. // item we are about to add to the view.
  787. //
  788. if (iResult)
  789. {
  790. if ((dsbItem.dwMask & DSBF_STATE) &&
  791. (dsbItem.dwStateMask & DSBS_HIDDEN))
  792. {
  793. ExitGracefully(hr, E_FAIL, "Item is hidden, therefore skipping");
  794. }
  795. if ((_bi.dwFlags & DSBI_CHECKBOXES) &&
  796. (dsbItem.dwMask & DSBF_STATE) &&
  797. (dsbItem.dwStateMask & DSBS_CHECKED))
  798. {
  799. // FEATURE: set the state image
  800. }
  801. if (dsbItem.dwMask & DSBF_ICONLOCATION)
  802. {
  803. StrCpyW(szIconLocation, T2CW(dsbItem.szIconLocation));
  804. iIconResID = dsbItem.iIconResID;
  805. }
  806. if (dsbItem.dwMask & DSBF_DISPLAYNAME)
  807. {
  808. dsbItem.szDisplayName[DSB_MAX_DISPLAYNAME_CHARS-1] = TEXT('\0');
  809. tvi.item.pszText = dsbItem.szDisplayName;
  810. }
  811. }
  812. }
  813. //
  814. // convert the icon location to an index that we can use in the tree view
  815. //
  816. Trace(TEXT("Icon location is: %s,%d"), W2T(szIconLocation), iIconResID);
  817. tvi.item.iImage = tvi.item.iSelectedImage = Shell_GetCachedImageIndex(W2T(szIconLocation), iIconResID, 0x0);
  818. Trace(TEXT("Index into the shell image list %d"), tvi.item.iImage);
  819. //
  820. // Make a copy of the path to store as the node data.
  821. // Try the path cracker first, so we get a known format.
  822. // If that fails, just make a copy of what we've got.
  823. //
  824. // The problem with the path cracker is that it just cannot cope
  825. // with names with no elements, therefore we have to work around
  826. // this by checking for no elements then looking at the retrieved
  827. // path to see if it terminates in a bogus way, if it does then
  828. // lets fix it in a local buffer before creating the tree view node.
  829. //
  830. hr = _pPathCracker->GetNumElements(&nElements);
  831. if (SUCCEEDED(hr))
  832. {
  833. _pPathCracker->SetDisplayType(ADS_DISPLAY_FULL);
  834. Trace(TEXT("nElements %d"), nElements);
  835. hr = _pPathCracker->Retrieve(ADS_FORMAT_WINDOWS, &bstrWinPath);
  836. if (SUCCEEDED(hr))
  837. {
  838. int cchWinPath = lstrlenW(bstrWinPath);
  839. Trace(TEXT("bstrWinPath %s (%d), nElements %d"),
  840. W2T(bstrWinPath), cchWinPath, nElements);
  841. if ((!nElements) &&
  842. (cchWinPath >= 3) &&
  843. (bstrWinPath[cchWinPath-1] == L'/') &&
  844. (bstrWinPath[cchWinPath-2] == L'/') &&
  845. (bstrWinPath[cchWinPath-3] == L':'))
  846. {
  847. LPWSTR pFixedWinPath = NULL;
  848. hr = LocalAllocStringW(&pFixedWinPath, bstrWinPath);
  849. if (SUCCEEDED(hr))
  850. {
  851. pFixedWinPath[cchWinPath-2] = L'\0';
  852. Trace(TEXT("pFixedWinPath %s"), W2T(pFixedWinPath));
  853. hr = _BuildNodeString(pFixedWinPath, pszClass, (LPWSTR*)&tvi.item.lParam);
  854. LocalFreeStringW(&pFixedWinPath);
  855. }
  856. }
  857. else
  858. {
  859. hr = _BuildNodeString(bstrWinPath, pszClass, (LPWSTR*)&tvi.item.lParam);
  860. }
  861. }
  862. }
  863. if (FAILED(hr))
  864. hr = _BuildNodeString(pszPath, pszClass, (LPWSTR*)&tvi.item.lParam);
  865. FailGracefully(hr, "Unable to build node data");
  866. //
  867. // finally lets add the item to the tree, if that fails then ensure we free the
  868. // structure hanging from the TVI.
  869. //
  870. hitem = TreeView_InsertItem(hwndTree, &tvi);
  871. if (!hitem)
  872. {
  873. LocalFreeStringW((LPWSTR*)&tvi.item.lParam);
  874. hr = E_FAIL;
  875. }
  876. exit_gracefully:
  877. SysFreeString(bstrName);
  878. SysFreeString(bstrWinPath);
  879. if (phitem)
  880. *phitem = hitem;
  881. TraceLeaveResult(hr);
  882. }
  883. HRESULT CBrowseDlg::_AddTreeNode(LPDOMAINDESC pDomainDesc, HWND hwndTree, HTREEITEM hParentItem, HTREEITEM* phitem)
  884. {
  885. HRESULT hr;
  886. WCHAR szBuffer[MAX_PATH];
  887. DWORD dwIndex;
  888. HTREEITEM hitem;
  889. USES_CONVERSION;
  890. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_AddTreeNode");
  891. while (pDomainDesc)
  892. {
  893. StrCpyW(szBuffer, c_szLDAPPrefix);
  894. if (_pServer)
  895. {
  896. StrCatW(szBuffer, _pServer);
  897. StrCatW(szBuffer, L"/");
  898. }
  899. StrCatW(szBuffer, pDomainDesc->pszNCName);
  900. Trace(TEXT("Scope is: %s"), W2T(szBuffer));
  901. hr = _AddTreeNode(szBuffer,
  902. pDomainDesc->pszObjectClass, NULL,
  903. hwndTree, hParentItem,
  904. &hitem);
  905. FailGracefully(hr, "Failed to add location node");
  906. if (pDomainDesc->pdChildList)
  907. {
  908. hr = _AddTreeNode(pDomainDesc->pdChildList,
  909. hwndTree, hitem,
  910. NULL);
  911. FailGracefully(hr, "Failed to add children");
  912. }
  913. pDomainDesc = pDomainDesc->pdNextSibling;
  914. }
  915. hr = S_OK;
  916. exit_gracefully:
  917. if (phitem)
  918. *phitem = hitem;
  919. TraceLeaveResult(hr);
  920. }
  921. BOOL CBrowseDlg::_OnInitDlg(HWND hDlg)
  922. {
  923. HRESULT hr;
  924. HWND hwndTree;
  925. IADs *pRoot = NULL;
  926. IDsBrowseDomainTree* pDsDomains = NULL;
  927. BSTR bstrServer = NULL;
  928. LPDOMAINTREE pDomainTree = NULL;
  929. HTREEITEM hitemRoot = NULL;
  930. USES_CONVERSION;
  931. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_OnInitDlg");
  932. TraceAssert(hDlg != NULL);
  933. if (_bi.pszCaption != NULL)
  934. SetWindowText(hDlg, W2CT(_bi.pszCaption));
  935. if (_bi.pszTitle == NULL)
  936. {
  937. RECT rc;
  938. LONG yPos;
  939. HWND hwnd;
  940. // Get the position of the title window and hide it
  941. hwnd = GetDlgItem(hDlg, DSBID_BANNER);
  942. GetWindowRect(hwnd, &rc);
  943. yPos = rc.top;
  944. ShowWindow(hwnd, SW_HIDE);
  945. // Get the position of the tree control and adjust it
  946. // to cover the title window.
  947. hwnd = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
  948. GetWindowRect(hwnd, &rc);
  949. rc.top = yPos;
  950. MapWindowPoints(NULL, hDlg, (LPPOINT)&rc, 2);
  951. MoveWindow(hwnd,
  952. rc.left, rc.top,
  953. rc.right - rc.left, rc.bottom - rc.top,
  954. FALSE);
  955. }
  956. else
  957. {
  958. SetDlgItemText(hDlg, DSBID_BANNER, W2CT(_bi.pszTitle));
  959. }
  960. hwndTree = GetDlgItem(hDlg, DSBID_CONTAINERLIST);
  961. TraceAssert(hwndTree != NULL);
  962. // Update the TreeView style according to what the caller wants
  963. if (_bi.dwFlags & (DSBI_NOBUTTONS | DSBI_NOLINES | DSBI_NOLINESATROOT | DSBI_CHECKBOXES))
  964. {
  965. DWORD dwStyle = GetWindowLong(hwndTree, GWL_STYLE);
  966. dwStyle &= ~(_bi.dwFlags & (DSBI_NOBUTTONS | DSBI_NOLINES | DSBI_NOLINESATROOT));
  967. dwStyle |= (_bi.dwFlags & DSBI_CHECKBOXES);
  968. SetWindowLong(hwndTree, GWL_STYLE, dwStyle);
  969. }
  970. if (_bi.dwFlags & DSBI_CHECKBOXES)
  971. {
  972. // load and set the state imagelist (unchecked and checked squares)
  973. }
  974. //
  975. // ensure we set the shared image list for the tree, this comes from shell32.
  976. //
  977. HIMAGELIST himlSmall;
  978. Shell_GetImageLists(NULL, &himlSmall);
  979. TreeView_SetImageList(hwndTree, himlSmall, TVSIL_NORMAL);
  980. TraceAssert(_bi.pszRoot != NULL);
  981. Trace(TEXT("pszRoot is: %s"), W2CT(_bi.pszRoot));
  982. //
  983. // if we have a callback function then we need to call it to get the information we need
  984. // to browse the DS namespace.
  985. //
  986. if (_bi.pfnCallback)
  987. {
  988. DSBROWSEDATA dbd = { 0 };
  989. dbd.pszFilter = _szFilter;
  990. dbd.cchFilter = ARRAYSIZE(_szFilter);
  991. dbd.pszNameAttribute = _szNameAttribute;
  992. dbd.cchNameAttribute = ARRAYSIZE(_szNameAttribute);
  993. TraceMsg("Calling callback to see if it can provide enumeration information");
  994. if (_bi.pfnCallback(hDlg, DSBM_GETBROWSEDATA, (LPARAM)&dbd, _bi.lParam))
  995. {
  996. Trace(TEXT("szFilter after DSBM_GETBROWSEDATA: %s"),
  997. _szFilter[0] ? W2T(_szFilter):TEXT("<not specified>"));
  998. Trace(TEXT("szNameAttribute after DSBM_GETBROWSEDATA: %s"),
  999. _szNameAttribute[0] ? W2T(_szNameAttribute):TEXT("<not specified>"));
  1000. if (!_szFilter[0])
  1001. ExitGracefully(hr, E_FAIL, "szFilter returned was NULL");
  1002. }
  1003. }
  1004. //
  1005. // Bind to the root object (make sure it's a valid object)
  1006. //
  1007. hr = ADsOpenObject((LPWSTR)_bi.pszRoot,
  1008. (_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pUserName:NULL,
  1009. (_bi.dwFlags & DSBI_HASCREDENTIALS) ? (LPWSTR)_bi.pPassword:NULL,
  1010. (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE) ? 0:ADS_SECURE_AUTHENTICATION,
  1011. IID_IADs, (LPVOID*)&pRoot);
  1012. FailGracefully(hr, "Unable to bind to root object");
  1013. // attempt to decode the root path we have been given, if this includes a server
  1014. // name then lets store that so that we can call the cache codes. internally
  1015. // we preserver this.
  1016. hr = _GetPathCracker();
  1017. FailGracefully(hr, "Failed to get the path cracker API");
  1018. hr = _pPathCracker->Set((LPWSTR)_bi.pszRoot, ADS_SETTYPE_FULL);
  1019. FailGracefully(hr, "Unable to put the path into the path cracker");
  1020. if (SUCCEEDED(_pPathCracker->Retrieve(ADS_FORMAT_SERVER, &bstrServer)))
  1021. {
  1022. Trace(TEXT("Root path contains a server: %s"), W2T(bstrServer));
  1023. hr = LocalAllocStringW(&_pServer, bstrServer);
  1024. FailGracefully(hr, "Failed to allocate copy of ADsPath");
  1025. }
  1026. // DSBI_ENTIREDIRECTORY contains 2 bits which means that
  1027. // "if (_bi.dwFlags & DSBI_ENTIREDIRECTORY)" gives a false
  1028. // positive when a client calls with only DSBI_NOROOT set. This
  1029. // causes the entire directory to be displayed when it shouldn't.
  1030. //if (_bi.dwFlags & DSBI_ENTIREDIRECTORY)
  1031. if (_bi.dwFlags & (DSBI_ENTIREDIRECTORY & ~DSBI_NOROOT))
  1032. {
  1033. TV_ITEM tvi;
  1034. LPDOMAINDESC pDomain;
  1035. if (!(_bi.dwFlags & DSBI_NOROOT))
  1036. {
  1037. hr = _AddTreeNode(pRoot, _bi.pszRoot, hwndTree, NULL, &hitemRoot);
  1038. FailGracefully(hr, "Failed when adding in root node");
  1039. tvi.mask = TVIF_STATE|TVIF_HANDLE;
  1040. tvi.hItem = hitemRoot;
  1041. tvi.stateMask = -1;
  1042. TreeView_GetItem(hwndTree, &tvi);
  1043. tvi.state |= TVIS_EXPANDEDONCE;
  1044. TreeView_SetItem(hwndTree, &tvi);
  1045. }
  1046. hr = CoCreateInstance(CLSID_DsDomainTreeBrowser, NULL,CLSCTX_INPROC_SERVER, IID_IDsBrowseDomainTree, (LPVOID*)&pDsDomains);
  1047. FailGracefully(hr, "Failed to get IDsDomainTreeBrowser");
  1048. LPCWSTR pUserName = (_bi.dwFlags & DSBI_HASCREDENTIALS) ? _bi.pUserName:NULL;
  1049. LPCWSTR pPassword = (_bi.dwFlags & DSBI_HASCREDENTIALS) ? _bi.pPassword:NULL;
  1050. hr = pDsDomains->SetComputer(_pServer, pUserName, pPassword);
  1051. if (SUCCEEDED(hr))
  1052. hr = pDsDomains->GetDomains(&pDomainTree, DBDTF_RETURNFQDN);
  1053. if (SUCCEEDED(hr))
  1054. _AddTreeNode(pDomainTree->aDomains, hwndTree, hitemRoot, NULL);
  1055. }
  1056. else if (_bi.dwFlags & DSBI_NOROOT)
  1057. {
  1058. // Skip root node and add its children as toplevel nodes
  1059. hr = _ExpandNode(pRoot, hwndTree, NULL);
  1060. }
  1061. else
  1062. {
  1063. // Add the root node
  1064. hr = _AddTreeNode(pRoot, _bi.pszRoot, hwndTree, NULL, &hitemRoot);
  1065. }
  1066. exit_gracefully:
  1067. if (SUCCEEDED(hr))
  1068. {
  1069. //
  1070. // Set the selected path to expand the tree, this can either be NULL, or
  1071. // a ADSI path. If we fail to do that and we have a root node then
  1072. // lets expand the root node to at least have something highlighted / expanded.
  1073. //
  1074. if (!(_bi.dwFlags & DSBI_EXPANDONOPEN) || (-1 == _SetSelectedPath(hDlg, _bi.pszPath)))
  1075. {
  1076. if (!(_bi.dwFlags & DSBI_NOROOT))
  1077. {
  1078. TraceMsg("Failed to select node, therefore expanding root");
  1079. _SetSelectedPath(hDlg, _bi.pszRoot);
  1080. }
  1081. }
  1082. if (_bi.pfnCallback)
  1083. _bi.pfnCallback(hDlg, BFFM_INITIALIZED, 0, _bi.lParam);
  1084. }
  1085. else
  1086. {
  1087. // SetLastError(???);
  1088. EndDialog(hDlg, -1);
  1089. }
  1090. DoRelease(pRoot);
  1091. DoRelease(pDsDomains);
  1092. SysFreeString(bstrServer);
  1093. TraceLeaveValue(TRUE);
  1094. }
  1095. BOOL CBrowseDlg::_OnNotify(HWND hDlg, int idCtrl, LPNMHDR pnmh)
  1096. {
  1097. LPNM_TREEVIEW pnmtv = (LPNM_TREEVIEW)pnmh;
  1098. LPWSTR pszClass;
  1099. HRESULT hr;
  1100. USES_CONVERSION;
  1101. switch (pnmh->code)
  1102. {
  1103. case TVN_DELETEITEM:
  1104. LocalFree((HLOCAL)pnmtv->itemOld.lParam);
  1105. break;
  1106. case TVN_ITEMEXPANDING:
  1107. if ((pnmtv->action == TVE_EXPAND) &&
  1108. !(pnmtv->itemNew.state & TVIS_EXPANDEDONCE))
  1109. {
  1110. if (pnmtv->itemNew.hItem == NULL)
  1111. {
  1112. pnmtv->itemNew.hItem = TreeView_GetSelection(pnmh->hwndFrom);
  1113. }
  1114. // Whether we succeed or not, mark the node as having been
  1115. // expanded once.
  1116. pnmtv->itemNew.mask = TVIF_STATE;
  1117. pnmtv->itemNew.stateMask = TVIS_EXPANDEDONCE;
  1118. pnmtv->itemNew.state = TVIS_EXPANDEDONCE;
  1119. if (!pnmtv->itemNew.lParam ||
  1120. FAILED(_ExpandNode((LPWSTR)pnmtv->itemNew.lParam,
  1121. pnmh->hwndFrom, pnmtv->itemNew.hItem)))
  1122. {
  1123. // Mark this node as having no children
  1124. pnmtv->itemNew.mask |= TVIF_CHILDREN;
  1125. pnmtv->itemNew.cChildren = 0;
  1126. }
  1127. TreeView_SetItem(pnmh->hwndFrom, &pnmtv->itemNew);
  1128. }
  1129. break;
  1130. case TVN_ITEMEXPANDED:
  1131. {
  1132. CLASSCACHEGETINFO ccgi = { 0 };
  1133. LPCLASSCACHEENTRY pCacheEntry = NULL;
  1134. pszClass = (LPWSTR)ByteOffset(pnmtv->itemNew.lParam, StringByteSizeW((LPWSTR)pnmtv->itemNew.lParam));
  1135. if (!pszClass)
  1136. return FALSE;
  1137. // Switch to the "open" image
  1138. ccgi.dwFlags = CLASSCACHE_DSAVAILABLE|CLASSCACHE_ICONS|(DSGIF_ISOPEN << CLASSCACHE_IMAGEMASK_BIT);
  1139. ccgi.pPath = (LPWSTR)pnmtv->itemNew.lParam;
  1140. ccgi.pObjectClass = pszClass;
  1141. ccgi.pServer = _pServer;
  1142. if (_bi.dwFlags & DSBI_HASCREDENTIALS)
  1143. {
  1144. ccgi.pUserName = (LPWSTR)_bi.pUserName;
  1145. ccgi.pPassword = (LPWSTR)_bi.pPassword;
  1146. }
  1147. if (_bi.dwFlags & DSBI_SIMPLEAUTHENTICATE)
  1148. ccgi.dwFlags |= CLASSCACHE_SIMPLEAUTHENTICATE;
  1149. if ((pnmtv->action == TVE_EXPAND) || (pnmtv->action == TVE_COLLAPSE))
  1150. {
  1151. //
  1152. // handle the expand and colapse of the icon in the tree, assuming that
  1153. // we show the correct state of corse (for those who don't have open states).
  1154. //
  1155. if (SUCCEEDED(ClassCache_GetClassInfo(&ccgi, &pCacheEntry)))
  1156. {
  1157. WCHAR szIconLocation[MAX_PATH];
  1158. INT iIconResID;
  1159. hr = _GetIconLocation(pCacheEntry, DSGIF_DEFAULTISCONTAINER|DSGIF_ISOPEN, szIconLocation, ARRAYSIZE(szIconLocation), &iIconResID);
  1160. ClassCache_ReleaseClassInfo(&pCacheEntry);
  1161. if (SUCCEEDED(hr))
  1162. {
  1163. pnmtv->itemNew.mask = TVIF_HANDLE | TVIF_IMAGE | TVIF_SELECTEDIMAGE;
  1164. pnmtv->itemNew.iImage = pnmtv->itemNew.iSelectedImage = Shell_GetCachedImageIndex(W2T(szIconLocation), iIconResID, 0x0);
  1165. TreeView_SetItem(pnmh->hwndFrom, &pnmtv->itemNew);
  1166. }
  1167. }
  1168. }
  1169. break;
  1170. }
  1171. case TVN_SELCHANGED:
  1172. {
  1173. if (_bi.pfnCallback)
  1174. _bi.pfnCallback(hDlg, BFFM_SELCHANGED, (LPARAM)_GetSelectedPath(hDlg), _bi.lParam);
  1175. break;
  1176. }
  1177. default:
  1178. return FALSE;
  1179. }
  1180. return TRUE;
  1181. }
  1182. void CBrowseDlg::_OnOK(HWND hDlg)
  1183. {
  1184. int nResult = -1;
  1185. LPCWSTR pszADsPath;
  1186. LPCWSTR pszObjectClass;
  1187. BSTR bstrPath = NULL;
  1188. DWORD dwFormat = _pServer ? ADS_FORMAT_X500:ADS_FORMAT_X500_NO_SERVER;
  1189. USES_CONVERSION;
  1190. TraceEnter(TRACE_BROWSE, "CBrowseDlg::_OnNotify");
  1191. TraceAssert(hDlg != NULL);
  1192. pszADsPath = _GetSelectedPath(hDlg);
  1193. pszObjectClass = GetSelectedObjectClass(hDlg);
  1194. if (!pszADsPath || !pszObjectClass)
  1195. ExitGracefully(nResult, -1, "Failed to get selected object");
  1196. // Honor the return type if they gave us one.
  1197. if ((_bi.dwFlags & DSBI_RETURN_FORMAT) && _bi.dwReturnFormat)
  1198. {
  1199. TraceMsg("Caller specified a dwReturnFormat");
  1200. dwFormat = _bi.dwReturnFormat;
  1201. }
  1202. // CBrowseDlg uses ADS_FORMAT_WINDOWS internally, so no need to convert if
  1203. // that's what they want.
  1204. if (dwFormat != ADS_FORMAT_WINDOWS)
  1205. {
  1206. HRESULT hr;
  1207. hr = _GetPathCracker();
  1208. FailGracefully(hr, "Unable to create ADsPathname object");
  1209. hr = _pPathCracker->Set((LPWSTR)pszADsPath, ADS_SETTYPE_FULL);
  1210. FailGracefully(hr, "Unable to parse path");
  1211. hr = _pPathCracker->Retrieve(dwFormat, &bstrPath);
  1212. FailGracefully(hr, "Unable to retrieve requested path format");
  1213. pszADsPath = bstrPath;
  1214. }
  1215. // return the object class to the caller
  1216. StrCpyNW(_bi.pszPath, pszADsPath, _bi.cchPath);
  1217. if ((_bi.dwFlags & DSBI_RETURNOBJECTCLASS) && _bi.pszObjectClass)
  1218. {
  1219. Trace(TEXT("Returning objectClass: %s"), W2CT(pszObjectClass));
  1220. StrCpyNW(_bi.pszObjectClass, pszObjectClass, _bi.cchObjectClass);
  1221. }
  1222. nResult = IDOK;
  1223. exit_gracefully:
  1224. SysFreeString(bstrPath);
  1225. EndDialog(hDlg, nResult);
  1226. TraceLeaveVoid();
  1227. }
  1228. BOOL CBrowseDlg::_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1229. {
  1230. switch (uMsg)
  1231. {
  1232. case WM_NOTIFY:
  1233. return _OnNotify(hDlg, (int)wParam, (LPNMHDR)lParam);
  1234. case BFFM_ENABLEOK:
  1235. EnableWindow(GetDlgItem(hDlg, IDOK), (BOOL)wParam);
  1236. break;
  1237. case BFFM_SETSELECTIONA:
  1238. case BFFM_SETSELECTIONW:
  1239. _SetSelectedPath(hDlg, (LPCWSTR)lParam); // lParam points to an ADSI path, which we require to be UNICODE
  1240. break;
  1241. case WM_COMMAND:
  1242. switch (GET_WM_COMMAND_ID(wParam, lParam))
  1243. {
  1244. case IDOK:
  1245. _OnOK(hDlg);
  1246. break;
  1247. case IDCANCEL:
  1248. EndDialog(hDlg, IDCANCEL);
  1249. break;
  1250. default:
  1251. // Message not handled
  1252. return FALSE;
  1253. }
  1254. break;
  1255. case WM_INITDIALOG:
  1256. return _OnInitDlg(hDlg);
  1257. case WM_HELP:
  1258. case WM_CONTEXTMENU:
  1259. {
  1260. // check to see if we have a callback, if so then lets call so that they
  1261. // can display help on this object.
  1262. if (!_bi.pfnCallback)
  1263. return FALSE;
  1264. _bi.pfnCallback(hDlg,
  1265. (uMsg == WM_HELP) ? DSBM_HELP:DSBM_CONTEXTMENU,
  1266. (uMsg == WM_HELP) ? lParam:(LPARAM)wParam,
  1267. _bi.lParam);
  1268. break;
  1269. }
  1270. default:
  1271. return FALSE; // not handled
  1272. }
  1273. return TRUE;
  1274. }
  1275. INT_PTR CALLBACK CBrowseDlg::s_DlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1276. {
  1277. CBrowseDlg *pbd = (CBrowseDlg *)GetWindowLongPtr(hDlg, DWLP_USER);
  1278. if (uMsg == WM_INITDIALOG)
  1279. {
  1280. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  1281. pbd = (CBrowseDlg *)lParam;
  1282. }
  1283. if (pbd != NULL)
  1284. return (INT_PTR)pbd->_DlgProc(hDlg, uMsg, wParam, lParam);
  1285. return FALSE;
  1286. }