Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5575 lines
168 KiB

  1. #include "priv.h"
  2. #include <shlobj.h>
  3. #include <shellp.h>
  4. #include <ieguidp.h>
  5. #include "ids.h"
  6. #include <objbase.h>
  7. #include <trayp.h>
  8. #include <shdocvw.h>
  9. #include <mshtmhst.h>
  10. #include <shsemip.h>
  11. #include <winnetp.h>
  12. #include <inetreg.h>
  13. #include <shguidp.h>
  14. #include <shlguid.h> // Defines: CLSID_ACLMRU
  15. #include <htmlhelp.h>
  16. #include <mluisupp.h>
  17. #include "apithk.h"
  18. #include <initguid.h>
  19. #include <shimgdata.h>
  20. #include <varutil.h>
  21. #define REGSTR_PATH_MESSAGEBOXCHECKA "Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DontShowMeThisDialogAgain"
  22. #define REGSTR_PATH_MESSAGEBOXCHECKW L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\DontShowMeThisDialogAgain"
  23. // Raw accelerator table
  24. typedef struct
  25. {
  26. int cEntries;
  27. ACCEL rgacc[0];
  28. } CA_ACCEL;
  29. STDAPI_(HANDLE) SHLoadRawAccelerators(HINSTANCE hInst, LPCTSTR lpTableName)
  30. {
  31. CA_ACCEL *pca = NULL;
  32. HACCEL hAcc = LoadAccelerators(hInst, lpTableName); // Load the accelerator resource
  33. if (hAcc)
  34. {
  35. // Retrieve the number of entries
  36. int cEntries = CopyAcceleratorTable(hAcc, NULL, 0);
  37. if (cEntries > 0)
  38. {
  39. // Allocate a counted array and copy the elements
  40. pca = (CA_ACCEL*)LocalAlloc(LPTR, sizeof(CA_ACCEL) + cEntries * sizeof(ACCEL));
  41. if (pca)
  42. {
  43. pca->cEntries = cEntries;
  44. if (cEntries != CopyAcceleratorTable(hAcc, pca->rgacc, cEntries))
  45. {
  46. LocalFree(pca);
  47. pca = NULL;
  48. }
  49. }
  50. }
  51. DestroyAcceleratorTable(hAcc);
  52. }
  53. return pca;
  54. }
  55. STDAPI_(BOOL) SHQueryRawAccelerator(HANDLE hcaAcc, IN BYTE fVirtMask, IN BYTE fVirt, IN WPARAM wKey, OUT OPTIONAL UINT* puCmdID)
  56. {
  57. ASSERT(hcaAcc);
  58. CA_ACCEL* pca = (CA_ACCEL*)hcaAcc;
  59. if (puCmdID)
  60. *puCmdID = 0;
  61. for(int i = 0; i < pca->cEntries; i++)
  62. {
  63. if (fVirt == (pca->rgacc[i].fVirt & fVirtMask) && wKey == pca->rgacc[i].key)
  64. {
  65. if (puCmdID)
  66. *puCmdID = pca->rgacc[i].cmd;
  67. return TRUE;
  68. }
  69. }
  70. return FALSE;
  71. }
  72. STDAPI_(BOOL) SHQueryRawAcceleratorMsg(HANDLE hcaAcc, MSG* pmsg, OUT OPTIONAL UINT* puCmdID)
  73. {
  74. if (WM_KEYDOWN == pmsg->message || WM_KEYUP == pmsg->message)
  75. {
  76. #define TESTKEYSTATE(vk) ((GetKeyState(vk) & 0x8000)!=0)
  77. BYTE fVirt = FVIRTKEY;
  78. if (TESTKEYSTATE(VK_CONTROL))
  79. fVirt |= FCONTROL;
  80. else if (TESTKEYSTATE(VK_SHIFT))
  81. fVirt |= FSHIFT;
  82. else if (TESTKEYSTATE(VK_MENU))
  83. fVirt |= FALT;
  84. return SHQueryRawAccelerator(hcaAcc, fVirt, fVirt, pmsg->wParam, puCmdID);
  85. }
  86. return FALSE;
  87. }
  88. STDAPI SHSetThreadRef(IUnknown *punk)
  89. {
  90. #ifdef DEBUG
  91. IUnknown* tmp;
  92. tmp = (IUnknown*)TlsGetValue(g_tlsThreadRef);
  93. ASSERT(NULL==tmp || NULL==punk);
  94. #endif
  95. return TlsSetValue(g_tlsThreadRef, punk) ? S_OK : E_FAIL;
  96. }
  97. STDAPI SHGetThreadRef(IUnknown **ppunk)
  98. {
  99. *ppunk = (IUnknown *)TlsGetValue(g_tlsThreadRef);
  100. if (*ppunk)
  101. {
  102. (*ppunk)->AddRef();
  103. return S_OK;
  104. }
  105. return E_NOINTERFACE;
  106. }
  107. STDAPI SHSetOtherThreadsRef(IUnknown *punk)
  108. {
  109. #ifdef DEBUG
  110. IUnknown* tmp;
  111. tmp = (IUnknown*)TlsGetValue(g_tlsOtherThreadsRef);
  112. ASSERT(NULL==tmp || NULL==punk);
  113. #endif
  114. return TlsSetValue(g_tlsOtherThreadsRef, punk) ? S_OK : E_FAIL;
  115. }
  116. // release a CTF_THREAD_REF reference earlier than the return of pfnThreadProc
  117. STDAPI SHReleaseThreadRef()
  118. {
  119. IUnknown* punk;
  120. punk = (IUnknown *)TlsGetValue(g_tlsOtherThreadsRef);
  121. if (punk)
  122. {
  123. TlsSetValue(g_tlsOtherThreadsRef, NULL);
  124. punk->Release();
  125. return S_OK;
  126. }
  127. RIPMSG(FALSE, "Why is caller SHRealeaseThreadRef()ing when they don't have a thread ref?");
  128. return S_FALSE;
  129. }
  130. // thread reference count object, this uses SHSetThreadRef()to let other code
  131. // in this process hold a reference to this main thread, and thus the main thread in this process
  132. class CRefThread : public IUnknown
  133. {
  134. public:
  135. // IUnknown
  136. virtual STDMETHODIMP QueryInterface(REFIID riid, void **ppvObj);
  137. virtual STDMETHODIMP_(ULONG) AddRef(void);
  138. virtual STDMETHODIMP_(ULONG) Release(void);
  139. CRefThread(LONG *pcRef);
  140. private:
  141. ~CRefThread();
  142. LONG *_pcRef;
  143. UINT _idThread;
  144. };
  145. CRefThread::CRefThread(LONG *pcRef)
  146. {
  147. _idThread = GetCurrentThreadId();
  148. _pcRef = pcRef;
  149. *_pcRef = 1;
  150. }
  151. //
  152. // Note that this code tightens but does not close a race window.
  153. // Although we nuke the process reference, the class factory for
  154. // the web browser has yet to be deregistered, so if somebody decides
  155. // to create one, our class factory will wake up and create a
  156. // shell folder, which will flake out because it can't get a
  157. // process reference.
  158. //
  159. CRefThread::~CRefThread()
  160. {
  161. *_pcRef = 0;
  162. // get the other thread out of WaitMessage() or GetMessage()
  163. PostThreadMessage(_idThread, WM_NULL, 0, 0);
  164. }
  165. HRESULT CRefThread::QueryInterface(REFIID riid, void **ppvObj)
  166. {
  167. static const QITAB qit[] = { { 0 }, };
  168. return QISearch(this, qit, riid, ppvObj);
  169. }
  170. ULONG CRefThread::AddRef()
  171. {
  172. return InterlockedIncrement(_pcRef);
  173. }
  174. ULONG CRefThread::Release()
  175. {
  176. if (InterlockedDecrement(_pcRef))
  177. return *_pcRef;
  178. delete this;
  179. return 0;
  180. }
  181. STDAPI _CreateThreadRef(LONG *pcRef, IUnknown **ppunk)
  182. {
  183. *ppunk = new CRefThread(pcRef);
  184. if (*ppunk)
  185. return S_OK;
  186. *pcRef = 0;
  187. *ppunk = NULL;
  188. return E_OUTOFMEMORY;
  189. }
  190. // call if you want to kick off an independant thread.. you don't want handles back, or ids
  191. // and if the create fails, call synchronously
  192. typedef struct
  193. {
  194. LPTHREAD_START_ROUTINE pfnMain;
  195. LPTHREAD_START_ROUTINE pfnSync;
  196. HANDLE hSync;
  197. void *pvData;
  198. DWORD dwFlags;
  199. IUnknown *punkThreadRef;
  200. IUnknown *punkProcessRef;
  201. HMODULE hmodFree;
  202. HRESULT hrThreadStart;
  203. } PRIVCREATETHREADDATA;
  204. DWORD CALLBACK WrapperThreadProc(void *pv)
  205. {
  206. // make a copy of the input buffer, this is sitting on the calling threads stack
  207. // once we signal him his copy will be invalid
  208. PRIVCREATETHREADDATA rgCreate = *((PRIVCREATETHREADDATA *)pv);
  209. HRESULT hrInit;
  210. LONG cThreadRef;
  211. IUnknown *punkThreadRef;
  212. DWORD dwRes = 0;
  213. if (rgCreate.dwFlags & CTF_REF_COUNTED)
  214. {
  215. rgCreate.hrThreadStart = _CreateThreadRef(&cThreadRef, &punkThreadRef);
  216. if (SUCCEEDED(rgCreate.hrThreadStart))
  217. rgCreate.hrThreadStart = SHSetThreadRef(punkThreadRef);
  218. }
  219. if (SUCCEEDED(rgCreate.hrThreadStart) && rgCreate.punkThreadRef)
  220. {
  221. rgCreate.hrThreadStart = SHSetOtherThreadsRef(rgCreate.punkThreadRef); // hand punkThreadRef off to our tls value
  222. }
  223. if (SUCCEEDED(rgCreate.hrThreadStart))
  224. {
  225. if (rgCreate.dwFlags & CTF_COINIT)
  226. hrInit = SHCoInitialize();
  227. // call the synchronous ThreadProc while the other thread is waiting on hSync
  228. if (rgCreate.pfnSync)
  229. rgCreate.pfnSync(rgCreate.pvData);
  230. }
  231. // poke our return value back before releasing the main thread
  232. ((PRIVCREATETHREADDATA *)pv)->hrThreadStart = rgCreate.hrThreadStart;
  233. SetEvent(rgCreate.hSync); // release the main thread..
  234. if (SUCCEEDED(rgCreate.hrThreadStart))
  235. {
  236. // call the main thread proc
  237. dwRes = rgCreate.pfnMain(rgCreate.pvData);
  238. if ((rgCreate.dwFlags & CTF_REF_COUNTED) && punkThreadRef)
  239. {
  240. MSG msg;
  241. // release our ref on ourselves.
  242. // then pump until everyone else
  243. // has finished using our thread.
  244. // this is important for COM objects
  245. // that were created on this thread
  246. // but are being used by another thread
  247. punkThreadRef->Release();
  248. while (cThreadRef)
  249. {
  250. if (GetMessage(&msg, NULL, 0, 0))
  251. {
  252. TranslateMessage(&msg);
  253. DispatchMessage(&msg);
  254. }
  255. }
  256. }
  257. if (rgCreate.punkThreadRef)
  258. {
  259. // If pfnMain hasn't released the thread reference yet, do it ourselves
  260. IUnknown* tmp;
  261. tmp = (IUnknown*)TlsGetValue(g_tlsOtherThreadsRef);
  262. if (tmp)
  263. SHReleaseThreadRef();
  264. }
  265. if (rgCreate.punkProcessRef)
  266. rgCreate.punkProcessRef->Release();
  267. if (rgCreate.dwFlags & CTF_COINIT)
  268. SHCoUninitialize(hrInit);
  269. if (rgCreate.hmodFree)
  270. FreeLibraryAndExitThread(rgCreate.hmodFree, dwRes);
  271. }
  272. return dwRes;
  273. }
  274. // Call if you want to kick off an independent thread and
  275. // you don't care about the handle or thread ID.
  276. //
  277. // If the create fails, call synchronously.
  278. //
  279. // optionally call a secondary callback when the thread
  280. // is created.
  281. // returns:
  282. // TRUE if pfnThreadProc was executed
  283. STDAPI_(BOOL) SHCreateThread(
  284. LPTHREAD_START_ROUTINE pfnThreadProc,
  285. void *pvData,
  286. DWORD dwFlags, // CTF_*
  287. LPTHREAD_START_ROUTINE pfnCallback) OPTIONAL
  288. {
  289. BOOL bRet = FALSE;
  290. PRIVCREATETHREADDATA rgCreate = {0}; // can be on the stack since we sync the thread
  291. if ((dwFlags & CTF_INSIST) && pfnCallback)
  292. {
  293. ASSERTMSG(FALSE, "SHCreateThread: cannot specify CTF_INSIST and pfnCallback at the same time");
  294. return FALSE;
  295. }
  296. if (CTF_THREAD_REF & dwFlags)
  297. {
  298. if (FAILED(SHGetThreadRef(&rgCreate.punkThreadRef)))
  299. {
  300. TraceMsg(TF_WARNING, "SHCreateThread is failing since caller requested CTF_THREAD_REF but does not support thread references.");
  301. // The calling code is requesting a thread-ref, but the thread doesn't support it.
  302. // Whatever requires this thread-ref will break, so we must return FALSE here (and
  303. // let the caller work around it).
  304. return FALSE;
  305. }
  306. }
  307. if (CTF_PROCESS_REF & dwFlags)
  308. {
  309. SHGetInstanceExplorer(&rgCreate.punkProcessRef);
  310. }
  311. if (CTF_FREELIBANDEXIT & dwFlags)
  312. {
  313. MEMORY_BASIC_INFORMATION mbi;
  314. if (VirtualQuery(pfnThreadProc, &mbi, sizeof(mbi)))
  315. {
  316. TCHAR szModule[MAX_PATH];
  317. if (GetModuleFileName((HMODULE)mbi.AllocationBase, szModule, ARRAYSIZE(szModule)))
  318. {
  319. rgCreate.hmodFree = LoadLibrary(szModule);
  320. }
  321. }
  322. }
  323. rgCreate.pfnMain = pfnThreadProc;
  324. rgCreate.pfnSync = pfnCallback;
  325. rgCreate.pvData = pvData;
  326. rgCreate.dwFlags = dwFlags;
  327. rgCreate.hSync = CreateEvent(NULL, FALSE, FALSE, NULL);
  328. if (rgCreate.hSync)
  329. {
  330. DWORD idThread;
  331. HANDLE hThread = CreateThread(NULL, 0, WrapperThreadProc, &rgCreate, 0, &idThread);
  332. if (hThread)
  333. {
  334. // Some pfnCallback procs need to do SendMessage to the calling thread, others
  335. // want to do COM. This COM thing is new, so reduce the risk of breaking things
  336. // by making it by-request only
  337. if (CTF_WAIT_ALLOWCOM & dwFlags)
  338. SHWaitForCOMSendMessageThread(rgCreate.hSync, INFINITE);
  339. else
  340. SHWaitForSendMessageThread(rgCreate.hSync, INFINITE);
  341. CloseHandle(hThread);
  342. // If the WrapperThreadProc failed to initialize itself, pretend we failed to create the thread
  343. bRet = SUCCEEDED(rgCreate.hrThreadStart);
  344. }
  345. CloseHandle(rgCreate.hSync);
  346. }
  347. if (!bRet)
  348. {
  349. if (rgCreate.punkThreadRef)
  350. {
  351. rgCreate.punkThreadRef->Release();
  352. }
  353. if (rgCreate.punkProcessRef)
  354. {
  355. rgCreate.punkProcessRef->Release();
  356. }
  357. if (rgCreate.hmodFree)
  358. FreeLibrary(rgCreate.hmodFree);
  359. if (dwFlags & CTF_INSIST)
  360. {
  361. // failed to create another thread... call synchronously
  362. pfnThreadProc(pvData);
  363. bRet = TRUE;
  364. }
  365. }
  366. return bRet;
  367. }
  368. STDAPI_(BOOL) SHIsLowMemoryMachine(DWORD dwType)
  369. // Are we an 8 meg Win95 machine or 16 meg NT machine.
  370. // Back in the old days...
  371. {
  372. #ifdef UNIX
  373. return 0; //due to GlobalMemoryStatus() always return 0 physical mem size.
  374. #else
  375. static int fLowMem = -1;
  376. if (ILMM_IE4 == dwType && fLowMem == -1)
  377. {
  378. MEMORYSTATUS ms;
  379. GlobalMemoryStatus(&ms);
  380. if (g_bRunningOnNT)
  381. fLowMem = (ms.dwTotalPhys <= 16*1024*1024);
  382. else
  383. fLowMem = (ms.dwTotalPhys <= 8*1024*1024);
  384. }
  385. return fLowMem;
  386. #endif
  387. }
  388. #if 0
  389. // MultiByteToWideChar doesn't truncate if the buffer is too small.
  390. // these utils do.
  391. // returns:
  392. // # of chars converted (WIDE chars) into out buffer (pwstr)
  393. int _AnsiToUnicode(UINT uiCP, LPCSTR pstr, LPWSTR pwstr, int cch)
  394. {
  395. int cchDst = 0;
  396. ASSERT(IS_VALID_STRING_PTRA(pstr, -1));
  397. ASSERT(NULL == pwstr || IS_VALID_WRITE_BUFFER(pwstr, WCHAR, cch));
  398. if (cch && pwstr)
  399. pwstr[0] = 0;
  400. #if 0
  401. switch (uiCP)
  402. {
  403. case 1200: // UCS-2 (Unicode)
  404. uiCP = 65001;
  405. // fall through;
  406. case 65000: // UTF-7
  407. case 65001: // UTF-8
  408. {
  409. INT cchSrc, cchSrcOriginal;
  410. cchSrc = cchSrcOriginal = lstrlenA(pstr) + 1;
  411. cchDst = cch;
  412. if (SUCCEEDED(ConvertINetMultiByteToUnicode(NULL, uiCP, pstr,
  413. &cchSrc, pwstr, &cchDst)) &&
  414. cchSrc < cchSrcOriginal)
  415. {
  416. LPWSTR pwsz = (LPWSTR)LocalAlloc(LPTR, cchDst * sizeof(WCHAR));
  417. if (pwsz)
  418. {
  419. if (SUCCEEDED(ConvertINetMultiByteToUnicode(NULL, uiCP, pstr,
  420. &cchSrcOriginal, pwsz, &cchDst)))
  421. {
  422. StrCpyNW(pwstr, pwsz, cch);
  423. cchDst = cch;
  424. }
  425. LocalFree(pwsz);
  426. }
  427. }
  428. break;
  429. }
  430. default:
  431. #endif
  432. cchDst = MultiByteToWideChar(uiCP, 0, pstr, -1, pwstr, cch);
  433. if (!cchDst)
  434. {
  435. // failed.
  436. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  437. {
  438. int cchNeeded = MultiByteToWideChar(uiCP, 0, pstr, -1, NULL, 0);
  439. if (cchNeeded)
  440. {
  441. LPWSTR pwsz = (LPWSTR)LocalAlloc(LPTR, cchNeeded * sizeof(WCHAR));
  442. if (pwsz)
  443. {
  444. cchDst = MultiByteToWideChar(uiCP, 0, pstr, -1, pwsz, cchNeeded);
  445. if (cchDst)
  446. {
  447. StrCpyNW(pwstr, pwsz, cch);
  448. cchDst = cch;
  449. }
  450. LocalFree(pwsz);
  451. }
  452. }
  453. }
  454. }
  455. #if 0
  456. break;
  457. }
  458. #endif
  459. return cchDst;
  460. }
  461. // returns:
  462. // # of BYTES written to output buffer (pstr)
  463. int _UnicodeToAnsi(UINT uiCP, LPCWSTR pwstr, LPSTR pstr, int cch)
  464. {
  465. int cchDst = 0;
  466. ASSERT(IS_VALID_STRING_PTRW(pwstr, -1));
  467. ASSERT(NULL == pstr || IS_VALID_WRITE_BUFFER(pstr, char, cch));
  468. if (pstr && cch)
  469. pstr[0] = 0;
  470. #if 0
  471. switch (uiCP)
  472. {
  473. case 1200: // UCS-2 (Unicode)
  474. uiCP = 65001;
  475. // fall through
  476. case 65000: // UTF-7
  477. case 65001: // UTF-8
  478. {
  479. INT cchSrc, cchSrcOriginal;
  480. cchSrc = cchSrcOriginal = lstrlenW(pwstr) + 1;
  481. cchDst = cch;
  482. if (SUCCEEDED(ConvertINetUnicodeToMultiByte(NULL, uiCP, pwstr,
  483. &cchSrc, pstr, &cchDst)) &&
  484. cchSrc < cchSrcOriginal)
  485. {
  486. LPSTR psz = (LPSTR)LocalAlloc(LPTR, cchDst * sizeof(CHAR));
  487. if (psz)
  488. {
  489. if (SUCCEEDED(ConvertINetUnicodeToMultiByte(NULL, uiCP, pwstr,
  490. &cchSrcOriginal, psz, &cchDst)))
  491. {
  492. // lstrcpyn puts NULL at pstr[cch-1]
  493. // without considering if it'd cut in dbcs
  494. TruncateString(psz, cch);
  495. lstrcpynA(pstr, psz, cch);
  496. cchDst = cch;
  497. }
  498. LocalFree(psz);
  499. }
  500. }
  501. break;
  502. }
  503. default:
  504. #endif
  505. cchDst = WideCharToMultiByte(uiCP, 0, pwstr, -1, pstr, cch, NULL, NULL);
  506. if (!cchDst)
  507. {
  508. // failed.
  509. if (GetLastError() == ERROR_INSUFFICIENT_BUFFER)
  510. {
  511. int cchNeeded = WideCharToMultiByte(uiCP, 0, pwstr, -1, NULL, 0, NULL, NULL);
  512. if (cchNeeded > 0)
  513. {
  514. LPSTR psz = (LPSTR)LocalAlloc(LPTR, cchNeeded * sizeof(CHAR));
  515. if (psz)
  516. {
  517. cchDst = WideCharToMultiByte(uiCP, 0, pwstr, -1, psz, cchNeeded, NULL, NULL);
  518. if (cchDst)
  519. {
  520. // lstrcpyn puts NULL at pstr[cch-1]
  521. // without considering if it'd cut in dbcs
  522. SHTruncateString(psz, cch);
  523. lstrcpynA(pstr, psz, cch);
  524. cchDst = cch;
  525. }
  526. LocalFree(psz);
  527. }
  528. }
  529. }
  530. }
  531. #if 0
  532. break;
  533. }
  534. #endif
  535. return cchDst;
  536. }
  537. #endif
  538. // SHTruncateString
  539. //
  540. // purpose: cut a string at the given length in dbcs safe manner.
  541. // the string may be truncated at cch-2 if the sz[cch] points
  542. // to a lead byte that would result in cutting in the middle
  543. // of double byte character.
  544. //
  545. // The character at sz[cchBufferSize-1] is not consulted, so you
  546. // can call this after lstrcpyn (which forces sz[cchBufferSize-1]=0).
  547. //
  548. // If the source string is shorter than cchBufferSize-1 characters,
  549. // we fiddle some bytes that have no effect, in which case the return
  550. // value is random.
  551. //
  552. // update: made it faster for sbcs environment (5/26/97)
  553. // now returns adjusted cch (6/20/97)
  554. //
  555. STDAPI_(int) SHTruncateString(CHAR *sz, int cchBufferSize)
  556. {
  557. if (!sz || cchBufferSize <= 0) return 0;
  558. int cch = cchBufferSize - 1; // get index position to NULL out
  559. LPSTR psz = &sz[cch];
  560. while (psz >sz)
  561. {
  562. psz--;
  563. if (!IsDBCSLeadByte(*psz))
  564. {
  565. // Found non-leadbyte for the first time.
  566. // This is either a trail byte of double byte char
  567. // or a single byte character we've first seen.
  568. // Thus, the next pointer must be at either of a leadbyte
  569. // or &sz[cch]
  570. psz++;
  571. break;
  572. }
  573. }
  574. if (((&sz[cch] - psz) & 1) && cch > 0)
  575. {
  576. // we're truncating the string in the middle of dbcs
  577. cch--;
  578. }
  579. sz[cch] = '\0';
  580. return cch;
  581. }
  582. //
  583. // Why do we use the unsafe version?
  584. //
  585. // - Unsafe is much faster.
  586. //
  587. // - The safe version isn't safe after all and serves only to mask
  588. // existing bugs. The situation the safe version "saves" is if
  589. // two threads both try to atomicrelease the same object. This
  590. // means that at the same moment, both threads think the object
  591. // is alive. Change the timing slightly, and now one thread
  592. // atomicreleases the object before the other one, so the other
  593. // thread is now using an object after the first thread already
  594. // atomicreleased it. Bug.
  595. //
  596. STDAPI_(void) IUnknown_AtomicRelease(void **ppunk)
  597. {
  598. #if 1 // Unsafe
  599. if (ppunk && *ppunk)
  600. {
  601. IUnknown* punk = *(IUnknown**)ppunk;
  602. *ppunk = NULL;
  603. punk->Release();
  604. }
  605. #else // Safe
  606. if (ppunk)
  607. {
  608. IUnknown* punk = (IUnknown *)InterlockedExchangePointer(ppunk, NULL);
  609. if (punk)
  610. {
  611. punk->Release();
  612. }
  613. }
  614. #endif
  615. }
  616. STDAPI ConnectToConnectionPoint(IUnknown* punk, REFIID riidEvent, BOOL fConnect, IUnknown* punkTarget, DWORD* pdwCookie, IConnectionPoint** ppcpOut)
  617. {
  618. // We always need punkTarget, we only need punk on connect
  619. if (!punkTarget || (fConnect && !punk))
  620. return E_FAIL;
  621. if (ppcpOut)
  622. *ppcpOut = NULL;
  623. IConnectionPointContainer *pcpc;
  624. HRESULT hr = punkTarget->QueryInterface(IID_PPV_ARG(IConnectionPointContainer, &pcpc));
  625. if (SUCCEEDED(hr))
  626. {
  627. IConnectionPoint *pcp;
  628. hr = pcpc->FindConnectionPoint(riidEvent, &pcp);
  629. if (SUCCEEDED(hr))
  630. {
  631. if (fConnect)
  632. {
  633. // Add us to the list of people interested...
  634. hr = pcp->Advise(punk, pdwCookie);
  635. if (FAILED(hr))
  636. *pdwCookie = 0;
  637. }
  638. else
  639. {
  640. // Remove us from the list of people interested...
  641. hr = pcp->Unadvise(*pdwCookie);
  642. *pdwCookie = 0;
  643. }
  644. if (ppcpOut && SUCCEEDED(hr))
  645. *ppcpOut = pcp;
  646. else
  647. pcp->Release();
  648. }
  649. pcpc->Release();
  650. }
  651. return hr;
  652. }
  653. STDAPI IUnknown_QueryStatus(IUnknown *punk, const GUID *pguidCmdGroup,
  654. ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  655. {
  656. HRESULT hr = E_FAIL;
  657. if (punk)
  658. {
  659. IOleCommandTarget* pct;
  660. hr = punk->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &pct));
  661. if (pct)
  662. {
  663. hr = pct->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
  664. pct->Release();
  665. }
  666. }
  667. return hr;
  668. }
  669. STDAPI IUnknown_Exec(IUnknown* punk, const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  670. {
  671. HRESULT hr = E_FAIL;
  672. if (punk)
  673. {
  674. IOleCommandTarget* pct;
  675. hr = punk->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &pct));
  676. if (SUCCEEDED(hr))
  677. {
  678. hr = pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  679. pct->Release();
  680. }
  681. }
  682. return hr;
  683. }
  684. STDAPI IUnknown_TranslateAcceleratorIO(IUnknown* punk, LPMSG lpMsg)
  685. {
  686. HRESULT hr = E_FAIL;
  687. if (punk != NULL)
  688. {
  689. IInputObject *pio;
  690. hr = punk->QueryInterface(IID_PPV_ARG(IInputObject, &pio));
  691. if (SUCCEEDED(hr))
  692. {
  693. hr = pio->TranslateAcceleratorIO(lpMsg);
  694. pio->Release();
  695. }
  696. }
  697. return hr;
  698. }
  699. STDAPI IUnknown_UIActivateIO(IUnknown *punk, BOOL fActivate, LPMSG lpMsg)
  700. {
  701. HRESULT hr = E_FAIL;
  702. if (punk != NULL)
  703. {
  704. IInputObject *pio;
  705. hr = punk->QueryInterface(IID_PPV_ARG(IInputObject, &pio));
  706. if (SUCCEEDED(hr))
  707. {
  708. hr = pio->UIActivateIO(fActivate, lpMsg);
  709. pio->Release();
  710. }
  711. }
  712. return hr;
  713. }
  714. STDAPI IUnknown_OnFocusChangeIS(IUnknown *punk, IUnknown *punkSrc, BOOL fSetFocus)
  715. {
  716. HRESULT hr = E_FAIL;
  717. if (punk != NULL)
  718. {
  719. IInputObjectSite *pis;
  720. hr = punk->QueryInterface(IID_PPV_ARG(IInputObjectSite, &pis));
  721. if (SUCCEEDED(hr))
  722. {
  723. hr = pis->OnFocusChangeIS(punkSrc, fSetFocus);
  724. pis->Release();
  725. }
  726. }
  727. return hr;
  728. }
  729. STDAPI IUnknown_HasFocusIO(IUnknown *punk)
  730. {
  731. HRESULT hr = E_FAIL;
  732. if (punk != NULL)
  733. {
  734. IInputObject *pio;
  735. hr = punk->QueryInterface(IID_PPV_ARG(IInputObject, &pio));
  736. if (SUCCEEDED(hr))
  737. {
  738. hr = pio->HasFocusIO();
  739. pio->Release();
  740. }
  741. }
  742. return hr;
  743. }
  744. STDAPI IUnknown_DoContextMenuPopup(IUnknown *punkSite, IContextMenu* pcm, UINT fFlags, POINT pt)
  745. {
  746. IContextMenuSite* pcms;
  747. HRESULT hr = IUnknown_QueryService(punkSite, SID_SContextMenuSite, IID_PPV_ARG(IContextMenuSite, &pcms));
  748. if (SUCCEEDED(hr))
  749. {
  750. hr = pcms->DoContextMenuPopup(pcm, fFlags, pt);
  751. pcms->Release();
  752. }
  753. else
  754. {
  755. #if 0 // REVIEW: do we want fall-back code?
  756. HWND hwnd;
  757. hr = IUnknown_GetWindow(punkSite, &hwnd);
  758. if (SUCCEEDED(hr))
  759. {
  760. HMENU hmenu = CreatePopupMenu();
  761. if (hmenu)
  762. {
  763. IShellBrowser* psb;
  764. if (SUCCEEDED(IUnknown_QueryService(punkSite, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowser, &psb))))
  765. {
  766. HWND hwnd;
  767. if (SUCCEEDED(psb->GetControlWindow(FCW_TREE, &hwnd)) && hwnd)
  768. {
  769. fFlags |= CMF_EXPLORE;
  770. }
  771. psb->Release();
  772. }
  773. if (GetKeyState(VK_SHIFT) < 0)
  774. {
  775. fFlags |= CMF_EXTENDEDVERBS;
  776. }
  777. hr = pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, fFlags);
  778. if (SUCCEEDED(hr))
  779. {
  780. int idCmd = TrackPopupMenuEx(hmenu,
  781. TPM_RETURNCMD | TPM_NONOTIFY | TPM_RIGHTBUTTON | TPM_LEFTALIGN,
  782. pt.x,
  783. pt.y,
  784. hwnd,
  785. NULL);
  786. if (0 != idCmd)
  787. {
  788. idCmd -= CONTEXTMENU_IDCMD_FIRST;
  789. CMINVOKECOMMANDINFOEX ici = {0};
  790. ici.cbSize = sizeof(ici);
  791. ici.fMask = CMIC_MASK_ASYNCOK | CMIC_MASK_PTINVOKE;
  792. ici.hwnd = hwnd;
  793. ici.lpVerb = (LPCSTR)IntToPtr(idCmd);
  794. ici.nShow = SW_SHOWDEFAULT;
  795. ici.lpVerbW = (LPCWSTR)IntToPtr(idCmd);
  796. ici.pt.x = pt.x;
  797. ici.pt.y = pt.y;
  798. if (GetKeyState(VK_SHIFT) < 0)
  799. ici.fMask |= CMIC_MASK_SHIFT_DOWN;
  800. if (GetKeyState(VK_CONTROL) < 0)
  801. ici.fMask |= CMIC_MASK_CONTROL_DOWN;
  802. hr = pcm->InvokeCommand(reinterpret_cast<LPCMINVOKECOMMANDINFO>(&ici));
  803. }
  804. else
  805. {
  806. hr = ERROR_CANCELLED;
  807. }
  808. }
  809. DestroyMenu(hmenu);
  810. }
  811. else
  812. {
  813. hr = E_OUTOFMEMORY;
  814. }
  815. }
  816. #endif
  817. }
  818. return hr;
  819. }
  820. STDAPI_(DWORD) SHSetWindowBits(HWND hWnd, int iWhich, DWORD dwBits, DWORD dwValue)
  821. {
  822. DWORD dwStyle = GetWindowLong(hWnd, iWhich);
  823. DWORD dwNewStyle = (dwStyle & ~dwBits) | (dwValue & dwBits);
  824. if (dwStyle != dwNewStyle)
  825. {
  826. SetWindowLong(hWnd, iWhich, dwNewStyle);
  827. }
  828. return dwStyle;
  829. }
  830. // OpenRegStream API in SHELL32 returns an emtpy
  831. // stream if we ask for read-only, if the entry does not exist.
  832. // we need to detect that case.
  833. //
  834. const LARGE_INTEGER c_li0 = { 0, 0 };
  835. STDAPI_(BOOL) SHIsEmptyStream(IStream* pstm)
  836. {
  837. #ifdef DEBUG
  838. // We always call this function when we open a new stream,
  839. // so we should always be at the beginning of the stream.
  840. //
  841. // We need this assert for the <NT5 shell case.
  842. //
  843. ULARGE_INTEGER liStart;
  844. pstm->Seek(c_li0, STREAM_SEEK_CUR, &liStart);
  845. ASSERT(0==liStart.HighPart && 0==liStart.LowPart);
  846. #endif
  847. STATSTG st;
  848. if (SUCCEEDED(pstm->Stat(&st, STATFLAG_NONAME)))
  849. {
  850. if (st.cbSize.LowPart || st.cbSize.HighPart)
  851. return FALSE;
  852. }
  853. else
  854. {
  855. // Win95 IStream code did not implement stat, so check
  856. // emptiness by trying to read.
  857. //
  858. int iTmp;
  859. if (SUCCEEDED(IStream_Read(pstm, &iTmp, sizeof(iTmp))))
  860. {
  861. // The stream is indeed present, seek back to start
  862. //
  863. pstm->Seek(c_li0, STREAM_SEEK_SET, NULL);
  864. return FALSE; // not empty
  865. }
  866. }
  867. return TRUE;
  868. }
  869. STDAPI_(void) SHSetParentHwnd(HWND hwnd, HWND hwndParent)
  870. {
  871. HWND hwndOldParent = GetParent(hwnd);
  872. if (hwndParent != hwndOldParent)
  873. {
  874. //
  875. // Get the child flag correct! If we don't do this and
  876. // somebody calls DialogBox on us while we are parented to NULL
  877. // and WS_CHILD, the desktop will be disabled, thereby causing
  878. // all mouse hit-testing to fail systemwide.
  879. // we also want to do this in the right order so the window
  880. // manager does the correct attachthreadinput if required...
  881. //
  882. if (hwndParent)
  883. SHSetWindowBits(hwnd, GWL_STYLE, WS_CHILD | WS_POPUP, WS_CHILD);
  884. SetParent(hwnd, hwndParent);
  885. if (!hwndParent)
  886. SHSetWindowBits(hwnd, GWL_STYLE, WS_CHILD | WS_POPUP, WS_POPUP);
  887. //
  888. // (jbeda) USER32 doesn't mirror the UIS bits correctly when windows
  889. // are reparented. They (mcostea) say that it would cause
  890. // compat problems. So to work around this, when
  891. // we reparent, we grab the bits on the parent window
  892. // and mirror them to the child.
  893. //
  894. if (g_bRunningOnNT5OrHigher)
  895. {
  896. LRESULT lUIState;
  897. lUIState = SendMessage(hwndParent, WM_QUERYUISTATE, 0, 0);
  898. if (lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL))
  899. {
  900. SendMessage(hwnd, WM_UPDATEUISTATE,
  901. MAKEWPARAM(UIS_SET,
  902. lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL)), 0);
  903. }
  904. if (~lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL))
  905. {
  906. SendMessage(hwnd, WM_UPDATEUISTATE,
  907. MAKEWPARAM(UIS_CLEAR,
  908. ~lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL)), 0);
  909. }
  910. }
  911. }
  912. }
  913. // IsSameObject checks for OLE object identity.
  914. //
  915. STDAPI_(BOOL) SHIsSameObject(IUnknown* punk1, IUnknown* punk2)
  916. {
  917. if (!punk1 || !punk2)
  918. {
  919. return FALSE;
  920. }
  921. else if (punk1 == punk2)
  922. {
  923. // Quick shortcut -- if they're the same pointer
  924. // already then they must be the same object
  925. //
  926. return TRUE;
  927. }
  928. else
  929. {
  930. IUnknown* punkI1;
  931. IUnknown* punkI2;
  932. // Some apps don't implement QueryInterface! (SecureFile)
  933. HRESULT hr = punk1->QueryInterface(IID_PPV_ARG(IUnknown, &punkI1));
  934. if (SUCCEEDED(hr))
  935. {
  936. punkI1->Release();
  937. hr = punk2->QueryInterface(IID_PPV_ARG(IUnknown, &punkI2));
  938. if (SUCCEEDED(hr))
  939. punkI2->Release();
  940. }
  941. return SUCCEEDED(hr) && (punkI1 == punkI2);
  942. }
  943. }
  944. // pass the CLSID of the object you are about to bind to. this queries
  945. // the bind context to see if that guy should be avoided
  946. // this would be a good shlwapi service.
  947. STDAPI_(BOOL) SHSkipJunction(IBindCtx *pbc, const CLSID *pclsid)
  948. {
  949. IUnknown *punk;
  950. if (pbc && SUCCEEDED(pbc->GetObjectParam(STR_SKIP_BINDING_CLSID, &punk)))
  951. {
  952. CLSID clsid;
  953. BOOL bSkip = SUCCEEDED(IUnknown_GetClassID(punk, &clsid)) && IsEqualCLSID(clsid, *pclsid);
  954. punk->Release();
  955. return bSkip;
  956. }
  957. return FALSE;
  958. }
  959. STDAPI IUnknown_GetWindow(IUnknown* punk, HWND* phwnd)
  960. {
  961. HRESULT hr = E_FAIL;
  962. *phwnd = NULL;
  963. if (punk)
  964. {
  965. IOleWindow* pow;
  966. IInternetSecurityMgrSite* pisms;
  967. IShellView* psv;
  968. // How many ways are there to get a window? Let me count the ways...
  969. hr = punk->QueryInterface(IID_PPV_ARG(IOleWindow, &pow));
  970. if (SUCCEEDED(hr))
  971. {
  972. hr = pow->GetWindow(phwnd);
  973. pow->Release();
  974. }
  975. else if (SUCCEEDED(hr = punk->QueryInterface(IID_PPV_ARG(IInternetSecurityMgrSite, &pisms))))
  976. {
  977. hr = pisms->GetWindow(phwnd);
  978. pisms->Release();
  979. }
  980. else if (SUCCEEDED(hr = punk->QueryInterface(IID_PPV_ARG(IShellView, &psv))))
  981. {
  982. hr = psv->GetWindow(phwnd);
  983. psv->Release();
  984. }
  985. }
  986. return hr;
  987. }
  988. /*****************************************************************************\
  989. FUNCTION: IUnknown_EnableModless
  990. DESCRIPTION:
  991. Several interfaces implement the ::EnableModeless() or equivalent methods.
  992. This requires us to use a utility function to query the punk until one is
  993. implemented and then use it.
  994. \*****************************************************************************/
  995. HRESULT IUnknown_EnableModeless(IUnknown * punk, BOOL fEnabled)
  996. {
  997. HRESULT hr = E_FAIL;
  998. if (punk)
  999. {
  1000. IOleInPlaceActiveObject * poipao;
  1001. IInternetSecurityMgrSite * pisms;
  1002. IOleInPlaceFrame * poipf;
  1003. IShellBrowser * psb;
  1004. IDocHostUIHandler * pdhuh;
  1005. // How many ways are there to enable modless? Let me count the ways...
  1006. hr = punk->QueryInterface(IID_PPV_ARG(IOleInPlaceActiveObject, &poipao));
  1007. if (SUCCEEDED(hr))
  1008. {
  1009. hr = poipao->EnableModeless(fEnabled);
  1010. poipao->Release();
  1011. }
  1012. else if (SUCCEEDED(hr = punk->QueryInterface(IID_PPV_ARG(IInternetSecurityMgrSite, &pisms))))
  1013. {
  1014. hr = pisms->EnableModeless(fEnabled);
  1015. pisms->Release();
  1016. }
  1017. else if (SUCCEEDED(hr = punk->QueryInterface(IID_PPV_ARG(IOleInPlaceFrame, &poipf))))
  1018. {
  1019. hr = poipf->EnableModeless(fEnabled);
  1020. poipf->Release();
  1021. }
  1022. else if (SUCCEEDED(hr = punk->QueryInterface(IID_PPV_ARG(IShellBrowser, &psb))))
  1023. {
  1024. hr = psb->EnableModelessSB(fEnabled);
  1025. psb->Release();
  1026. }
  1027. else if (SUCCEEDED(hr = punk->QueryInterface(IID_PPV_ARG(IDocHostUIHandler, &pdhuh))))
  1028. {
  1029. hr = pdhuh->EnableModeless(fEnabled);
  1030. pdhuh->Release();
  1031. }
  1032. }
  1033. return hr;
  1034. }
  1035. STDAPI IUnknown_SetOwner(IUnknown* punk, IUnknown* punkOwner)
  1036. {
  1037. HRESULT hr = E_FAIL;
  1038. if (punk)
  1039. {
  1040. IShellService* pss;
  1041. hr = punk->QueryInterface(IID_PPV_ARG(IShellService, &pss));
  1042. if (SUCCEEDED(hr))
  1043. {
  1044. pss->SetOwner(punkOwner);
  1045. pss->Release();
  1046. }
  1047. }
  1048. return hr;
  1049. }
  1050. STDAPI IUnknown_SetSite(IUnknown *punk, IUnknown *punkSite)
  1051. {
  1052. HRESULT hr = E_FAIL;
  1053. if (punk)
  1054. {
  1055. IObjectWithSite *pows;
  1056. hr = punk->QueryInterface(IID_PPV_ARG(IObjectWithSite, &pows));
  1057. if (SUCCEEDED(hr))
  1058. {
  1059. hr = pows->SetSite(punkSite);
  1060. ASSERT(SUCCEEDED(hr));
  1061. pows->Release();
  1062. }
  1063. else
  1064. {
  1065. IInternetSecurityManager * pism;
  1066. // The security guys should have used IObjectWithSite, but no....
  1067. hr = punk->QueryInterface(IID_PPV_ARG(IInternetSecurityManager, &pism));
  1068. if (SUCCEEDED(hr))
  1069. {
  1070. hr = pism->SetSecuritySite((IInternetSecurityMgrSite *) punkSite);
  1071. ASSERT(SUCCEEDED(hr));
  1072. pism->Release();
  1073. }
  1074. }
  1075. }
  1076. return hr;
  1077. }
  1078. STDAPI IUnknown_GetSite(IUnknown *punk, REFIID riid, void **ppv)
  1079. {
  1080. HRESULT hr = E_FAIL;
  1081. *ppv = NULL;
  1082. if (punk)
  1083. {
  1084. IObjectWithSite *pows;
  1085. hr = punk->QueryInterface(IID_PPV_ARG(IObjectWithSite, &pows));
  1086. ASSERT(SUCCEEDED(hr) || pows == NULL); // paranoia
  1087. if (SUCCEEDED(hr))
  1088. {
  1089. hr = pows->GetSite(riid, ppv);
  1090. // Note: The GetSite can legitimately fail if there is no site
  1091. // or the site doesn't support the requested interface.
  1092. ASSERT(SUCCEEDED(hr) || *ppv == NULL);
  1093. pows->Release();
  1094. }
  1095. }
  1096. return hr;
  1097. }
  1098. //
  1099. // GetUIVersion()
  1100. //
  1101. // returns the version of shell32
  1102. // 3 == win95 gold / NT4
  1103. // 4 == IE4 Integ / win98
  1104. // 5 == win2k
  1105. // 6 == Whistler
  1106. //
  1107. STDAPI_(UINT) GetUIVersion()
  1108. {
  1109. static UINT s_uiShell32 = 0;
  1110. if (s_uiShell32 == 0)
  1111. {
  1112. HINSTANCE hinst = GetModuleHandle(TEXT("SHELL32.DLL"));
  1113. if (hinst)
  1114. {
  1115. DLLGETVERSIONPROC pfnGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinst, "DllGetVersion");
  1116. DLLVERSIONINFO dllinfo;
  1117. dllinfo.cbSize = sizeof(DLLVERSIONINFO);
  1118. if (pfnGetVersion && pfnGetVersion(&dllinfo) == NOERROR)
  1119. s_uiShell32 = dllinfo.dwMajorVersion;
  1120. else
  1121. s_uiShell32 = 3;
  1122. }
  1123. }
  1124. return s_uiShell32;
  1125. }
  1126. //*** IUnknown_GetClassID -- do punk->IPS::GetClassID
  1127. STDAPI IUnknown_GetClassID(IUnknown *punk, CLSID *pclsid)
  1128. {
  1129. HRESULT hr = E_FAIL;
  1130. ASSERT(punk); // currently nobody does
  1131. if (punk)
  1132. {
  1133. IPersist *p;
  1134. hr = punk->QueryInterface(IID_PPV_ARG(IPersist, &p));
  1135. // sometimes we can do this since they dont answer the
  1136. // the QI for IPersist. but we cant do it on NT4 plain
  1137. // since the net hood faults if the psf is just a \\server
  1138. if (FAILED(hr) && (GetUIVersion() > 3))
  1139. {
  1140. //
  1141. // APPCOMPAT - we have some issues here on downlevel implementations of IPersistFolder
  1142. // so in order to protect ourselves from bad implementations we will wrap this in a simple
  1143. // exception handler.
  1144. //
  1145. __try
  1146. {
  1147. IPersistFolder *pf;
  1148. hr = punk->QueryInterface(IID_PPV_ARG(IPersistFolder, &pf));
  1149. p = pf;
  1150. }
  1151. __except (EXCEPTION_EXECUTE_HANDLER)
  1152. {
  1153. hr = E_NOTIMPL;
  1154. }
  1155. }
  1156. if (SUCCEEDED(hr))
  1157. {
  1158. hr = p->GetClassID(pclsid);
  1159. p->Release();
  1160. }
  1161. }
  1162. return hr;
  1163. }
  1164. STDAPI IUnknown_QueryService(IUnknown* punk, REFGUID guidService, REFIID riid, void **ppvOut)
  1165. {
  1166. *ppvOut = NULL;
  1167. HRESULT hr = E_FAIL;
  1168. if (punk)
  1169. {
  1170. IServiceProvider *psp;
  1171. hr = punk->QueryInterface(IID_PPV_ARG(IServiceProvider, &psp));
  1172. ASSERT(SUCCEEDED(hr) ? psp != NULL : psp == NULL); // COM rules
  1173. if (SUCCEEDED(hr))
  1174. {
  1175. hr = psp->QueryService(guidService, riid, ppvOut);
  1176. psp->Release();
  1177. }
  1178. }
  1179. return hr;
  1180. }
  1181. STDAPI IUnknown_QueryServiceForWebBrowserApp(IUnknown* punk, REFIID riid, void **ppvOut)
  1182. {
  1183. IServiceProvider* psp;
  1184. HRESULT hr = IUnknown_QueryService(punk, SID_STopLevelBrowser, IID_IServiceProvider, (LPVOID*)&psp);
  1185. if (SUCCEEDED(hr))
  1186. {
  1187. hr = psp->QueryService(SID_SWebBrowserApp, riid, ppvOut);
  1188. psp->Release();
  1189. }
  1190. else
  1191. *ppvOut = NULL;
  1192. return hr;
  1193. }
  1194. STDAPI IUnknown_ShowBrowserBar(IUnknown* punk, REFCLSID clsidBrowserBar, BOOL fShow)
  1195. {
  1196. IWebBrowser2* pwb2;
  1197. HRESULT hr = IUnknown_QueryServiceForWebBrowserApp(punk, IID_PPV_ARG(IWebBrowser2, &pwb2));
  1198. if (SUCCEEDED(hr))
  1199. {
  1200. SA_BSTRGUID strClsid;
  1201. InitFakeBSTR(&strClsid, clsidBrowserBar);
  1202. VARIANT varClsid;
  1203. V_VT(&varClsid) = VT_BSTR;
  1204. V_BSTR(&varClsid) = strClsid.wsz;
  1205. VARIANT varShow;
  1206. V_VT(&varShow) = VT_BOOL;
  1207. V_BOOL(&varShow) = (fShow) ? VARIANT_TRUE : VARIANT_FALSE;
  1208. VARIANT varEmpty = {0};
  1209. hr = pwb2->ShowBrowserBar(&varClsid, &varShow, &varEmpty);
  1210. pwb2->Release();
  1211. }
  1212. return hr;
  1213. }
  1214. #if defined(DEBUG) && 0 // defined(NOTYET)
  1215. //
  1216. // IUnknown_IsCanonical checks if the interface is the canonical IUnknown
  1217. // for the object.
  1218. //
  1219. // S_OK = yes it is
  1220. // S_FALSE = no it isn't
  1221. // error = IUnknown implementation is buggy.
  1222. //
  1223. // If you get an error back, it means that the IUnknown is incorrectly
  1224. // implemented, and you probably should avoid doing anything with it.
  1225. //
  1226. STDAPI_(HRESULT) IUnknown_IsCanonical(IUnknown *punk)
  1227. {
  1228. IUnknown *punkT;
  1229. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IUnknown, &punkT));
  1230. if (EVAL(SUCCEEDED(hr)))
  1231. {
  1232. punkT->Release();
  1233. if (punk == punkT)
  1234. {
  1235. hr = S_OK;
  1236. }
  1237. else
  1238. {
  1239. hr = S_FALSE;
  1240. }
  1241. }
  1242. return hr;
  1243. }
  1244. #endif
  1245. //
  1246. // QueryInterface that doesn't affect the refcount. Use this when doing
  1247. // funny aggregation games.
  1248. //
  1249. // In order for this QI/Release trick to work, the punkOuter must be the
  1250. // canonical IUnknown for the outer object. It is the caller's
  1251. // responsibility to ensure this.
  1252. //
  1253. // punkOuter - The controlling unknown (must be canonical)
  1254. // punkTarget - The thing that receives the QI (must be controlled
  1255. // by punkOuter)
  1256. // riid - The interface to get
  1257. // ppvOut - Where to put the result
  1258. //
  1259. // On success, the interface is obtained from the punkTarget, and the
  1260. // refcount generated by the QI is removed from the punkOuter.
  1261. //
  1262. // If either punkOuter or punkTarget is NULL, we vacuously fail with
  1263. // E_NOINTERFACE.
  1264. //
  1265. // When querying from an outer to an inner, punkOuter is the outer, and
  1266. // punkTarget is the inner.
  1267. //
  1268. // When querying from an inner to an outer, punkOuter and punkTarget are
  1269. // both the outer.
  1270. //
  1271. STDAPI SHWeakQueryInterface(IUnknown *punkOuter, IUnknown *punkTarget, REFIID riid, void **ppvOut)
  1272. {
  1273. HRESULT hres;
  1274. if (punkOuter && punkTarget)
  1275. {
  1276. #if defined(DEBUG) && 0 // defined(NOTYET)
  1277. // RaymondC hasn't yet fixed all our aggregatable classes, so this
  1278. // assertion fires too often
  1279. ASSERT(IUnknown_IsCanonical(punkOuter));
  1280. #endif
  1281. hres = punkTarget->QueryInterface(riid, ppvOut);
  1282. if (SUCCEEDED(hres))
  1283. {
  1284. punkOuter->Release();
  1285. hres = S_OK;
  1286. }
  1287. else
  1288. {
  1289. // Double-check that QI isn't buggy.
  1290. ASSERT(*ppvOut == NULL);
  1291. }
  1292. }
  1293. else
  1294. {
  1295. hres = E_NOINTERFACE;
  1296. }
  1297. if (FAILED(hres))
  1298. {
  1299. *ppvOut = NULL;
  1300. }
  1301. return hres;
  1302. }
  1303. //
  1304. // Release an interface that was obtained via SHWeakQueryInterface.
  1305. //
  1306. // punkOuter - The controlling unknown
  1307. // ppunk - The IUnknown to release
  1308. //
  1309. STDAPI_(void) SHWeakReleaseInterface(IUnknown *punkOuter, IUnknown **ppunk)
  1310. {
  1311. if (*ppunk)
  1312. {
  1313. ASSERT(IS_VALID_CODE_PTR(punkOuter, IUnknown));
  1314. punkOuter->AddRef();
  1315. IUnknown_AtomicRelease((void **)ppunk);
  1316. }
  1317. }
  1318. HRESULT IUnknown_SetOptions(IUnknown * punk, DWORD dwACLOptions)
  1319. {
  1320. IACList2 * pal2;
  1321. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IACList2, &pal2));
  1322. if (SUCCEEDED(hr))
  1323. {
  1324. hr = pal2->SetOptions(dwACLOptions);
  1325. pal2->Release();
  1326. }
  1327. return hr;
  1328. }
  1329. #define SZ_REGKEY_TYPEDCMDMRU L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\RunMRU"
  1330. #define SZ_REGKEY_TYPEDURLMRU L"Software\\Microsoft\\Internet Explorer\\TypedURLs"
  1331. HRESULT InitializeAndAddACLMRU(IObjMgr *pmulti, LPCWSTR pszRegKey)
  1332. {
  1333. IUnknown *punk;
  1334. HRESULT hr = CoCreateInstance(CLSID_ACLCustomMRU, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punk));
  1335. if (SUCCEEDED(hr))
  1336. {
  1337. IACLCustomMRU *pmru;
  1338. hr = punk->QueryInterface(IID_PPV_ARG(IACLCustomMRU, &pmru));
  1339. if (SUCCEEDED(hr))
  1340. {
  1341. hr = pmru->Initialize(pszRegKey, 26);
  1342. if (SUCCEEDED(hr))
  1343. {
  1344. pmulti->Append(punk);
  1345. }
  1346. pmru->Release();
  1347. }
  1348. punk->Release();
  1349. }
  1350. return hr;
  1351. }
  1352. IUnknown * ACGetLists(DWORD dwFlags, DWORD dwACLOptions)
  1353. {
  1354. IUnknown * punkACLMulti = NULL;
  1355. HRESULT hr = CoCreateInstance(CLSID_ACLMulti, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punkACLMulti));
  1356. AssertMsg((CO_E_NOTINITIALIZED != hr), TEXT("SHAutoComplete() can not use AutoComplete because OleInitialize() was never called."));
  1357. if (SUCCEEDED(hr))
  1358. {
  1359. IObjMgr * pomMulti;
  1360. hr = punkACLMulti->QueryInterface(IID_PPV_ARG(IObjMgr, &pomMulti));
  1361. if (SUCCEEDED(hr))
  1362. {
  1363. if (dwFlags & SHACF_URLMRU)
  1364. {
  1365. // ADD The MRU List-- add both URL and run dialog MRU
  1366. hr = InitializeAndAddACLMRU(pomMulti, SZ_REGKEY_TYPEDCMDMRU);
  1367. if (SUCCEEDED(hr))
  1368. hr = InitializeAndAddACLMRU(pomMulti, SZ_REGKEY_TYPEDURLMRU);
  1369. }
  1370. if (dwFlags & SHACF_URLHISTORY)
  1371. {
  1372. // ADD The History List
  1373. IUnknown * punkACLHist;
  1374. hr = CoCreateInstance(CLSID_ACLHistory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punkACLHist));
  1375. if (SUCCEEDED(hr))
  1376. {
  1377. pomMulti->Append(punkACLHist);
  1378. IUnknown_SetOptions(punkACLHist, dwACLOptions);
  1379. punkACLHist->Release();
  1380. }
  1381. }
  1382. if ((dwFlags & SHACF_FILESYSTEM) ||
  1383. (dwFlags & SHACF_FILESYS_DIRS) ||
  1384. (dwFlags & SHACF_FILESYS_ONLY))
  1385. {
  1386. // ADD The ISF List
  1387. IUnknown * punkACLISF;
  1388. hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IUnknown, &punkACLISF));
  1389. if (SUCCEEDED(hr))
  1390. {
  1391. pomMulti->Append(punkACLISF);
  1392. IUnknown_SetOptions(punkACLISF, dwACLOptions);
  1393. punkACLISF->Release();
  1394. }
  1395. }
  1396. pomMulti->Release();
  1397. }
  1398. }
  1399. return punkACLMulti;
  1400. }
  1401. #define SZ_REGKEY_AUTOCOMPLETE_TAB TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Explorer\\AutoComplete")
  1402. #define SZ_REGVALUE_AUTOCOMPLETE_TAB TEXT("Always Use Tab")
  1403. #define BOOL_NOT_SET 0x00000005
  1404. DWORD _UpdateAutoCompleteFlags(DWORD dwFlags, DWORD * pdwACLOptions)
  1405. {
  1406. DWORD dwACOptions = 0;
  1407. *pdwACLOptions = 0;
  1408. if (!(SHACF_AUTOAPPEND_FORCE_OFF & dwFlags) &&
  1409. ((SHACF_AUTOAPPEND_FORCE_ON & dwFlags) ||
  1410. SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOAPPEND, FALSE, /*default:*/FALSE)))
  1411. {
  1412. dwACOptions |= ACO_AUTOAPPEND;
  1413. }
  1414. if (!(SHACF_AUTOSUGGEST_FORCE_OFF & dwFlags) &&
  1415. ((SHACF_AUTOSUGGEST_FORCE_ON & dwFlags) ||
  1416. SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOSUGGEST, FALSE, /*default:*/TRUE)))
  1417. {
  1418. dwACOptions |= ACO_AUTOSUGGEST;
  1419. }
  1420. if (SHACF_USETAB & dwFlags)
  1421. {
  1422. dwACOptions |= ACO_USETAB;
  1423. }
  1424. if (SHACF_FILESYS_DIRS & dwFlags)
  1425. {
  1426. *pdwACLOptions |= ACLO_FILESYSDIRS;
  1427. }
  1428. else if (SHACF_FILESYS_ONLY & dwFlags)
  1429. {
  1430. *pdwACLOptions |= ACLO_FILESYSONLY;
  1431. }
  1432. // Windows uses the TAB key to move between controls in a dialog. UNIX and other
  1433. // operating systems that use AutoComplete have traditionally used the TAB key to
  1434. // iterate thru the AutoComplete possibilities. We need to default to disable the
  1435. // TAB key (ACO_USETAB) unless the caller specifically wants it. We will also
  1436. // turn it on
  1437. static BOOL s_fAlwaysUseTab = BOOL_NOT_SET;
  1438. if (BOOL_NOT_SET == s_fAlwaysUseTab)
  1439. s_fAlwaysUseTab = SHRegGetBoolUSValue(SZ_REGKEY_AUTOCOMPLETE_TAB, SZ_REGVALUE_AUTOCOMPLETE_TAB, FALSE, FALSE);
  1440. if (s_fAlwaysUseTab)
  1441. dwACOptions |= ACO_USETAB;
  1442. return dwACOptions;
  1443. }
  1444. /****************************************************\
  1445. FUNCTION: SHAutoComplete
  1446. DESCRIPTION:
  1447. This function will have AutoComplete take over
  1448. an editbox to help autocomplete DOS paths.
  1449. Caller needs to have called CoInitialize() or OleInitialize()
  1450. and cannot call CoUninit/OleUninit until after
  1451. WM_DESTROY on hwndEdit.
  1452. \****************************************************/
  1453. STDAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
  1454. {
  1455. IUnknown * punkACL;
  1456. HRESULT hr = E_OUTOFMEMORY;
  1457. DWORD dwACLOptions = 0;
  1458. DWORD dwACOptions = _UpdateAutoCompleteFlags(dwFlags, &dwACLOptions);
  1459. if (SHACF_DEFAULT == dwFlags)
  1460. dwFlags = (SHACF_FILESYSTEM | SHACF_URLALL);
  1461. punkACL = ACGetLists(dwFlags, dwACLOptions);
  1462. if (punkACL) // May fail on low memory.
  1463. {
  1464. IAutoComplete2 * pac;
  1465. // Create the AutoComplete Object
  1466. hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IAutoComplete2, &pac));
  1467. if (SUCCEEDED(hr))
  1468. {
  1469. if (SHPinDllOfCLSID(&CLSID_ACListISF) &&
  1470. SHPinDllOfCLSID(&CLSID_AutoComplete))
  1471. {
  1472. hr = pac->Init(hwndEdit, punkACL, NULL, NULL);
  1473. pac->SetOptions(dwACOptions);
  1474. }
  1475. else
  1476. {
  1477. hr = E_FAIL;
  1478. }
  1479. pac->Release();
  1480. }
  1481. punkACL->Release();
  1482. }
  1483. return hr;
  1484. }
  1485. //*** IOleCommandTarget helpers {
  1486. #define ISPOW2(i) (((i) & ~((i) - 1)) == (i))
  1487. //*** IsQSForward -- (how) should i forward an IOleCT::Exec/QS command?
  1488. // ENTRY/EXIT
  1489. // nCmdID the usual; plus special value -1 means just check pguidCmdGroup
  1490. // hr S_OK|n if recognized (see below); o.w. OLECMDERR_E_NOTSUPPORTED
  1491. // S_OK|+1 down
  1492. // S_OK|+2 broadcast down
  1493. // S_OK|-1 up
  1494. // S_OK|-2 broadcast up (unused?)
  1495. // NOTES
  1496. // WARNING: we never touch anything but the 1st field of rgCmds, so
  1497. // IsExecForward can (and does!) lie and pass us '(OLECMD *) &nCmdID'.
  1498. //
  1499. STDAPI IsQSForward(const GUID *pguidCmdGroup, int cCmds, OLECMD *pCmds)
  1500. {
  1501. int octd = 0;
  1502. ASSERT(OCTD_DOWN > 0 && OCTD_DOWNBROADCAST > OCTD_DOWN);
  1503. ASSERT(ISPOW2(OCTD_DOWN) && ISPOW2(OCTD_DOWNBROADCAST));
  1504. ASSERT(OCTD_UP < 0);
  1505. if (pguidCmdGroup == NULL)
  1506. {
  1507. for (; cCmds > 0; pCmds++, cCmds--)
  1508. {
  1509. switch (pCmds->cmdID)
  1510. {
  1511. case OLECMDID_STOP:
  1512. case OLECMDID_REFRESH:
  1513. case OLECMDID_ENABLE_INTERACTION:
  1514. // down (broadcast)
  1515. octd |= OCTD_DOWNBROADCAST;
  1516. break;
  1517. case OLECMDID_CUT:
  1518. case OLECMDID_COPY:
  1519. case OLECMDID_PASTE:
  1520. case OLECMDID_SELECTALL:
  1521. // down (singleton)
  1522. octd |= OCTD_DOWN;
  1523. break;
  1524. default:
  1525. octd |= +4;
  1526. break;
  1527. }
  1528. }
  1529. }
  1530. else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
  1531. {
  1532. for (; cCmds > 0; pCmds++, cCmds--)
  1533. {
  1534. switch (pCmds->cmdID)
  1535. {
  1536. case SBCMDID_FILEDELETE:
  1537. case SBCMDID_FILEPROPERTIES:
  1538. case SBCMDID_FILERENAME:
  1539. case SBCMDID_CREATESHORTCUT:
  1540. octd |= OCTD_DOWN;
  1541. break;
  1542. }
  1543. }
  1544. }
  1545. #ifdef DEBUG
  1546. // make sure only one bit set
  1547. if (!ISPOW2(octd))
  1548. {
  1549. // e.g. if we have both down and broadcast guys, the caller
  1550. // will have to be careful to have his IOleCT::QS forward them
  1551. // separately and then merge them together.
  1552. ASSERT(0); // probably best for caller to do 2 separate calls
  1553. TraceMsg(DM_WARNING, "ief: singleton/broadcast mixture");
  1554. }
  1555. #endif
  1556. if (octd == 0 || (octd & 4))
  1557. {
  1558. // octd&4: if anyone is bogus, make them all be, to flesh out
  1559. // bugs where the caller is passing us a mixture we can't handle
  1560. return OLECMDERR_E_NOTSUPPORTED;
  1561. }
  1562. // aka (S_OK|octd)
  1563. return MAKE_HRESULT(ERROR_SUCCESS, FACILITY_NULL, octd);
  1564. }
  1565. //*** MayQSForward -- forward IOleCT::QS if appropriate
  1566. //
  1567. STDAPI MayQSForward(IUnknown *punk, int iUpDown, const GUID *pguidCmdGroup,
  1568. ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  1569. {
  1570. HRESULT hrTmp;
  1571. hrTmp = IsQSForward(pguidCmdGroup, cCmds, rgCmds);
  1572. if (SUCCEEDED(hrTmp))
  1573. {
  1574. // we know how to forward
  1575. if (HRESULT_CODE(hrTmp) > 0 && iUpDown > 0
  1576. || HRESULT_CODE(hrTmp) < 0 && iUpDown < 0)
  1577. {
  1578. // punk pts in the right direction for nCmdID
  1579. #if 0
  1580. // it's caller's bug if they have more than 1 kid
  1581. // can't ASSERT though because broadcast to only child is o.k.
  1582. if (HRESULT_CODE(hrTmp) > 1)
  1583. TraceMsg(DM_WARNING, "med: Exec broadcast to singleton");
  1584. #endif
  1585. return IUnknown_QueryStatus(punk, pguidCmdGroup, cCmds, rgCmds,
  1586. pcmdtext);
  1587. }
  1588. }
  1589. return OLECMDERR_E_NOTSUPPORTED;
  1590. }
  1591. //*** MayExecForward -- forward IOleCT::Exec if appropriate
  1592. // NOTES
  1593. // should iUpDown be an int or an HRESULT?
  1594. STDAPI MayExecForward(IUnknown *punk, int iUpDown, const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  1595. {
  1596. HRESULT hrTmp;
  1597. hrTmp = IsExecForward(pguidCmdGroup, nCmdID);
  1598. if (SUCCEEDED(hrTmp))
  1599. {
  1600. // we know how to forward
  1601. if (HRESULT_CODE(hrTmp) > 0 && iUpDown > 0
  1602. || HRESULT_CODE(hrTmp) < 0 && iUpDown < 0)
  1603. {
  1604. // punk pts in the right direction for nCmdID
  1605. #if 0
  1606. // it's caller's bug if they have more than 1 kid
  1607. // can't ASSERT though because broadcast to only child is o.k.
  1608. if (HRESULT_CODE(hrTmp) > 1)
  1609. TraceMsg(DM_WARNING, "md: Exec broadcast to singleton));
  1610. #endif
  1611. return IUnknown_Exec(punk, pguidCmdGroup, nCmdID, nCmdexecopt,
  1612. pvarargIn, pvarargOut);
  1613. }
  1614. }
  1615. return OLECMDERR_E_NOTSUPPORTED;
  1616. }
  1617. STDAPI_(HMENU) SHLoadMenuPopup(HINSTANCE hinst, UINT id)
  1618. {
  1619. HMENU hMenuSub = NULL;
  1620. HMENU hMenu = LoadMenuWrapW(hinst, MAKEINTRESOURCEW(id));
  1621. if (hMenu)
  1622. {
  1623. hMenuSub = GetSubMenu(hMenu, 0);
  1624. if (hMenuSub)
  1625. {
  1626. RemoveMenu(hMenu, 0, MF_BYPOSITION);
  1627. }
  1628. DestroyMenuWrap(hMenu);
  1629. }
  1630. return hMenuSub;
  1631. }
  1632. //-----------------------------------------------------------------------------
  1633. typedef LRESULT (WINAPI *POSTORSENDMESSAGEPROC)(HWND, UINT, WPARAM, LPARAM);
  1634. struct propagatemsg
  1635. {
  1636. HWND hwndParent;
  1637. int iFlags; // "int" for back compatibility; used to be "BOOL"
  1638. UINT uMsg;
  1639. WPARAM wParam;
  1640. LPARAM lParam;
  1641. POSTORSENDMESSAGEPROC PostOrSendMessage;
  1642. };
  1643. BOOL CALLBACK PropagateCallback(HWND hwndChild, LPARAM lParam)
  1644. {
  1645. struct propagatemsg *pmsg = (struct propagatemsg *)lParam;
  1646. if ((pmsg->iFlags & SPM_ONELEVEL) && GetParent(hwndChild) != pmsg->hwndParent)
  1647. {
  1648. // Wrong parent; skip it
  1649. return TRUE;
  1650. }
  1651. pmsg->PostOrSendMessage(hwndChild, pmsg->uMsg, pmsg->wParam, pmsg->lParam);
  1652. return TRUE;
  1653. }
  1654. STDAPI_(void) SHPropagateMessage(HWND hwndParent, UINT uMsg, WPARAM wParam, LPARAM lParam, int iFlags)
  1655. {
  1656. if (!hwndParent)
  1657. return;
  1658. struct propagatemsg msg;
  1659. msg.hwndParent = hwndParent;
  1660. msg.iFlags = iFlags;
  1661. msg.uMsg = uMsg;
  1662. msg.wParam = wParam;
  1663. msg.lParam = lParam;
  1664. if (iFlags & SPM_SEND)
  1665. {
  1666. msg.PostOrSendMessage = IsWindowUnicode(hwndParent) ?
  1667. SendMessageW : SendMessageA;
  1668. }
  1669. else
  1670. {
  1671. msg.PostOrSendMessage = (POSTORSENDMESSAGEPROC)
  1672. (IsWindowUnicode(hwndParent) ?
  1673. PostMessageW : PostMessageA);
  1674. }
  1675. EnumChildWindows(hwndParent, /*(WNDENUMPROC)*/PropagateCallback, (LPARAM)&msg);
  1676. }
  1677. LRESULT SHDefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1678. {
  1679. if (IsWindowUnicode(hwnd))
  1680. {
  1681. return DefWindowProcW(hwnd, uMsg, wParam, lParam);
  1682. }
  1683. else
  1684. {
  1685. return DefWindowProcA(hwnd, uMsg, wParam, lParam);
  1686. }
  1687. }
  1688. // Returns the submenu of the given menu and ID. Returns NULL if there
  1689. // is no submenu
  1690. STDAPI_(HMENU) SHGetMenuFromID(HMENU hmMain, UINT uID)
  1691. {
  1692. HMENU hmenuRet = NULL;
  1693. if (!hmMain)
  1694. return NULL;
  1695. MENUITEMINFO mii;
  1696. mii.cbSize = sizeof(mii);
  1697. mii.fMask = MIIM_SUBMENU;
  1698. if (GetMenuItemInfo(hmMain, uID, FALSE, &mii))
  1699. hmenuRet = mii.hSubMenu;
  1700. return hmenuRet;
  1701. }
  1702. STDAPI_(int) SHMenuIndexFromID(HMENU hm, UINT id)
  1703. {
  1704. for (int index = GetMenuItemCount(hm)-1; index>=0; index--)
  1705. {
  1706. // We have to use GetMenuItemInfo and not the simpler GetMenuItemID
  1707. // because GetMenuItemID does not support submenus (grr).
  1708. MENUITEMINFO mii;
  1709. mii.cbSize = sizeof(MENUITEMINFO);
  1710. mii.fMask = MIIM_ID;
  1711. mii.cch = 0; // just in case
  1712. if (GetMenuItemInfo(hm, (UINT)index, TRUE, &mii)
  1713. && (mii.wID == id))
  1714. {
  1715. break;
  1716. }
  1717. }
  1718. return(index);
  1719. }
  1720. STDAPI_(void) SHRemoveAllSubMenus(HMENU hmenu)
  1721. {
  1722. int cItems = GetMenuItemCount(hmenu);
  1723. int i;
  1724. for (i=cItems-1; i>=0; i--)
  1725. {
  1726. if (GetSubMenu(hmenu, i))
  1727. RemoveMenu(hmenu, i, MF_BYPOSITION);
  1728. }
  1729. }
  1730. STDAPI_(void) SHEnableMenuItem(HMENU hmenu, UINT id, BOOL fEnable)
  1731. {
  1732. EnableMenuItem(hmenu, id, fEnable ?
  1733. (MF_BYCOMMAND | MF_ENABLED) : (MF_BYCOMMAND| MF_GRAYED));
  1734. }
  1735. STDAPI_(void) SHCheckMenuItem(HMENU hmenu, UINT id, BOOL fChecked)
  1736. {
  1737. CheckMenuItem(hmenu, id,
  1738. fChecked ? (MF_BYCOMMAND | MF_CHECKED) : (MF_BYCOMMAND | MF_UNCHECKED));
  1739. }
  1740. //
  1741. // IStream 'saner/simpler' Wrappers that dont use exactly the same params, and have simpler
  1742. // output. closer mirroring the way we use them.
  1743. //
  1744. // NOTES
  1745. // 'saner' means that it only returns SUCCEEDED when it reads everything
  1746. // you asked for. 'simpler' means no 'pcbRead' param.
  1747. STDAPI IStream_Read(IStream *pstm, void *pv, ULONG cb)
  1748. {
  1749. ASSERT(pstm);
  1750. ULONG cbRead;
  1751. HRESULT hr = pstm->Read(pv, cb, &cbRead);
  1752. if (SUCCEEDED(hr) && cbRead != cb)
  1753. {
  1754. hr = E_FAIL;
  1755. }
  1756. return hr;
  1757. }
  1758. STDAPI IStream_Write(IStream *pstm, LPCVOID pvIn, ULONG cbIn)
  1759. {
  1760. ASSERT(pstm);
  1761. DWORD cb;
  1762. HRESULT hr = pstm->Write(pvIn, cbIn, &cb);
  1763. if (SUCCEEDED(hr) && cbIn != cb)
  1764. hr = E_FAIL;
  1765. return hr;
  1766. }
  1767. STDAPI IStream_Reset(IStream *pstm)
  1768. {
  1769. return pstm->Seek(c_li0, STREAM_SEEK_SET, NULL);
  1770. }
  1771. STDAPI IStream_Size(IStream *pstm, ULARGE_INTEGER *pui)
  1772. {
  1773. ASSERT(pstm);
  1774. ASSERT(pui);
  1775. STATSTG st = {0};
  1776. // WARNING: What if IStream::Stat is not implemented?
  1777. // Win95/NT4/IE4's IStream had this problem. Fixed
  1778. // for NT5...
  1779. //
  1780. HRESULT hr = pstm->Stat(&st, STATFLAG_NONAME);
  1781. if (SUCCEEDED(hr))
  1782. {
  1783. *pui = st.cbSize;
  1784. }
  1785. return hr;
  1786. }
  1787. STDAPI IStream_ReadPidl(IStream *pstm, LPITEMIDLIST *ppidlOut)
  1788. {
  1789. *ppidlOut = NULL;
  1790. DWORD cbPidl;
  1791. HRESULT hr = IStream_Read(pstm, &cbPidl, sizeof(cbPidl));
  1792. if (SUCCEEDED(hr))
  1793. {
  1794. LPITEMIDLIST pidl = (LPITEMIDLIST)CoTaskMemAlloc(cbPidl);
  1795. if (pidl)
  1796. {
  1797. if (SUCCEEDED(hr = IStream_Read(pstm, pidl, cbPidl)))
  1798. {
  1799. // Validate that what we have is a well-formed pidl
  1800. LPITEMIDLIST pidlEnd = _ILSkip(pidl, cbPidl - sizeof(USHORT));
  1801. LPITEMIDLIST pidlT = pidl;
  1802. while (pidlT <= pidlEnd && pidlT->mkid.cb)
  1803. {
  1804. pidlT = _ILNext(pidlT);
  1805. }
  1806. if (pidlT == pidlEnd && pidlT->mkid.cb == 0)
  1807. {
  1808. *ppidlOut = pidl;
  1809. hr = S_OK;
  1810. }
  1811. else
  1812. {
  1813. hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
  1814. }
  1815. }
  1816. if (FAILED(hr))
  1817. {
  1818. // Cannot use ILFree because it might not be a valid pidl
  1819. CoTaskMemFree(pidl);
  1820. }
  1821. }
  1822. else
  1823. {
  1824. hr = E_OUTOFMEMORY;
  1825. }
  1826. }
  1827. return hr;
  1828. }
  1829. STDAPI IStream_WritePidl(IStream *pstm, LPCITEMIDLIST pidlWrite)
  1830. {
  1831. HRESULT hr;
  1832. ASSERT(pidlWrite);
  1833. DWORD cbPidl = ILGetSize(pidlWrite);
  1834. if (SUCCEEDED(hr = IStream_Write(pstm, &cbPidl, sizeof(cbPidl))) &&
  1835. SUCCEEDED(hr = IStream_Write(pstm, pidlWrite, cbPidl)))
  1836. {
  1837. // woo-hoo, all written successfully
  1838. }
  1839. return hr;
  1840. }
  1841. STDAPI_(BOOL) SHRegisterClassA(const WNDCLASSA* pwc)
  1842. {
  1843. WNDCLASSA wc;
  1844. if (!GetClassInfoA(pwc->hInstance, pwc->lpszClassName, &wc))
  1845. {
  1846. return RegisterClassA(pwc);
  1847. }
  1848. return TRUE;
  1849. }
  1850. //
  1851. // Warning! This uses RegisterClassWrap, which means that if we
  1852. // are an ANSI-only platform, your window class will be registered
  1853. // as **ANSI**, not as UNICODE. You window procedure needs to call
  1854. // IsWindowUnicode() to determine how to interpret incoming string
  1855. // parameters.
  1856. //
  1857. STDAPI_(BOOL) SHRegisterClassW(const WNDCLASSW* pwc)
  1858. {
  1859. WNDCLASSW wc;
  1860. if (!GetClassInfoWrapW(pwc->hInstance, pwc->lpszClassName, &wc))
  1861. {
  1862. return RegisterClassWrapW(pwc);
  1863. }
  1864. return TRUE;
  1865. }
  1866. //
  1867. // SHUnregisterClasses unregisters an array of class names.
  1868. //
  1869. STDAPI_(void) SHUnregisterClassesA(HINSTANCE hinst, const LPCSTR *rgpszClasses, UINT cpsz)
  1870. {
  1871. for (UINT i = 0; i < cpsz; i++)
  1872. {
  1873. WNDCLASSA wc;
  1874. if (GetClassInfoA(hinst, rgpszClasses[i], &wc))
  1875. {
  1876. UnregisterClassA(rgpszClasses[i], hinst);
  1877. }
  1878. }
  1879. }
  1880. STDAPI_(void) SHUnregisterClassesW(HINSTANCE hinst, const LPCWSTR *rgpszClasses, UINT cpsz)
  1881. {
  1882. for (UINT i = 0; i < cpsz; i++)
  1883. {
  1884. WNDCLASSW wc;
  1885. if (GetClassInfoWrapW(hinst, rgpszClasses[i], &wc))
  1886. {
  1887. UnregisterClassWrapW(rgpszClasses[i], hinst);
  1888. }
  1889. }
  1890. }
  1891. //
  1892. // This structure is used by the UNICODE version of this function. So, the pointers point to
  1893. // wide characters strings.
  1894. typedef struct tagMBCINFOW { // Used only between the routine and its DlgProc
  1895. UINT uType;
  1896. LPCWSTR pwszText;
  1897. LPCWSTR pwszTitle;
  1898. LPCWSTR pwszRegPath;
  1899. LPCWSTR pwszRegVal;
  1900. DLGPROC pUserDlgProc;
  1901. void * pUserData;
  1902. } MBCINFOW, *LPMBCINFOW;
  1903. void _MoveDlgItem(HDWP hdwp, HWND hDlg, int nItem, int x, int y)
  1904. {
  1905. RECT rc;
  1906. HWND hwnd = GetDlgItem(hDlg, nItem);
  1907. GetClientRect(hwnd, &rc);
  1908. MapWindowPoints(hwnd, hDlg, (LPPOINT) &rc, 2);
  1909. DeferWindowPos(hdwp, hwnd, 0, rc.left + x, rc.top + y, 0, 0,
  1910. SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
  1911. }
  1912. void _AddIcon(HWND hDlg, LPRECT prcNextChild, UINT uType)
  1913. {
  1914. HICON hic;
  1915. switch (uType & MB_ICONMASK)
  1916. {
  1917. case MB_ICONHAND:
  1918. hic = LoadIcon(NULL, IDI_ERROR); // == IDI_HAND
  1919. break;
  1920. case MB_ICONQUESTION:
  1921. hic = LoadIcon(NULL, IDI_QUESTION);
  1922. break;
  1923. case MB_ICONEXCLAMATION:
  1924. hic = LoadIcon(NULL, IDI_WARNING); // == IDI_EXCLAMATION
  1925. break;
  1926. case MB_ICONINFORMATION:
  1927. hic = LoadIcon(NULL, IDI_INFORMATION); // == IDI_ASTERISK
  1928. break;
  1929. default:
  1930. hic = NULL;
  1931. break;
  1932. }
  1933. if (hic)
  1934. {
  1935. prcNextChild->left += GetSystemMetrics(SM_CXICON) + 10;
  1936. SendDlgItemMessage(hDlg, IDC_MBC_ICON, STM_SETIMAGE, (WPARAM) IMAGE_ICON, (LPARAM) hic);
  1937. }
  1938. }
  1939. void _RecalcWindowHeight(HWND hWnd, LPMBCINFOW lpmbci)
  1940. {
  1941. HDC hdc = GetDC(hWnd);
  1942. RECT rc;
  1943. HWND hwndText = GetDlgItem(hWnd,IDC_MBC_TEXT);
  1944. HDWP hdwp;
  1945. int iHeightDelta, cx, cxIcon;
  1946. HFONT hFontSave;
  1947. hFontSave = (HFONT)SelectObject(hdc, GetWindowFont(hwndText));
  1948. // Get the starting rect of the text area (for the width)
  1949. GetClientRect(hwndText, &rc);
  1950. MapWindowPoints(hwndText, hWnd, (LPPOINT) &rc, 2);
  1951. // See if we need to add an icon, slide rc over if we do
  1952. cxIcon = RECTWIDTH(rc);
  1953. _AddIcon(hWnd, &rc, lpmbci->uType);
  1954. cxIcon = RECTWIDTH(rc) - cxIcon;
  1955. // Calc how high the static text area needs to be, given the above width
  1956. iHeightDelta = RECTHEIGHT(rc);
  1957. cx = RECTWIDTH(rc);
  1958. //Note: We need to call the Wide version of DrawText here!
  1959. DrawTextWrapW(hdc, lpmbci->pwszText, -1, &rc, DT_CALCRECT | DT_LEFT | DT_WORDBREAK);
  1960. iHeightDelta = RECTHEIGHT(rc) - iHeightDelta;
  1961. cx = RECTWIDTH(rc) - cx; // Should only change for really long words w/o spaces
  1962. if (cx < 0)
  1963. cx = 0;
  1964. if (hFontSave)
  1965. SelectObject(hdc, hFontSave);
  1966. ReleaseDC(hWnd, hdc);
  1967. hdwp = BeginDeferWindowPos(6);
  1968. if (hdwp)
  1969. {
  1970. DeferWindowPos(hdwp, hwndText, 0, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_NOACTIVATE);
  1971. _MoveDlgItem(hdwp, hWnd, IDC_MBC_CHECK, -cxIcon, iHeightDelta);
  1972. _MoveDlgItem(hdwp, hWnd, IDCANCEL, cx, iHeightDelta);
  1973. _MoveDlgItem(hdwp, hWnd, IDOK, cx, iHeightDelta);
  1974. _MoveDlgItem(hdwp, hWnd, IDYES, cx, iHeightDelta);
  1975. _MoveDlgItem(hdwp, hWnd, IDNO, cx, iHeightDelta);
  1976. EndDeferWindowPos(hdwp);
  1977. GetWindowRect(hWnd, &rc);
  1978. SetWindowPos(hWnd, 0, rc.left - (cx/2), rc.top - (iHeightDelta/2), RECTWIDTH(rc)+cx, RECTHEIGHT(rc)+iHeightDelta, SWP_NOZORDER | SWP_NOACTIVATE);
  1979. }
  1980. return;
  1981. }
  1982. void HideAndDisableWindow(HWND hwnd)
  1983. {
  1984. ShowWindow(hwnd, SW_HIDE);
  1985. EnableWindow(hwnd, FALSE);
  1986. }
  1987. //
  1988. // NOTE: This dialog proc is always UNICODE since both SHMessageBoxCheckA/W thunk to UNICODE and
  1989. // use this procedure.
  1990. //
  1991. BOOL_PTR CALLBACK MessageBoxCheckDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1992. {
  1993. switch (uMsg)
  1994. {
  1995. // we only handle the WM_INITDIALOG so that we can resize the dialog
  1996. // approprately
  1997. case WM_INITDIALOG:
  1998. {
  1999. LPMBCINFOW lpmbci = (LPMBCINFOW)lParam;
  2000. HWND hwndYES = GetDlgItem(hDlg, IDYES);
  2001. HWND hwndNO = GetDlgItem(hDlg, IDNO);
  2002. HWND hwndCANCEL = GetDlgItem(hDlg, IDCANCEL);
  2003. HWND hwndOK = GetDlgItem(hDlg, IDOK);
  2004. _RecalcWindowHeight(hDlg, lpmbci);
  2005. //Note: We need to call the Wide version of SetDlgItemText() here.
  2006. SetDlgItemTextWrapW(hDlg,IDC_MBC_TEXT,lpmbci->pwszText);
  2007. if (lpmbci->pwszTitle)
  2008. SetWindowTextWrapW(hDlg,lpmbci->pwszTitle);
  2009. if ((lpmbci->uType & MB_TYPEMASK) == MB_OKCANCEL)
  2010. {
  2011. SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
  2012. HideAndDisableWindow(hwndYES);
  2013. HideAndDisableWindow(hwndNO);
  2014. SetFocus(hwndOK);
  2015. }
  2016. else if ((lpmbci->uType & MB_TYPEMASK) == MB_OK)
  2017. {
  2018. RECT rc;
  2019. SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
  2020. HideAndDisableWindow(hwndYES);
  2021. HideAndDisableWindow(hwndNO);
  2022. HideAndDisableWindow(hwndCANCEL);
  2023. if (EVAL(GetClientRect(hwndCANCEL, &rc)))
  2024. {
  2025. MapWindowPoints(hwndCANCEL, hDlg, (LPPOINT) &rc, 2);
  2026. EVAL(SetWindowPos(hwndOK, hDlg, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_SHOWWINDOW));
  2027. }
  2028. SetFocus(hwndOK);
  2029. }
  2030. else // MB_YESNO
  2031. {
  2032. SendMessage(hDlg, DM_SETDEFID, IDYES, 0);
  2033. HideAndDisableWindow(hwndOK);
  2034. HideAndDisableWindow(hwndCANCEL);
  2035. SetFocus(hwndYES);
  2036. }
  2037. return (FALSE); // we set the focus, so return false
  2038. }
  2039. }
  2040. // didnt handle this message
  2041. return FALSE;
  2042. }
  2043. //
  2044. // NOTE: The MessageBoxCheckExDlgProc is both UNICODE and ANSI since it dosent really do any string
  2045. // stuff. Our UNICODE/ANSI-ness is determined by our caller.
  2046. //
  2047. BOOL_PTR CALLBACK MessageBoxCheckExDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2048. {
  2049. MBCINFOW* pmbci = NULL;
  2050. HWND hwndCheckBox = GetDlgItem(hDlg, IDC_MESSAGEBOXCHECKEX);
  2051. if (uMsg == WM_INITDIALOG)
  2052. {
  2053. pmbci = (MBCINFOW*)lParam;
  2054. // we have to have this control or we're hopeless
  2055. if (!hwndCheckBox)
  2056. {
  2057. AssertMsg(FALSE, "MessageBoxCheckEx dialog templates must have a control whos ID is IDC_MESSAGEBOXCHECKEX!!");
  2058. EndDialog(hDlg, 0);
  2059. }
  2060. // we use the checkbox to hang our data off of, since the caller
  2061. // might want to use hDlg to hang its data off of.
  2062. SetWindowPtr(hwndCheckBox, GWLP_USERDATA, pmbci);
  2063. }
  2064. else
  2065. {
  2066. pmbci = (MBCINFOW*)GetWindowPtr(hwndCheckBox, GWLP_USERDATA);
  2067. }
  2068. // we get a few messages before we get the WM_INITDIALOG (such as WM_SETFONT)
  2069. // and until we get the WM_INITDIALOG we dont have our pmbci pointer, we just
  2070. // return false
  2071. if (!pmbci)
  2072. return FALSE;
  2073. // now check to see if we have a user specified dlg proc
  2074. if (pmbci->pUserDlgProc)
  2075. {
  2076. // for the messages below, we simply return what the "real" dialog proc
  2077. // said since they do NOT return TRUE/FALSE (eg handled or not handled).
  2078. if (uMsg == WM_CTLCOLORMSGBOX ||
  2079. uMsg == WM_CTLCOLOREDIT ||
  2080. uMsg == WM_CTLCOLORLISTBOX ||
  2081. uMsg == WM_CTLCOLORBTN ||
  2082. uMsg == WM_CTLCOLORDLG ||
  2083. uMsg == WM_CTLCOLORSCROLLBAR ||
  2084. uMsg == WM_CTLCOLORSTATIC ||
  2085. uMsg == WM_COMPAREITEM ||
  2086. uMsg == WM_VKEYTOITEM ||
  2087. uMsg == WM_CHARTOITEM ||
  2088. uMsg == WM_QUERYDRAGICON ||
  2089. uMsg == WM_INITDIALOG)
  2090. {
  2091. return pmbci->pUserDlgProc(hDlg, uMsg, wParam, (uMsg == WM_INITDIALOG) ? (LPARAM)(pmbci->pUserData) : lParam);
  2092. }
  2093. if ((pmbci->pUserDlgProc(hDlg, uMsg, wParam, lParam) != FALSE) &&
  2094. (uMsg != WM_DESTROY))
  2095. {
  2096. // the "real" dialog proc handled it so we are done, except we always
  2097. // need to handle the WM_DESTROY message so we can check the state of
  2098. // the checkbox in order to set the registry key accordingly.
  2099. return TRUE;
  2100. }
  2101. }
  2102. switch (uMsg)
  2103. {
  2104. case WM_CLOSE:
  2105. wParam = IDCANCEL;
  2106. // fall through
  2107. case WM_COMMAND:
  2108. {
  2109. switch (LOWORD(wParam))
  2110. {
  2111. case IDOK:
  2112. case IDYES:
  2113. case IDCANCEL:
  2114. case IDNO:
  2115. EndDialog(hDlg, (int) LOWORD(wParam));
  2116. break;
  2117. }
  2118. break;
  2119. }
  2120. case WM_DESTROY:
  2121. if (IsDlgButtonChecked(hDlg, IDC_MESSAGEBOXCHECKEX) == BST_CHECKED)
  2122. {
  2123. // Note: we need to call the Wide version of this function,
  2124. // since our pmbci is always UNICODE
  2125. SHRegSetUSValueW(pmbci->pwszRegPath, pmbci->pwszRegVal, REG_SZ,
  2126. L"no", sizeof(L"no"), SHREGSET_HKCU);
  2127. }
  2128. break;
  2129. }
  2130. return FALSE;
  2131. }
  2132. // MessageBoxCheckW puts up a simple dialog box, with a checkbox to control if the
  2133. // dialog is to be shown again (eg gives you "dont show me this dlg again" functionality).
  2134. //
  2135. // Call as you would MessageBox, but with three additional parameters:
  2136. // The value to return if they checked "never again", and the reg path and value
  2137. // to store whether it gets shown again.
  2138. // MessageBoxCheckEx allows the user to specify a template that will be used along with
  2139. // a dialog proc that will be called. The only must for the dialog template is that it has
  2140. // to have a checkbox control with ID IDC_MESSAGEBOXCHECKEX (this is the "dont show me this
  2141. // again" checkbox).
  2142. //
  2143. // The default template looks something like the following:
  2144. //
  2145. // DLG_MESSAGEBOXCHECK DIALOG DISCARDABLE 0, 0, 210, 55
  2146. // STYLE DS_MODALFRAME | DS_NOIDLEMSG | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
  2147. // CAPTION "Error!"
  2148. // FONT 8, "MS Shell Dlg"
  2149. // BEGIN
  2150. // ICON 0, IDC_MBC_ICON,5,5,18,20
  2151. // LTEXT "",IDC_MBC_TEXT,5,5,200,8
  2152. // CONTROL "&In the future, do not show me this dialog box",
  2153. // IDC_MESSAGEBOXCHECKEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,20,155,10
  2154. // PUSHBUTTON "OK",IDOK,95,35,50,14
  2155. // PUSHBUTTON "Cancel",IDCANCEL,150,35,50,14
  2156. // END
  2157. //
  2158. // This function is fully implemented in UNICODE and the ANSI version of this function is thunked
  2159. // to this UNICODE version.
  2160. //
  2161. STDAPI_(int) SHMessageBoxCheckW(HWND hwnd, LPCWSTR pwszText, LPCWSTR pwszTitle, UINT uType, int iDefault, LPCWSTR pwszRegVal)
  2162. {
  2163. // -- browseui(unicode) uses this
  2164. MBCINFOW mbci;
  2165. // check first to see if the "dont show me this again" is set
  2166. if (!SHRegGetBoolUSValueW(REGSTR_PATH_MESSAGEBOXCHECKW, pwszRegVal, FALSE, /*default:*/TRUE))
  2167. return iDefault;
  2168. ASSERT(((uType & MB_TYPEMASK) == MB_OKCANCEL) ||
  2169. ((uType & MB_TYPEMASK) == MB_YESNO) ||
  2170. ((uType & MB_TYPEMASK) == MB_OK));
  2171. ASSERT(pwszText != NULL);
  2172. mbci.pwszText = pwszText;
  2173. mbci.pwszTitle = pwszTitle;
  2174. mbci.pwszRegPath = REGSTR_PATH_MESSAGEBOXCHECKW;
  2175. mbci.pwszRegVal = pwszRegVal;
  2176. mbci.uType = uType;
  2177. mbci.pUserData = (LPVOID) &mbci;
  2178. mbci.pUserDlgProc = MessageBoxCheckDlgProc;
  2179. // we use the MessageBoxCheckExDlgProc as the main dlg proc, and allow the MessageBoxCheckDlgProc
  2180. // to be the "user" dlg proc
  2181. ULONG_PTR ul;
  2182. HANDLE h = XP_CreateAndActivateContext(&ul);
  2183. int i = (int)DialogBoxParamWrapW(HINST_THISDLL, MAKEINTRESOURCEW(DLG_MESSAGEBOXCHECK),
  2184. hwnd, MessageBoxCheckExDlgProc, (LPARAM)&mbci);
  2185. XP_DeactivateAndDestroyContext(h, ul);
  2186. return i;
  2187. }
  2188. //
  2189. // This function simply thunks to the UNICODE version.
  2190. //
  2191. //
  2192. STDAPI_(int) SHMessageBoxCheckA(HWND hwnd, LPCSTR pszText, LPCSTR pszTitle, UINT uType, int iDefault, LPCSTR pszRegVal)
  2193. {
  2194. LPWSTR lpwszText = NULL, lpwszTitle = NULL;
  2195. int iTextBuffSize = 0, iTitleBuffSize = 0;
  2196. WCHAR wszRegVal[REGSTR_MAX_VALUE_LENGTH];
  2197. // check first to see if the "dont show me this again" is set
  2198. if (!SHRegGetBoolUSValueA(REGSTR_PATH_MESSAGEBOXCHECKA, pszRegVal, FALSE, /*default:*/TRUE))
  2199. return iDefault;
  2200. // Since there is no MAX possible size for these strings, we dynamically allocate them.
  2201. // Convert the input params into UNICODE.
  2202. if (!(lpwszText = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(iTextBuffSize = lstrlen(pszText)+1))))
  2203. goto End_MsgBoxCheck;
  2204. if (!(lpwszTitle = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(iTitleBuffSize = lstrlen(pszTitle)+1))))
  2205. goto End_MsgBoxCheck;
  2206. // Conver the Ansi strings into Unicode strings.
  2207. SHAnsiToUnicode(pszText, lpwszText, iTextBuffSize);
  2208. SHAnsiToUnicode(pszTitle, lpwszTitle, iTitleBuffSize);
  2209. SHAnsiToUnicode(pszRegVal, wszRegVal, ARRAYSIZE(wszRegVal));
  2210. // Call the UNICODE version of this function.
  2211. iDefault = SHMessageBoxCheckW(hwnd, lpwszText, lpwszTitle, uType, iDefault, wszRegVal);
  2212. // Clean up and return.
  2213. End_MsgBoxCheck:
  2214. if (lpwszText)
  2215. LocalFree((HANDLE)lpwszText);
  2216. if (lpwszTitle)
  2217. LocalFree((HANDLE)lpwszTitle);
  2218. return iDefault;
  2219. }
  2220. //
  2221. // This function calls directly to the helper function
  2222. //
  2223. STDAPI_(int) SHMessageBoxCheckExW(HWND hwnd, HINSTANCE hinst, LPCWSTR pwszTemplateName, DLGPROC pDlgProc, void *pData,
  2224. int iDefault, LPCWSTR pwszRegVal)
  2225. {
  2226. MBCINFOW mbci = {0};
  2227. // check first to see if the "dont show me this again" is set
  2228. if (!SHRegGetBoolUSValueW(REGSTR_PATH_MESSAGEBOXCHECKW, pwszRegVal, FALSE, /*default:*/TRUE))
  2229. return iDefault;
  2230. mbci.pwszRegPath = REGSTR_PATH_MESSAGEBOXCHECKW;
  2231. mbci.pwszRegVal = pwszRegVal;
  2232. mbci.pUserDlgProc = pDlgProc;
  2233. mbci.pUserData = pData;
  2234. ULONG_PTR ul;
  2235. HANDLE h = XP_CreateAndActivateContext(&ul);
  2236. // call the UNICODE function, since the users dlg proc (if it exists) is UNICODE
  2237. int i = (int)DialogBoxParamWrapW(hinst, pwszTemplateName, hwnd, MessageBoxCheckExDlgProc, (LPARAM)&mbci);
  2238. XP_DeactivateAndDestroyContext(h, ul);
  2239. return i;
  2240. }
  2241. //
  2242. // This function thunks the strings and calls the helper function
  2243. //
  2244. STDAPI_(int) SHMessageBoxCheckExA(HWND hwnd, HINSTANCE hinst, LPCSTR pszTemplateName, DLGPROC pDlgProc, void *pData,
  2245. int iDefault, LPCSTR pszRegVal)
  2246. {
  2247. WCHAR wszRegVal[REGSTR_MAX_VALUE_LENGTH];
  2248. MBCINFOW mbci = {0};
  2249. // check first to see if the "dont show me this again" is set
  2250. if (!SHRegGetBoolUSValueA(REGSTR_PATH_MESSAGEBOXCHECKA, pszRegVal, FALSE, /*default:*/TRUE))
  2251. return iDefault;
  2252. // Conver the Ansi strings into Unicode strings.
  2253. SHAnsiToUnicode(pszRegVal, wszRegVal, ARRAYSIZE(wszRegVal));
  2254. mbci.pwszRegPath = REGSTR_PATH_MESSAGEBOXCHECKW; // the MBCINFOW is always UNICODE
  2255. mbci.pwszRegVal = wszRegVal;
  2256. mbci.pUserDlgProc = pDlgProc;
  2257. mbci.pUserData = pData;
  2258. ULONG_PTR ul;
  2259. HANDLE h = XP_CreateAndActivateContext(&ul);
  2260. // call the ANSI function since the users dlg proc (if it exists) is ANSI
  2261. iDefault = (int)DialogBoxParamA(hinst, pszTemplateName, hwnd, MessageBoxCheckExDlgProc, (LPARAM)&mbci);
  2262. XP_DeactivateAndDestroyContext(h, ul);
  2263. return iDefault;
  2264. }
  2265. // "I'm sorry, Dave, I can't let you do that."
  2266. LWSTDAPI_(void) SHRestrictedMessageBox(HWND hwnd)
  2267. {
  2268. ShellMessageBoxWrapW(MLGetHinst(),
  2269. hwnd,
  2270. MAKEINTRESOURCEW(IDS_RESTRICTIONS),
  2271. MAKEINTRESOURCEW(IDS_RESTRICTIONSTITLE),
  2272. MB_OK | MB_ICONSTOP);
  2273. }
  2274. // in:
  2275. // pdrop thing to drop on
  2276. // pdataobj thing we are dropping
  2277. // grfKeyState to force certain operations
  2278. // ppt [optional] point where the drop happens (screen coords)
  2279. //
  2280. // in/out
  2281. // pdwEffect [optional] effects allowed and returns what was performed.
  2282. STDAPI SHSimulateDrop(IDropTarget *pdrop, IDataObject *pdtobj, DWORD grfKeyState,
  2283. const POINTL *ppt, DWORD *pdwEffect)
  2284. {
  2285. POINTL pt;
  2286. DWORD dwEffect;
  2287. if (!ppt)
  2288. {
  2289. ppt = &pt;
  2290. pt.x = 0;
  2291. pt.y = 0;
  2292. }
  2293. if (!pdwEffect)
  2294. {
  2295. pdwEffect = &dwEffect;
  2296. dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
  2297. }
  2298. DWORD dwEffectSave = *pdwEffect; // drag enter returns the default effect
  2299. HRESULT hr = pdrop->DragEnter(pdtobj, grfKeyState, *ppt, pdwEffect);
  2300. if (*pdwEffect)
  2301. {
  2302. *pdwEffect = dwEffectSave; // do Drop with the full set of bits
  2303. hr = pdrop->Drop(pdtobj, grfKeyState, *ppt, pdwEffect);
  2304. }
  2305. else
  2306. {
  2307. pdrop->DragLeave();
  2308. hr = S_FALSE; // HACK? S_FALSE DragEnter said no
  2309. }
  2310. return hr;
  2311. }
  2312. STDAPI SHLoadFromPropertyBag(IUnknown* punk, IPropertyBag* ppg)
  2313. {
  2314. IPersistPropertyBag* pppg;
  2315. HRESULT hr = punk->QueryInterface(IID_PPV_ARG(IPersistPropertyBag, &pppg));
  2316. if (SUCCEEDED(hr))
  2317. {
  2318. hr = pppg->Load(ppg, NULL);
  2319. pppg->Release();
  2320. }
  2321. return hr;
  2322. }
  2323. //*** IUnknown_TranslateAcceleratorOCS -- do punk->IOCS::TranslateAccelerator
  2324. STDAPI IUnknown_TranslateAcceleratorOCS(IUnknown *punk, LPMSG lpMsg, DWORD grfMods)
  2325. {
  2326. HRESULT hr = E_FAIL;
  2327. if (punk)
  2328. {
  2329. IOleControlSite *pocs;
  2330. hr = punk->QueryInterface(IID_PPV_ARG(IOleControlSite, &pocs));
  2331. if (SUCCEEDED(hr))
  2332. {
  2333. hr = pocs->TranslateAccelerator(lpMsg, grfMods);
  2334. pocs->Release();
  2335. }
  2336. }
  2337. return hr;
  2338. }
  2339. STDAPI IUnknown_OnFocusOCS(IUnknown *punk, BOOL fGotFocus)
  2340. {
  2341. HRESULT hr = E_FAIL;
  2342. if (punk)
  2343. {
  2344. IOleControlSite *pocs;
  2345. hr = punk->QueryInterface(IID_PPV_ARG(IOleControlSite, &pocs));
  2346. if (SUCCEEDED(hr))
  2347. {
  2348. hr = pocs->OnFocus(fGotFocus);
  2349. pocs->Release();
  2350. }
  2351. }
  2352. return hr;
  2353. }
  2354. STDAPI IUnknown_HandleIRestrict(IUnknown * punk, const GUID * pguidID, DWORD dwRestrictAction, VARIANT * pvarArgs, DWORD * pdwRestrictionResult)
  2355. {
  2356. *pdwRestrictionResult = RR_NOCHANGE; // init to something reasonable in case of failure
  2357. IRestrict * pr;
  2358. HRESULT hr = IUnknown_QueryService(punk, SID_SRestrictionHandler, IID_PPV_ARG(IRestrict, &pr));
  2359. if (SUCCEEDED(hr))
  2360. {
  2361. hr = pr->IsRestricted(pguidID, dwRestrictAction, pvarArgs, pdwRestrictionResult);
  2362. pr->Release();
  2363. }
  2364. return hr;
  2365. }
  2366. /*----------------------------------------------------------
  2367. Purpose: Get color resolution of the current display
  2368. */
  2369. STDAPI_(UINT) SHGetCurColorRes(void)
  2370. {
  2371. HDC hdc;
  2372. UINT uColorRes;
  2373. hdc = GetDC(NULL);
  2374. uColorRes = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
  2375. ReleaseDC(NULL, hdc);
  2376. return uColorRes;
  2377. }
  2378. //
  2379. // If a folder returns QIF_DONTEXPANDFODLER from QueryInfo, the folder should
  2380. // not get expanded. This is used by menu code to not expand channel folders.
  2381. //
  2382. STDAPI_(BOOL) SHIsExpandableFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
  2383. {
  2384. ASSERT(psf);
  2385. ASSERT(pidl);
  2386. BOOL fRet = TRUE;
  2387. IQueryInfo* pqi;
  2388. if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidl, IID_PPV_ARG_NULL(IQueryInfo, &pqi))))
  2389. {
  2390. ASSERT(pqi);
  2391. DWORD dwFlags;
  2392. if (SUCCEEDED(pqi->GetInfoFlags(&dwFlags)))
  2393. {
  2394. fRet = !(dwFlags & QIF_DONTEXPANDFOLDER);
  2395. }
  2396. pqi->Release();
  2397. }
  2398. return fRet;
  2399. }
  2400. STDAPI_(DWORD) SHWaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout)
  2401. {
  2402. MSG msg;
  2403. DWORD dwRet;
  2404. DWORD dwEnd = GetTickCount() + dwTimeout;
  2405. // We will attempt to wait up to dwTimeout for the thread to
  2406. // terminate
  2407. do
  2408. {
  2409. dwRet = MsgWaitForMultipleObjects(1, &hThread, FALSE,
  2410. dwTimeout, QS_SENDMESSAGE);
  2411. if (dwRet == (WAIT_OBJECT_0 + 1))
  2412. {
  2413. // There must be a pending SendMessage from either the
  2414. // thread we are killing or some other thread/process besides
  2415. // this one. Do a PeekMessage to process the pending
  2416. // SendMessage and try waiting again
  2417. PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  2418. // Calculate if we have any more time left in the timeout to
  2419. // wait on.
  2420. if (dwTimeout != INFINITE)
  2421. {
  2422. dwTimeout = dwEnd - GetTickCount();
  2423. if ((long)dwTimeout <= 0)
  2424. {
  2425. // No more time left, fail with WAIT_TIMEOUT
  2426. dwRet = WAIT_TIMEOUT;
  2427. }
  2428. }
  2429. }
  2430. // dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED
  2431. // The thread must have exited, so we are happy
  2432. //
  2433. // dwRet == WAIT_TIMEOUT
  2434. // The thread is taking too long to finish, so just
  2435. // return and let the caller kill it
  2436. } while (dwRet == (WAIT_OBJECT_0 + 1));
  2437. return(dwRet);
  2438. }
  2439. STDAPI SHWaitForCOMSendMessageThread(HANDLE hThread, DWORD dwTimeout)
  2440. {
  2441. DWORD dwIndex;
  2442. return CoWaitForMultipleHandles(0, dwTimeout, 1, &hThread, &dwIndex); // COWAIT_ALERTABLE?
  2443. }
  2444. STDAPI_(BOOL) SHVerbExistsNA(LPCSTR szExtension, LPCSTR pszVerb, LPSTR pszCommand, DWORD cchCommand)
  2445. {
  2446. /*
  2447. This Private API was being exported only for usage in shdocvw\dochost.cpp. We
  2448. don't use this function any more.
  2449. I searched in srch in index1, and there was no users execpt us. Just to make sure that
  2450. this assert is added so as to .to find out if there are any other users of this api which
  2451. I might have missed.
  2452. -KishoreP 5/4/2000
  2453. */
  2454. ASSERT(!"This Private API has been Removed");
  2455. return FALSE;
  2456. }
  2457. STDAPI_(void) SHFillRectClr(HDC hdc, LPRECT prc, COLORREF clr)
  2458. {
  2459. COLORREF clrSave = SetBkColor(hdc, clr);
  2460. ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL);
  2461. SetBkColor(hdc, clrSave);
  2462. }
  2463. //*** SearchMapInt -- map int->int
  2464. //
  2465. STDAPI_(int) SHSearchMapInt(const int *src, const int *dst, int cnt, int val)
  2466. {
  2467. for (; cnt > 0; cnt--, src++, dst++)
  2468. {
  2469. if (*src == val)
  2470. return *dst;
  2471. }
  2472. return -1;
  2473. }
  2474. STDAPI_(void) IUnknown_Set(IUnknown ** ppunk, IUnknown * punk)
  2475. {
  2476. ASSERT(ppunk);
  2477. if (*ppunk != punk)
  2478. {
  2479. IUnknown_AtomicRelease((void **)ppunk);
  2480. if (punk)
  2481. {
  2482. punk->AddRef();
  2483. *ppunk = punk;
  2484. }
  2485. }
  2486. }
  2487. /*----------------------------------------------------------
  2488. Purpose: Removes '&'s from a string, returning the character after
  2489. the last '&'. Double-ampersands are collapsed into a single
  2490. ampersand. (This is important so "&Help && Support" works.)
  2491. If a string has multiple mnemonics ("&t&wo") USER is inconsistent.
  2492. DrawText uses the last one, but the dialog manager uses the first
  2493. one. So we use whichever one is most convenient.
  2494. */
  2495. STDAPI_(CHAR) SHStripMneumonicA(LPSTR pszMenu)
  2496. {
  2497. ASSERT(pszMenu);
  2498. CHAR cMneumonic = pszMenu[0]; // Default is first char
  2499. // Early-out: Many strings don't have ampersands at all
  2500. LPSTR pszAmp = StrChrA(pszMenu, '&');
  2501. if (pszAmp)
  2502. {
  2503. LPSTR pszCopy = pszAmp;
  2504. while (*pszAmp)
  2505. {
  2506. // Protect against string that ends in '&' - don't read past the end!
  2507. if (*pszAmp == L'&' && pszAmp[1])
  2508. {
  2509. // ++ is safe here because & is never a DBCS lead byte
  2510. pszAmp++; // Don't copy the ampersand itself
  2511. if (*pszAmp != L'&') // && is not a mnemonic
  2512. {
  2513. cMneumonic = *pszAmp;
  2514. }
  2515. }
  2516. *pszCopy++ = *pszAmp++;
  2517. // If I just copied a lead byte and there is a trail byte,
  2518. // then copy the trail byte, too.
  2519. if (IsDBCSLeadByte(pszCopy[-1]) && *pszAmp)
  2520. {
  2521. *pszCopy++ = *pszAmp++;
  2522. }
  2523. }
  2524. *pszCopy = 0;
  2525. }
  2526. return cMneumonic;
  2527. }
  2528. /*----------------------------------------------------------
  2529. Purpose: Removes '&'s from a string, returning the character after
  2530. the last '&'. Double-ampersands are collapsed into a single
  2531. ampersand. (This is important so "&Help && Support" works.)
  2532. If a string has multiple mnemonics ("&t&wo") USER is inconsistent.
  2533. DrawText uses the last one, but the dialog manager uses the first
  2534. one. So we use whichever one is most convenient.
  2535. */
  2536. STDAPI_(WCHAR) SHStripMneumonicW(LPWSTR pszMenu)
  2537. {
  2538. ASSERT(pszMenu);
  2539. WCHAR cMneumonic = pszMenu[0]; // Default is first char
  2540. // Early-out: Many strings don't have ampersands at all
  2541. LPWSTR pszAmp = StrChrW(pszMenu, L'&');
  2542. if (pszAmp)
  2543. {
  2544. LPWSTR pszCopy = pszAmp - 1;
  2545. // FAREAST some localized builds have an mnemonic that looks like
  2546. // "Localized Text (&L)" we should remove that, too
  2547. if (pszAmp > pszMenu && *pszCopy == L'(')
  2548. {
  2549. if (pszAmp[2] == L')')
  2550. {
  2551. cMneumonic = *pszAmp;
  2552. // move amp so that we arent past the potential terminator
  2553. pszAmp += 3;
  2554. pszAmp = pszCopy;
  2555. }
  2556. }
  2557. else
  2558. {
  2559. // move it up so that we copy on top of amp
  2560. pszCopy++;
  2561. }
  2562. while (*pszAmp)
  2563. {
  2564. // Protect against string that ends in '&' - don't read past the end!
  2565. if (*pszAmp == L'&' && pszAmp[1])
  2566. {
  2567. pszAmp++; // Don't copy the ampersand itself
  2568. if (*pszAmp != L'&') // && is not a mnemonic
  2569. {
  2570. cMneumonic = *pszAmp;
  2571. }
  2572. }
  2573. *pszCopy++ = *pszAmp++;
  2574. }
  2575. *pszCopy = 0;
  2576. }
  2577. return cMneumonic;
  2578. }
  2579. // don't use IsChild. that walks down all children.
  2580. // faster to walk up the parent chain
  2581. //*** IsChildOrSelf -- is hwnd either a child of or equal to hwndParent?
  2582. // NOTES
  2583. // HasFocus is IsChildOrSelf(hwnd, GetFocus())
  2584. // IsWindowOwner is IsChildOrSelf(hwnd, hwndOwner)
  2585. // n.b. hwnd==0 is special and yields FALSE. this is presumbably what
  2586. // one wants for both HasFocus and IsWindowOwner.
  2587. //
  2588. // NOTE: S_OK means TRUE, S_FALSE meanse FALSE
  2589. //
  2590. STDAPI SHIsChildOrSelf(HWND hwndParent, HWND hwnd)
  2591. {
  2592. // SHDOCVW likes to pass hwndParent == NULL. Oops.
  2593. // SHDOCVW even likes to pass hwndParent == hwnd == NULL. Double oops.
  2594. if (hwndParent == NULL || hwnd == NULL)
  2595. {
  2596. return S_FALSE;
  2597. }
  2598. // Old code here used to walk the GetParent chain which is bogus
  2599. // because GetParent will return the Window Owner when there is
  2600. // no parent window. Bug 63233 got bit by this -- a dialog box
  2601. // has no parent but it's owned by the window at the top of
  2602. // the hwndParent chain. Since GetParent returns the window owner
  2603. // if there is no parent, we'd incorrectly think we should translate
  2604. // this message. I switched this to calling IsChild directly. Note:
  2605. // in asking around it appears that this function was written
  2606. // because of the mistaken assumption that IsChild was implemented
  2607. // in a perf-unfriendly way. [mikesh 15 Oct 97]
  2608. //
  2609. return ((hwndParent == hwnd) || IsChild(hwndParent, hwnd)) ? S_OK : S_FALSE;
  2610. }
  2611. STDAPI IContextMenu_Invoke(IContextMenu* pcm, HWND hwndOwner, LPCSTR pVerb, UINT fFlags)
  2612. {
  2613. HRESULT hres = S_OK;
  2614. if (pcm)
  2615. {
  2616. UINT idCmd = 0;
  2617. DECLAREWAITCURSOR;
  2618. SetWaitCursor();
  2619. HMENU hmenu = NULL;
  2620. CMINVOKECOMMANDINFO ici = {
  2621. sizeof(CMINVOKECOMMANDINFO),
  2622. 0,
  2623. hwndOwner,
  2624. NULL,
  2625. NULL, NULL,
  2626. SW_NORMAL,
  2627. };
  2628. if (!IS_INTRESOURCE(pVerb))
  2629. {
  2630. #ifdef UNICODE
  2631. ici.lpVerbW = pVerb;
  2632. ici.lpVerb = makeansi(pVerb);
  2633. ici.fMask |= CMIC_MASK_UNICODE;
  2634. #else
  2635. ici.lpVerb = pVerb;
  2636. #endif
  2637. }
  2638. else
  2639. {
  2640. hmenu = CreatePopupMenu();
  2641. if (hmenu)
  2642. {
  2643. fFlags |= CMF_DEFAULTONLY;
  2644. pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, fFlags);
  2645. idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
  2646. if (-1 != idCmd)
  2647. {
  2648. ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CONTEXTMENU_IDCMD_FIRST);
  2649. }
  2650. }
  2651. }
  2652. // need to reset it so that user won't blow off the app starting cursor
  2653. // also so that if we won't leave the wait cursor up when we're not waiting
  2654. // (like in a prop sheet or something that has a message loop
  2655. ResetWaitCursor();
  2656. // can't just check verb because it could be 0 if idCmd == CMD_ID_FIRST
  2657. if ((-1 != idCmd) || ici.lpVerb)
  2658. {
  2659. if (!hwndOwner)
  2660. ici.fMask |= CMIC_MASK_FLAG_NO_UI;
  2661. pcm->InvokeCommand(&ici);
  2662. hres = (HRESULT)1;
  2663. }
  2664. if (hmenu)
  2665. DestroyMenu(hmenu);
  2666. }
  2667. return hres;
  2668. }
  2669. //
  2670. // SetDefaultDialogFont
  2671. //
  2672. // purpose: set font to the given control of the dialog
  2673. // with platform's default character set so that
  2674. // user can see whatever string in the native
  2675. // language on the platform.
  2676. //
  2677. // in: hDlg - the parent window handle of the given control
  2678. // idCtl - ID of the control
  2679. //
  2680. // note: this will store created font with window property
  2681. // so that we can destroy it later.
  2682. //
  2683. const TCHAR c_szPropDlgFont[] = TEXT("PropDlgFont");
  2684. STDAPI_(void) SHSetDefaultDialogFont(HWND hDlg, int idCtl)
  2685. {
  2686. HFONT hfont;
  2687. HFONT hfontDefault;
  2688. LOGFONT lf;
  2689. LOGFONT lfDefault;
  2690. hfont = GetWindowFont(hDlg);
  2691. GetObject(hfont, sizeof(LOGFONT), &lf);
  2692. SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lfDefault, 0);
  2693. if (lfDefault.lfCharSet == lf.lfCharSet)
  2694. {
  2695. // if the dialog already has correct character set
  2696. // don't do anything.
  2697. return;
  2698. }
  2699. // If we already have hfont created, use it.
  2700. if (!(hfontDefault = (HFONT)GetProp(hDlg, c_szPropDlgFont)))
  2701. {
  2702. // assign the same height of the dialog font
  2703. lfDefault.lfHeight = lf.lfHeight;
  2704. if (!(hfontDefault=CreateFontIndirect(&lfDefault)))
  2705. {
  2706. // restore back in failure
  2707. hfontDefault = hfont;
  2708. }
  2709. if (hfontDefault != hfont)
  2710. SetProp(hDlg, c_szPropDlgFont, hfontDefault);
  2711. }
  2712. SetWindowFont(GetDlgItem(hDlg, idCtl), hfontDefault, FALSE);
  2713. }
  2714. //
  2715. // RemoveDefaultDialogFont
  2716. //
  2717. // purpose: Destroy the font we used to set gui default font
  2718. // Also removes the window property used to store the font.
  2719. //
  2720. // in: hDlg - the parent window handle of the given control
  2721. //
  2722. // note:
  2723. STDAPI_(void) SHRemoveDefaultDialogFont(HWND hDlg)
  2724. {
  2725. HFONT hfont;
  2726. if (hfont = (HFONT)GetProp(hDlg, c_szPropDlgFont))
  2727. {
  2728. DeleteObject(hfont);
  2729. RemoveProp(hDlg, c_szPropDlgFont);
  2730. }
  2731. }
  2732. // NOTE: since this is a worker window it probably doesn't care about
  2733. // system messages that are ansi/unicode, so only support an ansi version.
  2734. // If the pfnWndProc cares, it can thunk the messages. (Do this because
  2735. // Win95 doesn't support RegisterClassW.)
  2736. HWND SHCreateWorkerWindowA(WNDPROC pfnWndProc, HWND hwndParent, DWORD dwExStyle, DWORD dwFlags, HMENU hmenu, void * p)
  2737. {
  2738. WNDCLASSA wc = {0};
  2739. wc.lpfnWndProc = DefWindowProcA;
  2740. wc.cbWndExtra = sizeof(void *);
  2741. wc.hInstance = HINST_THISDLL;
  2742. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  2743. wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
  2744. wc.lpszClassName = "WorkerA";
  2745. dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
  2746. SHRegisterClassA(&wc);
  2747. HWND hwnd = CreateWindowExA(dwExStyle, "WorkerA", NULL, dwFlags,
  2748. 0, 0, 0, 0, hwndParent,
  2749. (HMENU)hmenu, HINST_THISDLL, NULL);
  2750. if (hwnd)
  2751. {
  2752. SetWindowPtr(hwnd, 0, p);
  2753. if (pfnWndProc)
  2754. SetWindowPtr(hwnd, GWLP_WNDPROC, pfnWndProc);
  2755. }
  2756. return hwnd;
  2757. }
  2758. // WARNING: since this is a worker window it probably doesn't care about
  2759. // system messages that are ansi/unicode, default to an ansi version on Win95.
  2760. //
  2761. // this forces callers to be aware of the fact that they are getting into
  2762. // a mess if they want compatibility with Win95.
  2763. //
  2764. // If the pfnWndProc cares, it can thunk the messages. (Do this because
  2765. // Win95 doesn't support RegisterClassW.)
  2766. //
  2767. HWND SHCreateWorkerWindowW(WNDPROC pfnWndProc, HWND hwndParent, DWORD dwExStyle, DWORD dwFlags, HMENU hmenu, void * p)
  2768. {
  2769. // must default to ANSI on win95
  2770. if (!g_bRunningOnNT)
  2771. {
  2772. return SHCreateWorkerWindowA(pfnWndProc, hwndParent, dwExStyle, dwFlags, hmenu, p);
  2773. }
  2774. WNDCLASSW wc = {0};
  2775. wc.lpfnWndProc = DefWindowProcW;
  2776. wc.cbWndExtra = sizeof(void *);
  2777. wc.hInstance = HINST_THISDLL;
  2778. wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  2779. wc.hbrBackground = (HBRUSH) (COLOR_BTNFACE + 1);
  2780. wc.lpszClassName = L"WorkerW";
  2781. dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
  2782. SHRegisterClassW(&wc);
  2783. HWND hwnd = CreateWindowExW(dwExStyle, L"WorkerW", NULL, dwFlags,
  2784. 0, 0, 0, 0, hwndParent,
  2785. (HMENU)hmenu, HINST_THISDLL, NULL);
  2786. if (hwnd)
  2787. {
  2788. SetWindowPtr(hwnd, 0, p);
  2789. // Note: Must explicitly use W version to avoid charset thunks
  2790. if (pfnWndProc)
  2791. SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc);
  2792. }
  2793. return hwnd;
  2794. }
  2795. #pragma warning(disable:4035) // no return value
  2796. #undef SHInterlockedCompareExchange
  2797. STDAPI_(void *) SHInterlockedCompareExchange(void **ppDest, void *pExch, void *pComp)
  2798. {
  2799. #if defined(_X86_)
  2800. _asm {
  2801. mov ecx,ppDest
  2802. mov edx,pExch
  2803. mov eax,pComp
  2804. lock cmpxchg [ecx],edx
  2805. }
  2806. #else
  2807. return InterlockedCompareExchangePointer(ppDest, pExch, pComp);
  2808. #endif
  2809. }
  2810. #pragma warning(default:4035)
  2811. #define REGSTR_PATH_POLICIESW L"Software\\Microsoft\\Windows\\CurrentVersion\\Policies"
  2812. STDAPI_(DWORD) SHRestrictionLookup(INT rest, LPCWSTR pszBaseKey, const SHRESTRICTIONITEMS *pRestrictions,
  2813. DWORD* pdwRestrictionItemValues)
  2814. {
  2815. int i;
  2816. DWORD dw = 0;
  2817. //
  2818. // Loop through the restrictions
  2819. //
  2820. for (i=0; pRestrictions[i].pszKey; i++)
  2821. {
  2822. if (rest == pRestrictions[i].iFlag)
  2823. {
  2824. dw = pdwRestrictionItemValues[i];
  2825. // Has this restriction been initialized yet?
  2826. //
  2827. if (dw == -1)
  2828. {
  2829. dw = SHGetRestriction(pszBaseKey, pRestrictions[i].pszKey, pRestrictions[i].pszValue);
  2830. pdwRestrictionItemValues[i] = dw;
  2831. }
  2832. // Got the restriction we needed. Get out of here.
  2833. break;
  2834. }
  2835. }
  2836. return dw;
  2837. }
  2838. STDAPI_(DWORD) SHGetRestriction(LPCWSTR pszBaseKey, LPCWSTR pszGroup, LPCWSTR pszRestriction)
  2839. {
  2840. // Make sure the string is long enough to hold longest one...
  2841. COMPILETIME_ASSERT(MAX_PATH > ARRAYSIZE(REGSTR_PATH_POLICIESW) + 40); // PathCombine *assumes* MAX_PATH
  2842. WCHAR szSubKey[MAX_PATH];
  2843. DWORD dwSize;
  2844. // A sensible default
  2845. DWORD dw = 0;
  2846. //
  2847. // This restriction hasn't been read yet.
  2848. //
  2849. if (!pszBaseKey)
  2850. {
  2851. pszBaseKey = REGSTR_PATH_POLICIESW;
  2852. }
  2853. #ifndef UNIX
  2854. PathCombineW(szSubKey, pszBaseKey, pszGroup);
  2855. #else
  2856. wsprintfW(szSubKey, L"%s\\%s", pszBaseKey, pszGroup);
  2857. #endif
  2858. // Check local machine first and let it override what the
  2859. // HKCU policy has done.
  2860. dwSize = sizeof(dw);
  2861. if (ERROR_SUCCESS != SHGetValueW(HKEY_LOCAL_MACHINE,
  2862. szSubKey, pszRestriction,
  2863. NULL, &dw, &dwSize))
  2864. {
  2865. // Check current user if we didn't find anything for the local machine.
  2866. dwSize = sizeof(dw);
  2867. SHGetValueW(HKEY_CURRENT_USER,
  2868. szSubKey, pszRestriction,
  2869. NULL, &dw, &dwSize);
  2870. }
  2871. return dw;
  2872. }
  2873. // WhichPlatform
  2874. // Determine if we're running on integrated shell or browser-only.
  2875. STDAPI_(UINT) WhichPlatform(void)
  2876. {
  2877. HINSTANCE hinst;
  2878. // Cache this info
  2879. static UINT uInstall = PLATFORM_UNKNOWN;
  2880. if (uInstall != PLATFORM_UNKNOWN)
  2881. return uInstall;
  2882. // Not all callers are linked to SHELL32.DLL, so we must use LoadLibrary.
  2883. hinst = LoadLibraryA("SHELL32.DLL");
  2884. if (hinst)
  2885. {
  2886. DWORD fValue;
  2887. DWORD cbSize = sizeof(fValue);
  2888. HKEY hKey;
  2889. LONG lRes;
  2890. // NOTE: GetProcAddress always takes ANSI strings!
  2891. DLLGETVERSIONPROC pfnGetVersion =
  2892. (DLLGETVERSIONPROC)GetProcAddress(hinst, "DllGetVersion");
  2893. uInstall = (NULL != pfnGetVersion) ? PLATFORM_INTEGRATED : PLATFORM_BROWSERONLY;
  2894. // check that the registry reflects the right value... (this is so iexplore can check efficiently)
  2895. lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Internet Explorer"),
  2896. 0, KEY_READ | KEY_WRITE, &hKey);
  2897. if (lRes == ERROR_SUCCESS)
  2898. {
  2899. lRes = RegQueryValueEx(hKey, REGVAL_INTEGRATEDBROWSER,
  2900. NULL, NULL,
  2901. (LPBYTE) &fValue, &cbSize);
  2902. if (lRes == ERROR_SUCCESS && uInstall == PLATFORM_BROWSERONLY)
  2903. {
  2904. // remove the value, we are now Browser only release
  2905. RegDeleteValue(hKey, REGVAL_INTEGRATEDBROWSER);
  2906. }
  2907. else if (lRes != ERROR_SUCCESS && uInstall == PLATFORM_INTEGRATED)
  2908. {
  2909. // install the RegValue, we are integrated browser mode...
  2910. fValue = TRUE;
  2911. cbSize = sizeof(fValue);
  2912. RegSetValueEx(hKey, REGVAL_INTEGRATEDBROWSER,
  2913. (DWORD) NULL, REG_DWORD,
  2914. (LPBYTE) &fValue, cbSize);
  2915. // ignore the failure, if the key is not present, shdocvw will be loaded and this
  2916. // function called anyway....
  2917. }
  2918. RegCloseKey(hKey);
  2919. }
  2920. else
  2921. {
  2922. // a machine without our regKey,
  2923. TraceMsg(TF_WARNING, "WhichPlatform: failed to open 'HKLM\\Software\\Microsoft\\Internet Explorer'");
  2924. }
  2925. FreeLibrary(hinst);
  2926. }
  2927. return uInstall;
  2928. }
  2929. // Tray notification window class
  2930. CHAR const c_szTrayNotificationClass[] = WNDCLASS_TRAYNOTIFY;
  2931. BOOL DoRegisterGlobalHotkey(WORD wOldHotkey, WORD wNewHotkey,
  2932. LPCSTR pcszPath, LPCWSTR pcwszPath)
  2933. {
  2934. BOOL bResult;
  2935. HWND hwndTray;
  2936. CHAR szPath[MAX_PATH];
  2937. ASSERT((NULL != pcszPath) || (NULL != pcwszPath));
  2938. hwndTray = FindWindowA(c_szTrayNotificationClass, 0);
  2939. if (hwndTray)
  2940. {
  2941. if (wOldHotkey)
  2942. {
  2943. SendMessage(hwndTray, WMTRAY_SCUNREGISTERHOTKEY, wOldHotkey, 0);
  2944. TraceMsg(TF_FUNC, "RegisterGlobalHotkey(): Unregistered old hotkey %#04x.", wOldHotkey);
  2945. }
  2946. if (wNewHotkey)
  2947. {
  2948. // If not running on NT and we have a widestr, need to convert to ansi
  2949. if ((!g_bRunningOnNT) && (NULL == pcszPath))
  2950. {
  2951. SHUnicodeToAnsi(pcwszPath, szPath, ARRAYSIZE(szPath));
  2952. pcszPath = szPath;
  2953. }
  2954. // Aargh: The W95/browser only mode sends a string, other platforms sends
  2955. // Atom...
  2956. if (!g_bRunningOnNT && (WhichPlatform() == PLATFORM_IE3))
  2957. {
  2958. SendMessage(hwndTray, WMTRAY_SCREGISTERHOTKEY, wNewHotkey, (LPARAM)pcszPath);
  2959. }
  2960. else
  2961. {
  2962. ATOM atom = (NULL != pcszPath) ?
  2963. GlobalAddAtomA(pcszPath) :
  2964. GlobalAddAtomW(pcwszPath);
  2965. ASSERT(atom);
  2966. if (atom)
  2967. {
  2968. SendMessage(hwndTray, WMTRAY_SCREGISTERHOTKEY, wNewHotkey, (LPARAM)atom);
  2969. GlobalDeleteAtom(atom);
  2970. }
  2971. }
  2972. TraceMsg(TF_FUNC, "RegisterGlobalHotkey(): Registered new hotkey %#04x.",wNewHotkey);
  2973. }
  2974. bResult = TRUE;
  2975. }
  2976. else
  2977. {
  2978. bResult = FALSE;
  2979. TraceMsgA(TF_WARNING, "RegisterGlobalHotkey(): Unable to find Tray window of class %s to notify.",
  2980. c_szTrayNotificationClass);
  2981. }
  2982. return(bResult);
  2983. }
  2984. BOOL
  2985. RegisterGlobalHotkeyW(
  2986. WORD wOldHotkey,
  2987. WORD wNewHotkey,
  2988. LPCWSTR pcwszPath)
  2989. {
  2990. ASSERT(IsValidPathW(pcwszPath));
  2991. return DoRegisterGlobalHotkey(wOldHotkey, wNewHotkey, NULL, pcwszPath);
  2992. }
  2993. BOOL
  2994. RegisterGlobalHotkeyA(
  2995. WORD wOldHotkey,
  2996. WORD wNewHotkey,
  2997. LPCSTR pcszPath)
  2998. {
  2999. ASSERT(IsValidPathA(pcszPath));
  3000. return DoRegisterGlobalHotkey(wOldHotkey, wNewHotkey, pcszPath, NULL);
  3001. }
  3002. typedef UINT (__stdcall * PFNGETSYSTEMWINDOWSDIRECTORYW)(LPWSTR pwszBuffer, UINT cchBuff);
  3003. UINT WINAPI
  3004. SHGetSystemWindowsDirectoryW(LPWSTR lpBuffer, UINT uSize)
  3005. {
  3006. if (g_bRunningOnNT)
  3007. {
  3008. static PFNGETSYSTEMWINDOWSDIRECTORYW s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)-1;
  3009. if (((PFNGETSYSTEMWINDOWSDIRECTORYW)-1) == s_pfn)
  3010. {
  3011. HINSTANCE hinst = GetModuleHandle(TEXT("KERNEL32.DLL"));
  3012. ASSERT(NULL != hinst); // YIKES!
  3013. if (hinst)
  3014. s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)GetProcAddress(hinst, "GetSystemWindowsDirectoryW");
  3015. else
  3016. s_pfn = NULL;
  3017. }
  3018. if (s_pfn)
  3019. {
  3020. // we use the new API so we dont get lied to by hydra
  3021. return s_pfn(lpBuffer, uSize);
  3022. }
  3023. else
  3024. {
  3025. GetSystemDirectoryW(lpBuffer, uSize);
  3026. PathRemoveFileSpecW(lpBuffer);
  3027. return lstrlenW(lpBuffer);
  3028. }
  3029. }
  3030. else
  3031. {
  3032. return GetWindowsDirectoryWrapW(lpBuffer, uSize);
  3033. }
  3034. }
  3035. UINT WINAPI
  3036. SHGetSystemWindowsDirectoryA(LPSTR lpBuffer, UINT uSize)
  3037. {
  3038. if (g_bRunningOnNT)
  3039. {
  3040. WCHAR wszPath[MAX_PATH];
  3041. SHGetSystemWindowsDirectoryW(wszPath, ARRAYSIZE(wszPath));
  3042. return SHUnicodeToAnsi(wszPath, lpBuffer, uSize);
  3043. }
  3044. else
  3045. {
  3046. if (lpBuffer && uSize)
  3047. *lpBuffer = '\0';
  3048. return GetWindowsDirectoryA(lpBuffer, uSize);
  3049. }
  3050. }
  3051. typedef struct {
  3052. SHDLGPROC pfnDlgProc;
  3053. VOID* pData;
  3054. } SHDIALOGDATA;
  3055. BOOL_PTR DialogBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  3056. {
  3057. SHDIALOGDATA* pdd = (SHDIALOGDATA*)GetWindowPtr(hwnd, DWLP_USER);
  3058. if (uMsg == WM_INITDIALOG)
  3059. {
  3060. pdd = (SHDIALOGDATA*)lParam;
  3061. SetWindowPtr(hwnd, DWLP_USER, pdd);
  3062. lParam = (LPARAM)pdd->pData;
  3063. }
  3064. if (pdd && pdd->pfnDlgProc)
  3065. {
  3066. // Must return bResult instead of unconditional TRUE because it
  3067. // might be a WM_CTLCOLOR message.
  3068. BOOL_PTR bResult = pdd->pfnDlgProc(pdd->pData, hwnd, uMsg, wParam, lParam);
  3069. if (bResult)
  3070. return bResult;
  3071. }
  3072. switch (uMsg)
  3073. {
  3074. case WM_INITDIALOG:
  3075. return TRUE;
  3076. case WM_COMMAND:
  3077. {
  3078. int id = GET_WM_COMMAND_ID(wParam, lParam);
  3079. HWND hwndCtrl = GetDlgItem(hwnd, id);
  3080. if ((id != IDHELP) && SendMessage(hwndCtrl, WM_GETDLGCODE, 0, 0) & (DLGC_DEFPUSHBUTTON | DLGC_UNDEFPUSHBUTTON))
  3081. {
  3082. EndDialog(hwnd, id);
  3083. return TRUE;
  3084. }
  3085. break;
  3086. }
  3087. }
  3088. return FALSE;
  3089. }
  3090. STDAPI_(INT_PTR) SHDialogBox(HINSTANCE hInstance, LPCWSTR lpTemplateName,
  3091. HWND hwndParent, SHDLGPROC lpDlgFunc, VOID* lpData)
  3092. {
  3093. SHDIALOGDATA dd;
  3094. dd.pfnDlgProc = lpDlgFunc;
  3095. dd.pData = lpData;
  3096. // we currently only support resource id #'s
  3097. ASSERT(IS_INTRESOURCE(lpTemplateName));
  3098. return DialogBoxParam(hInstance, (LPCTSTR)lpTemplateName, hwndParent, DialogBoxProc, (LPARAM)&dd);
  3099. }
  3100. //---------------------------------------------------------------------------
  3101. // NOTE! SHInvokeDefaultCommand logs the action as user-initiated!
  3102. STDAPI SHInvokeDefaultCommand(HWND hwnd, IShellFolder* psf, LPCITEMIDLIST pidlItem)
  3103. {
  3104. return SHInvokeCommand(hwnd, psf, pidlItem, NULL);
  3105. }
  3106. // NOTE! SHInvokeDefaultCommand logs the action as user-initiated!
  3107. STDAPI SHInvokeCommand(HWND hwnd, IShellFolder* psf, LPCITEMIDLIST pidlItem, LPCSTR lpVerb)
  3108. {
  3109. HRESULT hr = E_FAIL;
  3110. if (psf)
  3111. {
  3112. IContextMenu *pcm;
  3113. if (SUCCEEDED(psf->GetUIObjectOf(hwnd, 1, &pidlItem, IID_X_PPV_ARG(IContextMenu, 0, &pcm))))
  3114. {
  3115. DWORD dwFlags = IsOS(OS_WHISTLERORGREATER) ? CMIC_MASK_FLAG_LOG_USAGE : 0;
  3116. hr = SHInvokeCommandsOnContextMenu(hwnd, NULL, pcm, dwFlags, lpVerb ? &lpVerb : NULL, lpVerb ? 1 : 0);
  3117. pcm->Release();
  3118. }
  3119. }
  3120. return hr;
  3121. }
  3122. HRESULT SHInvokeCommandOnContextMenu(HWND hwnd, IUnknown* punk, IContextMenu *pcm, DWORD fMask, LPCSTR lpVerb)
  3123. {
  3124. return SHInvokeCommandsOnContextMenu(hwnd, punk, pcm, fMask, lpVerb ? &lpVerb : NULL, lpVerb ? 1 : 0);
  3125. }
  3126. STDAPI SHInvokeCommandsOnContextMenu(HWND hwnd, IUnknown* punk, IContextMenu *pcm, DWORD fMask, const LPCSTR rgszVerbs[], UINT cVerbs)
  3127. {
  3128. HRESULT hr = E_OUTOFMEMORY;
  3129. if (pcm)
  3130. {
  3131. HMENU hmenu = CreatePopupMenu();
  3132. if (hmenu)
  3133. {
  3134. if (punk)
  3135. IUnknown_SetSite(pcm, punk);
  3136. hr = pcm->QueryContextMenu(hmenu, 0, CONTEXTMENU_IDCMD_FIRST, CONTEXTMENU_IDCMD_LAST, cVerbs ? 0 : CMF_DEFAULTONLY);
  3137. if (SUCCEEDED(hr))
  3138. {
  3139. LPCSTR lpVerb = NULL;
  3140. // set up the default verb case outside the loop
  3141. UINT idCmd = -1;
  3142. if (0 == cVerbs)
  3143. {
  3144. idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
  3145. if ((UINT)-1 != idCmd)
  3146. lpVerb = MAKEINTRESOURCE(idCmd - CONTEXTMENU_IDCMD_FIRST);
  3147. }
  3148. UINT i = 0;
  3149. do {
  3150. if (cVerbs)
  3151. lpVerb = rgszVerbs[i];
  3152. // if idCmd == 0, then lpVerb would be Zero. So we need to check to
  3153. // see if idCmd is not -1.
  3154. if (lpVerb || idCmd != (UINT)-1)
  3155. {
  3156. CMINVOKECOMMANDINFOEX ici = { 0 };
  3157. ici.cbSize = sizeof(ici);
  3158. ici.fMask = fMask;
  3159. ici.hwnd = hwnd;
  3160. ici.lpVerb = lpVerb;
  3161. ici.nShow = SW_NORMAL;
  3162. // shell32 converted ASCII canonical name to Unicode, we do it faster in-line
  3163. // NOTE: should create an SHAsciiToUnicode function for this...
  3164. WCHAR szVerbW[128];
  3165. if (idCmd == (UINT)-1)
  3166. {
  3167. WCHAR wch = L'\0';
  3168. LPCSTR pSrc = lpVerb;
  3169. LPWSTR pDst = szVerbW;
  3170. UINT cch = ARRAYSIZE(szVerbW);
  3171. do {
  3172. *(LPSTR)&wch = *pSrc++;
  3173. *pDst++ = wch;
  3174. } while (wch && (wch <= (WCHAR)127));
  3175. // all of our calls are ASCII
  3176. RIPMSG(!wch, "Caller of SHInvokeCommandXXX passed in bogus canonical name");
  3177. if (!wch)
  3178. {
  3179. ici.lpVerbW = szVerbW;
  3180. ici.fMask |= CMIC_MASK_UNICODE;
  3181. }
  3182. }
  3183. hr = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  3184. if (SUCCEEDED(hr))
  3185. break;
  3186. if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED) )
  3187. break; // user aborted
  3188. }
  3189. else
  3190. {
  3191. hr = E_FAIL;
  3192. }
  3193. } while (++i < cVerbs);
  3194. }
  3195. if (punk)
  3196. IUnknown_SetSite(pcm, NULL);
  3197. DestroyMenu(hmenu);
  3198. }
  3199. }
  3200. return hr;
  3201. }
  3202. HRESULT SHForwardContextMenuMsg(IContextMenu* pcm, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plResult, BOOL fAllowICM2)
  3203. {
  3204. HRESULT hr = E_FAIL;
  3205. if (pcm)
  3206. {
  3207. IContextMenu3 *pcm3;
  3208. if (SUCCEEDED(pcm->QueryInterface(IID_PPV_ARG(IContextMenu3, &pcm3))))
  3209. {
  3210. hr = pcm3->HandleMenuMsg2(uMsg, wParam, lParam, plResult);
  3211. pcm3->Release();
  3212. }
  3213. else if (fAllowICM2)
  3214. {
  3215. IContextMenu2 *pcm2;
  3216. if (SUCCEEDED(pcm->QueryInterface(IID_PPV_ARG(IContextMenu2, &pcm2))))
  3217. {
  3218. hr = pcm2->HandleMenuMsg(uMsg, wParam, lParam);
  3219. pcm2->Release();
  3220. if (plResult)
  3221. *plResult = 0;
  3222. if (SUCCEEDED(hr))
  3223. hr = S_FALSE; // so caller knows the return result is bogus
  3224. }
  3225. }
  3226. }
  3227. return hr;
  3228. }
  3229. int MessageBoxHelper(HINSTANCE hInst, HWND hwnd, IUnknown *punkEnableModless, LPCWSTR pwzMessage, UINT idTitle, UINT nFlags)
  3230. {
  3231. WCHAR wzTitle[MAX_PATH];
  3232. UINT uiResult;
  3233. ULONG_PTR ul;
  3234. HANDLE h = XP_CreateAndActivateContext(&ul);
  3235. EVAL(LoadStringWrapW(hInst, idTitle, wzTitle, ARRAYSIZE(wzTitle)));
  3236. if (hwnd)
  3237. IUnknown_EnableModless(punkEnableModless, TRUE);
  3238. uiResult = MessageBoxWrapW(hwnd, pwzMessage, wzTitle, nFlags);
  3239. if (hwnd)
  3240. IUnknown_EnableModless(punkEnableModless, TRUE);
  3241. XP_DeactivateAndDestroyContext(h, ul);
  3242. return uiResult;
  3243. }
  3244. int MessageBoxDiskHelper(HINSTANCE hInst, HWND hwnd, IUnknown *punkEnableModless, UINT idMessage, UINT idTitle, UINT nFlags, BOOL fDrive, DWORD dwDrive)
  3245. {
  3246. WCHAR wzMessage[MAX_PATH];
  3247. EVAL(LoadStringWrapW(hInst, idMessage, wzMessage, ARRAYSIZE(wzMessage)));
  3248. if (fDrive)
  3249. {
  3250. WCHAR wzTemp[MAX_PATH];
  3251. wnsprintfW(wzTemp, ARRAYSIZE(wzTemp), wzMessage, dwDrive);
  3252. StrCpyNW(wzMessage, wzTemp, ARRAYSIZE(wzMessage));
  3253. }
  3254. return MessageBoxHelper(hInst, hwnd, punkEnableModless, wzMessage, idTitle, nFlags);
  3255. }
  3256. BOOL DoMediaPrompt(HWND hwnd, IUnknown *punkEnableModless, int nDrive, LPCWSTR pwzDrive, BOOL fOfferToFormat, DWORD dwError, UINT wFunc, BOOL * pfRetry)
  3257. {
  3258. BOOL fDiskHasMedia = TRUE; // Assume yes
  3259. *pfRetry = FALSE;
  3260. TraceMsg(TF_FUNC, "DOS Extended error %X", dwError);
  3261. // FEATURE, flash (ROM?) drives return a different error code here
  3262. // that we need to map to not formatted, talk to robwi...
  3263. // Is it true that it's not ready or we can't format it?
  3264. if ((dwError == ERROR_NOT_READY) || !fOfferToFormat)
  3265. {
  3266. // Yes, so do the disk insert w/o offering to format.
  3267. fDiskHasMedia = FALSE;
  3268. // drive not ready (no disk in the drive)
  3269. if (hwnd &&
  3270. (IDRETRY == MessageBoxDiskHelper(HINST_THISDLL,
  3271. hwnd,
  3272. punkEnableModless,
  3273. IDS_DRIVENOTREADY,
  3274. (IDS_FILEERROR + wFunc),
  3275. (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_RETRYCANCEL),
  3276. TRUE,
  3277. (DWORD)(nDrive + TEXT('A')))))
  3278. {
  3279. *pfRetry = TRUE; // The user wants to try again, bless their heart.
  3280. }
  3281. else
  3282. {
  3283. // The user was informed that media isn't present and they basically
  3284. // informed us to cancel the operation.
  3285. *pfRetry = FALSE;
  3286. }
  3287. }
  3288. else if ((dwError == ERROR_GEN_FAILURE) ||
  3289. (dwError == ERROR_UNRECOGNIZED_MEDIA) ||
  3290. (dwError == ERROR_UNRECOGNIZED_VOLUME))
  3291. {
  3292. // general failue (disk not formatted)
  3293. if (hwnd &&
  3294. (MessageBoxDiskHelper(HINST_THISDLL,
  3295. hwnd,
  3296. punkEnableModless,
  3297. IDS_UNFORMATTED,
  3298. (IDS_FILEERROR + wFunc),
  3299. (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_YESNO),
  3300. TRUE,
  3301. (DWORD)(nDrive + TEXT('A'))) == IDYES))
  3302. {
  3303. if (hwnd)
  3304. {
  3305. IUnknown_EnableModless(punkEnableModless, FALSE);
  3306. }
  3307. UINT uiFormat = SHFormatDrive(hwnd, nDrive, SHFMT_ID_DEFAULT, 0);
  3308. if (hwnd)
  3309. {
  3310. IUnknown_EnableModless(punkEnableModless, TRUE);
  3311. }
  3312. switch (uiFormat)
  3313. {
  3314. case SHFMT_CANCEL:
  3315. *pfRetry = FALSE;
  3316. fDiskHasMedia = FALSE;
  3317. break;
  3318. case SHFMT_ERROR:
  3319. case SHFMT_NOFORMAT:
  3320. fDiskHasMedia = FALSE; // We still don't have a formatted drive
  3321. if (hwnd)
  3322. {
  3323. MessageBoxDiskHelper(HINST_THISDLL,
  3324. hwnd,
  3325. punkEnableModless,
  3326. IDS_NOFMT,
  3327. (IDS_FILEERROR + wFunc),
  3328. (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK),
  3329. TRUE,
  3330. (DWORD)(nDrive + TEXT('A')));
  3331. *pfRetry = TRUE;
  3332. }
  3333. else
  3334. {
  3335. // If we can't display UI, no need to try again.
  3336. *pfRetry = FALSE;
  3337. }
  3338. break;
  3339. default:
  3340. // Disk should now be formatted, verify
  3341. *pfRetry = TRUE;
  3342. fDiskHasMedia = TRUE;
  3343. break;
  3344. }
  3345. }
  3346. else
  3347. {
  3348. *pfRetry = FALSE; // If we can't display UI, or no need to try again.
  3349. fDiskHasMedia = FALSE; // The user either wasn't given the option of formatting or decided not to format.
  3350. }
  3351. }
  3352. else
  3353. {
  3354. if (hwnd)
  3355. {
  3356. MessageBoxDiskHelper(HINST_THISDLL, hwnd, punkEnableModless, IDS_NOSUCHDRIVE, (IDS_FILEERROR + wFunc),
  3357. (MB_SETFOREGROUND | MB_ICONHAND), TRUE, (DWORD)(nDrive + TEXT('A')));
  3358. *pfRetry = FALSE;
  3359. fDiskHasMedia = FALSE;
  3360. }
  3361. else
  3362. {
  3363. *pfRetry = FALSE;
  3364. fDiskHasMedia = FALSE;
  3365. }
  3366. }
  3367. return fDiskHasMedia;
  3368. }
  3369. BOOL CheckDiskForMedia(HWND hwnd, IUnknown *punkEnableModless, int nDrive, LPCWSTR pwzDrive, UINT wFunc, BOOL * pfRetry)
  3370. {
  3371. BOOL fDiskHasMedia = TRUE; // Assume yes because of the fall thru case. (Path Exists)
  3372. *pfRetry = FALSE; // If we fall thru and the destination path exists, don't retry.
  3373. // REARCHITECT, we need to do the find first here instead of GetCurrentDirectory()
  3374. // because redirected devices (network, cdrom) do not actually hit the disk
  3375. // on the GetCurrentDirectory() call (dos busted)
  3376. // Is it a CD-ROM Drive?
  3377. if (RealDriveType(nDrive, FALSE) == DRIVE_CDROM)
  3378. {
  3379. // Is the CD not in and the caller wants UI?
  3380. if (!PathFileExistsW(pwzDrive) && hwnd)
  3381. fDiskHasMedia = DoMediaPrompt(hwnd, punkEnableModless, nDrive, pwzDrive, wFunc, FALSE, GetLastError(), pfRetry);
  3382. }
  3383. else
  3384. {
  3385. int iIsNet;
  3386. // Is this some kind of net drive?
  3387. if ((DriveType(nDrive) != DRIVE_FIXED) && (FALSE != (iIsNet = IsNetDrive(nDrive))))
  3388. {
  3389. // Yes, so see if the connection still exists.
  3390. if (iIsNet == 1)
  3391. {
  3392. // Yes, it exists so we are done.
  3393. *pfRetry = FALSE;
  3394. fDiskHasMedia = TRUE;
  3395. }
  3396. else
  3397. {
  3398. // No, so try to restore the connection.
  3399. DWORD dwError = WNetRestoreConnectionWrapW(hwnd, pwzDrive);
  3400. if (dwError != WN_SUCCESS)
  3401. {
  3402. // Restoring the connection failed, so prepare to tell the
  3403. // caller the bad news and then display UI to the user if appropriate.
  3404. *pfRetry = FALSE;
  3405. fDiskHasMedia = TRUE;
  3406. if (!(dwError == WN_CANCEL || dwError == ERROR_CONTINUE) && hwnd)
  3407. {
  3408. WCHAR wzMessage[128];
  3409. WNetGetLastErrorWrapW(&dwError, wzMessage, ARRAYSIZE(wzMessage), NULL, 0);
  3410. IUnknown_EnableModless(punkEnableModless, FALSE); // Cover me, I'm going to do UI
  3411. MessageBoxHelper(HINST_THISDLL, hwnd, punkEnableModless, wzMessage, (IDS_FILEERROR + wFunc),
  3412. (MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND));
  3413. IUnknown_EnableModless(punkEnableModless, TRUE);
  3414. }
  3415. }
  3416. else
  3417. {
  3418. // Restoring the connection worked.
  3419. *pfRetry = FALSE;
  3420. fDiskHasMedia = TRUE;
  3421. }
  3422. }
  3423. }
  3424. else
  3425. {
  3426. // No, so see if it's a floppy or unformatted drive.
  3427. // Is the destination reachable?
  3428. if (!PathFileExistsW(pwzDrive))
  3429. {
  3430. // No so ask the user about formatting or inserting the media.
  3431. fDiskHasMedia = DoMediaPrompt(hwnd, punkEnableModless, nDrive, pwzDrive, TRUE, GetLastError(), wFunc, pfRetry);
  3432. }
  3433. else
  3434. {
  3435. ASSERT(FALSE == *pfRetry); // Make sure the defaults are still true.
  3436. ASSERT(TRUE == fDiskHasMedia);
  3437. }
  3438. }
  3439. }
  3440. return fDiskHasMedia;
  3441. }
  3442. // FUNCTION: SHCheckDiskForMedia
  3443. //
  3444. // DESCRIPTION:
  3445. // note: this has the side effect of setting the
  3446. // current drive to the new disk if it is successful
  3447. //
  3448. // The default impl being ansi isn't very good, but we need to
  3449. // see if the unicode versions of the WNet APIs are impl on Win95.
  3450. //
  3451. // PARAMETERS:
  3452. // hwnd - NULL means no UI will be displayed. Non-NULL means
  3453. // punkEnableModless - Make caller modal during UI. (OPTIONAL)
  3454. // pszPath - Path that needs verification.
  3455. // wFunc - Type of operation (FO_MOVE, FO_COPY, FO_DELETE, FO_RENAME - shellapi.h)
  3456. //
  3457. // Keep the return value a strict TRUE/FALSE because some callers rely on it.
  3458. BOOL SHCheckDiskForMediaW(HWND hwnd, IUnknown *punkEnableModless, LPCWSTR pwzPath, UINT wFunc)
  3459. {
  3460. BOOL fDiskHasMedia = FALSE; // Assume yes
  3461. int nDrive = PathGetDriveNumberW(pwzPath);
  3462. ASSERT(nDrive != -1); // should not get a UNC here
  3463. if (nDrive != -1) // not supported on UNCs
  3464. {
  3465. WCHAR wzDrive[10];
  3466. PathBuildRootW(wzDrive, nDrive);
  3467. BOOL fKeepRetrying;
  3468. do
  3469. {
  3470. fDiskHasMedia = CheckDiskForMedia(hwnd, punkEnableModless, nDrive, wzDrive, wFunc, &fKeepRetrying);
  3471. }
  3472. while (fKeepRetrying);
  3473. }
  3474. return fDiskHasMedia;
  3475. }
  3476. BOOL SHCheckDiskForMediaA(HWND hwnd, IUnknown *punkEnableModless, LPCSTR pszPath, UINT wFunc)
  3477. {
  3478. WCHAR wzPath[MAX_PATH];
  3479. SHAnsiToUnicode(pszPath, wzPath, ARRAYSIZE(wzPath));
  3480. return SHCheckDiskForMediaW(hwnd, punkEnableModless, wzPath, wFunc);
  3481. }
  3482. HRESULT _FaultInIEFeature(HWND hwnd, uCLSSPEC *pclsspec, QUERYCONTEXT *pQ, DWORD dwFlags);
  3483. struct HELPCONT_FILE
  3484. {
  3485. const CHAR *pszFile;
  3486. int nLength;
  3487. } g_helpConts[] =
  3488. {
  3489. { "iexplore.chm", ARRAYSIZE("iexplore.chm") - 1 },
  3490. { "iexplore.hlp", ARRAYSIZE("iexplore.hlp") - 1 },
  3491. { "update.chm", ARRAYSIZE("update.chm") - 1 },
  3492. { "update.cnt", ARRAYSIZE("update.cnt") - 1 },
  3493. { "users.chm", ARRAYSIZE("users.chm") - 1 },
  3494. { "users.hlp", ARRAYSIZE("users.hlp") - 1 },
  3495. { "accessib.chm", ARRAYSIZE("accessib.chm") - 1 },
  3496. { "ieeula.chm", ARRAYSIZE("ieeula.chm") - 1 },
  3497. { "iesupp.chm", ARRAYSIZE("iesupp.chm") - 1 },
  3498. { "msnauth.hlp", ARRAYSIZE("msnauth.hlp") - 1 },
  3499. { "ratings.chm", ARRAYSIZE("ratings.chm") - 1 },
  3500. { "ratings.hlp", ARRAYSIZE("ratings.hlp") - 1 }
  3501. };
  3502. HRESULT _JITHelpFileA(HWND hwnd, LPCSTR pszPath)
  3503. {
  3504. if (!pszPath)
  3505. return S_OK;
  3506. HRESULT hr = S_OK;
  3507. BOOL bMustJIT = FALSE;
  3508. CHAR *pszFile = PathFindFileName(pszPath);
  3509. for (int i = 0; i < ARRAYSIZE(g_helpConts); i++)
  3510. {
  3511. if (StrCmpNIA(g_helpConts[i].pszFile, pszFile, g_helpConts[i].nLength) == 0)
  3512. {
  3513. bMustJIT = TRUE;
  3514. break;
  3515. }
  3516. }
  3517. if (bMustJIT)
  3518. {
  3519. uCLSSPEC ucs;
  3520. QUERYCONTEXT qc = { 0 };
  3521. ucs.tyspec = TYSPEC_CLSID;
  3522. ucs.tagged_union.clsid = CLSID_IEHelp;
  3523. hr = _FaultInIEFeature(hwnd, &ucs, &qc, FIEF_FLAG_FORCE_JITUI);
  3524. }
  3525. return hr;
  3526. }
  3527. HRESULT _JITHelpFileW(HWND hwnd, LPCWSTR pwszFile)
  3528. {
  3529. if (!pwszFile)
  3530. return S_OK;
  3531. CHAR szFile[MAX_PATH];
  3532. SHUnicodeToAnsi(pwszFile, szFile, ARRAYSIZE(szFile));
  3533. return _JITHelpFileA(hwnd, szFile);
  3534. }
  3535. BOOL _JITSetLastError(HRESULT hr)
  3536. {
  3537. DWORD err;
  3538. if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
  3539. {
  3540. err = HRESULT_CODE(hr);
  3541. }
  3542. else if (hr == E_ACCESSDENIED)
  3543. {
  3544. err = ERROR_ACCESS_DENIED;
  3545. }
  3546. else
  3547. {
  3548. err = ERROR_FILE_NOT_FOUND;
  3549. }
  3550. SetLastError(err);
  3551. return FALSE;
  3552. }
  3553. HWND SHHtmlHelpOnDemandW(HWND hwnd, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage, BOOL bUseML)
  3554. {
  3555. return SUCCEEDED(_JITHelpFileW(hwnd, pszFile)) ?
  3556. (bUseML ? MLHtmlHelpW(hwnd, pszFile, uCommand, dwData, dwCrossCodePage) :
  3557. HtmlHelpW(hwnd, pszFile, uCommand, dwData)) :
  3558. NULL;
  3559. }
  3560. HWND SHHtmlHelpOnDemandA(HWND hwnd, LPCSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage, BOOL bUseML)
  3561. {
  3562. return SUCCEEDED(_JITHelpFileA(hwnd, pszFile)) ?
  3563. (bUseML ? MLHtmlHelpA(hwnd, pszFile, uCommand, dwData, dwCrossCodePage) :
  3564. HtmlHelpA(hwnd, pszFile, uCommand, dwData)) :
  3565. NULL;
  3566. }
  3567. BOOL SHWinHelpOnDemandW(HWND hwnd, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData, BOOL bUseML)
  3568. {
  3569. HRESULT hr;
  3570. return SUCCEEDED(hr = _JITHelpFileW(hwnd, pszFile)) ?
  3571. (bUseML ? MLWinHelpW(hwnd, pszFile, uCommand, dwData) :
  3572. WinHelpWrapW(hwnd,pszFile, uCommand, dwData)) :
  3573. _JITSetLastError(hr);
  3574. }
  3575. BOOL SHWinHelpOnDemandA(HWND hwnd, LPCSTR pszFile, UINT uCommand, DWORD_PTR dwData, BOOL bUseML)
  3576. {
  3577. HRESULT hr;
  3578. return SUCCEEDED(hr = _JITHelpFileA(hwnd, pszFile)) ?
  3579. (bUseML ? MLWinHelpA(hwnd, pszFile, uCommand, dwData) :
  3580. WinHelpA(hwnd,pszFile, uCommand, dwData)) :
  3581. _JITSetLastError(hr);
  3582. }
  3583. /*****************************************************************************\
  3584. FUNCTION: SHPersistDataObject
  3585. DESCRIPTION:
  3586. This funciton exists for IDataObjects that don't want OLE to use the
  3587. default IDataObject implementation if OleFlushClipboard is called.
  3588. How to use:
  3589. 1. This function should be called when the IDataObject::GetData() method
  3590. is called with (FORMATETC.cfFormat ==
  3591. RegisterClipboardFormat(CFSTR_PERSISTEDDATAOBJECT)).
  3592. 2. OleFlushClipboard copies pMedium to it's own implementation of IDataObject
  3593. which doesn't work with the lindex parameter of FORMATETC or for private interfaces.
  3594. 3. OLE or the IDropTarget calls SHLoadPersistedDataObject(). The first
  3595. param will be OLE's IDataObject impl, and the second param (out param)
  3596. will be the original IDataObject. The new IDataObject will contains
  3597. the original state as long as it correctly implemented IPersistStream.
  3598. PARAMETERS:
  3599. pdoToPersist - This is the original IDataObject that implements IPersistStream.
  3600. pMedium - This is contain the persisted state of this object.
  3601. CFSTR_PERSISTEDDATAOBJECT can be used to read the data.
  3602. \*****************************************************************************/
  3603. #define SIZE_PERSISTDATAOBJECT (10 * 1024)
  3604. STDAPI SHPersistDataObject(/*IN*/ IDataObject * pdoToPersist, /*OUT*/ STGMEDIUM * pMedium)
  3605. {
  3606. HRESULT hr = E_NOTIMPL;
  3607. // We shipped IE 5.0 RTM with this and SHLoadPersistedDataObject(). We removed
  3608. // the code after the OLE32.DLL guys moved the functionality into ole32.dll.
  3609. // See the "OleClipboardPersistOnFlush" clipboard format.
  3610. return hr;
  3611. }
  3612. /*****************************************************************************\
  3613. FUNCTION: SHLoadPersistedDataObject
  3614. DESCRIPTION:
  3615. This funciton exists for IDataObjects that don't want OLE to use the
  3616. default IDataObject implementation if OleFlushClipboard is called.
  3617. How to use:
  3618. 1. SHPersistDataObject() was called when the IDataObject::GetData() method
  3619. is called with (FORMATETC.cfFormat == RegisterClipboardFormat(CFSTR_PERSISTEDDATAOBJECT)).
  3620. 2. OleFlushClipboard copies pMedium to it's own implementation of IDataObject
  3621. which doesn't work with the lindex parameter of FORMATETC or for private interfaces.
  3622. 3. OLE or the IDropTarget calls SHLoadPersistedDataObject(). The first
  3623. param will be OLE's IDataObject impl, and the second param (out param)
  3624. will be the original IDataObject. The new IDataObject will contains
  3625. the original state as long as it correctly implemented IPersistStream.
  3626. PARAMETERS:
  3627. pdo - This is OLE's IDataObject.
  3628. ppdoToPersist - This is the original IDataObject or equal to pdo if
  3629. un-serializing the object didn't work. It always has
  3630. it's own ref.
  3631. \*****************************************************************************/
  3632. STDAPI SHLoadPersistedDataObject(/*IN*/ IDataObject * pdo, /*OUT*/ IDataObject ** ppdoToPersist)
  3633. {
  3634. // See SHPersistDataObject() for details
  3635. return pdo->QueryInterface(IID_PPV_ARG(IDataObject, ppdoToPersist));
  3636. }
  3637. #ifndef SMTO_NOTIMEOUTIFNOTHUNG
  3638. #define SMTO_NOTIMEOUTIFNOTHUNG 0x0008
  3639. #endif
  3640. LWSTDAPI_(LRESULT) SHSendMessageBroadcastA(UINT uMsg, WPARAM wParam, LPARAM lParam)
  3641. {
  3642. ULONG_PTR lres = 0;
  3643. DWORD dwFlags = SMTO_ABORTIFHUNG;
  3644. if (g_bRunningOnNT5OrHigher)
  3645. dwFlags |= SMTO_NOTIMEOUTIFNOTHUNG;
  3646. SendMessageTimeoutA(HWND_BROADCAST, uMsg, wParam, lParam, dwFlags, 30 * 1000, &lres);
  3647. return (LRESULT) lres;
  3648. }
  3649. LWSTDAPI_(LRESULT) SHSendMessageBroadcastW(UINT uMsg, WPARAM wParam, LPARAM lParam)
  3650. {
  3651. ULONG_PTR lres = 0;
  3652. DWORD dwFlags = SMTO_ABORTIFHUNG;
  3653. if (g_bRunningOnNT5OrHigher)
  3654. dwFlags |= SMTO_NOTIMEOUTIFNOTHUNG;
  3655. SendMessageTimeoutWrapW(HWND_BROADCAST, uMsg, wParam, lParam, dwFlags, 30 * 1000, &lres);
  3656. return (LRESULT) lres;
  3657. }
  3658. #ifdef UNIX
  3659. /*****************************************************************************\
  3660. IEUNIX : Wrapper to get rid of undefined symbol on UNIX.
  3661. \*****************************************************************************/
  3662. STDAPI_(BOOL) SHCreateThreadPriv(
  3663. LPTHREAD_START_ROUTINE pfnThreadProc,
  3664. void *pvData,
  3665. DWORD dwFlags, // CTF_*
  3666. LPTHREAD_START_ROUTINE pfnCallback) OPTIONAL
  3667. {
  3668. return SHCreateThread(pfnThreadProc, pvData, dwFlags, pfnCallback);
  3669. }
  3670. #endif
  3671. #define MODULE_NAME_SIZE 128
  3672. #define MODULE_VERSION_SIZE 15
  3673. //
  3674. // If version is NULL, then we do it for all versions of the app.
  3675. //
  3676. // If version begins with MAJORVERSION, then we check only the major version.
  3677. // (CH_MAJORVERSION is the char version of same.)
  3678. //
  3679. #define MAJORVERSION TEXT("\1")
  3680. #define CH_MAJORVERSION TEXT('\1')
  3681. typedef struct tagAPPCOMPAT
  3682. {
  3683. LPCTSTR pszModule;
  3684. LPCTSTR pszVersion;
  3685. DWORD dwFlags;
  3686. } APPCOMPAT, *LPAPPCOMPAT;
  3687. typedef struct tagAPPCLASS
  3688. {
  3689. LPCTSTR pstzWndClass;
  3690. DWORD dwFlags;
  3691. } APPCLASS, *LPAPPCLASS;
  3692. typedef struct tagWNDDAT
  3693. {
  3694. const APPCLASS *rgAppClass;
  3695. DWORD cAppClass;
  3696. DWORD dwPid;
  3697. int irgFound;
  3698. } WNDDAT, *LPWNDDAT;
  3699. BOOL CALLBACK EnumWnd (HWND hwnd, LPARAM lParam)
  3700. {
  3701. TCHAR sz[256];
  3702. DWORD dwPid;
  3703. int cch;
  3704. LPWNDDAT pwd = (LPWNDDAT) lParam;
  3705. if (GetClassName (hwnd, sz, ARRAYSIZE(sz)))
  3706. {
  3707. cch = lstrlen (sz);
  3708. for (DWORD irg = 0; irg < pwd->cAppClass; irg++)
  3709. {
  3710. ASSERT(lstrlen(&(pwd->rgAppClass[irg].pstzWndClass[1])) == (int) pwd->rgAppClass[irg].pstzWndClass[0]);
  3711. if (lstrncmp (sz, &(pwd->rgAppClass[irg].pstzWndClass[1]),
  3712. min(cch, (int) pwd->rgAppClass[irg].pstzWndClass[0])) == 0)
  3713. {
  3714. GetWindowThreadProcessId(hwnd, &dwPid);
  3715. if (dwPid == pwd->dwPid)
  3716. {
  3717. pwd->irgFound = irg;
  3718. return FALSE;
  3719. }
  3720. }
  3721. }
  3722. }
  3723. return TRUE;
  3724. }
  3725. BOOL _IsAppCompatVersion(LPTSTR szModulePath, LPCTSTR pszVersionMatch)
  3726. {
  3727. if (pszVersionMatch == NULL) // Wildcard - match all versions
  3728. {
  3729. return TRUE;
  3730. }
  3731. else
  3732. {
  3733. CHAR chBuffer[4096]; // hopefully this is enough... Star Office 5 requires 3172
  3734. TCHAR* pszVersion = NULL;
  3735. UINT cb;
  3736. DWORD dwHandle;
  3737. // get module version here!
  3738. //
  3739. // Some apps use codepage 0x04E4 (1252 = CP_USASCII) and some use
  3740. // codepage 0x04B0 (1200 = CP_UNICODE).
  3741. //
  3742. // ...and then Star Office 5.00 uses 0407 instead of 0409.
  3743. // ...and then recycle.exe uses 041D Swedish
  3744. cb = GetFileVersionInfoSize(szModulePath, &dwHandle);
  3745. if (cb <= ARRAYSIZE(chBuffer) &&
  3746. GetFileVersionInfo(szModulePath, dwHandle, ARRAYSIZE(chBuffer), (void *)chBuffer) &&
  3747. (VerQueryValue((void *)chBuffer, TEXT("\\StringFileInfo\\040904E4\\ProductVersion"), (void **) &pszVersion, &cb) ||
  3748. VerQueryValue((void *)chBuffer, TEXT("\\StringFileInfo\\040704E4\\ProductVersion"), (void **) &pszVersion, &cb) ||
  3749. VerQueryValue((void *)chBuffer, TEXT("\\StringFileInfo\\040904B0\\ProductVersion"), (void **) &pszVersion, &cb) ||
  3750. //The following 040900000 was added for SnapShot.exe
  3751. VerQueryValue((void *)chBuffer, TEXT("\\StringFileInfo\\04090000\\ProductVersion"), (void **) &pszVersion, &cb) ||
  3752. VerQueryValue((void *)chBuffer, TEXT("\\StringFileInfo\\041D04B0\\ProductVersion"), (void **) &pszVersion, &cb)))
  3753. {
  3754. DWORD_PTR cchCmp = 0;
  3755. if (pszVersionMatch[0] == CH_MAJORVERSION)
  3756. {
  3757. // Truncate at the first comma or period
  3758. LPTSTR pszTemp = StrChr(pszVersion, TEXT(','));
  3759. if (pszTemp)
  3760. *pszTemp = 0;
  3761. pszTemp = StrChr(pszVersion, TEXT('.'));
  3762. if (pszTemp)
  3763. *pszTemp = 0;
  3764. pszVersionMatch++;
  3765. }
  3766. else
  3767. {
  3768. TCHAR *pch = StrChr(pszVersionMatch, TEXT('*'));
  3769. if (pch)
  3770. {
  3771. cchCmp = pch - pszVersionMatch;
  3772. }
  3773. }
  3774. if ((cchCmp && StrCmpNI(pszVersion, pszVersionMatch, (int)cchCmp) == 0)
  3775. || lstrcmpi(pszVersion, pszVersionMatch) == 0)
  3776. {
  3777. DebugMsg(TF_ALWAYS, TEXT("%s ver %s - compatibility hacks enabled"), PathFindFileName(szModulePath), pszVersion);
  3778. return TRUE;
  3779. }
  3780. }
  3781. }
  3782. return FALSE;
  3783. }
  3784. typedef struct {
  3785. DWORD flag;
  3786. LPCTSTR psz;
  3787. } FLAGMAP;
  3788. DWORD _GetMappedFlags(HKEY hk, const FLAGMAP *pmaps, DWORD cmaps)
  3789. {
  3790. DWORD dwRet = 0;
  3791. for (DWORD i = 0; i < cmaps; i++)
  3792. {
  3793. if (NOERROR == SHGetValue(hk, NULL, pmaps[i].psz, NULL, NULL, NULL))
  3794. dwRet |= pmaps[i].flag;
  3795. }
  3796. return dwRet;
  3797. }
  3798. #define ACFMAPPING(acf) {ACF_##acf, TEXT(#acf)}
  3799. DWORD _GetRegistryCompatFlags(LPTSTR pszModulePath)
  3800. {
  3801. DWORD dwRet = 0;
  3802. LPCTSTR pszModule = PathFindFileName(pszModulePath);
  3803. TCHAR sz[MAX_PATH];
  3804. HKEY hkApp;
  3805. wnsprintf(sz, ARRAYSIZE(sz), TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ShellCompatibility\\Applications\\%s"), pszModule);
  3806. if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, sz, 0, KEY_QUERY_VALUE | KEY_ENUMERATE_SUB_KEYS, &hkApp))
  3807. {
  3808. // Convert the module path into a directory so we can PathCombine it
  3809. TCHAR szDir[MAX_PATH];
  3810. lstrcpyn(szDir, pszModulePath, ARRAYSIZE(szDir));
  3811. PathRemoveFileSpec(szDir);
  3812. //
  3813. // HEADSUP! Strange loop ahead!
  3814. //
  3815. // We want the first RegOpenKeyEx to pass sz = NULL (so we look
  3816. // inside hkApp directly), and subsequent RegOpenKeyEx calls to
  3817. // pass the name of a subkey (so we look inside the subkeys).
  3818. //
  3819. // So the first time through the loop, we set sz = NULL.
  3820. // At the bottom of the loop, we set sz = Next Enumerated Key.
  3821. sz[0] = TEXT('\0'); /* Preinitialize for first iteration */
  3822. DWORD dwIndex = 0;
  3823. do {
  3824. HKEY hkSub;
  3825. if (ERROR_SUCCESS == RegOpenKeyEx(hkApp, sz, 0, KEY_QUERY_VALUE, &hkSub))
  3826. {
  3827. LPCTSTR pszValue;
  3828. DWORD dw = sizeof(sz);
  3829. if (NOERROR == SHGetValue(hkSub, NULL, TEXT("RequiredFile"), NULL, sz, &dw))
  3830. {
  3831. pszValue = PathCombine(sz, szDir, sz);
  3832. }
  3833. else
  3834. pszValue = NULL;
  3835. // If no RequiredFile or RequiredFile exists...
  3836. if (pszValue == NULL || GetFileAttributes(pszValue) != 0xFFFFFFFF)
  3837. {
  3838. if (NOERROR == SHGetValue(hkSub, NULL, TEXT("Version"), NULL, sz, &dw))
  3839. pszValue = sz;
  3840. else
  3841. pszValue = NULL;
  3842. if (_IsAppCompatVersion(pszModulePath, pszValue))
  3843. {
  3844. static const FLAGMAP rgAcfMaps[] = {
  3845. ACFMAPPING(CONTEXTMENU),
  3846. ACFMAPPING(CORELINTERNETENUM),
  3847. ACFMAPPING(OLDCREATEVIEWWND),
  3848. ACFMAPPING(WIN95DEFVIEW),
  3849. ACFMAPPING(DOCOBJECT),
  3850. ACFMAPPING(FLUSHNOWAITALWAYS),
  3851. ACFMAPPING(MYCOMPUTERFIRST),
  3852. ACFMAPPING(OLDREGITEMGDN),
  3853. ACFMAPPING(LOADCOLUMNHANDLER),
  3854. ACFMAPPING(ANSI),
  3855. ACFMAPPING(STAROFFICE5PRINTER),
  3856. ACFMAPPING(NOVALIDATEFSIDS),
  3857. ACFMAPPING(WIN95SHLEXEC),
  3858. ACFMAPPING(FILEOPENNEEDSEXT),
  3859. ACFMAPPING(WIN95BINDTOOBJECT),
  3860. ACFMAPPING(IGNOREENUMRESET),
  3861. ACFMAPPING(ANSIDISPLAYNAMES),
  3862. ACFMAPPING(FILEOPENBOGUSCTRLID),
  3863. ACFMAPPING(FORCELFNIDLIST),
  3864. };
  3865. dwRet |= _GetMappedFlags(hkSub, rgAcfMaps, ARRAYSIZE(rgAcfMaps));
  3866. }
  3867. }
  3868. RegCloseKey(hkSub);
  3869. }
  3870. } while (ERROR_SUCCESS == RegEnumKey(hkApp, dwIndex++, sz, ARRAYSIZE(sz)));
  3871. RegCloseKey(hkApp);
  3872. }
  3873. return dwRet;
  3874. }
  3875. DWORD SHGetAppCompatFlags (DWORD dwFlagsNeeded)
  3876. {
  3877. static BOOL bInitialized = FALSE;
  3878. static DWORD dwCachedProcessFlags = 0;
  3879. static const APPCOMPAT aAppCompat[] =
  3880. {
  3881. {TEXT("WPWIN7.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM},
  3882. {TEXT("PRWIN70.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM},
  3883. {TEXT("PS80.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3884. {TEXT("QPW.EXE"), MAJORVERSION TEXT("7"), ACF_CONTEXTMENU},
  3885. {TEXT("QFINDER.EXE"), NULL, ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3886. {TEXT("PFIM80.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3887. {TEXT("UA80.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3888. {TEXT("PDXWIN32.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3889. {TEXT("SITEBUILDER.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3890. {TEXT("HOTDOG4.EXE"), NULL, ACF_DOCOBJECT},
  3891. {TEXT("RNAAPP.EXE"), NULL, ACF_FLUSHNOWAITALWAYS},
  3892. //
  3893. // PDEXPLO.EXE version "2, 0, 2, 0" requires ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST
  3894. // PDEXPLO.EXE version "1, 0, 0, 0" requires ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST
  3895. // PDEXPLO.EXE version "3, 0, 0, 1" requires ACF_MYCOMPUTERFIRST
  3896. // PDEXPLO.EXE version "3, 0, 3, 0" requires ACF_MYCOMPUTERFIRST
  3897. //
  3898. // So I'm just going to key off the major versions.
  3899. //
  3900. {TEXT("PDEXPLO.EXE"), MAJORVERSION TEXT("2"), ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST},
  3901. {TEXT("PDEXPLO.EXE"), MAJORVERSION TEXT("1"), ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST},
  3902. {TEXT("PDEXPLO.EXE"), MAJORVERSION TEXT("3"), ACF_MYCOMPUTERFIRST | ACF_OLDREGITEMGDN},
  3903. // SIZEMGR.EXE is part of the PowerDesk 98 suite, so we also key off
  3904. // only the major version
  3905. {TEXT("SIZEMGR.EXE"), MAJORVERSION TEXT("3"), ACF_OLDCREATEVIEWWND | ACF_OLDREGITEMGDN},
  3906. {TEXT("SMARTCTR.EXE"), TEXT("96.0"), ACF_CONTEXTMENU},
  3907. // new programs, old bugs
  3908. {TEXT("WPWIN8.EXE"), NULL, ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3909. {TEXT("PRWIN8.EXE"), NULL, ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3910. {TEXT("UE32.EXE"), TEXT("2.00.0.0"), ACF_OLDREGITEMGDN},
  3911. {TEXT("PP70.EXE"),NULL, ACF_LOADCOLUMNHANDLER},
  3912. {TEXT("PP80.EXE"),NULL, ACF_LOADCOLUMNHANDLER},
  3913. {TEXT("PS80.EXE"),NULL, ACF_OLDREGITEMGDN},
  3914. {TEXT("ABCMM.EXE"),NULL,ACF_LOADCOLUMNHANDLER},
  3915. // We've found versions 8.0.0.153 and 8.0.0.227, so just use 8.*
  3916. {TEXT("QPW.EXE"), MAJORVERSION TEXT("8"), ACF_CORELINTERNETENUM | ACF_OLDCREATEVIEWWND | ACF_OLDREGITEMGDN | ACF_ANSIDISPLAYNAMES },
  3917. {TEXT("CORELDRW.EXE"), MAJORVERSION TEXT("7"), ACF_OLDREGITEMGDN},
  3918. {TEXT("FILLER51.EXE"), NULL, ACF_OLDREGITEMGDN},
  3919. //For Win95 and Win98
  3920. {TEXT("AUTORUN.EXE"), TEXT("4.10.1998"),ACF_ANSI},
  3921. {TEXT("AUTORUN.EXE"), TEXT("4.00.950"),ACF_ANSI},
  3922. //Powerpoint
  3923. {TEXT("POWERPNT.EXE"), MAJORVERSION TEXT("8"), ACF_WIN95SHLEXEC},
  3924. // msmoney
  3925. {TEXT("MSMONEY.EXE"), TEXT("7.05.1107"), ACF_WIN95SHLEXEC},
  3926. //Star Office 5.0
  3927. {TEXT("soffice.EXE"), MAJORVERSION TEXT("5"), ACF_STAROFFICE5PRINTER},
  3928. // All of the "Corel WordPerfect Office 2000" suite apps need ACF_WIN95DEFVIEW. Since the shipping
  3929. // version (9.0.0.528) as well as their SR1 release (9.0.0.588) are both broken, we key off
  3930. // of the major version
  3931. {TEXT("WPWIN9.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3932. {TEXT("QPW.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3933. {TEXT("PRWIN9.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3934. {TEXT("DAD9.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3935. //
  3936. // WARNING DONT ADD NEW COMPATIBILITY HERE - ZekeL - 18-OCT-99
  3937. // Add new entries to the registry. each component
  3938. // that needs compatibility flags should register
  3939. // during selfregistration. (see the RegExternal
  3940. // section of selfreg.inx in shell32 for an example.)
  3941. // all new flags should be added to the FLAGMAP array.
  3942. //
  3943. // the register under:
  3944. // HKLM\SW\MS\Win\CV\ShellCompatibility\Applications
  3945. // \App.exe
  3946. // RequiredFile="OtherFile.dat" // optional
  3947. // Version = "1.0.0.1" or "1.*" // version of App.exe
  3948. // // NOTE version supports basic pattern matching,
  3949. // // but doesnt currently support multiple versions
  3950. // // for multiple versions, see below
  3951. // FLAGNAME // requires no value
  3952. //
  3953. // If a RequiredFile is present, then it is PathCombine'd with
  3954. // the directory that App.exe is in and checked for existence.
  3955. // The file must exist for the app compat flag to be used.
  3956. // RequiredFile is strongly recommended to avoid false positives.
  3957. //
  3958. // If the app name is generic (like "setup.exe" or "install.exe")
  3959. // then you must use the RequiredFile method to ensure
  3960. // that the app compat flag fires only for the app you care about.
  3961. //
  3962. // To accomodate multiple entries for one EXE name (e.g., the
  3963. // multiple versions problem described above), we will
  3964. // also look inside all subdirectories of Applications\App.exe
  3965. // for a matching app compat flag.
  3966. //
  3967. // For example, Starcraft 1.03 INSTALL.EXE uses this key layout:
  3968. //
  3969. // HKLM\SW\MS\Win\CV\ShellCompatibility\Applications
  3970. // \install.exe (name of exe)
  3971. // \Starcraft 1.03 (arbitrary unique string)
  3972. // RequiredFile="HELP\STAR.HTM" (unique file on CD)
  3973. // Version = "1.0.0.1" or "1.*" (same as above)
  3974. // FLAGNAME (same as above)
  3975. //
  3976. };
  3977. static const APPCLASS aAppClass[] =
  3978. {
  3979. // note that the strings here are stz's....
  3980. {TEXT("\x9""bosa_sdm_"), ACF_APPISOFFICE | ACF_STRIPFOLDERBIT},
  3981. {TEXT("\x18""File Open Message Window"), ACF_APPISOFFICE | ACF_STRIPFOLDERBIT},
  3982. };
  3983. if (dwFlagsNeeded & (ACF_PERPROCESSFLAGS))
  3984. {
  3985. if (!bInitialized)
  3986. {
  3987. //
  3988. // Do this only for old apps.
  3989. //
  3990. // Once an app marks itself as NT5-compatible, we stop doing
  3991. // NT4/Win5 app hacks for it.
  3992. //
  3993. if (GetProcessVersion(0) < MAKELONG(0, 5))
  3994. {
  3995. TCHAR szModulePath[MODULE_NAME_SIZE];
  3996. TCHAR* pszModuleName = NULL;
  3997. if (GetModuleFileName(GetModuleHandle(NULL), szModulePath, ARRAYSIZE(szModulePath)))
  3998. pszModuleName = PathFindFileName(szModulePath);
  3999. if (pszModuleName)
  4000. {
  4001. for (int i=0; i < ARRAYSIZE(aAppCompat); i++)
  4002. {
  4003. if (lstrcmpi(aAppCompat[i].pszModule, pszModuleName) == 0)
  4004. {
  4005. if (_IsAppCompatVersion(szModulePath, aAppCompat[i].pszVersion))
  4006. {
  4007. dwCachedProcessFlags = aAppCompat[i].dwFlags;
  4008. break;
  4009. }
  4010. }
  4011. }
  4012. dwCachedProcessFlags |= _GetRegistryCompatFlags(szModulePath);
  4013. }
  4014. }
  4015. bInitialized = TRUE;
  4016. }
  4017. }
  4018. if ((dwFlagsNeeded & ACF_PERCALLFLAGS) &&
  4019. !(dwCachedProcessFlags & ACF_KNOWPERPROCESS))
  4020. {
  4021. WNDDAT wd;
  4022. wd.dwPid = GetCurrentProcessId();
  4023. wd.irgFound = -1;
  4024. wd.rgAppClass = aAppClass;
  4025. wd.cAppClass = ARRAYSIZE(aAppClass);
  4026. EnumWindows (EnumWnd, (LPARAM) &wd);
  4027. if (wd.irgFound > -1)
  4028. {
  4029. dwCachedProcessFlags |= (aAppClass[wd.irgFound].dwFlags);
  4030. }
  4031. dwCachedProcessFlags |= ACF_KNOWPERPROCESS;
  4032. }
  4033. return dwCachedProcessFlags;
  4034. }
  4035. // {9EAC43C0-53EC-11CE-8230-CA8A32CF5494}
  4036. //static const GUID GUID_WINAMP =
  4037. //{ 0x9eac43c0, 0x53ec, 0x11ce, { 0x82, 0x30, 0xca, 0x8a, 0x32, 0xcf, 0x54, 0x94} };
  4038. // {E9779583-939D-11CE-8A77-444553540000}
  4039. static const GUID GUID_AECOZIPARCHIVE =
  4040. { 0xE9779583, 0x939D, 0x11ce, { 0x8a, 0x77, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} };
  4041. // {49707377-6974-6368-2E4A-756E6F644A01}
  4042. static const GUID CLSID_WS_FTP_PRO_EXPLORER =
  4043. { 0x49707377, 0x6974, 0x6368, {0x2E, 0x4A,0x75, 0x6E, 0x6F, 0x64, 0x4A, 0x01} };
  4044. // {49707377-6974-6368-2E4A-756E6F644A0A}
  4045. static const GUID CLSID_WS_FTP_PRO =
  4046. { 0x49707377, 0x6974, 0x6368, {0x2E, 0x4A,0x75, 0x6E, 0x6F, 0x64, 0x4A, 0x0A} };
  4047. // {2bbbb600-3f0a-11d1-8aeb-00c04fd28d85}
  4048. static const GUID CLSID_KODAK_DC260_ZOOM_CAMERA =
  4049. { 0x2bbbb600, 0x3f0a, 0x11d1, {0x8a, 0xeb, 0x00, 0xc0, 0x4f, 0xd2, 0x8d, 0x85} };
  4050. // {00F43EE0-EB46-11D1-8443-444553540000}
  4051. static const GUID GUID_MACINDOS =
  4052. { 0x00F43EE0, 0xEB46, 0x11D1, { 0x84, 0x43, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} };
  4053. static const GUID CLSID_EasyZIP =
  4054. { 0xD1069700, 0x932E, 0x11cf, { 0xAB, 0x59, 0x00, 0x60, 0x8C, 0xBF, 0x2C, 0xE0} };
  4055. static const GUID CLSID_PAGISPRO_FOLDER =
  4056. { 0x7877C8E0, 0x8B13, 0x11D0, { 0x92, 0xC2, 0x00, 0xAA, 0x00, 0x4B, 0x25, 0x6F} };
  4057. // {61E285C0-DCF4-11cf-9FF4-444553540000}
  4058. static const GUID CLSID_FILENET_IDMDS_NEIGHBORHOOD =
  4059. { 0x61e285c0, 0xdcf4, 0x11cf, { 0x9f, 0xf4, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} };
  4060. // These guys call CoFreeUnusedLibraries inside their Release() handler, so
  4061. // if you are releasing the last object, they end up FreeLibrary()ing
  4062. // themselves!
  4063. // {b8777200-d640-11ce-b9aa-444553540000}
  4064. static const GUID CLSID_NOVELLX =
  4065. { 0xb8777200, 0xd640, 0x11ce, { 0xb9, 0xaa, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} };
  4066. static const GUID CLSID_PGP50_CONTEXTMENU = //{969223C0-26AA-11D0-90EE-444553540000}
  4067. { 0x969223C0, 0x26AA, 0x11D0, { 0x90, 0xEE, 0x44, 0x45, 0x53, 0x54, 0x00, 0x00} };
  4068. static const GUID CLSID_QUICKFINDER_CONTEXTMENU = // {CD949A20-BDC8-11CE-8919-00608C39D066}
  4069. { 0xCD949A20, 0xBDC8, 0x11CE, { 0x89, 0x19, 0x00, 0x60, 0x8C, 0x39, 0xD0, 0x66} };
  4070. static const GUID CLSID_HERCULES_HCTNT_V1001 = // {921BD320-8CB5-11CF-84CF-885835D9DC01}
  4071. { 0x921BD320, 0x8CB5, 0x11CF, { 0x84, 0xCF, 0x88, 0x58, 0x35, 0xD9, 0xDC, 0x01} };
  4072. //
  4073. // NOTICE - DONT ADD ANYMORE HARDCODED CLSIDS
  4074. // add them to the ShellCompatibility key. register in the client DLL
  4075. //
  4076. #define OCFMAPPING(ocf) {OBJCOMPATF_##ocf, TEXT(#ocf)}
  4077. DWORD _GetRegistryObjectCompatFlags(REFGUID clsid)
  4078. {
  4079. DWORD dwRet = 0;
  4080. TCHAR szGuid[GUIDSTR_MAX];
  4081. TCHAR sz[MAX_PATH];
  4082. HKEY hk;
  4083. SHStringFromGUID(clsid, szGuid, ARRAYSIZE(szGuid));
  4084. wnsprintf(sz, ARRAYSIZE(sz), TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\ShellCompatibility\\Objects\\%s"), szGuid);
  4085. if (NOERROR == RegOpenKeyEx(HKEY_LOCAL_MACHINE, sz, 0, KEY_QUERY_VALUE, &hk))
  4086. {
  4087. static const FLAGMAP rgOcfMaps[] = {
  4088. OCFMAPPING(OTNEEDSSFCACHE),
  4089. OCFMAPPING(NO_WEBVIEW),
  4090. OCFMAPPING(UNBINDABLE),
  4091. OCFMAPPING(PINDLL),
  4092. OCFMAPPING(NEEDSFILESYSANCESTOR),
  4093. OCFMAPPING(NOTAFILESYSTEM),
  4094. OCFMAPPING(CTXMENU_NOVERBS),
  4095. OCFMAPPING(CTXMENU_LIMITEDQI),
  4096. OCFMAPPING(COCREATESHELLFOLDERONLY),
  4097. OCFMAPPING(NEEDSSTORAGEANCESTOR),
  4098. OCFMAPPING(NOLEGACYWEBVIEW),
  4099. };
  4100. dwRet = _GetMappedFlags(hk, rgOcfMaps, ARRAYSIZE(rgOcfMaps));
  4101. RegCloseKey(hk);
  4102. }
  4103. return dwRet;
  4104. }
  4105. typedef struct _CLSIDCOMPAT
  4106. {
  4107. const GUID *pclsid;
  4108. OBJCOMPATFLAGS flags;
  4109. }CLSIDCOMPAT, *PCLSIDCOMPAT;
  4110. STDAPI_(OBJCOMPATFLAGS) SHGetObjectCompatFlags(IUnknown *punk, const CLSID *pclsid)
  4111. {
  4112. HRESULT hr = E_INVALIDARG;
  4113. OBJCOMPATFLAGS ocf = 0;
  4114. CLSID clsid;
  4115. if (punk)
  4116. hr = IUnknown_GetClassID(punk, &clsid);
  4117. else if (pclsid)
  4118. {
  4119. clsid = *pclsid;
  4120. hr = S_OK;
  4121. }
  4122. if (SUCCEEDED(hr))
  4123. {
  4124. static const CLSIDCOMPAT s_rgCompat[] =
  4125. {
  4126. {&CLSID_WS_FTP_PRO_EXPLORER,
  4127. OBJCOMPATF_OTNEEDSSFCACHE | OBJCOMPATF_PINDLL },
  4128. {&CLSID_WS_FTP_PRO,
  4129. OBJCOMPATF_UNBINDABLE},
  4130. {&GUID_AECOZIPARCHIVE,
  4131. OBJCOMPATF_OTNEEDSSFCACHE | OBJCOMPATF_NO_WEBVIEW},
  4132. {&CLSID_KODAK_DC260_ZOOM_CAMERA,
  4133. OBJCOMPATF_OTNEEDSSFCACHE | OBJCOMPATF_PINDLL},
  4134. {&GUID_MACINDOS,
  4135. OBJCOMPATF_NO_WEBVIEW},
  4136. {&CLSID_EasyZIP,
  4137. OBJCOMPATF_NO_WEBVIEW},
  4138. {&CLSID_PAGISPRO_FOLDER,
  4139. OBJCOMPATF_NEEDSFILESYSANCESTOR},
  4140. {&CLSID_FILENET_IDMDS_NEIGHBORHOOD,
  4141. OBJCOMPATF_NOTAFILESYSTEM},
  4142. {&CLSID_NOVELLX,
  4143. OBJCOMPATF_PINDLL},
  4144. {&CLSID_PGP50_CONTEXTMENU,
  4145. OBJCOMPATF_CTXMENU_LIMITEDQI},
  4146. {&CLSID_QUICKFINDER_CONTEXTMENU,
  4147. OBJCOMPATF_CTXMENU_NOVERBS},
  4148. {&CLSID_HERCULES_HCTNT_V1001,
  4149. OBJCOMPATF_PINDLL},
  4150. //
  4151. // WARNING DONT ADD NEW COMPATIBILITY HERE - ZekeL - 18-OCT-99
  4152. // Add new entries to the registry. each component
  4153. // that needs compatibility flags should register
  4154. // during selfregistration. (see the RegExternal
  4155. // section of selfreg.inx in shell32 for an example.)
  4156. // all new flags should be added to the FLAGMAP array.
  4157. //
  4158. // the register under:
  4159. // HKLM\SW\MS\Win\CV\ShellCompatibility\Objects
  4160. // \{CLSID}
  4161. // FLAGNAME // requires no value
  4162. //
  4163. // NOTE: there is no version checking
  4164. // but we could add it as the data attached to
  4165. // the flags, and compare with the version
  4166. // of the LocalServer32 dll.
  4167. //
  4168. {NULL, 0}
  4169. };
  4170. for (int i = 0; s_rgCompat[i].pclsid; i++)
  4171. {
  4172. if (IsEqualGUID(clsid, *(s_rgCompat[i].pclsid)))
  4173. {
  4174. // we could check version based
  4175. // on what is in under HKCR\CLSID\{clsid}
  4176. ocf = s_rgCompat[i].flags;
  4177. break;
  4178. }
  4179. }
  4180. ocf |= _GetRegistryObjectCompatFlags(clsid);
  4181. }
  4182. return ocf;
  4183. }
  4184. STDAPI IUnknown_ProfferServiceOld(IUnknown *punkSite, REFGUID sidWhere,
  4185. REFGUID sidWhat, IServiceProvider *pService,
  4186. DWORD *pdwCookie)
  4187. {
  4188. IProfferService *pps;
  4189. HRESULT hr = IUnknown_QueryService(punkSite, sidWhere, IID_PPV_ARG(IProfferService, &pps));
  4190. if (SUCCEEDED(hr))
  4191. {
  4192. if (pService)
  4193. hr = pps->ProfferService(sidWhat, pService, pdwCookie);
  4194. else
  4195. hr = pps->RevokeService(*pdwCookie);
  4196. pps->Release();
  4197. }
  4198. return hr;
  4199. }
  4200. // helper to get up to service provider and to do register/unregister
  4201. // two forms:
  4202. // pService != NULL, register, pdwCookie is [out] returns cookie
  4203. // pService == NULL, unregister, *pdwCookie is [in] de-registers the service
  4204. STDAPI IUnknown_ProfferService(IUnknown *punkSite,
  4205. REFGUID sidWhat, IServiceProvider *pService,
  4206. DWORD *pdwCookie)
  4207. {
  4208. IProfferService *pps;
  4209. HRESULT hr = IUnknown_QueryService(punkSite, SID_SProfferService, IID_PPV_ARG(IProfferService, &pps));
  4210. if (SUCCEEDED(hr))
  4211. {
  4212. if (pService)
  4213. hr = pps->ProfferService(sidWhat, pService, pdwCookie);
  4214. else
  4215. {
  4216. hr = pps->RevokeService(*pdwCookie);
  4217. *pdwCookie = 0;
  4218. }
  4219. pps->Release();
  4220. }
  4221. return hr;
  4222. }
  4223. HRESULT IUnknown_QueryServiceExec(IUnknown* punk, REFGUID guidService, const GUID *guid,
  4224. DWORD cmdID, DWORD cmdParam, VARIANT* pvarargIn, VARIANT* pvarargOut)
  4225. {
  4226. IOleCommandTarget* poct;
  4227. HRESULT hres = IUnknown_QueryService(punk, guidService, IID_PPV_ARG(IOleCommandTarget, &poct));
  4228. if (SUCCEEDED(hres))
  4229. {
  4230. hres = poct->Exec(guid, cmdID, cmdParam, pvarargIn, pvarargOut);
  4231. poct->Release();
  4232. }
  4233. return hres;
  4234. }
  4235. HRESULT IUnknown_QueryServicePropertyBag(IUnknown* punk, DWORD dwFlags, REFIID riid, void** ppv)
  4236. {
  4237. IShellBrowserService* psbs;
  4238. HRESULT hr = IUnknown_QueryService(punk, SID_STopLevelBrowser, IID_PPV_ARG(IShellBrowserService, &psbs));
  4239. if (SUCCEEDED(hr))
  4240. {
  4241. hr = psbs->GetPropertyBag(dwFlags, riid, ppv);
  4242. psbs->Release();
  4243. }
  4244. return hr;
  4245. }
  4246. HRESULT SHConvertGraphicsFile(IN LPCWSTR pszSourceFile, IN LPCWSTR pszDestFile, IN DWORD dwFlags)
  4247. {
  4248. HRESULT hr = S_OK;
  4249. HRESULT hrInit = SHCoInitialize();
  4250. if ((dwFlags & SHCGF_REPLACEFILE) && PathFileExistsW(pszDestFile))
  4251. {
  4252. if (!DeleteFileW(pszDestFile))
  4253. {
  4254. hr = HRESULT_FROM_WIN32(GetLastError());
  4255. }
  4256. }
  4257. if (SUCCEEDED(hr))
  4258. {
  4259. if (PathFileExistsW(pszDestFile))
  4260. {
  4261. hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
  4262. }
  4263. else
  4264. {
  4265. IShellImageDataFactory * pImgFact;
  4266. hr = CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IShellImageDataFactory, &pImgFact));
  4267. if (SUCCEEDED(hr))
  4268. {
  4269. GUID guidDontCare;
  4270. hr = pImgFact->GetDataFormatFromPath(pszDestFile, &guidDontCare);
  4271. if (SUCCEEDED(hr))
  4272. {
  4273. // Open image file
  4274. IShellImageData * pImage;
  4275. hr = pImgFact->CreateImageFromFile(pszSourceFile, &pImage);
  4276. if (SUCCEEDED(hr))
  4277. {
  4278. hr = pImage->Decode(SHIMGDEC_DEFAULT, 0, 0);
  4279. if (SUCCEEDED(hr))
  4280. {
  4281. // load the file
  4282. IPersistFile *ppfImg;
  4283. hr = pImage->QueryInterface(IID_PPV_ARG(IPersistFile, &ppfImg));
  4284. if (SUCCEEDED(hr))
  4285. {
  4286. // saving to a different extention automatically changes the file type
  4287. hr = ppfImg->Save(pszDestFile, TRUE);
  4288. ppfImg->Release();
  4289. }
  4290. }
  4291. pImage->Release();
  4292. }
  4293. }
  4294. pImgFact->Release();
  4295. }
  4296. }
  4297. }
  4298. SHCoUninitialize(hrInit);
  4299. return hr;
  4300. }
  4301. void _ValidateShellNoRoam(HKEY hk)
  4302. {
  4303. WCHAR szOld[MAX_COMPUTERNAME_LENGTH + 1] = L"";
  4304. WCHAR szNew[MAX_COMPUTERNAME_LENGTH + 1] = L"";
  4305. DWORD cb = sizeof(szOld);
  4306. SHGetValueW(hk, NULL, NULL, NULL, szOld, &cb);
  4307. cb = ARRAYSIZE(szNew);
  4308. GetComputerNameW(szNew, &cb);
  4309. if (StrCmpICW(szNew, szOld))
  4310. {
  4311. // need to delete this key's kids
  4312. SHDeleteKey(hk, NULL);
  4313. SHSetValueW(hk, NULL, NULL, REG_SZ, szNew, CbFromCchW(lstrlenW(szNew)+1));
  4314. }
  4315. }
  4316. void _ValidateMUICache(HKEY hk)
  4317. {
  4318. LANGID lidOld = 0;
  4319. // if we are running on legacy platforms, we aggressively invalidate
  4320. LANGID lidNew = g_bRunningOnNT5OrHigher ? MLGetUILanguage() : -1;
  4321. DWORD cb = sizeof(lidOld);
  4322. SHGetValueW(hk, NULL, L"LangID", NULL, &lidOld, &cb);
  4323. if (lidOld != lidNew)
  4324. {
  4325. SHDeleteKey(hk, NULL);
  4326. SHSetValueW(hk, NULL, L"LangID", REG_BINARY, &lidNew, sizeof(lidNew));
  4327. }
  4328. }
  4329. typedef void (*PFNVALIDATE)(HKEY);
  4330. typedef struct
  4331. {
  4332. LPCWSTR psz;
  4333. DWORD dwOption;
  4334. PFNVALIDATE pfnValidate;
  4335. HKEY hkCU;
  4336. HKEY hkLM;
  4337. } SKCACHE;
  4338. #define SKENTRY(s) {s, REG_OPTION_NON_VOLATILE, NULL, NULL, NULL}
  4339. #define SKENTRYOPT(s, o) {s, o, NULL, NULL, NULL}
  4340. #define SKENTRYVAL(s, pfnV) {s, REG_OPTION_NON_VOLATILE, pfnV, NULL, NULL}
  4341. static SKCACHE s_skPath[] =
  4342. {
  4343. SKENTRY(L"Software\\Microsoft\\Windows\\CurrentVersion\\Explorer"),
  4344. SKENTRY(L"Software\\Microsoft\\Windows\\Shell"),
  4345. SKENTRYVAL(L"Software\\Microsoft\\Windows\\ShellNoRoam", _ValidateShellNoRoam),
  4346. SKENTRY(L"Software\\Classes"),
  4347. };
  4348. static SKCACHE s_skSub[] =
  4349. {
  4350. SKENTRY(L"LocalizedResourceName"),
  4351. SKENTRY(L"Handlers"),
  4352. SKENTRY(L"Associations"),
  4353. SKENTRYOPT(L"Volatile", REG_OPTION_VOLATILE),
  4354. SKENTRYVAL(L"MUICache", _ValidateMUICache),
  4355. SKENTRY(L"FileExts"),
  4356. };
  4357. HKEY _OpenKey(HKEY hk, LPCWSTR psz, BOOL fCreate, DWORD dwOption)
  4358. {
  4359. HKEY hkRet = NULL;
  4360. DWORD err;
  4361. if (fCreate && psz)
  4362. {
  4363. DWORD dwDisp;
  4364. err = RegCreateKeyExWrapW(hk, psz, 0, NULL, dwOption, MAXIMUM_ALLOWED, NULL, &hkRet, &dwDisp);
  4365. }
  4366. else
  4367. {
  4368. err = RegOpenKeyExWrapW(hk, psz, 0, MAXIMUM_ALLOWED, &hkRet);
  4369. }
  4370. if (!hkRet)
  4371. {
  4372. // if ERROR_KEY_DELETED
  4373. // should we invalidate our cache??
  4374. // cause we will fail forever...
  4375. SetLastError(err);
  4376. }
  4377. return hkRet;
  4378. }
  4379. HKEY _OpenSKCache(HKEY hk, BOOL fHKLM, BOOL fNoCaching, BOOL fCreateSub, SKCACHE *psk, DWORD *pdwOption)
  4380. {
  4381. HKEY hkSub = fHKLM ? psk->hkLM : psk->hkCU;
  4382. *pdwOption = psk->dwOption;
  4383. if (!hkSub || fNoCaching)
  4384. {
  4385. hkSub = _OpenKey(hk, psk->psz, fCreateSub, psk->dwOption);
  4386. if (hkSub)
  4387. {
  4388. if (psk->pfnValidate)
  4389. psk->pfnValidate(hkSub);
  4390. if (!fNoCaching)
  4391. {
  4392. ENTERCRITICAL;
  4393. HKEY *phk = fHKLM ? &psk->hkLM : &psk->hkCU;
  4394. if (!*phk)
  4395. {
  4396. *phk = hkSub;
  4397. }
  4398. else
  4399. {
  4400. RegCloseKey(hkSub);
  4401. hkSub = *phk;
  4402. }
  4403. LEAVECRITICAL;
  4404. }
  4405. }
  4406. }
  4407. return hkSub;
  4408. }
  4409. #define HKEY_FROM_SKROOT(sk) ((sk & SKROOT_MASK) == SKROOT_HKLM ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER)
  4410. HKEY _OpenShellKey(SHELLKEY sk, HKEY hkRoot, BOOL fNoCaching, BOOL fCreateSub, DWORD *pdwOption)
  4411. {
  4412. BOOL fHKLM = (sk & SKROOT_MASK) == SKROOT_HKLM;
  4413. ULONG uPath = (sk & SKPATH_MASK) >> 4;
  4414. ULONG uSub = (sk & SKSUB_MASK) >> 12;
  4415. ASSERT(uPath < ARRAYSIZE(s_skPath));
  4416. HKEY hkPath = NULL;
  4417. if (uPath < ARRAYSIZE(s_skPath))
  4418. {
  4419. hkPath = _OpenSKCache(hkRoot, fHKLM, fNoCaching, fCreateSub, &s_skPath[uPath], pdwOption);
  4420. }
  4421. else
  4422. SetLastError(E_INVALIDARG);
  4423. // see if there is a sub value to add
  4424. if (hkPath && uSub != SKSUB_NONE && --uSub < ARRAYSIZE(s_skSub))
  4425. {
  4426. HKEY hkSub = _OpenSKCache(hkPath, fHKLM, fNoCaching, fCreateSub, &s_skSub[uSub], pdwOption);
  4427. if (fNoCaching)
  4428. RegCloseKey(hkPath);
  4429. hkPath = hkSub;
  4430. }
  4431. return hkPath;
  4432. }
  4433. HKEY _GetRootKey(SHELLKEY sk, BOOL *pfNoCaching)
  4434. {
  4435. HKEY hkRoot = HKEY_FROM_SKROOT(sk);
  4436. HANDLE hToken;
  4437. if (hkRoot == HKEY_CURRENT_USER && OpenThreadToken(GetCurrentThread(), TOKEN_QUERY | TOKEN_IMPERSONATE, TRUE, &hToken))
  4438. {
  4439. // we dont support ARBITRARY tokens
  4440. // but RegOpenCurrentUser() opens the current thread token
  4441. NT5_RegOpenCurrentUser(MAXIMUM_ALLOWED, &hkRoot);
  4442. // if we wanted to we would have to do something
  4443. // like shell32!GetUserProfileKey(hToken, &hkRoot);
  4444. CloseHandle(hToken);
  4445. }
  4446. *pfNoCaching = HKEY_FROM_SKROOT(sk) != hkRoot;
  4447. return hkRoot;
  4448. }
  4449. STDAPI_(HKEY) SHGetShellKey(SHELLKEY sk, LPCWSTR pszSubKey, BOOL fCreateSub)
  4450. {
  4451. BOOL fNoCaching;
  4452. HKEY hkRoot = _GetRootKey(sk, &fNoCaching);
  4453. HKEY hkRet = NULL;
  4454. if (hkRoot)
  4455. {
  4456. DWORD dwOption;
  4457. HKEY hkPath = _OpenShellKey(sk, hkRoot, fNoCaching, fCreateSub, &dwOption);
  4458. // this duplicates when there is no subkey
  4459. if (hkPath)
  4460. {
  4461. hkRet = _OpenKey(hkPath, pszSubKey, fCreateSub, dwOption);
  4462. if (fNoCaching)
  4463. RegCloseKey(hkPath);
  4464. }
  4465. if (fNoCaching)
  4466. RegCloseKey(hkRoot);
  4467. }
  4468. else
  4469. SetLastError(ERROR_ACCESS_DENIED);
  4470. return hkRet;
  4471. }
  4472. STDAPI_(void) InitShellKeys(BOOL fInit)
  4473. {
  4474. if (!fInit)
  4475. {
  4476. int i;
  4477. // walk each array and close the cached keys
  4478. for (i = 0; i < ARRAYSIZE(s_skPath); i++)
  4479. {
  4480. if (s_skPath[i].hkCU)
  4481. {
  4482. RegCloseKey(s_skPath[i].hkCU);
  4483. s_skPath[i].hkCU = NULL;
  4484. }
  4485. if (s_skPath[i].hkLM)
  4486. {
  4487. RegCloseKey(s_skPath[i].hkLM);
  4488. s_skPath[i].hkLM = NULL;
  4489. }
  4490. }
  4491. for (i = 0; i < ARRAYSIZE(s_skSub); i++)
  4492. {
  4493. if (s_skSub[i].hkCU)
  4494. {
  4495. RegCloseKey(s_skSub[i].hkCU);
  4496. s_skSub[i].hkCU = NULL;
  4497. }
  4498. if (s_skSub[i].hkLM)
  4499. {
  4500. RegCloseKey(s_skSub[i].hkLM);
  4501. s_skSub[i].hkLM = NULL;
  4502. }
  4503. }
  4504. }
  4505. }
  4506. STDAPI SKGetValueW(
  4507. IN SHELLKEY sk,
  4508. IN LPCWSTR pwszSubKey, OPTIONAL
  4509. IN LPCWSTR pwszValue, OPTIONAL
  4510. OUT DWORD * pdwType, OPTIONAL
  4511. OUT void * pvData, OPTIONAL
  4512. OUT DWORD * pcbData) OPTIONAL
  4513. {
  4514. HKEY hk = SHGetShellKey(sk, pwszSubKey, FALSE);
  4515. if (hk)
  4516. {
  4517. DWORD err = SHQueryValueExW(hk, pwszValue, NULL, pdwType, pvData, pcbData);
  4518. RegCloseKey(hk);
  4519. return HRESULT_FROM_WIN32(err);
  4520. }
  4521. return HRESULT_FROM_WIN32(GetLastError());
  4522. }
  4523. STDAPI SKSetValueW(
  4524. IN SHELLKEY sk,
  4525. IN LPCWSTR pwszSubKey, OPTIONAL
  4526. IN LPCWSTR pwszValue,
  4527. IN DWORD dwType,
  4528. IN LPCVOID pvData,
  4529. IN DWORD cbData)
  4530. {
  4531. HKEY hk = SHGetShellKey(sk, pwszSubKey, TRUE);
  4532. if (hk)
  4533. {
  4534. // RegSetValueExW is not supported on Win95 but we have a thunking function.
  4535. DWORD err = RegSetValueExWrapW(hk, pwszValue, 0, dwType, (BYTE *)pvData, cbData);
  4536. RegCloseKey(hk);
  4537. return HRESULT_FROM_WIN32(err);
  4538. }
  4539. return HRESULT_FROM_WIN32(GetLastError());
  4540. }
  4541. STDAPI SKDeleteValueW(
  4542. IN SHELLKEY sk,
  4543. IN LPCWSTR pwszSubKey, OPTIONAL
  4544. IN LPCWSTR pwszValue)
  4545. {
  4546. HKEY hk = SHGetShellKey(sk, pwszSubKey, TRUE);
  4547. if (hk)
  4548. {
  4549. // RegSetValueExW is not supported on Win95 but we have a thunking function.
  4550. DWORD err = RegDeleteValueWrapW(hk, pwszValue);
  4551. RegCloseKey(hk);
  4552. return HRESULT_FROM_WIN32(err);
  4553. }
  4554. return HRESULT_FROM_WIN32(GetLastError());
  4555. }
  4556. STDAPI SKAllocValueW(
  4557. IN SHELLKEY sk,
  4558. IN LPCWSTR pwszSubKey, OPTIONAL
  4559. IN LPCWSTR pwszValue, OPTIONAL
  4560. OUT DWORD * pdwType, OPTIONAL
  4561. OUT void ** ppvData,
  4562. OUT DWORD * pcbData) OPTIONAL
  4563. {
  4564. HKEY hk = SHGetShellKey(sk, pwszSubKey, FALSE);
  4565. if (hk)
  4566. {
  4567. DWORD cbData;
  4568. DWORD err = SHQueryValueExW(hk, pwszValue, NULL, NULL, NULL, &cbData);
  4569. if (err == ERROR_SUCCESS)
  4570. {
  4571. // we add an extra char incase we need a NULL terminator
  4572. *ppvData = LocalAlloc(LPTR, cbData + sizeof(WCHAR));
  4573. if (*ppvData)
  4574. {
  4575. err = SHQueryValueExW(hk, pwszValue, NULL, pdwType, *ppvData, &cbData);
  4576. if (err == ERROR_SUCCESS)
  4577. {
  4578. if (pcbData)
  4579. *pcbData = cbData;
  4580. }
  4581. else
  4582. {
  4583. LocalFree(*ppvData);
  4584. *ppvData = NULL;
  4585. }
  4586. }
  4587. else
  4588. err = ERROR_NOT_ENOUGH_MEMORY;
  4589. }
  4590. RegCloseKey(hk);
  4591. return HRESULT_FROM_WIN32(err);
  4592. }
  4593. return HRESULT_FROM_WIN32(GetLastError());
  4594. }
  4595. //
  4596. // SHBoolSystemParametersInfo
  4597. //
  4598. // Wrapper around SystemParametersInfo to deal with various
  4599. // parameter semantics of boolean SPI's.
  4600. //
  4601. // The return value is just the result of the call to SPI.
  4602. // If you're querying for a value, you need to look at the
  4603. // value returned in pdwParam.
  4604. //
  4605. // Feel free to add more cases to the switch statement below
  4606. // if you need them.
  4607. //
  4608. STDAPI_(BOOL) SHBoolSystemParametersInfo(UINT uiAction, DWORD *pdwParam)
  4609. {
  4610. //
  4611. // Figure out the SPI parameters depending on uiAction.
  4612. //
  4613. UINT uiParam = 0;
  4614. PVOID pvParam = NULL;
  4615. ANIMATIONINFO aii;
  4616. if (uiAction & (SPIF_BOOL | SPIF_DWORD))
  4617. {
  4618. if (uiAction & SPIF_SET)
  4619. {
  4620. pvParam = IntToPtr(*pdwParam);
  4621. }
  4622. else
  4623. {
  4624. pvParam = pdwParam;
  4625. }
  4626. }
  4627. else
  4628. {
  4629. switch (uiAction)
  4630. {
  4631. case SPI_GETANIMATION:
  4632. case SPI_SETANIMATION:
  4633. aii.cbSize = uiParam = sizeof(ANIMATIONINFO);
  4634. aii.iMinAnimate = *pdwParam;
  4635. pvParam = &aii;
  4636. break;
  4637. case SPI_GETDRAGFULLWINDOWS:
  4638. case SPI_GETFONTSMOOTHING:
  4639. pvParam = pdwParam;
  4640. break;
  4641. case SPI_SETDRAGFULLWINDOWS:
  4642. case SPI_SETFONTSMOOTHING:
  4643. uiParam = *pdwParam;
  4644. break;
  4645. default:
  4646. RIPMSG(0, "SHBoolSystemParametersInfo: unknown SPI_ %x, need to add code for this case", uiAction);
  4647. return ERROR_INVALID_PARAMETER;
  4648. }
  4649. }
  4650. //
  4651. // do the SPI call
  4652. //
  4653. BOOL fRet = SystemParametersInfo(uiAction, uiParam, pvParam, SPIF_UPDATEINIFILE | SPIF_SENDCHANGE);
  4654. //
  4655. // copy return value if necessary
  4656. //
  4657. if (uiAction == SPI_GETANIMATION)
  4658. {
  4659. *pdwParam = aii.iMinAnimate;
  4660. }
  4661. return fRet;
  4662. }
  4663. //
  4664. // Determine if the images represented by the two icons are the same
  4665. // (NOTE: this does not compare ICON masks, but this should never be a distinguishing factor)
  4666. //
  4667. STDAPI_(BOOL) SHAreIconsEqual(HICON hIcon1, HICON hIcon2)
  4668. {
  4669. BOOL bRet = FALSE;
  4670. ICONINFO ii1;
  4671. if (hIcon1 && hIcon2 && GetIconInfo(hIcon1, &ii1))
  4672. {
  4673. ICONINFO ii2;
  4674. if (GetIconInfo(hIcon2, &ii2))
  4675. {
  4676. BITMAP bm1 = {0};
  4677. if (GetObject(ii1.hbmColor, sizeof(bm1), &bm1))
  4678. {
  4679. BITMAP bm2 = {0};
  4680. if (GetObject(ii2.hbmColor, sizeof(bm2), &bm2))
  4681. {
  4682. if ((bm1.bmWidth == bm2.bmWidth) && (bm1.bmHeight == bm2.bmHeight))
  4683. {
  4684. BITMAPINFO bmi = {0};
  4685. bmi.bmiHeader.biSize = sizeof(bmi.bmiHeader);
  4686. bmi.bmiHeader.biWidth = bm1.bmWidth;
  4687. bmi.bmiHeader.biHeight = bm1.bmHeight;
  4688. bmi.bmiHeader.biPlanes = 1;
  4689. bmi.bmiHeader.biBitCount = 32;
  4690. bmi.bmiHeader.biCompression = BI_RGB;
  4691. HDC hdc = GetDC(NULL);
  4692. if (hdc)
  4693. {
  4694. ULONG* pulIcon1 = new ULONG[bm1.bmWidth * bm1.bmHeight];
  4695. if (pulIcon1)
  4696. {
  4697. if (GetDIBits(hdc, ii1.hbmColor, 0, bm1.bmHeight, (LPVOID)pulIcon1, &bmi, DIB_RGB_COLORS))
  4698. {
  4699. ULONG* pulIcon2 = new ULONG[bm1.bmWidth * bm1.bmHeight];
  4700. if (pulIcon2)
  4701. {
  4702. if (GetDIBits(hdc, ii2.hbmColor, 0, bm1.bmHeight, (LPVOID)pulIcon2, &bmi, DIB_RGB_COLORS))
  4703. {
  4704. bRet = (0 == memcmp(pulIcon1, pulIcon2, bm1.bmWidth * bm1.bmHeight * sizeof(ULONG)));
  4705. }
  4706. delete[] pulIcon2;
  4707. }
  4708. }
  4709. delete[] pulIcon1;
  4710. }
  4711. ReleaseDC(NULL, hdc);
  4712. }
  4713. }
  4714. }
  4715. }
  4716. DeleteObject(ii2.hbmColor);
  4717. DeleteObject(ii2.hbmMask);
  4718. }
  4719. DeleteObject(ii1.hbmColor);
  4720. DeleteObject(ii1.hbmMask);
  4721. }
  4722. return bRet;
  4723. }
  4724. //
  4725. // CoCreateInstance that queries the app compat layer first, giving
  4726. // it a chance to load any necessary shims in anticipation of the
  4727. // bad DLL being loaded.
  4728. //
  4729. EXTERN_C DECLSPEC_IMPORT BOOL STDAPICALLTYPE
  4730. ApphelpCheckShellObject(
  4731. IN REFCLSID ObjectCLSID,
  4732. IN BOOL bShimIfNecessary,
  4733. OUT ULONGLONG* pullFlags
  4734. );
  4735. STDAPI SHCoCreateInstanceAC(REFCLSID rclsid, IUnknown *punkOuter,
  4736. DWORD dwClsCtx, REFIID riid, void **ppvOut)
  4737. {
  4738. *ppvOut = NULL;
  4739. ULONGLONG ullFlags;
  4740. // Note that on downlevel, our delayload stub will save us
  4741. if (!ApphelpCheckShellObject(rclsid, TRUE, &ullFlags))
  4742. {
  4743. // App compat says "Do not load under any circumstances!"
  4744. return HRESULT_FROM_WIN32(ERROR_ACCESS_DENIED);
  4745. }
  4746. return CoCreateInstance(rclsid, punkOuter, dwClsCtx, riid, ppvOut);
  4747. }