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.

1964 lines
64 KiB

  1. #include "shellprv.h"
  2. #include "shlexec.h"
  3. #include "netview.h"
  4. extern "C" {
  5. #include <badapps.h>
  6. }
  7. #include <htmlhelp.h>
  8. #include "ole2dup.h"
  9. #include <newexe.h>
  10. #include "ids.h"
  11. #define SAFE_DEBUGSTR(str) ((str) ? (str) : "<NULL>")
  12. HINSTANCE Window_GetInstance(HWND hwnd)
  13. {
  14. DWORD idProcess;
  15. GetWindowThreadProcessId(hwnd, &idProcess);
  16. // HINSTANCEs are pointers valid only within
  17. // a single process, so 33 is returned to indicate success
  18. // as 0-32 are reserved for error. (Actually 32 is supposed
  19. // to be a valid success return but some apps get it wrong.)
  20. return (HINSTANCE)(DWORD_PTR)(idProcess ? 33 : 0);
  21. }
  22. // Return TRUE if the window belongs to a 32bit or a Win4.0 app.
  23. // NB We can't just check if it's a 32bit window
  24. // since many apps use 16bit ddeml windows to communicate with the shell
  25. // On NT we can.
  26. BOOL Window_IsLFNAware(HWND hwnd)
  27. {
  28. // 32-bit window
  29. return LOWORD(GetWindowLongPtr(hwnd,GWLP_HINSTANCE)) == 0;
  30. }
  31. #define COPYTODST(_szdst, _szend, _szsrc, _ulen, _ret) \
  32. { \
  33. UINT _utemp = _ulen; \
  34. if ((UINT)(_szend-_szdst) < _utemp + 1) { \
  35. return(_ret); \
  36. } \
  37. StrCpyN(_szdst, _szsrc, _utemp + 1); \
  38. _szdst += _utemp; \
  39. }
  40. /* Returns NULL if this is the last parm, pointer to next space otherwise
  41. */
  42. LPTSTR _GetNextParm(LPCTSTR lpSrc, LPTSTR lpDst, UINT cchDst)
  43. {
  44. LPCTSTR lpNextQuote, lpNextSpace;
  45. LPTSTR lpEnd = lpDst+cchDst-1; // dec to account for trailing NULL
  46. BOOL fQuote; // quoted string?
  47. BOOL fDoubleQuote; // is this quote a double quote?
  48. while (*lpSrc == TEXT(' '))
  49. ++lpSrc;
  50. if (!*lpSrc)
  51. return(NULL);
  52. fQuote = (*lpSrc == TEXT('"'));
  53. if (fQuote)
  54. lpSrc++; // skip leading quote
  55. for (;;)
  56. {
  57. lpNextQuote = StrChr(lpSrc, TEXT('"'));
  58. if (!fQuote)
  59. {
  60. // for an un-quoted string, copy all chars to first space/null
  61. lpNextSpace = StrChr(lpSrc, TEXT(' '));
  62. if (!lpNextSpace) // null before space! (end of string)
  63. {
  64. if (!lpNextQuote)
  65. {
  66. // copy all chars to the null
  67. if (lpDst)
  68. {
  69. COPYTODST(lpDst, lpEnd, lpSrc, lstrlen(lpSrc), NULL);
  70. }
  71. return NULL;
  72. }
  73. else
  74. {
  75. // we have a quote to convert. Fall through.
  76. }
  77. }
  78. else if (!lpNextQuote || lpNextSpace < lpNextQuote)
  79. {
  80. // copy all chars to the space
  81. if (lpDst)
  82. {
  83. COPYTODST(lpDst, lpEnd, lpSrc, (UINT)(lpNextSpace-lpSrc), NULL);
  84. }
  85. return (LPTSTR)lpNextSpace;
  86. }
  87. else
  88. {
  89. // quote before space. Fall through to convert quote.
  90. }
  91. }
  92. else if (!lpNextQuote)
  93. {
  94. // a quoted string without a terminating quote? Illegal!
  95. ASSERT(0);
  96. return NULL;
  97. }
  98. // we have a potential quote to convert
  99. ASSERT(lpNextQuote);
  100. fDoubleQuote = *(lpNextQuote+1) == TEXT('"');
  101. if (fDoubleQuote)
  102. lpNextQuote++; // so the quote is copied
  103. if (lpDst)
  104. {
  105. COPYTODST(lpDst, lpEnd, lpSrc, (UINT) (lpNextQuote-lpSrc), NULL);
  106. }
  107. lpSrc = lpNextQuote+1;
  108. if (!fDoubleQuote)
  109. {
  110. // we just copied the rest of this quoted string. if this wasn't
  111. // quoted, it's an illegal string... treat the quote as a space.
  112. ASSERT(fQuote);
  113. return (LPTSTR)lpSrc;
  114. }
  115. }
  116. }
  117. #define PEMAGIC ((WORD)'P'+((WORD)'E'<<8))
  118. // Returns TRUE is app is LFN aware.
  119. // This assumes all Win32 apps are LFN aware.
  120. BOOL App_IsLFNAware(LPCTSTR pszFile)
  121. {
  122. BOOL fRet = FALSE;
  123. // Assume Win 4.0 apps and Win32 apps are LFN aware.
  124. DWORD dw = GetExeType(pszFile);
  125. // TraceMsg(TF_SHELLEXEC, "s.aila: %s %s %x", pszFile, szFile, dw);
  126. if ((LOWORD(dw) == PEMAGIC) || ((LOWORD(dw) == NEMAGIC) && (HIWORD(dw) >= 0x0400)))
  127. {
  128. TCHAR sz[MAX_PATH];
  129. PathToAppPathKey(pszFile, sz, ARRAYSIZE(sz));
  130. fRet = (ERROR_SUCCESS != SHGetValue(HKEY_LOCAL_MACHINE, sz, TEXT("UseShortName"), NULL, NULL, NULL));
  131. }
  132. return fRet;
  133. }
  134. // apps can tag themselves in a way so we know we can pass an URL on the cmd
  135. // line. this uses the existance of a value called "UseURL" under the
  136. // App Paths key in the registry associated with the app that is passed in.
  137. // pszPath is the path to the exe
  138. BOOL DoesAppWantUrl(LPCTSTR pszPath)
  139. {
  140. TCHAR sz[MAX_PATH];
  141. PathToAppPathKey(pszPath, sz, ARRAYSIZE(sz));
  142. return (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, sz, TEXT("UseURL"), NULL, NULL, NULL));
  143. }
  144. BOOL _AppIsLFNAware(LPCTSTR pszFile)
  145. {
  146. TCHAR szFile[MAX_PATH];
  147. // Does it look like a DDE command?
  148. if (pszFile && *pszFile && (*pszFile != TEXT('[')))
  149. {
  150. // Nope - Hopefully just a regular old command %1 thing.
  151. lstrcpyn(szFile, pszFile, ARRAYSIZE(szFile));
  152. LPTSTR pszArgs = PathGetArgs(szFile);
  153. if (*pszArgs)
  154. *(pszArgs - 1) = TEXT('\0');
  155. PathRemoveBlanks(szFile); // remove any blanks that may be after the command
  156. PathUnquoteSpaces(szFile);
  157. return App_IsLFNAware(szFile);
  158. }
  159. return FALSE;
  160. }
  161. // in:
  162. // lpFile exe name (used for %0 or %1 in replacement string)
  163. // lpFrom string template to sub params and file into "excel.exe %1 %2 /n %3"
  164. // lpParams parameter list "foo.txt bar.txt"
  165. // out:
  166. // lpTo output string with all parameters replaced
  167. //
  168. // supports:
  169. // %* replace with all parameters
  170. // %0, %1 replace with file
  171. // %n use nth parameter
  172. //
  173. // replace parameter placeholders (%1 %2 ... %n) with parameters
  174. //
  175. UINT ReplaceParameters(LPTSTR lpTo, UINT cchTo, LPCTSTR lpFile,
  176. LPCTSTR lpFrom, LPCTSTR lpParms, int nShow, DWORD * pdwHotKey, BOOL fLFNAware,
  177. LPCITEMIDLIST lpID, LPITEMIDLIST *ppidlGlobal)
  178. {
  179. int i;
  180. TCHAR c;
  181. LPCTSTR lpT;
  182. TCHAR sz[MAX_PATH];
  183. BOOL fFirstParam = TRUE;
  184. LPTSTR lpEnd = lpTo + cchTo - 1; // dec to allow trailing NULL
  185. LPTSTR pToOrig = lpTo;
  186. for (; *lpFrom; lpFrom++)
  187. {
  188. if (*lpFrom == TEXT('%'))
  189. {
  190. switch (*(++lpFrom))
  191. {
  192. case TEXT('~'): // Copy all parms starting with nth (n >= 2 and <= 9)
  193. c = *(++lpFrom);
  194. if (c >= TEXT('2') && c <= TEXT('9'))
  195. {
  196. for (i = 2, lpT = lpParms; i < c-TEXT('0') && lpT; i++)
  197. {
  198. lpT = _GetNextParm(lpT, NULL, 0);
  199. }
  200. if (lpT)
  201. {
  202. COPYTODST(lpTo, lpEnd, lpT, lstrlen(lpT), SE_ERR_ACCESSDENIED);
  203. }
  204. }
  205. else
  206. {
  207. lpFrom -= 2; // Backup over %~ and pass through
  208. goto NormalChar;
  209. }
  210. break;
  211. case TEXT('*'): // Copy all parms
  212. if (lpParms)
  213. {
  214. COPYTODST(lpTo, lpEnd, lpParms, lstrlen(lpParms), SE_ERR_ACCESSDENIED);
  215. }
  216. break;
  217. case TEXT('0'):
  218. case TEXT('1'):
  219. // %0, %1, copy the file name
  220. // If the filename comes first then we don't need to convert it to
  221. // a shortname. If it appears anywhere else and the app is not LFN
  222. // aware then we must.
  223. if (!(fFirstParam || fLFNAware || _AppIsLFNAware(pToOrig)) &&
  224. GetShortPathName(lpFile, sz, ARRAYSIZE(sz)) > 0)
  225. {
  226. TraceMsg(TF_SHELLEXEC, "ShellExecuteEx: Getting short version of path.");
  227. COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
  228. }
  229. else
  230. {
  231. TraceMsg(TF_SHELLEXEC, "ShellExecuteEx: Using long version of path.");
  232. COPYTODST(lpTo, lpEnd, lpFile, lstrlen(lpFile), SE_ERR_ACCESSDENIED);
  233. }
  234. break;
  235. case TEXT('2'):
  236. case TEXT('3'):
  237. case TEXT('4'):
  238. case TEXT('5'):
  239. case TEXT('6'):
  240. case TEXT('7'):
  241. case TEXT('8'):
  242. case TEXT('9'):
  243. for (i = *lpFrom-TEXT('2'), lpT = lpParms; lpT; --i)
  244. {
  245. if (i)
  246. lpT = _GetNextParm(lpT, NULL, 0);
  247. else
  248. {
  249. sz[0] = '\0'; // ensure a valid string, regardless of what happens within _GetNextParm
  250. _GetNextParm(lpT, sz, ARRAYSIZE(sz));
  251. COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
  252. break;
  253. }
  254. }
  255. break;
  256. case TEXT('s'):
  257. case TEXT('S'):
  258. wnsprintf(sz, ARRAYSIZE(sz), TEXT("%ld"), nShow);
  259. COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
  260. break;
  261. case TEXT('h'):
  262. case TEXT('H'):
  263. wnsprintf(sz, ARRAYSIZE(sz), TEXT("%X"), pdwHotKey ? *pdwHotKey : 0);
  264. COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
  265. if (pdwHotKey)
  266. *pdwHotKey = 0;
  267. break;
  268. // Note that a new global IDList is created for each
  269. case TEXT('i'):
  270. case TEXT('I'):
  271. // Note that a single global ID list is created and used over
  272. // again, so that it may be easily destroyed if anything
  273. // goes wrong
  274. if (ppidlGlobal)
  275. {
  276. if (lpID && !*ppidlGlobal)
  277. {
  278. *ppidlGlobal = (LPITEMIDLIST)SHAllocShared(lpID,ILGetSize(lpID),GetCurrentProcessId());
  279. if (!*ppidlGlobal)
  280. {
  281. return SE_ERR_OOM;
  282. }
  283. }
  284. wnsprintf(sz, ARRAYSIZE(sz), TEXT(":%ld:%ld"), *ppidlGlobal, GetCurrentProcessId());
  285. }
  286. else
  287. {
  288. StrCpyN(sz,TEXT(":0"), ARRAYSIZE(sz));
  289. }
  290. COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
  291. break;
  292. case TEXT('l'):
  293. case TEXT('L'):
  294. // Like %1 only using the long name.
  295. // REVIEW UNDONE IANEL Remove the fFirstParam and fLFNAware stuff as soon as this
  296. // is up and running.
  297. TraceMsg(TF_SHELLEXEC, "ShellExecuteEx: Using long version of path.");
  298. COPYTODST(lpTo, lpEnd, lpFile, lstrlen(lpFile), SE_ERR_ACCESSDENIED);
  299. break;
  300. case TEXT('D'):
  301. case TEXT('d'):
  302. {
  303. // %D gives the display name of an object.
  304. if (lpID && SUCCEEDED(SHGetNameAndFlags(lpID, SHGDN_FORPARSING, sz, ARRAYSIZE(sz), NULL)))
  305. {
  306. COPYTODST(lpTo, lpEnd, sz, lstrlen(sz), SE_ERR_ACCESSDENIED);
  307. }
  308. else
  309. return SE_ERR_ACCESSDENIED;
  310. break;
  311. }
  312. default:
  313. goto NormalChar;
  314. }
  315. // TraceMsg(TF_SHELLEXEC, "s.rp: Past first param (1).");
  316. fFirstParam = FALSE;
  317. }
  318. else
  319. {
  320. NormalChar:
  321. // not a "%?" thing, just copy this to the destination
  322. if (lpEnd-lpTo < 2)
  323. {
  324. // Always check for room for DBCS char
  325. return(SE_ERR_ACCESSDENIED);
  326. }
  327. *lpTo++ = *lpFrom;
  328. // Special case for things like "%1" ie don't clear the first param flag
  329. // if we hit a dbl-quote.
  330. if (*lpFrom != TEXT('"'))
  331. {
  332. // TraceMsg(TF_SHELLEXEC, "s.rp: Past first param (2).");
  333. fFirstParam = FALSE;
  334. }
  335. else if (IsDBCSLeadByte(*lpFrom))
  336. {
  337. *lpTo++ = *(++lpFrom);
  338. }
  339. }
  340. }
  341. // We should always have enough room since we dec'ed cchTo when determining
  342. // lpEnd
  343. *lpTo = 0;
  344. // This means success
  345. return(0);
  346. }
  347. HWND ThreadID_GetVisibleWindow(DWORD dwID)
  348. {
  349. HWND hwnd;
  350. for (hwnd = GetWindow(GetDesktopWindow(), GW_CHILD); hwnd; hwnd = GetWindow(hwnd, GW_HWNDNEXT))
  351. {
  352. DWORD dwIDTmp = GetWindowThreadProcessId(hwnd, NULL);
  353. TraceMsg(TF_SHELLEXEC, "s.ti_gvw: Hwnd %x Thread ID %x.", hwnd, dwIDTmp);
  354. if (IsWindowVisible(hwnd) && (dwIDTmp == dwID))
  355. {
  356. TraceMsg(TF_SHELLEXEC, "s.ti_gvw: Found match %x.", hwnd);
  357. return hwnd;
  358. }
  359. }
  360. return NULL;
  361. }
  362. void ActivateHandler(HWND hwnd, DWORD_PTR dwHotKey)
  363. {
  364. ASSERT(hwnd);
  365. hwnd = GetTopParentWindow(hwnd); // returns non-NULL for any non-NULL input
  366. HWND hwndT = GetLastActivePopup(hwnd); // returns non-NULL for any non-NULL input
  367. if (!IsWindowVisible(hwndT))
  368. {
  369. DWORD dwID = GetWindowThreadProcessId(hwnd, NULL);
  370. TraceMsg(TF_SHELLEXEC, "ActivateHandler: Hwnd %x Thread ID %x.", hwnd, dwID);
  371. ASSERT(dwID);
  372. // Find the first visible top level window owned by the
  373. // same guy that's handling the DDE conversation.
  374. hwnd = ThreadID_GetVisibleWindow(dwID);
  375. if (hwnd)
  376. {
  377. hwndT = GetLastActivePopup(hwnd);
  378. if (IsIconic(hwnd))
  379. {
  380. TraceMsg(TF_SHELLEXEC, "ActivateHandler: Window is iconic, restoring.");
  381. ShowWindow(hwnd,SW_RESTORE);
  382. }
  383. else
  384. {
  385. TraceMsg(TF_SHELLEXEC, "ActivateHandler: Window is normal, bringing to top.");
  386. BringWindowToTop(hwnd);
  387. if (hwndT && hwnd != hwndT)
  388. BringWindowToTop(hwndT);
  389. }
  390. // set the hotkey
  391. if (dwHotKey)
  392. {
  393. SendMessage(hwnd, WM_SETHOTKEY, dwHotKey, 0);
  394. }
  395. }
  396. }
  397. }
  398. BOOL FindExistingDrv(LPCTSTR pszUNCRoot, LPTSTR pszLocalName, DWORD cchLocalName)
  399. {
  400. int iDrive;
  401. for (iDrive = 0; iDrive < 26; iDrive++)
  402. {
  403. if (IsRemoteDrive(iDrive))
  404. {
  405. TCHAR szDriveName[3];
  406. DWORD cb = MAX_PATH;
  407. szDriveName[0] = (TCHAR)iDrive + (TCHAR)TEXT('A');
  408. szDriveName[1] = TEXT(':');
  409. szDriveName[2] = 0;
  410. SHWNetGetConnection(szDriveName, pszLocalName, &cb);
  411. if (lstrcmpi(pszUNCRoot, pszLocalName) == 0)
  412. {
  413. StrCpyN(pszLocalName, szDriveName, cchLocalName);
  414. return(TRUE);
  415. }
  416. }
  417. }
  418. return(FALSE);
  419. }
  420. // Returns whether the given net path exists. This fails for NON net paths.
  421. //
  422. BOOL NetPathExists(LPCTSTR lpszPath, DWORD *lpdwType)
  423. {
  424. BOOL fResult = FALSE;
  425. NETRESOURCE nr;
  426. LPTSTR lpSystem;
  427. DWORD dwRes, dwSize = 1024;
  428. void * lpv;
  429. if (!lpszPath || !*lpszPath)
  430. return FALSE;
  431. lpv = (void *)LocalAlloc(LPTR, dwSize);
  432. if (!lpv)
  433. return FALSE;
  434. TryWNetAgain:
  435. nr.dwScope = RESOURCE_GLOBALNET;
  436. nr.dwType = RESOURCETYPE_ANY;
  437. nr.dwDisplayType = 0;
  438. nr.lpLocalName = NULL;
  439. nr.lpRemoteName = (LPTSTR)lpszPath;
  440. nr.lpProvider = NULL;
  441. nr.lpComment = NULL;
  442. dwRes = WNetGetResourceInformation(&nr, lpv, &dwSize, &lpSystem);
  443. // If our buffer wasn't big enough, try a bigger buffer...
  444. if (dwRes == WN_MORE_DATA)
  445. {
  446. void * tmp = LocalReAlloc(lpv, dwSize, LMEM_MOVEABLE);
  447. if (!tmp)
  448. {
  449. LocalFree(lpv);
  450. SetLastError(ERROR_OUTOFMEMORY);
  451. return FALSE;
  452. }
  453. lpv = tmp;
  454. goto TryWNetAgain;
  455. }
  456. fResult = (dwRes == WN_SUCCESS);
  457. if (fResult && lpdwType)
  458. *lpdwType = ((LPNETRESOURCE)lpv)->dwType;
  459. LocalFree(lpv);
  460. return fResult;
  461. }
  462. HRESULT _CheckExistingNet(LPCTSTR pszFile, LPCTSTR pszRoot, BOOL fPrint)
  463. {
  464. //
  465. // This used to be a call to GetFileAttributes(), but
  466. // GetFileAttributes() doesn't handle net paths very well.
  467. // However, we need to be careful, because other shell code
  468. // expects SHValidateUNC to return false for paths that point
  469. // to print shares.
  470. //
  471. HRESULT hr = S_FALSE;
  472. if (!PathIsRoot(pszFile))
  473. {
  474. // if we are checking for a printshare, then it must be a Root
  475. if (fPrint)
  476. hr = E_FAIL;
  477. else if (PathFileExists(pszFile))
  478. hr = S_OK;
  479. }
  480. if (S_FALSE == hr)
  481. {
  482. DWORD dwType;
  483. if (NetPathExists(pszRoot, &dwType))
  484. {
  485. if (fPrint ? dwType != RESOURCETYPE_PRINT : dwType == RESOURCETYPE_PRINT)
  486. hr = E_FAIL;
  487. else
  488. hr = S_OK;
  489. }
  490. else if (-1 != GetFileAttributes(pszRoot))
  491. {
  492. //
  493. // IE 4.01 SP1 QFE #104. GetFileAttributes now called
  494. // as a last resort become some clients often fail when using
  495. // WNetGetResourceInformation. For example, many NFS clients were
  496. // broken because of this.
  497. //
  498. hr = S_OK;
  499. }
  500. }
  501. if (hr == E_FAIL)
  502. SetLastError(ERROR_NOT_SUPPORTED);
  503. return hr;
  504. }
  505. HRESULT _CheckNetUse(HWND hwnd, LPTSTR pszShare, UINT fConnect, LPTSTR pszOut, DWORD cchOut)
  506. {
  507. NETRESOURCE rc;
  508. DWORD dw, err;
  509. DWORD dwRedir = CONNECT_TEMPORARY;
  510. if (!(fConnect & VALIDATEUNC_NOUI))
  511. dwRedir |= CONNECT_INTERACTIVE;
  512. if (fConnect & VALIDATEUNC_CONNECT)
  513. dwRedir |= CONNECT_REDIRECT;
  514. // VALIDATE_PRINT happens only after a failed attempt to validate for
  515. // a file. That previous attempt will have given the option to
  516. // connect to other media -- don't do it here or the user will be
  517. // presented with the same dialog twice when the first one is cancelled.
  518. if (fConnect & VALIDATEUNC_PRINT)
  519. dwRedir |= CONNECT_CURRENT_MEDIA;
  520. rc.lpRemoteName = pszShare;
  521. rc.lpLocalName = NULL;
  522. rc.lpProvider = NULL;
  523. rc.dwType = (fConnect & VALIDATEUNC_PRINT) ? RESOURCETYPE_PRINT : RESOURCETYPE_DISK;
  524. err = WNetUseConnection(hwnd, &rc, NULL, NULL, dwRedir, pszOut, &cchOut, &dw);
  525. TraceMsg(TF_SHELLEXEC, "SHValidateUNC WNetUseConnection(%s) returned %x", pszShare, err);
  526. if (err)
  527. {
  528. SetLastError(err);
  529. return E_FAIL;
  530. }
  531. else if (fConnect & VALIDATEUNC_PRINT)
  532. {
  533. // just because WNetUse succeeded, doesnt mean
  534. // NetPathExists will. if it fails then
  535. // we shouldnt succeed this call regardless
  536. // because we are only interested in print shares.
  537. if (!NetPathExists(pszShare, &dw)
  538. || (dw != RESOURCETYPE_PRINT))
  539. {
  540. SetLastError(ERROR_NOT_SUPPORTED);
  541. return E_FAIL;
  542. }
  543. }
  544. return S_OK;
  545. }
  546. //
  547. // SHValidateUNC
  548. //
  549. // This function validates a UNC path by calling WNetAddConnection3.
  550. // It will make it possible for the user to type a remote (RNA) UNC
  551. // app/document name from Start->Run dialog.
  552. //
  553. // fConnect - flags controling what to do
  554. //
  555. // VALIDATEUNC_NOUI // dont bring up stinking UI!
  556. // VALIDATEUNC_CONNECT // connect a drive letter
  557. // VALIDATEUNC_PRINT // validate as print share instead of disk share
  558. //
  559. BOOL WINAPI SHValidateUNC(HWND hwndOwner, LPTSTR pszFile, UINT fConnect)
  560. {
  561. HRESULT hr;
  562. TCHAR szShare[MAX_PATH];
  563. BOOL fPrint = (fConnect & VALIDATEUNC_PRINT);
  564. UINT cchOrig = lstrlen(pszFile) + 1;
  565. ASSERT(PathIsUNC(pszFile));
  566. ASSERT((fConnect & ~VALIDATEUNC_VALID) == 0);
  567. ASSERT((fConnect & VALIDATEUNC_CONNECT) ? !fPrint : TRUE);
  568. lstrcpyn(szShare, pszFile, ARRAYSIZE(szShare));
  569. if (!PathStripToRoot(szShare))
  570. {
  571. SetLastError(ERROR_PATH_NOT_FOUND);
  572. return FALSE;
  573. }
  574. if (fConnect & VALIDATEUNC_CONNECT)
  575. hr = S_FALSE;
  576. else
  577. hr = _CheckExistingNet(pszFile, szShare, fPrint);
  578. if (S_FALSE == hr)
  579. {
  580. TCHAR szAccessName[MAX_PATH];
  581. if (!fPrint && FindExistingDrv(szShare, szAccessName, ARRAYSIZE(szAccessName)))
  582. {
  583. hr = S_OK;
  584. }
  585. else
  586. hr = _CheckNetUse(hwndOwner, szShare, fConnect, szAccessName, SIZECHARS(szAccessName));
  587. if (S_OK == hr && !fPrint)
  588. {
  589. StrCatBuff(szAccessName, pszFile + lstrlen(szShare), ARRAYSIZE(szAccessName));
  590. // The name should only get shorter, so no need to check length
  591. lstrcpyn(pszFile, szAccessName, cchOrig);
  592. // Handle the root case
  593. if (cchOrig >= 4 && pszFile[2] == TEXT('\0'))
  594. {
  595. pszFile[2] = TEXT('\\');
  596. pszFile[3] = TEXT('\0');
  597. }
  598. hr = _CheckExistingNet(pszFile, szShare, FALSE);
  599. }
  600. }
  601. return (hr == S_OK);
  602. }
  603. HINSTANCE WINAPI RealShellExecuteExA(HWND hwnd, LPCSTR lpOp, LPCSTR lpFile,
  604. LPCSTR lpArgs, LPCSTR lpDir, LPSTR lpResult,
  605. LPCSTR lpTitle, LPSTR lpReserved,
  606. WORD nShowCmd, LPHANDLE lphProcess,
  607. DWORD dwFlags)
  608. {
  609. SHELLEXECUTEINFOA sei = { sizeof(SHELLEXECUTEINFOA), SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
  610. TraceMsg(TF_SHELLEXEC, "RealShellExecuteExA(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX, %d)",
  611. hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
  612. lpReserved, nShowCmd, lphProcess, dwFlags);
  613. // Pass along the lpReserved parameter to the new process
  614. if (lpReserved)
  615. {
  616. sei.fMask |= SEE_MASK_RESERVED;
  617. sei.hInstApp = (HINSTANCE)lpReserved;
  618. }
  619. // Pass along the lpTitle parameter to the new process
  620. if (lpTitle)
  621. {
  622. sei.fMask |= SEE_MASK_HASTITLE;
  623. sei.lpClass = lpTitle;
  624. }
  625. // Pass along the SEPARATE_VDM flag
  626. if (dwFlags & EXEC_SEPARATE_VDM)
  627. {
  628. sei.fMask |= SEE_MASK_FLAG_SEPVDM;
  629. }
  630. // Pass along the NO_CONSOLE flag
  631. if (dwFlags & EXEC_NO_CONSOLE)
  632. {
  633. sei.fMask |= SEE_MASK_NO_CONSOLE;
  634. }
  635. if (lphProcess)
  636. {
  637. // Return the process handle
  638. sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
  639. ShellExecuteExA(&sei);
  640. *lphProcess = sei.hProcess;
  641. }
  642. else
  643. {
  644. ShellExecuteExA(&sei);
  645. }
  646. return sei.hInstApp;
  647. }
  648. HINSTANCE WINAPI RealShellExecuteExW(HWND hwnd, LPCWSTR lpOp, LPCWSTR lpFile,
  649. LPCWSTR lpArgs, LPCWSTR lpDir, LPWSTR lpResult,
  650. LPCWSTR lpTitle, LPWSTR lpReserved,
  651. WORD nShowCmd, LPHANDLE lphProcess,
  652. DWORD dwFlags)
  653. {
  654. SHELLEXECUTEINFOW sei = { sizeof(SHELLEXECUTEINFOW), SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
  655. TraceMsg(TF_SHELLEXEC, "RealShellExecuteExW(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX, %d)",
  656. hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
  657. lpReserved, nShowCmd, lphProcess, dwFlags);
  658. if (lpReserved)
  659. {
  660. sei.fMask |= SEE_MASK_RESERVED;
  661. sei.hInstApp = (HINSTANCE)lpReserved;
  662. }
  663. if (lpTitle)
  664. {
  665. sei.fMask |= SEE_MASK_HASTITLE;
  666. sei.lpClass = lpTitle;
  667. }
  668. if (dwFlags & EXEC_SEPARATE_VDM)
  669. {
  670. sei.fMask |= SEE_MASK_FLAG_SEPVDM;
  671. }
  672. if (dwFlags & EXEC_NO_CONSOLE)
  673. {
  674. sei.fMask |= SEE_MASK_NO_CONSOLE;
  675. }
  676. if (lphProcess)
  677. {
  678. // Return the process handle
  679. sei.fMask |= SEE_MASK_NOCLOSEPROCESS;
  680. ShellExecuteExW(&sei);
  681. *lphProcess = sei.hProcess;
  682. }
  683. else
  684. {
  685. ShellExecuteExW(&sei);
  686. }
  687. return sei.hInstApp;
  688. }
  689. HINSTANCE WINAPI RealShellExecuteA(HWND hwnd, LPCSTR lpOp, LPCSTR lpFile,
  690. LPCSTR lpArgs, LPCSTR lpDir, LPSTR lpResult,
  691. LPCSTR lpTitle, LPSTR lpReserved,
  692. WORD nShowCmd, LPHANDLE lphProcess)
  693. {
  694. TraceMsg(TF_SHELLEXEC, "RealShellExecuteA(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX)",
  695. hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
  696. lpReserved, nShowCmd, lphProcess);
  697. return RealShellExecuteExA(hwnd,lpOp,lpFile,lpArgs,lpDir,lpResult,lpTitle,lpReserved,nShowCmd,lphProcess,0);
  698. }
  699. HINSTANCE RealShellExecuteW(HWND hwnd, LPCWSTR lpOp, LPCWSTR lpFile,
  700. LPCWSTR lpArgs, LPCWSTR lpDir, LPWSTR lpResult,
  701. LPCWSTR lpTitle, LPWSTR lpReserved,
  702. WORD nShowCmd, LPHANDLE lphProcess)
  703. {
  704. TraceMsg(TF_SHELLEXEC, "RealShellExecuteW(%04X, %s, %s, %s, %s, %s, %s, %s, %d, %08lX)",
  705. hwnd, lpOp, lpFile, lpArgs, lpDir, lpResult, lpTitle,
  706. lpReserved, nShowCmd, lphProcess);
  707. return RealShellExecuteExW(hwnd,lpOp,lpFile,lpArgs,lpDir,lpResult,lpTitle,lpReserved,nShowCmd,lphProcess,0);
  708. }
  709. HINSTANCE WINAPI ShellExecute(HWND hwnd, LPCTSTR lpOp, LPCTSTR lpFile, LPCTSTR lpArgs,
  710. LPCTSTR lpDir, int nShowCmd)
  711. {
  712. // NB The FORCENOIDLIST flag stops us from going through the ShellExecPidl()
  713. // code (for backwards compatability with progman).
  714. // DDEWAIT makes us synchronous, and gets around threads without
  715. // msg pumps and ones that are killed immediately after shellexec()
  716. SHELLEXECUTEINFO sei = { sizeof(SHELLEXECUTEINFO), 0, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
  717. ULONG fMask = SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST;
  718. if(!(SHGetAppCompatFlags(ACF_WIN95SHLEXEC) & ACF_WIN95SHLEXEC))
  719. fMask |= SEE_MASK_FLAG_DDEWAIT;
  720. sei.fMask = fMask;
  721. TraceMsg(TF_SHELLEXEC, "ShellExecute(%04X, %s, %s, %s, %s, %d)", hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd);
  722. ShellExecuteEx(&sei);
  723. return sei.hInstApp;
  724. }
  725. HINSTANCE WINAPI ShellExecuteA(HWND hwnd, LPCSTR lpOp, LPCSTR lpFile, LPCSTR lpArgs,
  726. LPCSTR lpDir, int nShowCmd)
  727. {
  728. // NB The FORCENOIDLIST flag stops us from going through the ShellExecPidl()
  729. // code (for backwards compatability with progman).
  730. // DDEWAIT makes us synchronous, and gets around threads without
  731. // msg pumps and ones that are killed immediately after shellexec()
  732. SHELLEXECUTEINFOA sei = { sizeof(SHELLEXECUTEINFOA), 0, hwnd, lpOp, lpFile, lpArgs, lpDir, nShowCmd, NULL};
  733. ULONG fMask = SEE_MASK_FLAG_NO_UI|SEE_MASK_FORCENOIDLIST;
  734. if (!(SHGetAppCompatFlags(ACF_WIN95SHLEXEC) & ACF_WIN95SHLEXEC))
  735. fMask |= SEE_MASK_FLAG_DDEWAIT;
  736. sei.fMask = fMask;
  737. TraceMsg(TF_SHELLEXEC, "ShellExecuteA(%04X, %S, %S, %S, %S, %d)", hwnd,
  738. SAFE_DEBUGSTR(lpOp), SAFE_DEBUGSTR(lpFile), SAFE_DEBUGSTR(lpArgs),
  739. SAFE_DEBUGSTR(lpDir), nShowCmd);
  740. ShellExecuteExA(&sei);
  741. return sei.hInstApp;
  742. }
  743. // Returns TRUE if the specified app is listed under the specified key
  744. STDAPI_(BOOL) IsNameListedUnderKey(LPCTSTR pszFileName, LPCTSTR pszKey)
  745. {
  746. HKEY hkey;
  747. // Enum through the list of apps.
  748. if (RegOpenKeyEx(HKEY_CURRENT_USER, pszKey, 0, KEY_READ, &hkey) == ERROR_SUCCESS)
  749. {
  750. TCHAR szValue[MAX_PATH], szData[MAX_PATH];
  751. DWORD dwType, cbData = sizeof(szData);
  752. DWORD cchValue = ARRAYSIZE(szValue);
  753. int iValue = 0;
  754. while (RegEnumValue(hkey, iValue, szValue, &cchValue, NULL, &dwType,
  755. (LPBYTE)szData, &cbData) == ERROR_SUCCESS)
  756. {
  757. if (lstrcmpi(szData, pszFileName) == 0)
  758. {
  759. RegCloseKey(hkey);
  760. return TRUE;
  761. }
  762. cbData = sizeof(szData);
  763. cchValue = ARRAYSIZE(szValue);
  764. iValue++;
  765. }
  766. RegCloseKey(hkey);
  767. }
  768. return FALSE;
  769. }
  770. #define REGSTR_PATH_POLICIES_EXPLORER REGSTR_PATH_POLICIES TEXT("\\Explorer\\RestrictRun")
  771. #define REGSTR_PATH_POLICIES_EXPLORER_DISALLOW REGSTR_PATH_POLICIES TEXT("\\Explorer\\DisallowRun")
  772. //----------------------------------------------------------------------------
  773. // Returns TRUE if the specified app is not on the list of unrestricted apps.
  774. BOOL RestrictedApp(LPCTSTR pszApp)
  775. {
  776. LPTSTR pszFileName = PathFindFileName(pszApp);
  777. TraceMsg(TF_SHELLEXEC, "RestrictedApp: %s ", pszFileName);
  778. // Special cases:
  779. // Apps you can always run.
  780. if (lstrcmpi(pszFileName, c_szRunDll) == 0)
  781. return FALSE;
  782. if (lstrcmpi(pszFileName, TEXT("systray.exe")) == 0)
  783. return FALSE;
  784. return !IsNameListedUnderKey(pszFileName, REGSTR_PATH_POLICIES_EXPLORER);
  785. }
  786. //----------------------------------------------------------------------------
  787. // Returns TRUE if the specified app is on the list of disallowed apps.
  788. // not much safety gained from filename checking.
  789. BOOL DisallowedApp(LPCTSTR pszApp)
  790. {
  791. LPTSTR pszFileName = PathFindFileName(pszApp);
  792. TraceMsg(TF_SHELLEXEC, "DisallowedApp: %s ", pszFileName);
  793. return IsNameListedUnderKey(pszFileName, REGSTR_PATH_POLICIES_EXPLORER_DISALLOW);
  794. }
  795. /*
  796. * Returns:
  797. * S_OK or error.
  798. * *phrHook is hook result if S_OK is returned, otherwise it is S_FALSE.
  799. */
  800. HRESULT InvokeShellExecuteHook(REFGUID clsidHook, LPSHELLEXECUTEINFO pei, HRESULT *phrHook)
  801. {
  802. *phrHook = S_FALSE;
  803. IUnknown *punk;
  804. HRESULT hr = SHExtCoCreateInstance(NULL, &clsidHook, NULL, IID_PPV_ARG(IUnknown, &punk));
  805. if (hr == S_OK)
  806. {
  807. IShellExecuteHook *pshexhk;
  808. hr = punk->QueryInterface(IID_PPV_ARG(IShellExecuteHook, &pshexhk));
  809. if (hr == S_OK)
  810. {
  811. *phrHook = pshexhk->Execute(pei);
  812. pshexhk->Release();
  813. }
  814. else
  815. {
  816. IShellExecuteHookA *pshexhkA;
  817. hr = punk->QueryInterface(IID_PPV_ARG(IShellExecuteHookA, &pshexhkA));
  818. if (SUCCEEDED(hr))
  819. {
  820. SHELLEXECUTEINFOA seia;
  821. UINT cchVerb = 0;
  822. UINT cchFile = 0;
  823. UINT cchParameters = 0;
  824. UINT cchDirectory = 0;
  825. UINT cchClass = 0;
  826. LPSTR lpszBuffer;
  827. seia = *(SHELLEXECUTEINFOA*)pei; // Copy all of the binary data
  828. if (pei->lpVerb)
  829. {
  830. cchVerb = WideCharToMultiByte(CP_ACP,0,
  831. pei->lpVerb, -1,
  832. NULL, 0,
  833. NULL, NULL) + 1;
  834. }
  835. if (pei->lpFile)
  836. cchFile = WideCharToMultiByte(CP_ACP,0,
  837. pei->lpFile, -1,
  838. NULL, 0,
  839. NULL, NULL)+1;
  840. if (pei->lpParameters)
  841. cchParameters = WideCharToMultiByte(CP_ACP,0,
  842. pei->lpParameters, -1,
  843. NULL, 0,
  844. NULL, NULL)+1;
  845. if (pei->lpDirectory)
  846. cchDirectory = WideCharToMultiByte(CP_ACP,0,
  847. pei->lpDirectory, -1,
  848. NULL, 0,
  849. NULL, NULL)+1;
  850. if (_UseClassName(pei->fMask) && pei->lpClass)
  851. cchClass = WideCharToMultiByte(CP_ACP,0,
  852. pei->lpClass, -1,
  853. NULL, 0,
  854. NULL, NULL)+1;
  855. lpszBuffer = (LPSTR) alloca(cchVerb+cchFile+cchParameters+cchDirectory+cchClass);
  856. seia.lpVerb = NULL;
  857. seia.lpFile = NULL;
  858. seia.lpParameters = NULL;
  859. seia.lpDirectory = NULL;
  860. seia.lpClass = NULL;
  861. //
  862. // Convert all of the strings to ANSI
  863. //
  864. if (pei->lpVerb)
  865. {
  866. WideCharToMultiByte(CP_ACP, 0, pei->lpVerb, -1,
  867. lpszBuffer, cchVerb, NULL, NULL);
  868. seia.lpVerb = lpszBuffer;
  869. lpszBuffer += cchVerb;
  870. }
  871. if (pei->lpFile)
  872. {
  873. WideCharToMultiByte(CP_ACP, 0, pei->lpFile, -1,
  874. lpszBuffer, cchFile, NULL, NULL);
  875. seia.lpFile = lpszBuffer;
  876. lpszBuffer += cchFile;
  877. }
  878. if (pei->lpParameters)
  879. {
  880. WideCharToMultiByte(CP_ACP, 0,
  881. pei->lpParameters, -1,
  882. lpszBuffer, cchParameters, NULL, NULL);
  883. seia.lpParameters = lpszBuffer;
  884. lpszBuffer += cchParameters;
  885. }
  886. if (pei->lpDirectory)
  887. {
  888. WideCharToMultiByte(CP_ACP, 0,
  889. pei->lpDirectory, -1,
  890. lpszBuffer, cchDirectory, NULL, NULL);
  891. seia.lpDirectory = lpszBuffer;
  892. lpszBuffer += cchDirectory;
  893. }
  894. if (_UseClassName(pei->fMask) && pei->lpClass)
  895. {
  896. WideCharToMultiByte(CP_ACP, 0,
  897. pei->lpClass, -1,
  898. lpszBuffer, cchClass, NULL, NULL);
  899. seia.lpClass = lpszBuffer;
  900. }
  901. *phrHook = pshexhkA->Execute(&seia);
  902. pei->hInstApp = seia.hInstApp;
  903. // hook may set hProcess (e.g. CURLExec creates dummy process
  904. // to signal IEAK that IE setup failed -- in browser only mode)
  905. pei->hProcess = seia.hProcess;
  906. pshexhkA->Release();
  907. }
  908. }
  909. punk->Release();
  910. }
  911. return(hr);
  912. }
  913. const TCHAR c_szShellExecuteHooks[] = REGSTR_PATH_EXPLORER TEXT("\\ShellExecuteHooks");
  914. /*
  915. * Returns:
  916. * S_OK Execution handled by hook. pei->hInstApp filled in.
  917. * S_FALSE Execution not handled by hook. pei->hInstApp not filled in.
  918. * E_... Error during execution by hook. pei->hInstApp filled in.
  919. */
  920. HRESULT TryShellExecuteHooks(LPSHELLEXECUTEINFO pei)
  921. {
  922. HRESULT hr = S_FALSE;
  923. HKEY hkeyHooks;
  924. // Enumerate the list of hooks. A hook is registered as a GUID value of the
  925. // c_szShellExecuteHooks key.
  926. if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, c_szShellExecuteHooks, 0, KEY_READ, &hkeyHooks)
  927. == ERROR_SUCCESS)
  928. {
  929. DWORD dwiValue;
  930. TCHAR szCLSID[GUIDSTR_MAX];
  931. DWORD cchCLSID;
  932. // Invoke each hook. A hook returns S_FALSE if it does not handle the
  933. // exec. Stop when a hook returns S_OK (handled) or an error.
  934. for (cchCLSID = ARRAYSIZE(szCLSID), dwiValue = 0;
  935. RegEnumValue(hkeyHooks, dwiValue, szCLSID, &cchCLSID, NULL,
  936. NULL, NULL, NULL) == ERROR_SUCCESS;
  937. cchCLSID = ARRAYSIZE(szCLSID), dwiValue++)
  938. {
  939. CLSID clsidHook;
  940. if (SUCCEEDED(SHCLSIDFromString(szCLSID, &clsidHook)))
  941. {
  942. HRESULT hrHook;
  943. if (InvokeShellExecuteHook(clsidHook, pei, &hrHook) == S_OK &&
  944. hrHook != S_FALSE)
  945. {
  946. hr = hrHook;
  947. break;
  948. }
  949. }
  950. }
  951. RegCloseKey(hkeyHooks);
  952. }
  953. ASSERT(hr == S_FALSE ||
  954. (hr == S_OK && ISSHELLEXECSUCCEEDED(pei->hInstApp)) ||
  955. (FAILED(hr) && ! ISSHELLEXECSUCCEEDED(pei->hInstApp)));
  956. return(hr);
  957. }
  958. BOOL InRunDllProcess(void)
  959. {
  960. static BOOL s_fInRunDll = -1;
  961. if (-1 == s_fInRunDll)
  962. {
  963. TCHAR sz[MAX_PATH];
  964. s_fInRunDll = FALSE;
  965. if (GetModuleFileName(NULL, sz, SIZECHARS(sz)))
  966. {
  967. //
  968. // WARNING - rundll often seems to fail to add the DDEWAIT flag, and
  969. // it often needs to since it is common to use rundll as a fire
  970. // and forget process, and it exits too early.
  971. //
  972. // note: this changes DDE flags for any app with "rundll" in its name
  973. // shouldnt be a big deal from a security point of view.
  974. if (StrStrI(sz, TEXT("rundll")))
  975. s_fInRunDll = TRUE;
  976. }
  977. }
  978. return s_fInRunDll;
  979. }
  980. #ifdef DEBUG
  981. /*----------------------------------------------------------
  982. Purpose: Validation function for SHELLEXECUTEINFO
  983. */
  984. BOOL IsValidPSHELLEXECUTEINFO(LPSHELLEXECUTEINFO pei)
  985. {
  986. //
  987. // Note that we do *NOT* validate hInstApp, for several reasons.
  988. //
  989. // 1. It is an OUT parameter, not an IN parameter.
  990. // 2. It often contains an error code (see documentation).
  991. // 3. Even when it contains an HINSTANCE, it's an HINSTANCE
  992. // in another process, so we can't validate it anyway.
  993. //
  994. return (IS_VALID_WRITE_PTR(pei, SHELLEXECUTEINFO) &&
  995. IS_VALID_SIZE(pei->cbSize, sizeof(*pei)) &&
  996. (IsFlagSet(pei->fMask, SEE_MASK_FLAG_NO_UI) ||
  997. NULL == pei->hwnd ||
  998. IS_VALID_HANDLE(pei->hwnd, WND)) &&
  999. (NULL == pei->lpVerb || IS_VALID_STRING_PTR(pei->lpVerb, -1)) &&
  1000. (NULL == pei->lpFile || IS_VALID_STRING_PTR(pei->lpFile, -1)) &&
  1001. (NULL == pei->lpParameters || IS_VALID_STRING_PTR(pei->lpParameters, -1)) &&
  1002. (NULL == pei->lpDirectory || IS_VALID_STRING_PTR(pei->lpDirectory, -1)) &&
  1003. (IsFlagClear(pei->fMask, SEE_MASK_IDLIST) ||
  1004. IsFlagSet(pei->fMask, SEE_MASK_INVOKEIDLIST) || // because SEE_MASK_IDLIST is part of SEE_MASK_INVOKEIDLIST this line will
  1005. IS_VALID_PIDL((LPCITEMIDLIST)(pei->lpIDList))) && // defer to the next clause if the superset is true
  1006. (IsFlagClear(pei->fMask, SEE_MASK_INVOKEIDLIST) ||
  1007. NULL == pei->lpIDList ||
  1008. IS_VALID_PIDL((LPCITEMIDLIST)(pei->lpIDList))) &&
  1009. (!_UseClassName(pei->fMask) ||
  1010. IS_VALID_STRING_PTR(pei->lpClass, -1)) &&
  1011. (!_UseTitleName(pei->fMask) ||
  1012. NULL == pei->lpClass ||
  1013. IS_VALID_STRING_PTR(pei->lpClass, -1)) &&
  1014. (!_UseClassKey(pei->fMask) ||
  1015. IS_VALID_HANDLE(pei->hkeyClass, KEY)) &&
  1016. (IsFlagClear(pei->fMask, SEE_MASK_ICON) ||
  1017. IS_VALID_HANDLE(pei->hIcon, ICON)));
  1018. }
  1019. #endif // DEBUG
  1020. //
  1021. // ShellExecuteEx
  1022. //
  1023. // returns TRUE if the execute succeeded, in which case
  1024. // hInstApp should be the hinstance of the app executed (>32)
  1025. // NOTE: in some cases the HINSTANCE cannot (currently) be determined.
  1026. // In these cases, hInstApp is set to 42.
  1027. //
  1028. // returns FALSE if the execute did not succeed, in which case
  1029. // GetLastError will contain error information
  1030. // For backwards compatibility, hInstApp will contain the
  1031. // best SE_ERR_ error information (<=32) possible.
  1032. //
  1033. BOOL WINAPI ShellExecuteEx(LPSHELLEXECUTEINFO pei)
  1034. {
  1035. DWORD err = NOERROR;
  1036. // Don't overreact if CoInitializeEx fails; it just means we
  1037. // can't do our shell hooks.
  1038. HRESULT hrInit = SHCoInitialize();
  1039. if (IS_VALID_STRUCT_PTR(pei, SHELLEXECUTEINFO) &&
  1040. sizeof(*pei) == pei->cbSize)
  1041. {
  1042. // This internal bit prevents error message box reporting
  1043. // when we recurse back into ShellExecuteEx
  1044. ULONG ulOriginalMask = pei->fMask;
  1045. pei->fMask |= SEE_MASK_FLAG_SHELLEXEC;
  1046. if (SHRegGetBoolUSValue(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"), TEXT("MaximizeApps"),
  1047. FALSE, FALSE)) // && (GetSystemMetrics(SM_CYSCREEN)<=600))
  1048. {
  1049. switch (pei->nShow)
  1050. {
  1051. case SW_NORMAL:
  1052. case SW_SHOW:
  1053. case SW_RESTORE:
  1054. case SW_SHOWDEFAULT:
  1055. pei->nShow = SW_MAXIMIZE;
  1056. }
  1057. }
  1058. if (!(pei->fMask & SEE_MASK_FLAG_DDEWAIT) && InRunDllProcess())
  1059. {
  1060. //
  1061. // WARNING - rundll often seems to fail to add the DDEWAIT flag, and
  1062. // it often needs to since it is common to use rundll as a fire
  1063. // and forget process, and it exits too early.
  1064. //
  1065. pei->fMask |= (SEE_MASK_FLAG_DDEWAIT | SEE_MASK_WAITFORINPUTIDLE);
  1066. }
  1067. // ShellExecuteNormal does its own SetLastError
  1068. err = ShellExecuteNormal(pei);
  1069. // Mike's attempt to be consistent in error reporting:
  1070. if (err != ERROR_SUCCESS)
  1071. {
  1072. // we shouldn't put up errors on dll's not found.
  1073. // this is handled WITHIN shellexecuteNormal because
  1074. // sometimes kernel will put up the message for us, and sometimes
  1075. // we need to. we've put the curtion at ShellExecuteNormal
  1076. // LEGACY - ERROR_RESTRICTED_APP was never mapped to a valid error - ZekeL 2001-FEB-14
  1077. // because we always called _ShellExecuteError() before
  1078. // resetting the mask to ulOriginalMask, we never mapped
  1079. // ERROR_RESTRICTED_APP (which is -1) to a valid code
  1080. if (err != ERROR_DLL_NOT_FOUND &&
  1081. err != ERROR_CANCELLED)
  1082. {
  1083. _ShellExecuteError(pei, NULL, err);
  1084. }
  1085. }
  1086. pei->fMask = ulOriginalMask;
  1087. }
  1088. else
  1089. {
  1090. // Failed parameter validation
  1091. pei->hInstApp = (HINSTANCE)SE_ERR_ACCESSDENIED;
  1092. err = ERROR_ACCESS_DENIED;
  1093. }
  1094. SHCoUninitialize(hrInit);
  1095. if (err != ERROR_SUCCESS)
  1096. SetLastError(err);
  1097. return err == ERROR_SUCCESS;
  1098. }
  1099. //+-------------------------------------------------------------------------
  1100. //
  1101. // Function: ShellExecuteExA
  1102. //
  1103. // Synopsis: Thunks ANSI call to ShellExecuteA to ShellExecuteW
  1104. //
  1105. // Arguments: [pei] -- pointer to an ANSI SHELLEXECUTINFO struct
  1106. //
  1107. // Returns: BOOL success value
  1108. //
  1109. // History: 2-04-95 bobday Created
  1110. // 2-06-95 davepl Changed to ConvertStrings
  1111. //
  1112. // Notes:
  1113. //
  1114. //--------------------------------------------------------------------------
  1115. inline BOOL _ThunkClass(ULONG fMask)
  1116. {
  1117. return (fMask & SEE_MASK_HASLINKNAME)
  1118. || (fMask & SEE_MASK_HASTITLE)
  1119. || _UseClassName(fMask);
  1120. }
  1121. BOOL WINAPI ShellExecuteExA(LPSHELLEXECUTEINFOA pei)
  1122. {
  1123. if (pei->cbSize != sizeof(SHELLEXECUTEINFOA))
  1124. {
  1125. pei->hInstApp = (HINSTANCE)SE_ERR_ACCESSDENIED;
  1126. SetLastError(ERROR_ACCESS_DENIED);
  1127. return FALSE;
  1128. }
  1129. SHELLEXECUTEINFOW seiw = {0};
  1130. seiw.cbSize = sizeof(SHELLEXECUTEINFOW);
  1131. seiw.fMask = pei->fMask;
  1132. seiw.hwnd = pei->hwnd;
  1133. seiw.nShow = pei->nShow;
  1134. if (_UseClassKey(pei->fMask))
  1135. seiw.hkeyClass = pei->hkeyClass;
  1136. if (pei->fMask & SEE_MASK_IDLIST)
  1137. seiw.lpIDList = pei->lpIDList;
  1138. if (pei->fMask & SEE_MASK_HOTKEY)
  1139. seiw.dwHotKey = pei->dwHotKey;
  1140. if (pei->fMask & SEE_MASK_ICON)
  1141. seiw.hIcon = pei->hIcon;
  1142. // Thunk the text fields as appropriate
  1143. ThunkText *pThunkText = ConvertStrings(6,
  1144. pei->lpVerb,
  1145. pei->lpFile,
  1146. pei->lpParameters,
  1147. pei->lpDirectory,
  1148. _ThunkClass(pei->fMask) ? pei->lpClass : NULL,
  1149. (pei->fMask & SEE_MASK_RESERVED) ? pei->hInstApp : NULL);
  1150. if (NULL == pThunkText)
  1151. {
  1152. pei->hInstApp = (HINSTANCE)SE_ERR_OOM;
  1153. return FALSE;
  1154. }
  1155. // Set our UNICODE text fields to point to the thunked strings
  1156. seiw.lpVerb = pThunkText->m_pStr[0];
  1157. seiw.lpFile = pThunkText->m_pStr[1];
  1158. seiw.lpParameters = pThunkText->m_pStr[2];
  1159. seiw.lpDirectory = pThunkText->m_pStr[3];
  1160. seiw.lpClass = pThunkText->m_pStr[4];
  1161. seiw.hInstApp = (HINSTANCE)pThunkText->m_pStr[5];
  1162. // If we are passed the SEE_MASK_FILEANDURL flag, this means that
  1163. // we have a lpFile parameter that has both the CacheFilename and the URL
  1164. // (seperated by a single NULL, eg. "CacheFileName\0UrlName). We therefore
  1165. // need to special case the thunking of pei->lpFile.
  1166. LPWSTR pwszFileAndUrl = NULL;
  1167. if (pei->fMask & SEE_MASK_FILEANDURL)
  1168. {
  1169. int iUrlLength;
  1170. int iCacheFileLength = lstrlenW(pThunkText->m_pStr[1]);
  1171. WCHAR wszURL[INTERNET_MAX_URL_LENGTH];
  1172. LPSTR pszUrlPart = (LPSTR)&pei->lpFile[iCacheFileLength + 1];
  1173. if (IsBadStringPtrA(pszUrlPart, INTERNET_MAX_URL_LENGTH) || !PathIsURLA(pszUrlPart))
  1174. {
  1175. ASSERT(FALSE);
  1176. }
  1177. else
  1178. {
  1179. // we have a vaild URL, so thunk it
  1180. iUrlLength = lstrlenA(pszUrlPart);
  1181. DWORD cchFileAndUrl = iUrlLength + iCacheFileLength + 2;
  1182. pwszFileAndUrl = (LPWSTR)LocalAlloc(LPTR, cchFileAndUrl * sizeof(WCHAR));
  1183. if (!pwszFileAndUrl)
  1184. {
  1185. pei->hInstApp = (HINSTANCE)SE_ERR_OOM;
  1186. return FALSE;
  1187. }
  1188. SHAnsiToUnicode(pszUrlPart, wszURL, ARRAYSIZE(wszURL));
  1189. // construct the wide multi-string
  1190. StrCpyNW(pwszFileAndUrl, pThunkText->m_pStr[1], cchFileAndUrl);
  1191. StrCpyNW(&pwszFileAndUrl[iCacheFileLength + 1], wszURL, cchFileAndUrl - (iCacheFileLength + 1));
  1192. seiw.lpFile = pwszFileAndUrl;
  1193. }
  1194. }
  1195. // Call the real UNICODE ShellExecuteEx
  1196. BOOL fRet = ShellExecuteEx(&seiw);
  1197. pei->hInstApp = seiw.hInstApp;
  1198. if (pei->fMask & SEE_MASK_NOCLOSEPROCESS)
  1199. {
  1200. pei->hProcess = seiw.hProcess;
  1201. }
  1202. LocalFree(pThunkText);
  1203. if (pwszFileAndUrl)
  1204. LocalFree(pwszFileAndUrl);
  1205. return fRet;
  1206. }
  1207. // To display an error message appropriately, call this if ShellExecuteEx fails.
  1208. void _DisplayShellExecError(ULONG fMask, HWND hwnd, LPCTSTR pszFile, LPCTSTR pszTitle, DWORD dwErr)
  1209. {
  1210. if (!(fMask & SEE_MASK_FLAG_NO_UI))
  1211. {
  1212. if (dwErr != ERROR_CANCELLED)
  1213. {
  1214. LPCTSTR pszHeader;
  1215. UINT ids;
  1216. // don't display "user cancelled", the user knows that already
  1217. // make sure parent window is the foreground window
  1218. if (hwnd)
  1219. SetForegroundWindow(hwnd);
  1220. if (pszTitle)
  1221. pszHeader = pszTitle;
  1222. else
  1223. pszHeader = pszFile;
  1224. // Use our messages when we can -- they're more descriptive
  1225. switch (dwErr)
  1226. {
  1227. case 0:
  1228. case ERROR_NOT_ENOUGH_MEMORY:
  1229. case ERROR_OUTOFMEMORY:
  1230. ids = IDS_LowMemError;
  1231. break;
  1232. case ERROR_FILE_NOT_FOUND:
  1233. ids = IDS_RunFileNotFound;
  1234. break;
  1235. case ERROR_PATH_NOT_FOUND:
  1236. case ERROR_BAD_PATHNAME:
  1237. ids = IDS_PathNotFound;
  1238. break;
  1239. case ERROR_TOO_MANY_OPEN_FILES:
  1240. ids = IDS_TooManyOpenFiles;
  1241. break;
  1242. case ERROR_ACCESS_DENIED:
  1243. ids = IDS_RunAccessDenied;
  1244. break;
  1245. case ERROR_BAD_FORMAT:
  1246. // NB CreateProcess, when execing a Win16 apps maps just about all of
  1247. // these errors to BadFormat. Not very useful but there it is.
  1248. ids = IDS_BadFormat;
  1249. break;
  1250. case ERROR_SHARING_VIOLATION:
  1251. ids = IDS_ShareError;
  1252. break;
  1253. case ERROR_OLD_WIN_VERSION:
  1254. ids = IDS_OldWindowsVer;
  1255. break;
  1256. case ERROR_APP_WRONG_OS:
  1257. ids = IDS_OS2AppError;
  1258. break;
  1259. case ERROR_SINGLE_INSTANCE_APP:
  1260. ids = IDS_MultipleDS;
  1261. break;
  1262. case ERROR_RMODE_APP:
  1263. ids = IDS_RModeApp;
  1264. break;
  1265. case ERROR_INVALID_DLL:
  1266. ids = IDS_InvalidDLL;
  1267. break;
  1268. case ERROR_NO_ASSOCIATION:
  1269. ids = IDS_NoAssocError;
  1270. break;
  1271. case ERROR_DDE_FAIL:
  1272. ids = IDS_DDEFailError;
  1273. break;
  1274. case ERROR_BAD_NET_NAME:
  1275. case ERROR_SEM_TIMEOUT:
  1276. ids = IDS_REASONS_BADNETNAME;
  1277. break;
  1278. // LEGACY - ERROR_RESTRICTED_APP was never mapped to a valid error - ZekeL 2001-FEB-14
  1279. // because we always called _ShellExecuteError() before
  1280. // resetting the mask to ulOriginalMask, we never mapped
  1281. // ERROR_RESTRICTED_APP (which is -1) to a valid code
  1282. case ERROR_RESTRICTED_APP:
  1283. ids = IDS_RESTRICTIONS;
  1284. // restrictions like to use IDS_RESTRICTIONSTITLE
  1285. if (!pszTitle)
  1286. pszHeader = MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE);
  1287. break;
  1288. // If we don't get a match, let the system handle it for us
  1289. default:
  1290. ids = 0;
  1291. SHSysErrorMessageBox(
  1292. hwnd,
  1293. pszHeader,
  1294. IDS_SHLEXEC_ERROR,
  1295. dwErr,
  1296. pszFile,
  1297. MB_OK | MB_ICONSTOP);
  1298. break;
  1299. }
  1300. if (ids)
  1301. {
  1302. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(ids),
  1303. pszHeader, (ids == IDS_LowMemError)?
  1304. (MB_OK | MB_ICONSTOP | MB_SYSTEMMODAL):(MB_OK | MB_ICONSTOP),
  1305. pszFile);
  1306. }
  1307. }
  1308. }
  1309. SetLastError(dwErr); // The message box may have clobbered.
  1310. }
  1311. void _ShellExecuteError(LPSHELLEXECUTEINFO pei, LPCTSTR lpTitle, DWORD dwErr)
  1312. {
  1313. ASSERT(!ISSHELLEXECSUCCEEDED(pei->hInstApp));
  1314. // if dwErr not passed in, get it
  1315. if (dwErr == 0)
  1316. dwErr = GetLastError();
  1317. _DisplayShellExecError(pei->fMask, pei->hwnd, pei->lpFile, lpTitle, dwErr);
  1318. }
  1319. //----------------------------------------------------------------------------
  1320. // Given a file name and directory, get the path to the execuatable that
  1321. // would be exec'd if you tried to ShellExecute this thing.
  1322. HINSTANCE WINAPI FindExecutable(LPCTSTR lpFile, LPCTSTR lpDirectory, LPTSTR lpResult)
  1323. {
  1324. HINSTANCE hInstance = (HINSTANCE)42; // assume success must be > 32
  1325. TCHAR szOldDir[MAX_PATH];
  1326. TCHAR szFile[MAX_PATH];
  1327. LPCTSTR dirs[2];
  1328. // Progman relies on lpResult being a ptr to an null string on error.
  1329. *lpResult = TEXT('\0');
  1330. GetCurrentDirectory(ARRAYSIZE(szOldDir), szOldDir);
  1331. if (lpDirectory && *lpDirectory)
  1332. SetCurrentDirectory(lpDirectory);
  1333. else
  1334. lpDirectory = szOldDir; // needed for PathResolve()
  1335. if (!GetShortPathName(lpFile, szFile, ARRAYSIZE(szFile))) {
  1336. // if the lpFile is unqualified or bogus, let's use it down
  1337. // in PathResolve.
  1338. lstrcpyn(szFile, lpFile, ARRAYSIZE(szFile));
  1339. }
  1340. // get fully qualified path and add .exe extension if needed
  1341. dirs[0] = (LPTSTR)lpDirectory;
  1342. dirs[1] = NULL;
  1343. if (!PathResolve(szFile, dirs, PRF_VERIFYEXISTS | PRF_TRYPROGRAMEXTENSIONS | PRF_FIRSTDIRDEF))
  1344. {
  1345. // File doesn't exist, return file not found.
  1346. hInstance = (HINSTANCE)SE_ERR_FNF;
  1347. goto Exit;
  1348. }
  1349. TraceMsg(TF_SHELLEXEC, "FindExecutable: PathResolve -> %s", (LPCSTR)szFile);
  1350. if (PathIsExe(szFile))
  1351. {
  1352. // public API, can't change to have cch.
  1353. StrCpyN(lpResult, szFile, MAX_PATH); // assumed length!
  1354. goto Exit;
  1355. }
  1356. if (SUCCEEDED(AssocQueryString(0, ASSOCSTR_EXECUTABLE, szFile, NULL, szFile, (LPDWORD)MAKEINTRESOURCE(SIZECHARS(szFile)))))
  1357. {
  1358. StrCpyN(lpResult, szFile, MAX_PATH); // assumed length!
  1359. }
  1360. else
  1361. {
  1362. hInstance = (HINSTANCE)SE_ERR_NOASSOC;
  1363. }
  1364. Exit:
  1365. TraceMsg(TF_SHELLEXEC, "FindExec(%s) ==> %s", (LPTSTR)lpFile, (LPTSTR)lpResult);
  1366. SetCurrentDirectory(szOldDir);
  1367. return hInstance;
  1368. }
  1369. HINSTANCE WINAPI FindExecutableA(LPCSTR lpFile, LPCSTR lpDirectory, LPSTR lpResult)
  1370. {
  1371. HINSTANCE hResult;
  1372. WCHAR wszResult[MAX_PATH];
  1373. ThunkText * pThunkText = ConvertStrings(2, lpFile, lpDirectory);
  1374. *lpResult = '\0';
  1375. if (NULL == pThunkText)
  1376. {
  1377. return (HINSTANCE)SE_ERR_OOM;
  1378. }
  1379. hResult = FindExecutableW(pThunkText->m_pStr[0], pThunkText->m_pStr[1], wszResult);
  1380. LocalFree(pThunkText);
  1381. // FindExecutableW terminates wszResult for us, so this is safe
  1382. // even if the above call fails
  1383. // Thunk the output result string back to ANSI. If the conversion fails,
  1384. // or if the default char is used, we fail the API call.
  1385. // public API, assume MAX_PATH
  1386. if (0 == WideCharToMultiByte(CP_ACP, 0, wszResult, -1, lpResult, MAX_PATH, NULL, NULL))
  1387. {
  1388. SetLastError((DWORD)E_FAIL);
  1389. return (HINSTANCE) SE_ERR_FNF;
  1390. }
  1391. return hResult;
  1392. }
  1393. //----------------------------------------------------------------------------
  1394. // Data structures for our wait for file open functions
  1395. //
  1396. typedef struct _WaitForItem * PWAITFORITEM;
  1397. typedef struct _WaitForItem
  1398. {
  1399. DWORD dwSize;
  1400. DWORD fOperation; // Operation to perform
  1401. PWAITFORITEM pwfiNext;
  1402. HANDLE hEvent; // Handle to event that was registered.
  1403. UINT iWaiting; // Number of clients that are waiting.
  1404. ITEMIDLIST idlItem; // pidl to wait for
  1405. } WAITFORITEM;
  1406. //
  1407. // This is the form of the structure that is shoved into the shared memory
  1408. // block. It must be the 32-bit version for interoperability reasons.
  1409. //
  1410. typedef struct _WaitForItem32
  1411. {
  1412. DWORD dwSize;
  1413. DWORD fOperation; // Operation to perform
  1414. DWORD NotUsed1;
  1415. LONG hEvent; // Truncated event handle
  1416. UINT NotUsed2;
  1417. ITEMIDLIST idlItem; // pidl to wait for
  1418. } WAITFORITEM32, *PWAITFORITEM32;
  1419. //
  1420. // These macros enforce type safety so people are forced to use the
  1421. // WAITFORITEM32 structure when accessing the shared memory block.
  1422. //
  1423. #define SHLockWaitForItem(h, pid) ((PWAITFORITEM32)SHLockShared(h, pid))
  1424. __inline void SHUnlockWaitForItem(PWAITFORITEM32 pwfi)
  1425. {
  1426. SHUnlockShared(pwfi);
  1427. }
  1428. PWAITFORITEM g_pwfiHead = NULL;
  1429. HANDLE SHWaitOp_OperateInternal(DWORD fOperation, LPCITEMIDLIST pidlItem)
  1430. {
  1431. PWAITFORITEM pwfi;
  1432. HANDLE hEvent = (HANDLE)NULL;
  1433. for (pwfi = g_pwfiHead; pwfi != NULL; pwfi = pwfi->pwfiNext)
  1434. {
  1435. if (ILIsEqual(&(pwfi->idlItem), pidlItem))
  1436. {
  1437. hEvent = pwfi->hEvent;
  1438. break;
  1439. }
  1440. }
  1441. if (fOperation & WFFO_ADD)
  1442. {
  1443. if (!pwfi)
  1444. {
  1445. UINT uSize;
  1446. UINT uSizeIDList = 0;
  1447. if (pidlItem)
  1448. uSizeIDList = ILGetSize(pidlItem);
  1449. uSize = sizeof(WAITFORITEM) + uSizeIDList;
  1450. // Create an event to wait for
  1451. hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  1452. if (hEvent)
  1453. pwfi = (PWAITFORITEM)SHAlloc(uSize);
  1454. if (pwfi)
  1455. {
  1456. pwfi->dwSize = uSize;
  1457. // pwfi->fOperation = 0; // Meaningless
  1458. pwfi->hEvent = hEvent;
  1459. pwfi->iWaiting = ((fOperation & WFFO_WAIT) != 0);
  1460. memcpy(&(pwfi->idlItem), pidlItem, uSizeIDList);
  1461. // now link it in
  1462. pwfi->pwfiNext = g_pwfiHead;
  1463. g_pwfiHead = pwfi;
  1464. }
  1465. }
  1466. }
  1467. if (pwfi)
  1468. {
  1469. if (fOperation & WFFO_WAIT)
  1470. pwfi->iWaiting++;
  1471. if (fOperation & WFFO_SIGNAL)
  1472. SetEvent(hEvent);
  1473. if (fOperation & WFFO_REMOVE)
  1474. pwfi->iWaiting--; // decrement in use count;
  1475. // Only check removal case if not adding
  1476. if ((fOperation & WFFO_ADD) == 0)
  1477. {
  1478. // Remove it if nobody waiting on it
  1479. if (pwfi->iWaiting == 0)
  1480. {
  1481. if (g_pwfiHead == pwfi)
  1482. g_pwfiHead = pwfi->pwfiNext;
  1483. else
  1484. {
  1485. PWAITFORITEM pwfiT = g_pwfiHead;
  1486. while ((pwfiT != NULL) && (pwfiT->pwfiNext != pwfi))
  1487. pwfiT = pwfiT->pwfiNext;
  1488. ASSERT(pwfiT != NULL);
  1489. if (pwfiT != NULL)
  1490. pwfiT->pwfiNext = pwfi->pwfiNext;
  1491. }
  1492. // Close the handle
  1493. CloseHandle(pwfi->hEvent);
  1494. // Free the memory
  1495. SHFree(pwfi);
  1496. hEvent = NULL; // NULL indicates nobody waiting... (for remove case)
  1497. }
  1498. }
  1499. }
  1500. return hEvent;
  1501. }
  1502. void SHWaitOp_Operate(HANDLE hWait, DWORD dwProcId)
  1503. {
  1504. PWAITFORITEM32 pwfiFind = SHLockWaitForItem(hWait, dwProcId);
  1505. if (pwfiFind)
  1506. {
  1507. pwfiFind->hEvent = HandleToLong(SHWaitOp_OperateInternal(pwfiFind->fOperation, &(pwfiFind->idlItem)));
  1508. SHUnlockWaitForItem(pwfiFind);
  1509. }
  1510. }
  1511. HANDLE SHWaitOp_Create(DWORD fOperation, LPCITEMIDLIST pidlItem, DWORD dwProcId)
  1512. {
  1513. UINT uSizeIDList = pidlItem ? ILGetSize(pidlItem) : 0;
  1514. UINT uSize = sizeof(WAITFORITEM32) + uSizeIDList;
  1515. HANDLE hWaitOp = SHAllocShared(NULL, uSize, dwProcId);
  1516. if (hWaitOp)
  1517. {
  1518. PWAITFORITEM32 pwfi = SHLockWaitForItem(hWaitOp,dwProcId);
  1519. if (pwfi)
  1520. {
  1521. pwfi->dwSize = uSize;
  1522. pwfi->fOperation = fOperation;
  1523. pwfi->NotUsed1 = 0;
  1524. pwfi->hEvent = HandleToLong((HANDLE)NULL);
  1525. pwfi->NotUsed2 = 0;
  1526. if (pidlItem)
  1527. memcpy(&(pwfi->idlItem), pidlItem, uSizeIDList);
  1528. SHUnlockWaitForItem(pwfi);
  1529. }
  1530. else
  1531. {
  1532. // clean up
  1533. SHFreeShared(hWaitOp, dwProcId);
  1534. hWaitOp = NULL;
  1535. }
  1536. }
  1537. return hWaitOp;
  1538. }
  1539. // This function allows the cabinet to wait for a
  1540. // file (in particular folders) to signal us that they are in an open state.
  1541. // This should take care of several synchronazation problems with the shell
  1542. // not knowing when a folder is in the process of being opened or not
  1543. //
  1544. STDAPI_(DWORD) SHWaitForFileToOpen(LPCITEMIDLIST pidl, UINT uOptions, DWORD dwTimeout)
  1545. {
  1546. HWND hwndShell;
  1547. HANDLE hWaitOp;
  1548. HANDLE hEvent = NULL;
  1549. DWORD dwProcIdSrc = GetCurrentProcessId();
  1550. DWORD dwReturn = WAIT_OBJECT_0; // we need a default
  1551. hwndShell = GetShellWindow();
  1552. if ((uOptions & (WFFO_WAIT | WFFO_ADD)) != 0)
  1553. {
  1554. if (hwndShell)
  1555. {
  1556. DWORD dwProcIdDst;
  1557. GetWindowThreadProcessId(hwndShell, &dwProcIdDst);
  1558. // Do just the add and/or wait portions
  1559. hWaitOp = SHWaitOp_Create(uOptions & (WFFO_WAIT | WFFO_ADD), pidl, dwProcIdSrc);
  1560. if (hWaitOp)
  1561. {
  1562. SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcIdSrc);
  1563. // Now get the hEvent and convert to a local handle
  1564. PWAITFORITEM32 pwfi = SHLockWaitForItem(hWaitOp, dwProcIdSrc);
  1565. if (pwfi)
  1566. {
  1567. hEvent = SHMapHandle(LongToHandle(pwfi->hEvent),dwProcIdDst, dwProcIdSrc, EVENT_ALL_ACCESS, 0);
  1568. SHUnlockWaitForItem(pwfi);
  1569. }
  1570. SHFreeShared(hWaitOp,dwProcIdSrc);
  1571. }
  1572. }
  1573. else
  1574. {
  1575. // Do just the add and/or wait portions
  1576. hEvent = SHWaitOp_OperateInternal(uOptions & (WFFO_WAIT | WFFO_ADD), pidl);
  1577. }
  1578. if (hEvent)
  1579. {
  1580. if (uOptions & WFFO_WAIT)
  1581. dwReturn = SHProcessMessagesUntilEvent(NULL, hEvent, dwTimeout);
  1582. if (hwndShell) // Close the duplicated handle.
  1583. CloseHandle(hEvent);
  1584. }
  1585. }
  1586. if (uOptions & WFFO_REMOVE)
  1587. {
  1588. if (hwndShell)
  1589. {
  1590. hWaitOp = SHWaitOp_Create(WFFO_REMOVE, pidl, dwProcIdSrc);
  1591. if (hWaitOp)
  1592. {
  1593. SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcIdSrc);
  1594. SHFreeShared(hWaitOp,dwProcIdSrc);
  1595. }
  1596. }
  1597. else
  1598. {
  1599. SHWaitOp_OperateInternal(WFFO_REMOVE, pidl);
  1600. }
  1601. }
  1602. return dwReturn;
  1603. }
  1604. // Signals that the file is open
  1605. //
  1606. STDAPI_(BOOL) SignalFileOpen(LPCITEMIDLIST pidl)
  1607. {
  1608. BOOL fResult = FALSE;
  1609. HWND hwndShell = GetShellWindow();
  1610. if (hwndShell)
  1611. {
  1612. PWAITFORITEM32 pwfi;
  1613. DWORD dwProcId = GetCurrentProcessId();
  1614. HANDLE hWaitOp = SHWaitOp_Create(WFFO_SIGNAL, pidl, dwProcId);
  1615. if (hWaitOp)
  1616. {
  1617. SendMessage(hwndShell, CWM_WAITOP, (WPARAM)hWaitOp, (LPARAM)dwProcId);
  1618. // Now get the hEvent to determine return value...
  1619. pwfi = SHLockWaitForItem(hWaitOp, dwProcId);
  1620. if (pwfi)
  1621. {
  1622. fResult = (LongToHandle(pwfi->hEvent) != (HANDLE)NULL);
  1623. SHUnlockWaitForItem(pwfi);
  1624. }
  1625. SHFreeShared(hWaitOp,dwProcId);
  1626. }
  1627. }
  1628. else
  1629. {
  1630. fResult = (SHWaitOp_OperateInternal(WFFO_SIGNAL, pidl) == (HANDLE)NULL);
  1631. }
  1632. // Let everyone know that we opened something
  1633. UINT uMsg = RegisterWindowMessage(SH_FILEOPENED);
  1634. BroadcastSystemMessage(BSF_POSTMESSAGE, BSM_ALLCOMPONENTS, uMsg, NULL, NULL);
  1635. return fResult;
  1636. }
  1637. //
  1638. // Checks to see if darwin is enabled.
  1639. //
  1640. BOOL IsDarwinEnabled()
  1641. {
  1642. static BOOL s_fDarwinEnabled = TRUE;
  1643. static BOOL s_fInit = FALSE;
  1644. if (!s_fInit)
  1645. {
  1646. HKEY hkeyPolicy = 0;
  1647. if (RegOpenKeyEx(HKEY_CURRENT_USER, REGSTR_PATH_POLICIES_EXPLORER, 0, KEY_READ, &hkeyPolicy) == ERROR_SUCCESS)
  1648. {
  1649. if (SHQueryValueEx(hkeyPolicy, TEXT("DisableMSI"), NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
  1650. {
  1651. s_fDarwinEnabled = FALSE; // policy turns MSI off
  1652. }
  1653. RegCloseKey(hkeyPolicy);
  1654. }
  1655. s_fInit = TRUE;
  1656. }
  1657. return s_fDarwinEnabled;
  1658. }
  1659. // takes the darwin ID string from the registry, and calls darwin to get the
  1660. // full path to the application.
  1661. //
  1662. // IN: pszDarwinDescriptor - this has the contents of the darwin key read out of the registry.
  1663. // it should contain a string like "[Darwin-ID-for-App] /switches".
  1664. //
  1665. // OUT: pszDarwinComand - the full path to the application to this buffer w/ switches.
  1666. //
  1667. STDAPI ParseDarwinID(LPTSTR pszDarwinDescriptor, LPTSTR pszDarwinCommand, DWORD cchDarwinCommand)
  1668. {
  1669. DWORD dwError = CommandLineFromMsiDescriptor(pszDarwinDescriptor, pszDarwinCommand, &cchDarwinCommand);
  1670. return HRESULT_FROM_WIN32(dwError);
  1671. }