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.

532 lines
15 KiB

  1. #include "pch.h"
  2. #pragma hdrstop
  3. TCHAR const c_szClass[] = TEXT("Class");
  4. TCHAR const c_szClasses[] = TEXT("Classes");
  5. TCHAR const c_szAllContainers[] = TEXT("AllContainers");
  6. TCHAR const c_szAllObjects[] = TEXT("AllObjects");
  7. TCHAR const c_szDsPropertyUI[] = TEXT("PropertiesHandler");
  8. //
  9. // This function is supposed to ultimately shutdown COM
  10. // regardless of how many times CoInitialize(NULL) has
  11. // been called.
  12. //
  13. void ShutdownCOM()
  14. {
  15. for(;;)
  16. {
  17. //
  18. // Call CoUninitialze() twice
  19. //
  20. CoUninitialize();
  21. CoUninitialize();
  22. //
  23. // Call CoInitialize(NULL) to see whether this will be the first
  24. // COM initialization. S_OK means COM is initialized successfully,
  25. // S_FALSE means it has been initialized already.
  26. //
  27. HRESULT hr = CoInitialize(NULL);
  28. if (SUCCEEDED(hr))
  29. {
  30. // S_OK, S_FALSE case
  31. if (S_OK == hr)
  32. {
  33. CoUninitialize();
  34. break;
  35. }
  36. else
  37. {
  38. // The internal COM ref count
  39. // still hasn't reached zero
  40. continue;
  41. }
  42. }
  43. else
  44. {
  45. // RPC_E_CHANGED_MODE case
  46. if (RPC_E_CHANGED_MODE == hr)
  47. {
  48. continue;
  49. }
  50. else
  51. {
  52. // Some other failure
  53. // E_OUTOFMEMORY for example
  54. break;
  55. }
  56. }
  57. }
  58. }
  59. /*-----------------------------------------------------------------------------
  60. / Globals etc used for icon extraction
  61. /----------------------------------------------------------------------------*/
  62. /*-----------------------------------------------------------------------------
  63. / GetKeysForClass
  64. / ---------------
  65. / Given a class and the flags assocaited with that extract the keys that
  66. / represent it.
  67. /
  68. / In:
  69. / pObjectClass = class name to fetch keys for
  70. / fIsConatiner = object is a container
  71. / cKeys = number of keys to fetch
  72. / aKeys = array to be filled with keys
  73. /
  74. / Out:
  75. / HRESULT
  76. /----------------------------------------------------------------------------*/
  77. HRESULT GetKeysForClass(LPWSTR pObjectClass, BOOL fIsContainer, INT cKeys, HKEY* aKeys)
  78. {
  79. HRESULT hres;
  80. HKEY hkClasses = NULL;
  81. CLSID clsidBase;
  82. LPTSTR pMappedClass = NULL;
  83. USES_CONVERSION;
  84. TraceEnter(TRACE_UI, "GetKeysForClass");
  85. if (cKeys < UIKEY_MAX)
  86. ExitGracefully(hres, E_INVALIDARG, "cKeys < UIKEY_MAX");
  87. ZeroMemory(aKeys, SIZEOF(HKEY)*cKeys);
  88. hres = GetKeyForCLSID(CLSID_MicrosoftDS, c_szClasses, &hkClasses);
  89. FailGracefully(hres, "Failed to get Classes key from registry");
  90. //
  91. // Attempt to look up the class name in the registery under the namespaces "classes"
  92. // sub key. A class can also be mapped onto another one, if this happens then we have
  93. // a base CLASS key which we indirect via
  94. //
  95. if (pObjectClass)
  96. {
  97. if (ERROR_SUCCESS == RegOpenKeyEx(hkClasses, W2T(pObjectClass), NULL, KEY_READ, &aKeys[UIKEY_CLASS]))
  98. {
  99. if (SUCCEEDED(LocalQueryString(&pMappedClass, aKeys[UIKEY_CLASS], c_szClass)))
  100. {
  101. if (ERROR_SUCCESS != RegOpenKeyEx(hkClasses, pMappedClass, NULL, KEY_READ, &aKeys[UIKEY_BASECLASS]))
  102. {
  103. aKeys[UIKEY_BASECLASS] = NULL;
  104. }
  105. }
  106. }
  107. }
  108. //
  109. // Finally we need the root class (container or object)
  110. //
  111. hres = GetKeyForCLSID(CLSID_MicrosoftDS, (fIsContainer) ? c_szAllContainers:c_szAllObjects, &aKeys[UIKEY_ROOT]);
  112. FailGracefully(hres, "Failed to get root key");
  113. // hres = S_OK; // success
  114. exit_gracefully:
  115. LocalFreeString(&pMappedClass);
  116. if (hkClasses)
  117. RegCloseKey(hkClasses);
  118. TraceLeaveResult(hres);
  119. }
  120. /*-----------------------------------------------------------------------------
  121. / TidyKeys
  122. / --------
  123. / Given an array of keys release them and set them back to zero.
  124. /
  125. / In:
  126. / cKeys = number of keys in array
  127. / aKeys = keys to be released
  128. /
  129. / Out:
  130. / void
  131. /----------------------------------------------------------------------------*/
  132. void TidyKeys(INT cKeys, HKEY* aKeys)
  133. {
  134. TraceEnter(TRACE_UI, "TidyKeys");
  135. while (--cKeys >= 0)
  136. {
  137. if (aKeys[cKeys])
  138. {
  139. RegCloseKey(aKeys[cKeys]);
  140. aKeys[cKeys] = NULL; // key now closed
  141. }
  142. }
  143. TraceLeaveVoid();
  144. }
  145. /*-----------------------------------------------------------------------------
  146. / ShowObjectProperties
  147. / --------------------
  148. / Display properties for the given objects. This we do by invoking
  149. / the tab collector for the given IDataObject. First however we
  150. / look inside the object and see if we can find out what objects
  151. / were selected, having done that we can then find the HKEYs
  152. /
  153. / In:
  154. / hwndParent = parent dialog
  155. / pDataObject = data object that we must use
  156. /
  157. / Out:
  158. / HRESULT
  159. /----------------------------------------------------------------------------*/
  160. HRESULT _OverrideProperties(HWND hwndParent, LPDATAOBJECT pDataObject, HKEY* aKeys, INT cKeys, LPCWSTR pPrefix)
  161. {
  162. HRESULT hres;
  163. LPTSTR pPropertiesGUID = NULL;
  164. GUID guidProperties;
  165. IDsFolderProperties* pDsFolderProperties = NULL;
  166. TCHAR szBuffer[MAX_PATH];
  167. INT i;
  168. USES_CONVERSION;
  169. TraceEnter(TRACE_UI, "_OverrideProperties");
  170. // walk all the keys we were given, some will be NULL so ignore those
  171. StrCpy(szBuffer, W2CT(pPrefix));
  172. StrCat(szBuffer, c_szDsPropertyUI);
  173. Trace(TEXT("Prefixed property handler value: %s"), szBuffer);
  174. for (i = 0 ; i < cKeys ; i++)
  175. {
  176. LocalFreeString(&pPropertiesGUID);
  177. if (aKeys[i])
  178. {
  179. // if we have a handle attempt to get the GUID string from the registry
  180. // and convert it to a GUID so that we can call CoCreateInstance for the
  181. // IDsFolderProperites interface.
  182. if (FAILED(LocalQueryString(&pPropertiesGUID, aKeys[i], szBuffer)))
  183. {
  184. TraceMsg("Trying non-prefixed property handler");
  185. if (FAILED(LocalQueryString(&pPropertiesGUID, aKeys[i], c_szDsPropertyUI)))
  186. continue;
  187. }
  188. Trace(TEXT("GUID is: %s"), pPropertiesGUID);
  189. if (!GetGUIDFromString(pPropertiesGUID, &guidProperties))
  190. {
  191. TraceMsg("Failed to parse GUID");
  192. continue;
  193. }
  194. if (SUCCEEDED(CoCreateInstance(guidProperties, NULL, CLSCTX_INPROC_SERVER,
  195. IID_IDsFolderProperties, (LPVOID*)&pDsFolderProperties)))
  196. {
  197. TraceMsg("Calling IDsFolderProperties::ShowProperties");
  198. hres = pDsFolderProperties->ShowProperties(hwndParent, pDataObject);
  199. FailGracefully(hres, "Failed when calling ShowProperties");
  200. goto exit_gracefully;
  201. }
  202. }
  203. }
  204. hres = S_FALSE; // S_FALSE indicates that the caller should display properties
  205. exit_gracefully:
  206. LocalFreeString(&pPropertiesGUID);
  207. DoRelease(pDsFolderProperties);
  208. TraceLeaveResult(hres);
  209. }
  210. typedef struct
  211. {
  212. HWND hwndParent;
  213. IStream* pStream;
  214. } PROPERTIESTHREADDATA;
  215. DWORD WINAPI _ShowObjectPropertiesThread(LPVOID lpParam)
  216. {
  217. HRESULT hres;
  218. PROPERTIESTHREADDATA* pThreadData = (PROPERTIESTHREADDATA*)lpParam;
  219. IADsPathname* pPathname = NULL;
  220. IDataObject* pDataObject = NULL;
  221. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  222. STGMEDIUM mediumNames = { TYMED_NULL, NULL, NULL };
  223. STGMEDIUM mediumOptions = { TYMED_NULL, NULL, NULL };
  224. LPDSOBJECTNAMES pDsObjects;
  225. LPDSDISPLAYSPECOPTIONS pDispSpecOptions;
  226. HKEY hKeys[3] = { NULL, NULL, NULL };
  227. LPTSTR pTitle = NULL;
  228. LPCWSTR pPrefix = DS_PROP_SHELL_PREFIX;
  229. BSTR bstrName = NULL;
  230. INT i;
  231. USES_CONVERSION;
  232. TraceEnter(TRACE_UI, "ShowObjectPropertiesThread");
  233. CoInitialize(NULL);
  234. hres = CoGetInterfaceAndReleaseStream(pThreadData->pStream, IID_IDataObject, (void**)&pDataObject);
  235. FailGracefully(hres, "Failed to get data object from stream");
  236. // get the object names that we are showing properites on
  237. fmte.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSOBJECTNAMES);
  238. hres = pDataObject->GetData(&fmte, &mediumNames);
  239. FailGracefully(hres, "Failed to get selected objects");
  240. pDsObjects = (LPDSOBJECTNAMES)mediumNames.hGlobal;
  241. // get the attribute prefix, use this to key information from the registry
  242. fmte.cfFormat = (CLIPFORMAT)RegisterClipboardFormat(CFSTR_DSDISPLAYSPECOPTIONS);
  243. if (SUCCEEDED(pDataObject->GetData(&fmte, &mediumOptions)))
  244. {
  245. pDispSpecOptions = (LPDSDISPLAYSPECOPTIONS)mediumOptions.hGlobal;
  246. pPrefix = (LPCWSTR)ByteOffset(pDispSpecOptions, pDispSpecOptions->offsetAttribPrefix);
  247. }
  248. Trace(TEXT("Attribute prefix is: %s"), W2CT(pPrefix));
  249. if (pDsObjects && (pDsObjects->cItems >= 1))
  250. {
  251. LPWSTR pPath = (LPWSTR)ByteOffset(pDsObjects, pDsObjects->aObjects[0].offsetName);
  252. LPWSTR pObjectClass = (LPWSTR)ByteOffset(pDsObjects, pDsObjects->aObjects[0].offsetClass);
  253. BOOL fSelection = (pDsObjects->cItems > 1);
  254. Trace(TEXT("Items %d, 1st object: %s, 1st Class: %s"), pDsObjects->cItems, W2T(pPath), W2T(pObjectClass));
  255. // attempt to pick up the keys for the first element in the selection and get
  256. // the keys that map to that object
  257. hres = GetKeysForClass(pObjectClass,
  258. (pDsObjects->aObjects[0].dwFlags & DSOBJECT_ISCONTAINER),
  259. ARRAYSIZE(hKeys), hKeys);
  260. FailGracefully(hres, "Failed to get keys for class");
  261. hres = _OverrideProperties(pThreadData->hwndParent, pDataObject, hKeys, ARRAYSIZE(hKeys), pPrefix);
  262. FailGracefully(hres, "Failed when trying to call out for properties");
  263. // if the caller returns S_FALSE then we assume that they want us to display
  264. // the property pages for the given selection, so do so.
  265. if (hres == S_FALSE)
  266. {
  267. hres = CoCreateInstance(CLSID_Pathname, NULL, CLSCTX_INPROC_SERVER, IID_IADsPathname, (LPVOID*)&pPathname);
  268. FailGracefully(hres, "Failed to get the IADsPathname interface");
  269. hres = pPathname->Set(pPath, ADS_SETTYPE_FULL);
  270. FailGracefully(hres, "Failed to set the path of the name");
  271. pPathname->SetDisplayType(ADS_DISPLAY_VALUE_ONLY);
  272. pPathname->put_EscapedMode(ADS_ESCAPEDMODE_OFF_EX);
  273. hres = pPathname->Retrieve(ADS_FORMAT_LEAF, &bstrName);
  274. FailGracefully(hres, "Failed to get the leaf element");
  275. hres = FormatMsgResource(&pTitle,
  276. GLOBAL_HINSTANCE, (fSelection) ? IDS_LARGESEL:IDS_SINGLESEL,
  277. W2T(bstrName));
  278. FailGracefully(hres, "Failed to format dialog title");
  279. if (!SHOpenPropSheet(pTitle, hKeys, ARRAYSIZE(hKeys), NULL, pDataObject, NULL, NULL))
  280. ExitGracefully(hres, E_FAIL, "Failed to open property pages");
  281. }
  282. }
  283. hres = S_OK;
  284. exit_gracefully:
  285. ReleaseStgMedium(&mediumNames);
  286. ReleaseStgMedium(&mediumOptions);
  287. TidyKeys(ARRAYSIZE(hKeys), hKeys);
  288. LocalFreeString(&pTitle);
  289. SysFreeString(bstrName);
  290. DoRelease(pPathname);
  291. DoRelease(pDataObject);
  292. LocalFree(pThreadData);
  293. //
  294. // We need to shutdwn COM ultimately here as we don't
  295. // know how many times CoInitialize(NULL) has been called.
  296. // Otherwise COM is trying to shutdown itself inside compobj!DllMain,
  297. // while holding the loader lock which is causing a very bad deadlock.
  298. // For more information see bug #395293.
  299. //
  300. // However we need to brace this code as NT specific code because
  301. // otherwise it may cause problems with the Win9x DSUI client for
  302. // some weird reason.
  303. //
  304. ShutdownCOM();
  305. TraceLeave();
  306. DllRelease();
  307. ExitThread(0);
  308. return 0;
  309. }
  310. HRESULT ShowObjectProperties(HWND hwndParent, LPDATAOBJECT pDataObject)
  311. {
  312. HRESULT hres;
  313. PROPERTIESTHREADDATA* pThreadData;
  314. DWORD dwId;
  315. HANDLE hThread;
  316. TraceEnter(TRACE_UI, "ShowObjectProperties");
  317. // Allocate thread data for the new object we are launching
  318. pThreadData = (PROPERTIESTHREADDATA*)LocalAlloc(LPTR, SIZEOF(PROPERTIESTHREADDATA));
  319. TraceAssert(pThreadData);
  320. if (!pThreadData)
  321. ExitGracefully(hres, E_OUTOFMEMORY, "Failed to allocate thread data");
  322. // we have thread data lets fill it and spin the properties thread.
  323. pThreadData->hwndParent = hwndParent;
  324. hres = CoMarshalInterThreadInterfaceInStream(IID_IDataObject, pDataObject, &(pThreadData->pStream));
  325. FailGracefully(hres, "Failed to create marshaling object");
  326. DllAddRef();
  327. hThread = CreateThread(NULL, 0, _ShowObjectPropertiesThread, (LPVOID)pThreadData, 0, &dwId);
  328. TraceAssert(hThread);
  329. if (!hThread)
  330. {
  331. LocalFree(pThreadData);
  332. DllRelease();
  333. ExitGracefully(hres, E_UNEXPECTED, "Failed to kick off the thread");
  334. }
  335. CloseHandle(hThread);
  336. hres = S_OK;
  337. exit_gracefully:
  338. TraceLeaveResult(hres);
  339. }
  340. /*----------------------------------------------------------------------------
  341. / IDsFolderProperties
  342. /----------------------------------------------------------------------------*/
  343. class CDsFolderProperties : public IDsFolderProperties
  344. {
  345. private:
  346. LONG _cRef;
  347. public:
  348. CDsFolderProperties();
  349. ~CDsFolderProperties();
  350. // IUnknown
  351. STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
  352. STDMETHOD_(ULONG, AddRef)();
  353. STDMETHOD_(ULONG, Release)();
  354. // IDsFolderProperties
  355. STDMETHOD(ShowProperties)(HWND hwndParent, IDataObject *pDataObject);
  356. };
  357. CDsFolderProperties::CDsFolderProperties() :
  358. _cRef(1)
  359. {
  360. DllAddRef();
  361. }
  362. CDsFolderProperties::~CDsFolderProperties()
  363. {
  364. DllRelease();
  365. }
  366. STDAPI CDsFolderProperties_CreateInstance(IUnknown* punkOuter, IUnknown** ppunk, LPCOBJECTINFO poi)
  367. {
  368. CDsFolderProperties *pdfp = new CDsFolderProperties();
  369. if (!pdfp)
  370. return E_OUTOFMEMORY;
  371. HRESULT hres = pdfp->QueryInterface(IID_IUnknown, (void **)ppunk);
  372. pdfp->Release();
  373. return hres;
  374. }
  375. // IUnknown
  376. HRESULT CDsFolderProperties::QueryInterface(REFIID riid, void **ppv)
  377. {
  378. static const QITAB qit[] =
  379. {
  380. QITABENT(CDsFolderProperties, IDsFolderProperties), // IID_IDsFolderProperties
  381. {0, 0 },
  382. };
  383. return QISearch(this, qit, riid, ppv);
  384. }
  385. ULONG CDsFolderProperties::AddRef()
  386. {
  387. return InterlockedIncrement(&_cRef);
  388. }
  389. ULONG CDsFolderProperties::Release()
  390. {
  391. if (InterlockedDecrement(&_cRef))
  392. return _cRef;
  393. delete this;
  394. return 0;
  395. }
  396. // Show the property UI for the given selection
  397. STDMETHODIMP CDsFolderProperties::ShowProperties(HWND hwndParent, IDataObject *pDataObject)
  398. {
  399. HRESULT hres;
  400. TraceEnter(TRACE_UI, "CDsFolderProperties::ShowProperties");
  401. if (!pDataObject)
  402. ExitGracefully(hres, E_INVALIDARG, "No pDataObject given");
  403. CoInitialize(NULL); // ensure we have COM
  404. hres = ShowObjectProperties(hwndParent, pDataObject);
  405. FailGracefully(hres, "Failed to open property pages");
  406. // hres = S_OK; // success
  407. exit_gracefully:
  408. TraceLeaveResult(hres);
  409. }