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.

479 lines
13 KiB

  1. #include "shellprv.h"
  2. #pragma hdrstop
  3. #include "datautil.h"
  4. //
  5. // This function is a callback function from property sheet page extensions.
  6. //
  7. BOOL CALLBACK _AddPropSheetPage(HPROPSHEETPAGE hpage, LPARAM lParam)
  8. {
  9. PROPSHEETHEADER * ppsh = (PROPSHEETHEADER *)lParam;
  10. if (ppsh->nPages < MAX_FILE_PROP_PAGES)
  11. {
  12. ppsh->phpage[ppsh->nPages++] = hpage;
  13. return TRUE;
  14. }
  15. return FALSE;
  16. }
  17. //
  18. // This function enumerates all the property sheet page extensions for
  19. // specified class and let them add pages.
  20. //
  21. //
  22. int DCA_AppendClassSheetInfo(HDCA hdca, HKEY hkeyProgID, LPPROPSHEETHEADER ppsh, IDataObject *pdtobj)
  23. {
  24. int i, iStart = -1;
  25. for (i = 0; i < DCA_GetItemCount(hdca); i++)
  26. {
  27. IShellExtInit *psei;
  28. // These came from HKCR hence need to go through administrator approval
  29. if (DCA_ExtCreateInstance(hdca, i, &IID_IShellExtInit, &psei) == NOERROR)
  30. {
  31. IShellPropSheetExt *pspse;
  32. if (SUCCEEDED(psei->lpVtbl->Initialize(psei, NULL, pdtobj, hkeyProgID))
  33. && SUCCEEDED(psei->lpVtbl->QueryInterface(psei, &IID_IShellPropSheetExt, &pspse)))
  34. {
  35. int nPagesSave = ppsh->nPages;
  36. HRESULT hres = pspse->lpVtbl->AddPages(pspse, _AddPropSheetPage, (LPARAM)ppsh);
  37. if (SUCCEEDED(hres) && hres != S_OK)
  38. {
  39. // Some shell extensions get confused and return S_FALSE when
  40. // they didn't add anything, unaware that S_FALSE means "Please
  41. // take the page I added and make it the default". So ignore
  42. // the return value if it is out of range.
  43. DWORD nPagesAdded = ppsh->nPages - nPagesSave;
  44. DWORD nPageWanted = hres - 1;
  45. if (nPageWanted < nPagesAdded)
  46. iStart = nPagesSave + nPageWanted;
  47. }
  48. pspse->lpVtbl->Release(pspse);
  49. }
  50. psei->lpVtbl->Release(psei);
  51. }
  52. }
  53. return iStart;
  54. }
  55. HWND FindStubForPidlClass(LPCITEMIDLIST pidl, int iClass)
  56. {
  57. HWND hwnd;
  58. if (!pidl)
  59. return NULL;
  60. for (hwnd = FindWindow(c_szStubWindowClass, NULL); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
  61. {
  62. TCHAR szClass[80];
  63. // find stub windows only
  64. GetClassName(hwnd, szClass, ARRAYSIZE(szClass));
  65. if (lstrcmpi(szClass, c_szStubWindowClass) == 0)
  66. {
  67. HANDLE hClassPidl;
  68. DWORD dwProcId;
  69. DWORD_PTR dwResult;
  70. GetWindowThreadProcessId(hwnd, &dwProcId);
  71. // since a propsheet could be doing work & not pumping messages, use a timeout
  72. // we can almost just do this: hClassPidl = GetWindowLongPtr(hwnd, 0)
  73. if (!SendMessageTimeout(hwnd, STUBM_GETDATA, 0, 0, SMTO_BLOCK, 3000, &dwResult))
  74. continue;
  75. hClassPidl = (HANDLE)dwResult;
  76. if (hClassPidl)
  77. {
  78. LPBYTE lpb = (LPBYTE)SHLockShared(hClassPidl, dwProcId);
  79. if (lpb)
  80. {
  81. int iClassFound = *(int *)lpb;
  82. if (iClassFound == iClass &&
  83. ILIsEqual(pidl, (LPITEMIDLIST)(lpb + sizeof(int))) )
  84. {
  85. SHUnlockShared(lpb);
  86. return hwnd;
  87. }
  88. SHUnlockShared(lpb);
  89. }
  90. }
  91. }
  92. }
  93. return NULL;
  94. }
  95. HANDLE _StuffStubWindow(HWND hwnd, LPITEMIDLIST pidlT, int iClass)
  96. {
  97. DWORD dwProcId;
  98. HANDLE hSharedClassPidl;
  99. UINT uidlSize;
  100. uidlSize = ILGetSize(pidlT);
  101. GetWindowThreadProcessId(hwnd, &dwProcId);
  102. hSharedClassPidl = SHAllocShared(NULL, sizeof(int)+uidlSize, dwProcId);
  103. if (hSharedClassPidl)
  104. {
  105. LPBYTE lpb = SHLockShared(hSharedClassPidl, dwProcId);
  106. if (lpb)
  107. {
  108. *(int *)lpb = iClass;
  109. memcpy(lpb+sizeof(int),pidlT, uidlSize);
  110. SHUnlockShared(lpb);
  111. SendMessage(hwnd, STUBM_SETDATA, (WPARAM)hSharedClassPidl, 0);
  112. return hSharedClassPidl;
  113. }
  114. SHFreeShared(hSharedClassPidl, dwProcId);
  115. }
  116. return NULL;
  117. }
  118. //
  119. // Make sure we are the only stub window for this pidl/class.
  120. //
  121. // If so, saves information in the UNIQUESTUBINFO structure which keeps
  122. // track of the uniqueness key. After you are done, you must pass the
  123. // UNIQUESTUBINFO structure to FreeUniqueStub() to clean up the uniqueness
  124. // key and destroy the stub window. Returns TRUE.
  125. //
  126. // If a stub window already exists for this pidl/class, then sets focus
  127. // to the existing window that matches our uniqueness key and returns FALSE.
  128. //
  129. // In low memory conditions, plays it safe and declares the pidl/class
  130. // unique.
  131. //
  132. //
  133. // Example:
  134. //
  135. // UNIQUESTUBINFO usi;
  136. // if (EnsureUniqueStub(pidl, STUBCLASS_PROPSHEET, NULL, &usi)) {
  137. // DoStuff(usi.hwndStub, pidl);
  138. // FreeUniqueStub(&usi);
  139. // }
  140. //
  141. STDAPI_(BOOL)
  142. EnsureUniqueStub(LPITEMIDLIST pidl, int iClass, POINT *ppt, UNIQUESTUBINFO *pusi)
  143. {
  144. HWND hwndOther;
  145. ZeroMemory(pusi, sizeof(UNIQUESTUBINFO));
  146. hwndOther = FindStubForPidlClass(pidl, iClass);
  147. if (hwndOther)
  148. {
  149. SwitchToThisWindow(GetLastActivePopup(hwndOther), TRUE);
  150. return FALSE;
  151. }
  152. else
  153. { // Tag ourselves as the unique stub for this pidl/class
  154. pusi->hwndStub = _CreateStubWindow(ppt, NULL);
  155. // If no pidl, then nothing to tag *with*
  156. // If no stub window, then nothing to attach the tag *to*
  157. // But they are both still considered success.
  158. if (pusi->hwndStub && pidl)
  159. {
  160. SHFILEINFO sfi;
  161. pusi->hClassPidl = _StuffStubWindow(pusi->hwndStub, pidl, iClass);
  162. if (SHGetFileInfo((LPCTSTR)pidl, 0, &sfi, sizeof(sfi), SHGFI_ICON | SHGFI_PIDL)) {
  163. pusi->hicoStub = sfi.hIcon;
  164. // Cannot stuff the title because the window might belong to another process
  165. SendMessage(pusi->hwndStub, STUBM_SETICONTITLE, (WPARAM)pusi->hicoStub, 0);
  166. }
  167. }
  168. return TRUE;
  169. }
  170. }
  171. STDAPI_(void) FreeUniqueStub(UNIQUESTUBINFO *pusi)
  172. {
  173. if (pusi->hwndStub)
  174. DestroyWindow(pusi->hwndStub);
  175. if (pusi->hClassPidl)
  176. SHFreeShared(pusi->hClassPidl, GetCurrentProcessId());
  177. if (pusi->hicoStub)
  178. DestroyIcon(pusi->hicoStub);
  179. }
  180. BOOL _IsAnyDuplicatedKey(HKEY ahkeys[], UINT ckeys, HKEY hkey)
  181. {
  182. UINT ikey;
  183. for (ikey=0; ikey<ckeys; ikey++)
  184. {
  185. if (ahkeys[ikey]==hkey) {
  186. return TRUE;
  187. }
  188. }
  189. return FALSE;
  190. }
  191. STDAPI_(BOOL) SHOpenPropSheet(
  192. LPCTSTR pszCaption,
  193. HKEY ahkeys[],
  194. UINT ckeys,
  195. const CLSID * pclsidDef, OPTIONAL
  196. IDataObject *pdtobj,
  197. IShellBrowser * psb,
  198. LPCTSTR pStartPage) OPTIONAL
  199. {
  200. BOOL fSuccess = FALSE;
  201. BOOL fUnique;
  202. PROPSHEETHEADER psh;
  203. HPROPSHEETPAGE ahpage[MAX_FILE_PROP_PAGES];
  204. HWND hwndStub = NULL;
  205. STGMEDIUM medium;
  206. HDCA hdca = NULL;
  207. HICON hicoStuff = NULL;
  208. UNIQUESTUBINFO usi;
  209. ASSERT(IS_VALID_STRING_PTR(pszCaption, -1));
  210. ASSERT(NULL == pclsidDef || IS_VALID_READ_PTR(pclsidDef, CLSID));
  211. ASSERT(IS_VALID_CODE_PTR(pdtobj, DATAOBJECT));
  212. ASSERT(NULL == psb || IS_VALID_CODE_PTR(psb, IShellBrowser));
  213. ASSERT(NULL == pStartPage || IS_VALID_STRING_PTR(pStartPage, -1));
  214. // Create the stub window
  215. {
  216. POINT pt;
  217. POINT * ppt = NULL;
  218. LPITEMIDLIST pidl = NULL;
  219. if (SUCCEEDED(DataObj_GetOFFSETs(pdtobj, &pt)))
  220. ppt = &pt;
  221. if (DataObj_GetHIDA(pdtobj, &medium))
  222. {
  223. HIDA hida = medium.hGlobal;
  224. if (hida && (HIDA_GetCount(hida) == 1))
  225. {
  226. pidl = HIDA_ILClone(hida, 0);
  227. }
  228. HIDA_ReleaseStgMedium(NULL, &medium);
  229. }
  230. fUnique = EnsureUniqueStub(pidl, STUBCLASS_PROPSHEET, ppt, &usi);
  231. ILFree(pidl);
  232. }
  233. // If there's already a property sheet up for this guy, then our job is done
  234. if (!fUnique) {
  235. return TRUE;
  236. }
  237. psh.hwndParent = usi.hwndStub;
  238. psh.dwSize = sizeof(psh);
  239. psh.dwFlags = PSH_PROPTITLE;
  240. psh.hInstance = HINST_THISDLL;
  241. psh.pszCaption = pszCaption;
  242. psh.nPages = 0; // incremented in callback
  243. psh.nStartPage = 0; // set below if specified
  244. psh.phpage = ahpage;
  245. if (pStartPage)
  246. {
  247. psh.dwFlags |= PSH_USEPSTARTPAGE;
  248. psh.pStartPage = pStartPage;
  249. }
  250. hdca = DCA_Create();
  251. if (hdca)
  252. {
  253. UINT ikey;
  254. int nStartPage;
  255. //
  256. // Always add this default extention at the top, if any.
  257. //
  258. if (pclsidDef)
  259. {
  260. DCA_AddItem(hdca, pclsidDef);
  261. }
  262. for (ikey = 0; ikey < ckeys; ikey++)
  263. {
  264. if (ahkeys[ikey] && !_IsAnyDuplicatedKey(ahkeys, ikey, ahkeys[ikey]))
  265. {
  266. DCA_AddItemsFromKey(hdca, ahkeys[ikey], STRREG_SHEX_PROPSHEET);
  267. }
  268. }
  269. // Notes: ahkeys[ckeys-1] as hkeyProgID
  270. ASSERT(ckeys);
  271. nStartPage = DCA_AppendClassSheetInfo(hdca, ahkeys[ckeys-1], &psh, pdtobj);
  272. // If a shell extension requested to be the default and the caller didn't
  273. // specify a preferred initial page, then let the shell extension win.
  274. if (!pStartPage && nStartPage >= 0)
  275. psh.nStartPage = nStartPage;
  276. DCA_Destroy(hdca);
  277. }
  278. // Open the property sheet, only if we have some pages.
  279. if (psh.nPages > 0)
  280. {
  281. _try
  282. {
  283. if (PropertySheet(&psh) >= 0) // IDOK or IDCANCEL (< 0 is error)
  284. fSuccess = TRUE;
  285. }
  286. _except(UnhandledExceptionFilter(GetExceptionInformation()))
  287. {
  288. DebugMsg(DM_ERROR, TEXT("PRSHT: Fault in property sheet"));
  289. }
  290. }
  291. else
  292. {
  293. ShellMessageBox(HINST_THISDLL, NULL,
  294. MAKEINTRESOURCE(IDS_NOPAGE),
  295. MAKEINTRESOURCE(IDS_DESKTOP),
  296. MB_OK|MB_ICONHAND);
  297. }
  298. // clean up the stub window and data
  299. FreeUniqueStub(&usi);
  300. return fSuccess;
  301. }
  302. #ifdef UNICODE
  303. STDAPI_(BOOL) SHOpenPropSheetA(
  304. LPCSTR pszCaption,
  305. HKEY ahkeys[],
  306. UINT ckeys,
  307. const CLSID * pclsidDef,
  308. IDataObject *pdtobj,
  309. IShellBrowser * psb,
  310. LPCSTR pszStartPage) OPTIONAL
  311. {
  312. BOOL bRet = FALSE;
  313. if (IS_VALID_STRING_PTRA(pszCaption, MAX_PATH))
  314. {
  315. WCHAR wszCaption[MAX_PATH];
  316. WCHAR wszStartPage[MAX_PATH];
  317. SHAnsiToUnicode(pszCaption, wszCaption, SIZECHARS(wszCaption));
  318. if (pszStartPage)
  319. {
  320. ASSERT(IS_VALID_STRING_PTRA(pszStartPage, MAX_PATH));
  321. SHAnsiToUnicode(pszStartPage, wszStartPage, SIZECHARS(wszStartPage));
  322. pszStartPage = (LPCSTR)wszStartPage;
  323. }
  324. bRet = SHOpenPropSheet(wszCaption, ahkeys, ckeys, pclsidDef, pdtobj, psb, (LPCWSTR)pszStartPage);
  325. }
  326. return bRet;
  327. }
  328. #else
  329. STDAPI_(BOOL) SHOpenPropSheetW(
  330. LPCWSTR pszCaption,
  331. HKEY ahkeys[],
  332. UINT ckeys,
  333. const CLSID * pclsidDef,
  334. IDataObject *pdtobj,
  335. IShellBrowser * psb,
  336. LPCWSTR pszStartPage) OPTIONAL
  337. {
  338. BOOL bRet = FALSE;
  339. if (IS_VALID_STRING_PTRW(pszCaption, MAX_PATH))
  340. {
  341. char szCaption[MAX_PATH];
  342. char szStartPage[MAX_PATH];
  343. SHUnicodeToAnsi(pszCaption, szCaption, SIZECHARS(szCaption));
  344. if (pszStartPage)
  345. {
  346. ASSERT(IS_VALID_STRING_PTRW(pszStartPage, MAX_PATH));
  347. SHUnicodeToAnsi(pszStartPage, szStartPage, SIZECHARS(szStartPage));
  348. pszStartPage = (LPCWSTR)szStartPage;
  349. }
  350. bRet = SHOpenPropSheet(szCaption, ahkeys, ckeys, pclsidDef, pdtobj, psb, (LPCSTR)pszStartPage);
  351. }
  352. return bRet;
  353. }
  354. #endif // UNICODE
  355. //
  356. // Async version of SHFormatDrive - creates a separate thread to do the
  357. // format and returns immediately.
  358. //
  359. typedef struct {
  360. HWND hwnd;
  361. UINT drive;
  362. UINT fmtID;
  363. UINT options;
  364. } FORMATTHREADINFO;
  365. STDAPI_(DWORD) _FormatThreadProc(LPVOID lpParam)
  366. {
  367. FORMATTHREADINFO* pfi = (FORMATTHREADINFO*)lpParam;
  368. LPITEMIDLIST pidl;
  369. TCHAR szDrive[4];
  370. lstrcpy(szDrive, TEXT("A:\\"));
  371. ASSERT(pfi->drive < 26);
  372. szDrive[0] += (TCHAR)pfi->drive;
  373. pidl = ILCreateFromPath(szDrive);
  374. if (pidl)
  375. {
  376. UNIQUESTUBINFO usi;
  377. LPPOINT ppt = NULL;
  378. RECT rcWindow;
  379. if (pfi->hwnd)
  380. {
  381. GetWindowRect(pfi->hwnd, &rcWindow);
  382. ppt = (LPPOINT)&rcWindow;
  383. }
  384. if (EnsureUniqueStub(pidl, STUBCLASS_FORMAT, ppt, &usi))
  385. {
  386. SHFormatDrive(usi.hwndStub, pfi->drive, pfi->fmtID, pfi->options);
  387. FreeUniqueStub(&usi);
  388. }
  389. ILFree(pidl);
  390. }
  391. LocalFree(pfi);
  392. return 0;
  393. }
  394. STDAPI_(void) SHFormatDriveAsync(
  395. HWND hwnd,
  396. UINT drive,
  397. UINT fmtID,
  398. UINT options
  399. )
  400. {
  401. FORMATTHREADINFO* pfi = (FORMATTHREADINFO*)LocalAlloc(LPTR, sizeof(FORMATTHREADINFO));
  402. if (pfi)
  403. {
  404. pfi->hwnd = hwnd;
  405. pfi->drive = drive;
  406. pfi->fmtID = fmtID;
  407. pfi->options = options;
  408. SHCreateThread(_FormatThreadProc, pfi, CTF_INSIST | CTF_PROCESS_REF, NULL);
  409. }
  410. }