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.

977 lines
29 KiB

  1. #include "priv.h"
  2. #include "hnfblock.h"
  3. #include <trayp.h>
  4. #include "desktop.h"
  5. #include "shbrows2.h"
  6. #include "resource.h"
  7. #include "onetree.h"
  8. #include "apithk.h"
  9. #include <regitemp.h>
  10. #include "mluisupp.h"
  11. //forward declaration of private function
  12. BOOL _private_ParseField(LPCTSTR pszData, int n, LPTSTR szBuf, int iBufLen);
  13. BOOL _RootsEqual(HANDLE hCR, DWORD dwProcId, LPCITEMIDLIST pidlRoot)
  14. {
  15. BOOL bSame = FALSE;
  16. if (hCR)
  17. {
  18. LPITEMIDLIST pidl = (LPITEMIDLIST)SHLockShared(hCR, dwProcId);
  19. if (pidl)
  20. {
  21. bSame = ILIsEqualRoot(pidlRoot, pidl);
  22. SHUnlockShared(pidl);
  23. }
  24. }
  25. return bSame;
  26. }
  27. // NOTE: this export is new to IE5, so it can move to browseui
  28. // along with the rest of this proxy desktop code
  29. BOOL SHOnCWMCommandLine(LPARAM lParam)
  30. {
  31. HNFBLOCK hnf = (HNFBLOCK)lParam;
  32. IETHREADPARAM *piei = ConvertHNFBLOCKtoNFI(hnf);
  33. if (piei)
  34. return SHOpenFolderWindow(piei);
  35. // bad params passed, normal failure case
  36. return FALSE;
  37. }
  38. //---------------------------------------------------------------------------
  39. // This proxy desktop window procedure is used when we are run and we
  40. // are not the shell. We are a hidden window which will simply respond
  41. // to messages like the ones that create threads for folder windows.
  42. // This window procedure will close after all of the open windows
  43. // associated with it go away.
  44. class CProxyDesktop
  45. {
  46. private:
  47. static LRESULT CALLBACK ProxyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam);
  48. friend CProxyDesktop *CreateProxyDesktop(IETHREADPARAM *piei);
  49. friend BOOL SHCreateFromDesktop(PNEWFOLDERINFO pfi);
  50. CProxyDesktop() {};
  51. ~CProxyDesktop();
  52. HWND _hwnd;
  53. LPITEMIDLIST _pidlRoot;
  54. };
  55. CProxyDesktop::~CProxyDesktop()
  56. {
  57. ILFree(_pidlRoot);
  58. }
  59. LRESULT CALLBACK CProxyDesktop::ProxyWndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)
  60. {
  61. CProxyDesktop *pproxy = (CProxyDesktop *)GetWindowPtr0(hwnd);
  62. switch (msg)
  63. {
  64. case WM_CREATE:
  65. pproxy = (CProxyDesktop *)((CREATESTRUCT *)lParam)->lpCreateParams;
  66. SetWindowPtr0(hwnd, pproxy);
  67. pproxy->_hwnd = hwnd;
  68. return 0; // success
  69. case WM_DESTROY:
  70. if (pproxy)
  71. pproxy->_hwnd = NULL;
  72. return 0;
  73. case CWM_COMMANDLINE:
  74. SHOnCWMCommandLine(lParam);
  75. break;
  76. case CWM_COMPAREROOT:
  77. return _RootsEqual((HANDLE)lParam, (DWORD)wParam, pproxy->_pidlRoot);
  78. default:
  79. return DefWindowProcWrap(hwnd, msg, wParam, lParam);
  80. }
  81. return 0;
  82. }
  83. CProxyDesktop *CreateProxyDesktop(IETHREADPARAM *piei)
  84. {
  85. CProxyDesktop *pproxy = new CProxyDesktop();
  86. if (pproxy)
  87. {
  88. WNDCLASS wc = {0};
  89. wc.lpfnWndProc = CProxyDesktop::ProxyWndProc;
  90. wc.cbWndExtra = SIZEOF(CProxyDesktop *);
  91. wc.hInstance = HINST_THISDLL;
  92. wc.hbrBackground = (HBRUSH)(COLOR_DESKTOP + 1);
  93. wc.lpszClassName = DESKTOPPROXYCLASS;
  94. SHRegisterClass(&wc);
  95. if (CreateWindowEx(WS_EX_TOOLWINDOW, DESKTOPPROXYCLASS, DESKTOPPROXYCLASS,
  96. WS_POPUP, 0, 0, 0, 0, NULL, NULL, HINST_THISDLL, pproxy))
  97. {
  98. if (ILIsRooted(piei->pidl))
  99. {
  100. pproxy->_pidlRoot = ILCloneFirst(piei->pidl);
  101. if (pproxy->_pidlRoot == NULL)
  102. {
  103. DestroyWindow(pproxy->_hwnd);
  104. pproxy = NULL;
  105. }
  106. }
  107. }
  108. else
  109. {
  110. delete pproxy;
  111. pproxy = NULL;
  112. }
  113. }
  114. return pproxy;
  115. }
  116. // REVIEW: maybe just check (hwnd == GetShellWindow())
  117. STDAPI_(BOOL) IsDesktopWindow(HWND hwnd)
  118. {
  119. TCHAR szName[80];
  120. GetClassName(hwnd, szName, ARRAYSIZE(szName));
  121. if (!lstrcmp(szName, DESKTOPCLASS))
  122. {
  123. GetWindowText(hwnd, szName, ARRAYSIZE(szName));
  124. return !lstrcmp(szName, PROGMAN);
  125. }
  126. return FALSE;
  127. }
  128. typedef struct
  129. {
  130. HWND hwndDesktop;
  131. HANDLE hCR;
  132. DWORD dwProcId;
  133. HWND hwndResult;
  134. } FRDSTRUCT;
  135. BOOL CALLBACK FindRootEnumProc(HWND hwnd, LPARAM lParam)
  136. {
  137. FRDSTRUCT *pfrds = (FRDSTRUCT *)lParam;
  138. TCHAR szClassName[40];
  139. GetClassName(hwnd, szClassName, ARRAYSIZE(szClassName));
  140. if (lstrcmpi(szClassName, DESKTOPPROXYCLASS) == 0)
  141. {
  142. ASSERT(hwnd != pfrds->hwndDesktop);
  143. if (SendMessage(hwnd, CWM_COMPAREROOT, (WPARAM)pfrds->dwProcId, (LPARAM)pfrds->hCR))
  144. {
  145. // Found it, so stop enumerating
  146. pfrds->hwndResult = hwnd;
  147. return FALSE;
  148. }
  149. }
  150. return TRUE;
  151. }
  152. BOOL RunSeparateDesktop()
  153. {
  154. DWORD bSeparate = FALSE;
  155. if (SHRestricted(REST_SEPARATEDESKTOPPROCESS))
  156. bSeparate = TRUE;
  157. else
  158. {
  159. SHELLSTATE ss;
  160. SHGetSetSettings(&ss, SSF_SEPPROCESS, FALSE);
  161. bSeparate = ss.fSepProcess;
  162. if (!bSeparate)
  163. {
  164. DWORD cbData = SIZEOF(bSeparate);
  165. SHGetValue(HKEY_CURRENT_USER, REGSTR_PATH_EXPLORER, TEXT("DesktopProcess"), NULL, &bSeparate, &cbData);
  166. }
  167. }
  168. return bSeparate;
  169. }
  170. // if we need to force some legacy rootet explorers into their own process, implement this.
  171. //#define _RootRunSeparateProcess(pidlRoot) ILIsRooted(pidlRoot) OLD BEHAVIOR
  172. #define _RootRunSeparateProcess(pidlRoot) FALSE
  173. HWND FindRootedDesktop(LPCITEMIDLIST pidlRoot)
  174. {
  175. HWND hwndDesktop = GetShellWindow(); // This is the "normal" desktop
  176. if (!RunSeparateDesktop() && !_RootRunSeparateProcess(pidlRoot) && hwndDesktop)
  177. {
  178. ASSERT(IsDesktopWindow(hwndDesktop));
  179. return hwndDesktop;
  180. }
  181. FRDSTRUCT frds;
  182. frds.hwndDesktop = hwndDesktop;
  183. frds.hwndResult = NULL; // Initalize to no matching rooted expl
  184. frds.dwProcId = GetCurrentProcessId();
  185. frds.hCR = SHAllocShared(pidlRoot, ILGetSize(pidlRoot), frds.dwProcId);
  186. if (frds.hCR)
  187. {
  188. EnumWindows(FindRootEnumProc, (LPARAM)&frds);
  189. SHFreeShared(frds.hCR, frds.dwProcId);
  190. }
  191. return frds.hwndResult;
  192. }
  193. UINT _GetProcessHotkey(void)
  194. {
  195. STARTUPINFO si = {SIZEOF(si)};
  196. GetStartupInfo(&si);
  197. return (UINT)(DWORD_PTR)si.hStdInput;
  198. }
  199. void FolderInfoToIEThreadParam(PNEWFOLDERINFO pfi, IETHREADPARAM *piei)
  200. {
  201. piei->uFlags = pfi->uFlags;
  202. piei->nCmdShow = pfi->nShow;
  203. piei->wHotkey = _GetProcessHotkey();
  204. ASSERT(pfi->pszRoot == NULL); // explorer always converts to a PIDL for us
  205. // we no longer support rooted explorers this way
  206. // it should have been filtered out above us.
  207. ASSERT(!pfi->pidlRoot);
  208. ASSERT(!(pfi->uFlags & (COF_ROOTCLASS | COF_NEWROOT)));
  209. ASSERT(IsEqualGUID(pfi->clsid, CLSID_NULL));
  210. if (pfi->pidl)
  211. {
  212. piei->pidl = ILClone(pfi->pidl);
  213. }
  214. // COF_PARSEPATH means that we should defer the parsing of the pszPath
  215. else if (!(pfi->uFlags & COF_PARSEPATH) && pfi->pszPath && pfi->pszPath[0])
  216. {
  217. // maybe should use IECreateFromPath??
  218. // or maybe we should parse relative to the root??
  219. piei->pidl = ILCreateFromPathA(pfi->pszPath);
  220. }
  221. }
  222. // IE4 Integrated delay loads CreateFromDesktop from SHDOCVW.DLL
  223. // So we need to keep this function here. Forward to the correct
  224. // implementation in SHELL32 (if integrated) or SHDOC41 (if not)
  225. BOOL SHCreateFromDesktop(PNEWFOLDERINFO pfi)
  226. {
  227. IETHREADPARAM *piei = SHCreateIETHREADPARAM(NULL, 0, NULL, NULL);
  228. if (piei)
  229. {
  230. // ASSUMING UNICODE COMPILE!
  231. LPCTSTR pszPath = NULL;
  232. HWND hwndDesktop;
  233. if (pfi->uFlags & COF_PARSEPATH)
  234. {
  235. ASSERT(!pfi->pidl);
  236. pszPath = (LPCTSTR) pfi->pszPath;
  237. }
  238. FolderInfoToIEThreadParam(pfi, piei);
  239. if (pfi->uFlags & COF_SEPARATEPROCESS)
  240. {
  241. hwndDesktop = NULL; // Assume no desktop process exists
  242. }
  243. else
  244. {
  245. hwndDesktop = FindRootedDesktop(piei->pidl);
  246. }
  247. if (hwndDesktop)
  248. {
  249. DWORD dwProcId;
  250. DWORD dwThreadId = GetWindowThreadProcessId(hwndDesktop, &dwProcId);
  251. AllowSetForegroundWindow(dwProcId);
  252. HNFBLOCK hBlock = ConvertNFItoHNFBLOCK(piei, pszPath, dwProcId);
  253. if (hBlock)
  254. {
  255. PostMessage(hwndDesktop, CWM_COMMANDLINE, 0, (LPARAM)hBlock);
  256. HANDLE hExplorer = OpenProcess( PROCESS_QUERY_INFORMATION, FALSE, dwProcId );
  257. if ( hExplorer )
  258. {
  259. // wait for input idle 10 seconds.
  260. WaitForInputIdle( hExplorer, 10000 );
  261. CloseHandle( hExplorer );
  262. }
  263. }
  264. }
  265. else
  266. {
  267. HRESULT hrInit = SHCoInitialize();
  268. CProxyDesktop *pproxy = CreateProxyDesktop(piei);
  269. if (pproxy)
  270. {
  271. // CRefThread controls this processes reference count. browser windows use this
  272. // to keep this process (window) around and this also lets thrid parties hold
  273. // references to our process, MSN uses this for example
  274. LONG cRefMsgLoop;
  275. IUnknown *punkRefMsgLoop;
  276. if (SUCCEEDED(SHCreateThreadRef(&cRefMsgLoop, &punkRefMsgLoop)))
  277. {
  278. SHSetInstanceExplorer(punkRefMsgLoop);
  279. // we needed to wait for this for the CoInit()
  280. if (pszPath)
  281. piei->pidl = ILCreateFromPath(pszPath);
  282. SHOpenFolderWindow(piei);
  283. piei = NULL; // OpenFolderWindow() takes ownership of this
  284. punkRefMsgLoop->Release(); // we now depend on the browser window to keep our msg loop
  285. }
  286. MSG msg;
  287. while (GetMessage(&msg, NULL, 0, 0))
  288. {
  289. if (cRefMsgLoop == 0)
  290. break; // no more refs on this thread, done
  291. TranslateMessage(&msg);
  292. DispatchMessage(&msg);
  293. }
  294. delete pproxy;
  295. }
  296. SHCoUninitialize(hrInit);
  297. }
  298. if (piei)
  299. SHDestroyIETHREADPARAM(piei);
  300. }
  301. return TRUE; // no one pays attention to this
  302. }
  303. HNFBLOCK ConvertNFItoHNFBLOCK(IETHREADPARAM* pInfo, LPCTSTR pszPath, DWORD dwProcId)
  304. {
  305. UINT uSize;
  306. UINT uPidl;
  307. UINT uPidlSelect;
  308. UINT uPidlRoot;
  309. UINT upszPath;
  310. PNEWFOLDERBLOCK pnfb;
  311. LPBYTE lpb;
  312. HNFBLOCK hBlock;
  313. LPVOID pidlRootOrMonitor = NULL; // pidlRoot or &hMonitor
  314. uSize = SIZEOF(NEWFOLDERBLOCK);
  315. if (pInfo->pidl)
  316. {
  317. uPidl = ILGetSize(pInfo->pidl);
  318. uSize += uPidl;
  319. }
  320. if (pInfo->pidlSelect)
  321. {
  322. uPidlSelect = ILGetSize(pInfo->pidlSelect);
  323. uSize += uPidlSelect;
  324. }
  325. if (pInfo->uFlags & COF_HASHMONITOR)
  326. {
  327. pidlRootOrMonitor = &pInfo->pidlRoot;
  328. uPidlRoot = sizeof(HMONITOR);
  329. uSize += uPidlRoot;
  330. }
  331. else if (pInfo->pidlRoot)
  332. {
  333. pidlRootOrMonitor = pInfo->pidlRoot;
  334. uPidlRoot = ILGetSize(pInfo->pidlRoot);
  335. uSize += uPidlRoot;
  336. }
  337. if (pszPath) {
  338. upszPath = CbFromCch(lstrlen(pszPath) + 1);
  339. uSize += upszPath;
  340. }
  341. hBlock = (HNFBLOCK)SHAllocShared(NULL, uSize, dwProcId);
  342. if (hBlock == NULL)
  343. return NULL;
  344. pnfb = (PNEWFOLDERBLOCK)SHLockShared(hBlock, dwProcId);
  345. if (pnfb == NULL)
  346. {
  347. SHFreeShared(hBlock, dwProcId);
  348. return NULL;
  349. }
  350. pnfb->dwSize = uSize;
  351. pnfb->uFlags = pInfo->uFlags;
  352. pnfb->nShow = pInfo->nCmdShow;
  353. pnfb->dwHwndCaller= PtrToInt(pInfo->hwndCaller);
  354. pnfb->dwHotKey = pInfo->wHotkey;
  355. pnfb->clsid = pInfo->clsid;
  356. pnfb->clsidInProc = pInfo->clsidInProc;
  357. pnfb->oidl = 0;
  358. pnfb->oidlSelect = 0;
  359. pnfb->oidlRoot = 0;
  360. pnfb->opszPath = 0;
  361. lpb = (LPBYTE)(pnfb+1); // Point just past the structure
  362. if (pInfo->pidl)
  363. {
  364. memcpy(lpb,pInfo->pidl,uPidl);
  365. pnfb->oidl = (int)(lpb-(LPBYTE)pnfb);
  366. lpb += uPidl;
  367. }
  368. if (pInfo->pidlSelect)
  369. {
  370. memcpy(lpb,pInfo->pidlSelect,uPidlSelect);
  371. pnfb->oidlSelect = (int)(lpb-(LPBYTE)pnfb);
  372. lpb += uPidlSelect;
  373. }
  374. if (pidlRootOrMonitor)
  375. {
  376. memcpy(lpb, pidlRootOrMonitor, uPidlRoot);
  377. pnfb->oidlRoot = (int)(lpb-(LPBYTE)pnfb);
  378. lpb += uPidlRoot;
  379. }
  380. if (pszPath)
  381. {
  382. memcpy(lpb, pszPath, upszPath);
  383. pnfb->opszPath = (int)(lpb-(LPBYTE)pnfb);
  384. lpb += upszPath;
  385. }
  386. SHUnlockShared(pnfb);
  387. return hBlock;
  388. }
  389. IETHREADPARAM* ConvertHNFBLOCKtoNFI(HNFBLOCK hBlock)
  390. {
  391. BOOL fFailure = FALSE;
  392. IETHREADPARAM* piei = NULL;
  393. if (hBlock)
  394. {
  395. DWORD dwProcId = GetCurrentProcessId();
  396. PNEWFOLDERBLOCK pnfb = (PNEWFOLDERBLOCK)SHLockShared(hBlock, dwProcId);
  397. if (pnfb)
  398. {
  399. if (pnfb->dwSize >= SIZEOF(NEWFOLDERBLOCK))
  400. {
  401. piei = SHCreateIETHREADPARAM(NULL, pnfb->nShow, NULL, NULL);
  402. if (piei)
  403. {
  404. LPITEMIDLIST pidl = NULL;
  405. piei->uFlags = pnfb->uFlags;
  406. piei->hwndCaller = IntToPtr_(HWND, pnfb->dwHwndCaller);
  407. piei->wHotkey = pnfb->dwHotKey;
  408. piei->clsid = pnfb->clsid;
  409. piei->clsidInProc = pnfb->clsidInProc;
  410. if (pnfb->oidlSelect)
  411. piei->pidlSelect = ILClone((LPITEMIDLIST)((LPBYTE)pnfb+pnfb->oidlSelect));
  412. if (pnfb->oidlRoot)
  413. {
  414. LPITEMIDLIST pidlRoot = (LPITEMIDLIST)((LPBYTE)pnfb+pnfb->oidlRoot);
  415. if (pnfb->uFlags & COF_HASHMONITOR)
  416. {
  417. piei->pidlRoot = (LPITEMIDLIST)*(UNALIGNED HMONITOR *)pidlRoot;
  418. }
  419. else
  420. {
  421. piei->pidlRoot = ILClone(pidl);
  422. }
  423. }
  424. if (pnfb->oidl)
  425. pidl = ILClone((LPITEMIDLIST)((LPBYTE)pnfb+pnfb->oidl));
  426. if (pidl)
  427. {
  428. piei->pidl = pidl;
  429. }
  430. // we pass this string through because msn fails the cocreateinstane of
  431. // their desktop if another one is up and running, so we can't convert
  432. // this from path to pidl except in the current process context
  433. if (pnfb->opszPath)
  434. {
  435. LPTSTR pszPath = (LPTSTR)((LPBYTE)pnfb+pnfb->opszPath);
  436. HRESULT hr = E_FAIL;
  437. if (ILIsRooted(pidl))
  438. {
  439. // let the root handle the parsing.
  440. IShellFolder *psf;
  441. if (SUCCEEDED(IEBindToObject(pidl, &psf)))
  442. {
  443. hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, pszPath, NULL, &(piei->pidl), NULL);
  444. psf->Release();
  445. }
  446. }
  447. else
  448. IECreateFromPath(pszPath, &(piei->pidl));
  449. // APP COMPAT: these two specific return result codes are the two we ignored for win95.
  450. // APP COMPAT: MSN 1.3 Classic accidentally on purpose returns one of these...
  451. if ( !piei->pidl )
  452. {
  453. // failed, report the error to the user ... (will only fail for paths)
  454. ASSERT( !PathIsURL( pszPath))
  455. if (! (piei->uFlags & COF_NOTUSERDRIVEN) && ( hr != E_OUTOFMEMORY ) && ( hr != HRESULT_FROM_WIN32( ERROR_CANCELLED )))
  456. {
  457. MLShellMessageBox(
  458. NULL,
  459. MAKEINTRESOURCE( IDS_NOTADIR ),
  460. MAKEINTRESOURCE( IDS_CABINET ),
  461. MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND,
  462. pszPath);
  463. }
  464. fFailure = TRUE;
  465. }
  466. }
  467. }
  468. }
  469. SHUnlockShared(pnfb);
  470. }
  471. SHFreeShared(hBlock, dwProcId);
  472. }
  473. // if we really failed somewhere, return NULL
  474. if (fFailure)
  475. {
  476. SHDestroyIETHREADPARAM(piei);
  477. piei = NULL;
  478. }
  479. return piei;
  480. }
  481. // Check the registry for a shell root under this CLSID.
  482. BOOL GetRootFromRootClass(LPCTSTR pszGUID, LPTSTR pszPath, int cchPath)
  483. {
  484. TCHAR szClass[MAX_PATH];
  485. wnsprintf(szClass, ARRAYSIZE(szClass), TEXT("CLSID\\%s\\ShellExplorerRoot"), pszGUID);
  486. DWORD cbPath = cchPath * sizeof(TCHAR);
  487. return SHGetValueGoodBoot(HKEY_CLASSES_ROOT, szClass, NULL, NULL, (BYTE *)pszPath, &cbPath) == ERROR_SUCCESS;
  488. }
  489. // format is ":<hMem>:<hProcess>"
  490. LPITEMIDLIST IDListFromCmdLine(LPCTSTR pszCmdLine, int i)
  491. {
  492. LPITEMIDLIST pidl = NULL;
  493. TCHAR szField[80];
  494. if (_private_ParseField(pszCmdLine, i, szField, ARRAYSIZE(szField)) && szField[0] == TEXT(':'))
  495. {
  496. // Convert the string of format ":<hmem>:<hprocess>" into a pointer
  497. HANDLE hMem = LongToHandle(StrToLong(szField + 1));
  498. LPTSTR pszNextColon = StrChr(szField + 1, TEXT(':'));
  499. if (pszNextColon)
  500. {
  501. DWORD dwProcId = (DWORD)StrToLong(pszNextColon + 1);
  502. LPITEMIDLIST pidlGlobal = (LPITEMIDLIST) SHLockShared(hMem, dwProcId);
  503. if (pidlGlobal)
  504. {
  505. if (!IsBadReadPtr(pidlGlobal, 1))
  506. pidl = ILClone(pidlGlobal);
  507. SHUnlockShared(pidlGlobal);
  508. SHFreeShared(hMem, dwProcId);
  509. }
  510. }
  511. }
  512. return pidl;
  513. }
  514. #define MYDOCS_CLSIDW L"{450d8fba-ad25-11d0-98a8-0800361b1103}" // CLSID_MyDocuments
  515. LPITEMIDLIST MyDocsIDList(void)
  516. {
  517. LPITEMIDLIST pidl = NULL;
  518. IShellFolder *psf;
  519. HRESULT hres = SHGetDesktopFolder(&psf);
  520. if (SUCCEEDED(hres))
  521. {
  522. hres = psf->ParseDisplayName(NULL, NULL, L"::" MYDOCS_CLSIDW, NULL, &pidl, NULL);
  523. psf->Release();
  524. }
  525. // Win95/NT4 case, go for the real MyDocs folder
  526. if (FAILED(hres))
  527. {
  528. hres = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);
  529. }
  530. return SUCCEEDED(hres) ? pidl : NULL;
  531. }
  532. BOOL SHExplorerParseCmdLine(PNEWFOLDERINFO pfi)
  533. {
  534. int i;
  535. TCHAR szField[MAX_PATH];
  536. LPCTSTR pszCmdLine = GetCommandLine();
  537. pszCmdLine = PathGetArgs(pszCmdLine);
  538. // empty command line -> explorer My Docs
  539. if (*pszCmdLine == 0)
  540. {
  541. pfi->uFlags = COF_CREATENEWWINDOW | COF_EXPLORE;
  542. // try MyDocs first?
  543. pfi->pidl = MyDocsIDList();
  544. if (pfi->pidl == NULL)
  545. {
  546. TCHAR szPath[MAX_PATH];
  547. GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
  548. PathStripToRoot(szPath);
  549. pfi->pidl = ILCreateFromPath(szPath);
  550. }
  551. return BOOLFROMPTR(pfi->pidl);
  552. }
  553. // Arguments must be separated by '=' or ','
  554. for (i = 1; _private_ParseField(pszCmdLine, i, szField, ARRAYSIZE(szField)); i++)
  555. {
  556. if (lstrcmpi(szField, TEXT("/N")) == 0)
  557. {
  558. pfi->uFlags |= COF_CREATENEWWINDOW | COF_NOFINDWINDOW;
  559. }
  560. else if (lstrcmpi(szField, TEXT("/S")) == 0)
  561. {
  562. pfi->uFlags |= COF_USEOPENSETTINGS;
  563. }
  564. else if (lstrcmpi(szField, TEXT("/E")) == 0)
  565. {
  566. pfi->uFlags |= COF_EXPLORE;
  567. }
  568. else if (lstrcmpi(szField, TEXT("/ROOT")) == 0)
  569. {
  570. LPITEMIDLIST pidlRoot = NULL;
  571. CLSID *pclsidRoot = NULL;
  572. CLSID clsid;
  573. RIPMSG(!pfi->pidl, "SHExplorerParseCommandLine: (/ROOT) caller passed bad params");
  574. // of the form:
  575. // /ROOT,{clsid}[,<path>]
  576. // /ROOT,/IDLIST,:<hmem>:<hprocess>
  577. // /ROOT,<path>
  578. if (!_private_ParseField(pszCmdLine, ++i, szField, ARRAYSIZE(szField)))
  579. return FALSE;
  580. // {clsid}
  581. if (GUIDFromString(szField, &clsid))
  582. {
  583. TCHAR szGUID[GUIDSTR_MAX];
  584. StrCpyN(szGUID, szField, SIZECHARS(szGUID));
  585. // {clsid} case, if not path compute from the registry
  586. if (!_private_ParseField(pszCmdLine, ++i, szField, ARRAYSIZE(szField)))
  587. {
  588. // path must come from the registry now
  589. if (!GetRootFromRootClass(szGUID, szField, ARRAYSIZE(szField)))
  590. {
  591. return FALSE; // bad command line
  592. }
  593. }
  594. IECreateFromPath(szField, &pidlRoot);
  595. pclsidRoot = &clsid;
  596. }
  597. else if (lstrcmpi(szField, TEXT("/IDLIST")) == 0)
  598. {
  599. // /IDLIST
  600. pidlRoot = IDListFromCmdLine(pszCmdLine, ++i);
  601. }
  602. else
  603. {
  604. // <path>
  605. IECreateFromPath(szField, &pidlRoot);
  606. }
  607. // fix up bad cmd line "explorer.exe /root," case
  608. if (pidlRoot == NULL)
  609. {
  610. HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pfi->pidlRoot);
  611. if (FAILED(hr))
  612. {
  613. pfi->pidlRoot = NULL;
  614. }
  615. }
  616. if (pidlRoot)
  617. {
  618. pfi->pidl = ILRootedCreateIDList(pclsidRoot, pidlRoot);
  619. ILFree(pidlRoot);
  620. }
  621. }
  622. else if (lstrcmpi(szField, TEXT("/INPROC")) == 0)
  623. {
  624. // Parse and skip the next arg or 2
  625. if (!_private_ParseField(pszCmdLine, ++i, szField, ARRAYSIZE(szField)))
  626. {
  627. return FALSE;
  628. }
  629. // The next arg must be a GUID
  630. if (!GUIDFromString(szField, &pfi->clsidInProc))
  631. {
  632. return FALSE;
  633. }
  634. pfi->uFlags |= COF_INPROC;
  635. }
  636. else if (lstrcmpi(szField, TEXT("/SELECT")) == 0)
  637. {
  638. pfi->uFlags |= COF_SELECT;
  639. }
  640. else if (lstrcmpi(szField, TEXT("/NOUI")) == 0)
  641. {
  642. pfi->uFlags |= COF_NOUI;
  643. }
  644. else if (lstrcmpi(szField, TEXT("-embedding")) == 0)
  645. {
  646. pfi->uFlags |= COF_AUTOMATION;
  647. }
  648. else if (lstrcmpi(szField, TEXT("/IDLIST")) == 0)
  649. {
  650. LPITEMIDLIST pidl = IDListFromCmdLine(pszCmdLine, ++i);
  651. if (pidl)
  652. {
  653. if (pfi->pidl)
  654. {
  655. // again, this is kind of bogus (see comment below). If we already have a
  656. // pidl, free it and use the new one.
  657. ILFree(pfi->pidl);
  658. }
  659. pfi->pidl = pidl;
  660. }
  661. else if (pfi->pidl == NULL)
  662. {
  663. // if we didn't have a pidl before and we dont have one now, we are in trouble, so get out
  664. return FALSE;
  665. }
  666. }
  667. else if (lstrcmpi(szField, TEXT("/SEPARATE")) == 0)
  668. {
  669. pfi->uFlags |= COF_SEPARATEPROCESS;
  670. }
  671. else
  672. {
  673. LPITEMIDLIST pidl = ILCreateFromPath(szField);
  674. if (!pidl)
  675. {
  676. //
  677. // LEGACY - if this is unparseable, then guess it is relative path
  678. // this catches "explorer ." as opening the current directory
  679. //
  680. TCHAR szDir[MAX_PATH];
  681. TCHAR szCombined[MAX_PATH];
  682. GetCurrentDirectory(SIZECHARS(szDir), szDir);
  683. PathCombine(szCombined, szDir, szField);
  684. pidl = ILCreateFromPath(szCombined);
  685. }
  686. // this is kind of bogus: we have traditionally passed both the idlist (/idlist,:580:1612) and the path
  687. // (C:\Winnt\Profiles\reinerf\Desktop) as the default command string to explorer (see HKCR\Folder\shell
  688. // \open\command). Since we have both a /idlist and a path, we have always used the latter so that is what
  689. // we continue to do here.
  690. if (pfi->pidl)
  691. {
  692. ILFree(pfi->pidl); // free the /idlist pidl and use the one from the path
  693. }
  694. pfi->pidl = pidl;
  695. if (pidl)
  696. {
  697. pfi->uFlags |= COF_NOTRANSLATE; // pidl is abosolute from the desktop
  698. }
  699. else
  700. {
  701. pfi->pszPath = (LPSTR) StrDup(szField);
  702. if (pfi->pszPath)
  703. {
  704. pfi->uFlags |= COF_PARSEPATH;
  705. }
  706. }
  707. }
  708. }
  709. return TRUE;
  710. }
  711. #define ISSEP(c) ((c) == TEXT('=') || (c) == TEXT(','))
  712. #define ISWHITE(c) ((c) == TEXT(' ') || (c) == TEXT('\t') || (c) == TEXT('\n') || (c) == TEXT('\r'))
  713. #define ISNOISE(c) ((c) == TEXT('"'))
  714. #define QUOTE TEXT('"')
  715. #define COMMA TEXT(',')
  716. #define SPACE TEXT(' ')
  717. #define EQUAL TEXT('=')
  718. /* BOOL ParseField(szData,n,szBuf,iBufLen)
  719. *
  720. * Given a line from SETUP.INF, will extract the nth field from the string
  721. * fields are assumed separated by comma's. Leading and trailing spaces
  722. * are removed.
  723. *
  724. * ENTRY:
  725. *
  726. * szData : pointer to line from SETUP.INF
  727. * n : field to extract. ( 1 based )
  728. * 0 is field before a '=' sign
  729. * szDataStr : pointer to buffer to hold extracted field
  730. * iBufLen : size of buffer to receive extracted field.
  731. *
  732. * EXIT: returns TRUE if successful, FALSE if failure.
  733. *
  734. * Copied from shell32\util.cpp
  735. * note that this is now used to parse the Explorer command line
  736. * --ccooney
  737. */
  738. BOOL _private_ParseField(LPCTSTR pszData, int n, LPTSTR szBuf, int iBufLen)
  739. {
  740. BOOL fQuote = FALSE;
  741. LPCTSTR pszInf = pszData;
  742. LPTSTR ptr;
  743. int iLen = 1;
  744. if (!pszData || !szBuf)
  745. return FALSE;
  746. /*
  747. * find the first separator
  748. */
  749. while (*pszInf && !ISSEP(*pszInf))
  750. {
  751. if (*pszInf == QUOTE)
  752. fQuote = !fQuote;
  753. pszInf = CharNext(pszInf);
  754. }
  755. if (n == 0 && *pszInf != TEXT('='))
  756. return FALSE;
  757. if (n > 0 && *pszInf == TEXT('=') && !fQuote)
  758. // Change pszData to point to first field
  759. pszData = ++pszInf; // Ok for DBCS
  760. /*
  761. * locate the nth comma, that is not inside of quotes
  762. */
  763. fQuote = FALSE;
  764. while (n > 1)
  765. {
  766. while (*pszData)
  767. {
  768. if (!fQuote && ISSEP(*pszData))
  769. break;
  770. if (*pszData == QUOTE)
  771. fQuote = !fQuote;
  772. pszData = CharNext(pszData);
  773. }
  774. if (!*pszData)
  775. {
  776. szBuf[0] = 0; // make szBuf empty
  777. return FALSE;
  778. }
  779. pszData = CharNext(pszData); // we could do ++ here since we got here
  780. // after finding comma or equal
  781. n--;
  782. }
  783. /*
  784. * now copy the field to szBuf
  785. */
  786. while (ISWHITE(*pszData))
  787. pszData = CharNext(pszData); // we could do ++ here since white space can
  788. // NOT be a lead byte
  789. fQuote = FALSE;
  790. ptr = szBuf; // fill output buffer with this
  791. while (*pszData)
  792. {
  793. if (*pszData == QUOTE)
  794. {
  795. //
  796. // If we're in quotes already, maybe this
  797. // is a double quote as in: "He said ""Hello"" to me"
  798. //
  799. if (fQuote && *(pszData+1) == QUOTE) // Yep, double-quoting - QUOTE is non-DBCS
  800. {
  801. if (iLen < iBufLen)
  802. {
  803. *ptr++ = QUOTE;
  804. ++iLen;
  805. }
  806. pszData++; // now skip past 1st quote
  807. }
  808. else
  809. fQuote = !fQuote;
  810. }
  811. else if (!fQuote && ISSEP(*pszData))
  812. break;
  813. else
  814. {
  815. if ( iLen < iBufLen )
  816. {
  817. *ptr++ = *pszData; // Thank you, Dave
  818. ++iLen;
  819. }
  820. if ( IsDBCSLeadByte(*pszData) && (iLen < iBufLen) )
  821. {
  822. *ptr++ = pszData[1];
  823. ++iLen;
  824. }
  825. }
  826. pszData = CharNext(pszData);
  827. }
  828. /*
  829. * remove trailing spaces
  830. */
  831. while (ptr > szBuf)
  832. {
  833. ptr = CharPrev(szBuf, ptr);
  834. if (!ISWHITE(*ptr))
  835. {
  836. ptr = CharNext(ptr);
  837. break;
  838. }
  839. }
  840. *ptr = 0;
  841. return TRUE;
  842. }