Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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