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.

987 lines
30 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. BOOL bRet;
  485. TCHAR szClass[MAX_PATH];
  486. if (SUCCEEDED(StringCchPrintf(szClass, ARRAYSIZE(szClass), TEXT("CLSID\\%s\\ShellExplorerRoot"), pszGUID)))
  487. {
  488. // REVIEW: Do we need SRRF_RM_NORMAL? Is there a reason we wouldn't
  489. // want this to succeed in either safe or safe network boot modes?
  490. DWORD cbPath = cchPath * sizeof(TCHAR);
  491. bRet = ERROR_SUCCESS == SHRegGetValue(HKEY_CLASSES_ROOT, szClass, NULL, SRRF_RT_REG_SZ | SRRF_RM_NORMAL, NULL, pszPath, &cbPath);
  492. }
  493. else
  494. {
  495. bRet = FALSE;
  496. }
  497. return bRet;
  498. }
  499. // format is ":<hMem>:<hProcess>"
  500. LPITEMIDLIST IDListFromCmdLine(LPCTSTR pszCmdLine, int i)
  501. {
  502. LPITEMIDLIST pidl = NULL;
  503. TCHAR szField[80];
  504. if (_private_ParseField(pszCmdLine, i, szField, ARRAYSIZE(szField)) && szField[0] == TEXT(':'))
  505. {
  506. // Convert the string of format ":<hmem>:<hprocess>" into a pointer
  507. HANDLE hMem = LongToHandle(StrToLong(szField + 1));
  508. LPTSTR pszNextColon = StrChr(szField + 1, TEXT(':'));
  509. if (pszNextColon)
  510. {
  511. DWORD dwProcId = (DWORD)StrToLong(pszNextColon + 1);
  512. LPITEMIDLIST pidlGlobal = (LPITEMIDLIST) SHLockShared(hMem, dwProcId);
  513. if (pidlGlobal)
  514. {
  515. pidl = ILClone(pidlGlobal);
  516. SHUnlockShared(pidlGlobal);
  517. SHFreeShared(hMem, dwProcId);
  518. }
  519. }
  520. }
  521. return pidl;
  522. }
  523. #define MYDOCS_CLSIDW L"{450d8fba-ad25-11d0-98a8-0800361b1103}" // CLSID_MyDocuments
  524. LPITEMIDLIST MyDocsIDList(void)
  525. {
  526. LPITEMIDLIST pidl = NULL;
  527. IShellFolder *psf;
  528. HRESULT hres = SHGetDesktopFolder(&psf);
  529. if (SUCCEEDED(hres))
  530. {
  531. hres = psf->ParseDisplayName(NULL, NULL, L"::" MYDOCS_CLSIDW, NULL, &pidl, NULL);
  532. psf->Release();
  533. }
  534. // Win95/NT4 case, go for the real MyDocs folder
  535. if (FAILED(hres))
  536. {
  537. hres = SHGetSpecialFolderLocation(NULL, CSIDL_PERSONAL, &pidl);
  538. }
  539. return SUCCEEDED(hres) ? pidl : NULL;
  540. }
  541. BOOL SHExplorerParseCmdLine(PNEWFOLDERINFO pfi)
  542. {
  543. int i;
  544. TCHAR szField[MAX_PATH];
  545. LPCTSTR pszCmdLine = GetCommandLine();
  546. pszCmdLine = PathGetArgs(pszCmdLine);
  547. // empty command line -> explorer My Docs
  548. if (*pszCmdLine == 0)
  549. {
  550. pfi->uFlags = COF_CREATENEWWINDOW | COF_EXPLORE;
  551. // try MyDocs first?
  552. pfi->pidl = MyDocsIDList();
  553. if (pfi->pidl == NULL)
  554. {
  555. TCHAR szPath[MAX_PATH];
  556. GetWindowsDirectory(szPath, ARRAYSIZE(szPath));
  557. PathStripToRoot(szPath);
  558. pfi->pidl = ILCreateFromPath(szPath);
  559. }
  560. return BOOLFROMPTR(pfi->pidl);
  561. }
  562. // Arguments must be separated by '=' or ','
  563. for (i = 1; _private_ParseField(pszCmdLine, i, szField, ARRAYSIZE(szField)); i++)
  564. {
  565. if (lstrcmpi(szField, TEXT("/N")) == 0)
  566. {
  567. pfi->uFlags |= COF_CREATENEWWINDOW | COF_NOFINDWINDOW;
  568. }
  569. else if (lstrcmpi(szField, TEXT("/S")) == 0)
  570. {
  571. pfi->uFlags |= COF_USEOPENSETTINGS;
  572. }
  573. else if (lstrcmpi(szField, TEXT("/E")) == 0)
  574. {
  575. pfi->uFlags |= COF_EXPLORE;
  576. }
  577. else if (lstrcmpi(szField, TEXT("/ROOT")) == 0)
  578. {
  579. LPITEMIDLIST pidlRoot = NULL;
  580. CLSID *pclsidRoot = NULL;
  581. CLSID clsid;
  582. RIPMSG(!pfi->pidl, "SHExplorerParseCommandLine: (/ROOT) caller passed bad params");
  583. // of the form:
  584. // /ROOT,{clsid}[,<path>]
  585. // /ROOT,/IDLIST,:<hmem>:<hprocess>
  586. // /ROOT,<path>
  587. if (!_private_ParseField(pszCmdLine, ++i, szField, ARRAYSIZE(szField)))
  588. return FALSE;
  589. // {clsid}
  590. if (GUIDFromString(szField, &clsid))
  591. {
  592. TCHAR szGUID[GUIDSTR_MAX];
  593. StringCchCopy(szGUID, ARRAYSIZE(szGUID), szField);
  594. // {clsid} case, if not path compute from the registry
  595. if (!_private_ParseField(pszCmdLine, ++i, szField, ARRAYSIZE(szField)))
  596. {
  597. // path must come from the registry now
  598. if (!GetRootFromRootClass(szGUID, szField, ARRAYSIZE(szField)))
  599. {
  600. return FALSE; // bad command line
  601. }
  602. }
  603. IECreateFromPath(szField, &pidlRoot);
  604. pclsidRoot = &clsid;
  605. }
  606. else if (lstrcmpi(szField, TEXT("/IDLIST")) == 0)
  607. {
  608. // /IDLIST
  609. pidlRoot = IDListFromCmdLine(pszCmdLine, ++i);
  610. }
  611. else
  612. {
  613. // <path>
  614. IECreateFromPath(szField, &pidlRoot);
  615. }
  616. // fix up bad cmd line "explorer.exe /root," case
  617. if (pidlRoot == NULL)
  618. {
  619. HRESULT hr = SHGetSpecialFolderLocation(NULL, CSIDL_DESKTOP, &pfi->pidlRoot);
  620. if (FAILED(hr))
  621. {
  622. pfi->pidlRoot = NULL;
  623. }
  624. }
  625. if (pidlRoot)
  626. {
  627. pfi->pidl = ILRootedCreateIDList(pclsidRoot, pidlRoot);
  628. ILFree(pidlRoot);
  629. }
  630. }
  631. else if (lstrcmpi(szField, TEXT("/INPROC")) == 0)
  632. {
  633. // Parse and skip the next arg or 2
  634. if (!_private_ParseField(pszCmdLine, ++i, szField, ARRAYSIZE(szField)))
  635. {
  636. return FALSE;
  637. }
  638. // The next arg must be a GUID
  639. if (!GUIDFromString(szField, &pfi->clsidInProc))
  640. {
  641. return FALSE;
  642. }
  643. pfi->uFlags |= COF_INPROC;
  644. }
  645. else if (lstrcmpi(szField, TEXT("/SELECT")) == 0)
  646. {
  647. pfi->uFlags |= COF_SELECT;
  648. }
  649. else if (lstrcmpi(szField, TEXT("/NOUI")) == 0)
  650. {
  651. pfi->uFlags |= COF_NOUI;
  652. }
  653. else if (lstrcmpi(szField, TEXT("-embedding")) == 0)
  654. {
  655. pfi->uFlags |= COF_AUTOMATION;
  656. }
  657. else if (lstrcmpi(szField, TEXT("/IDLIST")) == 0)
  658. {
  659. LPITEMIDLIST pidl = IDListFromCmdLine(pszCmdLine, ++i);
  660. if (pidl)
  661. {
  662. if (pfi->pidl)
  663. {
  664. // again, this is kind of bogus (see comment below). If we already have a
  665. // pidl, free it and use the new one.
  666. ILFree(pfi->pidl);
  667. }
  668. pfi->pidl = pidl;
  669. }
  670. else if (pfi->pidl == NULL)
  671. {
  672. // if we didn't have a pidl before and we dont have one now, we are in trouble, so get out
  673. return FALSE;
  674. }
  675. }
  676. else if (lstrcmpi(szField, TEXT("/SEPARATE")) == 0)
  677. {
  678. pfi->uFlags |= COF_SEPARATEPROCESS;
  679. }
  680. else
  681. {
  682. LPITEMIDLIST pidl = ILCreateFromPath(szField);
  683. if (!pidl)
  684. {
  685. //
  686. // LEGACY - if this is unparseable, then guess it is relative path
  687. // this catches "explorer ." as opening the current directory
  688. //
  689. TCHAR szDir[MAX_PATH];
  690. TCHAR szCombined[MAX_PATH];
  691. DWORD lenDir = GetCurrentDirectory(ARRAYSIZE(szDir), szDir);
  692. if (lenDir > 0 && lenDir < ARRAYSIZE(szDir) && NULL != PathCombine(szCombined, szDir, szField))
  693. {
  694. pidl = ILCreateFromPath(szCombined);
  695. }
  696. }
  697. // this is kind of bogus: we have traditionally passed both the idlist (/idlist,:580:1612) and the path
  698. // (C:\Winnt\Profiles\reinerf\Desktop) as the default command string to explorer (see HKCR\Folder\shell
  699. // \open\command). Since we have both a /idlist and a path, we have always used the latter so that is what
  700. // we continue to do here.
  701. if (pfi->pidl)
  702. {
  703. ILFree(pfi->pidl); // free the /idlist pidl and use the one from the path
  704. }
  705. pfi->pidl = pidl;
  706. if (pidl)
  707. {
  708. pfi->uFlags |= COF_NOTRANSLATE; // pidl is abosolute from the desktop
  709. }
  710. else
  711. {
  712. pfi->pszPath = (LPSTR) StrDup(szField);
  713. if (pfi->pszPath)
  714. {
  715. pfi->uFlags |= COF_PARSEPATH;
  716. }
  717. }
  718. }
  719. }
  720. return TRUE;
  721. }
  722. #define ISSEP(c) ((c) == TEXT('=') || (c) == TEXT(','))
  723. #define ISWHITE(c) ((c) == TEXT(' ') || (c) == TEXT('\t') || (c) == TEXT('\n') || (c) == TEXT('\r'))
  724. #define ISNOISE(c) ((c) == TEXT('"'))
  725. #define QUOTE TEXT('"')
  726. #define COMMA TEXT(',')
  727. #define SPACE TEXT(' ')
  728. #define EQUAL TEXT('=')
  729. /* BOOL ParseField(szData,n,szBuf,iBufLen)
  730. *
  731. * Given a line from SETUP.INF, will extract the nth field from the string
  732. * fields are assumed separated by comma's. Leading and trailing spaces
  733. * are removed.
  734. *
  735. * ENTRY:
  736. *
  737. * szData : pointer to line from SETUP.INF
  738. * n : field to extract. ( 1 based )
  739. * 0 is field before a '=' sign
  740. * szDataStr : pointer to buffer to hold extracted field
  741. * iBufLen : size of buffer to receive extracted field.
  742. *
  743. * EXIT: returns TRUE if successful, FALSE if failure.
  744. *
  745. * Copied from shell32\util.cpp
  746. * note that this is now used to parse the Explorer command line
  747. * --ccooney
  748. */
  749. BOOL _private_ParseField(LPCTSTR pszData, int n, LPTSTR szBuf, int iBufLen)
  750. {
  751. BOOL fQuote = FALSE;
  752. LPCTSTR pszInf = pszData;
  753. LPTSTR ptr;
  754. int iLen = 1;
  755. if (!pszData || !szBuf)
  756. return FALSE;
  757. /*
  758. * find the first separator
  759. */
  760. while (*pszInf && !ISSEP(*pszInf))
  761. {
  762. if (*pszInf == QUOTE)
  763. fQuote = !fQuote;
  764. pszInf = CharNext(pszInf);
  765. }
  766. if (n == 0 && *pszInf != TEXT('='))
  767. return FALSE;
  768. if (n > 0 && *pszInf == TEXT('=') && !fQuote)
  769. // Change pszData to point to first field
  770. pszData = ++pszInf; // Ok for DBCS
  771. /*
  772. * locate the nth comma, that is not inside of quotes
  773. */
  774. fQuote = FALSE;
  775. while (n > 1)
  776. {
  777. while (*pszData)
  778. {
  779. if (!fQuote && ISSEP(*pszData))
  780. break;
  781. if (*pszData == QUOTE)
  782. fQuote = !fQuote;
  783. pszData = CharNext(pszData);
  784. }
  785. if (!*pszData)
  786. {
  787. szBuf[0] = 0; // make szBuf empty
  788. return FALSE;
  789. }
  790. pszData = CharNext(pszData); // we could do ++ here since we got here
  791. // after finding comma or equal
  792. n--;
  793. }
  794. /*
  795. * now copy the field to szBuf
  796. */
  797. while (ISWHITE(*pszData))
  798. pszData = CharNext(pszData); // we could do ++ here since white space can
  799. // NOT be a lead byte
  800. fQuote = FALSE;
  801. ptr = szBuf; // fill output buffer with this
  802. while (*pszData)
  803. {
  804. if (*pszData == QUOTE)
  805. {
  806. //
  807. // If we're in quotes already, maybe this
  808. // is a double quote as in: "He said ""Hello"" to me"
  809. //
  810. if (fQuote && *(pszData+1) == QUOTE) // Yep, double-quoting - QUOTE is non-DBCS
  811. {
  812. if (iLen < iBufLen)
  813. {
  814. *ptr++ = QUOTE;
  815. ++iLen;
  816. }
  817. pszData++; // now skip past 1st quote
  818. }
  819. else
  820. fQuote = !fQuote;
  821. }
  822. else if (!fQuote && ISSEP(*pszData))
  823. break;
  824. else
  825. {
  826. if ( iLen < iBufLen )
  827. {
  828. *ptr++ = *pszData; // Thank you, Dave
  829. ++iLen;
  830. }
  831. if ( IsDBCSLeadByte(*pszData) && (iLen < iBufLen) )
  832. {
  833. *ptr++ = pszData[1];
  834. ++iLen;
  835. }
  836. }
  837. pszData = CharNext(pszData);
  838. }
  839. /*
  840. * remove trailing spaces
  841. */
  842. while (ptr > szBuf)
  843. {
  844. ptr = CharPrev(szBuf, ptr);
  845. if (!ISWHITE(*ptr))
  846. {
  847. ptr = CharNext(ptr);
  848. break;
  849. }
  850. }
  851. *ptr = 0;
  852. return TRUE;
  853. }