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.

537 lines
16 KiB

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