Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1543 lines
48 KiB

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