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.

9358 lines
298 KiB

  1. #include "shellprv.h"
  2. #include "guids.h" // for PRINTER_BIND_INFO
  3. #include "printer.h" // for IPrintersBindInfo
  4. #include "util.h"
  5. #include <advpub.h> // For REGINSTALL
  6. #include <ntverp.h>
  7. #include <urlmon.h>
  8. #include <shlwapi.h>
  9. #include "shldisp.h"
  10. #include <malloc.h>
  11. #include "shitemid.h"
  12. #include "datautil.h"
  13. #include <perhist.h> // IPersistHistory is defined here.
  14. #include "ids.h"
  15. #include "views.h"
  16. #include "ole2dup.h"
  17. #include <vdate.h>
  18. #include <regstr.h>
  19. #include "unicpp\dutil.h"
  20. #include <stdlib.h>
  21. #include "prop.h"
  22. #include "ftascstr.h" // for CFTAssocStore
  23. #include "ftcmmn.h" // for MAX_APPFRIENDLYNAME
  24. #include "ascstr.h" // for IAssocInfo class
  25. #include "fstreex.h" // for CFSFolder_CreateFolder
  26. #include "deskfldr.h"
  27. #include "cscuiext.h"
  28. #include "netview.h" // SHGetNetJoinInformation
  29. #include "mtpt.h"
  30. #include <cscapi.h> // for CSCQueryFileStatus
  31. #include <winsta.h>
  32. #include <dsgetdc.h>
  33. #include <uxtheme.h>
  34. #include <duithread.h>
  35. //The following is defined in shell32\unicpp\dutil.cpp
  36. void GetRegLocation(LPTSTR lpszResult, DWORD cchResult, LPCTSTR lpszKey, LPCTSTR lpszScheme);
  37. #define DM_STRICT TF_WARNING // audit menus, etc.
  38. #define DM_STRICT2 0 // verbose
  39. //
  40. // We need to put this one in per-instance data section because during log-off
  41. // and log-on, this info needs to be re-read from the registry.
  42. //
  43. // REGSHELLSTATE is the version of the SHELLSTATE that goes into the
  44. // registry. When loading a REGSHELLSTATE, you have to study the
  45. // cbSize to see if it's a downlevel structure and upgrade it accordingly.
  46. //
  47. typedef struct
  48. {
  49. UINT cbSize;
  50. SHELLSTATE ss;
  51. } REGSHELLSTATE;
  52. #define REGSHELLSTATE_SIZE_WIN95 (sizeof(UINT)+SHELLSTATE_SIZE_WIN95) // Win95 Gold
  53. #define REGSHELLSTATE_SIZE_NT4 (sizeof(UINT)+SHELLSTATE_SIZE_NT4) // Win95 OSR / NT 4
  54. #define REGSHELLSTATE_SIZE_IE4 (sizeof(UINT)+SHELLSTATE_SIZE_IE4) // IE 4, 4.01
  55. #define REGSHELLSTATE_SIZE_WIN2K (sizeof(UINT)+SHELLSTATE_SIZE_WIN2K) // ie5, win2k, millennium, whistler
  56. // If the SHELLSTATE size changes, we need to add a new define
  57. // above and new upgrade code in SHRefreshSettings
  58. #ifdef DEBUG
  59. void snafu () {COMPILETIME_ASSERT(REGSHELLSTATE_SIZE_WIN2K == sizeof(REGSHELLSTATE));}
  60. #endif DEBUG
  61. REGSHELLSTATE * g_pShellState = 0;
  62. // detect an "empty" sound scheme key. this deals with the NULL case that returns
  63. // "2" as that is enough space for a NULL char
  64. BOOL NonEmptySoundKey(HKEY, LPCTSTR pszKey)
  65. {
  66. LONG cb = 0; // in/out
  67. return (ERROR_SUCCESS == SHRegQueryValue(HKEY_CURRENT_USER, pszKey, NULL, &cb)) && (cb > sizeof(TCHAR));
  68. }
  69. STDAPI_(void) SHPlaySound(LPCTSTR pszSound)
  70. {
  71. TCHAR szKey[CCH_KEYMAX];
  72. // to avoid loading all of the MM system DLLs we check the registry first
  73. // if there's nothing registered, we blow off the play,
  74. wsprintf(szKey, TEXT("AppEvents\\Schemes\\Apps\\Explorer\\%s\\.current"), pszSound);
  75. if (NonEmptySoundKey(HKEY_CURRENT_USER, szKey))
  76. {
  77. PlaySound(pszSound, NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP);
  78. }
  79. else
  80. {
  81. // support system sounds too
  82. wsprintf(szKey, TEXT("AppEvents\\Schemes\\Apps\\.Default\\%s\\.current"), pszSound);
  83. if (NonEmptySoundKey(HKEY_CURRENT_USER, szKey))
  84. {
  85. PlaySound(pszSound, NULL, SND_ALIAS | SND_APPLICATION | SND_ASYNC | SND_NODEFAULT | SND_NOSTOP);
  86. }
  87. }
  88. }
  89. // helper function to set whether shift or control is down at the start of the invoke
  90. // operation, so others down the line can check this instead of calling GetAsyncKeyState themselves
  91. STDAPI_(void) SetICIKeyModifiers(DWORD* pfMask)
  92. {
  93. ASSERT(pfMask);
  94. if (GetKeyState(VK_SHIFT) < 0)
  95. {
  96. *pfMask |= CMIC_MASK_SHIFT_DOWN;
  97. }
  98. if (GetKeyState(VK_CONTROL) < 0)
  99. {
  100. *pfMask |= CMIC_MASK_CONTROL_DOWN;
  101. }
  102. }
  103. // sane way to get the msg pos into a point, mostly needed for win32
  104. void GetMsgPos(POINT *ppt)
  105. {
  106. DWORD dw = GetMessagePos();
  107. ppt->x = GET_X_LPARAM(dw);
  108. ppt->y = GET_Y_LPARAM(dw);
  109. }
  110. /* This gets the number of consecutive chrs of the same kind. This is used
  111. * to parse the time picture. Returns 0 on error.
  112. */
  113. int GetPict(WCHAR ch, LPWSTR wszStr)
  114. {
  115. int count = 0;
  116. while (ch == *wszStr++)
  117. count++;
  118. return count;
  119. }
  120. DWORD CALLBACK _PropSheetThreadProc(void *ppv)
  121. {
  122. PROPSTUFF * pps = (PROPSTUFF *)ppv;
  123. // CoInitializeEx(0, COINIT_MULTITHREADED); // to test stuff in multithread case
  124. HRESULT hrInit = SHOleInitialize(0);
  125. DWORD dwRet = pps->lpStartAddress(pps);
  126. // cleanup
  127. if (pps->pdtobj)
  128. pps->pdtobj->Release();
  129. if (pps->pidlParent)
  130. ILFree(pps->pidlParent);
  131. if (pps->psf)
  132. pps->psf->Release();
  133. LocalFree(pps);
  134. SHOleUninitialize(hrInit);
  135. return dwRet;
  136. }
  137. // reinerf: alpha cpp compiler confused by the type "LPITEMIDLIST", so to work
  138. // around this we pass the last param as an void *instead of a LPITEMIDLIST
  139. //
  140. HRESULT SHLaunchPropSheet(LPTHREAD_START_ROUTINE pStartAddress, IDataObject *pdtobj, LPCTSTR pStartPage, IShellFolder *psf, void *pidl)
  141. {
  142. LPITEMIDLIST pidlParent = (LPITEMIDLIST)pidl;
  143. UINT cbStartPage = !IS_INTRESOURCE(pStartPage) ? ((lstrlen(pStartPage) + 1) * sizeof(*pStartPage)) : 0 ;
  144. PROPSTUFF * pps = (PROPSTUFF *)LocalAlloc(LPTR, sizeof(PROPSTUFF) + cbStartPage);
  145. if (pps)
  146. {
  147. pps->lpStartAddress = pStartAddress;
  148. if (pdtobj)
  149. {
  150. pps->pdtobj = pdtobj;
  151. pdtobj->AddRef();
  152. }
  153. if (pidlParent)
  154. pps->pidlParent = ILClone(pidlParent);
  155. if (psf)
  156. {
  157. pps->psf = psf;
  158. psf->AddRef();
  159. }
  160. pps->pStartPage = pStartPage;
  161. if (!IS_INTRESOURCE(pStartPage))
  162. {
  163. pps->pStartPage = (LPTSTR)(pps + 1);
  164. lstrcpy((LPTSTR)(pps->pStartPage), pStartPage);
  165. }
  166. // _PropSheetThreadProc does not do any modality stuff, so we can't CTF_INSIST
  167. if (SHCreateThread(_PropSheetThreadProc, pps, CTF_PROCESS_REF, NULL))
  168. return S_OK;
  169. }
  170. return E_OUTOFMEMORY;
  171. }
  172. /* This picks up the values in wValArray, converts them
  173. * in a string containing the formatted date.
  174. * wValArray should contain Month-Day-Year (in that order).
  175. */
  176. int CreateDate(WORD *wValArray, LPWSTR wszOutStr)
  177. {
  178. int cchPictPart;
  179. WORD wDigit;
  180. WORD wIndex;
  181. WORD wTempVal;
  182. LPWSTR pwszPict, pwszInStr;
  183. WCHAR wszShortDate[30]; // need more room for LOCALE_SSHORTDATE
  184. GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, wszShortDate, ARRAYSIZE(wszShortDate));
  185. pwszPict = wszShortDate;
  186. pwszInStr = wszOutStr;
  187. for (int i = 0; (i < 5) && (*pwszPict); i++)
  188. {
  189. cchPictPart = GetPict(*pwszPict, pwszPict);
  190. switch (*pwszPict)
  191. {
  192. case TEXT('M'):
  193. case TEXT('m'):
  194. {
  195. wIndex = 0;
  196. break;
  197. }
  198. case TEXT('D'):
  199. case TEXT('d'):
  200. {
  201. //
  202. // if short date style && *pszPict is 'd' &&
  203. // cchPictPart is more than equal 3,
  204. // then it is the day of the week.
  205. //
  206. if (cchPictPart >= 3)
  207. {
  208. pwszPict += cchPictPart;
  209. continue;
  210. }
  211. wIndex = 1;
  212. break;
  213. }
  214. case TEXT('Y'):
  215. case TEXT('y'):
  216. {
  217. wIndex = 2;
  218. if (cchPictPart == 4)
  219. {
  220. if (wValArray[2] >=100)
  221. {
  222. *pwszInStr++ = TEXT('2');
  223. *pwszInStr++ = TEXT('0');
  224. wValArray[2]-= 100;
  225. }
  226. else
  227. {
  228. *pwszInStr++ = TEXT('1');
  229. *pwszInStr++ = TEXT('9');
  230. }
  231. }
  232. else if (wValArray[2] >=100) // handle year 2000
  233. wValArray[2]-= 100;
  234. break;
  235. }
  236. case TEXT('g'):
  237. {
  238. // era string
  239. pwszPict += cchPictPart;
  240. while (*pwszPict == TEXT(' ')) pwszPict++;
  241. continue;
  242. }
  243. case TEXT('\''):
  244. {
  245. while (*pwszPict && *++pwszPict != TEXT('\'')) ;
  246. continue;
  247. }
  248. default:
  249. {
  250. goto CDFillIn;
  251. break;
  252. }
  253. }
  254. /* This assumes that the values are of two digits only. */
  255. wTempVal = wValArray[wIndex];
  256. wDigit = wTempVal / 10;
  257. if (wDigit)
  258. *pwszInStr++ = (TCHAR)(wDigit + TEXT('0'));
  259. else if (cchPictPart > 1)
  260. *pwszInStr++ = TEXT('0');
  261. *pwszInStr++ = (TCHAR)((wTempVal % 10) + TEXT('0'));
  262. pwszPict += cchPictPart;
  263. CDFillIn:
  264. /* Add the separator. */
  265. while ((*pwszPict) &&
  266. (*pwszPict != TEXT('\'')) &&
  267. (*pwszPict != TEXT('M')) && (*pwszPict != TEXT('m')) &&
  268. (*pwszPict != TEXT('D')) && (*pwszPict != TEXT('d')) &&
  269. (*pwszPict != TEXT('Y')) && (*pwszPict != TEXT('y')))
  270. {
  271. *pwszInStr++ = *pwszPict++;
  272. }
  273. }
  274. *pwszInStr = 0;
  275. return lstrlenW(wszOutStr);
  276. }
  277. #define DATEMASK 0x001F
  278. #define MONTHMASK 0x01E0
  279. #define MINUTEMASK 0x07E0
  280. #define SECONDSMASK 0x001F
  281. STDAPI_(int) GetDateString(WORD wDate, LPWSTR wszStr)
  282. {
  283. WORD wValArray[3];
  284. wValArray[0] = (wDate & MONTHMASK) >> 5; /* Month */
  285. wValArray[1] = (wDate & DATEMASK); /* Date */
  286. wValArray[2] = (wDate >> 9) + 80; /* Year */
  287. return CreateDate(wValArray, wszStr);
  288. }
  289. //
  290. // We need to loop through the string and extract off the month/day/year
  291. // We will do it in the order of the NlS definition...
  292. //
  293. STDAPI_(WORD) ParseDateString(LPCWSTR pszStr)
  294. {
  295. WORD wParts[3];
  296. int cchPictPart;
  297. WORD wIndex;
  298. WORD wTempVal;
  299. WCHAR szShortDate[30]; // need more room for LOCALE_SSHORTDATE
  300. GetLocaleInfoW(LOCALE_USER_DEFAULT, LOCALE_SSHORTDATE, szShortDate, ARRAYSIZE(szShortDate));
  301. LPWSTR pszPict = szShortDate;
  302. while (*pszPict && (*pszPict == *pszStr))
  303. {
  304. pszPict++;
  305. pszStr++;
  306. }
  307. for (int i = 0; (i < 5) && (*pszPict); i++)
  308. {
  309. cchPictPart = GetPict(*pszPict, pszPict);
  310. switch (*pszPict)
  311. {
  312. case TEXT('M'):
  313. case TEXT('m'):
  314. wIndex = 0;
  315. break;
  316. case TEXT('D'):
  317. case TEXT('d'):
  318. //
  319. // if short date style && *pszPict is 'd' &&
  320. // cchPictPart is more than equal 3,
  321. // then it is the day of the week.
  322. if (cchPictPart >= 3)
  323. {
  324. pszPict += cchPictPart;
  325. continue;
  326. }
  327. wIndex = 1;
  328. break;
  329. case TEXT('Y'):
  330. case TEXT('y'):
  331. wIndex = 2;
  332. break;
  333. case TEXT('g'):
  334. {
  335. // era string
  336. pszPict += cchPictPart;
  337. while (*pszPict == TEXT(' ')) pszPict++;
  338. continue;
  339. }
  340. case TEXT('\''):
  341. {
  342. while (*pszPict && *++pszPict != TEXT('\'')) ;
  343. continue;
  344. }
  345. default:
  346. return 0;
  347. }
  348. // We now want to loop through each of the characters while
  349. // they are numbers and build the number;
  350. //
  351. wTempVal = 0;
  352. while ((*pszStr >= TEXT('0')) && (*pszStr <= TEXT('9')))
  353. {
  354. wTempVal = wTempVal * 10 + (WORD)(*pszStr - TEXT('0'));
  355. pszStr++;
  356. }
  357. wParts[wIndex] = wTempVal;
  358. // Now make sure we have the correct separator
  359. pszPict += cchPictPart;
  360. if (*pszPict != *pszStr)
  361. {
  362. return 0;
  363. }
  364. while (*pszPict && (*pszPict == *pszStr))
  365. {
  366. //
  367. // The separator can actually be more than one character
  368. // in length.
  369. //
  370. pszPict++; // align to the next field
  371. pszStr++; // Align to next field
  372. }
  373. }
  374. //
  375. // Do some simple checks to see if the date looks half way reasonable.
  376. //
  377. if (wParts[2] < 80)
  378. wParts[2] += (2000 - 1900); // Wrap to next century but leave as two digits...
  379. if (wParts[2] >= 1900)
  380. wParts[2] -= 1900; // Get rid of Century
  381. if ((wParts[0] == 0) || (wParts[0] > 12) ||
  382. (wParts[1] == 0) || (wParts[1] > 31) ||
  383. (wParts[2] >= 200))
  384. {
  385. return 0;
  386. }
  387. // We now have the three parts so lets construct the date value
  388. // Now construct the date number
  389. return ((wParts[2] - 80) << 9) + (wParts[0] << 5) + wParts[1];
  390. }
  391. STDAPI_(BOOL) IsNullTime(const FILETIME *pft)
  392. {
  393. FILETIME ftNull = {0, 0};
  394. return CompareFileTime(&ftNull, pft) == 0;
  395. }
  396. STDAPI_(BOOL) TouchFile(LPCTSTR pszFile)
  397. {
  398. BOOL bRet = FALSE;
  399. HANDLE hFile = CreateFile(pszFile, GENERIC_WRITE, FILE_SHARE_READ,
  400. NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING | FILE_FLAG_OPEN_NO_RECALL, NULL);
  401. if (hFile != INVALID_HANDLE_VALUE)
  402. {
  403. SYSTEMTIME st;
  404. FILETIME ft;
  405. GetSystemTime(&st);
  406. SystemTimeToFileTime(&st, &ft);
  407. bRet = SetFileTime(hFile, &ft, &ft, &ft);
  408. CloseHandle(hFile);
  409. }
  410. return bRet;
  411. }
  412. void Int64ToStr(LONGLONG n, LPTSTR lpBuffer)
  413. {
  414. TCHAR szTemp[40];
  415. LONGLONG iChr = 0;
  416. do {
  417. szTemp[iChr++] = TEXT('0') + (TCHAR)(n % 10);
  418. n = n / 10;
  419. } while (n != 0);
  420. do {
  421. iChr--;
  422. *lpBuffer++ = szTemp[iChr];
  423. } while (iChr != 0);
  424. *lpBuffer++ = '\0';
  425. }
  426. //
  427. // Obtain NLS info about how numbers should be grouped.
  428. //
  429. // The annoying thing is that LOCALE_SGROUPING and NUMBERFORMAT
  430. // have different ways of specifying number grouping.
  431. //
  432. // LOCALE NUMBERFMT Sample Country
  433. //
  434. // 3;0 3 1,234,567 United States
  435. // 3;2;0 32 12,34,567 India
  436. // 3 30 1234,567 ??
  437. //
  438. // Not my idea. That's the way it works.
  439. //
  440. // Bonus treat - Win9x doesn't support complex number formats,
  441. // so we return only the first number.
  442. //
  443. UINT GetNLSGrouping(void)
  444. {
  445. TCHAR szGrouping[32];
  446. // If no locale info, then assume Western style thousands
  447. if (!GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szGrouping, ARRAYSIZE(szGrouping)))
  448. return 3;
  449. UINT grouping = 0;
  450. LPTSTR psz = szGrouping;
  451. for (;;)
  452. {
  453. if (*psz == '0') break; // zero - stop
  454. else if ((UINT)(*psz - '0') < 10) // digit - accumulate it
  455. grouping = grouping * 10 + (UINT)(*psz - '0');
  456. else if (*psz) // punctuation - ignore it
  457. { }
  458. else // end of string, no "0" found
  459. {
  460. grouping = grouping * 10; // put zero on end (see examples)
  461. break; // and finished
  462. }
  463. psz++;
  464. }
  465. return grouping;
  466. }
  467. // takes a DWORD add commas etc to it and puts the result in the buffer
  468. STDAPI_(LPTSTR) AddCommas64(LONGLONG n, LPTSTR pszResult, UINT cchResult)
  469. {
  470. TCHAR szTemp[MAX_COMMA_NUMBER_SIZE];
  471. TCHAR szSep[5];
  472. NUMBERFMT nfmt;
  473. nfmt.NumDigits=0;
  474. nfmt.LeadingZero=0;
  475. nfmt.Grouping = GetNLSGrouping();
  476. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szSep, ARRAYSIZE(szSep));
  477. nfmt.lpDecimalSep = nfmt.lpThousandSep = szSep;
  478. nfmt.NegativeOrder= 0;
  479. Int64ToStr(n, szTemp);
  480. if (GetNumberFormat(LOCALE_USER_DEFAULT, 0, szTemp, &nfmt, pszResult, cchResult) == 0)
  481. StrCpyN(pszResult, szTemp, cchResult);
  482. return pszResult;
  483. }
  484. // takes a DWORD add commas etc to it and puts the result in the buffer
  485. STDAPI_(LPTSTR) AddCommas(DWORD n, LPTSTR pszResult, UINT cchResult)
  486. {
  487. return AddCommas64(n, pszResult, cchResult);
  488. }
  489. STDAPI_(LPTSTR) ShortSizeFormat64(LONGLONG n, LPTSTR szBuf, UINT cchBuf)
  490. {
  491. return StrFormatByteSize64(n, szBuf, cchBuf);
  492. }
  493. STDAPI_(LPTSTR) ShortSizeFormat(DWORD n, LPTSTR szBuf, UINT cchBuf)
  494. {
  495. return StrFormatByteSize64(n, szBuf, cchBuf);
  496. }
  497. // exported w/o cch, so assume it'll fit
  498. STDAPI_(LPWSTR) AddCommasExportW(DWORD n, LPWSTR pszResult)
  499. {
  500. return AddCommas(n, pszResult, 0x8FFF);
  501. }
  502. STDAPI_(LPTSTR) ShortSizeFormatExportW(DWORD n, LPWSTR szBuf)
  503. {
  504. return StrFormatByteSize64(n, szBuf, 0x8FFF);
  505. }
  506. //
  507. // Converts the numeric value of a LONGLONG to a text string.
  508. // The string may optionally be formatted to include decimal places
  509. // and commas according to current user locale settings.
  510. //
  511. // ARGUMENTS:
  512. // n
  513. // The 64-bit integer to format.
  514. //
  515. // szOutStr
  516. // Address of the destination buffer.
  517. //
  518. // nSize
  519. // Number of characters in the destination buffer.
  520. //
  521. // bFormat
  522. // TRUE = Format per locale settings.
  523. // FALSE = Leave number unformatted.
  524. //
  525. // pFmt
  526. // Address of a number format structure of type NUMBERFMT.
  527. // If NULL, the function automatically provides this information
  528. // based on the user's default locale settings.
  529. //
  530. // dwNumFmtFlags
  531. // Encoded flag word indicating which members of *pFmt to use in
  532. // formatting the number. If a bit is clear, the user's default
  533. // locale setting is used for the corresponding format value. These
  534. // constants can be OR'd together.
  535. //
  536. // NUMFMT_IDIGITS
  537. // NUMFMT_ILZERO
  538. // NUMFMT_SGROUPING
  539. // NUMFMT_SDECIMAL
  540. // NUMFMT_STHOUSAND
  541. // NUMFMT_INEGNUMBER
  542. //
  543. ///////////////////////////////////////////////////////////////////////////////
  544. STDAPI_(int) Int64ToString(LONGLONG n, LPTSTR szOutStr, UINT nSize, BOOL bFormat,
  545. NUMBERFMT *pFmt, DWORD dwNumFmtFlags)
  546. {
  547. INT nResultSize;
  548. TCHAR szBuffer[_MAX_PATH + 1];
  549. NUMBERFMT NumFmt;
  550. TCHAR szDecimalSep[5];
  551. TCHAR szThousandSep[5];
  552. ASSERT(NULL != szOutStr);
  553. //
  554. // Use only those fields in caller-provided NUMBERFMT structure
  555. // that correspond to bits set in dwNumFmtFlags. If a bit is clear,
  556. // get format value from locale info.
  557. //
  558. if (bFormat)
  559. {
  560. TCHAR szInfo[20];
  561. if (NULL == pFmt)
  562. dwNumFmtFlags = 0; // Get all format data from locale info.
  563. if (dwNumFmtFlags & NUMFMT_IDIGITS)
  564. {
  565. NumFmt.NumDigits = pFmt->NumDigits;
  566. }
  567. else
  568. {
  569. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_IDIGITS, szInfo, ARRAYSIZE(szInfo));
  570. NumFmt.NumDigits = StrToLong(szInfo);
  571. }
  572. if (dwNumFmtFlags & NUMFMT_ILZERO)
  573. {
  574. NumFmt.LeadingZero = pFmt->LeadingZero;
  575. }
  576. else
  577. {
  578. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_ILZERO, szInfo, ARRAYSIZE(szInfo));
  579. NumFmt.LeadingZero = StrToLong(szInfo);
  580. }
  581. if (dwNumFmtFlags & NUMFMT_SGROUPING)
  582. {
  583. NumFmt.Grouping = pFmt->Grouping;
  584. }
  585. else
  586. {
  587. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SGROUPING, szInfo, ARRAYSIZE(szInfo));
  588. NumFmt.Grouping = StrToLong(szInfo);
  589. }
  590. if (dwNumFmtFlags & NUMFMT_SDECIMAL)
  591. {
  592. NumFmt.lpDecimalSep = pFmt->lpDecimalSep;
  593. }
  594. else
  595. {
  596. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_SDECIMAL, szDecimalSep, ARRAYSIZE(szDecimalSep));
  597. NumFmt.lpDecimalSep = szDecimalSep;
  598. }
  599. if (dwNumFmtFlags & NUMFMT_STHOUSAND)
  600. {
  601. NumFmt.lpThousandSep = pFmt->lpThousandSep;
  602. }
  603. else
  604. {
  605. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_STHOUSAND, szThousandSep, ARRAYSIZE(szThousandSep));
  606. NumFmt.lpThousandSep = szThousandSep;
  607. }
  608. if (dwNumFmtFlags & NUMFMT_INEGNUMBER)
  609. {
  610. NumFmt.NegativeOrder = pFmt->NegativeOrder;
  611. }
  612. else
  613. {
  614. GetLocaleInfo(LOCALE_USER_DEFAULT, LOCALE_INEGNUMBER, szInfo, ARRAYSIZE(szInfo));
  615. NumFmt.NegativeOrder = StrToLong(szInfo);
  616. }
  617. pFmt = &NumFmt;
  618. }
  619. Int64ToStr(n, szBuffer);
  620. // Format the number string for the locale if the caller wants a
  621. // formatted number string.
  622. if (bFormat)
  623. {
  624. nResultSize = GetNumberFormat(LOCALE_USER_DEFAULT, 0, szBuffer, pFmt, szOutStr, nSize);
  625. if (0 != nResultSize) // Chars in output buffer.
  626. {
  627. // Remove nul terminator char from return size count.
  628. --nResultSize;
  629. }
  630. }
  631. else
  632. {
  633. // GetNumberFormat call failed, so just return the number string
  634. // unformatted.
  635. lstrcpyn(szOutStr, szBuffer, nSize);
  636. nResultSize = lstrlen(szOutStr);
  637. }
  638. return nResultSize;
  639. }
  640. ///////////////////////////////////////////////////////////////////////////////
  641. //
  642. // Converts the numeric value of a LARGE_INTEGER to a text string.
  643. // The string may optionally be formatted to include decimal places
  644. // and commas according to current user locale settings.
  645. //
  646. // ARGUMENTS:
  647. // pN
  648. // Address of the large integer to format.
  649. //
  650. // See description of Int64ToString for remaining arguments.
  651. //
  652. ///////////////////////////////////////////////////////////////////////////////
  653. STDAPI_(int) LargeIntegerToString(LARGE_INTEGER *pN, LPTSTR szOutStr, UINT nSize,
  654. BOOL bFormat, NUMBERFMT *pFmt,
  655. DWORD dwNumFmtFlags)
  656. {
  657. ASSERT(NULL != pN);
  658. return Int64ToString(pN->QuadPart, szOutStr, nSize, bFormat, pFmt, dwNumFmtFlags);
  659. }
  660. #define ISSEP(c) ((c) == TEXT('=') || (c) == TEXT(','))
  661. #define ISWHITE(c) ((c) == TEXT(' ') || (c) == TEXT('\t') || (c) == TEXT('\n') || (c) == TEXT('\r'))
  662. #define ISNOISE(c) ((c) == TEXT('"'))
  663. #define EOF 26
  664. #define QUOTE TEXT('"')
  665. #define COMMA TEXT(',')
  666. #define SPACE TEXT(' ')
  667. #define EQUAL TEXT('=')
  668. /*
  669. * Given a line from SETUP.INF, will extract the nth field from the string
  670. * fields are assumed separated by comma's. Leading and trailing spaces
  671. * are removed.
  672. *
  673. * ENTRY:
  674. *
  675. * szData : pointer to line from SETUP.INF
  676. * n : field to extract. (1 based)
  677. * 0 is field before a '=' sign
  678. * szDataStr : pointer to buffer to hold extracted field
  679. * iBufLen : size of buffer to receive extracted field.
  680. *
  681. * EXIT: returns TRUE if successful, FALSE if failure.
  682. *
  683. */
  684. STDAPI_(BOOL) ParseField(LPCTSTR szData, int n, LPTSTR szBuf, int iBufLen)
  685. {
  686. BOOL fQuote = FALSE;
  687. LPCTSTR pszInf = szData;
  688. LPTSTR ptr;
  689. int iLen = 1;
  690. if (!szData || !szBuf)
  691. return FALSE;
  692. /*
  693. * find the first separator
  694. */
  695. while (*pszInf && !ISSEP(*pszInf))
  696. {
  697. if (*pszInf == QUOTE)
  698. fQuote = !fQuote;
  699. pszInf = CharNext(pszInf);
  700. }
  701. if (n == 0 && *pszInf != TEXT('='))
  702. return FALSE;
  703. if (n > 0 && *pszInf == TEXT('=') && !fQuote)
  704. // Change szData to point to first field
  705. szData = ++pszInf; // Ok for DBCS
  706. /*
  707. * locate the nth comma, that is not inside of quotes
  708. */
  709. fQuote = FALSE;
  710. while (n > 1)
  711. {
  712. while (*szData)
  713. {
  714. if (!fQuote && ISSEP(*szData))
  715. break;
  716. if (*szData == QUOTE)
  717. fQuote = !fQuote;
  718. szData = CharNext(szData);
  719. }
  720. if (!*szData)
  721. {
  722. szBuf[0] = 0; // make szBuf empty
  723. return FALSE;
  724. }
  725. szData = CharNext(szData); // we could do ++ here since we got here
  726. // after finding comma or equal
  727. n--;
  728. }
  729. /*
  730. * now copy the field to szBuf
  731. */
  732. while (ISWHITE(*szData))
  733. szData = CharNext(szData); // we could do ++ here since white space can
  734. // NOT be a lead byte
  735. fQuote = FALSE;
  736. ptr = szBuf; // fill output buffer with this
  737. while (*szData)
  738. {
  739. if (*szData == QUOTE)
  740. {
  741. //
  742. // If we're in quotes already, maybe this
  743. // is a double quote as in: "He said ""Hello"" to me"
  744. //
  745. if (fQuote && *(szData+1) == QUOTE) // Yep, double-quoting - QUOTE is non-DBCS
  746. {
  747. if (iLen < iBufLen)
  748. {
  749. *ptr++ = QUOTE;
  750. ++iLen;
  751. }
  752. szData++; // now skip past 1st quote
  753. }
  754. else
  755. fQuote = !fQuote;
  756. }
  757. else if (!fQuote && ISSEP(*szData))
  758. break;
  759. else
  760. {
  761. if (iLen < iBufLen)
  762. {
  763. *ptr++ = *szData; // Thank you, Dave
  764. ++iLen;
  765. }
  766. if (IsDBCSLeadByte(*szData) && (iLen < iBufLen))
  767. {
  768. *ptr++ = szData[1];
  769. ++iLen;
  770. }
  771. }
  772. szData = CharNext(szData);
  773. }
  774. /*
  775. * remove trailing spaces
  776. */
  777. while (ptr > szBuf)
  778. {
  779. ptr = CharPrev(szBuf, ptr);
  780. if (!ISWHITE(*ptr))
  781. {
  782. ptr = CharNext(ptr);
  783. break;
  784. }
  785. }
  786. *ptr = 0;
  787. return TRUE;
  788. }
  789. // Sets and clears the "wait" cursor.
  790. // REVIEW UNDONE - wait a specific period of time before actually bothering
  791. // to change the cursor.
  792. // REVIEW UNDONE - support for SetWaitPercent();
  793. // BOOL bSet TRUE if you want to change to the wait cursor, FALSE if
  794. // you want to change it back.
  795. STDAPI_(void) SetAppStartingCursor(HWND hwnd, BOOL bSet)
  796. {
  797. if (hwnd && IsWindow(hwnd))
  798. {
  799. DWORD dwTargetProcID;
  800. HWND hwndOwner;
  801. while((NULL != (hwndOwner = GetParent(hwnd))) || (NULL != (hwndOwner = GetWindow(hwnd, GW_OWNER))))
  802. {
  803. hwnd = hwndOwner;
  804. }
  805. // SendNotify is documented to only work in-process (and can
  806. // crash if we pass the pnmhdr across process boundaries on
  807. // NT, because DLLs aren't all shared in one address space).
  808. // So, if this SendNotify would go cross-process, blow it off.
  809. GetWindowThreadProcessId(hwnd, &dwTargetProcID);
  810. if (GetCurrentProcessId() == dwTargetProcID)
  811. SendNotify(hwnd, NULL, bSet ? NM_STARTWAIT : NM_ENDWAIT, NULL);
  812. }
  813. }
  814. #ifdef DEBUG // {
  815. //*** IS_* -- character classification routines
  816. // ENTRY/EXIT
  817. // ch TCHAR to be checked
  818. // return TRUE if in range, FALSE if not
  819. #define IS_LOWER(ch) InRange(ch, TEXT('a'), TEXT('z'))
  820. #define IS_UPPER(ch) InRange(ch, TEXT('A'), TEXT('Z'))
  821. #define IS_ALPHA(ch) (IS_LOWER(ch) || IS_UPPER(ch))
  822. #define IS_DIGIT(ch) InRange(ch, TEXT('0'), TEXT('9'))
  823. #define TO_UPPER(ch) ((ch) - TEXT('a') + TEXT('A'))
  824. //*** BMAP_* -- bitmap routines
  825. // ENTRY/EXIT
  826. // pBits ptr to bitmap (array of bytes)
  827. // iBit bit# to be manipulated
  828. // return various...
  829. // DESCRIPTION
  830. // BMAP_TEST check bit #iBit of bitmap pBits
  831. // BMAP_SET set bit #iBit of bitmap pBits
  832. // NOTES
  833. // warning: no overflow checks
  834. #define BMAP_INDEX(iBit) ((iBit) / 8)
  835. #define BMAP_MASK(iBit) (1 << ((iBit) % 8))
  836. #define BMAP_BYTE(pBits, iBit) (((char *)pBits)[BMAP_INDEX(iBit)])
  837. #define BMAP_TEST(pBits, iBit) (BMAP_BYTE(pBits, iBit) & BMAP_MASK(iBit))
  838. #define BMAP_SET(pBits, iBit) (BMAP_BYTE(pBits, iBit) |= BMAP_MASK(iBit))
  839. //*** DBGetMnemonic -- get menu mnemonic
  840. // ENTRY/EXIT
  841. // return mnemonic if found, o.w. 0
  842. // NOTES
  843. // we handle and skip escaped-& ('&&')
  844. //
  845. TCHAR DBGetMnemonic(LPTSTR pszName)
  846. {
  847. for (; *pszName != 0; pszName = CharNext(pszName))
  848. {
  849. if (*pszName == TEXT('&'))
  850. {
  851. pszName = CharNext(pszName); // skip '&'
  852. if (*pszName != TEXT('&'))
  853. return *pszName;
  854. ASSERT(0); // untested! (but should work...)
  855. pszName = CharNext(pszName); // skip 2nd '&'
  856. }
  857. }
  858. // this one happens a lot w/ weird things like "", "..", "..."
  859. return 0;
  860. }
  861. //*** DBCheckMenu -- check menu for 'style' conformance
  862. // DESCRIPTION
  863. // currently we just check for mnemonic collisions (and only a-z,0-9)
  864. void DBCheckMenu(HMENU hmChk)
  865. {
  866. long bfAlpha = 0;
  867. long bfDigit = 0;
  868. long *pbfMne;
  869. int nItem;
  870. int iMne;
  871. TCHAR chMne;
  872. TCHAR szName[256]; // 256 characters of the menu name should be plenty...
  873. MENUITEMINFO miiChk;
  874. if (!DM_STRICT)
  875. return;
  876. for (nItem = GetMenuItemCount(hmChk) - 1; nItem >= 0; nItem--)
  877. {
  878. miiChk.cbSize = sizeof(MENUITEMINFO);
  879. miiChk.fMask = MIIM_TYPE|MIIM_DATA;
  880. // We need to reset this every time through the loop in case
  881. // menus DON'T have IDs
  882. miiChk.fType = MFT_STRING;
  883. miiChk.dwTypeData = szName;
  884. szName[0] = 0;
  885. miiChk.dwItemData = 0;
  886. miiChk.cch = ARRAYSIZE(szName);
  887. if (!GetMenuItemInfo(hmChk, nItem, TRUE, &miiChk))
  888. {
  889. TraceMsg(TF_WARNING, "dbcm: fail iMenu=%d (skip)", nItem);
  890. continue;
  891. }
  892. if (! (miiChk.fType & MFT_STRING))
  893. {
  894. // skip separators, etc.
  895. continue;
  896. }
  897. chMne = DBGetMnemonic(szName);
  898. if (chMne == 0 || ! (IS_ALPHA(chMne) || IS_DIGIT(chMne)))
  899. {
  900. // this one actually happens a lot w/ chMne==0
  901. if (DM_STRICT2)
  902. TraceMsg(TF_WARNING, "dbcm: skip iMenu=%d mne=%c", nItem, chMne ? chMne : TEXT('0'));
  903. continue;
  904. }
  905. if (IS_LOWER(chMne))
  906. {
  907. chMne = TO_UPPER(chMne);
  908. }
  909. if (IS_UPPER(chMne))
  910. {
  911. iMne = chMne - TEXT('A');
  912. pbfMne = &bfAlpha;
  913. }
  914. else if (IS_DIGIT(chMne))
  915. {
  916. iMne = chMne - TEXT('0');
  917. pbfMne = &bfDigit;
  918. }
  919. else
  920. {
  921. ASSERT(0);
  922. continue;
  923. }
  924. if (BMAP_TEST(pbfMne, iMne))
  925. {
  926. TraceMsg(TF_ERROR, "dbcm: mnemonic collision hm=%x iM=%d szMen=%s",
  927. hmChk, nItem, szName);
  928. }
  929. BMAP_SET(pbfMne, iMne);
  930. }
  931. return;
  932. }
  933. #else // }{
  934. #define DBCheckMenu(hmChk) 0
  935. #endif // }
  936. // Copy a menu onto the beginning or end of another menu
  937. // Adds uIDAdjust to each menu ID (pass in 0 for no adjustment)
  938. // Will not add any item whose adjusted ID is greater than uMaxIDAdjust
  939. // (pass in 0xffff to allow everything)
  940. // Returns one more than the maximum adjusted ID that is used
  941. //
  942. UINT WINAPI Shell_MergeMenus(HMENU hmDst, HMENU hmSrc, UINT uInsert, UINT uIDAdjust, UINT uIDAdjustMax, ULONG uFlags)
  943. {
  944. int nItem;
  945. HMENU hmSubMenu;
  946. BOOL bAlreadySeparated;
  947. MENUITEMINFO miiSrc;
  948. TCHAR szName[256];
  949. UINT uTemp, uIDMax = uIDAdjust;
  950. if (!hmDst || !hmSrc)
  951. {
  952. goto MM_Exit;
  953. }
  954. nItem = GetMenuItemCount(hmDst);
  955. if (uInsert >= (UINT)nItem)
  956. {
  957. uInsert = (UINT)nItem;
  958. bAlreadySeparated = TRUE;
  959. }
  960. else
  961. {
  962. bAlreadySeparated = _SHIsMenuSeparator(hmDst, uInsert);
  963. }
  964. if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
  965. {
  966. // Add a separator between the menus
  967. InsertMenu(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  968. bAlreadySeparated = TRUE;
  969. }
  970. // Go through the menu items and clone them
  971. for (nItem = GetMenuItemCount(hmSrc) - 1; nItem >= 0; nItem--)
  972. {
  973. miiSrc.cbSize = sizeof(MENUITEMINFO);
  974. miiSrc.fMask = MIIM_STATE | MIIM_ID | MIIM_SUBMENU | MIIM_CHECKMARKS | MIIM_TYPE | MIIM_DATA;
  975. // We need to reset this every time through the loop in case
  976. // menus DON'T have IDs
  977. miiSrc.fType = MFT_STRING;
  978. miiSrc.dwTypeData = szName;
  979. miiSrc.dwItemData = 0;
  980. miiSrc.cch = ARRAYSIZE(szName);
  981. if (!GetMenuItemInfo(hmSrc, nItem, TRUE, &miiSrc))
  982. {
  983. continue;
  984. }
  985. // If it's a separator, then add it. If the separator has a
  986. // submenu, then the caller is smoking crash and needs their butt kicked.
  987. if ((miiSrc.fType & MFT_SEPARATOR) && EVAL(!miiSrc.hSubMenu))
  988. {
  989. // This is a separator; don't put two of them in a row
  990. if (bAlreadySeparated && miiSrc.wID == -1 && !(uFlags & MM_DONTREMOVESEPS))
  991. {
  992. continue;
  993. }
  994. bAlreadySeparated = TRUE;
  995. }
  996. else if (miiSrc.hSubMenu)
  997. {
  998. if (uFlags & MM_SUBMENUSHAVEIDS)
  999. {
  1000. // Adjust the ID and check it
  1001. miiSrc.wID += uIDAdjust;
  1002. if (miiSrc.wID > uIDAdjustMax)
  1003. {
  1004. continue;
  1005. }
  1006. if (uIDMax <= miiSrc.wID)
  1007. {
  1008. uIDMax = miiSrc.wID + 1;
  1009. }
  1010. }
  1011. else
  1012. {
  1013. // Don't set IDs for submenus that didn't have
  1014. // them already
  1015. miiSrc.fMask &= ~MIIM_ID;
  1016. }
  1017. hmSubMenu = miiSrc.hSubMenu;
  1018. miiSrc.hSubMenu = CreatePopupMenu();
  1019. if (!miiSrc.hSubMenu)
  1020. {
  1021. goto MM_Exit;
  1022. }
  1023. uTemp = Shell_MergeMenus(miiSrc.hSubMenu, hmSubMenu, 0, uIDAdjust,
  1024. uIDAdjustMax, uFlags&MM_SUBMENUSHAVEIDS);
  1025. if (uIDMax <= uTemp)
  1026. {
  1027. uIDMax = uTemp;
  1028. }
  1029. bAlreadySeparated = FALSE;
  1030. }
  1031. else
  1032. {
  1033. // Adjust the ID and check it
  1034. miiSrc.wID += uIDAdjust;
  1035. if (miiSrc.wID > uIDAdjustMax)
  1036. {
  1037. continue;
  1038. }
  1039. if (uIDMax <= miiSrc.wID)
  1040. {
  1041. uIDMax = miiSrc.wID + 1;
  1042. }
  1043. bAlreadySeparated = FALSE;
  1044. }
  1045. if (!InsertMenuItem(hmDst, uInsert, TRUE, &miiSrc))
  1046. {
  1047. goto MM_Exit;
  1048. }
  1049. }
  1050. // Ensure the correct number of separators at the beginning of the
  1051. // inserted menu items
  1052. if (uInsert == 0)
  1053. {
  1054. if (bAlreadySeparated && !(uFlags & MM_DONTREMOVESEPS))
  1055. {
  1056. DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
  1057. }
  1058. }
  1059. else
  1060. {
  1061. if (_SHIsMenuSeparator(hmDst, uInsert-1))
  1062. {
  1063. if (bAlreadySeparated && !(uFlags & MM_DONTREMOVESEPS))
  1064. {
  1065. DeleteMenu(hmDst, uInsert, MF_BYPOSITION);
  1066. }
  1067. }
  1068. else
  1069. {
  1070. if ((uFlags & MM_ADDSEPARATOR) && !bAlreadySeparated)
  1071. {
  1072. // Add a separator between the menus
  1073. InsertMenu(hmDst, uInsert, MF_BYPOSITION | MF_SEPARATOR, 0, NULL);
  1074. }
  1075. }
  1076. }
  1077. MM_Exit:
  1078. #ifdef DEBUG
  1079. DBCheckMenu(hmDst);
  1080. #endif
  1081. return uIDMax;
  1082. }
  1083. #define REG_WINLOGON_KEY TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon")
  1084. #define REG_PREV_OS_VERSION TEXT("PrevOsVersion")
  1085. #define REG_VAL_PLATFORM_ID TEXT("PlatformId")
  1086. #define REG_VAL_MINORVERSION TEXT("MinorVersion")
  1087. //
  1088. // The following function is called, when we detect that IE4 was installed in this machine earlier.
  1089. // We want to see if that IE4 was there because of Win98 (if so, the ActiveDesktop is OFF by default)
  1090. //
  1091. BOOL WasPrevOsWin98()
  1092. {
  1093. BOOL fWin98;
  1094. HKEY hkeyWinlogon;
  1095. // 99/10/26 Millennium #94983 vtan: When upgrading Win98 to Millennium this
  1096. // will incorrectly detect the system as NT4/IE4 which has Active Desktop
  1097. // set to default to on. Because Windows 2000 setup when upgrading from
  1098. // Windows 98 writes out the previous OS key and Windows Millennium setup
  1099. // when upgrading from Windows 98 does NOT and some Windows NT specific
  1100. // keys and values have never been present on a Windows 98 system it
  1101. // should be adequate to check their presence to determine whether this is
  1102. // an NT4/IE4 upgrade or a Windows 98 upgrade to Millennium.
  1103. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_WINLOGON_KEY, 0, KEY_READ, &hkeyWinlogon))
  1104. {
  1105. HKEY hk;
  1106. DWORD dwType;
  1107. DWORD dwPlatformId, dwMinorVersion;
  1108. DWORD dwDataLength;
  1109. // 99/04/09 #319056 vtan: We'll assume that previous OS is known on
  1110. // a Win9x upgrade from keys written by setup. If no keys are present
  1111. // we'll assume NT4 upgrade where with IE4 integrated shell the default
  1112. // was ON.
  1113. fWin98 = FALSE;
  1114. // See it the prev OS info is available. Caution: This info gets written in registry by
  1115. // NT setup at the far end of the setup process (after all our DLLs are already registered).
  1116. // So, we use extra care here to see of that key and values really exist or not!
  1117. if (RegOpenKeyEx(hkeyWinlogon, REG_PREV_OS_VERSION, 0, KEY_READ, &hk) == ERROR_SUCCESS)
  1118. {
  1119. dwType = 0;
  1120. dwDataLength = sizeof(dwPlatformId);
  1121. if (RegQueryValueEx(hk, REG_VAL_PLATFORM_ID, NULL, &dwType, (LPBYTE)(&dwPlatformId), &dwDataLength) == ERROR_SUCCESS)
  1122. {
  1123. dwType = 0;
  1124. dwDataLength = sizeof(dwMinorVersion);
  1125. if (RegQueryValueEx(hk, REG_VAL_MINORVERSION, NULL, &dwType, (LPBYTE)(&dwMinorVersion), &dwDataLength) == ERROR_SUCCESS)
  1126. {
  1127. if ((dwPlatformId == VER_PLATFORM_WIN32_WINDOWS) && (dwMinorVersion > 0))
  1128. fWin98 = TRUE; //This is Win98 for sure!
  1129. else
  1130. fWin98 = FALSE; //The prev OS is NOT win98 for sure!
  1131. }
  1132. }
  1133. RegCloseKey(hk);
  1134. }
  1135. RegCloseKey(hkeyWinlogon);
  1136. }
  1137. else
  1138. {
  1139. fWin98 = TRUE;
  1140. }
  1141. return fWin98;
  1142. }
  1143. void _SetIE4DefaultShellState(SHELLSTATE *pss)
  1144. {
  1145. pss->fDoubleClickInWebView = TRUE;
  1146. pss->fShowInfoTip = TRUE;
  1147. pss->fWebView = TRUE;
  1148. pss->fDesktopHTML = FALSE;
  1149. // IE4 defaulted to fDesktopHTML on, and on NT5 upgrade we don't
  1150. // want to override that (and probably not on Win98 upgrade, but
  1151. // it's too late for that). To determine this here, check a
  1152. // uniquely IE4 reg-key. (Note, this will catch the case if the
  1153. // user *modified* their desktop. If they just went with the
  1154. // defaults, this key won't be there and we'll remove AD.)
  1155. TCHAR szDeskcomp[MAX_PATH];
  1156. DWORD dwType = 0, dwDeskHtmlVersion = 0;
  1157. DWORD dwDataLength = sizeof(dwDeskHtmlVersion);
  1158. GetRegLocation(szDeskcomp, SIZECHARS(szDeskcomp), REG_DESKCOMP_COMPONENTS, NULL);
  1159. SHGetValue(HKEY_CURRENT_USER, szDeskcomp, REG_VAL_COMP_VERSION, &dwType, (LPBYTE)(&dwDeskHtmlVersion), &dwDataLength);
  1160. // 99/05/03 #292269: Notice the difference in the order of the
  1161. // bits. The current structure (IE4 and later) has fShowSysFiles
  1162. // between fNoConfirmRecycle and fShowCompColor. Move the bit
  1163. // based on the size of the struct and reset fShowSysFiles to TRUE.
  1164. // WIN95 SHELLSTATE struct bit fields
  1165. // BOOL fShowAllObjects : 1;
  1166. // BOOL fShowExtensions : 1;
  1167. // BOOL fNoConfirmRecycle : 1;
  1168. // BOOL fShowCompColor : 1;
  1169. // UINT fRestFlags : 13;
  1170. // IE4 SHELLSTATE struct bit fields
  1171. // BOOL fShowAllObjects : 1;
  1172. // BOOL fShowExtensions : 1;
  1173. // BOOL fNoConfirmRecycle : 1;
  1174. // BOOL fShowSysFiles : 1;
  1175. // BOOL fShowCompColor : 1;
  1176. // BOOL fDoubleClickInWebView : 1;
  1177. // BOOL fDesktopHTML : 1;
  1178. // BOOL fWin95Classic : 1;
  1179. // BOOL fDontPrettyPath : 1;
  1180. // BOOL fShowAttribCol : 1;
  1181. // BOOL fMapNetDrvBtn : 1;
  1182. // BOOL fShowInfoTip : 1;
  1183. // BOOL fHideIcons : 1;
  1184. // BOOL fWebView : 1;
  1185. // BOOL fFilter : 1;
  1186. // BOOL fShowSuperHidden : 1;
  1187. // Millennium SHELLSTATE struct bit fields
  1188. // BOOL fNoNetCrawling : 1;
  1189. // Whistler SHELLSTATE struct bit fields
  1190. // BOOL fStartPanelOn : 1;
  1191. // BOOL fShowStartPage : 1;
  1192. if ((g_pShellState->cbSize == REGSHELLSTATE_SIZE_WIN95) || (g_pShellState->cbSize == REGSHELLSTATE_SIZE_NT4))
  1193. {
  1194. pss->fShowCompColor = TRUE;
  1195. pss->fShowSysFiles = TRUE;
  1196. }
  1197. if (dwDeskHtmlVersion == IE4_DESKHTML_VERSION)
  1198. pss->fDesktopHTML = !WasPrevOsWin98(); //This is an upgrade from IE4; but, the registry is not updated yet.
  1199. else
  1200. {
  1201. if (dwDeskHtmlVersion > IE4_DESKHTML_VERSION)
  1202. {
  1203. DWORD dwOldHtmlVersion = 0;
  1204. dwDataLength = sizeof(dwOldHtmlVersion);
  1205. // This is NT5 or above! Check to see if we have "UpgradedFrom" value.
  1206. // NOTE: The "UpgradedFrom" value is at "...\Desktop" and NOT at "..\Desktop\Components"
  1207. // This is because the "Components" key gets destroyed very often.
  1208. SHGetValue(HKEY_CURRENT_USER, REG_DESKCOMP, REG_VAL_COMP_UPGRADED_FROM, &dwType, (LPBYTE)&dwOldHtmlVersion, &dwDataLength);
  1209. // 99/05/17 #333384 vtan: Check for IE5 as an old version too. The current version
  1210. // is now 0x0110 (from 0x010F) and this causes the HKCU\Software\Microsoft\Internet
  1211. // Explorer\Desktop\UpgradedFrom value to get created in CDeskHtmlProp_RegUnReg().
  1212. // This is executed by IE4UINIT.EXE as well as REGSVR32.EXE with the "/U" parameter
  1213. // so this field should be present at the time this executes. Note this only
  1214. // executes the once on upgrade because the ShellState will get written.
  1215. if ((dwOldHtmlVersion == IE4_DESKHTML_VERSION) || (dwOldHtmlVersion == IE5_DESKHTML_VERSION))
  1216. pss->fDesktopHTML = !WasPrevOsWin98(); //This is an upgrade from IE4;
  1217. }
  1218. }
  1219. }
  1220. //
  1221. // This function checks if the caller is running in an explorer process.
  1222. //
  1223. STDAPI_(BOOL) IsProcessAnExplorer()
  1224. {
  1225. return BOOLFROMPTR(GetModuleHandle(TEXT("EXPLORER.EXE")));
  1226. }
  1227. //
  1228. // Is this the main shell process? (eg the one that owns the desktop window)
  1229. //
  1230. // NOTE: if the desktop window has not been created, we assume that this is NOT the
  1231. // main shell process and return FALSE;
  1232. //
  1233. STDAPI_(BOOL) IsMainShellProcess()
  1234. {
  1235. static int s_fIsMainShellProcess = -1;
  1236. if (s_fIsMainShellProcess == -1)
  1237. {
  1238. HWND hwndDesktop = GetShellWindow();
  1239. if (hwndDesktop)
  1240. {
  1241. s_fIsMainShellProcess = (int)IsWindowInProcess(hwndDesktop);
  1242. if ((s_fIsMainShellProcess != 0) && !IsProcessAnExplorer())
  1243. {
  1244. TraceMsg(TF_WARNING, "IsMainShellProcess: the main shell process (owner of the desktop) is NOT an explorer window?!?");
  1245. }
  1246. }
  1247. else
  1248. {
  1249. #ifdef FULL_DEBUG
  1250. // only spew on FULL_DEBUG to cut down on chattyness in normal debug builds
  1251. TraceMsg(TF_WARNING, "IsMainShellProcess: hwndDesktop does not exist, assuming we are NOT the main shell process");
  1252. #endif // FULL_DEBUG
  1253. return FALSE;
  1254. }
  1255. }
  1256. return s_fIsMainShellProcess ? TRUE : FALSE;
  1257. }
  1258. BOOL _ShouldStartPanelBeEnabledByDefault()
  1259. {
  1260. DWORD dwDefaultPanelOff;
  1261. DWORD cbSize;
  1262. cbSize = sizeof(dwDefaultPanelOff);
  1263. // We respect a regkey that can be set by an unattend file to default to the classic start menu
  1264. if ((SHRegGetUSValue(TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\StartMenu\\StartPanel"),
  1265. TEXT("DefaultStartPanelOff"),
  1266. NULL,
  1267. &dwDefaultPanelOff,
  1268. &cbSize,
  1269. FALSE,
  1270. NULL,
  1271. 0) == ERROR_SUCCESS) && dwDefaultPanelOff)
  1272. {
  1273. return FALSE;
  1274. }
  1275. // Start Panel needs to be enabled by default only for the "Personal" and "Professional" versions.
  1276. return (IsOS(OS_PERSONAL) || IsOS(OS_PROFESSIONAL));
  1277. }
  1278. DWORD GetCurrentSessionID(void)
  1279. {
  1280. DWORD dwProcessID = (DWORD) -1;
  1281. ProcessIdToSessionId(GetCurrentProcessId(), &dwProcessID);
  1282. return dwProcessID;
  1283. }
  1284. typedef struct
  1285. {
  1286. LPCWSTR pszRegKey;
  1287. LPCWSTR pszRegValue;
  1288. } TSPERFFLAG_ITEM;
  1289. const TSPERFFLAG_ITEM s_TSPerfFlagItems[] =
  1290. {
  1291. {L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\Remote\\%d", L"ActiveDesktop"}, // TSPerFlag_NoADWallpaper
  1292. {L"Control Panel\\Desktop\\Remote\\%d", L"Wallpaper"}, // TSPerFlag_NoWallpaper
  1293. {L"Software\\Microsoft\\Windows\\CurrentVersion\\ThemeManager\\Remote\\%d", L"ThemeActive"}, // TSPerFlag_NoVisualStyles
  1294. {L"Control Panel\\Desktop\\Remote\\%d", L"DragFullWindows"}, // TSPerFlag_NoWindowDrag
  1295. {L"Control Panel\\Desktop\\Remote\\%d", L"SmoothScroll"}, // TSPerFlag_NoAnimation
  1296. };
  1297. BOOL IsTSPerfFlagEnabled(enumTSPerfFlag eTSFlag)
  1298. {
  1299. BOOL fIsTSFlagEnabled = FALSE;
  1300. if (GetSystemMetrics(SM_REMOTESESSION))
  1301. {
  1302. TCHAR szTemp[MAX_PATH];
  1303. DWORD dwType;
  1304. DWORD cbSize = sizeof(szTemp);
  1305. TCHAR szRegKey[MAX_PATH];
  1306. wnsprintf(szRegKey, ARRAYSIZE(szRegKey), s_TSPerfFlagItems[eTSFlag].pszRegKey, GetCurrentSessionID());
  1307. if (ERROR_SUCCESS == SHGetValueW(HKEY_CURRENT_USER, szRegKey, s_TSPerfFlagItems[eTSFlag].pszRegValue, &dwType, (void *)szTemp, &cbSize))
  1308. {
  1309. fIsTSFlagEnabled = TRUE;
  1310. }
  1311. }
  1312. return fIsTSFlagEnabled;
  1313. }
  1314. BOOL PolicyNoActiveDesktop(void)
  1315. {
  1316. BOOL fNoActiveDesktop = SHRestricted(REST_NOACTIVEDESKTOP);
  1317. if (!fNoActiveDesktop)
  1318. {
  1319. fNoActiveDesktop = (IsTSPerfFlagEnabled(TSPerFlag_NoADWallpaper) || IsTSPerfFlagEnabled(TSPerFlag_NoWallpaper));
  1320. }
  1321. return fNoActiveDesktop;
  1322. }
  1323. BOOL _RefreshSettingsFromReg()
  1324. {
  1325. BOOL fNeedToUpdateReg = FALSE;
  1326. static REGSHELLSTATE ShellStateBuf = {0,};
  1327. DWORD cbSize;
  1328. ASSERTCRITICAL;
  1329. if (g_pShellState)
  1330. {
  1331. // reuse the buffer if possible
  1332. cbSize = g_pShellState->cbSize;
  1333. if (FAILED(SKGetValue(SHELLKEY_HKCU_EXPLORER, NULL, TEXT("ShellState"), NULL, g_pShellState, &cbSize)))
  1334. {
  1335. if (&ShellStateBuf != g_pShellState)
  1336. {
  1337. LocalFree(g_pShellState);
  1338. }
  1339. g_pShellState = NULL;
  1340. }
  1341. }
  1342. if (!g_pShellState)
  1343. {
  1344. if (FAILED(SKAllocValue(SHELLKEY_HKCU_EXPLORER, NULL, TEXT("ShellState"), NULL, (void **)&g_pShellState, NULL)))
  1345. {
  1346. g_pShellState = &ShellStateBuf;
  1347. }
  1348. else
  1349. {
  1350. cbSize = LocalSize(g_pShellState);
  1351. // if we read out an smaller size from the registry, then copy it into our stack buffer and use that.
  1352. // we need a struct at least as big as the current size
  1353. if (cbSize <= sizeof(ShellStateBuf))
  1354. {
  1355. CopyMemory(&ShellStateBuf, g_pShellState, cbSize);
  1356. LocalFree(g_pShellState);
  1357. g_pShellState = &ShellStateBuf;
  1358. // g_pShellState->cbSize will be updated in the below
  1359. fNeedToUpdateReg = TRUE;
  1360. }
  1361. }
  1362. }
  1363. // Upgrade what we read out of the registry
  1364. if ((g_pShellState->cbSize == REGSHELLSTATE_SIZE_WIN95) ||
  1365. (g_pShellState->cbSize == REGSHELLSTATE_SIZE_NT4))
  1366. {
  1367. // Upgrade Win95 bits. Too bad our defaults weren't
  1368. // FALSE for everything, because whacking these bits
  1369. // breaks roaming. Of course, if you ever roam to
  1370. // a Win95 machine, it whacks all bits to zero...
  1371. _SetIE4DefaultShellState(&g_pShellState->ss);
  1372. //New bits added for Whistler
  1373. //g_pShellState->ss.fNoNetCrawling = FALSE;
  1374. g_pShellState->ss.fStartPanelOn = _ShouldStartPanelBeEnabledByDefault();
  1375. //g_pShellState->ss.fShowStartPage = FALSE; // Off by default for now!
  1376. g_pShellState->ss.version = SHELLSTATEVERSION;
  1377. g_pShellState->cbSize = sizeof(REGSHELLSTATE);
  1378. fNeedToUpdateReg = TRUE;
  1379. }
  1380. else if (g_pShellState->cbSize >= REGSHELLSTATE_SIZE_IE4)
  1381. {
  1382. // Since the version field was new to IE4, this should be true:
  1383. ASSERT(g_pShellState->ss.version >= SHELLSTATEVERSION_IE4);
  1384. if (g_pShellState->ss.version < SHELLSTATEVERSION)
  1385. {
  1386. //Since the version # read from the registry is old!
  1387. fNeedToUpdateReg = TRUE;
  1388. }
  1389. // Upgrade to current version here - make sure we don't
  1390. // stomp on bits unnecessarily, as that will break roaming...
  1391. if (g_pShellState->ss.version == SHELLSTATEVERSION_IE4)
  1392. {
  1393. // IE4.0 shipped with verion = 9; The SHELLSTATEVERSION was changed to 10 later.
  1394. // But the structure size or defaults have not changed since IE4.0 shipped. So,
  1395. // the following code treats version 9 as the same as version 10. If we do not do this,
  1396. // the IE4.0 users who upgrade to Memphis or IE4.01 will lose all their settings
  1397. // (Bug #62389).
  1398. g_pShellState->ss.version = SHELLSTATEVERSION_WIN2K;
  1399. }
  1400. // Since this could be an upgrade from Win98, the fWebView bit, which was not used in win98
  1401. // could be zero; We must set the default value of fWebView = ON here and later in
  1402. // _RefreshSettings() functions we read from Advanced\WebView value and reset it (if it is there).
  1403. // If Advanced\WebView is not there, then this must be an upgrade from Win98 and WebView should be
  1404. // turned ON.
  1405. g_pShellState->ss.fWebView = TRUE;
  1406. // Upgrade from Win2K to Millennium/Whistler installs
  1407. if (g_pShellState->ss.version == SHELLSTATEVERSION_WIN2K)
  1408. {
  1409. //g_pShellState->ss.fNoNetCrawling = FALSE;
  1410. //g_pShellState->ss.fShowStartPage = FALSE;
  1411. g_pShellState->ss.version = 11;
  1412. }
  1413. if (g_pShellState->ss.version < 13)
  1414. {
  1415. //This is the new bit added between versions 11 and 12. The default changed for 13.
  1416. g_pShellState->ss.fStartPanelOn = _ShouldStartPanelBeEnabledByDefault();//Turn it ON by default only for "Personal" or "Professional".
  1417. g_pShellState->ss.version = 13;
  1418. }
  1419. // Ensure that the CB reflects the current size of the structure
  1420. if (fNeedToUpdateReg)
  1421. {
  1422. g_pShellState->cbSize = sizeof(REGSHELLSTATE);
  1423. }
  1424. // Must be saved state from this or an uplevel platform. Don't touch the bits.
  1425. ASSERT(g_pShellState->ss.version >= SHELLSTATEVERSION);
  1426. }
  1427. else
  1428. {
  1429. // We could not read anything from reg. Initialize all fields.
  1430. // 0 should be the default for *most* everything
  1431. g_pShellState->cbSize = sizeof(REGSHELLSTATE);
  1432. g_pShellState->ss.iSortDirection = 1;
  1433. _SetIE4DefaultShellState(&g_pShellState->ss);
  1434. // New bits added for Whistler.
  1435. // g_pShellState->ss.fNoNetCrawling = FALSE;
  1436. g_pShellState->ss.fStartPanelOn = _ShouldStartPanelBeEnabledByDefault(); //Turn it ON by default only for "Personal".
  1437. //g_pShellState->ss.fShowStartPage = FALSE;
  1438. // New defaults for Whistler.
  1439. g_pShellState->ss.fShowCompColor = TRUE;
  1440. g_pShellState->ss.version = SHELLSTATEVERSION;
  1441. fNeedToUpdateReg = TRUE;
  1442. }
  1443. // Apply restrictions
  1444. //Note: This restriction supercedes the NOACTIVEDESKTOP!
  1445. if (SHRestricted(REST_FORCEACTIVEDESKTOPON))
  1446. {
  1447. g_pShellState->ss.fDesktopHTML = TRUE;
  1448. }
  1449. else
  1450. {
  1451. if (PolicyNoActiveDesktop())
  1452. {
  1453. //Note this restriction is superceded by FORCEACTIVEDESKTOPON!
  1454. g_pShellState->ss.fDesktopHTML = FALSE;
  1455. }
  1456. }
  1457. if (SHRestricted(REST_NOWEBVIEW))
  1458. {
  1459. g_pShellState->ss.fWebView = FALSE;
  1460. }
  1461. // ClassicShell restriction makes all web view off and forces even more win95 behaviours
  1462. // so we still need to fDoubleClickInWebView off.
  1463. if (SHRestricted(REST_CLASSICSHELL))
  1464. {
  1465. g_pShellState->ss.fWin95Classic = TRUE;
  1466. g_pShellState->ss.fDoubleClickInWebView = FALSE;
  1467. g_pShellState->ss.fWebView = FALSE;
  1468. g_pShellState->ss.fDesktopHTML = FALSE;
  1469. }
  1470. if (SHRestricted(REST_DONTSHOWSUPERHIDDEN))
  1471. {
  1472. g_pShellState->ss.fShowSuperHidden = FALSE;
  1473. }
  1474. if (SHRestricted(REST_SEPARATEDESKTOPPROCESS))
  1475. {
  1476. g_pShellState->ss.fSepProcess = TRUE;
  1477. }
  1478. if (SHRestricted(REST_NONETCRAWL))
  1479. {
  1480. g_pShellState->ss.fNoNetCrawling = FALSE;
  1481. }
  1482. if (SHRestricted(REST_NOSTARTPANEL))
  1483. {
  1484. g_pShellState->ss.fStartPanelOn = FALSE;
  1485. }
  1486. if (SHRestricted(REST_NOSTARTPAGE))
  1487. {
  1488. g_pShellState->ss.fShowStartPage = FALSE;
  1489. }
  1490. if (fNeedToUpdateReg)
  1491. {
  1492. // There is a need to update ShellState in registry. Do it only if current procees is
  1493. // an Explorer process.
  1494. //
  1495. // Because, only when the explorer process is running, we can be
  1496. // assured that the NT5 setup is complete and all the PrevOsVersion info is available and
  1497. // _SetIE4DefaultShellState() and WasPrevOsWin98() etc, would have set the proper value
  1498. // for fDesktopHHTML. If we don't do the following check, we will end-up updating the
  1499. // registry the first time someone (like setup) called SHGetSettings() and that would be
  1500. // too early to update the ShellState since we don't have all the info needed to decide if
  1501. // fDesktopHTML needs to be ON or OFF based on previous OS, previous IE version etc.,
  1502. fNeedToUpdateReg = IsProcessAnExplorer();
  1503. }
  1504. return (fNeedToUpdateReg);
  1505. }
  1506. EXTERN_C HANDLE g_hSettings = NULL; // global shell settings counter
  1507. LONG g_lProcessSettingsCount = -1; // current process's count
  1508. const GUID GUID_ShellSettingsChanged = { 0x7cb834f0, 0x527b, 0x11d2, {0x9d, 0x1f, 0x00, 0x00, 0xf8, 0x05, 0xca, 0x57}}; // 7cb834f0-527b-11d2-9d1f-0000f805ca57
  1509. HANDLE _GetSettingsCounter()
  1510. {
  1511. return SHGetCachedGlobalCounter(&g_hSettings, &GUID_ShellSettingsChanged);
  1512. }
  1513. BOOL _QuerySettingsChanged(void)
  1514. {
  1515. long lGlobalCount = SHGlobalCounterGetValue(_GetSettingsCounter());
  1516. if (g_lProcessSettingsCount != lGlobalCount)
  1517. {
  1518. g_lProcessSettingsCount = lGlobalCount;
  1519. return TRUE;
  1520. }
  1521. return FALSE;
  1522. }
  1523. //
  1524. // SHRefreshSettings now just invalidates the settings cache.
  1525. // so that the next time that SHGetSetSettings() is called
  1526. // it will reread all the settings
  1527. //
  1528. STDAPI_(void) SHRefreshSettings(void)
  1529. {
  1530. SHGlobalCounterIncrement(_GetSettingsCounter());
  1531. }
  1532. // this needs to get called periodically to re-fetch the settings from the
  1533. // registry as we no longer store them in a share data segment
  1534. BOOL _RefreshSettings(void)
  1535. {
  1536. BOOL fNeedToUpdateReg = FALSE;
  1537. ENTERCRITICAL;
  1538. fNeedToUpdateReg = _RefreshSettingsFromReg();
  1539. // get the advanced options.
  1540. // they are stored as individual values so that policy editor can change it.
  1541. HKEY hkeyAdv = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, TEXT("Advanced"), FALSE);
  1542. if (hkeyAdv)
  1543. {
  1544. DWORD dwData;
  1545. DWORD dwSize = sizeof(dwData);
  1546. if (SHQueryValueEx(hkeyAdv, TEXT("Hidden"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1547. {
  1548. // Map the obsolete value of 0 to 2.
  1549. if (dwData == 0)
  1550. dwData = 2;
  1551. g_pShellState->ss.fShowAllObjects = (dwData == 1);
  1552. g_pShellState->ss.fShowSysFiles = (dwData == 2);
  1553. }
  1554. dwSize = sizeof(dwData);
  1555. if (SHQueryValueEx(hkeyAdv, TEXT("ShowCompColor"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1556. {
  1557. g_pShellState->ss.fShowCompColor = (BOOL)dwData;
  1558. }
  1559. dwSize = sizeof(dwData);
  1560. if (SHQueryValueEx(hkeyAdv, TEXT("HideFileExt"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1561. {
  1562. g_pShellState->ss.fShowExtensions = (BOOL)dwData ? FALSE : TRUE;
  1563. }
  1564. dwSize = sizeof(dwData);
  1565. if (SHQueryValueEx(hkeyAdv, TEXT("DontPrettyPath"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1566. {
  1567. g_pShellState->ss.fDontPrettyPath = (BOOL)dwData;
  1568. }
  1569. dwSize = sizeof(dwData);
  1570. if (SHQueryValueEx(hkeyAdv, TEXT("ShowInfoTip"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1571. {
  1572. g_pShellState->ss.fShowInfoTip = (BOOL)dwData;
  1573. }
  1574. dwSize = sizeof(dwData);
  1575. if (SHQueryValueEx(hkeyAdv, TEXT("HideIcons"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1576. {
  1577. g_pShellState->ss.fHideIcons = (BOOL)dwData;
  1578. }
  1579. dwSize = sizeof(dwData);
  1580. if (SHQueryValueEx(hkeyAdv, TEXT("MapNetDrvBtn"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1581. {
  1582. g_pShellState->ss.fMapNetDrvBtn = (BOOL)dwData;
  1583. }
  1584. dwSize = sizeof(dwData);
  1585. if (!SHRestricted(REST_CLASSICSHELL))
  1586. {
  1587. if (SHQueryValueEx(hkeyAdv, TEXT("WebView"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1588. {
  1589. g_pShellState->ss.fWebView = (BOOL)dwData;
  1590. }
  1591. else
  1592. {
  1593. //If Advanced/WebView value is not there, then this could be an upgrade from win98/IE4
  1594. // where we stored this info in DEFFOLDERSETTINGS in Explorer\Streams\Settings.
  1595. // See if that info is there; If so, use it!
  1596. DEFFOLDERSETTINGS dfs;
  1597. DWORD dwType, cbData = sizeof(dfs);
  1598. if (SUCCEEDED(SKGetValue(SHELLKEY_HKCU_EXPLORER, TEXT("Streams"), TEXT("Settings"), &dwType, &dfs, &cbData))
  1599. && (dwType == REG_BINARY))
  1600. {
  1601. //DefFolderSettings is there; Check if this is the correct struct.
  1602. //Note:In Win98/IE4, we wrongly initialized dwStructVersion to zero.
  1603. if ((cbData == sizeof(dfs)) &&
  1604. ((dfs.dwStructVersion == 0) || (dfs.dwStructVersion == DFS_NASH_VER)))
  1605. {
  1606. g_pShellState->ss.fWebView = ((dfs.bUseVID) && (dfs.vid == VID_WebView));
  1607. }
  1608. }
  1609. }
  1610. }
  1611. dwSize = sizeof(dwData);
  1612. if (SHQueryValueEx(hkeyAdv, TEXT("Filter"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1613. {
  1614. g_pShellState->ss.fFilter = (BOOL)dwData;
  1615. }
  1616. if (SHQueryValueEx(hkeyAdv, TEXT("ShowSuperHidden"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1617. {
  1618. g_pShellState->ss.fShowSuperHidden = (BOOL)dwData;
  1619. }
  1620. if (SHQueryValueEx(hkeyAdv, TEXT("SeparateProcess"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1621. {
  1622. g_pShellState->ss.fSepProcess = (BOOL)dwData;
  1623. }
  1624. if (SHQueryValueEx(hkeyAdv, TEXT("NoNetCrawling"), NULL, NULL, (LPBYTE)&dwData, &dwSize) == ERROR_SUCCESS)
  1625. {
  1626. g_pShellState->ss.fNoNetCrawling = (BOOL)dwData;
  1627. }
  1628. RegCloseKey(hkeyAdv);
  1629. }
  1630. else
  1631. {
  1632. // Hey, if the advanced key is not there, this must be a Win9x upgrade...
  1633. // Fortunately the SHELLSTATE defaults and the not-in-registry defaults
  1634. // are the same, so we don't need any auto-propogate code here.
  1635. }
  1636. // this process is now in sync
  1637. g_lProcessSettingsCount = SHGlobalCounterGetValue(_GetSettingsCounter());
  1638. LEAVECRITICAL;
  1639. return fNeedToUpdateReg;
  1640. }
  1641. // This function moves SHELLSTATE settings into the Advanced Settings
  1642. // portion of the registry. If no SHELLSTATE is passed in, it uses
  1643. // the current state stored in the registry.
  1644. //
  1645. void Install_AdvancedShellSettings(SHELLSTATE * pss)
  1646. {
  1647. DWORD dw;
  1648. BOOL fCrit = FALSE;
  1649. if (NULL == pss)
  1650. {
  1651. // Get the current values in the registry or the default values
  1652. // as determined by the following function.
  1653. //
  1654. // we'll be partying on g_pShellState, so grab the critical section here
  1655. //
  1656. ENTERCRITICALNOASSERT;
  1657. fCrit = TRUE;
  1658. // Win95 and NT5 kept the SHELLSTATE bits in the registry up to date,
  1659. // but apparently IE4 only kept the ADVANCED section up to date.
  1660. // It would be nice to call _RefreshSettingsFromReg(); here, but
  1661. // that won't keep IE4 happy. Call _RefreshSettings() instead.
  1662. _RefreshSettings();
  1663. pss = &g_pShellState->ss;
  1664. }
  1665. HKEY hkeyAdv = SHGetShellKey(SHELLKEY_HKCU_EXPLORER, TEXT("Advanced"), TRUE);
  1666. if (hkeyAdv)
  1667. {
  1668. DWORD dwData;
  1669. dw = sizeof(dwData);
  1670. dwData = (DWORD)(pss->fShowAllObjects ? 1 : 2);
  1671. RegSetValueEx(hkeyAdv, TEXT("Hidden") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1672. dwData = (DWORD)pss->fShowCompColor ? 1 : 0;
  1673. RegSetValueEx(hkeyAdv, TEXT("ShowCompColor") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1674. dwData = (DWORD)pss->fShowExtensions ? 0 : 1;
  1675. RegSetValueEx(hkeyAdv, TEXT("HideFileExt") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1676. dwData = (DWORD)pss->fDontPrettyPath ? 1 : 0;
  1677. RegSetValueEx(hkeyAdv, TEXT("DontPrettyPath") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1678. dwData = (DWORD)pss->fShowInfoTip ? 1 : 0;
  1679. RegSetValueEx(hkeyAdv, TEXT("ShowInfoTip") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1680. dwData = (DWORD)pss->fHideIcons ? 1 : 0;
  1681. RegSetValueEx(hkeyAdv, TEXT("HideIcons") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1682. dwData = (DWORD)pss->fMapNetDrvBtn ? 1 : 0;
  1683. RegSetValueEx(hkeyAdv, TEXT("MapNetDrvBtn") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1684. dwData = (DWORD)pss->fWebView ? 1 : 0;
  1685. RegSetValueEx(hkeyAdv, TEXT("WebView") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1686. dwData = (DWORD)pss->fFilter ? 1 : 0;
  1687. RegSetValueEx(hkeyAdv, TEXT("Filter") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1688. dwData = (DWORD)pss->fShowSuperHidden ? 1 : 0;
  1689. RegSetValueEx(hkeyAdv, TEXT("SuperHidden") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1690. dwData = (DWORD)pss->fSepProcess ? 1 : 0;
  1691. RegSetValueEx(hkeyAdv, TEXT("SeparateProcess") ,0, REG_DWORD, (LPBYTE)&dwData, dw);
  1692. RegCloseKey(hkeyAdv);
  1693. }
  1694. if (fCrit)
  1695. {
  1696. LEAVECRITICALNOASSERT;
  1697. }
  1698. }
  1699. STDAPI_(void) SHGetSetSettings(LPSHELLSTATE lpss, DWORD dwMask, BOOL bSet)
  1700. {
  1701. //Does the ShellState in Reg is old? If so, we need to update it!
  1702. BOOL fUpdateShellStateInReg = FALSE; //Assume, no need to update it!
  1703. if (!lpss && !dwMask && bSet)
  1704. {
  1705. // this was a special way to call
  1706. // SHRefreshSettings() from an external module.
  1707. // special case it out now.
  1708. SHRefreshSettings();
  1709. return;
  1710. }
  1711. if (!g_pShellState || _QuerySettingsChanged())
  1712. {
  1713. // if it hasn't been init'd or we are setting the values. We must do it
  1714. // on the save case because it may have changed in the registry since
  1715. // we last fetched it..
  1716. fUpdateShellStateInReg = _RefreshSettings();
  1717. }
  1718. else if (g_pShellState)
  1719. {
  1720. // _RefreshSettingsFromReg sets g_pShellState to non null value
  1721. // and then starts stuffing values into it and all within our
  1722. // glorious critsec, but unless we check for the critsec here,
  1723. // we will start partying on g_pShellState before it is finished
  1724. // loading.
  1725. ENTERCRITICAL;
  1726. LEAVECRITICAL;
  1727. }
  1728. BOOL fSave = FALSE;
  1729. BOOL fSaveAdvanced = FALSE;
  1730. if (bSet)
  1731. {
  1732. if ((dwMask & SSF_SHOWALLOBJECTS) && (g_pShellState->ss.fShowAllObjects != lpss->fShowAllObjects))
  1733. {
  1734. g_pShellState->ss.fShowAllObjects = lpss->fShowAllObjects;
  1735. fSaveAdvanced = TRUE;
  1736. }
  1737. if ((dwMask & SSF_SHOWSYSFILES) && (g_pShellState->ss.fShowSysFiles != lpss->fShowSysFiles))
  1738. {
  1739. g_pShellState->ss.fShowSysFiles = lpss->fShowSysFiles;
  1740. fSaveAdvanced = TRUE;
  1741. }
  1742. if ((dwMask & SSF_SHOWEXTENSIONS) && (g_pShellState->ss.fShowExtensions != lpss->fShowExtensions))
  1743. {
  1744. g_pShellState->ss.fShowExtensions = lpss->fShowExtensions;
  1745. fSaveAdvanced = TRUE;
  1746. }
  1747. if ((dwMask & SSF_SHOWCOMPCOLOR) && (g_pShellState->ss.fShowCompColor != lpss->fShowCompColor))
  1748. {
  1749. g_pShellState->ss.fShowCompColor = lpss->fShowCompColor;
  1750. fSaveAdvanced = TRUE;
  1751. }
  1752. if ((dwMask & SSF_NOCONFIRMRECYCLE) && (g_pShellState->ss.fNoConfirmRecycle != lpss->fNoConfirmRecycle))
  1753. {
  1754. if (!SHRestricted(REST_BITBUCKCONFIRMDELETE))
  1755. {
  1756. g_pShellState->ss.fNoConfirmRecycle = lpss->fNoConfirmRecycle;
  1757. fSave = TRUE;
  1758. }
  1759. }
  1760. if ((dwMask & SSF_DOUBLECLICKINWEBVIEW) && (g_pShellState->ss.fDoubleClickInWebView != lpss->fDoubleClickInWebView))
  1761. {
  1762. if (!SHRestricted(REST_CLASSICSHELL))
  1763. {
  1764. g_pShellState->ss.fDoubleClickInWebView = lpss->fDoubleClickInWebView;
  1765. fSave = TRUE;
  1766. }
  1767. }
  1768. if ((dwMask & SSF_DESKTOPHTML) && (g_pShellState->ss.fDesktopHTML != lpss->fDesktopHTML))
  1769. {
  1770. if (!SHRestricted(REST_NOACTIVEDESKTOP) && !SHRestricted(REST_CLASSICSHELL)
  1771. && !SHRestricted(REST_FORCEACTIVEDESKTOPON))
  1772. {
  1773. g_pShellState->ss.fDesktopHTML = lpss->fDesktopHTML;
  1774. fSave = TRUE;
  1775. }
  1776. }
  1777. if ((dwMask & SSF_WIN95CLASSIC) && (g_pShellState->ss.fWin95Classic != lpss->fWin95Classic))
  1778. {
  1779. if (!SHRestricted(REST_CLASSICSHELL))
  1780. {
  1781. g_pShellState->ss.fWin95Classic = lpss->fWin95Classic;
  1782. fSave = TRUE;
  1783. }
  1784. }
  1785. if ((dwMask & SSF_WEBVIEW) && (g_pShellState->ss.fWebView != lpss->fWebView))
  1786. {
  1787. if (!SHRestricted(REST_NOWEBVIEW) && !SHRestricted(REST_CLASSICSHELL))
  1788. {
  1789. g_pShellState->ss.fWebView = lpss->fWebView;
  1790. fSaveAdvanced = TRUE;
  1791. }
  1792. }
  1793. if ((dwMask & SSF_DONTPRETTYPATH) && (g_pShellState->ss.fDontPrettyPath != lpss->fDontPrettyPath))
  1794. {
  1795. g_pShellState->ss.fDontPrettyPath = lpss->fDontPrettyPath;
  1796. fSaveAdvanced = TRUE;
  1797. }
  1798. if ((dwMask & SSF_SHOWINFOTIP) && (g_pShellState->ss.fShowInfoTip != lpss->fShowInfoTip))
  1799. {
  1800. g_pShellState->ss.fShowInfoTip = lpss->fShowInfoTip;
  1801. fSaveAdvanced = TRUE;
  1802. }
  1803. if ((dwMask & SSF_HIDEICONS) && (g_pShellState->ss.fHideIcons != lpss->fHideIcons))
  1804. {
  1805. g_pShellState->ss.fHideIcons = lpss->fHideIcons;
  1806. fSaveAdvanced = TRUE;
  1807. }
  1808. if ((dwMask & SSF_MAPNETDRVBUTTON) && (g_pShellState->ss.fMapNetDrvBtn != lpss->fMapNetDrvBtn))
  1809. {
  1810. g_pShellState->ss.fMapNetDrvBtn = lpss->fMapNetDrvBtn;
  1811. fSaveAdvanced = TRUE;
  1812. }
  1813. if ((dwMask & SSF_SORTCOLUMNS) &&
  1814. ((g_pShellState->ss.lParamSort != lpss->lParamSort) || (g_pShellState->ss.iSortDirection != lpss->iSortDirection)))
  1815. {
  1816. g_pShellState->ss.iSortDirection = lpss->iSortDirection;
  1817. g_pShellState->ss.lParamSort = lpss->lParamSort;
  1818. fSave = TRUE;
  1819. }
  1820. if (dwMask & SSF_HIDDENFILEEXTS)
  1821. {
  1822. // Setting hidden extensions is not supported
  1823. }
  1824. if ((dwMask & SSF_FILTER) && (g_pShellState->ss.fFilter != lpss->fFilter))
  1825. {
  1826. g_pShellState->ss.fFilter = lpss->fFilter;
  1827. fSaveAdvanced = TRUE;
  1828. }
  1829. if ((dwMask & SSF_SHOWSUPERHIDDEN) && (g_pShellState->ss.fShowSuperHidden != lpss->fShowSuperHidden))
  1830. {
  1831. g_pShellState->ss.fShowSuperHidden = lpss->fShowSuperHidden;
  1832. fSaveAdvanced = TRUE;
  1833. }
  1834. if ((dwMask & SSF_SEPPROCESS) && (g_pShellState->ss.fSepProcess != lpss->fSepProcess))
  1835. {
  1836. g_pShellState->ss.fSepProcess = lpss->fSepProcess;
  1837. fSaveAdvanced = TRUE;
  1838. }
  1839. if ((dwMask & SSF_NONETCRAWLING) && (g_pShellState->ss.fNoNetCrawling != lpss->fNoNetCrawling))
  1840. {
  1841. g_pShellState->ss.fNoNetCrawling = lpss->fNoNetCrawling;
  1842. fSaveAdvanced = TRUE;
  1843. }
  1844. if ((dwMask & SSF_STARTPANELON) && (g_pShellState->ss.fStartPanelOn != lpss->fStartPanelOn))
  1845. {
  1846. g_pShellState->ss.fStartPanelOn = lpss->fStartPanelOn;
  1847. fSaveAdvanced = TRUE;
  1848. }
  1849. if ((dwMask & SSF_SHOWSTARTPAGE) && (g_pShellState->ss.fShowStartPage != lpss->fShowStartPage))
  1850. {
  1851. g_pShellState->ss.fShowStartPage = lpss->fShowStartPage;
  1852. fSaveAdvanced = TRUE;
  1853. }
  1854. }
  1855. if (fUpdateShellStateInReg || fSave || fSaveAdvanced)
  1856. {
  1857. // Write out the SHELLSTATE even if only fSaveAdvanced just to
  1858. // make sure everything stays in sync.
  1859. // We save 8 extra bytes for the ExcludeFileExts stuff.
  1860. // Oh well.
  1861. SKSetValue(SHELLKEY_HKCU_EXPLORER, NULL, TEXT("ShellState"), REG_BINARY, g_pShellState, g_pShellState->cbSize);
  1862. }
  1863. if (fUpdateShellStateInReg || fSaveAdvanced)
  1864. {
  1865. // SHRefreshSettingsPriv overwrites the SHELLSTATE values with whatever
  1866. // the user specifies in View.FolderOptions.View.AdvancedSettings dialog.
  1867. // These values are stored elsewhere in the registry, so we
  1868. // better migrate SHELLSTATE to that part of the registry now.
  1869. //
  1870. // Might as well only do this if the registry settings we care about change.
  1871. Install_AdvancedShellSettings(&g_pShellState->ss);
  1872. }
  1873. if (fSave || fSaveAdvanced)
  1874. {
  1875. // Let apps know the state has changed.
  1876. SHRefreshSettings();
  1877. SHSendMessageBroadcast(WM_SETTINGCHANGE, 0, (LPARAM)TEXT("ShellState"));
  1878. }
  1879. if (!bSet)
  1880. {
  1881. if (dwMask & SSF_SHOWEXTENSIONS)
  1882. {
  1883. lpss->fShowExtensions = g_pShellState->ss.fShowExtensions;
  1884. }
  1885. if (dwMask & SSF_SHOWALLOBJECTS)
  1886. {
  1887. lpss->fShowAllObjects = g_pShellState->ss.fShowAllObjects; // users "show hidden" setting
  1888. }
  1889. if (dwMask & SSF_SHOWSYSFILES)
  1890. {
  1891. lpss->fShowSysFiles = g_pShellState->ss.fShowSysFiles; // this is ignored
  1892. }
  1893. if (dwMask & SSF_SHOWCOMPCOLOR)
  1894. {
  1895. lpss->fShowCompColor = g_pShellState->ss.fShowCompColor;
  1896. }
  1897. if (dwMask & SSF_NOCONFIRMRECYCLE)
  1898. {
  1899. lpss->fNoConfirmRecycle = SHRestricted(REST_BITBUCKCONFIRMDELETE) ? FALSE : g_pShellState->ss.fNoConfirmRecycle;
  1900. }
  1901. if (dwMask & SSF_DOUBLECLICKINWEBVIEW)
  1902. {
  1903. lpss->fDoubleClickInWebView = g_pShellState->ss.fDoubleClickInWebView;
  1904. }
  1905. if (dwMask & SSF_DESKTOPHTML)
  1906. {
  1907. lpss->fDesktopHTML = g_pShellState->ss.fDesktopHTML;
  1908. }
  1909. if (dwMask & SSF_WIN95CLASSIC)
  1910. {
  1911. lpss->fWin95Classic = g_pShellState->ss.fWin95Classic;
  1912. }
  1913. if (dwMask & SSF_WEBVIEW)
  1914. {
  1915. lpss->fWebView = g_pShellState->ss.fWebView;
  1916. }
  1917. if (dwMask & SSF_DONTPRETTYPATH)
  1918. {
  1919. lpss->fDontPrettyPath = g_pShellState->ss.fDontPrettyPath;
  1920. }
  1921. if (dwMask & SSF_SHOWINFOTIP)
  1922. {
  1923. lpss->fShowInfoTip = g_pShellState->ss.fShowInfoTip;
  1924. }
  1925. if (dwMask & SSF_HIDEICONS)
  1926. {
  1927. lpss->fHideIcons = g_pShellState->ss.fHideIcons;
  1928. }
  1929. if (dwMask & SSF_MAPNETDRVBUTTON)
  1930. {
  1931. lpss->fMapNetDrvBtn = g_pShellState->ss.fMapNetDrvBtn;
  1932. }
  1933. if (dwMask & SSF_SORTCOLUMNS)
  1934. {
  1935. lpss->iSortDirection = g_pShellState->ss.iSortDirection;
  1936. lpss->lParamSort = g_pShellState->ss.lParamSort;
  1937. }
  1938. if (dwMask & SSF_FILTER)
  1939. {
  1940. lpss->fFilter = g_pShellState->ss.fFilter;
  1941. }
  1942. if (dwMask & SSF_SHOWSUPERHIDDEN)
  1943. {
  1944. lpss->fShowSuperHidden = g_pShellState->ss.fShowSuperHidden;
  1945. }
  1946. if (dwMask & SSF_SEPPROCESS)
  1947. {
  1948. lpss->fSepProcess = g_pShellState->ss.fSepProcess;
  1949. }
  1950. if (dwMask & SSF_NONETCRAWLING)
  1951. {
  1952. lpss->fNoNetCrawling = g_pShellState->ss.fNoNetCrawling;
  1953. }
  1954. if (dwMask & SSF_STARTPANELON)
  1955. {
  1956. lpss->fStartPanelOn = g_pShellState->ss.fStartPanelOn;
  1957. }
  1958. if (dwMask & SSF_SHOWSTARTPAGE)
  1959. {
  1960. lpss->fShowStartPage = g_pShellState->ss.fShowStartPage;
  1961. }
  1962. }
  1963. }
  1964. // A public version of the Get function so ISVs can track the shell flag state
  1965. //
  1966. STDAPI_(void) SHGetSettings(LPSHELLFLAGSTATE lpsfs, DWORD dwMask)
  1967. {
  1968. if (lpsfs)
  1969. {
  1970. SHELLSTATE ss={0};
  1971. // SSF_HIDDENFILEEXTS and SSF_SORTCOLUMNS don't work with
  1972. // the SHELLFLAGSTATE struct, make sure they are off
  1973. // (because the corresponding SHELLSTATE fields don't
  1974. // exist in SHELLFLAGSTATE.)
  1975. //
  1976. dwMask &= ~(SSF_HIDDENFILEEXTS | SSF_SORTCOLUMNS);
  1977. SHGetSetSettings(&ss, dwMask, FALSE);
  1978. // copy the dword of flags out
  1979. *((DWORD *)lpsfs) = *((DWORD *)(&ss));
  1980. }
  1981. }
  1982. // app compatibility HACK stuff. the following stuff including CheckWinIniForAssocs()
  1983. // is used by the new version of SHDOCVW
  1984. // and EXPLORER.EXE to patch the registry for old win31 apps.
  1985. BOOL _PathIsExe(LPCTSTR pszPath)
  1986. {
  1987. TCHAR szPath[MAX_PATH];
  1988. lstrcpy(szPath, pszPath);
  1989. PathRemoveBlanks(szPath);
  1990. return PathIsExe(szPath);
  1991. }
  1992. // tests to see if pszSubFolder is the same as or a sub folder of pszParent
  1993. // in:
  1994. // pszFolder parent folder to test against
  1995. // this may be a CSIDL value if the HIWORD() is 0
  1996. // pszSubFolder possible sub folder
  1997. //
  1998. // example:
  1999. // TRUE pszFolder = c:\windows, pszSubFolder = c:\windows\system
  2000. // TRUE pszFolder = c:\windows, pszSubFolder = c:\windows
  2001. // FALSE pszFolder = c:\windows, pszSubFolder = c:\winnt
  2002. //
  2003. SHSTDAPI_(BOOL) PathIsEqualOrSubFolder(LPCTSTR pszFolder, LPCTSTR pszSubFolder)
  2004. {
  2005. TCHAR szParent[MAX_PATH], szCommon[MAX_PATH];
  2006. if (!IS_INTRESOURCE(pszFolder))
  2007. lstrcpyn(szParent, pszFolder, ARRAYSIZE(szParent));
  2008. else
  2009. SHGetFolderPath(NULL, PtrToUlong((void *) pszFolder) | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szParent);
  2010. // PathCommonPrefix() always removes the slash on common
  2011. return szParent[0] && PathRemoveBackslash(szParent)
  2012. && PathCommonPrefix(szParent, pszSubFolder, szCommon)
  2013. && lstrcmpi(szParent, szCommon) == 0;
  2014. }
  2015. // pass an array of CSIDL values (-1 terminated)
  2016. STDAPI_(BOOL) PathIsEqualOrSubFolderOf(LPCTSTR pszSubFolder, const UINT rgFolders[], DWORD crgFolders)
  2017. {
  2018. for (DWORD i = 0; i < crgFolders; i++)
  2019. {
  2020. if (PathIsEqualOrSubFolder(MAKEINTRESOURCE(rgFolders[i]), pszSubFolder))
  2021. return TRUE;
  2022. }
  2023. return FALSE;
  2024. }
  2025. // pass an array of CSIDL values (-1 terminated)
  2026. STDAPI_(BOOL) PathIsOneOf(LPCTSTR pszFolder, const UINT rgFolders[], DWORD crgFolders)
  2027. {
  2028. for (DWORD i = 0; i < crgFolders; i++)
  2029. {
  2030. TCHAR szParent[MAX_PATH];
  2031. SHGetFolderPath(NULL, rgFolders[i] | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szParent);
  2032. // the trailing slashes are assumed to match
  2033. if (lstrcmpi(szParent, pszFolder) == 0)
  2034. return TRUE;
  2035. }
  2036. return FALSE;
  2037. }
  2038. // test pszChild against pszParent to see if
  2039. // pszChild is a direct child (one level) of pszParent
  2040. STDAPI_(BOOL) PathIsDirectChildOf(LPCTSTR pszParent, LPCTSTR pszChild)
  2041. {
  2042. BOOL bDirectChild = FALSE;
  2043. TCHAR szParent[MAX_PATH];
  2044. if (!IS_INTRESOURCE(pszParent))
  2045. lstrcpyn(szParent, pszParent, ARRAYSIZE(szParent));
  2046. else
  2047. SHGetFolderPath(NULL, PtrToUlong((void *)pszParent) | CSIDL_FLAG_DONT_VERIFY, NULL, SHGFP_TYPE_CURRENT, szParent);
  2048. if (PathIsRoot(szParent) && (-1 != PathGetDriveNumber(szParent)))
  2049. {
  2050. szParent[2] = 0; // trip D:\ -> D: to make code below work
  2051. }
  2052. INT cchParent = lstrlen(szParent);
  2053. INT cchChild = lstrlen(pszChild);
  2054. if (cchParent <= cchChild)
  2055. {
  2056. TCHAR szChild[MAX_PATH];
  2057. lstrcpyn(szChild, pszChild, ARRAYSIZE(szChild));
  2058. LPTSTR pszChildSlice = szChild + cchParent;
  2059. if (TEXT('\\') == *pszChildSlice)
  2060. {
  2061. *pszChildSlice = 0;
  2062. }
  2063. if (lstrcmpi(szChild, szParent) == 0)
  2064. {
  2065. if (cchParent < cchChild)
  2066. {
  2067. LPTSTR pTmp = pszChildSlice + 1;
  2068. while (*pTmp && *pTmp != TEXT('\\'))
  2069. {
  2070. pTmp++; // find second level path segments
  2071. }
  2072. if (!(*pTmp))
  2073. {
  2074. bDirectChild = TRUE;
  2075. }
  2076. }
  2077. }
  2078. }
  2079. return bDirectChild;
  2080. }
  2081. // many net providers (Vines and PCNFS) don't
  2082. // like "C:\" frormat volumes names, this code returns "C:" format
  2083. // for use with WNet calls
  2084. STDAPI_(LPTSTR) PathBuildSimpleRoot(int iDrive, LPTSTR pszDrive)
  2085. {
  2086. pszDrive[0] = iDrive + TEXT('A');
  2087. pszDrive[1] = TEXT(':');
  2088. pszDrive[2] = 0;
  2089. return pszDrive;
  2090. }
  2091. // Return TRUE for exe, com, bat, pif and lnk.
  2092. BOOL ReservedExtension(LPCTSTR pszExt)
  2093. {
  2094. TCHAR szExt[5]; // Dot+ext+null.
  2095. lstrcpyn(szExt, pszExt, ARRAYSIZE(szExt));
  2096. PathRemoveBlanks(szExt);
  2097. if (PathIsExe(szExt) || (lstrcmpi(szExt, TEXT(".lnk")) == 0))
  2098. {
  2099. return TRUE;
  2100. }
  2101. return FALSE;
  2102. }
  2103. TCHAR const c_szRegPathIniExtensions[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Extensions");
  2104. STDAPI_(LONG) RegSetString(HKEY hk, LPCTSTR pszSubKey, LPCTSTR pszValue)
  2105. {
  2106. return RegSetValue(hk, pszSubKey, REG_SZ, pszValue, (lstrlen(pszValue) + 1) * sizeof(TCHAR));
  2107. }
  2108. STDAPI_(BOOL) RegSetValueString(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPCTSTR psz)
  2109. {
  2110. return (S_OK == SHSetValue(hkey, pszSubKey, pszValue, REG_SZ, psz, CbFromCch(lstrlen(psz) + 1)));
  2111. }
  2112. STDAPI_(BOOL) RegGetValueString(HKEY hkey, LPCTSTR pszSubKey, LPCTSTR pszValue, LPTSTR psz, DWORD cb)
  2113. {
  2114. return (!GetSystemMetrics(SM_CLEANBOOT)
  2115. && (S_OK == SHGetValue(hkey, pszSubKey, pszValue, NULL, psz, &cb)));
  2116. }
  2117. // Returns TRUE if there is a proper shell\open\command for the given
  2118. // extension that matches the given command line.
  2119. // NB This is for MGX Designer which registers an extension and commands for
  2120. // printing but relies on win.ini extensions for Open. We need to detect that
  2121. // there's no open command and add the appropriate entries to the registry.
  2122. // If the given extension maps to a type name we return that in pszTypeName
  2123. // otherwise it will be null.
  2124. // FMH This also affects Asymetric Compel which makes a new .CPL association.
  2125. // We need to merge it up into our Control Panel .CPL association. We depend
  2126. // on Control Panels NOT having a proper Open so users can see both verb sets.
  2127. // NB pszLine is the original line from win.ini eg foo.exe /bar ^.fred, see
  2128. // comments below...
  2129. BOOL Reg_ShellOpenForExtension(LPCTSTR pszExt, LPTSTR pszCmdLine,
  2130. LPTSTR pszTypeName, int cchTypeName, LPCTSTR pszLine)
  2131. {
  2132. TCHAR sz[MAX_PATH];
  2133. TCHAR szExt[MAX_PATH];
  2134. LONG cb;
  2135. if (pszTypeName)
  2136. pszTypeName[0] = 0;
  2137. // Is the extension registed at all?
  2138. cb = sizeof(sz);
  2139. sz[0] = 0;
  2140. if (SHRegQueryValue(HKEY_CLASSES_ROOT, pszExt, sz, &cb) == ERROR_SUCCESS)
  2141. {
  2142. // Is there a file type?
  2143. if (*sz)
  2144. {
  2145. // Yep, check there.
  2146. // DebugMsg(DM_TRACE, "c.r_rofe: Extension has a file type name %s.", sz);
  2147. lstrcpy(szExt, sz);
  2148. if (pszTypeName)
  2149. lstrcpyn(pszTypeName, sz, cchTypeName);
  2150. }
  2151. else
  2152. {
  2153. // No, check old style associations.
  2154. // DebugMsg(DM_TRACE, "c.r_rofe: Extension has no file type name.", pszExt);
  2155. lstrcpy(szExt, pszExt);
  2156. }
  2157. // See if there's an open command.
  2158. lstrcat(szExt, TEXT("\\shell\\open\\command"));
  2159. cb = sizeof(sz);
  2160. if (SHRegQueryValue(HKEY_CLASSES_ROOT, szExt, sz, &cb) == ERROR_SUCCESS)
  2161. {
  2162. // DebugMsg(DM_TRACE, "c.r_rofe: Extension %s already registed with an open command.", pszExt);
  2163. // NB We want to compare the paths only, not the %1 stuff.
  2164. if (PathIsRelative(pszCmdLine))
  2165. {
  2166. int cch;
  2167. // If a relative path was passed in, we may have a fully qualifed
  2168. // one that is now in the registry... In that case we should
  2169. // say that it matches...
  2170. LPTSTR pszT = PathGetArgs(sz);
  2171. if (pszT)
  2172. {
  2173. *(pszT-1) = 0;
  2174. }
  2175. PathUnquoteSpaces(sz);
  2176. PathRemoveBlanks(pszCmdLine);
  2177. cch = lstrlen(sz) - lstrlen(pszCmdLine);
  2178. if ((cch >= 0) && (lstrcmpi(sz+cch, pszCmdLine) == 0))
  2179. {
  2180. // DebugMsg(DM_TRACE, "c.r_rofe: Open commands match.");
  2181. return TRUE;
  2182. }
  2183. lstrcat(pszCmdLine, TEXT(" ")); // Append blank back on...
  2184. }
  2185. else
  2186. {
  2187. // If absolute path we can cheat for matches
  2188. *(sz+lstrlen(pszCmdLine)) = 0;
  2189. if (lstrcmpi(sz, pszCmdLine) == 0)
  2190. {
  2191. // DebugMsg(DM_TRACE, "c.r_rofe: Open commands match.");
  2192. return TRUE;
  2193. }
  2194. }
  2195. // DebugMsg(DM_TRACE, "c.r_rofe: Open commands don't match.");
  2196. // Open commands don't match, check to see if it's because the ini
  2197. // changed (return FALSE so the change is reflected in the registry) or
  2198. // if the registry changed (return TRUE so we keep the registry the way
  2199. // it is.
  2200. if (RegGetValueString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, pszExt, sz, ARRAYSIZE(sz)))
  2201. {
  2202. if (lstrcmpi(sz, pszLine) == 0)
  2203. return TRUE;
  2204. }
  2205. return FALSE;
  2206. }
  2207. else
  2208. {
  2209. // DebugMsg(DM_TRACE, "c.r_rofe: Extension %s already registed but with no open command.", pszExt);
  2210. return FALSE;
  2211. }
  2212. }
  2213. // DebugMsg(DM_TRACE, "c.r_rofe: No open command for %s.", pszExt);
  2214. return FALSE;
  2215. }
  2216. // This function will read in the extensions section of win.ini to see if
  2217. // there are any old style associations that we have not accounted for.
  2218. // NB Some apps mess up if their extensions magically disappear from the
  2219. // extensions section so DON'T DELETE the old entries from win.ini.
  2220. //
  2221. // Since this is for win3.1 compat, CWIFA_SIZE should be enough (it has been so far...)
  2222. //
  2223. #define CWIFA_SIZE 4096
  2224. STDAPI_(void) CheckWinIniForAssocs(void)
  2225. {
  2226. LPTSTR pszBuf;
  2227. int cbRet;
  2228. LPTSTR pszLine;
  2229. TCHAR szExtension[MAX_PATH];
  2230. TCHAR szTypeName[MAX_PATH];
  2231. TCHAR szCmdLine[MAX_PATH];
  2232. LPTSTR pszExt;
  2233. LPTSTR pszT;
  2234. BOOL fAssocsMade = FALSE;
  2235. szExtension[0]=TEXT('.');
  2236. szExtension[1]=0;
  2237. pszBuf = (LPTSTR)LocalAlloc(LPTR, CWIFA_SIZE*sizeof(TCHAR));
  2238. if (!pszBuf)
  2239. return; // Could not allocate the memory
  2240. cbRet = (int)GetProfileSection(TEXT("Extensions"), pszBuf, CWIFA_SIZE);
  2241. //
  2242. // We now walk through the list to find any items that is not
  2243. // in the registry.
  2244. //
  2245. for (pszLine = pszBuf; *pszLine; pszLine += lstrlen(pszLine)+1)
  2246. {
  2247. // Get the extension for this file into a buffer.
  2248. pszExt = StrChr(pszLine, TEXT('='));
  2249. if (pszExt == NULL)
  2250. continue; // skip this line
  2251. szExtension[0]=TEXT('.');
  2252. // lstrcpyn will put the null terminator for us.
  2253. // We should now have something like .xls in szExtension.
  2254. lstrcpyn(szExtension+1, pszLine, (int)(pszExt-pszLine)+1);
  2255. // Ignore extensions bigger than dot + 3 chars.
  2256. if (lstrlen(szExtension) > 4)
  2257. {
  2258. DebugMsg(DM_ERROR, TEXT("CheckWinIniForAssocs: Invalid extension, skipped."));
  2259. continue;
  2260. }
  2261. pszLine = pszExt+1; // Points to after the =;
  2262. while (*pszLine == TEXT(' '))
  2263. pszLine++; // skip blanks
  2264. // Now find the ^ in the command line.
  2265. pszExt = StrChr(pszLine, TEXT('^'));
  2266. if (pszExt == NULL)
  2267. continue; // dont process
  2268. // Now setup the command line
  2269. // WARNING: This assumes only 1 ^ and it assumes the extension...
  2270. lstrcpyn(szCmdLine, pszLine, (int)(pszExt-pszLine)+1);
  2271. // Don't bother moving over invalid entries (like the busted .hlp
  2272. // entry VB 3.0 creates).
  2273. if (!_PathIsExe(szCmdLine))
  2274. {
  2275. DebugMsg(DM_ERROR, TEXT("c.cwia: Invalid app, skipped."));
  2276. continue;
  2277. }
  2278. if (ReservedExtension(szExtension))
  2279. {
  2280. DebugMsg(DM_ERROR, TEXT("c.cwia: Invalid extension (%s), skipped."), szExtension);
  2281. continue;
  2282. }
  2283. // Now see if there is already a mapping for this extension.
  2284. if (Reg_ShellOpenForExtension(szExtension, szCmdLine, szTypeName, ARRAYSIZE(szTypeName), pszLine))
  2285. {
  2286. // Yep, Setup the initial list of ini extensions in the registry if they are
  2287. // not there already.
  2288. if (!RegGetValueString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, szExtension, szTypeName, sizeof(szTypeName)))
  2289. {
  2290. RegSetValueString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, szExtension, pszLine);
  2291. }
  2292. continue;
  2293. }
  2294. // No mapping.
  2295. // HACK for Expert Home Design. They put an association in win.ini
  2296. // (which we propagate as typeless) but then register a type and a
  2297. // print command the first time they run - stomping on our propagated
  2298. // Open command. The fix is to put their open command under the proper
  2299. // type instead of leaving it typeless.
  2300. if (lstrcmpi(szExtension, TEXT(".dgw")) == 0)
  2301. {
  2302. if (lstrcmpi(PathFindFileName(szCmdLine), TEXT("designw.exe ")) == 0)
  2303. {
  2304. // Put in a ProgID for them.
  2305. RegSetValue(HKEY_CLASSES_ROOT, szExtension, REG_SZ, TEXT("HDesign"), 0L);
  2306. // Force Open command under their ProgID.
  2307. TraceMsg(DM_TRACE, "c.cwifa: Expert Home Design special case hit.");
  2308. lstrcpy(szTypeName, TEXT("HDesign"));
  2309. }
  2310. }
  2311. //
  2312. // HACK for Windows OrgChart which does not register OLE1 class
  2313. // if ".WOC" is registered in the registry.
  2314. //
  2315. if (lstrcmpi(szExtension, TEXT(".WOC")) == 0)
  2316. {
  2317. if (lstrcmpi(PathFindFileName(szCmdLine), TEXT("WINORG.EXE ")) == 0)
  2318. {
  2319. DebugMsg(DM_ERROR, TEXT("c.cwia: HACK: Found WINORG (%s, %s), skipped."), szExtension, pszLine);
  2320. continue;
  2321. }
  2322. }
  2323. // Record that we're about to move things over in the registry so we won't keep
  2324. // doing it all the time.
  2325. RegSetValueString(HKEY_LOCAL_MACHINE, c_szRegPathIniExtensions, szExtension, pszLine);
  2326. lstrcat(szCmdLine, TEXT("%1"));
  2327. // see if there are anything else to copy out...
  2328. pszExt++; // get beyond the ^
  2329. pszT = szExtension;
  2330. while (*pszExt && (CharLowerChar(*pszExt) == CharLowerChar(*pszT)))
  2331. {
  2332. // Look for the next character...
  2333. pszExt++;
  2334. pszT++;
  2335. }
  2336. if (*pszExt)
  2337. lstrcat(szCmdLine, pszExt); // add the rest onto the command line
  2338. // Now lets make the actual association.
  2339. // We need to add on the right stuff onto the key...
  2340. if (*szTypeName)
  2341. lstrcpy(szExtension, szTypeName);
  2342. lstrcat(szExtension, TEXT("\\shell\\open\\command"));
  2343. RegSetValue(HKEY_CLASSES_ROOT, szExtension, REG_SZ, szCmdLine, 0L);
  2344. // DebugMsg(DM_TRACE, "c.cwifa: %s %s", szExtension, szCmdLine);
  2345. fAssocsMade = TRUE;
  2346. }
  2347. // If we made any associations we should let the cabinet know.
  2348. //
  2349. // Now call off to the notify function.
  2350. if (fAssocsMade)
  2351. SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
  2352. // And cleanup our allocation
  2353. LocalFree((HLOCAL)pszBuf);
  2354. }
  2355. typedef struct
  2356. {
  2357. INT iFlag;
  2358. LPCTSTR pszKey;
  2359. LPCTSTR pszValue;
  2360. } RESTRICTIONITEMS;
  2361. #define SZ_RESTRICTED_ACTIVEDESKTOP L"ActiveDesktop"
  2362. #define REGSTR_VAL_RESTRICTRUNW L"RestrictRun"
  2363. #define REGSTR_VAL_PRINTERS_HIDETABSW L"NoPrinterTabs"
  2364. #define REGSTR_VAL_PRINTERS_NODELETEW L"NoDeletePrinter"
  2365. #define REGSTR_VAL_PRINTERS_NOADDW L"NoAddPrinter"
  2366. const SHRESTRICTIONITEMS c_rgRestrictionItems[] =
  2367. {
  2368. {REST_NORUN, L"Explorer", L"NoRun"},
  2369. {REST_NOCLOSE, L"Explorer", L"NoClose"},
  2370. {REST_NOSAVESET , L"Explorer", L"NoSaveSettings"},
  2371. {REST_NOFILEMENU, L"Explorer", L"NoFileMenu"},
  2372. {REST_NOSETFOLDERS, L"Explorer", L"NoSetFolders"},
  2373. {REST_NOSETTASKBAR, L"Explorer", L"NoSetTaskbar"},
  2374. {REST_NODESKTOP, L"Explorer", L"NoDesktop"},
  2375. {REST_NOFIND, L"Explorer", L"NoFind"},
  2376. {REST_NODRIVES, L"Explorer", L"NoDrives"},
  2377. {REST_NODRIVEAUTORUN, L"Explorer", L"NoDriveAutoRun"},
  2378. {REST_NODRIVETYPEAUTORUN, L"Explorer", L"NoDriveTypeAutoRun"},
  2379. {REST_NONETHOOD, L"Explorer", L"NoNetHood"},
  2380. {REST_STARTBANNER, L"Explorer", L"NoStartBanner"},
  2381. {REST_RESTRICTRUN, L"Explorer", REGSTR_VAL_RESTRICTRUNW},
  2382. {REST_NOPRINTERTABS, L"Explorer", REGSTR_VAL_PRINTERS_HIDETABSW},
  2383. {REST_NOPRINTERDELETE, L"Explorer", REGSTR_VAL_PRINTERS_NODELETEW},
  2384. {REST_NOPRINTERADD, L"Explorer", REGSTR_VAL_PRINTERS_NOADDW},
  2385. {REST_NOSTARTMENUSUBFOLDERS, L"Explorer", L"NoStartMenuSubFolders"},
  2386. {REST_MYDOCSONNET, L"Explorer", L"MyDocsOnNet"},
  2387. {REST_NOEXITTODOS, L"WinOldApp", L"NoRealMode"},
  2388. {REST_ENFORCESHELLEXTSECURITY, L"Explorer", L"EnforceShellExtensionSecurity"},
  2389. {REST_NOCOMMONGROUPS, L"Explorer", L"NoCommonGroups"},
  2390. {REST_LINKRESOLVEIGNORELINKINFO,L"Explorer", L"LinkResolveIgnoreLinkInfo"},
  2391. {REST_NOWEB, L"Explorer", L"NoWebMenu"},
  2392. {REST_NOTRAYCONTEXTMENU, L"Explorer", L"NoTrayContextMenu"},
  2393. {REST_NOVIEWCONTEXTMENU, L"Explorer", L"NoViewContextMenu"},
  2394. {REST_NONETCONNECTDISCONNECT, L"Explorer", L"NoNetConnectDisconnect"},
  2395. {REST_STARTMENULOGOFF, L"Explorer", L"StartMenuLogoff"},
  2396. {REST_NOSETTINGSASSIST, L"Explorer", L"NoSettingsWizards"},
  2397. {REST_NODISCONNECT, L"Explorer", L"NoDisconnect"},
  2398. {REST_NOSECURITY, L"Explorer", L"NoNTSecurity" },
  2399. {REST_NOFILEASSOCIATE, L"Explorer", L"NoFileAssociate" },
  2400. // New for IE4
  2401. {REST_NOINTERNETICON, L"Explorer", L"NoInternetIcon"},
  2402. {REST_NORECENTDOCSHISTORY, L"Explorer", L"NoRecentDocsHistory"},
  2403. {REST_NORECENTDOCSMENU, L"Explorer", L"NoRecentDocsMenu"},
  2404. {REST_NOACTIVEDESKTOP, L"Explorer", L"NoActiveDesktop"},
  2405. {REST_NOACTIVEDESKTOPCHANGES, L"Explorer", L"NoActiveDesktopChanges"},
  2406. {REST_NOFAVORITESMENU, L"Explorer", L"NoFavoritesMenu"},
  2407. {REST_CLEARRECENTDOCSONEXIT, L"Explorer", L"ClearRecentDocsOnExit"},
  2408. {REST_CLASSICSHELL, L"Explorer", L"ClassicShell"},
  2409. {REST_NOCUSTOMIZEWEBVIEW, L"Explorer", L"NoCustomizeWebView"},
  2410. {REST_NOHTMLWALLPAPER, SZ_RESTRICTED_ACTIVEDESKTOP, L"NoHTMLWallPaper"},
  2411. {REST_NOCHANGINGWALLPAPER, SZ_RESTRICTED_ACTIVEDESKTOP, L"NoChangingWallPaper"},
  2412. {REST_NODESKCOMP, SZ_RESTRICTED_ACTIVEDESKTOP, L"NoComponents"},
  2413. {REST_NOADDDESKCOMP, SZ_RESTRICTED_ACTIVEDESKTOP, L"NoAddingComponents"},
  2414. {REST_NODELDESKCOMP, SZ_RESTRICTED_ACTIVEDESKTOP, L"NoDeletingComponents"},
  2415. {REST_NOCLOSEDESKCOMP, SZ_RESTRICTED_ACTIVEDESKTOP, L"NoClosingComponents"},
  2416. {REST_NOCLOSE_DRAGDROPBAND, L"Explorer", L"NoCloseDragDropBands"},
  2417. {REST_NOMOVINGBAND, L"Explorer", L"NoMovingBands"},
  2418. {REST_NOEDITDESKCOMP, SZ_RESTRICTED_ACTIVEDESKTOP, L"NoEditingComponents"},
  2419. {REST_NORESOLVESEARCH, L"Explorer", L"NoResolveSearch"},
  2420. {REST_NORESOLVETRACK, L"Explorer", L"NoResolveTrack"},
  2421. {REST_FORCECOPYACLWITHFILE, L"Explorer", L"ForceCopyACLWithFile"},
  2422. {REST_NOLOGO3CHANNELNOTIFY, L"Explorer", L"NoMSAppLogo5ChannelNotify"},
  2423. {REST_NOFORGETSOFTWAREUPDATE, L"Explorer", L"NoForgetSoftwareUpdate"},
  2424. {REST_GREYMSIADS, L"Explorer", L"GreyMSIAds"},
  2425. // More start menu Restritions for 4.01
  2426. {REST_NOSETACTIVEDESKTOP, L"Explorer", L"NoSetActiveDesktop"},
  2427. {REST_NOUPDATEWINDOWS, L"Explorer", L"NoWindowsUpdate"},
  2428. {REST_NOCHANGESTARMENU, L"Explorer", L"NoChangeStartMenu"},
  2429. {REST_NOFOLDEROPTIONS, L"Explorer", L"NoFolderOptions"},
  2430. {REST_NOCSC, L"Explorer", L"NoSyncAll"},
  2431. // NT5 shell restrictions
  2432. {REST_HASFINDCOMPUTERS, L"Explorer", L"FindComputers"},
  2433. {REST_RUNDLGMEMCHECKBOX, L"Explorer", L"MemCheckBoxInRunDlg"},
  2434. {REST_INTELLIMENUS, L"Explorer", L"IntelliMenus"},
  2435. {REST_SEPARATEDESKTOPPROCESS, L"Explorer", L"SeparateProcess"}, // this one was actually checked in IE4 in shdocvw, but not here. Duh.
  2436. {REST_MaxRecentDocs, L"Explorer", L"MaxRecentDocs"},
  2437. {REST_NOCONTROLPANEL, L"Explorer", L"NoControlPanel"}, // Remove only the control panel from the Settings menu
  2438. {REST_ENUMWORKGROUP, L"Explorer", L"EnumWorkgroup"},
  2439. {REST_ARP_ShowPostSetup, L"Uninstall", L"ShowPostSetup"},
  2440. {REST_ARP_NOARP, L"Uninstall", L"NoAddRemovePrograms"},
  2441. {REST_ARP_NOREMOVEPAGE, L"Uninstall", L"NoRemovePage"},
  2442. {REST_ARP_NOADDPAGE, L"Uninstall", L"NoAddPage"},
  2443. {REST_ARP_NOWINSETUPPAGE, L"Uninstall", L"NoWindowsSetupPage"},
  2444. {REST_NOCHANGEMAPPEDDRIVELABEL, L"Explorer", L"NoChangeMappedDriveLabel"},
  2445. {REST_NOCHANGEMAPPEDDRIVECOMMENT, L"Explorer", L"NoChangeMappedDriveComment"},
  2446. {REST_NONETWORKCONNECTIONS, L"Explorer", L"NoNetworkConnections"},
  2447. {REST_FORCESTARTMENULOGOFF, L"Explorer", L"ForceStartMenuLogoff"},
  2448. {REST_NOWEBVIEW, L"Explorer", L"NoWebView"},
  2449. {REST_NOCUSTOMIZETHISFOLDER, L"Explorer", L"NoCustomizeThisFolder"},
  2450. {REST_NOENCRYPTION, L"Explorer", L"NoEncryption"},
  2451. {REST_DONTSHOWSUPERHIDDEN, L"Explorer", L"DontShowSuperHidden"},
  2452. {REST_NOSHELLSEARCHBUTTON, L"Explorer", L"NoShellSearchButton"},
  2453. {REST_NOHARDWARETAB, L"Explorer", L"NoHardwareTab"},
  2454. {REST_NORUNASINSTALLPROMPT, L"Explorer", L"NoRunasInstallPrompt"},
  2455. {REST_PROMPTRUNASINSTALLNETPATH, L"Explorer", L"PromptRunasInstallNetPath"},
  2456. {REST_NOMANAGEMYCOMPUTERVERB, L"Explorer", L"NoManageMyComputerVerb"},
  2457. {REST_NORECENTDOCSNETHOOD, L"Explorer", L"NoRecentDocsNetHood"},
  2458. {REST_DISALLOWRUN, L"Explorer", L"DisallowRun"},
  2459. {REST_NOWELCOMESCREEN, L"Explorer", L"NoWelcomeScreen"},
  2460. {REST_RESTRICTCPL, L"Explorer", L"RestrictCpl"},
  2461. {REST_DISALLOWCPL, L"Explorer", L"DisallowCpl"},
  2462. {REST_NOSMBALLOONTIP, L"Explorer", L"NoSMBalloonTip"},
  2463. {REST_NOSMHELP, L"Explorer", L"NoSMHelp"},
  2464. {REST_NOWINKEYS, L"Explorer", L"NoWinKeys"},
  2465. {REST_NOENCRYPTONMOVE, L"Explorer", L"NoEncryptOnMove"},
  2466. {REST_NOLOCALMACHINERUN, L"Explorer", L"DisableLocalMachineRun"},
  2467. {REST_NOCURRENTUSERRUN, L"Explorer", L"DisableCurrentUserRun"},
  2468. {REST_NOLOCALMACHINERUNONCE, L"Explorer", L"DisableLocalMachineRunOnce"},
  2469. {REST_NOCURRENTUSERRUNONCE, L"Explorer", L"DisableCurrentUserRunOnce"},
  2470. {REST_FORCEACTIVEDESKTOPON, L"Explorer", L"ForceActiveDesktopOn"},
  2471. {REST_NOCOMPUTERSNEARME, L"Explorer", L"NoComputersNearMe"},
  2472. {REST_NOVIEWONDRIVE, L"Explorer", L"NoViewOnDrive"},
  2473. // Millennium shell restrictions
  2474. // Exception: REST_NOSMMYDOCS is also supported on NT5.
  2475. {REST_NONETCRAWL, L"Explorer", L"NoNetCrawling"},
  2476. {REST_NOSHAREDDOCUMENTS, L"Explorer", L"NoSharedDocuments"},
  2477. {REST_NOSMMYDOCS, L"Explorer", L"NoSMMyDocs"},
  2478. {REST_NOSMMYPICS, L"Explorer", L"NoSMMyPictures"},
  2479. {REST_ALLOWBITBUCKDRIVES, L"Explorer", L"RecycleBinDrives"},
  2480. // These next few restrictions are mixed between Neptune and Millennium
  2481. // (Isn't simultaneous development fun?)
  2482. {REST_NONLEGACYSHELLMODE, L"Explorer", L"NoneLegacyShellMode"}, // Neptune
  2483. {REST_NOCONTROLPANELBARRICADE, L"Explorer", L"NoControlPanelBarricade"}, // Millennium
  2484. {REST_NOAUTOTRAYNOTIFY, L"Explorer", L"NoAutoTrayNotify"}, // traynot.h
  2485. {REST_NOTASKGROUPING, L"Explorer", L"NoTaskGrouping"},
  2486. {REST_NOCDBURNING, L"Explorer", L"NoCDBurning"},
  2487. {REST_MYCOMPNOPROP, L"Explorer", L"NoPropertiesMyComputer"},
  2488. {REST_MYDOCSNOPROP, L"Explorer", L"NoPropertiesMyDocuments"},
  2489. {REST_NODISPLAYAPPEARANCEPAGE, L"System", L"NoDispAppearancePage"},
  2490. {REST_NOTHEMESTAB, L"Explorer", L"NoThemesTab"},
  2491. {REST_NOVISUALSTYLECHOICE, L"System", L"NoVisualStyleChoice"},
  2492. {REST_NOSIZECHOICE, L"System", L"NoSizeChoice"},
  2493. {REST_NOCOLORCHOICE, L"System", L"NoColorChoice"},
  2494. {REST_SETVISUALSTYLE, L"System", L"SetVisualStyle"},
  2495. {REST_STARTRUNNOHOMEPATH, L"Explorer", L"StartRunNoHOMEPATH"},
  2496. {REST_NOSTARTPANEL, L"Explorer", L"NoSimpleStartMenu"},
  2497. {REST_NOUSERNAMEINSTARTPANEL, L"Explorer", L"NoUserNameInStartMenu"},
  2498. {REST_NOMYCOMPUTERICON, L"NonEnum", L"{20D04FE0-3AEA-1069-A2D8-08002B30309D}"},
  2499. {REST_NOSMNETWORKPLACES, L"Explorer", L"NoStartMenuNetworkPlaces"},
  2500. {REST_NOSMPINNEDLIST, L"Explorer", L"NoStartMenuPinnedList"},
  2501. {REST_NOSMMYMUSIC, L"Explorer", L"NoStartMenuMyMusic"},
  2502. {REST_NOSMEJECTPC, L"Explorer", L"NoStartMenuEjectPC"},
  2503. {REST_NOSMMOREPROGRAMS, L"Explorer", L"NoStartMenuMorePrograms"},
  2504. {REST_NOSMMFUPROGRAMS, L"Explorer", L"NoStartMenuMFUprogramsList"},
  2505. {REST_HIDECLOCK, L"Explorer", L"HideClock"},
  2506. {REST_NOLOWDISKSPACECHECKS, L"Explorer", L"NoLowDiskSpaceChecks"},
  2507. {REST_NODESKTOPCLEANUP, L"Explorer", L"NoDesktopCleanupWizard"},
  2508. // NT6 shell restrictions (Whistler)
  2509. {REST_NOENTIRENETWORK, L"Network", L"NoEntireNetwork"}, // Note WNet stores it's policy in "Network".
  2510. {REST_BITBUCKNUKEONDELETE, L"Explorer", L"NoRecycleFiles"},
  2511. {REST_BITBUCKCONFIRMDELETE, L"Explorer", L"ConfirmFileDelete"},
  2512. {REST_BITBUCKNOPROP, L"Explorer", L"NoPropertiesRecycleBin"},
  2513. {REST_NOTRAYITEMSDISPLAY, L"Explorer", L"NoTrayItemsDisplay"}, // traynot.h
  2514. {REST_NOTOOLBARSONTASKBAR, L"Explorer", L"NoToolbarsOnTaskbar"},
  2515. {REST_NODISPBACKGROUND, L"System", L"NoDispBackgroundPage"},
  2516. {REST_NODISPSCREENSAVEPG, L"System", L"NoDispScrSavPage"},
  2517. {REST_NODISPSETTINGSPG, L"System", L"NoDispSettingsPage"},
  2518. {REST_NODISPSCREENSAVEPREVIEW, L"System", L"NoScreenSavePreview"}, // Do not show screen saver previews
  2519. {REST_NODISPLAYCPL, L"System", L"NoDispCPL"}, // Do not show the Display Control Panel at all.
  2520. {REST_HIDERUNASVERB, L"Explorer", L"HideRunAsVerb"},
  2521. {REST_NOTHUMBNAILCACHE, L"Explorer", L"NoThumbnailCache"}, // Do not use a thumbnail cache
  2522. {REST_NOSTRCMPLOGICAL, L"Explorer", L"NoStrCmpLogical"}, // Do not use a logical sorting in the namespace
  2523. // Windows 2000 SP3 shell restriction
  2524. {REST_NOSMCONFIGUREPROGRAMS, L"Explorer", L"NoSMConfigurePrograms"},
  2525. {REST_ALLOWUNHASHEDWEBVIEW, L"Explorer", L"AllowUnhashedWebView"},
  2526. {REST_ALLOWLEGACYWEBVIEW, L"Explorer", L"AllowLegacyWebView"},
  2527. {REST_REVERTWEBVIEWSECURITY, L"Explorer", L"RevertWebViewSecurity"},
  2528. {REST_INHERITCONSOLEHANDLES, L"Explorer", L"InheritConsoleHandles"},
  2529. {0, NULL, NULL},
  2530. };
  2531. DWORD g_rgRestrictionItemValues[ARRAYSIZE(c_rgRestrictionItems) - 1 ] = { 0 };
  2532. EXTERN_C HANDLE g_hRestrictions = NULL;
  2533. LONG g_lProcessRestrictionsCount = -1; // current process's count
  2534. HANDLE _GetRestrictionsCounter()
  2535. {
  2536. return SHGetCachedGlobalCounter(&g_hRestrictions, &GUID_Restrictions);
  2537. }
  2538. BOOL _QueryRestrictionsChanged(void)
  2539. {
  2540. long lGlobalCount = SHGlobalCounterGetValue(_GetRestrictionsCounter());
  2541. if (g_lProcessRestrictionsCount != lGlobalCount)
  2542. {
  2543. g_lProcessRestrictionsCount = lGlobalCount;
  2544. return TRUE;
  2545. }
  2546. return FALSE;
  2547. }
  2548. // Returns DWORD vaolue if any of the specified restrictions are in place.
  2549. // 0 otherwise.
  2550. STDAPI_(DWORD) SHRestricted(RESTRICTIONS rest)
  2551. {
  2552. // The cache may be invalid. Check first! We have to use
  2553. // a global named semaphore in case this function is called
  2554. // from a process other than the shell process. (And we're
  2555. // sharing the same count between shell32 and shdocvw.)
  2556. if (_QueryRestrictionsChanged())
  2557. {
  2558. memset(g_rgRestrictionItemValues, (BYTE)-1, sizeof(g_rgRestrictionItemValues));
  2559. }
  2560. return SHRestrictionLookup(rest, NULL, c_rgRestrictionItems, g_rgRestrictionItemValues);
  2561. }
  2562. STDAPI_(BOOL) SHIsRestricted(HWND hwnd, RESTRICTIONS rest)
  2563. {
  2564. if (SHRestricted(rest))
  2565. {
  2566. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_RESTRICTIONS),
  2567. MAKEINTRESOURCE(IDS_RESTRICTIONSTITLE), MB_OK|MB_ICONSTOP);
  2568. return TRUE;
  2569. }
  2570. return FALSE;
  2571. }
  2572. BOOL UpdateScreenSaver(BOOL bActive, LPCTSTR pszNewSSName, int iNewSSTimeout)
  2573. {
  2574. BOOL bUpdatedSS = FALSE;
  2575. BOOL bCurrentActive;
  2576. TCHAR szCurrentSSPath[MAX_PATH];
  2577. int iCurrentSSTimeout;
  2578. HKEY hk;
  2579. // check the screen saver path
  2580. // first find out what the users screensaver path is set to
  2581. if (RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hk) == ERROR_SUCCESS)
  2582. {
  2583. DWORD cbSize;
  2584. cbSize = sizeof(szCurrentSSPath);
  2585. if (RegQueryValueEx(hk, TEXT("SCRNSAVE.EXE"), NULL, NULL, (LPBYTE)szCurrentSSPath, &cbSize) == ERROR_SUCCESS)
  2586. {
  2587. // if we have a new name, then we might need to override the users current value
  2588. if (pszNewSSName)
  2589. {
  2590. BOOL bTestExpandedPath;
  2591. TCHAR szExpandedSSPath[MAX_PATH];
  2592. // even though SCRNSAVE.EXE is of type REG_SZ, it can contain env variables (sigh)
  2593. bTestExpandedPath = SHExpandEnvironmentStrings(szCurrentSSPath, szExpandedSSPath, ARRAYSIZE(szExpandedSSPath));
  2594. // see if the new string matches the current
  2595. if ((lstrcmpi(pszNewSSName, szCurrentSSPath) != 0) &&
  2596. (!bTestExpandedPath || (lstrcmpi(pszNewSSName, szExpandedSSPath) != 0)))
  2597. {
  2598. // new screensaver string is different from the old, so update the users value w/ the policy setting
  2599. if (RegSetValueEx(hk,
  2600. TEXT("SCRNSAVE.EXE"),
  2601. 0,
  2602. REG_SZ,
  2603. (LPBYTE)pszNewSSName,
  2604. (lstrlen(pszNewSSName) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS)
  2605. {
  2606. bUpdatedSS = TRUE;
  2607. }
  2608. }
  2609. }
  2610. else
  2611. {
  2612. // we do not have a screensaver set via policy. if the user does not have one set, then
  2613. // there is going to be nothing to run! In this case, don't ever activate it
  2614. if ((szCurrentSSPath[0] == TEXT('\0')) ||
  2615. (lstrcmpi(szCurrentSSPath, TEXT("\"\"")) == 0) ||
  2616. (lstrcmpi(szCurrentSSPath, TEXT("none")) == 0) ||
  2617. (lstrcmpi(szCurrentSSPath, TEXT("(none)")) == 0))
  2618. {
  2619. // policy does not specify a screensaver and the user doesn't have one, so do
  2620. // not make the screensaver active.
  2621. bActive = FALSE;
  2622. }
  2623. }
  2624. }
  2625. else
  2626. {
  2627. // user did not have a screensaver registry value
  2628. if (pszNewSSName)
  2629. {
  2630. // update the users value w/ the policy setting
  2631. if (RegSetValueEx(hk,
  2632. TEXT("SCRNSAVE.EXE"),
  2633. 0,
  2634. REG_SZ,
  2635. (LPBYTE)pszNewSSName,
  2636. (lstrlen(pszNewSSName) + 1) * sizeof(TCHAR)) == ERROR_SUCCESS)
  2637. {
  2638. bUpdatedSS = TRUE;
  2639. }
  2640. else
  2641. {
  2642. // if we failed to set the screensaver then do not make it active
  2643. bActive = FALSE;
  2644. }
  2645. }
  2646. else
  2647. {
  2648. // policy does not specify a screensaver and the user doesn't have one, so do
  2649. // not make the screensaver active.
  2650. bActive = FALSE;
  2651. }
  2652. }
  2653. RegCloseKey(hk);
  2654. }
  2655. // check the timeout value
  2656. if (iNewSSTimeout && SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, (void*)&iCurrentSSTimeout, 0))
  2657. {
  2658. if (iNewSSTimeout != iCurrentSSTimeout)
  2659. {
  2660. if (SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, iNewSSTimeout, NULL, SPIF_UPDATEINIFILE))
  2661. {
  2662. bUpdatedSS = TRUE;
  2663. }
  2664. }
  2665. }
  2666. // check to see if we need to change our active status
  2667. if (SystemParametersInfo(SPI_GETSCREENSAVEACTIVE, 0, (void*)&bCurrentActive, 0) &&
  2668. (bActive != bCurrentActive))
  2669. {
  2670. if (SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, bActive, NULL, SPIF_UPDATEINIFILE))
  2671. {
  2672. bUpdatedSS = TRUE;
  2673. }
  2674. }
  2675. return bUpdatedSS;
  2676. }
  2677. // Called by Explorer.exe when things change so that we can zero our global
  2678. // data on ini changed status. Wparam and lparam are from a WM_SETTINGSCHANGED/WM_WININICHANGE
  2679. // message.
  2680. STDAPI_(void) SHSettingsChanged(WPARAM wParam, LPARAM lParam)
  2681. {
  2682. BOOL bPolicyChanged = FALSE;
  2683. if (lstrcmpi(TEXT("Policy"), (LPCTSTR)lParam) == 0)
  2684. {
  2685. bPolicyChanged = TRUE;
  2686. }
  2687. if (!lParam ||
  2688. bPolicyChanged ||
  2689. lstrcmpi(TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Policies"), (LPCTSTR)lParam) == 0)
  2690. {
  2691. SHGlobalCounterIncrement(_GetRestrictionsCounter());
  2692. }
  2693. }
  2694. LONG SHRegQueryValueExA(HKEY hKey, LPCSTR lpValueName, DWORD *pRes, DWORD *lpType, LPBYTE lpData, DWORD *lpcbData)
  2695. {
  2696. return SHQueryValueExA(hKey, lpValueName, pRes, lpType, lpData, lpcbData);
  2697. }
  2698. LONG SHRegQueryValueExW(HKEY hKey, LPCWSTR lpValueName, DWORD *pRes, DWORD *lpType, LPBYTE lpData, DWORD *lpcbData)
  2699. {
  2700. return SHQueryValueExW(hKey, lpValueName, pRes, lpType, lpData, lpcbData);
  2701. }
  2702. STDAPI_(LONG) SHRegQueryValueA(HKEY hKey, LPCSTR lpSubKey, LPSTR lpValue, LONG *lpcbValue)
  2703. {
  2704. LONG l;
  2705. HKEY ChildKey;
  2706. if ((lpSubKey == NULL) || (*lpSubKey == 0))
  2707. {
  2708. ChildKey = hKey;
  2709. }
  2710. else
  2711. {
  2712. l = RegOpenKeyExA(hKey, lpSubKey, 0, KEY_QUERY_VALUE, &ChildKey);
  2713. if (l != ERROR_SUCCESS)
  2714. return l;
  2715. }
  2716. l = SHQueryValueExA(ChildKey, NULL, NULL, NULL, (LPBYTE)lpValue, (DWORD *)lpcbValue);
  2717. if (ChildKey != hKey)
  2718. RegCloseKey(ChildKey);
  2719. if (l == ERROR_FILE_NOT_FOUND)
  2720. {
  2721. if (lpValue)
  2722. *lpValue = 0;
  2723. if (lpcbValue)
  2724. *lpcbValue = sizeof(CHAR);
  2725. l = ERROR_SUCCESS;
  2726. }
  2727. return l;
  2728. }
  2729. STDAPI_(LONG) SHRegQueryValueW(HKEY hKey, LPCWSTR lpSubKey, LPWSTR lpValue, LONG *lpcbValue)
  2730. {
  2731. LONG l;
  2732. HKEY ChildKey;
  2733. if ((lpSubKey == NULL) || (*lpSubKey == 0))
  2734. {
  2735. ChildKey = hKey;
  2736. }
  2737. else
  2738. {
  2739. l = RegOpenKeyExW(hKey, lpSubKey, 0, KEY_QUERY_VALUE, &ChildKey);
  2740. if (l != ERROR_SUCCESS)
  2741. return l;
  2742. }
  2743. l = SHQueryValueExW(ChildKey, NULL, NULL, NULL, (LPBYTE)lpValue, (DWORD *)lpcbValue);
  2744. if (ChildKey != hKey)
  2745. RegCloseKey(ChildKey);
  2746. if (l == ERROR_FILE_NOT_FOUND)
  2747. {
  2748. if (lpValue)
  2749. *lpValue = 0;
  2750. if (lpcbValue)
  2751. *lpcbValue = sizeof(WCHAR);
  2752. l = ERROR_SUCCESS;
  2753. }
  2754. return l;
  2755. }
  2756. void SHRegCloseKeys(HKEY ahkeys[], UINT ckeys)
  2757. {
  2758. UINT ikeys;
  2759. for (ikeys = 0; ikeys < ckeys; ikeys++)
  2760. {
  2761. if (ahkeys[ikeys])
  2762. {
  2763. RegCloseKey(ahkeys[ikeys]);
  2764. ahkeys[ikeys] = NULL;
  2765. }
  2766. }
  2767. }
  2768. STDAPI_(BOOL) SHWinHelp(HWND hwndMain, LPCTSTR lpszHelp, UINT usCommand, ULONG_PTR ulData)
  2769. {
  2770. // Try to show help
  2771. if (!WinHelp(hwndMain, lpszHelp, usCommand, ulData))
  2772. {
  2773. // Problem.
  2774. ShellMessageBox(HINST_THISDLL, hwndMain,
  2775. MAKEINTRESOURCE(IDS_WINHELPERROR),
  2776. MAKEINTRESOURCE(IDS_WINHELPTITLE),
  2777. MB_ICONHAND | MB_OK);
  2778. return FALSE;
  2779. }
  2780. return TRUE;
  2781. }
  2782. STDAPI StringToStrRet(LPCTSTR pszName, LPSTRRET pStrRet)
  2783. {
  2784. pStrRet->uType = STRRET_WSTR;
  2785. return SHStrDup(pszName, &pStrRet->pOleStr);
  2786. }
  2787. STDAPI ResToStrRet(UINT id, STRRET *pStrRet)
  2788. {
  2789. TCHAR szTemp[MAX_PATH];
  2790. pStrRet->uType = STRRET_WSTR;
  2791. LoadString(HINST_THISDLL, id, szTemp, ARRAYSIZE(szTemp));
  2792. return SHStrDup(szTemp, &pStrRet->pOleStr);
  2793. }
  2794. UINT g_uCodePage = 0;
  2795. LPCTSTR SkipLeadingSlashes(LPCTSTR pszURL)
  2796. {
  2797. LPCTSTR pszURLStart;
  2798. ASSERT(IS_VALID_STRING_PTR(pszURL, -1));
  2799. pszURLStart = pszURL;
  2800. // Skip two leading slashes.
  2801. if (pszURL[0] == TEXT('/') && pszURL[1] == TEXT('/'))
  2802. pszURLStart += 2;
  2803. ASSERT(IS_VALID_STRING_PTR(pszURL, -1) &&
  2804. IsStringContained(pszURL, pszURLStart));
  2805. return pszURLStart;
  2806. }
  2807. #undef PropVariantClear
  2808. STDAPI PropVariantClearLazy(PROPVARIANT *pvar)
  2809. {
  2810. switch(pvar->vt)
  2811. {
  2812. case VT_I4:
  2813. case VT_UI4:
  2814. case VT_EMPTY:
  2815. case VT_FILETIME:
  2816. // No operation
  2817. break;
  2818. // SHAlloc matches the CoTaskMemFree functions and will init OLE if it must be
  2819. // loaded.
  2820. case VT_LPSTR:
  2821. SHFree(pvar->pszVal);
  2822. break;
  2823. case VT_LPWSTR:
  2824. SHFree(pvar->pwszVal);
  2825. break;
  2826. default:
  2827. return PropVariantClear(pvar); // real version in OLE32
  2828. }
  2829. return S_OK;
  2830. }
  2831. // Return S_OK if all of the items are HTML or CDF references.
  2832. // Otherwise, return S_FALSE.
  2833. HRESULT IsDeskCompHDrop(IDataObject * pido)
  2834. {
  2835. STGMEDIUM medium;
  2836. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  2837. // asking for CF_HDROP
  2838. HRESULT hr = pido->GetData(&fmte, &medium);
  2839. if (SUCCEEDED(hr))
  2840. {
  2841. HDROP hDrop = (HDROP)medium.hGlobal;
  2842. DRAGINFO di;
  2843. di.uSize = sizeof(di);
  2844. if (DragQueryInfo(hDrop, &di)) // di.lpFileList will be in TCHAR format -- see DragQueryInfo impl
  2845. {
  2846. if (di.lpFileList)
  2847. {
  2848. LPTSTR pszCurrPath = di.lpFileList;
  2849. while (pszCurrPath && pszCurrPath[0])
  2850. {
  2851. // Is this file not acceptable to create a Desktop Component?
  2852. if (!PathIsContentType(pszCurrPath, SZ_CONTENTTYPE_HTML) &&
  2853. !PathIsContentType(pszCurrPath, SZ_CONTENTTYPE_CDF))
  2854. {
  2855. // Yes, I don't recognize this file as being acceptable.
  2856. hr = S_FALSE;
  2857. break;
  2858. }
  2859. pszCurrPath += lstrlen(pszCurrPath) + 1;
  2860. }
  2861. SHFree(di.lpFileList);
  2862. }
  2863. }
  2864. else
  2865. {
  2866. // NOTE: Win95/NT4 dont have this fix, you will fault if you hit this case!
  2867. AssertMsg(FALSE, TEXT("hDrop contains the opposite TCHAR (UNICODE when on ANSI)"));
  2868. }
  2869. ReleaseStgMedium(&medium);
  2870. }
  2871. return hr;
  2872. }
  2873. HRESULT _LocalAddDTI(LPCSTR pszUrl, HWND hwnd, int x, int y, int nType)
  2874. {
  2875. IActiveDesktop * pad;
  2876. HRESULT hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IActiveDesktop, &pad));
  2877. if (SUCCEEDED(hr))
  2878. {
  2879. COMPONENT comp = {
  2880. sizeof(COMPONENT), //Size of this structure
  2881. 0, //For Internal Use: Set it always to zero.
  2882. nType, //One of COMP_TYPE_*
  2883. TRUE, // Is this component enabled?
  2884. FALSE, // Had the component been modified and not yet saved to disk?
  2885. FALSE, // Is the component scrollable?
  2886. {
  2887. sizeof(COMPPOS), //Size of this structure
  2888. x - GetSystemMetrics(SM_XVIRTUALSCREEN), //Left of top-left corner in screen co-ordinates.
  2889. y - GetSystemMetrics(SM_YVIRTUALSCREEN), //Top of top-left corner in screen co-ordinates.
  2890. -1, // Width in pixels.
  2891. -1, // Height in pixels.
  2892. 10000, // Indicates the Z-order of the component.
  2893. TRUE, // Is the component resizeable?
  2894. TRUE, // Resizeable in X-direction?
  2895. TRUE, // Resizeable in Y-direction?
  2896. -1, //Left of top-left corner as percent of screen width
  2897. -1 //Top of top-left corner as percent of screen height
  2898. }, // Width, height etc.,
  2899. L"\0", // Friendly name of component.
  2900. L"\0", // URL of the component.
  2901. L"\0", // Subscrined URL.
  2902. IS_NORMAL // ItemState
  2903. };
  2904. SHAnsiToUnicodeCP(CP_UTF8, pszUrl, comp.wszSource, ARRAYSIZE(comp.wszSource));
  2905. SHAnsiToUnicodeCP(CP_UTF8, pszUrl, comp.wszFriendlyName, ARRAYSIZE(comp.wszFriendlyName));
  2906. SHAnsiToUnicodeCP(CP_UTF8, pszUrl, comp.wszSubscribedURL, ARRAYSIZE(comp.wszSubscribedURL));
  2907. hr = pad->AddDesktopItemWithUI(hwnd, &comp, DTI_ADDUI_DISPSUBWIZARD);
  2908. pad->Release();
  2909. }
  2910. return hr;
  2911. }
  2912. // Create Desktop Components for each item.
  2913. HRESULT ExecuteDeskCompHDrop(LPTSTR pszMultipleUrls, HWND hwnd, int x, int y)
  2914. {
  2915. IActiveDesktop * pad;
  2916. HRESULT hr = CoCreateInstance(CLSID_ActiveDesktop, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IActiveDesktop, &pad));
  2917. if (SUCCEEDED(hr))
  2918. {
  2919. COMPONENT comp = {
  2920. sizeof(COMPONENT), //Size of this structure
  2921. 0, //For Internal Use: Set it always to zero.
  2922. COMP_TYPE_WEBSITE, //One of COMP_TYPE_*
  2923. TRUE, // Is this component enabled?
  2924. FALSE, // Had the component been modified and not yet saved to disk?
  2925. FALSE, // Is the component scrollable?
  2926. {
  2927. sizeof(COMPPOS), //Size of this structure
  2928. x - GetSystemMetrics(SM_XVIRTUALSCREEN), //Left of top-left corner in screen co-ordinates.
  2929. y - GetSystemMetrics(SM_YVIRTUALSCREEN), //Top of top-left corner in screen co-ordinates.
  2930. -1, // Width in pixels.
  2931. -1, // Height in pixels.
  2932. 10000, // Indicates the Z-order of the component.
  2933. TRUE, // Is the component resizeable?
  2934. TRUE, // Resizeable in X-direction?
  2935. TRUE, // Resizeable in Y-direction?
  2936. -1, //Left of top-left corner as percent of screen width
  2937. -1 //Top of top-left corner as percent of screen height
  2938. }, // Width, height etc.,
  2939. L"\0", // Friendly name of component.
  2940. L"\0", // URL of the component.
  2941. L"\0", // Subscrined URL.
  2942. IS_NORMAL // ItemState
  2943. };
  2944. while (pszMultipleUrls[0])
  2945. {
  2946. SHTCharToUnicode(pszMultipleUrls, comp.wszSource, ARRAYSIZE(comp.wszSource));
  2947. SHTCharToUnicode(pszMultipleUrls, comp.wszFriendlyName, ARRAYSIZE(comp.wszFriendlyName));
  2948. SHTCharToUnicode(pszMultipleUrls, comp.wszSubscribedURL, ARRAYSIZE(comp.wszSubscribedURL));
  2949. hr = pad->AddDesktopItemWithUI(hwnd, &comp, DTI_ADDUI_DISPSUBWIZARD);
  2950. pszMultipleUrls += lstrlen(pszMultipleUrls) + 1;
  2951. }
  2952. pad->Release();
  2953. }
  2954. return hr;
  2955. }
  2956. typedef struct {
  2957. LPSTR pszUrl;
  2958. LPTSTR pszMultipleUrls;
  2959. BOOL fMultiString;
  2960. HWND hwnd;
  2961. DWORD dwFlags;
  2962. int x;
  2963. int y;
  2964. } CREATEDESKCOMP;
  2965. // Create Desktop Components for one or mowe items. We need to start
  2966. // a thread to do this because it may take a while and we don't want
  2967. // to block the UI thread because dialogs may be displayed.
  2968. DWORD CALLBACK _CreateDeskComp_ThreadProc(void *pvCreateDeskComp)
  2969. {
  2970. CREATEDESKCOMP * pcdc = (CREATEDESKCOMP *) pvCreateDeskComp;
  2971. HRESULT hr = OleInitialize(0);
  2972. if (EVAL(SUCCEEDED(hr)))
  2973. {
  2974. if (pcdc->fMultiString)
  2975. {
  2976. hr = ExecuteDeskCompHDrop(pcdc->pszMultipleUrls, pcdc->hwnd, pcdc->x, pcdc->y);
  2977. SHFree(pcdc->pszMultipleUrls);
  2978. }
  2979. else if (pcdc->dwFlags & DESKCOMP_URL)
  2980. {
  2981. hr = _LocalAddDTI(pcdc->pszUrl, pcdc->hwnd, pcdc->x, pcdc->y, COMP_TYPE_WEBSITE);
  2982. Str_SetPtrA(&(pcdc->pszUrl), NULL);
  2983. }
  2984. else if (pcdc->dwFlags & DESKCOMP_IMAGE)
  2985. {
  2986. hr = _LocalAddDTI(pcdc->pszUrl, pcdc->hwnd, pcdc->x, pcdc->y, COMP_TYPE_PICTURE);
  2987. }
  2988. OleUninitialize();
  2989. }
  2990. LocalFree(pcdc);
  2991. return 0;
  2992. }
  2993. /*********************************************************************\
  2994. Create Desktop Components for one or mowe items. We need to start
  2995. a thread to do this because it may take a while and we don't want
  2996. to block the UI thread because dialogs may be displayed.
  2997. \*********************************************************************/
  2998. HRESULT CreateDesktopComponents(LPCSTR pszUrl, IDataObject* pido, HWND hwnd, DWORD dwFlags, int x, int y)
  2999. {
  3000. CREATEDESKCOMP *pcdc;
  3001. HRESULT hr = SHLocalAlloc(sizeof(CREATEDESKCOMP), &pcdc);
  3002. // Create Thread....
  3003. if (SUCCEEDED(hr))
  3004. {
  3005. pcdc->pszUrl = NULL; // In case of failure.
  3006. pcdc->pszMultipleUrls = NULL; // In case of failure.
  3007. pcdc->fMultiString = (pido ? TRUE : FALSE);
  3008. pcdc->hwnd = hwnd;
  3009. pcdc->dwFlags = dwFlags;
  3010. pcdc->x = x;
  3011. pcdc->y = y;
  3012. if (!pcdc->fMultiString)
  3013. {
  3014. Str_SetPtrA(&(pcdc->pszUrl), pszUrl);
  3015. }
  3016. else
  3017. {
  3018. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  3019. STGMEDIUM medium;
  3020. // asking for CF_HDROP
  3021. hr = pido->GetData(&fmte, &medium);
  3022. if (SUCCEEDED(hr))
  3023. {
  3024. HDROP hDrop = (HDROP)medium.hGlobal;
  3025. DRAGINFO di;
  3026. di.uSize = sizeof(di);
  3027. if (DragQueryInfo(hDrop, &di))
  3028. {
  3029. // di.lpFileList will be in TCHAR format -- see DragQueryInfo impl
  3030. pcdc->pszMultipleUrls = di.lpFileList;
  3031. }
  3032. else
  3033. {
  3034. // NOTE: Win95/NT4 dont have this fix, you will fault if you hit this case!
  3035. AssertMsg(FALSE, TEXT("hDrop contains the opposite TCHAR (UNICODE when on ANSI)"));
  3036. }
  3037. ReleaseStgMedium(&medium);
  3038. }
  3039. }
  3040. if (pcdc->pszUrl || pcdc->pszMultipleUrls)
  3041. {
  3042. if (SHCreateThread(_CreateDeskComp_ThreadProc, pcdc, CTF_INSIST | CTF_PROCESS_REF, NULL))
  3043. {
  3044. hr = S_OK;
  3045. }
  3046. else
  3047. {
  3048. hr = ResultFromLastError();
  3049. LocalFree(pcdc);
  3050. }
  3051. }
  3052. else
  3053. {
  3054. hr = E_FAIL;
  3055. LocalFree(pcdc);
  3056. }
  3057. }
  3058. return hr;
  3059. }
  3060. // This is exported, as ordinal 184. It was in shell32\smrttile.c, but no-one was using it
  3061. // internally, and it does not appear that anyone external is using it (verified that taskman
  3062. // on NT and W95 uses the win32 api's CascadeWindows and TileWindows. This could probably be
  3063. // removed altogether. (t-saml, 12/97)
  3064. STDAPI_(WORD) ArrangeWindows(HWND hwndParent, WORD flags, LPCRECT lpRect, WORD chwnd, const HWND *ahwnd)
  3065. {
  3066. ASSERT(0);
  3067. return 0;
  3068. }
  3069. /*
  3070. GetFileDescription retrieves the friendly name from a file's verion rsource.
  3071. The first language we try will be the first item in the
  3072. "\VarFileInfo\Translations" section; if there's nothing there,
  3073. we try the one coded into the IDS_VN_FILEVERSIONKEY resource string.
  3074. If we can't even load that, we just use English (040904E4). We
  3075. also try English with a null codepage (04090000) since many apps
  3076. were stamped according to an old spec which specified this as
  3077. the required language instead of 040904E4.
  3078. If there is no FileDescription in version resource, return the file name.
  3079. Parameters:
  3080. LPCTSTR pszPath: full path of the file
  3081. LPTSTR pszDesc: pointer to the buffer to receive friendly name. If NULL,
  3082. *pcchDesc will be set to the length of friendly name in
  3083. characters, including ending NULL, on successful return.
  3084. UINT *pcchDesc: length of the buffer in characters. On successful return,
  3085. it contains number of characters copied to the buffer,
  3086. including ending NULL.
  3087. Return:
  3088. TRUE on success, and FALSE otherwise
  3089. */
  3090. STDAPI_(BOOL) GetFileDescription(LPCTSTR pszPath, LPTSTR pszDesc, UINT *pcchDesc)
  3091. {
  3092. TCHAR szVersionKey[60]; /* big enough for anything we need */
  3093. LPTSTR pszVersionKey = NULL;
  3094. // Try same language as this program
  3095. if (LoadString(HINST_THISDLL, IDS_VN_FILEVERSIONKEY, szVersionKey, ARRAYSIZE(szVersionKey)))
  3096. {
  3097. StrCatBuff(szVersionKey, TEXT("FileDescription"), SIZECHARS(szVersionKey));
  3098. pszVersionKey = szVersionKey;
  3099. }
  3100. // just use the default cut list
  3101. return SHGetFileDescription(pszPath, pszVersionKey, NULL, pszDesc, pcchDesc);
  3102. }
  3103. STDAPI_(int) SHOutOfMemoryMessageBox(HWND hwnd, LPTSTR pszTitle, UINT fuStyle)
  3104. {
  3105. TCHAR szOutOfMemory[128], szTitle[128];
  3106. szTitle[0] = 0;
  3107. if (pszTitle==NULL)
  3108. {
  3109. GetWindowText(hwnd, szTitle, ARRAYSIZE(szTitle));
  3110. pszTitle = szTitle;
  3111. }
  3112. szOutOfMemory[0] = 0;
  3113. FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, ERROR_OUTOFMEMORY, 0, szOutOfMemory,
  3114. ARRAYSIZE(szOutOfMemory), NULL);
  3115. int ret = MessageBox(hwnd, szOutOfMemory, pszTitle, fuStyle | MB_SETFOREGROUND);
  3116. if (ret == -1)
  3117. {
  3118. DebugMsg(DM_TRACE, TEXT("regular message box failed, trying sysmodal"));
  3119. ret = MessageBox(hwnd, szOutOfMemory, pszTitle, fuStyle | MB_SYSTEMMODAL);
  3120. }
  3121. return ret;
  3122. }
  3123. #define SZ_PATHLIST_SEPARATOR TEXT(";")
  3124. BOOL SafePathListAppend(LPTSTR pszDestPath, DWORD cchDestSize, LPCTSTR pszPathToAdd)
  3125. {
  3126. BOOL fResult = FALSE;
  3127. if ((lstrlen(pszDestPath) + lstrlen(pszPathToAdd) + 1) < (long) cchDestSize)
  3128. {
  3129. // Do we need to add a ";" between pszDestPath and pszPathToAdd?
  3130. if (pszDestPath[0] &&
  3131. (SZ_PATHLIST_SEPARATOR[0] != pszDestPath[lstrlen(pszDestPath)-1]))
  3132. {
  3133. StrCat(pszDestPath, SZ_PATHLIST_SEPARATOR);
  3134. }
  3135. StrCat(pszDestPath, pszPathToAdd);
  3136. fResult = TRUE;
  3137. }
  3138. return fResult;
  3139. }
  3140. bool IsDiscardablePropertySet(const FMTID & fmtid)
  3141. {
  3142. if (IsEqualGUID(fmtid, FMTID_DiscardableInformation))
  3143. return true;
  3144. return false;
  3145. }
  3146. bool IsDiscardableStream(LPCTSTR pszStreamName)
  3147. {
  3148. static const LPCTSTR _apszDiscardableStreams[] =
  3149. {
  3150. // Mike Hillberg claims this stream is discardable, and is used to
  3151. // hold a few state bytes for property set information
  3152. TEXT(":{4c8cc155-6c1e-11d1-8e41-00c04fb9386d}:$DATA")
  3153. };
  3154. for (int i = 0; i < ARRAYSIZE(_apszDiscardableStreams); i++)
  3155. {
  3156. if (0 == lstrcmpi(_apszDiscardableStreams[i], pszStreamName))
  3157. return TRUE;
  3158. }
  3159. return FALSE;
  3160. }
  3161. LPTSTR NTFSPropSetMsg(LPCWSTR pszSrcObject, LPTSTR pszUserMessage)
  3162. {
  3163. // Now look for native property NTFS sets
  3164. IPropertySetStorage *pPropSetStorage;
  3165. if (SUCCEEDED(StgOpenStorageEx(pszSrcObject,
  3166. STGM_READ | STGM_DIRECT | STGM_SHARE_DENY_WRITE,
  3167. STGFMT_FILE,
  3168. 0,0,0,
  3169. IID_PPV_ARG(IPropertySetStorage, &pPropSetStorage))))
  3170. {
  3171. // Enum the property set storages available for this file
  3172. IEnumSTATPROPSETSTG *pEnumSetStorage;
  3173. if (SUCCEEDED(pPropSetStorage->Enum(&pEnumSetStorage)))
  3174. {
  3175. STATPROPSETSTG statPropSet[10];
  3176. ULONG cSets;
  3177. // Enum the property sets available in this property set storage
  3178. while (SUCCEEDED(pEnumSetStorage->Next(ARRAYSIZE(statPropSet), statPropSet, &cSets)) && cSets > 0)
  3179. {
  3180. // For each property set we receive, open it and enumerate the
  3181. // properties contained withing it
  3182. for (ULONG iSet = 0; iSet < cSets; iSet++)
  3183. {
  3184. if (FALSE == IsDiscardablePropertySet(statPropSet[iSet].fmtid))
  3185. {
  3186. TCHAR szText[MAX_PATH];
  3187. size_t cch = 0;
  3188. static const struct
  3189. {
  3190. const FMTID * m_pFMTID;
  3191. UINT m_idTextID;
  3192. }
  3193. _aKnownPsets[] =
  3194. {
  3195. { &FMTID_SummaryInformation, IDS_DOCSUMINFOSTREAM },
  3196. { &FMTID_DocSummaryInformation, IDS_SUMINFOSTREAM },
  3197. { &FMTID_UserDefinedProperties, IDS_USERDEFPROP },
  3198. { &FMTID_ImageSummaryInformation, IDS_IMAGEINFO },
  3199. { &FMTID_AudioSummaryInformation, IDS_AUDIOSUMINFO },
  3200. { &FMTID_VideoSummaryInformation, IDS_VIDEOSUMINFO },
  3201. { &FMTID_MediaFileSummaryInformation, IDS_MEDIASUMINFO }
  3202. };
  3203. // First try to map the fmtid to a better name than what the api gave us
  3204. for (int i = 0; i < ARRAYSIZE(_aKnownPsets); i++)
  3205. {
  3206. if (IsEqualGUID(*(_aKnownPsets[i].m_pFMTID), statPropSet[iSet].fmtid))
  3207. {
  3208. cch = LoadString(HINST_THISDLL, _aKnownPsets[i].m_idTextID, szText, ARRAYSIZE(szText));
  3209. break;
  3210. }
  3211. }
  3212. // No useful name... use Unidentied User Properties
  3213. if (0 == cch)
  3214. cch = LoadString(HINST_THISDLL,
  3215. IDS_UNKNOWNPROPSET,
  3216. szText, ARRAYSIZE(szText));
  3217. if (cch)
  3218. {
  3219. LPTSTR pszOldMessage = pszUserMessage;
  3220. if (pszOldMessage)
  3221. {
  3222. pszUserMessage = (TCHAR *) LocalReAlloc(pszOldMessage,
  3223. (lstrlen(pszUserMessage) + cch + 3) * sizeof(TCHAR),
  3224. LMEM_MOVEABLE);
  3225. if (pszUserMessage)
  3226. lstrcat(pszUserMessage, TEXT("\r\n"));
  3227. }
  3228. else
  3229. pszUserMessage = (TCHAR *) LocalAlloc(LPTR, (cch + 1) * sizeof(TCHAR));
  3230. if (NULL == pszUserMessage)
  3231. pszUserMessage = pszOldMessage; // Can't grow it, but at least keep what we know so far
  3232. else
  3233. lstrcat(pszUserMessage, szText); // Index 1 to skip the \005
  3234. }
  3235. }
  3236. }
  3237. }
  3238. pEnumSetStorage->Release();
  3239. }
  3240. pPropSetStorage->Release();
  3241. }
  3242. return pszUserMessage;
  3243. }
  3244. // GetDownlevelCopyDataLossText
  3245. //
  3246. // If data will be lost on a downlevel copy from NTFS to FAT, we return
  3247. // a string containing a description of the data that will be lost,
  3248. // suitable for display to the user. String must be freed by the caller.
  3249. //
  3250. // If nothing will be lost, a NULL is returned.
  3251. //
  3252. // pbDirIsSafe points to a BOOL passed in by the caller. On return, if
  3253. // *pbDirIsSafe has been set to TRUE, no further data loss could occur
  3254. // in this directory
  3255. //
  3256. // Davepl 01-Mar-98
  3257. #define NT_FAILED(x) NT_ERROR(x) // More consistent name for this macro
  3258. LPWSTR GetDownlevelCopyDataLossText(LPCWSTR pszSrcObject, LPCWSTR pszDestDir, BOOL bIsADir, BOOL *pbDirIsSafe)
  3259. {
  3260. OBJECT_ATTRIBUTES SrcObjectAttributes;
  3261. OBJECT_ATTRIBUTES DestObjectAttributes;
  3262. IO_STATUS_BLOCK IoStatusBlock;
  3263. HANDLE SrcObjectHandle = INVALID_HANDLE_VALUE;
  3264. HANDLE DestPathHandle = INVALID_HANDLE_VALUE;
  3265. UNICODE_STRING UnicodeSrcObject;
  3266. UNICODE_STRING UnicodeDestPath;
  3267. *pbDirIsSafe = FALSE;
  3268. // pAttributeInfo will point to enough stack to hold the
  3269. // FILE_FS_ATTRIBUTE_INFORMATION and worst-case filesystem name
  3270. size_t cbAttributeInfo = sizeof(FILE_FS_ATTRIBUTE_INFORMATION) +
  3271. MAX_PATH * sizeof(TCHAR);
  3272. PFILE_FS_ATTRIBUTE_INFORMATION pAttributeInfo =
  3273. (PFILE_FS_ATTRIBUTE_INFORMATION) _alloca(cbAttributeInfo);
  3274. // Covert the conventional paths to UnicodePath descriptors
  3275. RtlInitUnicodeString(&UnicodeSrcObject, pszSrcObject);
  3276. if (!RtlDosPathNameToNtPathName_U(pszSrcObject, &UnicodeSrcObject, NULL, NULL))
  3277. {
  3278. AssertMsg(FALSE, TEXT("RtlDosPathNameToNtPathName_U failed for source."));
  3279. return NULL;
  3280. }
  3281. RtlInitUnicodeString(&UnicodeDestPath, pszDestDir);
  3282. if (!RtlDosPathNameToNtPathName_U(pszDestDir, &UnicodeDestPath, NULL, NULL))
  3283. {
  3284. RtlFreeHeap(RtlProcessHeap(), 0, UnicodeSrcObject.Buffer);
  3285. AssertMsg(FALSE, TEXT("RtlDosPathNameToNtPathName_U failed for dest."));
  3286. return NULL;
  3287. }
  3288. // Build an NT object descriptor from the UnicodeSrcObject
  3289. InitializeObjectAttributes(&SrcObjectAttributes, &UnicodeSrcObject, OBJ_CASE_INSENSITIVE, NULL, NULL);
  3290. InitializeObjectAttributes(&DestObjectAttributes, &UnicodeDestPath, OBJ_CASE_INSENSITIVE, NULL, NULL);
  3291. // Open the file for generic read, and the dest path for attribute read
  3292. NTSTATUS NtStatus = NtOpenFile(&SrcObjectHandle, FILE_GENERIC_READ, &SrcObjectAttributes,
  3293. &IoStatusBlock, FILE_SHARE_READ, (bIsADir ? FILE_DIRECTORY_FILE : FILE_NON_DIRECTORY_FILE));
  3294. if (NT_FAILED(NtStatus))
  3295. {
  3296. RtlFreeHeap(RtlProcessHeap(), 0, UnicodeSrcObject.Buffer);
  3297. RtlFreeHeap(RtlProcessHeap(), 0, UnicodeDestPath.Buffer);
  3298. return NULL;
  3299. }
  3300. NtStatus = NtOpenFile(&DestPathHandle, FILE_READ_ATTRIBUTES, &DestObjectAttributes,
  3301. &IoStatusBlock, FILE_SHARE_READ, FILE_DIRECTORY_FILE);
  3302. if (NT_FAILED(NtStatus))
  3303. {
  3304. NtClose(SrcObjectHandle);
  3305. RtlFreeHeap(RtlProcessHeap(), 0, UnicodeSrcObject.Buffer);
  3306. RtlFreeHeap(RtlProcessHeap(), 0, UnicodeDestPath.Buffer);
  3307. return NULL;
  3308. }
  3309. // Incrementally try allocation sizes for the ObjectStreamInformation,
  3310. // then retrieve the actual stream info
  3311. BYTE * pBuffer = NULL;
  3312. __try // Current and future allocations and handles free'd by __finally block
  3313. {
  3314. size_t cbBuffer;
  3315. LPTSTR pszUserMessage = NULL;
  3316. // Quick check of filesystem type for this file
  3317. NtStatus = NtQueryVolumeInformationFile(
  3318. SrcObjectHandle,
  3319. &IoStatusBlock,
  3320. (BYTE *) pAttributeInfo,
  3321. cbAttributeInfo,
  3322. FileFsAttributeInformation
  3323. );
  3324. if (NT_FAILED(NtStatus))
  3325. return NULL;
  3326. // If the source filesystem isn't NTFS, we can just bail now
  3327. pAttributeInfo->FileSystemName[
  3328. (pAttributeInfo->FileSystemNameLength / sizeof(WCHAR)) ] = L'\0';
  3329. if (0 == StrStrIW(pAttributeInfo->FileSystemName, L"NTFS"))
  3330. {
  3331. *pbDirIsSafe = TRUE;
  3332. return NULL;
  3333. }
  3334. NtStatus = NtQueryVolumeInformationFile(
  3335. DestPathHandle,
  3336. &IoStatusBlock,
  3337. (BYTE *) pAttributeInfo,
  3338. cbAttributeInfo,
  3339. FileFsAttributeInformation
  3340. );
  3341. if (NT_FAILED(NtStatus))
  3342. return NULL;
  3343. // If the target filesystem is NTFS, no stream loss will happen
  3344. pAttributeInfo->FileSystemName[
  3345. (pAttributeInfo->FileSystemNameLength / sizeof(WCHAR)) ] = L'\0';
  3346. if (StrStrIW(pAttributeInfo->FileSystemName, L"NTFS"))
  3347. {
  3348. *pbDirIsSafe = TRUE;
  3349. return NULL;
  3350. }
  3351. // At this point we know we're doing an NTFS->FAT copy, so we need
  3352. // to find out whether or not the source file has multiple streams
  3353. // pBuffer will point to enough memory to hold the worst case for
  3354. // a single stream.
  3355. cbBuffer = sizeof(FILE_STREAM_INFORMATION) + MAX_PATH * sizeof(WCHAR);
  3356. if (NULL == (pBuffer = (BYTE *) LocalAlloc(LPTR, cbBuffer)))
  3357. return NULL;
  3358. do
  3359. {
  3360. BYTE * pOldBuffer = pBuffer;
  3361. if (NULL == (pBuffer = (BYTE *) LocalReAlloc(pBuffer, cbBuffer, LMEM_MOVEABLE)))
  3362. {
  3363. LocalFree(pOldBuffer);
  3364. return NULL;
  3365. }
  3366. NtStatus = NtQueryInformationFile(SrcObjectHandle, &IoStatusBlock, pBuffer, cbBuffer,
  3367. FileStreamInformation);
  3368. cbBuffer *= 2;
  3369. } while (STATUS_BUFFER_OVERFLOW == NtStatus);
  3370. if (NT_SUCCESS(NtStatus))
  3371. {
  3372. FILE_STREAM_INFORMATION * pStreamInfo = (FILE_STREAM_INFORMATION *) pBuffer;
  3373. BOOL bLastPass = (0 == pStreamInfo->NextEntryOffset);
  3374. if (bIsADir)
  3375. {
  3376. // From experimentation, it seems that if there's only one stream on a directory and
  3377. // it has a zero-length name, its a vanilla directory
  3378. if ((0 == pStreamInfo->NextEntryOffset) && (0 == pStreamInfo->StreamNameLength))
  3379. return NULL;
  3380. }
  3381. else // File
  3382. {
  3383. // Single stream only if first stream has no next offset
  3384. if ((0 == pStreamInfo->NextEntryOffset) && (pBuffer == (BYTE *) pStreamInfo))
  3385. return NULL;
  3386. }
  3387. for(;;)
  3388. {
  3389. int i;
  3390. TCHAR szText[MAX_PATH];
  3391. // Table of known stream names and the string IDs that we actually want to show
  3392. // to the user instead of the raw stream name.
  3393. static const struct _ADATATYPES
  3394. {
  3395. LPCTSTR m_pszStreamName;
  3396. UINT m_idTextID;
  3397. }
  3398. _aDataTypes[] =
  3399. {
  3400. { TEXT("::"), 0 },
  3401. { TEXT(":AFP_AfpInfo:"), IDS_MACINFOSTREAM },
  3402. { TEXT(":AFP_Resource:"), IDS_MACRESSTREAM }
  3403. };
  3404. if (FALSE == IsDiscardableStream(pStreamInfo->StreamName))
  3405. {
  3406. for (i = 0; i < ARRAYSIZE(_aDataTypes); i++)
  3407. {
  3408. // Can't use string compare since they choke on the \005 character
  3409. // used in property storage streams
  3410. int cbComp = min(lstrlen(pStreamInfo->StreamName) * sizeof(TCHAR),
  3411. lstrlen(_aDataTypes[i].m_pszStreamName) * sizeof(TCHAR));
  3412. if (0 == memcmp(_aDataTypes[i].m_pszStreamName,
  3413. pStreamInfo->StreamName,
  3414. cbComp))
  3415. {
  3416. break;
  3417. }
  3418. }
  3419. size_t cch = 0;
  3420. if (i == ARRAYSIZE(_aDataTypes))
  3421. {
  3422. // Not found, so use the actual stream name, unless it has a \005
  3423. // at the beginning of its name, in which case we'll pick this one
  3424. // up when we check for property sets.
  3425. if (pStreamInfo->StreamName[1] == TEXT('\005'))
  3426. cch = 0;
  3427. else
  3428. {
  3429. lstrcpy(szText, pStreamInfo->StreamName);
  3430. cch = lstrlen(szText);
  3431. }
  3432. }
  3433. else
  3434. {
  3435. // We found this stream in our table of well-known streams, so
  3436. // load the string which the user will see describing this stream,
  3437. // as we likely have a more useful name than the stream itself.
  3438. cch = _aDataTypes[i].m_idTextID ?
  3439. LoadString(HINST_THISDLL, _aDataTypes[i].m_idTextID, szText, ARRAYSIZE(szText))
  3440. : 0;
  3441. }
  3442. // Reallocate the overall buffer to be large enough to add this new
  3443. // stream description, plus 2 chars for the crlf
  3444. if (cch)
  3445. {
  3446. LPTSTR pszOldMessage = pszUserMessage;
  3447. if (pszOldMessage)
  3448. {
  3449. pszUserMessage = (TCHAR *) LocalReAlloc(pszOldMessage,
  3450. (lstrlen(pszUserMessage) + cch + 3) * sizeof(TCHAR),
  3451. LMEM_MOVEABLE);
  3452. if (pszUserMessage)
  3453. lstrcat(pszUserMessage, TEXT("\r\n"));
  3454. }
  3455. else
  3456. pszUserMessage = (TCHAR *) LocalAlloc(LPTR, (cch + 1) * sizeof(TCHAR));
  3457. if (NULL == pszUserMessage)
  3458. return pszOldMessage; // Can't grow it, but at least return what we know so far
  3459. lstrcat(pszUserMessage, szText);
  3460. }
  3461. }
  3462. if (bLastPass)
  3463. break;
  3464. pStreamInfo = (FILE_STREAM_INFORMATION *) (((BYTE *) pStreamInfo) + pStreamInfo->NextEntryOffset);
  3465. bLastPass = (0 == pStreamInfo->NextEntryOffset);
  3466. }
  3467. }
  3468. pszUserMessage = NTFSPropSetMsg(pszSrcObject, pszUserMessage);
  3469. return pszUserMessage;
  3470. }
  3471. __finally // Cleanup
  3472. {
  3473. if (pBuffer)
  3474. LocalFree(pBuffer);
  3475. NtClose(SrcObjectHandle);
  3476. NtClose(DestPathHandle);
  3477. RtlFreeHeap(RtlProcessHeap(), 0, UnicodeSrcObject.Buffer);
  3478. RtlFreeHeap(RtlProcessHeap(), 0, UnicodeDestPath.Buffer);
  3479. }
  3480. return NULL;
  3481. }
  3482. // lstrcmp? uses thread lcid. but in UI visual sorting, we need
  3483. // to use the user's choice. (thus the u in ustrcmp)
  3484. int _ustrcmp(LPCTSTR psz1, LPCTSTR psz2, BOOL fCaseInsensitive)
  3485. {
  3486. COMPILETIME_ASSERT(CSTR_LESS_THAN == 1);
  3487. COMPILETIME_ASSERT(CSTR_EQUAL == 2);
  3488. COMPILETIME_ASSERT(CSTR_GREATER_THAN == 3);
  3489. return (CompareString(LOCALE_USER_DEFAULT,
  3490. fCaseInsensitive ? NORM_IGNORECASE : 0,
  3491. psz1, -1, psz2, -1) - CSTR_EQUAL);
  3492. }
  3493. void HWNDWSPrintf(HWND hwnd, LPCTSTR psz)
  3494. {
  3495. TCHAR szTemp[2048];
  3496. TCHAR szTemp1[2048];
  3497. GetWindowText(hwnd, szTemp, ARRAYSIZE(szTemp));
  3498. wnsprintf(szTemp1, ARRAYSIZE(szTemp1), szTemp, psz);
  3499. SetWindowText(hwnd, szTemp1);
  3500. }
  3501. STDAPI_(BOOL) Priv_Str_SetPtrW(WCHAR * UNALIGNED * ppwzCurrent, LPCWSTR pwzNew)
  3502. {
  3503. LPWSTR pwzOld;
  3504. LPWSTR pwzNewCopy = NULL;
  3505. if (pwzNew)
  3506. {
  3507. int cchLength = lstrlenW(pwzNew);
  3508. // alloc a new buffer w/ room for the null terminator
  3509. pwzNewCopy = (LPWSTR) LocalAlloc(LPTR, (cchLength + 1) * sizeof(WCHAR));
  3510. if (!pwzNewCopy)
  3511. return FALSE;
  3512. StrCpyNW(pwzNewCopy, pwzNew, cchLength + 1);
  3513. }
  3514. pwzOld = (LPWSTR) InterlockedExchangePointer((void * *)ppwzCurrent, pwzNewCopy);
  3515. if (pwzOld)
  3516. LocalFree(pwzOld);
  3517. return TRUE;
  3518. }
  3519. // combines pidlParent with part of pidl, upto pidlNext, example:
  3520. //
  3521. // in:
  3522. // pidlParent [c:] [windows]
  3523. // pidl [system] [foo.txt]
  3524. // pidlNext --^
  3525. //
  3526. // returns:
  3527. // [c:] [windows] [system]
  3528. //
  3529. STDAPI_(LPITEMIDLIST) ILCombineParentAndFirst(LPCITEMIDLIST pidlParent, LPCITEMIDLIST pidl, LPCITEMIDLIST pidlNext)
  3530. {
  3531. ULONG cbParent = ILGetSize(pidlParent);
  3532. ULONG cbRest = (ULONG)((ULONG_PTR)pidlNext - (ULONG_PTR)pidl);
  3533. LPITEMIDLIST pidlNew = _ILCreate(cbParent + cbRest);
  3534. if (pidlNew)
  3535. {
  3536. cbParent -= sizeof(pidlParent->mkid.cb);
  3537. memcpy(pidlNew, pidlParent, cbParent);
  3538. memcpy((BYTE *)pidlNew + cbParent, pidl, cbRest);
  3539. ASSERT(_ILSkip(pidlNew, cbParent + cbRest)->mkid.cb == 0);
  3540. }
  3541. return pidlNew;
  3542. }
  3543. STDAPI_(LPTSTR) DumpPidl(LPCITEMIDLIST pidl)
  3544. {
  3545. #ifdef DEBUG
  3546. static TCHAR szBuf[MAX_PATH];
  3547. TCHAR szTmp[MAX_PATH];
  3548. USHORT cb;
  3549. LPTSTR pszT;
  3550. szBuf[0] = 0;
  3551. if (NULL == pidl)
  3552. {
  3553. lstrcatn(szBuf, TEXT("Empty pidl"), ARRAYSIZE(szBuf));
  3554. return szBuf;
  3555. }
  3556. while (!ILIsEmpty(pidl))
  3557. {
  3558. cb = pidl->mkid.cb;
  3559. wsprintf(szTmp, TEXT("cb:%x id:"), cb);
  3560. lstrcatn(szBuf, szTmp, ARRAYSIZE(szBuf));
  3561. switch (SIL_GetType(pidl) & SHID_TYPEMASK)
  3562. {
  3563. case SHID_ROOT: pszT = TEXT("SHID_ROOT"); break;
  3564. case SHID_ROOT_REGITEM: pszT = TEXT("SHID_ROOT_REGITEM"); break;
  3565. case SHID_COMPUTER: pszT = TEXT("SHID_COMPUTER"); break;
  3566. case SHID_COMPUTER_1: pszT = TEXT("SHID_COMPUTER_1"); break;
  3567. case SHID_COMPUTER_REMOVABLE: pszT = TEXT("SHID_COMPUTER_REMOVABLE"); break;
  3568. case SHID_COMPUTER_FIXED: pszT = TEXT("SHID_COMPUTER_FIXED"); break;
  3569. case SHID_COMPUTER_REMOTE: pszT = TEXT("SHID_COMPUTER_REMOTE"); break;
  3570. case SHID_COMPUTER_CDROM: pszT = TEXT("SHID_COMPUTER_CDROM"); break;
  3571. case SHID_COMPUTER_RAMDISK: pszT = TEXT("SHID_COMPUTER_RAMDISK"); break;
  3572. case SHID_COMPUTER_7: pszT = TEXT("SHID_COMPUTER_7"); break;
  3573. case SHID_COMPUTER_DRIVE525: pszT = TEXT("SHID_COMPUTER_DRIVE525"); break;
  3574. case SHID_COMPUTER_DRIVE35: pszT = TEXT("SHID_COMPUTER_DRIVE35"); break;
  3575. case SHID_COMPUTER_NETDRIVE: pszT = TEXT("SHID_COMPUTER_NETDRIVE"); break;
  3576. case SHID_COMPUTER_NETUNAVAIL: pszT = TEXT("SHID_COMPUTER_NETUNAVAIL"); break;
  3577. case SHID_COMPUTER_C: pszT = TEXT("SHID_COMPUTER_C"); break;
  3578. case SHID_COMPUTER_D: pszT = TEXT("SHID_COMPUTER_D"); break;
  3579. case SHID_COMPUTER_REGITEM: pszT = TEXT("SHID_COMPUTER_REGITEM"); break;
  3580. case SHID_COMPUTER_MISC: pszT = TEXT("SHID_COMPUTER_MISC"); break;
  3581. case SHID_FS: pszT = TEXT("SHID_FS"); break;
  3582. case SHID_FS_TYPEMASK: pszT = TEXT("SHID_FS_TYPEMASK"); break;
  3583. case SHID_FS_DIRECTORY: pszT = TEXT("SHID_FS_DIRECTORY"); break;
  3584. case SHID_FS_FILE: pszT = TEXT("SHID_FS_FILE"); break;
  3585. case SHID_FS_UNICODE: pszT = TEXT("SHID_FS_UNICODE"); break;
  3586. case SHID_FS_DIRUNICODE: pszT = TEXT("SHID_FS_DIRUNICODE"); break;
  3587. case SHID_FS_FILEUNICODE: pszT = TEXT("SHID_FS_FILEUNICODE"); break;
  3588. case SHID_NET: pszT = TEXT("SHID_NET"); break;
  3589. case SHID_NET_DOMAIN: pszT = TEXT("SHID_NET_DOMAIN"); break;
  3590. case SHID_NET_SERVER: pszT = TEXT("SHID_NET_SERVER"); break;
  3591. case SHID_NET_SHARE: pszT = TEXT("SHID_NET_SHARE"); break;
  3592. case SHID_NET_FILE: pszT = TEXT("SHID_NET_FILE"); break;
  3593. case SHID_NET_GROUP: pszT = TEXT("SHID_NET_GROUP"); break;
  3594. case SHID_NET_NETWORK: pszT = TEXT("SHID_NET_NETWORK"); break;
  3595. case SHID_NET_RESTOFNET: pszT = TEXT("SHID_NET_RESTOFNET"); break;
  3596. case SHID_NET_SHAREADMIN: pszT = TEXT("SHID_NET_SHAREADMIN"); break;
  3597. case SHID_NET_DIRECTORY: pszT = TEXT("SHID_NET_DIRECTORY"); break;
  3598. case SHID_NET_TREE: pszT = TEXT("SHID_NET_TREE"); break;
  3599. case SHID_NET_REGITEM: pszT = TEXT("SHID_NET_REGITEM"); break;
  3600. case SHID_NET_PRINTER: pszT = TEXT("SHID_NET_PRINTER"); break;
  3601. default: pszT = TEXT("unknown"); break;
  3602. }
  3603. lstrcatn(szBuf, pszT, ARRAYSIZE(szBuf));
  3604. if (SIL_GetType(pidl) & SHID_JUNCTION)
  3605. {
  3606. lstrcatn(szBuf, TEXT(", junction"), ARRAYSIZE(szBuf));
  3607. }
  3608. pidl = _ILNext(pidl);
  3609. if (!ILIsEmpty(pidl))
  3610. {
  3611. lstrcatn(szBuf, TEXT("; "), ARRAYSIZE(szBuf));
  3612. }
  3613. }
  3614. return szBuf;
  3615. #else
  3616. return TEXT("");
  3617. #endif // DEBUG
  3618. }
  3619. STDAPI SaveShortcutInFolder(int csidl, LPTSTR pszName, IShellLink *psl)
  3620. {
  3621. TCHAR szPath[MAX_PATH];
  3622. HRESULT hr = SHGetFolderPath(NULL, csidl | CSIDL_FLAG_CREATE, NULL, 0, szPath);
  3623. if (SUCCEEDED(hr))
  3624. {
  3625. IPersistFile *ppf;
  3626. hr = psl->QueryInterface(IID_PPV_ARG(IPersistFile, &ppf));
  3627. if (SUCCEEDED(hr))
  3628. {
  3629. PathAppend(szPath, pszName);
  3630. WCHAR wszPath[MAX_PATH];
  3631. SHTCharToUnicode(szPath, wszPath, ARRAYSIZE(wszPath));
  3632. hr = ppf->Save(wszPath, TRUE);
  3633. ppf->Release();
  3634. }
  3635. }
  3636. return hr;
  3637. }
  3638. // TrackPopupMenu does not work, if the hwnd does not have
  3639. // the input focus. We believe this is a bug in USER
  3640. STDAPI_(BOOL) SHTrackPopupMenu(HMENU hmenu, UINT wFlags, int x, int y,
  3641. int wReserved, HWND hwndOwner, LPCRECT lprc)
  3642. {
  3643. int iRet = FALSE;
  3644. DWORD dwExStyle = 0L;
  3645. if (IS_WINDOW_RTL_MIRRORED(hwndOwner))
  3646. {
  3647. dwExStyle |= RTL_MIRRORED_WINDOW;
  3648. }
  3649. HWND hwndDummy = CreateWindowEx(dwExStyle, TEXT("Static"), NULL,
  3650. 0, x, y, 1, 1, HWND_DESKTOP,
  3651. NULL, HINST_THISDLL, NULL);
  3652. if (hwndDummy)
  3653. {
  3654. HWND hwndPrev = GetForegroundWindow(); // to restore
  3655. SetForegroundWindow(hwndDummy);
  3656. SetFocus(hwndDummy);
  3657. iRet = TrackPopupMenu(hmenu, wFlags, x, y, wReserved, hwndDummy, lprc);
  3658. //
  3659. // We MUST unlock the destination window before changing its Z-order.
  3660. //
  3661. DAD_DragLeave();
  3662. if (iRet && hwndOwner)
  3663. {
  3664. // non-cancel item is selected. Make the hwndOwner foreground.
  3665. SetForegroundWindow(hwndOwner);
  3666. SetFocus(hwndOwner);
  3667. }
  3668. else
  3669. {
  3670. // The user canceled the menu.
  3671. // Restore the previous foreground window (before destroying hwndDummy).
  3672. if (hwndPrev)
  3673. SetForegroundWindow(hwndPrev);
  3674. }
  3675. DestroyWindow(hwndDummy);
  3676. }
  3677. return iRet;
  3678. }
  3679. //
  3680. // user does not support pop-up only menu.
  3681. //
  3682. STDAPI_(HMENU) SHLoadPopupMenu(HINSTANCE hinst, UINT id)
  3683. {
  3684. HMENU hmenuParent = LoadMenu(hinst, MAKEINTRESOURCE(id));
  3685. if (hmenuParent)
  3686. {
  3687. HMENU hpopup = GetSubMenu(hmenuParent, 0);
  3688. RemoveMenu(hmenuParent, 0, MF_BYPOSITION);
  3689. DestroyMenu(hmenuParent);
  3690. return hpopup;
  3691. }
  3692. return NULL;
  3693. }
  3694. STDAPI_(void) PathToAppPathKeyBase(LPCTSTR pszBase, LPCTSTR pszPath, LPTSTR pszKey, int cchKey)
  3695. {
  3696. // Use the szTemp variable of pseem to build key to the programs specific
  3697. // key in the registry as well as other things...
  3698. StrCpyN(pszKey, pszBase, cchKey);
  3699. lstrcatn(pszKey, TEXT("\\"), cchKey);
  3700. lstrcatn(pszKey, PathFindFileName(pszPath), cchKey);
  3701. // Currently we will only look up .EXE if an extension is not
  3702. // specified
  3703. if (*PathFindExtension(pszKey) == 0)
  3704. {
  3705. lstrcatn(pszKey, c_szDotExe, cchKey);
  3706. }
  3707. }
  3708. STDAPI_(void) PathToAppPathKey(LPCTSTR pszPath, LPTSTR pszKey, int cchKey)
  3709. {
  3710. PathToAppPathKeyBase(REGSTR_PATH_APPPATHS, pszPath, pszKey, cchKey);
  3711. #ifdef _WIN64
  3712. //
  3713. // If the app isn't registered under Win64's AppPath,
  3714. // then try the 32-bit version.
  3715. //
  3716. #define REGSTR_PATH_APPPATHS32 TEXT("Software\\Wow6432Node\\Microsoft\\Windows\\CurrentVersion\\App Paths")
  3717. LONG cb;
  3718. if (RegQueryValue(HKEY_LOCAL_MACHINE, pszKey, 0, &cb) == ERROR_FILE_NOT_FOUND)
  3719. {
  3720. PathToAppPathKeyBase(REGSTR_PATH_APPPATHS32, pszPath, pszKey, cchKey);
  3721. }
  3722. #endif
  3723. }
  3724. // out:
  3725. // pszResultPath assumed to be MAX_PATH in length
  3726. STDAPI_(BOOL) PathToAppPath(LPCTSTR pszPath, LPTSTR pszResultPath)
  3727. {
  3728. TCHAR szRegKey[MAX_PATH];
  3729. LONG cbData = MAX_PATH * sizeof(TCHAR);
  3730. VDATEINPUTBUF(pszResultPath, TCHAR, MAX_PATH);
  3731. PathToAppPathKey(pszPath, szRegKey, ARRAYSIZE(szRegKey));
  3732. return SHRegQueryValue(HKEY_LOCAL_MACHINE, szRegKey, pszResultPath, &cbData) == ERROR_SUCCESS;
  3733. }
  3734. HWND GetTopParentWindow(HWND hwnd)
  3735. {
  3736. if (IsWindow(hwnd))
  3737. {
  3738. HWND hwndParent;
  3739. while (NULL != (hwndParent = GetWindow(hwnd, GW_OWNER)))
  3740. hwnd = hwndParent;
  3741. }
  3742. else
  3743. hwnd = NULL;
  3744. return hwnd;
  3745. }
  3746. BOOL _IsLink(LPCTSTR pszPath, DWORD dwAttributes)
  3747. {
  3748. SHFILEINFO sfi = {0};
  3749. DWORD dwFlags = SHGFI_ATTRIBUTES | SHGFI_ATTR_SPECIFIED;
  3750. sfi.dwAttributes = SFGAO_LINK; // setup in param (SHGFI_ATTR_SPECIFIED requires this)
  3751. if (-1 != dwAttributes)
  3752. dwFlags |= SHGFI_USEFILEATTRIBUTES;
  3753. return SHGetFileInfo(pszPath, dwAttributes, &sfi, sizeof(sfi), dwFlags) &&
  3754. (sfi.dwAttributes & SFGAO_LINK);
  3755. }
  3756. STDAPI_(BOOL) PathIsShortcut(LPCTSTR pszPath, DWORD dwAttributes)
  3757. {
  3758. BOOL bRet = FALSE;
  3759. BOOL bMightBeFile;
  3760. if (-1 == dwAttributes)
  3761. bMightBeFile = TRUE; // optmistically assume it is (to get shortcircut cases)
  3762. else
  3763. bMightBeFile = !(FILE_ATTRIBUTE_DIRECTORY & dwAttributes);
  3764. // optimistic shortcurcut. if we don't know it is a folder for sure use the extension test
  3765. if (bMightBeFile)
  3766. {
  3767. if (PathIsLnk(pszPath))
  3768. {
  3769. bRet = TRUE; // quick short-circut for perf
  3770. }
  3771. else if (PathIsExe(pszPath))
  3772. {
  3773. bRet = FALSE; // quick short-cut to avoid blowing stack on Win16
  3774. }
  3775. else
  3776. {
  3777. bRet = _IsLink(pszPath, dwAttributes);
  3778. }
  3779. }
  3780. else
  3781. {
  3782. bRet = _IsLink(pszPath, dwAttributes);
  3783. }
  3784. return bRet;
  3785. }
  3786. HRESULT _UIObject_AssocCreate(IShellFolder *psf, LPCITEMIDLIST pidl, REFIID riid, void **ppv)
  3787. {
  3788. // this means that the folder doesnt support
  3789. // the IQueryAssociations. so we will
  3790. // just check to see if this is a folder.
  3791. //
  3792. // some shellextensions mask the FILE system
  3793. // and want the file system associations to show
  3794. // up for their items. so we will try a simple pidl
  3795. //
  3796. HRESULT hr = E_NOTIMPL;
  3797. DWORD rgfAttrs = SHGetAttributes(psf, pidl, SFGAO_FOLDER | SFGAO_BROWSABLE | SFGAO_FILESYSTEM);
  3798. if (rgfAttrs & SFGAO_FILESYSTEM)
  3799. {
  3800. TCHAR sz[MAX_PATH];
  3801. hr = DisplayNameOf(psf, pidl, SHGDN_FORPARSING, sz, ARRAYSIZE(sz));
  3802. if (SUCCEEDED(hr))
  3803. {
  3804. WIN32_FIND_DATA fd = {0};
  3805. LPITEMIDLIST pidlSimple;
  3806. if (rgfAttrs & SFGAO_FOLDER)
  3807. fd.dwFileAttributes = FILE_ATTRIBUTE_DIRECTORY;
  3808. hr = SHSimpleIDListFromFindData(sz, &fd, &pidlSimple);
  3809. if (SUCCEEDED(hr))
  3810. {
  3811. // need to avoid recursion, so we cant call SHGetUIObjectOf()
  3812. hr = SHGetUIObjectFromFullPIDL(pidlSimple, NULL, riid, ppv);
  3813. ILFree(pidlSimple);
  3814. }
  3815. }
  3816. }
  3817. if (FAILED(hr) && (rgfAttrs & (SFGAO_FOLDER | SFGAO_BROWSABLE)))
  3818. {
  3819. IAssociationArrayInitialize *paa;
  3820. // make sure at least folders work.
  3821. hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IAssociationArrayInitialize, &paa));
  3822. if (SUCCEEDED(hr))
  3823. {
  3824. hr = paa->InitClassElements(0, L"Folder");
  3825. if (SUCCEEDED(hr))
  3826. {
  3827. hr = paa->QueryInterface(riid, ppv);
  3828. }
  3829. paa->Release();
  3830. }
  3831. }
  3832. return hr;
  3833. }
  3834. typedef HRESULT (* PFNUIOBJECTHELPER)(IShellFolder *psf, LPCITEMIDLIST pidl, REFIID riid, void **ppv);
  3835. typedef struct
  3836. {
  3837. const IID *piidDesired;
  3838. const IID *piidAlternate;
  3839. PFNUIOBJECTHELPER pfn;
  3840. } UIOBJECTMAP;
  3841. static const UIOBJECTMAP c_rgUIObjectMap[] =
  3842. {
  3843. {&IID_IQueryAssociations, NULL, _UIObject_AssocCreate},
  3844. {&IID_IAssociationArray, &IID_IQueryAssociations, _UIObject_AssocCreate},
  3845. // {&IID_IContextMenu2, &IID_IContextMenu, NULL},
  3846. };
  3847. const UIOBJECTMAP *_GetUiObjectMap(REFIID riid)
  3848. {
  3849. for (int i = 0; i < ARRAYSIZE(c_rgUIObjectMap); i++)
  3850. {
  3851. if (riid == *(c_rgUIObjectMap[i].piidDesired))
  3852. return &c_rgUIObjectMap[i];
  3853. }
  3854. return NULL;
  3855. }
  3856. STDAPI UIObjectOf(IShellFolder *psf, LPCITEMIDLIST pidl, HWND hwnd, REFIID riid, void **ppv)
  3857. {
  3858. HRESULT hr = psf->GetUIObjectOf(hwnd, 1, &pidl, riid, NULL, ppv);
  3859. if (FAILED(hr))
  3860. {
  3861. const UIOBJECTMAP *pmap = _GetUiObjectMap(riid);
  3862. if (pmap)
  3863. {
  3864. if (pmap->piidAlternate)
  3865. {
  3866. IUnknown *punk;
  3867. hr = psf->GetUIObjectOf(hwnd, 1, &pidl, *(pmap->piidAlternate), NULL, (void **)&punk);
  3868. if (SUCCEEDED(hr))
  3869. {
  3870. hr = punk->QueryInterface(riid, ppv);
  3871. punk->Release();
  3872. }
  3873. }
  3874. // let the fallback code run
  3875. if (FAILED(hr) && pmap->pfn)
  3876. {
  3877. hr = pmap->pfn(psf, pidl, riid, ppv);
  3878. }
  3879. }
  3880. }
  3881. return hr;
  3882. }
  3883. STDAPI AssocGetDetailsOfSCID(IShellFolder *psf, LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, VARIANT *pv, BOOL *pfFoundScid)
  3884. {
  3885. *pfFoundScid = FALSE;
  3886. HRESULT hr = E_NOTIMPL;
  3887. static const struct
  3888. {
  3889. const SHCOLUMNID *pscid;
  3890. ASSOCQUERY query;
  3891. LPCWSTR pszCue;
  3892. } s_rgAssocSCIDs[] =
  3893. {
  3894. { &SCID_DetailsProperties, AQN_NAMED_VALUE, L"Details"},
  3895. };
  3896. for (int i = 0; i < ARRAYSIZE(s_rgAssocSCIDs); i++)
  3897. {
  3898. if (IsEqualSCID(*pscid, *(s_rgAssocSCIDs[i].pscid)))
  3899. {
  3900. IAssociationArray *paa;
  3901. hr = UIObjectOf(psf, pidl, NULL, IID_PPV_ARG(IAssociationArray, &paa));
  3902. if (SUCCEEDED(hr))
  3903. {
  3904. CSmartCoTaskMem<WCHAR> spsz;
  3905. hr = paa->QueryString(ASSOCELEM_MASK_QUERYNORMAL, s_rgAssocSCIDs[i].query, s_rgAssocSCIDs[i].pszCue, &spsz);
  3906. if (SUCCEEDED(hr))
  3907. {
  3908. hr = InitVariantFromStr(pv, spsz);
  3909. }
  3910. paa->Release();
  3911. }
  3912. break;
  3913. }
  3914. }
  3915. return hr;
  3916. }
  3917. //
  3918. // retrieves the UIObject interface for the specified full pidl.
  3919. //
  3920. STDAPI SHGetUIObjectOf(LPCITEMIDLIST pidl, HWND hwnd, REFIID riid, void **ppv)
  3921. {
  3922. *ppv = NULL;
  3923. LPCITEMIDLIST pidlChild;
  3924. IShellFolder* psf;
  3925. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlChild);
  3926. if (SUCCEEDED(hr))
  3927. {
  3928. hr = UIObjectOf(psf, pidlChild, hwnd, riid, ppv);
  3929. psf->Release();
  3930. }
  3931. return hr;
  3932. }
  3933. STDAPI SHGetAssociations(LPCITEMIDLIST pidl, void **ppv)
  3934. {
  3935. return SHGetUIObjectOf(pidl, NULL, IID_IQueryAssociations, ppv);
  3936. }
  3937. //
  3938. // SHGetAssocKeys() retrieves an array of class keys
  3939. // from a pqa.
  3940. //
  3941. // if the caller is just interested in the primary class key,
  3942. // call with cKeys == 1. the return value is the number of keys
  3943. // inserted into the array.
  3944. //
  3945. STDAPI AssocKeyFromElement(IAssociationElement *pae, HKEY *phk)
  3946. {
  3947. IObjectWithQuerySource *powqs;
  3948. HRESULT hr = pae->QueryInterface(IID_PPV_ARG(IObjectWithQuerySource, &powqs));
  3949. if (SUCCEEDED(hr))
  3950. {
  3951. IObjectWithRegistryKey *powrk;
  3952. hr = powqs->GetSource(IID_PPV_ARG(IObjectWithRegistryKey, &powrk));
  3953. if (SUCCEEDED(hr))
  3954. {
  3955. hr = powrk->GetKey(phk);
  3956. powrk->Release();
  3957. }
  3958. powqs->Release();
  3959. }
  3960. return hr;
  3961. }
  3962. HRESULT AssocElemCreateForClass(const CLSID *pclsid, PCWSTR pszClass, IAssociationElement **ppae)
  3963. {
  3964. IPersistString2 *pips;
  3965. HRESULT hr = AssocCreate(*pclsid, IID_PPV_ARG(IPersistString2, &pips));
  3966. if (SUCCEEDED(hr))
  3967. {
  3968. hr = pips->SetString(pszClass);
  3969. if (SUCCEEDED(hr))
  3970. {
  3971. hr = pips->QueryInterface(IID_PPV_ARG(IAssociationElement, ppae));
  3972. }
  3973. pips->Release();
  3974. }
  3975. return hr;
  3976. }
  3977. HRESULT AssocElemCreateForKey(const CLSID *pclsid, HKEY hk, IAssociationElement **ppae)
  3978. {
  3979. IObjectWithQuerySource *powqs;
  3980. HRESULT hr = AssocCreate(*pclsid, IID_PPV_ARG(IObjectWithQuerySource, &powqs));
  3981. if (SUCCEEDED(hr))
  3982. {
  3983. IQuerySource *pqs;
  3984. hr = QuerySourceCreateFromKey(hk, NULL, FALSE, IID_PPV_ARG(IQuerySource, &pqs));
  3985. if (SUCCEEDED(hr))
  3986. {
  3987. hr = powqs->SetSource(pqs);
  3988. if (SUCCEEDED(hr))
  3989. {
  3990. hr = powqs->QueryInterface(IID_PPV_ARG(IAssociationElement, ppae));
  3991. }
  3992. pqs->Release();
  3993. }
  3994. powqs->Release();
  3995. }
  3996. return hr;
  3997. }
  3998. STDAPI_(DWORD) SHGetAssocKeysEx(IAssociationArray *paa, ASSOCELEM_MASK mask, HKEY *rgKeys, DWORD cKeys)
  3999. {
  4000. DWORD cRet = 0;
  4001. IEnumAssociationElements *penum;
  4002. HRESULT hr = paa->EnumElements(mask, &penum);
  4003. if (SUCCEEDED(hr))
  4004. {
  4005. IAssociationElement *pae;
  4006. ULONG c;
  4007. while (cRet < cKeys && S_OK == penum->Next(1, &pae, &c))
  4008. {
  4009. if (SUCCEEDED(AssocKeyFromElement(pae, &rgKeys[cRet])))
  4010. cRet++;
  4011. pae->Release();
  4012. }
  4013. penum->Release();
  4014. }
  4015. return cRet;
  4016. }
  4017. DWORD SHGetAssocKeys(IQueryAssociations *pqa, HKEY *rgKeys, DWORD cKeys)
  4018. {
  4019. IAssociationArray *paa;
  4020. if (SUCCEEDED(pqa->QueryInterface(IID_PPV_ARG(IAssociationArray, &paa))))
  4021. {
  4022. cKeys = SHGetAssocKeysEx(paa, ASSOCELEM_MASK_ENUMCONTEXTMENU, rgKeys, cKeys);
  4023. paa->Release();
  4024. }
  4025. else
  4026. cKeys = 0;
  4027. return cKeys;
  4028. }
  4029. STDAPI_(DWORD) SHGetAssocKeysForIDList(LPCITEMIDLIST pidl, HKEY *rghk, DWORD ck)
  4030. {
  4031. IQueryAssociations *pqa;
  4032. if (SUCCEEDED(SHGetAssociations(pidl, (void **)&pqa)))
  4033. {
  4034. ck = SHGetAssocKeys(pqa, rghk, ck);
  4035. pqa->Release();
  4036. }
  4037. else
  4038. ck = 0;
  4039. return ck;
  4040. }
  4041. // NOTE:
  4042. // this API returns a win32 file system path for the item in the name space
  4043. // and has a few special cases that include returning UNC printer names too!
  4044. STDAPI_(BOOL) SHGetPathFromIDListEx(LPCITEMIDLIST pidl, LPTSTR pszPath, UINT uOpts)
  4045. {
  4046. VDATEINPUTBUF(pszPath, TCHAR, MAX_PATH);
  4047. HRESULT hr;
  4048. *pszPath = 0; // zero output buffer
  4049. if (!pidl)
  4050. return FALSE; // bad params
  4051. if (ILIsEmpty(pidl))
  4052. {
  4053. // desktop special case because we can not depend on the desktop
  4054. // returning a file system path (APP compat)
  4055. hr = SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, 0, pszPath);
  4056. if (hr == S_FALSE)
  4057. hr = E_FAIL;
  4058. }
  4059. else
  4060. {
  4061. IShellFolder *psf;
  4062. LPCITEMIDLIST pidlLast;
  4063. hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  4064. if (SUCCEEDED(hr))
  4065. {
  4066. hr = DisplayNameOf(psf, pidlLast, SHGDN_FORPARSING, pszPath, MAX_PATH);
  4067. if (SUCCEEDED(hr))
  4068. {
  4069. DWORD dwAttributes = SFGAO_FILESYSTEM;
  4070. hr = psf->GetAttributesOf(1, (LPCITEMIDLIST *)&pidlLast, &dwAttributes);
  4071. if (SUCCEEDED(hr) && !(dwAttributes & SFGAO_FILESYSTEM))
  4072. {
  4073. // special case for UNC printer names. this is an app
  4074. // compat issue (HP LaserJet 2100 setup) & some Semantic apps
  4075. if (uOpts & GPFIDL_UNCPRINTER)
  4076. {
  4077. CLSID clsid;
  4078. hr = IUnknown_GetClassID(psf, &clsid);
  4079. if (FAILED(hr) || (clsid != CLSID_NetworkServer))
  4080. {
  4081. hr = E_FAIL;
  4082. *pszPath = 0;
  4083. }
  4084. }
  4085. else
  4086. {
  4087. hr = E_FAIL; // not a file system guy, slam it
  4088. *pszPath = 0;
  4089. }
  4090. }
  4091. }
  4092. psf->Release();
  4093. }
  4094. }
  4095. if (SUCCEEDED(hr) && (uOpts & GPFIDL_ALTNAME))
  4096. {
  4097. TCHAR szShort[MAX_PATH];
  4098. if (GetShortPathName(pszPath, szShort, ARRAYSIZE(szShort)))
  4099. {
  4100. lstrcpy(pszPath, szShort);
  4101. }
  4102. }
  4103. return SUCCEEDED(hr);
  4104. }
  4105. STDAPI_(BOOL) SHGetPathFromIDList(LPCITEMIDLIST pidl, LPTSTR pszPath)
  4106. {
  4107. // NOTE: we pass GPFIDL_UNCPRINTER to get UNC printer names too
  4108. return SHGetPathFromIDListEx(pidl, pszPath, GPFIDL_UNCPRINTER);
  4109. }
  4110. #define CBHUMHEADER 14
  4111. inline BOOL _DoHummingbirdHack(LPCITEMIDLIST pidl)
  4112. {
  4113. static const char rgchHum[] = {(char)0xe8, (char)0x03, 0,0,0,0,0,0,(char)0x10,0};
  4114. return (pidl && pidl->mkid.cb > CBHUMHEADER) && ILIsEmpty(_ILNext(pidl))
  4115. && (0 == memcmp(_ILSkip(pidl, 4), rgchHum, sizeof(rgchHum)))
  4116. && GetModuleHandle(TEXT("heshell"));
  4117. }
  4118. STDAPI_(BOOL) SHGetPathFromIDListA(LPCITEMIDLIST pidl, LPSTR pszPath)
  4119. {
  4120. WCHAR wszPath[MAX_PATH];
  4121. *pszPath = 0; // Assume error
  4122. if (SHGetPathFromIDListW(pidl, wszPath))
  4123. {
  4124. // Thunk the output result string back to ANSI. If the conversion fails,
  4125. // or if the default char is used, we fail the API call.
  4126. if (0 == WideCharToMultiByte(CP_ACP, 0, wszPath, -1, pszPath, MAX_PATH, NULL, NULL))
  4127. {
  4128. return FALSE; // (DavePl) Note failure only due to text thunking
  4129. }
  4130. return TRUE; // warning, word perfect tests explictly for TRUE (== TRUE)
  4131. }
  4132. else if (_DoHummingbirdHack(pidl))
  4133. {
  4134. //
  4135. // HACKHACK - hummingbird isn't very good here because we used be even worse - Zekel 7-OCT-99
  4136. // hummingbird's shell extension passes us a pidl that is relative to
  4137. // itself. however SHGetPathFromIDList() used to try some weird stuff
  4138. // when it didnt recognize the pidl. in this case it would combine the
  4139. // relative pidl with the CSIDL_DESKTOPDIRECTORY pidl, and then ask for
  4140. // the path. due to crappy parameter validation we would actually return
  4141. // back a path with a string from inside their relative pidl. the path
  4142. // of course doesnt exist at all, but hummingbird fails to initialize its
  4143. // subfolders if we fail here. they dont do anything with the path except
  4144. // look for a slash. (if the slash is missing they will fault.)
  4145. //
  4146. SHGetFolderPathA(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, pszPath);
  4147. PathAppendA(pszPath, (LPCSTR)_ILSkip(pidl, CBHUMHEADER));
  4148. return TRUE;
  4149. }
  4150. return FALSE;
  4151. }
  4152. //
  4153. // Race-condition-free version.
  4154. //
  4155. STDAPI_(HANDLE) SHGetCachedGlobalCounter(HANDLE *phCache, const GUID *pguid)
  4156. {
  4157. if (!*phCache)
  4158. {
  4159. HANDLE h = SHGlobalCounterCreate(*pguid);
  4160. if (SHInterlockedCompareExchange(phCache, h, 0))
  4161. {
  4162. // some other thread raced with us, throw away our copy
  4163. SHGlobalCounterDestroy(h);
  4164. }
  4165. }
  4166. return *phCache;
  4167. }
  4168. //
  4169. // Race-condition-free version.
  4170. //
  4171. STDAPI_(void) SHDestroyCachedGlobalCounter(HANDLE *phCache)
  4172. {
  4173. HANDLE h = InterlockedExchangePointer(phCache, NULL);
  4174. if (h)
  4175. {
  4176. SHGlobalCounterDestroy(h);
  4177. }
  4178. }
  4179. //
  4180. // Use this function when you want to lazy-create and cache some object.
  4181. // It's safe in the multithreaded case where two people lazy-create the
  4182. // object and both try to put it into the cache.
  4183. //
  4184. STDAPI_(void) SetUnknownOnSuccess(HRESULT hr, IUnknown *punk, IUnknown **ppunkToSet)
  4185. {
  4186. if (SUCCEEDED(hr))
  4187. {
  4188. if (SHInterlockedCompareExchange((void **)ppunkToSet, punk, 0))
  4189. punk->Release(); // race, someone did this already
  4190. }
  4191. }
  4192. //
  4193. // Create and cache a tracking folder.
  4194. //
  4195. // pidlRoot = where the folder should reside; can be MAKEINTIDLIST(csidl).
  4196. // csidlTarget = the csidl we should track, CSIDL_FLAG_CREATE is allowed
  4197. // ppsfOut = Receives the cached folder
  4198. //
  4199. // If there is already a folder in the cache, succeeds vacuously.
  4200. //
  4201. STDAPI SHCacheTrackingFolder(LPCITEMIDLIST pidlRoot, int csidlTarget, IShellFolder2 **ppsfCache)
  4202. {
  4203. HRESULT hr = S_OK;
  4204. if (!*ppsfCache)
  4205. {
  4206. PERSIST_FOLDER_TARGET_INFO pfti = {0};
  4207. IShellFolder2 *psf;
  4208. LPITEMIDLIST pidl;
  4209. // add FILE_ATTRIBUTE_SYSTEM to allow MUI stuff underneath this folder.
  4210. // since its just for these tracking folders it isnt a perf hit to enable this.
  4211. pfti.dwAttributes = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_SYSTEM;
  4212. pfti.csidl = csidlTarget | CSIDL_FLAG_PFTI_TRACKTARGET;
  4213. if (IS_INTRESOURCE(pidlRoot))
  4214. {
  4215. hr = SHGetFolderLocation(NULL, PtrToInt(pidlRoot), NULL, 0, &pidl);
  4216. }
  4217. else
  4218. {
  4219. pidl = const_cast<LPITEMIDLIST>(pidlRoot);
  4220. }
  4221. if (SUCCEEDED(hr))
  4222. {
  4223. hr = CFSFolder_CreateFolder(NULL, NULL, pidl, &pfti, IID_PPV_ARG(IShellFolder2, &psf));
  4224. SetUnknownOnSuccess(hr, psf, (IUnknown **)ppsfCache);
  4225. }
  4226. if (pidl != pidlRoot)
  4227. ILFree(pidl);
  4228. }
  4229. return hr;
  4230. }
  4231. STDAPI DefaultSearchGUID(GUID *pGuid)
  4232. {
  4233. if (SHRestricted(REST_NOFIND))
  4234. {
  4235. *pGuid = GUID_NULL;
  4236. return E_NOTIMPL;
  4237. }
  4238. *pGuid = SRCID_SFileSearch;
  4239. return S_OK;
  4240. }
  4241. // Helper function to save IPersistHistory stream for you
  4242. //
  4243. HRESULT SavePersistHistory(IUnknown* punk, IStream* pstm)
  4244. {
  4245. IPersistHistory* pPH;
  4246. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IPersistHistory, &pPH));
  4247. if (SUCCEEDED(hr))
  4248. {
  4249. CLSID clsid;
  4250. hr = pPH->GetClassID(&clsid);
  4251. if (SUCCEEDED(hr))
  4252. {
  4253. hr = IStream_Write(pstm, &clsid, sizeof(clsid));
  4254. if (SUCCEEDED(hr))
  4255. {
  4256. hr = pPH->SaveHistory(pstm);
  4257. }
  4258. }
  4259. pPH->Release();
  4260. }
  4261. return hr;
  4262. }
  4263. STDAPI Stream_WriteStringA(IStream *pstm, LPCSTR psz)
  4264. {
  4265. SHORT cch = (SHORT)lstrlenA(psz);
  4266. HRESULT hr = pstm->Write(&cch, sizeof(cch), NULL);
  4267. if (SUCCEEDED(hr))
  4268. hr = pstm->Write(psz, cch * sizeof(*psz), NULL);
  4269. return hr;
  4270. }
  4271. STDAPI Stream_WriteStringW(IStream *pstm, LPCWSTR psz)
  4272. {
  4273. SHORT cch = (SHORT)lstrlenW(psz);
  4274. HRESULT hr = pstm->Write(&cch, sizeof(cch), NULL);
  4275. if (SUCCEEDED(hr))
  4276. hr = pstm->Write(psz, cch * sizeof(*psz), NULL);
  4277. return hr;
  4278. }
  4279. STDAPI Stream_WriteString(IStream *pstm, LPCWSTR psz, BOOL bWideInStream)
  4280. {
  4281. HRESULT hr;
  4282. if (bWideInStream)
  4283. {
  4284. hr = Stream_WriteStringW(pstm, psz);
  4285. }
  4286. else
  4287. {
  4288. CHAR szBuf[MAX_PATH];
  4289. SHUnicodeToAnsi(psz, szBuf, ARRAYSIZE(szBuf));
  4290. hr = Stream_WriteStringA(pstm, szBuf);
  4291. }
  4292. return hr;
  4293. }
  4294. STDAPI Stream_ReadStringA(IStream *pstm, LPSTR pszBuf, UINT cchBuf)
  4295. {
  4296. *pszBuf = 0;
  4297. USHORT cch;
  4298. HRESULT hr = pstm->Read(&cch, sizeof(cch), NULL); // size of data
  4299. if (SUCCEEDED(hr))
  4300. {
  4301. if (cch >= (USHORT)cchBuf)
  4302. {
  4303. DebugMsg(DM_TRACE, TEXT("truncating string read(%d to %d)"), cch, cchBuf);
  4304. cch = (USHORT)cchBuf - 1; // leave room for null terminator
  4305. }
  4306. hr = pstm->Read(pszBuf, cch, NULL);
  4307. if (SUCCEEDED(hr))
  4308. pszBuf[cch] = 0; // add NULL terminator
  4309. }
  4310. return hr;
  4311. }
  4312. STDAPI Stream_ReadStringW(IStream *pstm, LPWSTR pwszBuf, UINT cchBuf)
  4313. {
  4314. *pwszBuf = 0;
  4315. USHORT cch;
  4316. HRESULT hr = pstm->Read(&cch, sizeof(cch), NULL); // size of data
  4317. if (SUCCEEDED(hr))
  4318. {
  4319. if (cch >= (USHORT)cchBuf)
  4320. {
  4321. DebugMsg(DM_TRACE, TEXT("truncating string read(%d to %d)"), cch, cchBuf);
  4322. cch = (USHORT)cchBuf - 1; // leave room for null terminator
  4323. }
  4324. hr = pstm->Read(pwszBuf, cch * sizeof(*pwszBuf), NULL);
  4325. if (SUCCEEDED(hr))
  4326. pwszBuf[cch] = 0; // add NULL terminator
  4327. }
  4328. return hr;
  4329. }
  4330. STDAPI Stream_ReadString(IStream *pstm, LPTSTR psz, UINT cchBuf, BOOL bWideInStream)
  4331. {
  4332. HRESULT hr;
  4333. if (bWideInStream)
  4334. {
  4335. hr = Stream_ReadStringW(pstm, psz, cchBuf);
  4336. }
  4337. else
  4338. {
  4339. CHAR szAnsiBuf[MAX_PATH];
  4340. hr = Stream_ReadStringA(pstm, szAnsiBuf, ARRAYSIZE(szAnsiBuf));
  4341. if (SUCCEEDED(hr))
  4342. SHAnsiToUnicode(szAnsiBuf, psz, cchBuf);
  4343. }
  4344. return hr;
  4345. }
  4346. STDAPI Str_SetFromStream(IStream *pstm, LPTSTR *ppsz, BOOL bWideInStream)
  4347. {
  4348. TCHAR szBuf[MAX_PATH];
  4349. HRESULT hr = Stream_ReadString(pstm, szBuf, ARRAYSIZE(szBuf), bWideInStream);
  4350. if (SUCCEEDED(hr))
  4351. if (!Str_SetPtr(ppsz, szBuf))
  4352. hr = E_OUTOFMEMORY;
  4353. return hr;
  4354. }
  4355. LPSTR ThunkStrToAnsi(LPCWSTR pszW, CHAR *pszA, UINT cchA)
  4356. {
  4357. if (pszW)
  4358. {
  4359. SHUnicodeToAnsi(pszW, pszA, cchA);
  4360. return pszA;
  4361. }
  4362. return NULL;
  4363. }
  4364. LPWSTR ThunkStrToWide(LPCSTR pszA, LPWSTR pszW, DWORD cchW)
  4365. {
  4366. if (pszA)
  4367. {
  4368. SHAnsiToUnicode(pszA, pszW, cchW);
  4369. return pszW;
  4370. }
  4371. return NULL;
  4372. }
  4373. #define ThunkSizeAnsi(pwsz) WideCharToMultiByte(CP_ACP,0,(pwsz),-1,NULL,0,NULL,NULL)
  4374. #define ThunkSizeWide(psz) MultiByteToWideChar(CP_ACP,0,(psz),-1,NULL,0)
  4375. STDAPI SEI2ICIX(LPSHELLEXECUTEINFO pei, LPCMINVOKECOMMANDINFOEX pici, void **ppvFree)
  4376. {
  4377. HRESULT hr = S_OK;
  4378. *ppvFree = NULL;
  4379. ZeroMemory(pici, sizeof(CMINVOKECOMMANDINFOEX));
  4380. pici->cbSize = sizeof(CMINVOKECOMMANDINFOEX);
  4381. pici->fMask = (pei->fMask & SEE_VALID_CMIC_BITS);
  4382. pici->hwnd = pei->hwnd;
  4383. pici->nShow = pei->nShow;
  4384. pici->dwHotKey = pei->dwHotKey;
  4385. pici->lpTitle = NULL;
  4386. // the pei->hIcon can have multiple meanings...
  4387. if ((pei->fMask & SEE_MASK_HMONITOR) && pei->hIcon)
  4388. {
  4389. // in this case we want the hMonitor to
  4390. // make it through to where the pcm calls shellexec
  4391. // again.
  4392. RECT rc;
  4393. if (GetMonitorRect((HMONITOR)pei->hIcon, &rc))
  4394. {
  4395. // default to the top left corner of
  4396. // the monitor. it is just the monitor
  4397. // that is relevant here.
  4398. pici->ptInvoke.x = rc.left;
  4399. pici->ptInvoke.y = rc.top;
  4400. pici->fMask |= CMIC_MASK_PTINVOKE;
  4401. }
  4402. }
  4403. else
  4404. {
  4405. pici->hIcon = pei->hIcon;
  4406. }
  4407. // again, pei->lpClass can have multiple meanings...
  4408. if (pei->fMask & (SEE_MASK_HASTITLE | SEE_MASK_HASLINKNAME))
  4409. {
  4410. pici->lpTitleW = pei->lpClass;
  4411. }
  4412. pici->lpVerbW = pei->lpVerb;
  4413. pici->lpParametersW = pei->lpParameters;
  4414. pici->lpDirectoryW = pei->lpDirectory;
  4415. // we need to thunk the strings down. first get the length of all the buffers
  4416. DWORD cbVerb = ThunkSizeAnsi(pei->lpVerb);
  4417. DWORD cbParameters = ThunkSizeAnsi(pei->lpParameters);
  4418. DWORD cbDirectory = ThunkSizeAnsi(pei->lpDirectory);
  4419. DWORD cbTotal = cbVerb + cbParameters + cbDirectory;
  4420. if (cbTotal)
  4421. {
  4422. hr = SHLocalAlloc(cbVerb + cbParameters + cbDirectory, ppvFree);
  4423. if (SUCCEEDED(hr))
  4424. {
  4425. LPSTR pch = (LPSTR) *ppvFree;
  4426. pici->lpVerb = ThunkStrToAnsi(pei->lpVerb, pch, cbVerb);
  4427. pch += cbVerb;
  4428. pici->lpParameters = ThunkStrToAnsi(pei->lpParameters, pch, cbParameters);
  4429. pch += cbParameters;
  4430. pici->lpDirectory = ThunkStrToAnsi(pei->lpDirectory, pch, cbDirectory);
  4431. }
  4432. }
  4433. pici->fMask |= CMIC_MASK_UNICODE;
  4434. return hr;
  4435. }
  4436. STDAPI ICIX2SEI(LPCMINVOKECOMMANDINFOEX pici, LPSHELLEXECUTEINFO pei)
  4437. {
  4438. // perhaps we should allow just plain ici's, and do the thunk in here, but
  4439. // it looks like all the callers want to do the thunk themselves...
  4440. // HRESULT hr = S_OK;
  4441. ZeroMemory(pei, sizeof(SHELLEXECUTEINFO));
  4442. pei->cbSize = sizeof(SHELLEXECUTEINFO);
  4443. pei->fMask = pici->fMask & SEE_VALID_CMIC_BITS;
  4444. // if we are doing this async, then we will abort this thread
  4445. // as soon as the shellexecute completes. If the app holds open
  4446. // a dde conversation, this may hang them. This happens on W95 base
  4447. // with winword95
  4448. IUnknown *punk;
  4449. HRESULT hr = TBCGetObjectParam(TBCDIDASYNC, IID_PPV_ARG(IUnknown, &punk));
  4450. if (SUCCEEDED(hr))
  4451. {
  4452. pei->fMask |= SEE_MASK_FLAG_DDEWAIT;
  4453. punk->Release();
  4454. }
  4455. pei->hwnd = pici->hwnd;
  4456. pei->nShow = pici->nShow;
  4457. pei->dwHotKey = pici->dwHotKey;
  4458. if (pici->fMask & CMIC_MASK_ICON)
  4459. {
  4460. pei->hIcon = pici->hIcon;
  4461. }
  4462. else if (pici->fMask & CMIC_MASK_PTINVOKE)
  4463. {
  4464. pei->hIcon = (HANDLE)MonitorFromPoint(pici->ptInvoke, MONITOR_DEFAULTTONEAREST);
  4465. pei->fMask |= SEE_MASK_HMONITOR;
  4466. }
  4467. ASSERT(pici->fMask & CMIC_MASK_UNICODE);
  4468. pei->lpParameters = pici->lpParametersW;
  4469. pei->lpDirectory = pici->lpDirectoryW;
  4470. if (!IS_INTRESOURCE(pici->lpVerbW))
  4471. {
  4472. pei->lpVerb = pici->lpVerbW;
  4473. }
  4474. // both the title and linkname can be stored the lpClass field
  4475. if (pici->fMask & (CMIC_MASK_HASTITLE | CMIC_MASK_HASLINKNAME))
  4476. {
  4477. pei->lpClass = pici->lpTitleW;
  4478. }
  4479. // if we have to do any thunking in here, we
  4480. // will have a real return hr.
  4481. return S_OK;
  4482. }
  4483. STDAPI ICI2ICIX(LPCMINVOKECOMMANDINFO piciIn, LPCMINVOKECOMMANDINFOEX piciOut, void **ppvFree)
  4484. {
  4485. ASSERT(piciIn->cbSize >= sizeof(CMINVOKECOMMANDINFO));
  4486. HRESULT hr = S_OK;
  4487. *ppvFree = NULL;
  4488. ZeroMemory(piciOut, sizeof(*piciOut));
  4489. memcpy(piciOut, piciIn, min(sizeof(*piciOut), piciIn->cbSize));
  4490. piciOut->cbSize = sizeof(*piciOut);
  4491. // if the UNICODE params arent there, we must put them there
  4492. if (!(piciIn->cbSize >= CMICEXSIZE_NT4) || !(piciIn->fMask & CMIC_MASK_UNICODE))
  4493. {
  4494. DWORD cchDirectory = ThunkSizeWide(piciOut->lpDirectory);
  4495. DWORD cchTitle = ThunkSizeWide(piciOut->lpTitle);
  4496. DWORD cchParameters = ThunkSizeWide(piciOut->lpParameters);
  4497. DWORD cchVerb = 0;
  4498. if (!IS_INTRESOURCE(piciOut->lpVerb))
  4499. cchVerb = ThunkSizeWide(piciOut->lpVerb);
  4500. DWORD cchTotal = (cchDirectory + cchTitle + cchVerb + cchParameters);
  4501. if (cchTotal)
  4502. {
  4503. hr = SHLocalAlloc(sizeof(WCHAR) * cchTotal, ppvFree);
  4504. if (SUCCEEDED(hr))
  4505. {
  4506. LPWSTR pch = (LPWSTR) *ppvFree;
  4507. piciOut->lpDirectoryW = ThunkStrToWide(piciOut->lpDirectory, pch, cchDirectory);
  4508. pch += cchDirectory;
  4509. piciOut->lpTitleW = ThunkStrToWide(piciOut->lpTitle, pch, cchTitle);
  4510. pch += cchTitle;
  4511. piciOut->lpParametersW = ThunkStrToWide(piciOut->lpParameters, pch, cchParameters);
  4512. pch += cchParameters;
  4513. //only thunk if it is a string...
  4514. if (!IS_INTRESOURCE(piciOut->lpVerb))
  4515. {
  4516. piciOut->lpVerbW = ThunkStrToWide(piciOut->lpVerb, pch, cchVerb);
  4517. }
  4518. else
  4519. {
  4520. piciOut->lpVerbW = (LPCWSTR)piciOut->lpVerb;
  4521. }
  4522. }
  4523. }
  4524. piciOut->fMask |= CMIC_MASK_UNICODE;
  4525. }
  4526. return hr;
  4527. }
  4528. IProgressDialog * CProgressDialog_CreateInstance(UINT idTitle, UINT idAnimation, HINSTANCE hAnimationInst)
  4529. {
  4530. IProgressDialog * ppd;
  4531. if (SUCCEEDED(CoCreateInstance(CLSID_ProgressDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IProgressDialog, &ppd))))
  4532. {
  4533. WCHAR wzTitle[MAX_PATH];
  4534. EVAL(SUCCEEDED(ppd->SetAnimation(hAnimationInst, idAnimation)));
  4535. if (EVAL(LoadStringW(HINST_THISDLL, idTitle, wzTitle, ARRAYSIZE(wzTitle))))
  4536. EVAL(SUCCEEDED(ppd->SetTitle(wzTitle)));
  4537. }
  4538. return ppd;
  4539. }
  4540. STDAPI GetCurFolderImpl(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl)
  4541. {
  4542. if (pidl)
  4543. return SHILClone(pidl, ppidl);
  4544. *ppidl = NULL;
  4545. return S_FALSE; // success but empty
  4546. }
  4547. //
  4548. // converts a simple PIDL to a real PIDL by converting to display name and then
  4549. // reparsing the name
  4550. //
  4551. STDAPI SHGetRealIDL(IShellFolder *psf, LPCITEMIDLIST pidlSimple, LPITEMIDLIST *ppidlReal)
  4552. {
  4553. *ppidlReal = NULL; // clear output
  4554. STRRET str;
  4555. HRESULT hr = IShellFolder_GetDisplayNameOf(psf, pidlSimple, SHGDN_FORPARSING | SHGDN_INFOLDER, &str, 0);
  4556. if (SUCCEEDED(hr))
  4557. {
  4558. WCHAR szPath[MAX_PATH];
  4559. hr = StrRetToBufW(&str, pidlSimple, szPath, ARRAYSIZE(szPath));
  4560. if (SUCCEEDED(hr))
  4561. {
  4562. DWORD dwAttrib = SFGAO_FILESYSTEM;
  4563. if (SUCCEEDED(psf->GetAttributesOf(1, &pidlSimple, &dwAttrib)) && !(dwAttrib & SFGAO_FILESYSTEM))
  4564. {
  4565. // not a file sys object, some name spaces (WinCE) support
  4566. // parse, but don't do a good job, in this case
  4567. // return the input as the output
  4568. hr = SHILClone(pidlSimple, ppidlReal);
  4569. }
  4570. else
  4571. {
  4572. hr = IShellFolder_ParseDisplayName(psf, NULL, NULL, szPath, NULL, ppidlReal, NULL);
  4573. if (E_INVALIDARG == hr || E_NOTIMPL == hr)
  4574. {
  4575. // name space does not support parse, assume pidlSimple is OK
  4576. hr = SHILClone(pidlSimple, ppidlReal);
  4577. }
  4578. }
  4579. }
  4580. }
  4581. return hr;
  4582. }
  4583. // trial and error has shown that
  4584. // 16k is a good number for FTP,
  4585. // thus we will use that as our default buffer
  4586. #define CBOPTIMAL (16 * 1024)
  4587. STDAPI CopyStreamUI(IStream *pstmSrc, IStream *pstmDest, IProgressDialog *pdlg, ULONGLONG ullMaxBytes)
  4588. {
  4589. HRESULT hr = E_FAIL;
  4590. ULONGLONG ullMax;
  4591. if (ullMaxBytes != 0)
  4592. {
  4593. ullMax = ullMaxBytes;
  4594. }
  4595. else if (pdlg)
  4596. {
  4597. STATSTG stat = {0};
  4598. if (FAILED(pstmSrc->Stat(&stat, STATFLAG_NONAME)))
  4599. pdlg = NULL;
  4600. else
  4601. ullMax = stat.cbSize.QuadPart;
  4602. }
  4603. if (!pdlg)
  4604. {
  4605. ULARGE_INTEGER ulMax = {-1, -1};
  4606. // If ullMaxBytes was passed in as non-zero, don't write more bytes than we were told to:
  4607. if (0 != ullMaxBytes)
  4608. {
  4609. ulMax.QuadPart = ullMaxBytes;
  4610. }
  4611. hr = pstmSrc->CopyTo(pstmDest, ulMax, NULL, NULL);
  4612. // BUBBUGREMOVE - URLMON has bug which breaks CopyTo() - Zekel
  4613. // fix URLMON and then we can remove this garbage.
  4614. // so we will fake it here
  4615. }
  4616. if (FAILED(hr))
  4617. {
  4618. // try doing it by hand
  4619. void *pv = LocalAlloc(LPTR, CBOPTIMAL);
  4620. BYTE buf[1024];
  4621. ULONG cbBuf, cbRead, cbBufReal;
  4622. ULONGLONG ullCurr = 0;
  4623. // need to reset the streams,
  4624. // because CopyTo() doesnt guarantee any kind
  4625. // of state
  4626. IStream_Reset(pstmSrc);
  4627. IStream_Reset(pstmDest);
  4628. // if we werent able to get the
  4629. // best size, just use a little stack space :)
  4630. if (pv)
  4631. cbBufReal = CBOPTIMAL;
  4632. else
  4633. {
  4634. pv = buf;
  4635. cbBufReal = sizeof(buf);
  4636. }
  4637. cbBuf = cbBufReal;
  4638. while (SUCCEEDED(pstmSrc->Read(pv, cbBuf, &cbRead)))
  4639. {
  4640. ullCurr += cbBuf;
  4641. if (pdlg)
  4642. {
  4643. if (pdlg->HasUserCancelled())
  4644. {
  4645. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  4646. break;
  4647. }
  4648. // Note: urlmon doesnt always fill in the correct value for cbBuf returned
  4649. // so we need to make sure we dont pass bigger curr than max
  4650. pdlg->SetProgress64(min(ullCurr, ullMax), ullMax);
  4651. }
  4652. // If ullMaxBytes was passed in as non-zero, don't write more bytes than we were told to:
  4653. ULONG ulBytesToWrite = (0 != ullMaxBytes) ?
  4654. (ULONG) min(cbRead, ullMaxBytes - (ullCurr - cbBuf)) :
  4655. cbRead;
  4656. if (!ulBytesToWrite)
  4657. {
  4658. hr = S_OK;
  4659. break;
  4660. }
  4661. hr = IStream_Write(pstmDest, pv, ulBytesToWrite);
  4662. if (S_OK != hr)
  4663. break; // failure!
  4664. cbBuf = cbBufReal;
  4665. }
  4666. if (pv != buf)
  4667. LocalFree(pv);
  4668. }
  4669. return hr;
  4670. }
  4671. STDAPI CopyStream(IStream *pstmSrc, IStream *pstmDest)
  4672. {
  4673. return CopyStreamUI(pstmSrc, pstmDest, NULL, 0);
  4674. }
  4675. STDAPI_(BOOL) IsWindowInProcess(HWND hwnd)
  4676. {
  4677. DWORD idProcess;
  4678. GetWindowThreadProcessId(hwnd, &idProcess);
  4679. return idProcess == GetCurrentProcessId();
  4680. }
  4681. class CFileSysBindData: public IFileSystemBindData
  4682. {
  4683. public:
  4684. CFileSysBindData();
  4685. // *** IUnknown methods ***
  4686. STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  4687. STDMETHODIMP_(ULONG) AddRef(void);
  4688. STDMETHODIMP_(ULONG) Release(void);
  4689. // IFileSystemBindData
  4690. STDMETHODIMP SetFindData(const WIN32_FIND_DATAW *pfd);
  4691. STDMETHODIMP GetFindData(WIN32_FIND_DATAW *pfd);
  4692. private:
  4693. ~CFileSysBindData();
  4694. LONG _cRef;
  4695. WIN32_FIND_DATAW _fd;
  4696. };
  4697. CFileSysBindData::CFileSysBindData() : _cRef(1)
  4698. {
  4699. ZeroMemory(&_fd, sizeof(_fd));
  4700. }
  4701. CFileSysBindData::~CFileSysBindData()
  4702. {
  4703. }
  4704. HRESULT CFileSysBindData::QueryInterface(REFIID riid, void **ppv)
  4705. {
  4706. static const QITAB qit[] = {
  4707. QITABENT(CFileSysBindData, IFileSystemBindData), // IID_IFileSystemBindData
  4708. { 0 },
  4709. };
  4710. return QISearch(this, qit, riid, ppv);
  4711. }
  4712. STDMETHODIMP_(ULONG) CFileSysBindData::AddRef(void)
  4713. {
  4714. return InterlockedIncrement(&_cRef);
  4715. }
  4716. STDMETHODIMP_(ULONG) CFileSysBindData::Release()
  4717. {
  4718. if (InterlockedDecrement(&_cRef))
  4719. return _cRef;
  4720. delete this;
  4721. return 0;
  4722. }
  4723. HRESULT CFileSysBindData::SetFindData(const WIN32_FIND_DATAW *pfd)
  4724. {
  4725. _fd = *pfd;
  4726. return S_OK;
  4727. }
  4728. HRESULT CFileSysBindData::GetFindData(WIN32_FIND_DATAW *pfd)
  4729. {
  4730. *pfd = _fd;
  4731. return S_OK;
  4732. }
  4733. STDAPI SHCreateFileSysBindCtx(const WIN32_FIND_DATA *pfd, IBindCtx **ppbc)
  4734. {
  4735. HRESULT hr;
  4736. IFileSystemBindData *pfsbd = new CFileSysBindData();
  4737. if (pfsbd)
  4738. {
  4739. if (pfd)
  4740. {
  4741. pfsbd->SetFindData(pfd);
  4742. }
  4743. hr = CreateBindCtx(0, ppbc);
  4744. if (SUCCEEDED(hr))
  4745. {
  4746. BIND_OPTS bo = {sizeof(bo)}; // Requires size filled in.
  4747. bo.grfMode = STGM_CREATE;
  4748. (*ppbc)->SetBindOptions(&bo);
  4749. (*ppbc)->RegisterObjectParam(STR_FILE_SYS_BIND_DATA, pfsbd);
  4750. }
  4751. pfsbd->Release();
  4752. }
  4753. else
  4754. {
  4755. *ppbc = NULL;
  4756. hr = E_OUTOFMEMORY;
  4757. }
  4758. return hr;
  4759. }
  4760. STDAPI SHCreateFileSysBindCtxEx(const WIN32_FIND_DATA *pfd, DWORD grfMode, DWORD grfFlags, IBindCtx **ppbc)
  4761. {
  4762. HRESULT hr = SHCreateFileSysBindCtx(pfd, ppbc);
  4763. if (SUCCEEDED(hr))
  4764. {
  4765. BIND_OPTS bo = {sizeof(bo)};
  4766. hr = (*ppbc)->GetBindOptions(&bo);
  4767. if (SUCCEEDED(hr))
  4768. {
  4769. bo.grfMode = grfMode;
  4770. bo.grfFlags = grfFlags;
  4771. hr = (*ppbc)->SetBindOptions(&bo);
  4772. }
  4773. if (FAILED(hr))
  4774. {
  4775. ATOMICRELEASE(*ppbc);
  4776. }
  4777. }
  4778. return hr;
  4779. }
  4780. // returns S_OK if this is a simple bind ctx
  4781. // out:
  4782. // optional (may be NULL) pfd
  4783. //
  4784. STDAPI SHIsFileSysBindCtx(IBindCtx *pbc, WIN32_FIND_DATA **ppfd)
  4785. {
  4786. HRESULT hr = S_FALSE; // default to no
  4787. IUnknown *punk;
  4788. if (pbc && SUCCEEDED(pbc->GetObjectParam(STR_FILE_SYS_BIND_DATA, &punk)))
  4789. {
  4790. IFileSystemBindData *pfsbd;
  4791. if (SUCCEEDED(punk->QueryInterface(IID_PPV_ARG(IFileSystemBindData, &pfsbd))))
  4792. {
  4793. hr = S_OK; // yes
  4794. if (ppfd)
  4795. {
  4796. hr = SHLocalAlloc(sizeof(WIN32_FIND_DATA), ppfd);
  4797. if (SUCCEEDED(hr))
  4798. pfsbd->GetFindData(*ppfd);
  4799. }
  4800. pfsbd->Release();
  4801. }
  4802. punk->Release();
  4803. }
  4804. return hr;
  4805. }
  4806. STDAPI SHCreateSkipBindCtx(IUnknown *punkToSkip, IBindCtx **ppbc)
  4807. {
  4808. HRESULT hr = CreateBindCtx(0, ppbc);
  4809. if (SUCCEEDED(hr))
  4810. {
  4811. // NULL clsid means bind context that skips all junction points
  4812. if (punkToSkip)
  4813. {
  4814. (*ppbc)->RegisterObjectParam(STR_SKIP_BINDING_CLSID, punkToSkip);
  4815. }
  4816. else
  4817. {
  4818. BIND_OPTS bo = {sizeof(bo)}; // Requires size filled in.
  4819. bo.grfFlags = BIND_JUSTTESTEXISTENCE;
  4820. (*ppbc)->SetBindOptions(&bo);
  4821. }
  4822. }
  4823. return hr;
  4824. }
  4825. // We've overloaded the meaning of the BIND_OPTS flag
  4826. // BIND_JUSTTESTEXISTENCE to mean don't bind to junctions
  4827. // when evaluating paths...so check it here...
  4828. //
  4829. // pbc optional bind context (can be NULL)
  4830. // pclsidSkip optional CLSID to test. if null we test for skiping all
  4831. // junction binding, not just on this specific CLSID
  4832. //
  4833. STDAPI_(BOOL) SHSkipJunctionBinding(IBindCtx *pbc, const CLSID *pclsidSkip)
  4834. {
  4835. if (pbc)
  4836. {
  4837. BIND_OPTS bo = {sizeof(BIND_OPTS), 0}; // Requires size filled in.
  4838. return (SUCCEEDED(pbc->GetBindOptions(&bo)) &&
  4839. bo.grfFlags == BIND_JUSTTESTEXISTENCE) ||
  4840. (pclsidSkip && SHSkipJunction(pbc, pclsidSkip)); // should we skip this specific CLSID?
  4841. }
  4842. return FALSE; // do junction binding, no context provided
  4843. }
  4844. // Bind file system object to storage.
  4845. // in:
  4846. // dwFileAttributes optional (-1)
  4847. //
  4848. STDAPI SHFileSysBindToStorage(LPCWSTR pszPath, DWORD dwFileAttributes, DWORD grfMode, DWORD grfFlags,
  4849. REFIID riid, void **ppv)
  4850. {
  4851. if (-1 == dwFileAttributes)
  4852. {
  4853. TCHAR szPath[MAX_PATH];
  4854. SHUnicodeToTChar(pszPath, szPath, ARRAYSIZE(szPath));
  4855. dwFileAttributes = GetFileAttributes(szPath);
  4856. if (-1 == dwFileAttributes)
  4857. return STG_E_FILENOTFOUND;
  4858. }
  4859. WIN32_FIND_DATA wfd = {0};
  4860. wfd.dwFileAttributes = dwFileAttributes;
  4861. IBindCtx *pbc;
  4862. HRESULT hr = SHCreateFileSysBindCtxEx(&wfd, grfMode, grfFlags, &pbc);
  4863. if (SUCCEEDED(hr))
  4864. {
  4865. IShellFolder *psfDesktop;
  4866. hr = SHGetDesktopFolder(&psfDesktop);
  4867. if (SUCCEEDED(hr))
  4868. {
  4869. LPITEMIDLIST pidl;
  4870. hr = psfDesktop->ParseDisplayName(NULL, pbc, (LPWSTR)pszPath, NULL, &pidl, NULL);
  4871. if (SUCCEEDED(hr))
  4872. {
  4873. IShellFolder *psf;
  4874. LPCITEMIDLIST pidlLast;
  4875. hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder, &psf), &pidlLast);
  4876. if (SUCCEEDED(hr))
  4877. {
  4878. hr = psf->BindToStorage(pidlLast, pbc, riid, ppv);
  4879. psf->Release();
  4880. }
  4881. ILFree(pidl);
  4882. }
  4883. psfDesktop->Release();
  4884. }
  4885. pbc->Release();
  4886. }
  4887. return hr;
  4888. }
  4889. // return a relative IDList to pszFolder given the find data for that item
  4890. STDAPI SHCreateFSIDList(LPCTSTR pszFolder, const WIN32_FIND_DATA *pfd, LPITEMIDLIST *ppidl)
  4891. {
  4892. HRESULT hr;
  4893. TCHAR szPath[MAX_PATH];
  4894. LPITEMIDLIST pidl;
  4895. *ppidl = NULL;
  4896. if (PathCombine(szPath, pszFolder, pfd->cFileName))
  4897. {
  4898. hr = SHSimpleIDListFromFindData(szPath, pfd, &pidl);
  4899. if (SUCCEEDED(hr))
  4900. {
  4901. hr = SHILClone(ILFindLastID(pidl), ppidl);
  4902. ILFree(pidl);
  4903. }
  4904. }
  4905. else
  4906. {
  4907. hr = E_FAIL;
  4908. }
  4909. return hr;
  4910. }
  4911. STDAPI SHSimulateDropWithSite(IDropTarget *pdrop, IDataObject *pdtobj, DWORD grfKeyState,
  4912. const POINTL *ppt, DWORD *pdwEffect, IUnknown *punkSite)
  4913. {
  4914. if (punkSite)
  4915. IUnknown_SetSite(pdrop, punkSite);
  4916. HRESULT hr = SHSimulateDrop(pdrop, pdtobj, grfKeyState, ppt, pdwEffect);
  4917. if (punkSite)
  4918. IUnknown_SetSite(pdrop, NULL);
  4919. return hr;
  4920. }
  4921. STDAPI SimulateDropWithPasteSucceeded(IDropTarget * pdrop, IDataObject * pdtobj,
  4922. DWORD grfKeyState, const POINTL *ppt, DWORD dwEffect,
  4923. IUnknown * punkSite, BOOL fClearClipboard)
  4924. {
  4925. // simulate the drag drop protocol
  4926. HRESULT hr = SHSimulateDropWithSite(pdrop, pdtobj, grfKeyState, ppt, &dwEffect, punkSite);
  4927. if (SUCCEEDED(hr))
  4928. {
  4929. // these formats are put into the data object by the drop target code. this
  4930. // requires the data object support ::SetData() for arbitrary data formats
  4931. //
  4932. // g_cfPerformedDropEffect effect is the reliable version of dwEffect (some targets
  4933. // return dwEffect == DROPEFFECT_MOVE always)
  4934. //
  4935. // g_cfLogicalPerformedDropEffect indicates the logical action so we can tell the
  4936. // difference between optmized and non optimized move
  4937. DWORD dwPerformedEffect = DataObj_GetDWORD(pdtobj, g_cfPerformedDropEffect, DROPEFFECT_NONE);
  4938. DWORD dwLogicalPerformedEffect = DataObj_GetDWORD(pdtobj, g_cfLogicalPerformedDropEffect, DROPEFFECT_NONE);
  4939. if ((DROPEFFECT_MOVE == dwLogicalPerformedEffect) ||
  4940. (DROPEFFECT_MOVE == dwEffect && DROPEFFECT_MOVE == dwPerformedEffect))
  4941. {
  4942. // communicate back the source data object
  4943. // so they can complete the "move" if necessary
  4944. DataObj_SetDWORD(pdtobj, g_cfPasteSucceeded, dwEffect);
  4945. // if we just did a paste and we moved the files we cant paste
  4946. // them again (because they moved!) so empty the clipboard
  4947. if (fClearClipboard)
  4948. {
  4949. OleSetClipboard(NULL);
  4950. }
  4951. }
  4952. }
  4953. return hr;
  4954. }
  4955. STDAPI TransferDelete(HWND hwnd, HDROP hDrop, UINT fOptions)
  4956. {
  4957. HRESULT hr = E_OUTOFMEMORY;
  4958. DRAGINFO di = { sizeof(DRAGINFO), 0 };
  4959. if (DragQueryInfo(hDrop, &di)) // di.lpFileList will be in TCHAR format -- see DragQueryInfo impl
  4960. {
  4961. FILEOP_FLAGS fFileop;
  4962. if (fOptions & SD_SILENT)
  4963. {
  4964. fFileop = FOF_SILENT | FOF_NOCONFIRMATION | FOF_NOERRORUI | FOF_ALLOWUNDO;
  4965. }
  4966. else
  4967. {
  4968. fFileop = ((fOptions & SD_NOUNDO) || (GetAsyncKeyState(VK_SHIFT) < 0)) ? 0 : FOF_ALLOWUNDO;
  4969. if (fOptions & SD_WARNONNUKE)
  4970. {
  4971. // we pass this so the user is warned that they will loose
  4972. // data during a move-to-recycle bin operation
  4973. fFileop |= FOF_WANTNUKEWARNING;
  4974. }
  4975. if (!(fOptions & SD_USERCONFIRMATION))
  4976. fFileop |= FOF_NOCONFIRMATION;
  4977. }
  4978. SHFILEOPSTRUCT fo = {
  4979. hwnd,
  4980. FO_DELETE,
  4981. di.lpFileList,
  4982. NULL,
  4983. fFileop,
  4984. };
  4985. int iErr = SHFileOperation(&fo);
  4986. if ((0 == iErr) && fo.fAnyOperationsAborted)
  4987. {
  4988. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // user canceled (at least something was canceled)
  4989. }
  4990. else
  4991. {
  4992. hr = HRESULT_FROM_WIN32(iErr);
  4993. }
  4994. SHFree(di.lpFileList);
  4995. }
  4996. return hr;
  4997. }
  4998. STDAPI DeleteFilesInDataObject(HWND hwnd, UINT uFlags, IDataObject *pdtobj, UINT fOptions)
  4999. {
  5000. STGMEDIUM medium;
  5001. FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  5002. HRESULT hr = pdtobj->GetData(&fmte, &medium);
  5003. if (SUCCEEDED(hr))
  5004. {
  5005. fOptions |= (uFlags & CMIC_MASK_FLAG_NO_UI) ? SD_SILENT : SD_USERCONFIRMATION;
  5006. if ((uFlags & CMIC_MASK_SHIFT_DOWN) || (GetKeyState(VK_SHIFT) < 0))
  5007. {
  5008. fOptions |= SD_NOUNDO;
  5009. }
  5010. hr = TransferDelete(hwnd, (HDROP)medium.hGlobal, fOptions);
  5011. ReleaseStgMedium(&medium);
  5012. SHChangeNotifyHandleEvents();
  5013. }
  5014. return hr;
  5015. }
  5016. STDAPI GetItemCLSID(IShellFolder2 *psf, LPCITEMIDLIST pidlLast, CLSID *pclsid)
  5017. {
  5018. VARIANT var;
  5019. HRESULT hr = psf->GetDetailsEx(pidlLast, &SCID_DESCRIPTIONID, &var);
  5020. if (SUCCEEDED(hr))
  5021. {
  5022. SHDESCRIPTIONID did;
  5023. hr = VariantToBuffer(&var, (void *)&did, sizeof(did));
  5024. if (SUCCEEDED(hr))
  5025. {
  5026. *pclsid = did.clsid;
  5027. }
  5028. VariantClear(&var);
  5029. }
  5030. return hr;
  5031. }
  5032. // in:
  5033. // pidl fully qualified IDList to test. we will bind to the
  5034. // parent of this and ask it for the CLSID
  5035. // out:
  5036. // pclsid return the CLSID of the item
  5037. STDAPI GetCLSIDFromIDList(LPCITEMIDLIST pidl, CLSID *pclsid)
  5038. {
  5039. IShellFolder2 *psf;
  5040. LPCITEMIDLIST pidlLast;
  5041. HRESULT hr = SHBindToIDListParent(pidl, IID_PPV_ARG(IShellFolder2, &psf), &pidlLast);
  5042. if (SUCCEEDED(hr))
  5043. {
  5044. hr = GetItemCLSID(psf, pidlLast, pclsid);
  5045. psf->Release();
  5046. }
  5047. return hr;
  5048. }
  5049. STDAPI _IsIDListInNameSpace(LPCITEMIDLIST pidl, const CLSID *pclsid)
  5050. {
  5051. HRESULT hr;
  5052. LPITEMIDLIST pidlFirst = ILCloneFirst(pidl);
  5053. if (pidlFirst)
  5054. {
  5055. CLSID clsid;
  5056. hr = GetCLSIDFromIDList(pidlFirst, &clsid);
  5057. if (SUCCEEDED(hr))
  5058. {
  5059. hr = (IsEqualCLSID(clsid, *pclsid) ? S_OK : S_FALSE);
  5060. }
  5061. ILFree(pidlFirst);
  5062. }
  5063. else
  5064. {
  5065. hr = E_OUTOFMEMORY;
  5066. }
  5067. return hr;
  5068. }
  5069. #ifdef DEBUG
  5070. // We do not want to use IsIDListInNameSpace in ASSERTs since this fct can fail.
  5071. // Instead, if we fail, we return TRUE. This will mainly happen because of low memory
  5072. // conditions.
  5073. STDAPI_(BOOL) AssertIsIDListInNameSpace(LPCITEMIDLIST pidl, const CLSID *pclsid)
  5074. {
  5075. // We return FALSE only if we succeeded and determined that the pidl is NOT in
  5076. // the namespace.
  5077. return (S_FALSE != _IsIDListInNameSpace(pidl, pclsid));
  5078. }
  5079. #endif
  5080. // test to see if this IDList is in the net hood name space scoped by clsid
  5081. // for example pass CLSID_NetworkPlaces or CLSID_MyComputer
  5082. STDAPI_(BOOL) IsIDListInNameSpace(LPCITEMIDLIST pidl, const CLSID *pclsid)
  5083. {
  5084. return (S_OK == _IsIDListInNameSpace(pidl, pclsid));
  5085. }
  5086. #define FILE_ATTRIBUTE_SH (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)
  5087. struct {
  5088. LPCTSTR pszFile;
  5089. BOOL bDeleteIfEmpty;
  5090. DWORD dwAttributes;
  5091. } const c_aFilesToFix[] = {
  5092. // autoexec.bat and config.sys are not in this list because hiding them
  5093. // breaks some 16 bit apps (yes, lame). but we deal with hiding those
  5094. // files in the file system enumerator (it special cases such files)
  5095. { TEXT("X:\\autoexec.000"), TRUE, FILE_ATTRIBUTE_SH },
  5096. { TEXT("X:\\autoexec.old"), TRUE, FILE_ATTRIBUTE_SH },
  5097. { TEXT("X:\\autoexec.bak"), TRUE, FILE_ATTRIBUTE_SH },
  5098. { TEXT("X:\\autoexec.dos"), TRUE, FILE_ATTRIBUTE_SH },
  5099. { TEXT("X:\\autoexec.win"), TRUE, FILE_ATTRIBUTE_SH },
  5100. { TEXT("X:\\config.dos"), TRUE, FILE_ATTRIBUTE_SH },
  5101. { TEXT("X:\\config.win"), TRUE, FILE_ATTRIBUTE_SH },
  5102. { TEXT("X:\\command.com"), FALSE, FILE_ATTRIBUTE_SH },
  5103. { TEXT("X:\\command.dos"), FALSE, FILE_ATTRIBUTE_SH },
  5104. { TEXT("X:\\logo.sys"), FALSE, FILE_ATTRIBUTE_SH },
  5105. { TEXT("X:\\msdos.---"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x backup of msdos.*
  5106. { TEXT("X:\\boot.ini"), FALSE, FILE_ATTRIBUTE_SH },
  5107. { TEXT("X:\\boot.bak"), FALSE, FILE_ATTRIBUTE_SH },
  5108. { TEXT("X:\\boot.---"), FALSE, FILE_ATTRIBUTE_SH },
  5109. { TEXT("X:\\bootsect.dos"), FALSE, FILE_ATTRIBUTE_SH },
  5110. { TEXT("X:\\bootlog.txt"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x first boot log
  5111. { TEXT("X:\\bootlog.prv"), FALSE, FILE_ATTRIBUTE_SH },
  5112. { TEXT("X:\\ffastun.ffa"), FALSE, FILE_ATTRIBUTE_SH }, // Office 97 only used hidden, O2K uses SH
  5113. { TEXT("X:\\ffastun.ffl"), FALSE, FILE_ATTRIBUTE_SH },
  5114. { TEXT("X:\\ffastun.ffx"), FALSE, FILE_ATTRIBUTE_SH },
  5115. { TEXT("X:\\ffastun0.ffx"), FALSE, FILE_ATTRIBUTE_SH },
  5116. { TEXT("X:\\ffstunt.ffl"), FALSE, FILE_ATTRIBUTE_SH },
  5117. { TEXT("X:\\sms.ini"), FALSE, FILE_ATTRIBUTE_SH }, // SMS
  5118. { TEXT("X:\\sms.new"), FALSE, FILE_ATTRIBUTE_SH },
  5119. { TEXT("X:\\sms_time.dat"), FALSE, FILE_ATTRIBUTE_SH },
  5120. { TEXT("X:\\smsdel.dat"), FALSE, FILE_ATTRIBUTE_SH },
  5121. { TEXT("X:\\mpcsetup.log"), FALSE, FILE_ATTRIBUTE_HIDDEN },// Microsoft Proxy Server
  5122. { TEXT("X:\\detlog.txt"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x PNP detection log
  5123. { TEXT("X:\\detlog.old"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x PNP detection log
  5124. { TEXT("X:\\setuplog.txt"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x setup log
  5125. { TEXT("X:\\setuplog.old"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x setup log
  5126. { TEXT("X:\\suhdlog.dat"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x setup log
  5127. { TEXT("X:\\suhdlog.---"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x setup log
  5128. { TEXT("X:\\suhdlog.bak"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x setup log
  5129. { TEXT("X:\\system.1st"), FALSE, FILE_ATTRIBUTE_SH }, // Win95 system.dat backup
  5130. { TEXT("X:\\netlog.txt"), FALSE, FILE_ATTRIBUTE_SH }, // Win9x network setup log file
  5131. { TEXT("X:\\setup.aif"), FALSE, FILE_ATTRIBUTE_SH }, // NT4 unattended setup script
  5132. { TEXT("X:\\catlog.wci"), FALSE, FILE_ATTRIBUTE_HIDDEN },// index server folder
  5133. { TEXT("X:\\cmsstorage.lst"), FALSE, FILE_ATTRIBUTE_SH }, // Microsoft Media Manager
  5134. };
  5135. void PathSetSystemDrive(TCHAR *pszPath)
  5136. {
  5137. TCHAR szWin[MAX_PATH];
  5138. if (GetWindowsDirectory(szWin, ARRAYSIZE(szWin)))
  5139. *pszPath = szWin[0];
  5140. else
  5141. *pszPath = TEXT('C'); // try this in failure
  5142. }
  5143. void PrettyPath(LPCTSTR pszPath)
  5144. {
  5145. TCHAR szPath[MAX_PATH];
  5146. lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath));
  5147. PathSetSystemDrive(szPath);
  5148. PathMakePretty(PathFindFileName(szPath)); // fix up the file spec part
  5149. MoveFile(pszPath, szPath); // rename to the good name.
  5150. }
  5151. void DeleteEmptyFile(LPCTSTR pszPath)
  5152. {
  5153. WIN32_FIND_DATA fd;
  5154. HANDLE hfind = FindFirstFile(pszPath, &fd);
  5155. if (hfind != INVALID_HANDLE_VALUE)
  5156. {
  5157. FindClose(hfind);
  5158. if (!(fd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
  5159. (fd.nFileSizeHigh == 0) && (fd.nFileSizeLow == 0))
  5160. DeleteFile(pszPath);
  5161. }
  5162. }
  5163. STDAPI_(void) CleanupFileSystem()
  5164. {
  5165. // try to fix up other variations of the windows folders that may
  5166. // not be the current windir. this is to make dual boots and old installs
  5167. // of windows have nicely cases file names when displayed in the explorer
  5168. PrettyPath(TEXT("X:\\WINDOWS"));
  5169. PrettyPath(TEXT("X:\\WINNT"));
  5170. for (int i = 0; i < ARRAYSIZE(c_aFilesToFix); i++)
  5171. {
  5172. TCHAR szPath[MAX_PATH];
  5173. lstrcpyn(szPath, c_aFilesToFix[i].pszFile, ARRAYSIZE(szPath));
  5174. PathSetSystemDrive(szPath);
  5175. if (c_aFilesToFix[i].bDeleteIfEmpty)
  5176. DeleteEmptyFile(szPath);
  5177. SetFileAttributes(szPath, c_aFilesToFix[i].dwAttributes);
  5178. }
  5179. }
  5180. // Always frees pidlToPrepend and *ppidl on failure
  5181. STDAPI SHILPrepend(LPITEMIDLIST pidlToPrepend, LPITEMIDLIST *ppidl)
  5182. {
  5183. HRESULT hr;
  5184. if (!*ppidl)
  5185. {
  5186. *ppidl = pidlToPrepend;
  5187. hr = S_OK;
  5188. }
  5189. else
  5190. {
  5191. LPITEMIDLIST pidlSave = *ppidl; // append to the list
  5192. hr = SHILCombine(pidlToPrepend, pidlSave, ppidl);
  5193. ILFree(pidlSave);
  5194. ILFree(pidlToPrepend);
  5195. }
  5196. return hr;
  5197. }
  5198. // in:
  5199. // pidlToAppend this item is appended to *ppidl and freed
  5200. //
  5201. // in/out:
  5202. // *ppidl idlist to append to, if empty gets pidlToAppend
  5203. //
  5204. STDAPI SHILAppend(LPITEMIDLIST pidlToAppend, LPITEMIDLIST *ppidl)
  5205. {
  5206. HRESULT hr;
  5207. if (!*ppidl)
  5208. {
  5209. *ppidl = pidlToAppend;
  5210. hr = S_OK;
  5211. }
  5212. else
  5213. {
  5214. LPITEMIDLIST pidlSave = *ppidl; // append to the list
  5215. hr = SHILCombine(pidlSave, pidlToAppend, ppidl);
  5216. ILFree(pidlSave);
  5217. ILFree(pidlToAppend);
  5218. }
  5219. return hr;
  5220. }
  5221. STDAPI SHSimpleIDListFromFindData(LPCTSTR pszPath, const WIN32_FIND_DATA *pfd, LPITEMIDLIST *ppidl)
  5222. {
  5223. HRESULT hr;
  5224. // Office 2000 does a SHChangeNotify(SHCNE_DELETE, ""), and
  5225. // ParseDisplayName("") returns CSIDL_DRIVES for app compat,
  5226. // so we must catch that case here and prevent Office from
  5227. // delete your My Computer icon (!)
  5228. if (pszPath[0])
  5229. {
  5230. IShellFolder *psfDesktop;
  5231. hr = SHGetDesktopFolder(&psfDesktop);
  5232. if (SUCCEEDED(hr))
  5233. {
  5234. IBindCtx *pbc;
  5235. hr = SHCreateFileSysBindCtx(pfd, &pbc);
  5236. if (SUCCEEDED(hr))
  5237. {
  5238. WCHAR wszPath[MAX_PATH];
  5239. // Must use a private buffer because ParseDisplayName takes a non-const pointer
  5240. SHTCharToUnicode(pszPath, wszPath, ARRAYSIZE(wszPath));
  5241. hr = psfDesktop->ParseDisplayName(NULL, pbc, wszPath, NULL, ppidl, NULL);
  5242. pbc->Release();
  5243. }
  5244. psfDesktop->Release();
  5245. }
  5246. }
  5247. else
  5248. hr = E_INVALIDARG;
  5249. if (FAILED(hr))
  5250. *ppidl = NULL;
  5251. return hr;
  5252. }
  5253. STDAPI SHSimpleIDListFromFindData2(IShellFolder *psf, const WIN32_FIND_DATA *pfd, LPITEMIDLIST *ppidl)
  5254. {
  5255. *ppidl = NULL; // assume failure
  5256. IBindCtx *pbc;
  5257. HRESULT hr = SHCreateFileSysBindCtx(pfd, &pbc);
  5258. if (SUCCEEDED(hr))
  5259. {
  5260. WCHAR wszPath[MAX_PATH];
  5261. // Must use a private buffer because ParseDisplayName takes a non-const pointer
  5262. SHTCharToUnicode(pfd->cFileName, wszPath, ARRAYSIZE(wszPath));
  5263. hr = psf->ParseDisplayName(NULL, pbc, wszPath, NULL, ppidl, NULL);
  5264. pbc->Release();
  5265. }
  5266. return hr;
  5267. }
  5268. STDAPI_(LPITEMIDLIST) SHSimpleIDListFromPath(LPCTSTR pszPath)
  5269. {
  5270. LPITEMIDLIST pidl;
  5271. HRESULT hr = SHSimpleIDListFromFindData(pszPath, NULL, &pidl);
  5272. ASSERT(SUCCEEDED(hr) ? pidl != NULL : pidl == NULL);
  5273. return pidl;
  5274. }
  5275. // convert a full file system IDList into one relative to the a special folder
  5276. // For example,
  5277. // pidlFS == [my computer] [c:] [windows] [desktop] [dir] [foo.txt]
  5278. // returns:
  5279. // [dir] [foo.txt]
  5280. //
  5281. // returns NULL if no translation was performed
  5282. STDAPI_(LPITEMIDLIST) SHLogILFromFSIL(LPCITEMIDLIST pidlFS)
  5283. {
  5284. LPITEMIDLIST pidlOut;
  5285. SHILAliasTranslate(pidlFS, &pidlOut, XLATEALIAS_ALL); // will set pidlOut=NULL on failure
  5286. return pidlOut;
  5287. }
  5288. //
  5289. // Returns:
  5290. // The resource index (of SHELL232.DLL) of the appropriate icon.
  5291. //
  5292. STDAPI_(UINT) SILGetIconIndex(LPCITEMIDLIST pidl, const ICONMAP aicmp[], UINT cmax)
  5293. {
  5294. UINT uType = (pidl->mkid.abID[0] & SHID_TYPEMASK);
  5295. for (UINT i = 0; i < cmax; i++)
  5296. {
  5297. if (aicmp[i].uType == uType)
  5298. {
  5299. return aicmp[i].indexResource;
  5300. }
  5301. }
  5302. return II_DOCUMENT; // default
  5303. }
  5304. BOOL IsSelf(UINT cidl, LPCITEMIDLIST *apidl)
  5305. {
  5306. return cidl == 0 || (cidl == 1 && (apidl == NULL || apidl[0] == NULL || ILIsEmpty(apidl[0])));
  5307. }
  5308. //
  5309. // GetIconLocationFromExt
  5310. //
  5311. // Given "txt" or ".txt" return "C:\WINNT\System32\Notepad.exe" and the index into this file for the icon.
  5312. //
  5313. // if pszIconPath/cchIconPath is too small, this api will fail
  5314. //
  5315. STDAPI GetIconLocationFromExt(IN LPTSTR pszExt, OUT LPTSTR pszIconPath, UINT cchIconPath, OUT LPINT piIconIndex)
  5316. {
  5317. IAssocStore* pas;
  5318. IAssocInfo* pai;
  5319. HRESULT hr;
  5320. RIPMSG(pszIconPath && IS_VALID_STRING_PTR(pszIconPath, cchIconPath), "GetIconLocationFromExt: caller passed bad pszIconPath");
  5321. if (!pszExt || !pszExt[0] || !pszIconPath)
  5322. return E_INVALIDARG;
  5323. pszIconPath[0] = 0;
  5324. pas = new CFTAssocStore();
  5325. if (pas)
  5326. {
  5327. hr = pas->GetAssocInfo(pszExt, AIINIT_EXT, &pai);
  5328. if (SUCCEEDED(hr))
  5329. {
  5330. DWORD cchSize = cchIconPath;
  5331. hr = pai->GetString(AISTR_ICONLOCATION, pszIconPath, &cchSize);
  5332. if (SUCCEEDED(hr))
  5333. {
  5334. *piIconIndex = PathParseIconLocation(pszIconPath);
  5335. }
  5336. pai->Release();
  5337. }
  5338. delete pas;
  5339. }
  5340. else
  5341. hr = E_OUTOFMEMORY;
  5342. return hr;
  5343. }
  5344. STDAPI SHFindFirstFile(LPCTSTR pszPath, WIN32_FIND_DATA *pfd, HANDLE *phfind)
  5345. {
  5346. HRESULT hr;
  5347. // instead of the ...erm HACK in the reserved word (win95), use the super new (NT only) FindFileEx instead
  5348. // this is not guaranteed to filter, it is a "hint" according to the manual
  5349. FINDEX_SEARCH_OPS eOps = FindExSearchNameMatch;
  5350. if (pfd->dwReserved0 == 0x56504347)
  5351. {
  5352. eOps = FindExSearchLimitToDirectories;
  5353. pfd->dwReserved0 = 0;
  5354. }
  5355. *phfind = FindFirstFileEx(pszPath, FindExInfoStandard, pfd, eOps, NULL, 0);
  5356. if (*phfind == INVALID_HANDLE_VALUE)
  5357. {
  5358. DWORD err = GetLastError();
  5359. if ((err == ERROR_NO_MORE_FILES || // what dos returns
  5360. err == ERROR_FILE_NOT_FOUND) && // win32 return for dir searches
  5361. PathIsWild(pszPath))
  5362. {
  5363. // convert search to empty success (probalby a root)
  5364. hr = S_FALSE;
  5365. }
  5366. else
  5367. {
  5368. hr = HRESULT_FROM_WIN32(err);
  5369. }
  5370. }
  5371. else
  5372. {
  5373. hr = S_OK;
  5374. }
  5375. return hr;
  5376. }
  5377. void _GoModal(HWND hwnd, IUnknown *punkModless, BOOL fModal)
  5378. {
  5379. if (hwnd)
  5380. {
  5381. IUnknown_EnableModless(punkModless, !fModal);
  5382. }
  5383. }
  5384. // in:
  5385. // hwnd NULL means no UI
  5386. // pfFromWNet The caller will pass this to us with it set to TRUE because they will assume
  5387. // that any error value returned from this function will come from a WNet API.
  5388. // We need to change this value to FALSE if it doesn't come from WNet so
  5389. // the string version of the error number is not generated from WNet.
  5390. HRESULT _RetryNetwork(HWND hwnd, IUnknown *punkModless, LPCTSTR pszPath, IN BOOL * pfFromWNet, WIN32_FIND_DATA *pfd, HANDLE *phfind)
  5391. {
  5392. HRESULT hr;
  5393. TCHAR szT[MAX_PATH];
  5394. DWORD err;
  5395. AssertMsg((TRUE == *pfFromWNet), TEXT("We assume that *pfFromWNet comes in TRUE. Someone changed that behavior. -BryanSt"));
  5396. if (PathIsUNC(pszPath))
  5397. {
  5398. NETRESOURCE rc = { 0, RESOURCETYPE_ANY, 0, 0, NULL, szT, NULL, NULL} ;
  5399. lstrcpy(szT, pszPath);
  5400. PathStripToRoot(szT);
  5401. _GoModal(hwnd, punkModless, TRUE);
  5402. err = WNetAddConnection3(hwnd, &rc, NULL, NULL, (hwnd ? (CONNECT_TEMPORARY | CONNECT_INTERACTIVE) : CONNECT_TEMPORARY));
  5403. if (WN_SUCCESS == err)
  5404. {
  5405. *pfFromWNet = FALSE;
  5406. }
  5407. _GoModal(hwnd, punkModless, FALSE);
  5408. }
  5409. else
  5410. {
  5411. TCHAR szDrive[4];
  5412. szDrive[0] = pszPath[0];
  5413. szDrive[1] = TEXT(':');
  5414. szDrive[2] = 0;
  5415. _GoModal(hwnd, punkModless, TRUE);
  5416. DWORD dwFlags = 0;
  5417. if (hwnd == NULL)
  5418. {
  5419. dwFlags |= WNRC_NOUI;
  5420. }
  5421. err = WNetRestoreConnection2(hwnd, szDrive, dwFlags, NULL);
  5422. _GoModal(hwnd, punkModless, FALSE);
  5423. if (err == WN_SUCCESS)
  5424. {
  5425. *pfFromWNet = FALSE;
  5426. // refresh drive info... generate change notify
  5427. szDrive[2] = TEXT('\\');
  5428. szDrive[3] = 0;
  5429. CMountPoint::NotifyReconnectedNetDrive(szDrive);
  5430. SHChangeNotify(SHCNE_DRIVEADD, SHCNF_PATH, szDrive, NULL);
  5431. }
  5432. else if (err != ERROR_OUTOFMEMORY)
  5433. {
  5434. err = WN_CANCEL; // user cancel (they saw UI) == ERROR_CANCELLED
  5435. }
  5436. }
  5437. if (err == WN_SUCCESS)
  5438. hr = SHFindFirstFile(pszPath, pfd, phfind);
  5439. else
  5440. hr = HRESULT_FROM_WIN32(err);
  5441. return hr;
  5442. }
  5443. typedef struct {
  5444. HWND hDlg;
  5445. LPCTSTR pszPath;
  5446. WIN32_FIND_DATA *pfd;
  5447. HANDLE *phfind;
  5448. HRESULT hr;
  5449. } RETRY_DATA;
  5450. STDAPI_(UINT) QueryCancelAutoPlayMsg()
  5451. {
  5452. static UINT s_msgQueryCancelAutoPlay = 0;
  5453. if (0 == s_msgQueryCancelAutoPlay)
  5454. s_msgQueryCancelAutoPlay = RegisterWindowMessage(TEXT("QueryCancelAutoPlay"));
  5455. return s_msgQueryCancelAutoPlay;
  5456. }
  5457. BOOL IsQueryCancelAutoPlay(UINT uMsg)
  5458. {
  5459. return uMsg == QueryCancelAutoPlayMsg();
  5460. }
  5461. #define IDT_RETRY 1
  5462. BOOL _IsUnformatedMediaResult(HRESULT hr)
  5463. {
  5464. return hr == HRESULT_FROM_WIN32(ERROR_GEN_FAILURE) || // Win9x
  5465. hr == HRESULT_FROM_WIN32(ERROR_UNRECOGNIZED_MEDIA) || // NT4
  5466. hr == HRESULT_FROM_WIN32(ERROR_NOT_DOS_DISK) || // Could happen, I think.
  5467. hr == HRESULT_FROM_WIN32(ERROR_SECTOR_NOT_FOUND) || // Happened on Magnatized disk
  5468. hr == HRESULT_FROM_WIN32(ERROR_CRC) || // Happened on Magnatized disk
  5469. hr == HRESULT_FROM_WIN32(ERROR_UNRECOGNIZED_VOLUME); // NT5
  5470. }
  5471. STDAPI_(BOOL) PathRetryRemovable(HRESULT hr, LPCTSTR pszPath)
  5472. {
  5473. return (hr == HRESULT_FROM_WIN32(ERROR_NOT_READY) || // normal case
  5474. hr == HRESULT_FROM_WIN32(ERROR_INVALID_PARAMETER)) && // SCSI ZIP drive does this
  5475. PathIsRemovable(pszPath);
  5476. }
  5477. BOOL_PTR CALLBACK _OnTimeCheckDiskForInsert(HWND hDlg, RETRY_DATA *prd)
  5478. {
  5479. BOOL_PTR fReturnValue = 0;
  5480. prd->hr = SHFindFirstFile(prd->pszPath, prd->pfd, prd->phfind);
  5481. // we are good or they inserted unformatted disk
  5482. if (SUCCEEDED(prd->hr) || _IsUnformatedMediaResult(prd->hr))
  5483. {
  5484. EndDialog(hDlg, IDRETRY);
  5485. }
  5486. return fReturnValue;
  5487. }
  5488. BOOL_PTR CALLBACK RetryDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  5489. {
  5490. RETRY_DATA *prd = (RETRY_DATA *)GetWindowLongPtr(hDlg, DWLP_USER);
  5491. switch (uMsg)
  5492. {
  5493. case WM_INITDIALOG:
  5494. SetWindowLongPtr(hDlg, DWLP_USER, lParam);
  5495. prd = (RETRY_DATA *)lParam;
  5496. prd->hDlg = hDlg;
  5497. {
  5498. TCHAR szFormat[128], szText[MAX_PATH];
  5499. GetDlgItemText(hDlg, IDD_TEXT, szFormat, ARRAYSIZE(szFormat));
  5500. wnsprintf(szText, ARRAYSIZE(szText), szFormat, prd->pszPath[0]);
  5501. SetDlgItemText(hDlg, IDD_TEXT, szText);
  5502. lstrcpyn(szText, prd->pszPath, ARRAYSIZE(szText));
  5503. PathStripToRoot(szText);
  5504. // get info about the file.
  5505. SHFILEINFO sfi = {0};
  5506. SHGetFileInfo(szText, FILE_ATTRIBUTE_DIRECTORY, &sfi, sizeof(sfi),
  5507. SHGFI_USEFILEATTRIBUTES |
  5508. SHGFI_ICON | SHGFI_LARGEICON | SHGFI_ADDOVERLAYS);
  5509. ReplaceDlgIcon(prd->hDlg, IDD_ICON, sfi.hIcon);
  5510. }
  5511. SetTimer(prd->hDlg, IDT_RETRY, 2000, NULL);
  5512. break;
  5513. case WM_DESTROY:
  5514. ReplaceDlgIcon(prd->hDlg, IDD_ICON, NULL);
  5515. break;
  5516. case WM_TIMER:
  5517. _OnTimeCheckDiskForInsert(hDlg, prd);
  5518. break;
  5519. case WM_COMMAND:
  5520. switch (GET_WM_COMMAND_ID(wParam, lParam))
  5521. {
  5522. case IDCANCEL:
  5523. prd->hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  5524. EndDialog(hDlg, GET_WM_COMMAND_ID(wParam, lParam));
  5525. break;
  5526. }
  5527. break;
  5528. default:
  5529. if (IsQueryCancelAutoPlay(uMsg))
  5530. {
  5531. SetWindowLongPtr(hDlg, DWLP_MSGRESULT, 1); // cancel AutoPlay
  5532. return TRUE;
  5533. }
  5534. return FALSE;
  5535. }
  5536. return TRUE;
  5537. }
  5538. // the removable media specific find first that does the UI prompt
  5539. // check for HRESULT_FROM_WIN32(ERROR_CANCELLED) for end user cancel
  5540. STDAPI FindFirstRetryRemovable(HWND hwnd, IUnknown *punkModless, LPCTSTR pszPath, WIN32_FIND_DATA *pfd, HANDLE *phfind)
  5541. {
  5542. RETRY_DATA rd = {0};
  5543. TCHAR szPath[MAX_PATH];
  5544. StrCpyN(szPath, pszPath, ARRAYSIZE(szPath));
  5545. PathStripToRoot(szPath);
  5546. PathAppend(szPath, TEXT("*.*"));
  5547. BOOL bPathChanged = (0 != StrCmpI(szPath, pszPath));
  5548. rd.pszPath = szPath;
  5549. rd.pfd = pfd;
  5550. rd.phfind = phfind;
  5551. rd.hr = E_OUTOFMEMORY;
  5552. _GoModal(hwnd, punkModless, TRUE);
  5553. DialogBoxParam(HINST_THISDLL, MAKEINTRESOURCE(DLG_RETRYFOLDERENUM), hwnd, RetryDlgProc, (LPARAM)&rd);
  5554. _GoModal(hwnd, punkModless, FALSE);
  5555. if (SUCCEEDED(rd.hr))
  5556. {
  5557. if (bPathChanged)
  5558. {
  5559. // strip produced another path, we need to retry on the requested path
  5560. if (S_OK == rd.hr)
  5561. {
  5562. FindClose(*phfind);
  5563. }
  5564. rd.hr = SHFindFirstFile(pszPath, pfd, phfind);
  5565. }
  5566. }
  5567. return rd.hr;
  5568. }
  5569. /***********************************************************************\
  5570. If the string was formatted as a UNC or Drive path, offer to
  5571. create the directory path if it doesn't exist.
  5572. PARAMETER:
  5573. RETURN: S_OK It exists.
  5574. FAILURE(): Caller should not display error UI because either
  5575. error UI was displayed or the user didn't want to create
  5576. the directory.
  5577. \***********************************************************************/
  5578. HRESULT _OfferToCreateDir(HWND hwnd, IUnknown *punkModless, LPCTSTR pszDir, DWORD dwFlags)
  5579. {
  5580. HRESULT hr = S_OK;
  5581. TCHAR szPath[MAX_PATH];
  5582. int nResult = IDYES;
  5583. StrCpyN(szPath, pszDir, ARRAYSIZE(szPath));
  5584. PathRemoveFileSpec(szPath); // wild card removed
  5585. if (SHPPFW_ASKDIRCREATE & dwFlags)
  5586. {
  5587. if (hwnd)
  5588. {
  5589. _GoModal(hwnd, punkModless, TRUE);
  5590. nResult = ShellMessageBox(HINST_THISDLL, hwnd,
  5591. MAKEINTRESOURCE(IDS_CREATEFOLDERPROMPT),
  5592. MAKEINTRESOURCE(IDS_FOLDERDOESNTEXIST),
  5593. (MB_YESNO | MB_ICONQUESTION),
  5594. szPath);
  5595. _GoModal(hwnd, punkModless, FALSE);
  5596. }
  5597. else
  5598. nResult = IDNO;
  5599. }
  5600. if (IDYES == nResult)
  5601. {
  5602. _GoModal(hwnd, punkModless, TRUE);
  5603. // SHCreateDirectoryEx() will display Error UI.
  5604. DWORD err = SHCreateDirectoryEx(hwnd, szPath, NULL);
  5605. hr = HRESULT_FROM_WIN32(err);
  5606. _GoModal(hwnd, punkModless, FALSE);
  5607. }
  5608. else
  5609. {
  5610. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED); // Until we get a valid directory, we can't do the download.
  5611. }
  5612. return hr;
  5613. }
  5614. /***********************************************************************\
  5615. See if the path w/o the spec exits. Examples:
  5616. pszPath="C:\dir1\dir2\*.*", Test="C:\dir1\dir2\"
  5617. pszPath="\\unc\share\dir1\dir2\*.*", Test="\\unc\share\dir1\dir2\"
  5618. \***********************************************************************/
  5619. BOOL PathExistsWithOutSpec(LPCTSTR pszPath)
  5620. {
  5621. TCHAR szPath[MAX_PATH];
  5622. StrCpyN(szPath, pszPath, ARRAYSIZE(szPath));
  5623. PathRemoveFileSpec(szPath);
  5624. return PathFileExists(szPath);
  5625. }
  5626. /***********************************************************************\
  5627. See if the Drive or UNC share exists.
  5628. Examples:
  5629. Path="C:\dir1\dir2\*.*", Test="C:\"
  5630. Path="\\unc\share\dir1\*.*", Test="\\unc\share\"
  5631. \***********************************************************************/
  5632. BOOL PathExistsRoot(LPCTSTR pszPath)
  5633. {
  5634. TCHAR szRoot[MAX_PATH];
  5635. StrCpyN(szRoot, pszPath, ARRAYSIZE(szRoot));
  5636. PathStripToRoot(szRoot);
  5637. return PathFileExists(szRoot);
  5638. }
  5639. inline DWORD _Win32FromHresult(HRESULT hr)
  5640. {
  5641. //
  5642. // NOTE - we get back 0x28 on 'Lock'ed volumes
  5643. // however even though that is the error that FindFirstFile
  5644. // returns, there is no corresponding entry in
  5645. // winerror.h and format message doesnt yield anything useful,
  5646. // so we map it to ERROR_ACCESS_DENIED.
  5647. //
  5648. if (HRESULT_FACILITY(hr) == FACILITY_WIN32
  5649. && (HRESULT_CODE(hr) != 0x28))
  5650. return HRESULT_CODE(hr);
  5651. return ERROR_ACCESS_DENIED;
  5652. }
  5653. BOOL _IsMountedFolder(LPCTSTR pszPath, LPTSTR pszMountPath, UINT cchMountPath)
  5654. {
  5655. BOOL fMountedOnFolder = FALSE;
  5656. // first check if it is mounted on a folder
  5657. if (GetVolumePathName(pszPath, pszMountPath, cchMountPath))
  5658. {
  5659. if ((0 != pszMountPath[2]) && (0 != pszMountPath[3]))
  5660. {
  5661. fMountedOnFolder = TRUE;
  5662. }
  5663. }
  5664. return fMountedOnFolder;
  5665. }
  5666. // like the Win32 FindFirstFile() but post UI and returns errors in HRESULT
  5667. // in:
  5668. // hwnd NULL -> disable UI (but do net reconnects, etc)
  5669. // non NULL enable UI including insert disk, format disk, net logon
  5670. //
  5671. // returns:
  5672. // S_OK hfind and find data are filled in with result
  5673. // S_FALSE no results, but media is present (hfind == INVALID_HANDLE_VALUE)
  5674. // (this is the empty enum case)
  5675. // HRESULT_FROM_WIN32(ERROR_CANCELLED) - user saw UI, thus canceled the operation
  5676. // or confirmed the failure
  5677. // FAILED() win32 error codes in the hresult (path not found, file not found, etc)
  5678. STDAPI SHFindFirstFileRetry(HWND hwnd, IUnknown *punkModless, LPCTSTR pszPath, WIN32_FIND_DATA *pfd, HANDLE *phfind, DWORD dwFlags)
  5679. {
  5680. HANDLE hfindToClose = INVALID_HANDLE_VALUE;
  5681. if (NULL == phfind)
  5682. phfind = &hfindToClose;
  5683. HRESULT hr = SHFindFirstFile(pszPath, pfd, phfind);
  5684. if (FAILED(hr))
  5685. {
  5686. BOOL fNet = PathIsUNC(pszPath) || IsDisconnectedNetDrive(DRIVEID(pszPath));
  5687. if (fNet)
  5688. {
  5689. hr = _RetryNetwork(hwnd, punkModless, pszPath, &fNet, pfd, phfind);
  5690. }
  5691. else if (hwnd)
  5692. {
  5693. if (PathRetryRemovable(hr, pszPath))
  5694. {
  5695. hr = FindFirstRetryRemovable(hwnd, punkModless, pszPath, pfd, phfind);
  5696. }
  5697. // disk should be in now, see if it needs format
  5698. if (_IsUnformatedMediaResult(hr))
  5699. {
  5700. CMountPoint* pmtpt = CMountPoint::GetMountPoint(pszPath);
  5701. if (pmtpt)
  5702. {
  5703. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  5704. if (pmtpt->IsFormattable())
  5705. {
  5706. TCHAR szMountPath[MAX_PATH];
  5707. if (_IsMountedFolder(pszPath, szMountPath, ARRAYSIZE(szMountPath)))
  5708. {
  5709. _GoModal(hwnd, punkModless, TRUE);
  5710. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_MVUNFORMATTED), MAKEINTRESOURCE(IDS_FORMAT_TITLE),
  5711. (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK),
  5712. szMountPath);
  5713. _GoModal(hwnd, punkModless, FALSE);
  5714. }
  5715. else
  5716. {
  5717. int iDrive = PathGetDriveNumber(pszPath);
  5718. _GoModal(hwnd, punkModless, TRUE);
  5719. int nResult = ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_UNFORMATTED), MAKEINTRESOURCE(IDS_FORMAT_TITLE),
  5720. (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_YESNO),
  5721. (DWORD)(iDrive + TEXT('A')));
  5722. _GoModal(hwnd, punkModless, FALSE);
  5723. if (IDYES == nResult)
  5724. {
  5725. _GoModal(hwnd, punkModless, TRUE);
  5726. DWORD dwError = SHFormatDrive(hwnd, iDrive, SHFMT_ID_DEFAULT, 0);
  5727. _GoModal(hwnd, punkModless, FALSE);
  5728. switch (dwError)
  5729. {
  5730. case SHFMT_ERROR:
  5731. case SHFMT_NOFORMAT:
  5732. _GoModal(hwnd, punkModless, TRUE);
  5733. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_NOFMT), MAKEINTRESOURCE(IDS_FORMAT_TITLE),
  5734. (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK), (DWORD)(iDrive + TEXT('A')));
  5735. _GoModal(hwnd, punkModless, FALSE);
  5736. break;
  5737. default:
  5738. hr = SHFindFirstFile(pszPath, pfd, phfind); // try again after format
  5739. }
  5740. }
  5741. }
  5742. }
  5743. else
  5744. {
  5745. _GoModal(hwnd, punkModless, TRUE);
  5746. ShellMessageBox(HINST_THISDLL, hwnd, MAKEINTRESOURCE(IDS_UNRECOGNIZED_DISK), MAKEINTRESOURCE(IDS_FORMAT_TITLE),
  5747. (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK),
  5748. NULL);
  5749. _GoModal(hwnd, punkModless, FALSE);
  5750. }
  5751. pmtpt->Release();
  5752. }
  5753. }
  5754. }
  5755. // If the caller wants us to create the directory (with or without asking), we
  5756. // need to see if we can display UI and if either that root exists (D:\ or \\unc\share\)
  5757. // Note that for PERF we want to check the full path.
  5758. if (FAILED_AND_NOT_CANCELED(hr) &&
  5759. ((SHPPFW_DIRCREATE | SHPPFW_ASKDIRCREATE) & dwFlags) &&
  5760. !PathExistsWithOutSpec(pszPath) && PathExistsRoot(pszPath))
  5761. {
  5762. hr = _OfferToCreateDir(hwnd, punkModless, pszPath, dwFlags);
  5763. ASSERT(INVALID_HANDLE_VALUE == *phfind);
  5764. if (SUCCEEDED(hr))
  5765. hr = SHFindFirstFile(pszPath, pfd, phfind); // try again after dir create
  5766. }
  5767. if (FAILED_AND_NOT_CANCELED(hr) && hwnd && !(SHPPFW_MEDIACHECKONLY & dwFlags))
  5768. {
  5769. DWORD err = _Win32FromHresult(hr);
  5770. TCHAR szPath[MAX_PATH];
  5771. UINT idTemplate = PathIsUNC(pszPath) ? IDS_ENUMERR_NETTEMPLATE2 : IDS_ENUMERR_FSTEMPLATE; // "%2 is not accessible.\n\n%1"
  5772. if (err == ERROR_PATH_NOT_FOUND)
  5773. idTemplate = IDS_ENUMERR_PATHNOTFOUND; // "%2 is not accessible.\n\nThis folder was moved or removed."
  5774. lstrcpyn(szPath, pszPath, ARRAYSIZE(szPath));
  5775. if (PathIsWild(szPath))
  5776. PathRemoveFileSpec(szPath); // wild card removed
  5777. _GoModal(hwnd, punkModless, TRUE);
  5778. SHEnumErrorMessageBox(hwnd, idTemplate, err, szPath, fNet, MB_OK | MB_ICONHAND);
  5779. _GoModal(hwnd, punkModless, FALSE);
  5780. hr = HRESULT_FROM_WIN32(ERROR_CANCELLED);
  5781. }
  5782. }
  5783. if (INVALID_HANDLE_VALUE != hfindToClose)
  5784. FindClose(hfindToClose);
  5785. return hr;
  5786. }
  5787. // TODO: Use the code in: \\orville\razzle\src\private\sm\sfc\dll\fileio.c to register
  5788. // for CD/DVD inserts instead of constantly pegging the CPU and drive. Too bad
  5789. // it doesn't work for floppies. SfcGetPathType(), RegisterForDevChange(),
  5790. // SfcQueueCallback(), SfcIsTargetAvailable()
  5791. STDAPI SHPathPrepareForWrite(HWND hwnd, IUnknown *punkModless, LPCTSTR pwzPath, DWORD dwFlags)
  5792. {
  5793. HRESULT hr = S_OK;
  5794. TCHAR szPath[MAX_PATH];
  5795. StrCpyN(szPath, pwzPath, ARRAYSIZE(szPath));
  5796. if (SHPPFW_IGNOREFILENAME & dwFlags)
  5797. PathRemoveFileSpec(szPath); // Strip file name so we just check the dir.
  5798. // We can't do anything about just a UNC server. "\\server" (no share)
  5799. if (!PathIsUNCServer(szPath))
  5800. {
  5801. HANDLE hFind;
  5802. WIN32_FIND_DATA wfd;
  5803. PathAppend(szPath, TEXT("*.*"));
  5804. hr = SHFindFirstFileRetry(hwnd, punkModless, szPath, &wfd, &hFind, dwFlags);
  5805. if (S_OK == hr)
  5806. FindClose(hFind);
  5807. else if (S_FALSE == hr)
  5808. {
  5809. // S_FALSE from SHFindFirstFileRetry() means it exists but there
  5810. // isn't a handle. We want to return S_OK for Yes, and E_FAIL or S_FALSE for no.
  5811. hr = S_OK;
  5812. }
  5813. }
  5814. return hr;
  5815. }
  5816. STDAPI SHPathPrepareForWriteA(HWND hwnd, IUnknown *punkModless, LPCSTR pszPath, DWORD dwFlags)
  5817. {
  5818. TCHAR szPath[MAX_PATH];
  5819. SHAnsiToTChar(pszPath, szPath, ARRAYSIZE(szPath));
  5820. return SHPathPrepareForWrite(hwnd, punkModless, szPath, dwFlags);
  5821. }
  5822. //
  5823. // public export of SHBindToIDlist() has a slightly different
  5824. // name so that we dont get compile link problems on legacy versions
  5825. // of the shell. shdocvw and browseui need to call SHBindToParentIDList()
  5826. STDAPI SHBindToParent(LPCITEMIDLIST pidl, REFIID riid, void **ppv, LPCITEMIDLIST *ppidlLast)
  5827. {
  5828. return SHBindToIDListParent(pidl, riid, ppv, ppidlLast);
  5829. }
  5830. // helper function to extract the target of a link file
  5831. STDAPI GetPathFromLinkFile(LPCTSTR pszLinkPath, LPTSTR pszTargetPath, int cchTargetPath)
  5832. {
  5833. IShellLink* psl;
  5834. HRESULT hr = LoadFromFile(CLSID_ShellLink, pszLinkPath, IID_PPV_ARG(IShellLink, &psl));
  5835. if (SUCCEEDED(hr))
  5836. {
  5837. IShellLinkDataList* psldl;
  5838. hr = psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psldl));
  5839. if (SUCCEEDED(hr))
  5840. {
  5841. EXP_DARWIN_LINK* pexpDarwin;
  5842. hr = psldl->CopyDataBlock(EXP_DARWIN_ID_SIG, (void**)&pexpDarwin);
  5843. if (SUCCEEDED(hr))
  5844. {
  5845. // woah, this is a darwin link. darwin links don't really have a path so we
  5846. // will fail in this case
  5847. SHUnicodeToTChar(pexpDarwin->szwDarwinID, pszTargetPath, cchTargetPath);
  5848. LocalFree(pexpDarwin);
  5849. hr = S_FALSE;
  5850. }
  5851. else
  5852. {
  5853. hr = psl->GetPath(pszTargetPath, cchTargetPath, NULL, NULL);
  5854. // FEATURE: (reinerf) - we might try getting the path from the idlist if
  5855. // pszTarget is empty (eg a link to "Control Panel" will return empyt string).
  5856. }
  5857. psldl->Release();
  5858. }
  5859. psl->Release();
  5860. }
  5861. return hr;
  5862. }
  5863. // a .EXE that is registered in the app paths key, this implies it is installed
  5864. STDAPI_(BOOL) PathIsRegisteredProgram(LPCTSTR pszPath)
  5865. {
  5866. TCHAR szTemp[MAX_PATH];
  5867. //
  5868. // PathIsBinaryExe() returns TRUE for .exe, .com
  5869. // PathIsExe() returns TRUE for .exe, .com, .bat, .cmd, .pif
  5870. //
  5871. // we dont want to treat .pif files as EXE files, because the
  5872. // user sees them as links.
  5873. //
  5874. return PathIsBinaryExe(pszPath) && PathToAppPath(pszPath, szTemp);
  5875. }
  5876. STDAPI_(void) ReplaceDlgIcon(HWND hDlg, UINT id, HICON hIcon)
  5877. {
  5878. hIcon = (HICON)SendDlgItemMessage(hDlg, id, STM_SETICON, (WPARAM)hIcon, 0);
  5879. if (hIcon)
  5880. DestroyIcon(hIcon);
  5881. }
  5882. STDAPI_(LONG) GetOfflineShareStatus(LPCTSTR pcszPath)
  5883. {
  5884. ASSERT(pcszPath);
  5885. LONG lResult = CSC_SHARESTATUS_INACTIVE;
  5886. HWND hwndCSCUI = FindWindow(STR_CSCHIDDENWND_CLASSNAME, NULL);
  5887. if (hwndCSCUI)
  5888. {
  5889. COPYDATASTRUCT cds;
  5890. cds.dwData = CSCWM_GETSHARESTATUS;
  5891. cds.cbData = sizeof(WCHAR) * (lstrlenW(pcszPath) + 1);
  5892. cds.lpData = (void *) pcszPath;
  5893. lResult = (LONG)SendMessage(hwndCSCUI, WM_COPYDATA, 0, (LPARAM) &cds);
  5894. }
  5895. return lResult;
  5896. }
  5897. #define _FLAG_CSC_COPY_STATUS_LOCALLY_DIRTY (FLAG_CSC_COPY_STATUS_DATA_LOCALLY_MODIFIED | \
  5898. FLAG_CSC_COPY_STATUS_ATTRIB_LOCALLY_MODIFIED | \
  5899. FLAG_CSC_COPY_STATUS_LOCALLY_DELETED | \
  5900. FLAG_CSC_COPY_STATUS_LOCALLY_CREATED)
  5901. // These are defined in shellapi.h, but require _WIN32_WINNT >= 0x0500.
  5902. // This file is currently compiled with _WIN32_WINNT = 0x0400. Rather
  5903. // that futz with the compile settings, just #define duplicates here.
  5904. // They'd better not change (ever) since this is a documented API.
  5905. #ifndef OFFLINE_STATUS_LOCAL
  5906. #define OFFLINE_STATUS_LOCAL 0x0001
  5907. #define OFFLINE_STATUS_REMOTE 0x0002
  5908. #define OFFLINE_STATUS_INCOMPLETE 0x0004
  5909. #endif
  5910. STDAPI SHIsFileAvailableOffline(LPCWSTR pwszPath, LPDWORD pdwStatus)
  5911. {
  5912. HRESULT hr = E_INVALIDARG;
  5913. TCHAR szUNC[MAX_PATH];
  5914. szUNC[0] = 0;
  5915. if (pdwStatus)
  5916. {
  5917. *pdwStatus = 0;
  5918. }
  5919. //
  5920. // Need full UNC path (TCHAR) for calling CSC APIs.
  5921. // (Non-net paths are "not cached" by definition.)
  5922. //
  5923. if (pwszPath && pwszPath[0])
  5924. {
  5925. if (PathIsUNCW(pwszPath))
  5926. {
  5927. SHUnicodeToTChar(pwszPath, szUNC, ARRAYSIZE(szUNC));
  5928. }
  5929. else if (L':' == pwszPath[1] && L':' != pwszPath[0])
  5930. {
  5931. // Check for mapped net drive
  5932. TCHAR szPath[MAX_PATH];
  5933. SHUnicodeToTChar(pwszPath, szPath, ARRAYSIZE(szPath));
  5934. DWORD dwLen = ARRAYSIZE(szUNC);
  5935. if (S_OK == SHWNetGetConnection(szPath, szUNC, &dwLen))
  5936. {
  5937. // Got \\server\share, append the rest
  5938. PathAppend(szUNC, PathSkipRoot(szPath));
  5939. }
  5940. // else not mapped
  5941. }
  5942. }
  5943. // Do we have a UNC path?
  5944. if (szUNC[0])
  5945. {
  5946. // Assume CSC not running
  5947. hr = E_FAIL;
  5948. if (CSCIsCSCEnabled())
  5949. {
  5950. DWORD dwCscStatus = 0;
  5951. // Assume cached
  5952. hr = S_OK;
  5953. if (!CSCQueryFileStatus(szUNC, &dwCscStatus, NULL, NULL))
  5954. {
  5955. // Not cached, return failure
  5956. DWORD dwErr = GetLastError();
  5957. if (ERROR_SUCCESS == dwErr)
  5958. dwErr = ERROR_PATH_NOT_FOUND;
  5959. hr = HRESULT_FROM_WIN32(dwErr);
  5960. }
  5961. else if (pdwStatus)
  5962. {
  5963. // File is cached, and caller wants extra status info
  5964. DWORD dwResult = 0;
  5965. BOOL bDirty = FALSE;
  5966. // Is it a sparse file?
  5967. // Note: CSC always marks directories as sparse
  5968. if ((dwCscStatus & FLAG_CSC_COPY_STATUS_IS_FILE) &&
  5969. (dwCscStatus & FLAG_CSC_COPY_STATUS_SPARSE))
  5970. {
  5971. dwResult |= OFFLINE_STATUS_INCOMPLETE;
  5972. }
  5973. // Is it dirty?
  5974. if (dwCscStatus & _FLAG_CSC_COPY_STATUS_LOCALLY_DIRTY)
  5975. {
  5976. bDirty = TRUE;
  5977. }
  5978. // Get share status
  5979. PathStripToRoot(szUNC);
  5980. dwCscStatus = 0;
  5981. if (CSCQueryFileStatus(szUNC, &dwCscStatus, NULL, NULL))
  5982. {
  5983. if (dwCscStatus & FLAG_CSC_SHARE_STATUS_DISCONNECTED_OP)
  5984. {
  5985. // Server offline --> all opens are local (only)
  5986. dwResult |= OFFLINE_STATUS_LOCAL;
  5987. }
  5988. else if (bDirty)
  5989. {
  5990. // Server online, but file is dirty --> open is remote
  5991. dwResult |= OFFLINE_STATUS_REMOTE;
  5992. }
  5993. else
  5994. {
  5995. // Server is online and file is in sync --> open is both
  5996. dwResult |= OFFLINE_STATUS_LOCAL | OFFLINE_STATUS_REMOTE;
  5997. if ((dwCscStatus & FLAG_CSC_SHARE_STATUS_CACHING_MASK) == FLAG_CSC_SHARE_STATUS_VDO)
  5998. {
  5999. // Feature: (JeffreyS) The share is VDO, but that only affects files
  6000. // opened for execution. Is there a way to tell whether
  6001. // the file is only open locally?
  6002. }
  6003. }
  6004. }
  6005. else
  6006. {
  6007. // Very strange. CSCQueryFileStatus succeeded for the file,
  6008. // but failed for the share. Assume no active connection
  6009. // exists and the server is online (open both).
  6010. dwResult |= OFFLINE_STATUS_LOCAL | OFFLINE_STATUS_REMOTE;
  6011. }
  6012. *pdwStatus = dwResult;
  6013. }
  6014. }
  6015. }
  6016. return hr;
  6017. }
  6018. STDAPI_(BOOL) GetShellClassInfo(LPCTSTR pszPath, LPTSTR pszKey, LPTSTR pszBuffer, DWORD cchBuffer)
  6019. {
  6020. *pszBuffer = 0;
  6021. TCHAR szIniFile[MAX_PATH];
  6022. PathCombine(szIniFile, pszPath, TEXT("Desktop.ini"));
  6023. return (SHGetIniString(TEXT(".ShellClassInfo"), pszKey, pszBuffer, cchBuffer, szIniFile) ? TRUE : FALSE);
  6024. }
  6025. STDAPI_(BOOL) GetShellClassInfoInfoTip(LPCTSTR pszPath, LPTSTR pszBuffer, DWORD cchBuffer)
  6026. {
  6027. BOOL bRet = GetShellClassInfo(pszPath, TEXT("InfoTip"), pszBuffer, cchBuffer);
  6028. if (bRet)
  6029. SHLoadIndirectString(pszBuffer, pszBuffer, cchBuffer, NULL);
  6030. else if (cchBuffer)
  6031. *pszBuffer = 0;
  6032. return bRet;
  6033. }
  6034. TCHAR const c_szUserAppData[] = TEXT("%userappdata%");
  6035. void ExpandUserAppData(LPTSTR pszFile)
  6036. {
  6037. //Check if the given string has %UserAppData%
  6038. LPTSTR psz = StrChr(pszFile, TEXT('%'));
  6039. if (psz)
  6040. {
  6041. if (!StrCmpNI(psz, c_szUserAppData, ARRAYSIZE(c_szUserAppData)-1))
  6042. {
  6043. TCHAR szTempBuff[MAX_PATH];
  6044. if (SHGetSpecialFolderPath(NULL, szTempBuff, CSIDL_APPDATA, TRUE))
  6045. {
  6046. PathAppend(szTempBuff, psz + lstrlen(c_szUserAppData));
  6047. //Copy back to the input buffer!
  6048. lstrcpy(psz, szTempBuff);
  6049. }
  6050. }
  6051. }
  6052. }
  6053. TCHAR const c_szWebDir[] = TEXT("%WebDir%");
  6054. void ExpandWebDir(LPTSTR pszFile, int cch)
  6055. {
  6056. //Check if the given string has %WebDir%
  6057. LPTSTR psz = StrChr(pszFile, TEXT('%'));
  6058. if (psz)
  6059. {
  6060. if (!StrCmpNI(psz, c_szWebDir, ARRAYSIZE(c_szWebDir) - 1))
  6061. {
  6062. //Skip the %WebDir% string plus the following backslash.
  6063. LPTSTR pszPathAndFileName = psz + lstrlen(c_szWebDir) + sizeof('\\');
  6064. if (pszPathAndFileName && (pszPathAndFileName != psz))
  6065. {
  6066. TCHAR szTempBuff[MAX_PATH];
  6067. StrCpyN(szTempBuff, pszPathAndFileName, ARRAYSIZE(szTempBuff));
  6068. SHGetWebFolderFilePath(szTempBuff, pszFile, cch);
  6069. }
  6070. }
  6071. }
  6072. }
  6073. void ExpandOtherVariables(LPTSTR pszFile, int cch)
  6074. {
  6075. ExpandUserAppData(pszFile);
  6076. ExpandWebDir(pszFile, cch);
  6077. }
  6078. void SubstituteWebDir(LPTSTR pszFile, int cch)
  6079. {
  6080. TCHAR szWebDirPath[MAX_PATH];
  6081. if (SUCCEEDED(SHGetWebFolderFilePath(TEXT("folder.htt"), szWebDirPath, ARRAYSIZE(szWebDirPath))))
  6082. {
  6083. LPTSTR pszWebDirPart;
  6084. PathRemoveFileSpec(szWebDirPath);
  6085. pszWebDirPart = StrStrI(pszFile, szWebDirPath);
  6086. if (pszWebDirPart)
  6087. {
  6088. TCHAR szTemp[MAX_PATH];
  6089. int cchBeforeWebDir = (int)(pszWebDirPart - pszFile);
  6090. // copy the part after C:\WINNT\Web into szTemp
  6091. lstrcpyn(szTemp, pszWebDirPart + lstrlen(szWebDirPath), ARRAYSIZE(szTemp));
  6092. // replace C:\WINNT\Web with %WebDir%
  6093. lstrcpyn(pszWebDirPart, c_szWebDir, cch - cchBeforeWebDir);
  6094. // add back on the part that came after
  6095. lstrcatn(pszFile, szTemp, cch);
  6096. }
  6097. }
  6098. }
  6099. STDAPI_(BOOL) IsExplorerBrowser(IShellBrowser *psb)
  6100. {
  6101. HWND hwnd;
  6102. return psb && SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwnd)) && hwnd;
  6103. }
  6104. STDAPI_(BOOL) IsExplorerModeBrowser(IUnknown *psite)
  6105. {
  6106. BOOL bRet = FALSE;
  6107. IShellBrowser *psb;
  6108. if (SUCCEEDED(IUnknown_QueryService(psite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
  6109. {
  6110. bRet = IsExplorerBrowser(psb);
  6111. psb->Release();
  6112. }
  6113. return bRet;
  6114. }
  6115. STDAPI InvokeFolderPidl(LPCITEMIDLIST pidl, int nCmdShow)
  6116. {
  6117. SHELLEXECUTEINFO ei = {0};
  6118. LPITEMIDLIST pidlFree = NULL;
  6119. if (IS_INTRESOURCE(pidl))
  6120. {
  6121. pidlFree = SHCloneSpecialIDList(NULL, PtrToUlong((void *)pidl), FALSE);
  6122. pidl = pidlFree;
  6123. }
  6124. ei.cbSize = sizeof(ei);
  6125. ei.fMask = SEE_MASK_IDLIST | SEE_MASK_CLASSNAME | SEE_MASK_FLAG_DDEWAIT;
  6126. ei.lpIDList = (void *)pidl;
  6127. ei.nShow = nCmdShow;
  6128. ei.lpClass = c_szFolderClass;
  6129. HRESULT hr = ShellExecuteEx(&ei) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
  6130. ILFree(pidlFree);
  6131. return hr;
  6132. }
  6133. HRESULT GetRGBFromBStr(BSTR bstr, COLORREF *pclr)
  6134. {
  6135. *pclr = CLR_INVALID;
  6136. HRESULT hr = E_FAIL;
  6137. if (bstr)
  6138. {
  6139. TCHAR szTemp[9], szColor[11] = {'0','x',0, };
  6140. SHUnicodeToTChar(bstr, szTemp, ARRAYSIZE(szTemp));
  6141. LPTSTR pszPound = StrChr(szTemp, TEXT('#'));
  6142. if (pszPound)
  6143. pszPound++; //Skip the pound sign!
  6144. else
  6145. pszPound = szTemp; //Pound sign is missing. Use the whole strng
  6146. StrCatN(szColor, pszPound, ARRAYSIZE(szColor));
  6147. INT rgb;
  6148. if (StrToIntEx(szColor, STIF_SUPPORT_HEX, &rgb))
  6149. {
  6150. *pclr = (COLORREF)(((rgb & 0x000000ff) << 16) | (rgb & 0x0000ff00) | ((rgb & 0x00ff0000) >> 16));
  6151. hr = S_OK;
  6152. }
  6153. }
  6154. return hr;
  6155. }
  6156. STDAPI IUnknown_HTMLBackgroundColor(IUnknown *punk, COLORREF *pclr)
  6157. {
  6158. HRESULT hr = E_FAIL;
  6159. if (punk)
  6160. {
  6161. IHTMLDocument2 *pDoc;
  6162. hr = punk->QueryInterface(IID_PPV_ARG(IHTMLDocument2, &pDoc));
  6163. if (SUCCEEDED(hr))
  6164. {
  6165. VARIANT v;
  6166. v.vt = VT_BSTR;
  6167. v.bstrVal = NULL;
  6168. hr = pDoc->get_bgColor(&v);
  6169. if (SUCCEEDED(hr))
  6170. {
  6171. hr = GetRGBFromBStr(v.bstrVal, pclr);
  6172. VariantClear(&v);
  6173. }
  6174. pDoc->Release();
  6175. }
  6176. }
  6177. return hr;
  6178. }
  6179. STDAPI_(int) MapSCIDToColumn(IShellFolder2* psf2, const SHCOLUMNID* pscid)
  6180. {
  6181. int i = 0;
  6182. SHCOLUMNID scid;
  6183. while (SUCCEEDED(psf2->MapColumnToSCID(i, &scid)))
  6184. {
  6185. if (IsEqualSCID(scid, *pscid))
  6186. return i;
  6187. i++;
  6188. }
  6189. return 0;
  6190. }
  6191. #ifdef COLUMNS_IN_DESKTOPINI
  6192. #define IsDigit(c) ((c) >= TEXT('0') && c <= TEXT('9'))
  6193. STDAPI _GetNextCol(LPTSTR* ppszText, DWORD* pnCol)
  6194. {
  6195. HRESULT hr = S_OK;
  6196. TCHAR *pszText;
  6197. if (*ppszText[0])
  6198. {
  6199. pszText = StrChrI(*ppszText, TEXT(','));
  6200. if (pszText)
  6201. {
  6202. *pszText = 0;
  6203. *pnCol = StrToInt(*ppszText);
  6204. *ppszText = ++pszText;
  6205. }
  6206. else if (IsDigit(*ppszText[0]))
  6207. {
  6208. *pnCol = StrToInt(*ppszText);
  6209. *ppszText = 0;
  6210. }
  6211. }
  6212. else
  6213. hr = E_FAIL;
  6214. return hr;
  6215. }
  6216. #endif
  6217. STDAPI_(int) DPA_ILFreeCallback(void *p, void *d)
  6218. {
  6219. ILFree((LPITEMIDLIST)p); // ILFree checks for NULL pointer
  6220. return 1;
  6221. }
  6222. STDAPI_(void) DPA_FreeIDArray(HDPA hdpa)
  6223. {
  6224. if (hdpa)
  6225. DPA_DestroyCallback(hdpa, DPA_ILFreeCallback, 0);
  6226. }
  6227. STDAPI_(void) EnableAndShowWindow(HWND hWnd, BOOL bShow)
  6228. {
  6229. ShowWindow(hWnd, bShow ? SW_SHOW : SW_HIDE);
  6230. EnableWindow(hWnd, bShow);
  6231. }
  6232. //
  6233. // Helper function which returns a string value instead of the SHELLDETAILS
  6234. // object, to be used by clients which are only interested in the returned
  6235. // string value and want to avoid the hassle of doing a STRRET to STR conversion,
  6236. //
  6237. STDAPI DetailsOf(IShellFolder2 *psf2, LPCITEMIDLIST pidl, DWORD flags, LPTSTR psz, UINT cch)
  6238. {
  6239. *psz = 0;
  6240. SHELLDETAILS sd;
  6241. HRESULT hr = psf2->GetDetailsOf(pidl, flags, &sd);
  6242. if (SUCCEEDED(hr))
  6243. {
  6244. hr = StrRetToBuf(&sd.str, pidl, psz, cch);
  6245. }
  6246. return hr;
  6247. }
  6248. #pragma warning(push,4)
  6249. // --------------------------------------------------------------------------
  6250. // ::SHGetUserDisplayName
  6251. //
  6252. // Arguments: pszDisplayName = Buffer to retrieve the display name.
  6253. // puLen = [in/out] Size of buffer
  6254. //
  6255. // Purpose: Returns the display name, or the logon name if the display name
  6256. // is not available
  6257. //
  6258. // Returns: HRESULT
  6259. //
  6260. //
  6261. //
  6262. // History: 2000-03-03 vtan created
  6263. // 2001-03-01 fabriced use GetUserNameEx
  6264. // --------------------------------------------------------------------------
  6265. STDAPI SHGetUserDisplayName (LPWSTR pszDisplayName, PULONG puLen)
  6266. {
  6267. HRESULT hr;
  6268. // Validate pszDisplayName.
  6269. if ((pszDisplayName == NULL) || (puLen == NULL) || IsBadWritePtr(pszDisplayName, (*puLen * sizeof(TCHAR))))
  6270. {
  6271. hr = E_INVALIDARG;
  6272. }
  6273. else
  6274. {
  6275. hr = S_OK;
  6276. if (!GetUserNameEx(NameDisplay, pszDisplayName, puLen))
  6277. {
  6278. TCHAR szName[UNLEN+1];
  6279. DWORD dwLen = ARRAYSIZE(szName);
  6280. DWORD dwLastError = GetLastError();
  6281. if (GetUserName(szName, &dwLen))
  6282. {
  6283. // If we are not on a domain, GetUserNameEx does not work :-(.
  6284. if (dwLastError == ERROR_NONE_MAPPED)
  6285. {
  6286. PUSER_INFO_2 pUserInfo;
  6287. DWORD dwErrorCode = NetUserGetInfo(NULL, // Local account
  6288. szName,
  6289. 2,
  6290. reinterpret_cast<LPBYTE*>(&pUserInfo));
  6291. if (ERROR_SUCCESS == dwErrorCode)
  6292. {
  6293. if (pUserInfo->usri2_full_name[0] != L'\0')
  6294. {
  6295. StrCpyN(pszDisplayName, pUserInfo->usri2_full_name, *puLen);
  6296. *puLen = lstrlen(pUserInfo->usri2_full_name) + 1;
  6297. }
  6298. else
  6299. {
  6300. hr = E_FAIL;
  6301. }
  6302. TW32(NetApiBufferFree(pUserInfo));
  6303. }
  6304. else
  6305. {
  6306. hr = HRESULT_FROM_WIN32(dwErrorCode);
  6307. }
  6308. }
  6309. else
  6310. {
  6311. hr = HRESULT_FROM_WIN32(dwLastError);
  6312. }
  6313. if (FAILED(hr))
  6314. {
  6315. hr = S_OK;
  6316. StrCpyN(pszDisplayName, szName, *puLen);
  6317. *puLen = dwLen;
  6318. }
  6319. }
  6320. else
  6321. {
  6322. hr = HRESULT_FROM_WIN32(GetLastError());
  6323. }
  6324. }
  6325. }
  6326. return hr;
  6327. }
  6328. // --------------------------------------------------------------------------
  6329. // ::SHIsCurrentThreadInteractive
  6330. //
  6331. // Arguments: <none>
  6332. //
  6333. // Returns: BOOL
  6334. //
  6335. // Purpose: Determine whether the current thread is running on the
  6336. // interactive desktop. This takes into account terminal services
  6337. // and console disconnects as well.
  6338. //
  6339. // History: 2000-03-23 vtan created
  6340. // --------------------------------------------------------------------------
  6341. STDAPI_(BOOL) SHIsCurrentThreadInteractive (void)
  6342. {
  6343. BOOL fResult = FALSE;
  6344. // Open handles to the current process window station and thread
  6345. // desktop. These handles do NOT need to be closed.
  6346. HWINSTA hWindowStation = GetProcessWindowStation();
  6347. if (hWindowStation != NULL)
  6348. {
  6349. HDESK hDesktop = GetThreadDesktop(GetCurrentThreadId());
  6350. if (hDesktop != NULL)
  6351. {
  6352. DWORD dwLengthNeeded = 0;
  6353. (BOOL)GetUserObjectInformation(hWindowStation, UOI_NAME, NULL, 0, &dwLengthNeeded);
  6354. if (dwLengthNeeded != 0)
  6355. {
  6356. TCHAR *pszWindowStationName = static_cast<TCHAR*>(LocalAlloc(LMEM_FIXED, dwLengthNeeded));
  6357. if (pszWindowStationName != NULL)
  6358. {
  6359. if (GetUserObjectInformation(hWindowStation, UOI_NAME, pszWindowStationName, dwLengthNeeded, &dwLengthNeeded) != FALSE)
  6360. {
  6361. dwLengthNeeded = 0;
  6362. (BOOL)GetUserObjectInformation(hDesktop, UOI_NAME, NULL, 0, &dwLengthNeeded);
  6363. if (dwLengthNeeded != 0)
  6364. {
  6365. TCHAR *pszDesktopName = static_cast<TCHAR*>(LocalAlloc(LMEM_FIXED, dwLengthNeeded));
  6366. if (pszDesktopName != NULL)
  6367. {
  6368. if (GetUserObjectInformation(hDesktop, UOI_NAME, pszDesktopName, dwLengthNeeded, &dwLengthNeeded) != FALSE)
  6369. {
  6370. // This is a hard coded string comparison. This name
  6371. // never changes WinSta0\Default
  6372. if (lstrcmpi(pszWindowStationName, TEXT("WinSta0")) == 0)
  6373. {
  6374. fResult = (lstrcmpi(pszDesktopName, TEXT("WinLogon")) != 0 &&
  6375. lstrcmpi(pszDesktopName, TEXT("Screen-Saver")) != 0);
  6376. }
  6377. }
  6378. LocalFree(pszDesktopName);
  6379. }
  6380. }
  6381. }
  6382. LocalFree(pszWindowStationName);
  6383. }
  6384. }
  6385. }
  6386. }
  6387. return fResult;
  6388. }
  6389. static const TCHAR s_szBaseKeyName[] = TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\UnreadMail");
  6390. static const TCHAR s_szMessageCountValueName[] = TEXT("MessageCount");
  6391. static const TCHAR s_szTimeStampValueName[] = TEXT("TimeStamp");
  6392. static const TCHAR s_szApplicationValueName[] = TEXT("Application");
  6393. // --------------------------------------------------------------------------
  6394. // ReadSingleUnreadMailCount
  6395. //
  6396. // Arguments: hKey = Base HKEY to read info from.
  6397. // pdwCount = Count returned.
  6398. // pFileTime = FILETIME stamp returned.
  6399. // pszShellExecuteCommand = Execute command returned.
  6400. // cchShellExecuteCommand = Size of execute command buffer.
  6401. //
  6402. // Returns: LONG
  6403. //
  6404. // Purpose: Reads a single unread mail account information from the given
  6405. // HKEY of the mail account.
  6406. //
  6407. // History: 2000-06-20 vtan created
  6408. // --------------------------------------------------------------------------
  6409. LONG ReadSingleUnreadMailCount (HKEY hKey, DWORD *pdwCount, FILETIME *pFileTime, LPTSTR pszShellExecuteCommand, int cchShellExecuteCommand)
  6410. {
  6411. LONG lError;
  6412. DWORD dwType, dwData, dwDataSize;
  6413. dwDataSize = sizeof(*pdwCount);
  6414. lError = RegQueryValueEx(hKey,
  6415. s_szMessageCountValueName,
  6416. NULL,
  6417. &dwType,
  6418. reinterpret_cast<LPBYTE>(&dwData),
  6419. &dwDataSize);
  6420. if (ERROR_SUCCESS == lError)
  6421. {
  6422. FILETIME fileTime;
  6423. if ((pdwCount != NULL) && !IsBadWritePtr(pdwCount, sizeof(*pdwCount)) && (REG_DWORD == dwType))
  6424. {
  6425. *pdwCount = dwData;
  6426. }
  6427. dwDataSize = sizeof(fileTime);
  6428. lError = RegQueryValueEx(hKey,
  6429. s_szTimeStampValueName,
  6430. NULL,
  6431. &dwType,
  6432. reinterpret_cast<LPBYTE>(&fileTime),
  6433. &dwDataSize);
  6434. if (ERROR_SUCCESS == lError)
  6435. {
  6436. TCHAR szTemp[512];
  6437. if ((pFileTime != NULL) && !IsBadWritePtr(pFileTime, sizeof(*pFileTime)) && (REG_BINARY == dwType))
  6438. {
  6439. *pFileTime = fileTime;
  6440. }
  6441. dwDataSize = sizeof(szTemp);
  6442. lError = SHQueryValueEx(hKey,
  6443. s_szApplicationValueName,
  6444. NULL,
  6445. &dwType,
  6446. reinterpret_cast<LPBYTE>(szTemp),
  6447. &dwDataSize);
  6448. if (ERROR_SUCCESS == lError)
  6449. {
  6450. if ((pszShellExecuteCommand != NULL) && !IsBadWritePtr(pszShellExecuteCommand, cchShellExecuteCommand * sizeof(TCHAR)) && (REG_SZ == dwType))
  6451. {
  6452. (TCHAR*)lstrcpyn(pszShellExecuteCommand, szTemp, cchShellExecuteCommand);
  6453. }
  6454. }
  6455. }
  6456. }
  6457. return lError;
  6458. }
  6459. // --------------------------------------------------------------------------
  6460. // ::SHEnumerateUnreadMailAccounts
  6461. //
  6462. // Arguments: hKeyUser = HKEY to user's hive.
  6463. // dwIndex = Index of mail account.
  6464. // pszMailAddress = Returned mail address for account.
  6465. // cchMailAddress = Characters in mail address buffer.
  6466. //
  6467. // Returns: HRESULT
  6468. //
  6469. // Purpose: Given an index returns the actual indexed email account from
  6470. // the given user's hive.
  6471. //
  6472. // History: 2000-06-29 vtan created
  6473. // --------------------------------------------------------------------------
  6474. STDAPI SHEnumerateUnreadMailAccounts (HKEY hKeyUser, DWORD dwIndex, LPTSTR pszMailAddress, int cchMailAddress)
  6475. {
  6476. HRESULT hr;
  6477. LONG lError;
  6478. HKEY hKey;
  6479. // Parameter validation.
  6480. if (IsBadWritePtr(pszMailAddress, cchMailAddress * sizeof(TCHAR)))
  6481. {
  6482. return E_INVALIDARG;
  6483. }
  6484. // Open the unread mail
  6485. lError = RegOpenKeyEx(hKeyUser != NULL ? hKeyUser : HKEY_CURRENT_USER,
  6486. s_szBaseKeyName,
  6487. 0,
  6488. KEY_ENUMERATE_SUB_KEYS,
  6489. &hKey);
  6490. if (ERROR_SUCCESS == lError)
  6491. {
  6492. DWORD dwMailAddressSize;
  6493. FILETIME ftLastWriteTime;
  6494. // Get the given index mail address.
  6495. dwMailAddressSize = static_cast<DWORD>(cchMailAddress * sizeof(TCHAR));
  6496. lError = RegEnumKeyEx(hKey,
  6497. dwIndex,
  6498. pszMailAddress,
  6499. &dwMailAddressSize,
  6500. NULL,
  6501. NULL,
  6502. NULL,
  6503. &ftLastWriteTime);
  6504. if (ERROR_SUCCESS != lError)
  6505. {
  6506. pszMailAddress[0] = TEXT('\0');
  6507. }
  6508. TW32(RegCloseKey(hKey));
  6509. }
  6510. if (ERROR_SUCCESS == lError)
  6511. {
  6512. hr = S_OK;
  6513. }
  6514. else
  6515. {
  6516. hr = HRESULT_FROM_WIN32(lError);
  6517. }
  6518. return hr;
  6519. }
  6520. // --------------------------------------------------------------------------
  6521. // ::SHGetUnreadMailCount
  6522. //
  6523. // Arguments: hKeyUser = HKEY to user's hive.
  6524. // pszMailAddress = Mail address for account.
  6525. // pdwCount = Number of unread messages.
  6526. // pszShellExecuteCommand = Execution command for application.
  6527. // cchShellExecuteCommand = Number of characters in buffer.
  6528. //
  6529. // Returns: HRESULT
  6530. //
  6531. // Purpose: Reads unread mail messages for the given user and mail
  6532. // address. If being run in a user's environment then hKeyUser
  6533. // should be NULL. This will use HKEY_CURRENT_USER. If being run
  6534. // from the SYSTEM context this will be HKEY_USERS\{SID}.
  6535. //
  6536. // Callers may pass NULL for output parameters if not required.
  6537. //
  6538. // History: 2000-06-20 vtan created
  6539. // --------------------------------------------------------------------------
  6540. STDAPI SHGetUnreadMailCount (HKEY hKeyUser, LPCTSTR pszMailAddress, DWORD *pdwCount, FILETIME *pFileTime, LPTSTR pszShellExecuteCommand, int cchShellExecuteCommand)
  6541. {
  6542. HRESULT hr;
  6543. LONG lError;
  6544. HKEY hKey;
  6545. TCHAR szTemp[512];
  6546. // Validate parameters. Valid parameters actually depends on pszMailAddress
  6547. // If pszMailAddress is NULL then the total unread mail count for ALL accounts
  6548. // is returned and pszShellExecuteCommand is ignored and MUST be
  6549. // NULL. pFileTime can be null and if so it is ignored but if it is non null, it
  6550. // is a filter to show only unread mail entries that are newer than the specified
  6551. // filetime. Otherwise only items for the specified mail account are returned.
  6552. if (pszMailAddress == NULL)
  6553. {
  6554. if (IsBadWritePtr(pdwCount, sizeof(*pdwCount)) ||
  6555. (pszShellExecuteCommand != NULL) ||
  6556. (cchShellExecuteCommand != 0))
  6557. {
  6558. return E_INVALIDARG;
  6559. }
  6560. else
  6561. {
  6562. LONG lError;
  6563. *pdwCount = 0;
  6564. lError = RegOpenKeyEx(hKeyUser != NULL ? hKeyUser : HKEY_CURRENT_USER,
  6565. s_szBaseKeyName,
  6566. 0,
  6567. KEY_ENUMERATE_SUB_KEYS,
  6568. &hKey);
  6569. if (ERROR_SUCCESS == lError)
  6570. {
  6571. DWORD dwIndex, dwTempSize;
  6572. FILETIME ftLastWriteTime;
  6573. // Because this uses advapi32!RegEnumKeyEx and this returns items
  6574. // in an arbitrary order and is subject to indeterminate behavior
  6575. // when keys are added while the key is enumerated there exists a
  6576. // possible race condition / dual access problem. Deal with this
  6577. // if it shows up. It's possible for a mail application to write
  6578. // information at the exact time this loop is retrieving the
  6579. // information. Slim chance but still possible.
  6580. dwIndex = 0;
  6581. do
  6582. {
  6583. dwTempSize = ARRAYSIZE(szTemp);
  6584. lError = RegEnumKeyEx(hKey,
  6585. dwIndex++,
  6586. szTemp,
  6587. &dwTempSize,
  6588. NULL,
  6589. NULL,
  6590. NULL,
  6591. &ftLastWriteTime);
  6592. if (ERROR_SUCCESS == lError)
  6593. {
  6594. HKEY hKeyMailAccount;
  6595. lError = RegOpenKeyEx(hKey,
  6596. szTemp,
  6597. 0,
  6598. KEY_QUERY_VALUE,
  6599. &hKeyMailAccount);
  6600. if (ERROR_SUCCESS == lError)
  6601. {
  6602. DWORD dwCount;
  6603. FILETIME ft;
  6604. lError = ReadSingleUnreadMailCount(hKeyMailAccount, &dwCount, &ft, NULL, 0);
  6605. if (ERROR_SUCCESS == lError)
  6606. {
  6607. BOOL ftExpired = false;
  6608. // If they pass in a pFileTime, use it as a filter and only
  6609. // count accounts that have been updated since the passed
  6610. // in file time
  6611. if (pFileTime)
  6612. {
  6613. ftExpired = (CompareFileTime(&ft, pFileTime) < 0);
  6614. }
  6615. if (!ftExpired)
  6616. {
  6617. *pdwCount += dwCount;
  6618. }
  6619. }
  6620. TW32(RegCloseKey(hKeyMailAccount));
  6621. }
  6622. }
  6623. } while (ERROR_SUCCESS == lError);
  6624. // Ignore the ERROR_NO_MORE_ITEMS that is returned when the
  6625. // enumeration is done.
  6626. if (ERROR_NO_MORE_ITEMS == lError)
  6627. {
  6628. lError = ERROR_SUCCESS;
  6629. }
  6630. TW32(RegCloseKey(hKey));
  6631. }
  6632. if (ERROR_SUCCESS == lError)
  6633. {
  6634. hr = S_OK;
  6635. }
  6636. else
  6637. {
  6638. hr = HRESULT_FROM_WIN32(lError);
  6639. }
  6640. }
  6641. }
  6642. else
  6643. {
  6644. if (IsBadStringPtr(pszMailAddress, static_cast<UINT_PTR>(-1)))
  6645. {
  6646. return E_INVALIDARG;
  6647. }
  6648. // Calculate the length of the registry path where we will create the
  6649. // key that will store the values. This is:
  6650. //
  6651. // HKCU\Software\Microsoft\Windows\CurrentVersion\UnreadMail\{MailAddr}
  6652. //
  6653. // Note that a NULL terminator is not used in the comparison because
  6654. // ARRAYSIZE on the static string is used which includes '\0'.
  6655. if ((ARRAYSIZE(s_szBaseKeyName) + sizeof('\\') + lstrlen(pszMailAddress)) < ARRAYSIZE(szTemp))
  6656. {
  6657. lstrcpy(szTemp, s_szBaseKeyName);
  6658. lstrcat(szTemp, TEXT("\\"));
  6659. lstrcat(szTemp, pszMailAddress);
  6660. lError = RegOpenKeyEx(hKeyUser != NULL ? hKeyUser : HKEY_CURRENT_USER,
  6661. szTemp,
  6662. 0,
  6663. KEY_QUERY_VALUE,
  6664. &hKey);
  6665. if (ERROR_SUCCESS == lError)
  6666. {
  6667. lError = ReadSingleUnreadMailCount(hKey, pdwCount, pFileTime, pszShellExecuteCommand, cchShellExecuteCommand);
  6668. TW32(RegCloseKey(hKey));
  6669. }
  6670. if (ERROR_SUCCESS == lError)
  6671. {
  6672. hr = S_OK;
  6673. }
  6674. else
  6675. {
  6676. hr = HRESULT_FROM_WIN32(lError);
  6677. }
  6678. }
  6679. else
  6680. {
  6681. hr = E_OUTOFMEMORY;
  6682. }
  6683. }
  6684. return hr;
  6685. }
  6686. // --------------------------------------------------------------------------
  6687. // ::SHSetUnreadMailCount
  6688. //
  6689. // Arguments: pszMailAddress = Mail address for account.
  6690. // dwCount = Number of unread messages.
  6691. // pszShellExecuteCommand = Execution command for application.
  6692. //
  6693. // Returns: HRESULT
  6694. //
  6695. // Purpose: Writes unread mail information to the registry for the
  6696. // current user. Do NOT call this API from a system process
  6697. // impersonating a user because it uses HKEY_CURRENT_USER. If
  6698. // this is required in future a thread impersonation token
  6699. // check will be required.
  6700. //
  6701. // History: 2000-06-19 vtan created
  6702. // --------------------------------------------------------------------------
  6703. STDAPI SHSetUnreadMailCount (LPCTSTR pszMailAddress, DWORD dwCount, LPCTSTR pszShellExecuteCommand)
  6704. {
  6705. HRESULT hr;
  6706. TCHAR szTemp[512];
  6707. // Validate parameters.
  6708. if (IsBadStringPtr(pszMailAddress, static_cast<UINT_PTR>(-1)) ||
  6709. IsBadStringPtr(pszShellExecuteCommand, static_cast<UINT_PTR>(-1)))
  6710. {
  6711. return E_INVALIDARG;
  6712. }
  6713. // Calculate the length of the registry path where we will create the
  6714. // key that will store the values. This is:
  6715. //
  6716. // HKCU\Software\Microsoft\Windows\CurrentVersion\UnreadMail\{MailAddr}
  6717. //
  6718. // Note that a NULL terminator is not used in the comparison because
  6719. // ARRAYSIZE on the static string is used which includes '\0'.
  6720. if ((ARRAYSIZE(s_szBaseKeyName) + sizeof('\\') + lstrlen(pszMailAddress)) < ARRAYSIZE(szTemp))
  6721. {
  6722. LONG lError;
  6723. DWORD dwDisposition;
  6724. HKEY hKey;
  6725. lstrcpy(szTemp, s_szBaseKeyName);
  6726. lstrcat(szTemp, TEXT("\\"));
  6727. lstrcat(szTemp, pszMailAddress);
  6728. lError = RegCreateKeyEx(HKEY_CURRENT_USER,
  6729. szTemp,
  6730. 0,
  6731. NULL,
  6732. REG_OPTION_NON_VOLATILE,
  6733. KEY_SET_VALUE,
  6734. NULL,
  6735. &hKey,
  6736. &dwDisposition);
  6737. if (ERROR_SUCCESS == lError)
  6738. {
  6739. lError = RegSetValueEx(hKey,
  6740. s_szMessageCountValueName,
  6741. 0,
  6742. REG_DWORD,
  6743. reinterpret_cast<LPBYTE>(&dwCount),
  6744. sizeof(dwCount));
  6745. if (ERROR_SUCCESS == lError)
  6746. {
  6747. FILETIME fileTime;
  6748. GetSystemTimeAsFileTime(&fileTime);
  6749. lError = RegSetValueEx(hKey,
  6750. s_szTimeStampValueName,
  6751. 0,
  6752. REG_BINARY,
  6753. reinterpret_cast<LPBYTE>(&fileTime),
  6754. sizeof(fileTime));
  6755. if (ERROR_SUCCESS == lError)
  6756. {
  6757. DWORD dwType;
  6758. if (PathUnExpandEnvStrings(pszShellExecuteCommand, szTemp, ARRAYSIZE(szTemp)) != FALSE)
  6759. {
  6760. dwType = REG_EXPAND_SZ;
  6761. }
  6762. else
  6763. {
  6764. (TCHAR*)lstrcpy(szTemp, pszShellExecuteCommand);
  6765. dwType = REG_SZ;
  6766. }
  6767. lError = RegSetValueEx(hKey,
  6768. s_szApplicationValueName,
  6769. 0,
  6770. REG_SZ, // This is HKLM so it doesn't need to be REG_EXPAND_SZ.
  6771. reinterpret_cast<LPBYTE>(szTemp),
  6772. (lstrlen(szTemp) + sizeof('\0')) * sizeof(TCHAR));
  6773. }
  6774. }
  6775. TW32(RegCloseKey(hKey));
  6776. }
  6777. if (ERROR_SUCCESS == lError)
  6778. {
  6779. hr = S_OK;
  6780. }
  6781. else
  6782. {
  6783. hr = HRESULT_FROM_WIN32(lError);
  6784. }
  6785. }
  6786. else
  6787. {
  6788. hr = E_OUTOFMEMORY;
  6789. }
  6790. return hr;
  6791. }
  6792. #pragma warning(pop)
  6793. // wrapper around name space stuff to parse printer names
  6794. // returns fully qualified pidl for printer
  6795. static HRESULT _ParsePrinterName(LPCTSTR pszPrinter, LPITEMIDLIST *ppidl, IBindCtx *pbc = NULL)
  6796. {
  6797. HRESULT hr = E_INVALIDARG;
  6798. if (ppidl)
  6799. {
  6800. *ppidl = NULL;
  6801. LPITEMIDLIST pidlFolder;
  6802. hr = SHGetFolderLocation(NULL, CSIDL_PRINTERS, NULL, 0, &pidlFolder);
  6803. if (SUCCEEDED(hr))
  6804. {
  6805. IShellFolder *psf;
  6806. hr = SHBindToObject(NULL, IID_X_PPV_ARG(IShellFolder, pidlFolder, &psf));
  6807. if (SUCCEEDED(hr))
  6808. {
  6809. LPITEMIDLIST pidl;
  6810. hr = psf->ParseDisplayName(NULL, pbc, (LPWSTR)pszPrinter, NULL, &pidl, NULL);
  6811. if (SUCCEEDED(hr))
  6812. {
  6813. hr = SHILCombine(pidlFolder, pidl, ppidl);
  6814. ILFree(pidl);
  6815. }
  6816. psf->Release();
  6817. }
  6818. ILFree(pidlFolder);
  6819. }
  6820. }
  6821. return hr;
  6822. }
  6823. STDAPI ParsePrinterName(LPCTSTR pszPrinter, LPITEMIDLIST *ppidl)
  6824. {
  6825. // invoke the internal routine with no bind context
  6826. return _ParsePrinterName(pszPrinter, ppidl, NULL);
  6827. }
  6828. STDAPI ParsePrinterNameEx(LPCTSTR pszPrinter, LPITEMIDLIST *ppidl, BOOL bValidated, DWORD dwType, USHORT uFlags)
  6829. {
  6830. // prepare the bind context
  6831. IPrintersBindInfo *pbi;
  6832. HRESULT hr = Printers_CreateBindInfo(pszPrinter, dwType, bValidated, NULL, &pbi);
  6833. if (SUCCEEDED(hr))
  6834. {
  6835. IBindCtx *pbc;
  6836. hr = CreateBindCtx(0, &pbc);
  6837. if (SUCCEEDED(hr))
  6838. {
  6839. hr = pbc->RegisterObjectParam(PRINTER_BIND_INFO, pbi);
  6840. if (SUCCEEDED(hr))
  6841. {
  6842. // invoke the internal routine with a valid bind context
  6843. hr = _ParsePrinterName(pszPrinter, ppidl, pbc);
  6844. }
  6845. pbc->Release();
  6846. }
  6847. pbi->Release();
  6848. }
  6849. return hr;
  6850. }
  6851. //
  6852. // A function to read a value from the registry and return it as a variant
  6853. // Currently it handles the following registry data types -
  6854. //
  6855. // DWORD -> returns a Variant int
  6856. // REG_SZ, REG_EXPAND_SZ -> returns a Variant str
  6857. //
  6858. STDAPI GetVariantFromRegistryValue(HKEY hkey, LPCTSTR pszValueName, VARIANT *pv)
  6859. {
  6860. HRESULT hr = E_FAIL;
  6861. BYTE ab[INFOTIPSIZE * sizeof(TCHAR)]; // this is the largest string value we expect
  6862. DWORD cb = sizeof(ab), dwType;
  6863. if (ERROR_SUCCESS == RegQueryValueEx(hkey, pszValueName, NULL, &dwType, (LPBYTE) ab, &cb))
  6864. {
  6865. switch (dwType)
  6866. {
  6867. case REG_SZ:
  6868. case REG_EXPAND_SZ:
  6869. hr = InitVariantFromStr(pv, (LPCTSTR) ab);
  6870. break;
  6871. case REG_DWORD:
  6872. pv->vt = VT_I4; // 4 byte integer
  6873. pv->lVal = *((LONG *) ab);
  6874. hr = S_OK;
  6875. break;
  6876. // FEATURE: should add some default handling here;
  6877. }
  6878. }
  6879. return hr;
  6880. }
  6881. STDAPI_(UINT) GetControlCharWidth(HWND hwnd)
  6882. {
  6883. SIZE siz;
  6884. siz.cx = 8; // guess a size in case something goes wrong.
  6885. HDC hdc = GetDC(NULL);
  6886. if (hdc)
  6887. {
  6888. HFONT hfOld = SelectFont(hdc, FORWARD_WM_GETFONT(hwnd, SendMessage));
  6889. if (hfOld)
  6890. {
  6891. GetTextExtentPoint(hdc, TEXT("0"), 1, &siz);
  6892. SelectFont(hdc, hfOld);
  6893. }
  6894. ReleaseDC(NULL, hdc);
  6895. }
  6896. return siz.cx;
  6897. }
  6898. STDAPI_(BOOL) ShowSuperHidden()
  6899. {
  6900. BOOL bRet = FALSE;
  6901. if (!SHRestricted(REST_DONTSHOWSUPERHIDDEN))
  6902. {
  6903. SHELLSTATE ss;
  6904. SHGetSetSettings(&ss, SSF_SHOWSUPERHIDDEN, FALSE);
  6905. bRet = ss.fShowSuperHidden;
  6906. }
  6907. return bRet;
  6908. }
  6909. #define FILE_ATTRIBUTE_SUPERHIDDEN (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN)
  6910. STDAPI_(BOOL) IsSuperHidden(DWORD dwAttribs)
  6911. {
  6912. BOOL bRet = FALSE;
  6913. if (!ShowSuperHidden())
  6914. {
  6915. bRet = (dwAttribs & FILE_ATTRIBUTE_SUPERHIDDEN) == FILE_ATTRIBUTE_SUPERHIDDEN;
  6916. }
  6917. return bRet;
  6918. }
  6919. // make sure LFN paths are nicly quoted and have args at the end
  6920. STDAPI_(void) PathComposeWithArgs(LPTSTR pszPath, LPTSTR pszArgs)
  6921. {
  6922. PathQuoteSpaces(pszPath);
  6923. if (pszArgs[0])
  6924. {
  6925. int len = lstrlen(pszPath);
  6926. if (len < (MAX_PATH - 3))
  6927. { // 1 for null, 1 for space, 1 for arg
  6928. pszPath[len++] = TEXT(' ');
  6929. lstrcpyn(pszPath + len, pszArgs, MAX_PATH - len);
  6930. }
  6931. }
  6932. }
  6933. // do the inverse of the above, parse pszPath into a unquoted
  6934. // path string and put the args in pszArgs
  6935. //
  6936. // returns:
  6937. // TRUE we verified the thing exists
  6938. // FALSE it may not exist
  6939. STDAPI_(BOOL) PathSeperateArgs(LPTSTR pszPath, LPTSTR pszArgs)
  6940. {
  6941. ASSERT(pszPath);
  6942. if (!pszPath)
  6943. return FALSE;//invalid args
  6944. PathRemoveBlanks(pszPath);
  6945. // if the unquoted sting exists as a file just use it
  6946. if (PathFileExistsAndAttributes(pszPath, NULL))
  6947. {
  6948. if (pszArgs)
  6949. *pszArgs = 0;
  6950. return TRUE;
  6951. }
  6952. LPTSTR pszT = PathGetArgs(pszPath);
  6953. if (*pszT)
  6954. *(pszT - 1) = 0;
  6955. if (pszArgs)
  6956. lstrcpy(pszArgs, pszT);
  6957. PathUnquoteSpaces(pszPath);
  6958. return FALSE;
  6959. }
  6960. STDAPI_(int) CompareIDsAlphabetical(IShellFolder2 *psf, UINT iColumn, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  6961. {
  6962. int iRes = 0;
  6963. LPITEMIDLIST pidlFirst1 = ILCloneFirst(pidl1);
  6964. LPITEMIDLIST pidlFirst2 = ILCloneFirst(pidl2);
  6965. if (pidlFirst1 && pidlFirst2)
  6966. {
  6967. TCHAR szName1[MAX_PATH], szName2[MAX_PATH];
  6968. HRESULT hr = DetailsOf(psf, pidlFirst1, iColumn, szName1, ARRAYSIZE(szName1));
  6969. if (SUCCEEDED(hr))
  6970. {
  6971. hr = DetailsOf(psf, pidlFirst2, iColumn, szName2, ARRAYSIZE(szName2));
  6972. if (SUCCEEDED(hr))
  6973. iRes = StrCmpLogicalRestricted(szName1, szName2);
  6974. }
  6975. if (FAILED(hr))
  6976. {
  6977. // revert to compare of names
  6978. hr = DisplayNameOf(psf, pidlFirst1, SHGDN_NORMAL, szName1, ARRAYSIZE(szName1));
  6979. if (SUCCEEDED(hr))
  6980. {
  6981. hr = DisplayNameOf(psf, pidlFirst2, SHGDN_NORMAL, szName2, ARRAYSIZE(szName2));
  6982. if (SUCCEEDED(hr))
  6983. iRes = StrCmpLogicalRestricted(szName1, szName2);
  6984. }
  6985. }
  6986. }
  6987. ILFree(pidlFirst1);
  6988. ILFree(pidlFirst2);
  6989. return iRes;
  6990. }
  6991. HMONITOR GetPrimaryMonitor()
  6992. {
  6993. POINT pt = {0,0};
  6994. return MonitorFromPoint(pt, MONITOR_DEFAULTTOPRIMARY);
  6995. }
  6996. // Gets the Monitor's bounding or work rectangle, if the hMon is bad, return
  6997. // the primary monitor's bounding rectangle.
  6998. BOOL GetMonitorRects(HMONITOR hMon, LPRECT prc, BOOL bWork)
  6999. {
  7000. MONITORINFO mi;
  7001. mi.cbSize = sizeof(mi);
  7002. if (hMon && GetMonitorInfo(hMon, &mi))
  7003. {
  7004. if (!prc)
  7005. return TRUE;
  7006. else if (bWork)
  7007. CopyRect(prc, &mi.rcWork);
  7008. else
  7009. CopyRect(prc, &mi.rcMonitor);
  7010. return TRUE;
  7011. }
  7012. if (prc)
  7013. SetRect(prc, 0, 0, GetSystemMetrics(SM_CXSCREEN), GetSystemMetrics(SM_CYSCREEN));
  7014. return FALSE;
  7015. }
  7016. STDAPI StatStgFromFindData(const WIN32_FIND_DATA * pfd, DWORD dwFlags, STATSTG * pstat)
  7017. {
  7018. HRESULT hr = S_OK;
  7019. if (dwFlags & STATFLAG_NONAME)
  7020. {
  7021. pstat->pwcsName = NULL;
  7022. }
  7023. else
  7024. {
  7025. hr = SHStrDup(pfd->cFileName, &pstat->pwcsName);
  7026. }
  7027. pstat->type = (pfd->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) ? STGTY_STORAGE : STGTY_STREAM;
  7028. pstat->cbSize.HighPart = pfd->nFileSizeHigh;
  7029. pstat->cbSize.LowPart = pfd->nFileSizeLow;
  7030. pstat->mtime = pfd->ftLastWriteTime;
  7031. pstat->ctime = pfd->ftCreationTime;
  7032. pstat->atime = pfd->ftLastAccessTime;
  7033. pstat->grfMode = 0;
  7034. pstat->grfLocksSupported = 0;
  7035. pstat->clsid = CLSID_NULL;
  7036. pstat->grfStateBits = 0;
  7037. pstat->reserved = pfd->dwFileAttributes;
  7038. return hr;
  7039. }
  7040. // see if the QueryService chain will hit the desktop browser (the desktop window)
  7041. STDAPI_(BOOL) IsDesktopBrowser(IUnknown *punkSite)
  7042. {
  7043. BOOL bRet = FALSE;
  7044. IUnknown *punk;
  7045. if (SUCCEEDED(IUnknown_QueryService(punkSite, SID_SShellDesktop, IID_PPV_ARG(IUnknown, &punk))))
  7046. {
  7047. punk->Release();
  7048. bRet = TRUE; // It's the actual desktop!
  7049. }
  7050. return bRet;
  7051. }
  7052. // calculates the maximum allowable char length for a given path.
  7053. // i.e. c:\winnt\system\bob
  7054. // this will return the maximum size the filename for bob can be changed to.
  7055. STDAPI GetCCHMaxFromPath(LPCTSTR pszFullPath, UINT *pcchMax, BOOL fShowExtension)
  7056. {
  7057. TCHAR szPath[MAX_PATH];
  7058. lstrcpy(szPath, pszFullPath);
  7059. // Now make sure that that size is valid for the
  7060. // type of drive that we are talking to
  7061. PathRemoveFileSpec(szPath);
  7062. //
  7063. // Get the maximum file name length.
  7064. // MAX_PATH - ('\\' + '\0') - lstrlen(szParent)
  7065. //
  7066. *pcchMax = MAX_PATH - 2 - lstrlen(szPath);
  7067. UINT cchCur = lstrlen(pszFullPath) - lstrlen(szPath) - 1;
  7068. PathStripToRoot(szPath);
  7069. DWORD dwMaxLength;
  7070. if (GetVolumeInformation(szPath, NULL, 0, NULL, &dwMaxLength, NULL, NULL, 0))
  7071. {
  7072. if (*pcchMax > (int)dwMaxLength)
  7073. {
  7074. // can't be loner than the FS allows
  7075. *pcchMax = (int)dwMaxLength;
  7076. }
  7077. }
  7078. else
  7079. {
  7080. dwMaxLength = 255; // Assume LFN for now...
  7081. }
  7082. // only do this if we're not on an 8.3 filesystem already.
  7083. if ((dwMaxLength > 12) && (GetFileAttributes(pszFullPath) & FILE_ATTRIBUTE_DIRECTORY))
  7084. {
  7085. // On NT, a directory must be able to contain an
  7086. // 8.3 name and STILL be less than MAX_PATH. The
  7087. // "12" below is the length of an 8.3 name (8+1+3).
  7088. // on win9x, we make sure that there is enough room
  7089. // to append \*.* in the directory case.
  7090. *pcchMax -= 12;
  7091. }
  7092. // If our code above restricted smaller than current size reset
  7093. // back to current size...
  7094. if (*pcchMax < cchCur)
  7095. {
  7096. // NOTE: this is bogus. if we ended up with a max smaller than what
  7097. // we currently have, the we are clearly doing something wrong!
  7098. *pcchMax = cchCur;
  7099. }
  7100. //
  7101. // Adjust the cchMax if we are hiding the extension
  7102. //
  7103. if (!fShowExtension)
  7104. {
  7105. *pcchMax -= lstrlen(PathFindExtension(pszFullPath));
  7106. if ((dwMaxLength <= 12) && (*pcchMax > 8))
  7107. {
  7108. // if max filesys path is <=12, then assume we are dealing w/ 8.3 names
  7109. // and since we are not showing the extension, cap the filename at 8.
  7110. *pcchMax = 8;
  7111. }
  7112. }
  7113. ASSERT((int)*pcchMax > 0);
  7114. return S_OK;
  7115. }
  7116. STDAPI ViewModeFromSVID(const SHELLVIEWID *pvid, FOLDERVIEWMODE *pViewMode)
  7117. {
  7118. HRESULT hr = S_OK;
  7119. if (IsEqualIID(*pvid, VID_LargeIcons))
  7120. *pViewMode = FVM_ICON;
  7121. else if (IsEqualIID(*pvid, VID_SmallIcons))
  7122. *pViewMode = FVM_SMALLICON;
  7123. else if (IsEqualIID(*pvid, VID_Thumbnails))
  7124. *pViewMode = FVM_THUMBNAIL;
  7125. else if (IsEqualIID(*pvid, VID_ThumbStrip))
  7126. *pViewMode = FVM_THUMBSTRIP;
  7127. else if (IsEqualIID(*pvid, VID_List))
  7128. *pViewMode = FVM_LIST;
  7129. else if (IsEqualIID(*pvid, VID_Tile))
  7130. *pViewMode = FVM_TILE;
  7131. else if (IsEqualIID(*pvid, VID_Details))
  7132. *pViewMode = FVM_DETAILS;
  7133. else
  7134. {
  7135. if (IsEqualIID(*pvid, VID_WebView))
  7136. TraceMsg(TF_WARNING, "ViewModeFromSVID received VID_WebView");
  7137. else
  7138. TraceMsg(TF_WARNING, "ViewModeFromSVID received unknown VID");
  7139. *pViewMode = FVM_ICON;
  7140. hr = E_FAIL;
  7141. }
  7142. return hr;
  7143. }
  7144. STDAPI SVIDFromViewMode(FOLDERVIEWMODE uViewMode, SHELLVIEWID *psvid)
  7145. {
  7146. switch (uViewMode)
  7147. {
  7148. case FVM_ICON:
  7149. *psvid = VID_LargeIcons;
  7150. break;
  7151. case FVM_SMALLICON:
  7152. *psvid = VID_SmallIcons;
  7153. break;
  7154. case FVM_LIST:
  7155. *psvid = VID_List;
  7156. break;
  7157. case FVM_DETAILS:
  7158. *psvid = VID_Details;
  7159. break;
  7160. case FVM_THUMBNAIL:
  7161. *psvid = VID_Thumbnails;
  7162. break;
  7163. case FVM_TILE:
  7164. *psvid = VID_Tile;
  7165. break;
  7166. case FVM_THUMBSTRIP:
  7167. *psvid = VID_ThumbStrip;
  7168. break;
  7169. default:
  7170. TraceMsg(TF_ERROR, "SVIDFromViewMode given invalid uViewMode!");
  7171. *psvid = VID_LargeIcons;
  7172. break;
  7173. }
  7174. return S_OK;
  7175. }
  7176. // modify the SLDF_ link flags via new bits + a mask, return the old flags
  7177. STDAPI_(DWORD) SetLinkFlags(IShellLink *psl, DWORD dwFlags, DWORD dwMask)
  7178. {
  7179. DWORD dwOldFlags = 0;
  7180. IShellLinkDataList *psld;
  7181. if (SUCCEEDED(psl->QueryInterface(IID_PPV_ARG(IShellLinkDataList, &psld))))
  7182. {
  7183. if (SUCCEEDED(psld->GetFlags(&dwOldFlags)))
  7184. {
  7185. if (dwMask)
  7186. psld->SetFlags((dwFlags & dwMask) | (dwOldFlags & ~dwMask));
  7187. }
  7188. psld->Release();
  7189. }
  7190. return dwOldFlags; // return the previous value
  7191. }
  7192. // Helper function to compare the 2 variants and return the standard
  7193. // C style (-1, 0, 1) value for the comparison.
  7194. STDAPI_(int) CompareVariants(VARIANT va1, VARIANT va2)
  7195. {
  7196. int iRetVal = 0;
  7197. if (va1.vt == VT_EMPTY)
  7198. {
  7199. if (va2.vt == VT_EMPTY)
  7200. iRetVal = 0;
  7201. else
  7202. iRetVal = 1;
  7203. }
  7204. else if (va2.vt == VT_EMPTY)
  7205. {
  7206. if (va1.vt == VT_EMPTY)
  7207. iRetVal = 0;
  7208. else
  7209. iRetVal = -1;
  7210. }
  7211. else
  7212. {
  7213. // Special case becasue VarCmp cannot handle ULONGLONG
  7214. if (va1.vt == VT_UI8 && va2.vt == VT_UI8)
  7215. {
  7216. if (va1.ullVal < va2.ullVal)
  7217. iRetVal = -1;
  7218. else if (va1.ullVal > va2.ullVal)
  7219. iRetVal = 1;
  7220. }
  7221. else if (va1.vt == VT_BSTR && va2.vt == VT_BSTR)
  7222. {
  7223. iRetVal = StrCmpLogicalRestricted(va1.bstrVal, va2.bstrVal);
  7224. }
  7225. else
  7226. {
  7227. HRESULT hr = VarCmp(&va1, &va2, GetUserDefaultLCID(), 0);
  7228. if (SUCCEEDED(hr))
  7229. {
  7230. // Translate the result into the C convention...
  7231. ASSERT(hr != VARCMP_NULL);
  7232. iRetVal = hr - VARCMP_EQ;
  7233. }
  7234. }
  7235. }
  7236. return iRetVal;
  7237. }
  7238. // Check which of the pidl's passed in represent a folder. If only one
  7239. // is a folder, then put it first.
  7240. STDAPI_(int) CompareFolderness(IShellFolder *psf, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  7241. {
  7242. BOOL bIsFolder1 = SHGetAttributes(psf, pidl1, SFGAO_FOLDER | SFGAO_STREAM) == SFGAO_FOLDER;
  7243. BOOL bIsFolder2 = SHGetAttributes(psf, pidl2, SFGAO_FOLDER | SFGAO_STREAM) == SFGAO_FOLDER;
  7244. int iRetVal;
  7245. if (bIsFolder1 && !bIsFolder2)
  7246. {
  7247. iRetVal = -1; // Don't swap
  7248. }
  7249. else if (!bIsFolder1 && bIsFolder2)
  7250. {
  7251. iRetVal = 1; // Swap
  7252. }
  7253. else
  7254. {
  7255. iRetVal = 0; // equal
  7256. }
  7257. return iRetVal;
  7258. }
  7259. // Compare the two items using GetDetailsEx, it is assumed that folderness has been established already
  7260. // Return -1 if pidl1 comes before pidl2,
  7261. // 0 if the same
  7262. // 1 if pidl2 comes before pidl1
  7263. STDAPI_(int) CompareBySCID(IShellFolder2 *psf, const SHCOLUMNID *pscid, LPCITEMIDLIST pidl1, LPCITEMIDLIST pidl2)
  7264. {
  7265. // ignore failures here, leaving VT_EMPTY that we compare below
  7266. VARIANT v1 = {0}, v2 = {0};
  7267. psf->GetDetailsEx(pidl1, pscid, &v1);
  7268. psf->GetDetailsEx(pidl2, pscid, &v2);
  7269. int iRet = CompareVariants(v1, v2);
  7270. VariantClear(&v2);
  7271. VariantClear(&v1);
  7272. return iRet;
  7273. }
  7274. //
  7275. // Determines if a filename is that of a regitem
  7276. //
  7277. // a regitem's SHGDN_INFOLDER | SHGDN_FORPARSING name is always "::{someguid}"
  7278. //
  7279. // This test can lead to false positives if you have other items which have infolder
  7280. // parsing names beginning with "::{", but as ':' is not presently allowed in filenames
  7281. // it should not be a problem.
  7282. //
  7283. STDAPI_(BOOL) IsRegItemName(LPCTSTR pszName, CLSID* pclsid)
  7284. {
  7285. BOOL fRetVal = FALSE;
  7286. if (pszName && lstrlen(pszName) >= 3)
  7287. {
  7288. if (pszName[0] == TEXT(':') && pszName[1] == TEXT(':') && pszName[2] == TEXT('{'))
  7289. {
  7290. CLSID clsid;
  7291. fRetVal = GUIDFromString(pszName + 2, &clsid); // skip the leading :: before regitem
  7292. if (pclsid)
  7293. {
  7294. memcpy(pclsid, &clsid, sizeof(clsid));
  7295. }
  7296. }
  7297. }
  7298. return fRetVal;
  7299. }
  7300. STDAPI GetMyDocumentsDisplayName(LPTSTR pszPath, UINT cch)
  7301. {
  7302. *pszPath = 0;
  7303. LPITEMIDLIST pidl;
  7304. if (SUCCEEDED(SHGetFolderLocation(NULL, CSIDL_PERSONAL, NULL, 0, &pidl)))
  7305. {
  7306. SHGetNameAndFlags(pidl, SHGDN_NORMAL, pszPath, cch, NULL);
  7307. ILFree(pidl);
  7308. }
  7309. return *pszPath ? S_OK : E_FAIL;
  7310. }
  7311. STDAPI BSTRFromCLSID(REFCLSID clsid, BSTR *pbstr)
  7312. {
  7313. WCHAR sz[GUIDSTR_MAX + 1];
  7314. // Get the File/Folders search guid in string form
  7315. SHStringFromGUIDW(clsid, sz, ARRAYSIZE(sz));
  7316. *pbstr = SysAllocString(sz);
  7317. return *pbstr ? S_OK : E_OUTOFMEMORY;
  7318. }
  7319. // [in] pdo -- data object we're interested in
  7320. // [in] dwAttributeMask -- the bits we want to know about
  7321. // [out,optional] pdwAttributes -- write the bits of dwAttributeMask that we calculate
  7322. // [out,optional] pcItems -- count of pidls in pdo
  7323. //
  7324. // returns S_FALSE only if the dataobject doesn't support HIDA, used to help out defview for legacy cases.
  7325. // in general callers dont have to check for success/failure and they can use the dwAttribs, it's zero-inited.
  7326. HRESULT SHGetAttributesFromDataObject(IDataObject *pdo, DWORD dwAttributeMask, DWORD *pdwAttributes, UINT *pcItems)
  7327. {
  7328. HRESULT hr = S_OK;
  7329. DWORD dwAttributes = 0;
  7330. DWORD cItems = 0;
  7331. // These are all the bits we regularly ask for:
  7332. #define TASK_ATTRIBUTES (SFGAO_READONLY|SFGAO_STORAGE|SFGAO_CANRENAME|SFGAO_CANMOVE|SFGAO_CANCOPY|SFGAO_CANDELETE|SFGAO_FOLDER|SFGAO_STREAM)
  7333. if ((dwAttributeMask&TASK_ATTRIBUTES) != dwAttributeMask)
  7334. {
  7335. TraceMsg(TF_WARNING, "SHGetAttributesFromDataObject cache can be more efficient");
  7336. }
  7337. if (pdo)
  7338. {
  7339. // We cache the attributes on the data object back in the data object,
  7340. // since we call for them so many times.
  7341. //
  7342. // To do this we need to remember:
  7343. struct {
  7344. DWORD dwRequested;
  7345. DWORD dwReceived;
  7346. UINT cItems;
  7347. } doAttributes = {0};
  7348. static UINT s_cfDataObjectAttributes = 0;
  7349. if (0 == s_cfDataObjectAttributes)
  7350. s_cfDataObjectAttributes = RegisterClipboardFormat(TEXT("DataObjectAttributes"));
  7351. if (FAILED(DataObj_GetBlob(pdo, s_cfDataObjectAttributes, &doAttributes, sizeof(doAttributes))) ||
  7352. ((doAttributes.dwRequested & dwAttributeMask) != dwAttributeMask))
  7353. {
  7354. // If we fail along the way, cache that we tried to ask for these bits,
  7355. // since we'll probably fail next time.
  7356. //
  7357. // Also, always ask for a superset of bits, and include the most commonly requested ones too
  7358. //
  7359. doAttributes.dwRequested |= (dwAttributeMask | TASK_ATTRIBUTES);
  7360. // try to get the attributes requested
  7361. STGMEDIUM medium = {0};
  7362. LPIDA pida = DataObj_GetHIDA(pdo, &medium);
  7363. if (pida)
  7364. {
  7365. doAttributes.cItems = pida->cidl;
  7366. if (pida->cidl >= 1)
  7367. {
  7368. IShellFolder* psf;
  7369. if (SUCCEEDED(SHBindToObjectEx(NULL, HIDA_GetPIDLFolder(pida), NULL, IID_PPV_ARG(IShellFolder, &psf))))
  7370. {
  7371. // who cares if we get the wrong bits when there are a bunch of items - check the first 10 or so...
  7372. LPCITEMIDLIST apidl[10];
  7373. UINT cItems = (UINT)min(pida->cidl, ARRAYSIZE(apidl));
  7374. for (UINT i = 0 ; i < cItems ; i++)
  7375. {
  7376. apidl[i] = HIDA_GetPIDLItem(pida, i);
  7377. if (ILGetNext(ILGetNext(apidl[i])))
  7378. {
  7379. // search namespace has non-flat HIDA, which is probably a bug.
  7380. // work around it here:
  7381. // if the first item is non-flat, use that one item for attributes
  7382. // otherwise use the flat items already enumerated
  7383. //
  7384. IShellFolder* psfNew;
  7385. if (0==i &&
  7386. (SUCCEEDED(SHBindToFolderIDListParent(psf, apidl[i], IID_PPV_ARG(IShellFolder, &psfNew), &(apidl[i])))))
  7387. {
  7388. psf->Release();
  7389. psf = psfNew;
  7390. cItems = 1;
  7391. }
  7392. else
  7393. {
  7394. cItems = i;
  7395. }
  7396. break;
  7397. }
  7398. }
  7399. DWORD dwAttribs = doAttributes.dwRequested;
  7400. if (SUCCEEDED(psf->GetAttributesOf(cItems, apidl, &dwAttribs)))
  7401. {
  7402. doAttributes.dwReceived = dwAttribs;
  7403. }
  7404. psf->Release();
  7405. }
  7406. }
  7407. HIDA_ReleaseStgMedium(pida, &medium);
  7408. }
  7409. else
  7410. {
  7411. hr = S_FALSE;
  7412. }
  7413. DataObj_SetBlob(pdo, s_cfDataObjectAttributes, &doAttributes, sizeof(doAttributes));
  7414. }
  7415. dwAttributes = doAttributes.dwReceived & dwAttributeMask;
  7416. cItems = doAttributes.cItems;
  7417. }
  7418. if (pdwAttributes)
  7419. *pdwAttributes = dwAttributes;
  7420. if (pcItems)
  7421. *pcItems = cItems;
  7422. return hr;
  7423. }
  7424. STDAPI SHSimulateDropOnClsid(REFCLSID clsidDrop, IUnknown* punkSite, IDataObject* pdo)
  7425. {
  7426. IDropTarget* pdt;
  7427. HRESULT hr = SHExtCoCreateInstance2(NULL, &clsidDrop, NULL, CLSCTX_ALL, IID_PPV_ARG(IDropTarget, &pdt));
  7428. if (SUCCEEDED(hr))
  7429. {
  7430. hr = SHSimulateDropWithSite(pdt, pdo, 0, NULL, NULL, punkSite);
  7431. pdt->Release();
  7432. }
  7433. return hr;
  7434. }
  7435. STDAPI SHPropertiesForUnk(HWND hwnd, IUnknown *punk, LPCTSTR psz)
  7436. {
  7437. HRESULT hr;
  7438. LPITEMIDLIST pidl;
  7439. if (S_OK == SHGetIDListFromUnk(punk, &pidl))
  7440. {
  7441. hr = SHPropertiesForPidl(hwnd, pidl, psz);
  7442. ILFree(pidl);
  7443. }
  7444. else
  7445. hr = E_FAIL;
  7446. return hr;
  7447. }
  7448. STDAPI SHFullIDListFromFolderAndItem(IShellFolder *psf, LPCITEMIDLIST pidl, LPITEMIDLIST *ppidl)
  7449. {
  7450. *ppidl = NULL;
  7451. LPITEMIDLIST pidlFolder;
  7452. HRESULT hr = SHGetIDListFromUnk(psf, &pidlFolder);
  7453. if (SUCCEEDED(hr))
  7454. {
  7455. hr = SHILCombine(pidlFolder, pidl, ppidl);
  7456. ILFree(pidlFolder);
  7457. }
  7458. return hr;
  7459. }
  7460. STDAPI_(BOOL) IsWindowClass(HWND hwndTest, LPCTSTR pszClass)
  7461. {
  7462. TCHAR szClass[128];
  7463. if (pszClass && GetClassName(hwndTest, szClass, ARRAYSIZE(szClass)))
  7464. return 0 == lstrcmpi(pszClass, szClass);
  7465. return FALSE;
  7466. }
  7467. STDAPI DCA_ExtCreateInstance(HDCA hdca, int iItem, REFIID riid, void **ppv)
  7468. {
  7469. const CLSID * pclsid = DCA_GetItem(hdca, iItem);
  7470. return pclsid ? SHExtCoCreateInstance(NULL, pclsid, NULL, riid, ppv) : E_INVALIDARG;
  7471. }
  7472. STDAPI_(HINSTANCE) SHGetShellStyleHInstance (void)
  7473. {
  7474. TCHAR szDir[MAX_PATH];
  7475. TCHAR szColor[100];
  7476. HINSTANCE hInst = NULL;
  7477. LPTSTR lpFullPath;
  7478. //
  7479. // First try to load shellstyle.dll from the theme (taking into account color variations)
  7480. //
  7481. if (SUCCEEDED(GetCurrentThemeName(szDir, ARRAYSIZE(szDir), szColor, ARRAYSIZE(szColor), NULL, NULL)))
  7482. {
  7483. PathRemoveFileSpec(szDir);
  7484. lpFullPath = (LPTSTR) LocalAlloc (LPTR, (lstrlen(szDir) + lstrlen(szColor) + 25) * sizeof(TCHAR));
  7485. if (lpFullPath)
  7486. {
  7487. lstrcpy (lpFullPath, szDir);
  7488. lstrcat (lpFullPath, TEXT("\\Shell\\"));
  7489. lstrcat (lpFullPath, szColor);
  7490. lstrcat (lpFullPath, TEXT("\\ShellStyle.dll"));
  7491. hInst = LoadLibraryEx(lpFullPath, NULL, LOAD_LIBRARY_AS_DATAFILE);
  7492. LocalFree (lpFullPath);
  7493. }
  7494. }
  7495. //
  7496. // If shellstyle.dll couldn't be loaded from the theme, load the default (classic)
  7497. // version from system32.
  7498. //
  7499. if (!hInst)
  7500. {
  7501. if (ExpandEnvironmentStrings (TEXT("%SystemRoot%\\System32\\ShellStyle.dll"),
  7502. szDir, ARRAYSIZE(szDir)))
  7503. {
  7504. hInst = LoadLibraryEx(szDir, NULL, LOAD_LIBRARY_AS_DATAFILE);
  7505. }
  7506. }
  7507. return hInst;
  7508. }
  7509. // adjust an infotip object in a custom way for the folder
  7510. // poke in an extra property to be displayed and wrap the object with the
  7511. // delegating outter folder if needed
  7512. STDAPI WrapInfotip(IShellFolder *psf, LPCITEMIDLIST pidl, const SHCOLUMNID *pscid, IUnknown *punk)
  7513. {
  7514. HRESULT hr = S_OK;
  7515. if (pscid)
  7516. {
  7517. ICustomizeInfoTip *pcit;
  7518. hr = punk->QueryInterface(IID_PPV_ARG(ICustomizeInfoTip, &pcit));
  7519. if (SUCCEEDED(hr))
  7520. {
  7521. hr = pcit->SetExtraProperties(pscid, 1);
  7522. pcit->Release();
  7523. }
  7524. }
  7525. if (psf && pidl)
  7526. {
  7527. IParentAndItem *ppai;
  7528. hr = punk->QueryInterface(IID_PPV_ARG(IParentAndItem, &ppai));
  7529. if (SUCCEEDED(hr))
  7530. {
  7531. ppai->SetParentAndItem(NULL, psf, pidl);
  7532. ppai->Release();
  7533. }
  7534. }
  7535. return hr;
  7536. }
  7537. STDAPI CloneIDListArray(UINT cidl, const LPCITEMIDLIST rgpidl[], UINT *pcidl, LPITEMIDLIST **papidl)
  7538. {
  7539. HRESULT hr;
  7540. LPITEMIDLIST *ppidl;
  7541. if (cidl && rgpidl)
  7542. {
  7543. ppidl = (LPITEMIDLIST *)LocalAlloc(LPTR, cidl * sizeof(*ppidl));
  7544. if (ppidl)
  7545. {
  7546. hr = S_OK;
  7547. for (UINT i = 0; i < cidl && SUCCEEDED(hr); i++)
  7548. {
  7549. hr = SHILClone(rgpidl[i], &ppidl[i]);
  7550. if (FAILED(hr))
  7551. {
  7552. FreeIDListArray(ppidl, i);
  7553. ppidl = NULL;
  7554. }
  7555. }
  7556. }
  7557. else
  7558. hr = E_OUTOFMEMORY;
  7559. }
  7560. else
  7561. {
  7562. ppidl = NULL;
  7563. hr = S_FALSE; // success by empty
  7564. }
  7565. *papidl = ppidl;
  7566. *pcidl = SUCCEEDED(hr) ? cidl : 0;
  7567. return hr;
  7568. }
  7569. BOOL RunWindowCallback(VARIANT var, ENUMSHELLWINPROC pEnumFunc, LPARAM lParam)
  7570. {
  7571. BOOL fKeepGoing = TRUE;
  7572. IShellBrowser *psb;
  7573. if (SUCCEEDED(IUnknown_QueryService(var.pdispVal, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
  7574. {
  7575. IShellView *psv;
  7576. if (SUCCEEDED(psb->QueryActiveShellView(&psv)))
  7577. {
  7578. HWND hwnd;
  7579. if (SUCCEEDED(psv->GetWindow(&hwnd)))
  7580. {
  7581. IPersistIDList *pPI;
  7582. if (SUCCEEDED(psv->QueryInterface(IID_PPV_ARG(IPersistIDList, &pPI))))
  7583. {
  7584. LPITEMIDLIST pidl;
  7585. if (SUCCEEDED(pPI->GetIDList(&pidl)))
  7586. {
  7587. fKeepGoing = pEnumFunc(hwnd, pidl, lParam);
  7588. ILFree(pidl);
  7589. }
  7590. pPI->Release();
  7591. }
  7592. }
  7593. psv->Release();
  7594. }
  7595. psb->Release();
  7596. }
  7597. return fKeepGoing;
  7598. }
  7599. // runs through all open shell browser windows and runs a caller-defined function on the hwnd and pidl.
  7600. STDAPI EnumShellWindows(ENUMSHELLWINPROC pEnumFunc, LPARAM lParam)
  7601. {
  7602. HRESULT hr;
  7603. IShellWindows *psw = WinList_GetShellWindows(TRUE);
  7604. if (psw)
  7605. {
  7606. IUnknown *punk;
  7607. hr = psw->_NewEnum(&punk);
  7608. if (SUCCEEDED(hr))
  7609. {
  7610. IEnumVARIANT *penum;
  7611. hr = punk->QueryInterface(IID_PPV_ARG(IEnumVARIANT, &penum));
  7612. if (SUCCEEDED(hr))
  7613. {
  7614. VARIANT var;
  7615. VariantInit(&var);
  7616. BOOL fKeepGoing = TRUE;
  7617. while (fKeepGoing && (S_OK == penum->Next(1, &var, NULL)))
  7618. {
  7619. ASSERT(var.vt == VT_DISPATCH);
  7620. ASSERT(var.pdispVal);
  7621. fKeepGoing = RunWindowCallback(var, pEnumFunc, lParam);
  7622. VariantClear(&var);
  7623. }
  7624. penum->Release();
  7625. }
  7626. punk->Release();
  7627. }
  7628. psw->Release();
  7629. }
  7630. else
  7631. {
  7632. hr = E_OUTOFMEMORY;
  7633. }
  7634. return hr;
  7635. }
  7636. // Determine if infotips are on or off (from the registry settings).
  7637. //
  7638. BOOL SHShowInfotips()
  7639. {
  7640. // REVIEW (buzzr): Is it necessary to force a refresh every time?
  7641. SHELLSTATE ss;
  7642. SHRefreshSettings();
  7643. SHGetSetSettings(&ss, SSF_SHOWINFOTIP, FALSE);
  7644. return ss.fShowInfoTip;
  7645. }
  7646. HRESULT SHCreateInfotipWindow(HWND hwndParent, LPWSTR pszInfotip, HWND *phwndInfotip)
  7647. {
  7648. HRESULT hr;
  7649. if (hwndParent && IsWindow(hwndParent))
  7650. {
  7651. DWORD dwExStyle = 0;
  7652. if (IS_WINDOW_RTL_MIRRORED(hwndParent) || IS_BIDI_LOCALIZED_SYSTEM())
  7653. {
  7654. dwExStyle = WS_EX_LAYOUTRTL;
  7655. }
  7656. *phwndInfotip = CreateWindowEx(dwExStyle,
  7657. TOOLTIPS_CLASS,
  7658. NULL,
  7659. WS_POPUP | TTS_NOPREFIX | TTS_ALWAYSTIP,
  7660. CW_USEDEFAULT,
  7661. CW_USEDEFAULT,
  7662. CW_USEDEFAULT,
  7663. CW_USEDEFAULT,
  7664. hwndParent,
  7665. NULL,
  7666. HINST_THISDLL,
  7667. NULL);
  7668. if (*phwndInfotip)
  7669. {
  7670. SetWindowPos(*phwndInfotip,
  7671. HWND_TOPMOST,
  7672. 0, 0, 0, 0,
  7673. SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
  7674. TOOLINFOW ti;
  7675. ZeroMemory(&ti, sizeof(ti));
  7676. ti.cbSize = sizeof(ti);
  7677. ti.uFlags = TTF_SUBCLASS;
  7678. ti.hwnd = hwndParent;
  7679. ti.hinst = HINST_THISDLL;
  7680. ti.lpszText = pszInfotip;
  7681. GetClientRect(hwndParent, &ti.rect);
  7682. if (SendMessage(*phwndInfotip, TTM_ADDTOOL, 0, (LPARAM)&ti))
  7683. {
  7684. static const RECT rcMargin = { 2, 2, 2, 2 };
  7685. SendMessage(*phwndInfotip, TTM_SETMARGIN, 0, (LPARAM)&rcMargin);
  7686. //
  7687. // Set the initial delay time to 2 times the default.
  7688. // Set the auto-pop time to a very large value.
  7689. // These are the same parameters used by defview for it's tooltips.
  7690. //
  7691. LRESULT uiShowTime = SendMessage(*phwndInfotip, TTM_GETDELAYTIME, TTDT_INITIAL, 0);
  7692. SendMessage(*phwndInfotip, TTM_SETDELAYTIME, TTDT_INITIAL, MAKELONG(uiShowTime * 2, 0));
  7693. SendMessage(*phwndInfotip, TTM_SETDELAYTIME, TTDT_AUTOPOP, (LPARAM)MAXSHORT);
  7694. SendMessage(*phwndInfotip, TTM_SETMAXTIPWIDTH, 0, 300);
  7695. hr = S_OK;
  7696. }
  7697. else
  7698. hr = ResultFromLastError();
  7699. }
  7700. else
  7701. hr = ResultFromLastError();
  7702. }
  7703. else
  7704. hr = E_INVALIDARG;
  7705. return THR(hr);
  7706. }
  7707. HRESULT SHShowInfotipWindow(HWND hwndInfotip, BOOL bShow)
  7708. {
  7709. HRESULT hr;
  7710. if (hwndInfotip && IsWindow(hwndInfotip))
  7711. {
  7712. SendMessage(hwndInfotip, TTM_ACTIVATE, (WPARAM)bShow, 0);
  7713. hr = S_OK;
  7714. }
  7715. else
  7716. hr = E_INVALIDARG;
  7717. return THR(hr);
  7718. }
  7719. HRESULT SHDestroyInfotipWindow(HWND *phwndInfotip)
  7720. {
  7721. HRESULT hr;
  7722. if (*phwndInfotip)
  7723. {
  7724. if (IsWindow(*phwndInfotip))
  7725. {
  7726. if (DestroyWindow(*phwndInfotip))
  7727. {
  7728. *phwndInfotip = NULL;
  7729. hr = S_OK;
  7730. }
  7731. else
  7732. hr = ResultFromLastError();
  7733. }
  7734. else
  7735. hr = S_OK;
  7736. }
  7737. else
  7738. hr = E_INVALIDARG;
  7739. return THR(hr);
  7740. }
  7741. // we hide wizards in defview iff we are in explorer and webview is on
  7742. STDAPI SHShouldShowWizards(IUnknown *punksite)
  7743. {
  7744. HRESULT hr = S_OK; // assume yes
  7745. IShellBrowser* psb;
  7746. if (SUCCEEDED(IUnknown_QueryService(punksite, SID_STopWindow, IID_PPV_ARG(IShellBrowser, &psb))))
  7747. {
  7748. SHELLSTATE ss;
  7749. SHGetSetSettings(&ss, SSF_WEBVIEW, FALSE);
  7750. if (ss.fWebView)
  7751. {
  7752. if (SHRegGetBoolUSValueW(REGSTR_EXPLORER_ADVANCED, TEXT("ShowWizardsTEST"),
  7753. FALSE, // Don't ignore HKCU
  7754. FALSE)) // By default we assume we're not using these test tools
  7755. {
  7756. // Test teams that have old test tools that don't know how to talk to DUI need a way
  7757. // to force the legacy wizards back into the listview to keep their automation working
  7758. }
  7759. else
  7760. {
  7761. hr = S_FALSE;
  7762. }
  7763. }
  7764. psb->Release();
  7765. }
  7766. return hr;
  7767. }
  7768. // split a full pidl into the "folder" part and the "item" part.
  7769. // callers need to free the folder part
  7770. // (pay special attention to the constness of the out params)
  7771. STDAPI SplitIDList(LPCITEMIDLIST pidl, LPITEMIDLIST *ppidlFolder, LPCITEMIDLIST *ppidlChild)
  7772. {
  7773. HRESULT hr;
  7774. *ppidlFolder = ILCloneParent(pidl);
  7775. if (*ppidlFolder)
  7776. {
  7777. *ppidlChild = ILFindLastID(pidl); // const alias result
  7778. hr = S_OK;
  7779. }
  7780. else
  7781. {
  7782. *ppidlChild = NULL;
  7783. hr = E_OUTOFMEMORY;
  7784. }
  7785. return hr;
  7786. }
  7787. HRESULT GetAppNameFromCLSID(CLSID clsid, PWSTR *ppszApp)
  7788. {
  7789. *ppszApp = NULL;
  7790. IQueryAssociations *pqa;
  7791. HRESULT hr = AssocCreate(CLSID_QueryAssociations, IID_PPV_ARG(IQueryAssociations, &pqa));
  7792. if (SUCCEEDED(hr))
  7793. {
  7794. PWSTR pszProgID;
  7795. hr = ProgIDFromCLSID(clsid, &pszProgID);
  7796. if (SUCCEEDED(hr))
  7797. {
  7798. hr = pqa->Init(NULL, pszProgID, NULL, NULL);
  7799. if (SUCCEEDED(hr))
  7800. {
  7801. hr = E_OUTOFMEMORY;
  7802. DWORD cch = 0;
  7803. pqa->GetString(ASSOCF_NOTRUNCATE, ASSOCSTR_FRIENDLYAPPNAME, NULL, NULL, &cch);
  7804. if (cch)
  7805. {
  7806. *ppszApp = (PWSTR)LocalAlloc(LPTR, cch * sizeof(WCHAR));
  7807. if (*ppszApp)
  7808. {
  7809. hr = pqa->GetString(0, ASSOCSTR_FRIENDLYAPPNAME, NULL, *ppszApp, &cch);
  7810. if (FAILED(hr))
  7811. {
  7812. Str_SetPtr(ppszApp, NULL);
  7813. }
  7814. }
  7815. }
  7816. }
  7817. CoTaskMemFree(pszProgID);
  7818. }
  7819. pqa->Release();
  7820. }
  7821. return hr;
  7822. }
  7823. HRESULT GetAppNameFromMoniker(IRunningObjectTable *prot, IMoniker *pmkFile, PWSTR *ppszApp)
  7824. {
  7825. HRESULT hr = E_FAIL;
  7826. IUnknown *punk;
  7827. if (prot->GetObject(pmkFile, &punk) == S_OK)
  7828. {
  7829. IOleObject *pole;
  7830. hr = punk->QueryInterface(IID_PPV_ARG(IOleObject, &pole));
  7831. if (SUCCEEDED(hr))
  7832. {
  7833. CLSID clsid;
  7834. hr = pole->GetUserClassID(&clsid);
  7835. if (SUCCEEDED(hr))
  7836. {
  7837. hr = GetAppNameFromCLSID(clsid, ppszApp);
  7838. }
  7839. pole->Release();
  7840. }
  7841. punk->Release();
  7842. }
  7843. return hr;
  7844. }
  7845. // pmkPath c:\foo
  7846. // pmk c:\foo\doc.txt
  7847. BOOL IsMonikerPrefix(IMoniker *pmkPath, IMoniker *pmkFile)
  7848. {
  7849. BOOL bRet = FALSE;
  7850. IMoniker *pmkPrefix;
  7851. if (SUCCEEDED(pmkPath->CommonPrefixWith(pmkFile, &pmkPrefix)))
  7852. {
  7853. bRet = (S_OK == pmkPath->IsEqual(pmkPrefix));
  7854. pmkPrefix->Release();
  7855. }
  7856. return bRet;
  7857. }
  7858. STDAPI FindAppForFileInUse(PCWSTR pszFile, PWSTR *ppszApp)
  7859. {
  7860. IRunningObjectTable *prot;
  7861. HRESULT hr = GetRunningObjectTable(0, &prot);
  7862. if (SUCCEEDED(hr))
  7863. {
  7864. IMoniker *pmkFile;
  7865. hr = CreateFileMoniker(pszFile, &pmkFile);
  7866. if (SUCCEEDED(hr))
  7867. {
  7868. IEnumMoniker *penumMk;
  7869. hr = prot->EnumRunning(&penumMk);
  7870. if (SUCCEEDED(hr))
  7871. {
  7872. hr = E_FAIL;
  7873. ULONG celt;
  7874. IMoniker *pmk;
  7875. while (FAILED(hr) && (penumMk->Next(1, &pmk, &celt) == S_OK))
  7876. {
  7877. DWORD dwType;
  7878. if (SUCCEEDED(pmk->IsSystemMoniker(&dwType)) && (dwType == MKSYS_FILEMONIKER))
  7879. {
  7880. if (IsMonikerPrefix(pmkFile, pmk))
  7881. {
  7882. hr = GetAppNameFromMoniker(prot, pmk, ppszApp);
  7883. }
  7884. }
  7885. pmk->Release();
  7886. }
  7887. penumMk->Release();
  7888. }
  7889. pmkFile->Release();
  7890. }
  7891. prot->Release();
  7892. }
  7893. return hr;
  7894. }
  7895. #include <trkwks_c.c>
  7896. // DirectUI initialization helper functions
  7897. static BOOL g_DirectUIInitialized = FALSE;
  7898. HRESULT InitializeDUIViewClasses(); // duiview.cpp
  7899. HRESULT InitializeCPClasses(); // cpview.cpp
  7900. HRESULT InitializeDirectUI()
  7901. {
  7902. HRESULT hr;
  7903. // If we have already initialized DirectUI, exit now.
  7904. // Multiple threads will be attempting to initialize. InitProcess
  7905. // and class registration expects to be run on the primary thread.
  7906. // Make sure it only happens once on a single thread
  7907. ENTERCRITICAL;
  7908. if (g_DirectUIInitialized)
  7909. {
  7910. hr = S_OK;
  7911. goto Done;
  7912. }
  7913. // Initialize DirectUI for the process
  7914. hr = DirectUI::InitProcess();
  7915. if (FAILED(hr))
  7916. goto Done;
  7917. // Initialize the classes that DUIView uses
  7918. hr = InitializeDUIViewClasses();
  7919. if (FAILED(hr))
  7920. goto Done;
  7921. // Initialize the classes that Control Panel uses
  7922. hr = InitializeCPClasses();
  7923. if (FAILED(hr))
  7924. goto Done;
  7925. g_DirectUIInitialized = TRUE;
  7926. Done:
  7927. if (FAILED(hr))
  7928. {
  7929. // Safe to call if InitProcess fails. Will unregister
  7930. // all registered classes. All InitThread calls will fail
  7931. DirectUI::UnInitProcess();
  7932. }
  7933. LEAVECRITICAL;
  7934. return hr;
  7935. }
  7936. void UnInitializeDirectUI(void)
  7937. {
  7938. ENTERCRITICAL;
  7939. if (g_DirectUIInitialized)
  7940. {
  7941. DirectUI::UnInitProcess();
  7942. g_DirectUIInitialized = FALSE;
  7943. }
  7944. LEAVECRITICAL;
  7945. }
  7946. BOOL IsForceGuestModeOn(void)
  7947. {
  7948. BOOL fIsForceGuestModeOn = FALSE;
  7949. if (IsOS(OS_PERSONAL))
  7950. {
  7951. // Guest mode is always on for Personal
  7952. fIsForceGuestModeOn = TRUE;
  7953. }
  7954. else if (IsOS(OS_PROFESSIONAL) && !IsOS(OS_DOMAINMEMBER))
  7955. {
  7956. DWORD dwForceGuest;
  7957. DWORD cb = sizeof(dwForceGuest);
  7958. // Professional, not in a domain. Check the ForceGuest value.
  7959. if (ERROR_SUCCESS == SHGetValue(HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Control\\LSA"), TEXT("ForceGuest"), NULL, &dwForceGuest, &cb)
  7960. && 1 == dwForceGuest)
  7961. {
  7962. fIsForceGuestModeOn = TRUE;
  7963. }
  7964. }
  7965. return fIsForceGuestModeOn;
  7966. }
  7967. BOOL IsFolderSecurityModeOn(void)
  7968. {
  7969. DWORD dwSecurity;
  7970. DWORD cb = sizeof(dwSecurity);
  7971. DWORD err;
  7972. err = SHGetValue(HKEY_LOCAL_MACHINE,
  7973. TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer"),
  7974. TEXT("MoveSecurityAttributes"), NULL, &dwSecurity, &cb);
  7975. if (err == ERROR_SUCCESS)
  7976. {
  7977. return (dwSecurity == 0);
  7978. }
  7979. else
  7980. {
  7981. return IsForceGuestModeOn();
  7982. }
  7983. }
  7984. STDAPI_(int) StrCmpLogicalRestricted(PCWSTR psz1, PCWSTR psz2)
  7985. {
  7986. if (!SHRestricted(REST_NOSTRCMPLOGICAL))
  7987. return StrCmpLogicalW(psz1, psz2);
  7988. else
  7989. return StrCmpIW(psz1, psz2);
  7990. }