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

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