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.

638 lines
18 KiB

  1. #include "pch.h"
  2. #include "stddef.h"
  3. #pragma hdrstop
  4. /*-----------------------------------------------------------------------------
  5. / Helper functions (exported)
  6. /----------------------------------------------------------------------------*/
  7. /*----------------------------------------------------------------------------
  8. / MergeMenu
  9. / ---------
  10. / Merge two menus together, taking the first popup menu and merging it into
  11. / the target. We use the caption from the pop-up menu as the caption
  12. / for the target.
  13. /
  14. / In:
  15. / hMenu = handle of menu to merge into
  16. / hMenuToInsert = handle of menu to get the popup from
  17. / iIndex = index to insert at
  18. /
  19. / Out:
  20. / -
  21. /----------------------------------------------------------------------------*/
  22. VOID MergeMenu(HMENU hMenu, HMENU hMenuToInsert, INT iIndex)
  23. {
  24. TCHAR szBuffer[MAX_PATH];
  25. HMENU hPopupMenu = NULL;
  26. TraceEnter(TRACE_HANDLER|TRACE_VIEW, "MergeMenu");
  27. hPopupMenu = CreatePopupMenu();
  28. if ( hPopupMenu )
  29. {
  30. GetMenuString(hMenuToInsert, 0, szBuffer, ARRAYSIZE(szBuffer), MF_BYPOSITION);
  31. InsertMenu(hMenu, iIndex, MF_BYPOSITION|MF_POPUP, (UINT_PTR)hPopupMenu, szBuffer);
  32. Shell_MergeMenus(hPopupMenu, GetSubMenu(hMenuToInsert, 0), 0x0, 0x0, 0x7fff, 0);
  33. }
  34. TraceLeave();
  35. }
  36. /*-----------------------------------------------------------------------------
  37. / BindToPath
  38. / ----------
  39. / Given a namespace path bind to it returning the shell object
  40. /
  41. / In:
  42. / pszPath -> path to bind to
  43. / riid = interface to request
  44. / ppvObject -> receives the object pointer
  45. /
  46. / Out:
  47. / HRESULT
  48. /----------------------------------------------------------------------------*/
  49. HRESULT BindToPath(LPWSTR pszPath, REFIID riid, LPVOID* ppObject)
  50. {
  51. HRESULT hres;
  52. IShellFolder* psfDesktop = NULL;
  53. LPITEMIDLIST pidl = NULL;
  54. TraceEnter(TRACE_VIEW, "BindToPath");
  55. hres = CoCreateInstance(CLSID_ShellDesktop, NULL, CLSCTX_INPROC_SERVER, IID_IShellFolder, (LPVOID*)&psfDesktop);
  56. FailGracefully(hres, "Failed to get IShellFolder for the desktop object");
  57. hres = psfDesktop->ParseDisplayName(NULL, NULL, pszPath, NULL, &pidl, NULL);
  58. FailGracefully(hres, "Failed when getting root path of DS");
  59. if ( ILIsEmpty(pidl) )
  60. {
  61. TraceMsg("PIDL is desktop, therefore just QIing for interface");
  62. hres = psfDesktop->QueryInterface(riid, ppObject);
  63. }
  64. else
  65. {
  66. TraceMsg("Binding to IDLIST via BindToObject");
  67. hres = psfDesktop->BindToObject(pidl, NULL, riid, ppObject);
  68. }
  69. exit_gracefully:
  70. if ( FAILED(hres) )
  71. *ppObject = NULL;
  72. DoRelease(psfDesktop);
  73. DoILFree(pidl);
  74. TraceLeaveResult(hres);
  75. }
  76. /*-----------------------------------------------------------------------------
  77. / GetColumnHandlerFromProperty
  78. / ----------------------------
  79. / Given a COLUMN structure allocate the property name appending the
  80. / CLSID of the handler if we have one.
  81. /
  82. / In:
  83. / pColumn -> column value to decode
  84. / pProperty -> property value parse
  85. /
  86. / Out:
  87. / HRESULT
  88. /----------------------------------------------------------------------------*/
  89. HRESULT GetColumnHandlerFromProperty(LPCOLUMN pColumn, LPWSTR pProperty)
  90. {
  91. HRESULT hres;
  92. LPWSTR pPropertyTemp;
  93. LPWSTR pColumnHandlerCLSID;
  94. USES_CONVERSION;
  95. TraceEnter(TRACE_VIEW, "GetColumnHandlerFromProperty");
  96. Trace(TEXT("pProperty is: %s"), W2T(pProperty));
  97. if ( !pProperty )
  98. pProperty = pColumn->pProperty;
  99. // if we find a ',' then we must parse the GUID as it may be a CLSID for a column handler.
  100. pColumnHandlerCLSID = wcschr(pProperty, L',');
  101. if ( pColumnHandlerCLSID )
  102. {
  103. // attempt to extract the CLSID form the property name
  104. *pColumnHandlerCLSID++ = L'\0'; // terminate the property name
  105. if ( GetGUIDFromStringW(pColumnHandlerCLSID, &pColumn->clsidColumnHandler) )
  106. {
  107. TraceGUID("CLSID for handler is:", pColumn->clsidColumnHandler);
  108. pColumn->fHasColumnHandler = TRUE;
  109. }
  110. else
  111. {
  112. TraceMsg("**** Failed to parse CLSID from property name ****");
  113. }
  114. // we truncated the string, so lets re-alloc the buffer with the
  115. // new string value.
  116. if ( SUCCEEDED(LocalAllocStringW(&pPropertyTemp, pProperty)) )
  117. {
  118. LocalFreeStringW(&pColumn->pProperty);
  119. pColumn->pProperty = pPropertyTemp;
  120. }
  121. Trace(TEXT("Property name is now: %s"), W2T(pColumn->pProperty));
  122. }
  123. else
  124. {
  125. // now CLSID, so just allocate the property string if we need to.
  126. if ( pColumn->pProperty != pProperty )
  127. {
  128. if ( SUCCEEDED(LocalAllocStringW(&pPropertyTemp, pProperty)) )
  129. {
  130. LocalFreeStringW(&pColumn->pProperty);
  131. pColumn->pProperty = pPropertyTemp;
  132. }
  133. }
  134. }
  135. TraceLeaveResult(S_OK);
  136. }
  137. /*-----------------------------------------------------------------------------
  138. / GetPropertyFromColumn
  139. / ---------------------
  140. / Given a COLUMN structure allocate the property name appending the
  141. / CLSID of the handler if we have one.
  142. /
  143. / In:
  144. / ppProperty -> receives a pointer to the property string
  145. / pColumn -> column value to decode
  146. /
  147. / Out:
  148. / HRESULT
  149. /----------------------------------------------------------------------------*/
  150. HRESULT GetPropertyFromColumn(LPWSTR* ppProperty, LPCOLUMN pColumn)
  151. {
  152. HRESULT hres;
  153. TCHAR szGUID[GUIDSTR_MAX+1];
  154. USES_CONVERSION;
  155. TraceEnter(TRACE_VIEW, "GetPropertyFromColumn");
  156. if ( !pColumn->fHasColumnHandler )
  157. {
  158. // just copy the property name
  159. hres = LocalAllocStringW(ppProperty, pColumn->pProperty);
  160. FailGracefully(hres, "Failed to allocate property");
  161. }
  162. else
  163. {
  164. // allocate a buffer and then place the property name and the
  165. // column handler CLSID.
  166. hres = LocalAllocStringLenW(ppProperty, lstrlenW(pColumn->pProperty)+GUIDSTR_MAX+1); // nb: +2 for ","
  167. FailGracefully(hres, "Failed to allocate buffer for property name");
  168. GetStringFromGUID(pColumn->clsidColumnHandler, szGUID, ARRAYSIZE(szGUID));
  169. StrCpyW(*ppProperty, pColumn->pProperty);
  170. StrCatW(*ppProperty, L",");
  171. StrCatW(*ppProperty, T2W(szGUID));
  172. }
  173. hres = S_OK;
  174. exit_gracefully:
  175. TraceLeaveResult(hres);
  176. }
  177. /*-----------------------------------------------------------------------------
  178. / FreeColumn / FreeColumnValue
  179. / ----------------------------
  180. / A column consists of the header and filter information including the underlying
  181. / property value.
  182. /
  183. / A COLUMNVALUE is the typed information for the column which must be freed
  184. / based the iPropertyType value.
  185. /
  186. / In:
  187. / pColumn -> LPCOLUMN structure to released
  188. / or
  189. / pColumnValue ->LPCOLUMNVALUE structure to be released
  190. /
  191. / Out:
  192. / -
  193. /----------------------------------------------------------------------------*/
  194. VOID FreeColumnValue(LPCOLUMNVALUE pColumnValue)
  195. {
  196. TraceEnter(TRACE_VIEW, "FreeColumnValue");
  197. switch ( pColumnValue->iPropertyType )
  198. {
  199. case PROPERTY_ISUNDEFINED:
  200. case PROPERTY_ISBOOL:
  201. case PROPERTY_ISNUMBER:
  202. break;
  203. case PROPERTY_ISUNKNOWN:
  204. case PROPERTY_ISSTRING:
  205. LocalFreeString(&pColumnValue->pszText);
  206. break;
  207. default:
  208. Trace(TEXT("iPropertyValue is %d"), pColumnValue->iPropertyType);
  209. TraceAssert(FALSE);
  210. break;
  211. }
  212. pColumnValue->iPropertyType = PROPERTY_ISUNDEFINED; // no value
  213. TraceLeave();
  214. }
  215. INT FreeColumnCB(LPVOID pItem, LPVOID pData)
  216. {
  217. FreeColumn((LPCOLUMN)pItem);
  218. return 1;
  219. }
  220. VOID FreeColumn(LPCOLUMN pColumn)
  221. {
  222. TraceEnter(TRACE_VIEW, "FreeQueryResult");
  223. if ( pColumn )
  224. {
  225. LocalFreeStringW(&pColumn->pProperty);
  226. LocalFreeString(&pColumn->pHeading);
  227. FreeColumnValue(&pColumn->filter);
  228. DoRelease(pColumn->pColumnHandler);
  229. }
  230. TraceLeave();
  231. }
  232. /*-----------------------------------------------------------------------------
  233. / FreeQueryResult
  234. / ---------------
  235. / Given a QUERYRESULT structure free the elements within it
  236. /
  237. / In:
  238. / pResult -> result blob to be released
  239. / cColumns = number of columns to be released
  240. /
  241. / Out:
  242. / -
  243. /----------------------------------------------------------------------------*/
  244. INT FreeQueryResultCB(LPVOID pItem, LPVOID pData)
  245. {
  246. FreeQueryResult((LPQUERYRESULT)pItem, PtrToUlong(pData));
  247. return 1;
  248. }
  249. VOID FreeQueryResult(LPQUERYRESULT pResult, INT cColumns)
  250. {
  251. INT i;
  252. TraceEnter(TRACE_VIEW, "FreeQueryResult");
  253. if ( pResult )
  254. {
  255. LocalFreeStringW(&pResult->pObjectClass);
  256. LocalFreeStringW(&pResult->pPath);
  257. for ( i = 0 ; i < cColumns ; i++ )
  258. FreeColumnValue(&pResult->aColumn[i]);
  259. }
  260. TraceLeave();
  261. }
  262. /*-----------------------------------------------------------------------------
  263. / PropertyIsFromAttribute
  264. / -----------------------
  265. / Get the property is value from the specified attribute.
  266. /
  267. / In:
  268. / pszAttributeName -> attribute name
  269. / pdds -> IDsDisplaySpecifier
  270. /
  271. / Out:
  272. / DWORD dwType
  273. /----------------------------------------------------------------------------*/
  274. DWORD PropertyIsFromAttribute(LPCWSTR pszAttributeName, IDsDisplaySpecifier *pdds)
  275. {
  276. DWORD dwResult = PROPERTY_ISUNKNOWN;
  277. USES_CONVERSION;
  278. TraceEnter(TRACE_CORE, "PropertyIsFromAttribute");
  279. Trace(TEXT("Fetching attribute type for: %s"), W2CT(pszAttributeName));
  280. switch ( pdds->GetAttributeADsType(pszAttributeName) )
  281. {
  282. case ADSTYPE_DN_STRING:
  283. case ADSTYPE_CASE_EXACT_STRING:
  284. case ADSTYPE_CASE_IGNORE_STRING:
  285. case ADSTYPE_PRINTABLE_STRING:
  286. case ADSTYPE_NUMERIC_STRING:
  287. TraceMsg("Property is a string");
  288. dwResult = PROPERTY_ISSTRING;
  289. break;
  290. case ADSTYPE_BOOLEAN:
  291. TraceMsg("Property is a BOOL");
  292. dwResult = PROPERTY_ISBOOL;
  293. break;
  294. case ADSTYPE_INTEGER:
  295. TraceMsg("Property is a number");
  296. dwResult = PROPERTY_ISNUMBER;
  297. break;
  298. default:
  299. TraceMsg("Property is UNKNOWN");
  300. break;
  301. }
  302. TraceLeaveValue(dwResult);
  303. }
  304. /*-----------------------------------------------------------------------------
  305. / MatchPattern
  306. / ------------
  307. / Given two strings, one a string the other a pattern match the two
  308. / using standard wildcarding "*" == any number of characters, "?" means
  309. / single character skip
  310. /
  311. / In:
  312. / pString = string to compare
  313. / pPattern = pattern to compare against
  314. /
  315. / Out:
  316. / HRESULT
  317. /----------------------------------------------------------------------------*/
  318. BOOL MatchPattern(LPTSTR pString, LPTSTR pPattern)
  319. {
  320. TCHAR c, p, l;
  321. for ( ;; )
  322. {
  323. switch (p = *pPattern++ )
  324. {
  325. case 0: // end of pattern
  326. return *pString ? FALSE : TRUE; // if end of pString TRUE
  327. case TEXT('*'):
  328. {
  329. while ( *pString )
  330. { // match zero or more char
  331. if ( MatchPattern(pString++, pPattern) )
  332. return TRUE;
  333. }
  334. return MatchPattern(pString, pPattern);
  335. }
  336. case TEXT('?'):
  337. {
  338. if (*pString++ == 0) // match any one char
  339. return FALSE; // not end of pString
  340. break;
  341. }
  342. default:
  343. {
  344. if ( *pString++ != p )
  345. return FALSE; // not a match
  346. break;
  347. }
  348. }
  349. }
  350. }
  351. /*-----------------------------------------------------------------------------
  352. / EnumClassAttrbutes
  353. / ------------------
  354. / This is a wrapper around the attribute enum functions exposed in
  355. / the IDsDisplaySpecifier interface.
  356. /
  357. / We read the attributes into a DPA, then sort them add in the
  358. / extra columns exposed from this UI.
  359. /
  360. / In:
  361. / pdds -> IDsDisplaySpecifier object
  362. / pszObjectClass = object class to enumerate
  363. / pcbEnum, lParam = enumeration callback
  364. /
  365. / Out:
  366. / HRESULT
  367. /----------------------------------------------------------------------------*/
  368. typedef struct
  369. {
  370. LPWSTR pszName;
  371. LPWSTR pszDisplayName;
  372. DWORD dwFlags;
  373. } CLASSATTRIBUTE, * LPCLASSATTRIBUTE;
  374. INT _FreeAttribute(LPCLASSATTRIBUTE pca)
  375. {
  376. LocalFreeStringW(&pca->pszName);
  377. LocalFreeStringW(&pca->pszDisplayName);
  378. LocalFree(pca);
  379. return 1;
  380. }
  381. INT _FreeAttributeCB(LPVOID pv1, LPVOID pv2)
  382. {
  383. return _FreeAttribute((LPCLASSATTRIBUTE)pv1);
  384. }
  385. HRESULT _AddAttribute(HDPA hdpa, LPCWSTR pszName, LPCWSTR pszDisplayName, DWORD dwFlags)
  386. {
  387. HRESULT hres;
  388. LPCLASSATTRIBUTE pca = NULL;
  389. USES_CONVERSION;
  390. TraceEnter(TRACE_CORE, "_AddAttribute");
  391. Trace(TEXT("Adding %s (%s)"), W2CT(pszDisplayName), W2CT(pszName));
  392. pca = (LPCLASSATTRIBUTE)LocalAlloc(LPTR, SIZEOF(CLASSATTRIBUTE));
  393. if ( !pca )
  394. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate CLASSATTRIBUTE");
  395. // pca->pszName = NULL;
  396. // pca->pszDisplayName = NULL;
  397. pca->dwFlags = dwFlags;
  398. hres = LocalAllocStringW(&pca->pszName, pszName);
  399. FailGracefully(hres, "Failed to copy the name");
  400. hres = LocalAllocStringW(&pca->pszDisplayName, pszDisplayName);
  401. FailGracefully(hres, "Failed to copy the name");
  402. if ( -1 == DPA_AppendPtr(hdpa, pca) )
  403. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to append the record to the DPA");
  404. hres = S_OK;
  405. exit_gracefully:
  406. if ( FAILED(hres) && pca )
  407. _FreeAttribute(pca);
  408. TraceLeaveResult(hres);
  409. }
  410. HRESULT _AddAttributeCB(LPARAM lParam, LPCWSTR pszName, LPCWSTR pszDisplayName, DWORD dwFlags)
  411. {
  412. return _AddAttribute((HDPA)lParam, pszName, pszDisplayName, dwFlags);
  413. }
  414. INT _CompareAttributeCB(LPVOID pv1, LPVOID pv2, LPARAM lParam)
  415. {
  416. LPCLASSATTRIBUTE pca1 = (LPCLASSATTRIBUTE)pv1;
  417. LPCLASSATTRIBUTE pca2 = (LPCLASSATTRIBUTE)pv2;
  418. return StrCmpIW(pca1->pszDisplayName, pca2->pszDisplayName);
  419. }
  420. HRESULT EnumClassAttributes(IDsDisplaySpecifier *pdds, LPCWSTR pszObjectClass, LPDSENUMATTRIBUTES pcbEnum, LPARAM lParam)
  421. {
  422. HRESULT hres;
  423. HDPA hdpaAttributes = NULL;
  424. WCHAR szBuffer[MAX_PATH];
  425. INT i;
  426. TraceEnter(TRACE_CORE, "EnumClassAttributes");
  427. hdpaAttributes = DPA_Create(16);
  428. if ( !hdpaAttributes )
  429. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate the DPA");
  430. //
  431. // add the stock properties for objectClass and ADsPath
  432. //
  433. LoadStringW(GLOBAL_HINSTANCE, IDS_OBJECTCLASS, szBuffer, ARRAYSIZE(szBuffer));
  434. hres = _AddAttribute(hdpaAttributes, c_szObjectClassCH, szBuffer, DSECAF_NOTLISTED);
  435. FailGracefully(hres, "Failed to add the ObjectClass default property");
  436. LoadStringW(GLOBAL_HINSTANCE, IDS_ADSPATH, szBuffer, ARRAYSIZE(szBuffer));
  437. hres = _AddAttribute(hdpaAttributes, c_szADsPathCH, szBuffer, DSECAF_NOTLISTED);
  438. FailGracefully(hres, "Failed to add the ObjectClass default property");
  439. //
  440. // now call the IDsDisplaySpecifier object to enumerate the properites correctly
  441. //
  442. TraceMsg("Calling IDsDisplaySpecifier::EnumClassAttributes");
  443. hres = pdds->EnumClassAttributes(pszObjectClass, _AddAttributeCB, (LPARAM)hdpaAttributes);
  444. FailGracefully(hres, "Failed to add the attributes");
  445. //
  446. // now sort and return them all to the caller via their callback funtion
  447. //
  448. Trace(TEXT("Sorting %d attributes, to return to the caller"), DPA_GetPtrCount(hdpaAttributes));
  449. DPA_Sort(hdpaAttributes, _CompareAttributeCB, NULL);
  450. for ( i = 0 ; i < DPA_GetPtrCount(hdpaAttributes) ; i++ )
  451. {
  452. LPCLASSATTRIBUTE pca = (LPCLASSATTRIBUTE)DPA_FastGetPtr(hdpaAttributes, i);
  453. TraceAssert(pca);
  454. hres = pcbEnum(lParam, pca->pszName, pca->pszDisplayName, pca->dwFlags);
  455. FailGracefully(hres, "Failed in cb to original caller");
  456. }
  457. hres = S_OK;
  458. exit_gracefully:
  459. if ( hdpaAttributes )
  460. DPA_DestroyCallback(hdpaAttributes, _FreeAttributeCB, NULL);
  461. TraceLeaveResult(hres);
  462. }
  463. /*-----------------------------------------------------------------------------
  464. / GetFriendlyAttributeName
  465. / ------------------------
  466. / Trim the column handler information if needed, and call the
  467. / friendly attribute name functions.
  468. /
  469. / In:
  470. / pdds -> IDsDisplaySpecifier object
  471. / pszObjectClass, pszAttributeName => attribute info to look up
  472. / pszBuffer, chh => return buffer
  473. /
  474. / Out:
  475. / HRESULT
  476. /----------------------------------------------------------------------------*/
  477. HRESULT GetFriendlyAttributeName(IDsDisplaySpecifier *pdds, LPCWSTR pszObjectClass, LPCWSTR pszAttributeName, LPWSTR pszBuffer, UINT cch)
  478. {
  479. HRESULT hres = S_OK;
  480. WCHAR szAttributeName[MAX_PATH];
  481. USES_CONVERSION;
  482. TraceEnter(TRACE_CORE, "GetFriendlyAttributeName");
  483. //
  484. // trim off the attribute suffix if we have one (eg: the GUID for the column handler)
  485. //
  486. if ( wcschr(pszAttributeName, L',') )
  487. {
  488. TraceMsg("Has column handler information");
  489. StrCpyW(szAttributeName, pszAttributeName);
  490. LPWSTR pszSeperator = wcschr(szAttributeName, L',');
  491. *pszSeperator = L'\0';
  492. pszAttributeName = szAttributeName;
  493. }
  494. //
  495. // pick off some special cases before passing onto the COM object to process
  496. //
  497. Trace(TEXT("Looking up name for: %s"), W2CT(pszAttributeName));
  498. if ( !StrCmpIW(pszAttributeName, c_szADsPath) )
  499. {
  500. LoadStringW(GLOBAL_HINSTANCE, IDS_ADSPATH, pszBuffer, cch);
  501. }
  502. else if ( !StrCmpIW(pszAttributeName, c_szObjectClass) )
  503. {
  504. LoadStringW(GLOBAL_HINSTANCE, IDS_OBJECTCLASS, pszBuffer, cch);
  505. }
  506. else
  507. {
  508. hres = pdds->GetFriendlyAttributeName(pszObjectClass, pszAttributeName, pszBuffer, cch);
  509. }
  510. TraceLeaveResult(hres);
  511. }