util.cpp
Upload User: xhy777
Upload Date: 2007-02-14
Package Size: 24088k
Code Size: 126k
Category:

Windows Kernel

Development Platform:

Visual C++

  1. //
  2. // Random stuff
  3. //
  4. //
  5. #include "priv.h"
  6. #include <shlobj.h>
  7. #include <shellp.h>
  8. #include <shdguid.h>
  9. #include "ids.h"
  10. #include <objbase.h>
  11. #include <trayp.h>
  12. #include <shdocvw.h>
  13. #include <mshtmhst.h>
  14. #include <shsemip.h>
  15. #include <winnetp.h>
  16. #include <inetreg.h>
  17. #include <shguidp.h>
  18. #include <shlguid.h>     // CLSID_ACLMRU
  19. #include <htmlhelp.h>
  20. #include <mluisupp.h>
  21. #include "apithk.h"
  22. #define REGSTR_PATH_MESSAGEBOXCHECKA "Software\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain"
  23. #define REGSTR_PATH_MESSAGEBOXCHECKW L"Software\Microsoft\Windows\CurrentVersion\Explorer\DontShowMeThisDialogAgain"
  24. #ifdef NOTYET
  25. // This code is not yet ready...
  26. #define c_szShell32     TEXT("shell32.dll")
  27. #define c_szShdocvw     TEXT("shdocvw.dll")
  28. // Table for SHGetCommonResourceID
  29. typedef struct tagCRID
  30. {
  31.     LPCTSTR pszDll;
  32.     DWORD dwRes;
  33. } CRID;
  34. const CRID g_rgcrid[] = {
  35.     { c_szShell32,   130  /* IDB_WINDOWS */ },      // SHGCR_BITMAP_WINDOWS_LOGO
  36.     { c_szShell32,   150  /* IDA_SEARCH */ },       // SHGCR_AVI_FLASHLIGHT
  37.     { c_szShell32,   151  /* IDA_FINDFILE */ },     // SHGCR_AVI_FINDFILE
  38.     { c_szShell32,   152  /* IDA_FINDCOMP */ },     // SHGCR_AVI_FINDCOMPUTER
  39.     { c_szShell32,   160  /* IDA_FILEMOVE */ },     // SHGCR_AVI_FILEMOVE
  40.     { c_szShell32,   161  /* IDA_FILECOPY */ },     // SHGCR_AVI_FILECOPY
  41.     { c_szShell32,   162  /* IDA_FILEDELETE */ },   // SHGCR_AVI_FILEDELETE
  42.     { c_szShell32,   163  /* IDA_FILENUKE */ },     // SHGCR_AVI_EMPTYWASTEBASKET
  43.     { c_szShell32,   164  /* IDA_FILEDELREAL */ },  // SHGCR_AVI_FILEREALDELETE
  44.     { c_szShdocvw, 0x100  /* IDA_DOWNLOAD */ },     // SHGCR_AVI_DOWNLOAD
  45.     };
  46. HRESULT _LoadCommonResource(LPCSTR pszDll, DWORD dwRes, HMODULE * phmod, UINT * pnID)
  47. {
  48.     HRESULT hres = E_FAIL;
  49.     HINSTANCE hinst;
  50.     // LoadLibraryEx is the preferred method of loading the library.  However,
  51.     // Win95 has some bugs with LoadLibraryEx (LoadImage fails when given a 
  52.     // handle from LoadLibraryEx).  
  53.     // bugbug:  Should fix like we did in _LoadIconFromInstanceA.
  54.     if (g_bRunningOnNT)
  55.     {
  56.         // Don't load code pages
  57.         hinst = LoadLibraryEx(pszDll, NULL, LOAD_LIBRARY_AS_DATAFILE);
  58.     }
  59.     else
  60.         hinst = LoadLibrary(pszDll);
  61.     if (hinst)
  62.     {
  63.         
  64.     }
  65.     
  66.     return hres;
  67. }
  68. #ifdef FEATURE_ANIMATIONSHARING
  69. // BryanSt: This code will be used to share animations across shell DLLs and maybe publicly.
  70. STDAPI SHGetAnimationFromGuids(LPGUID pguidSource, LPGUID pguidDestination, LPWSTR pwzID, LPDWORD pdwRes)
  71. {
  72.     HRESULT hres = E_INVALIDARG;
  73.     ASSERT(0 == HIWORD(pszID) || IS_VALID_STRING_PTR(pszID, -1));
  74.     ASSERT(IS_VALID_WRITE_PTR(phmod, HMODULE));
  75.     ASSERT(IS_VALID_WRITE_PTR(pnID, UINT));
  76.     if (phmod && pnID)
  77.     {
  78.         // Is this a predefined ID?
  79.         if (HIWORD(pszID) == 0)
  80.         {
  81.             // Yes
  82.             UINT icrid = LOWORD(pszID) - 1;
  83.             
  84.             if (icrid < ARRAYSIZE(g_rgcrid))
  85.             {
  86.                 hres = _LoadCommonResource(g_rgcrid[icrid].pszDll, g_rgcrid[icrid].dwRes, phmod, pnID);
  87.             }
  88.         }
  89.         else
  90.         {
  91.             // No; look it up in the registry
  92.             hres = E_NOTIMPL;
  93.         }
  94.     }
  95.     
  96.     return hres;
  97. }
  98. #endif // FEATURE_ANIMATIONSHARING
  99. STDAPI SHGetCommonResourceID(LPCSTR pszID, DWORD dwRes, HMODULE * phmod, UINT * pnID)
  100. {
  101.     HRESULT hres = E_INVALIDARG;
  102.     ASSERT(0 == HIWORD(pszID) || IS_VALID_STRING_PTR(pszID, -1));
  103.     ASSERT(IS_VALID_WRITE_PTR(phmod, HMODULE));
  104.     ASSERT(IS_VALID_WRITE_PTR(pnID, UINT));
  105.     if (phmod && pnID)
  106.     {
  107.         // Is this a predefined ID?
  108.         if (HIWORD(pszID) == 0)
  109.         {
  110.             // Yes
  111.             UINT icrid = LOWORD(pszID) - 1;
  112.             
  113.             if (icrid < ARRAYSIZE(g_rgcrid))
  114.             {
  115.                 hres = _LoadCommonResource(g_rgcrid[icrid].pszDll, g_rgcrid[icrid].dwRes, phmod, pnID);
  116.             }
  117.         }
  118.         else
  119.         {
  120.             // No; look it up in the registry
  121.             hres = E_NOTIMPL;
  122.         }
  123.     }
  124.     
  125.     return hres;
  126. }
  127. #endif // NOTYET
  128. //  Raw accelerator table
  129. typedef struct
  130. {
  131.     int     cEntries;
  132.     ACCEL   rgacc[0];
  133. } CA_ACCEL;
  134. STDAPI_(HANDLE) SHLoadRawAccelerators( HINSTANCE hInst, LPCTSTR lpTableName )
  135. {
  136.     HACCEL      hAcc;
  137.     CA_ACCEL*   pca = NULL;
  138.     //  Load the accelerator resource
  139.     if( (hAcc = LoadAccelerators( hInst, lpTableName )) != NULL )
  140.     {
  141.         //  Retrieve the number of entries
  142.         int  cEntries ;
  143.         if( (cEntries = CopyAcceleratorTable( hAcc, NULL, 0 )) > 0 )
  144.         {
  145.             //  Allocate a counted array and copy the elements
  146.             if( (pca = (CA_ACCEL*)LocalAlloc(LPTR, sizeof(CA_ACCEL) + cEntries * SIZEOF(ACCEL))) != NULL )
  147.             {
  148.                 pca->cEntries = cEntries;
  149.                 if( cEntries != CopyAcceleratorTable( hAcc, pca->rgacc, cEntries ) )
  150.                 {
  151.                     LocalFree( pca );
  152.                     pca = NULL;
  153.                 }
  154.             }
  155.         }
  156.         DestroyAcceleratorTable( hAcc );
  157.     }
  158.     
  159.     return pca;
  160. }
  161. STDAPI_(BOOL) SHQueryRawAccelerator( HANDLE hcaAcc, IN BYTE fVirtMask, IN BYTE fVirt, IN WPARAM wKey, OUT OPTIONAL UINT* puCmdID )
  162. {
  163.     ASSERT( hcaAcc );
  164.     CA_ACCEL* pca = (CA_ACCEL*)hcaAcc;
  165.     
  166.     if( puCmdID )
  167.         *puCmdID = 0;
  168.     for( int i = 0; i < pca->cEntries; i++ )
  169.     {
  170.         if( fVirt == (pca->rgacc[i].fVirt & fVirtMask) && wKey == pca->rgacc[i].key )
  171.         {
  172.             if( puCmdID )
  173.                 *puCmdID = pca->rgacc[i].cmd;
  174.             return TRUE;
  175.         }
  176.     }
  177.     return FALSE;
  178. }
  179. STDAPI_(BOOL) SHQueryRawAcceleratorMsg( HANDLE hcaAcc, MSG* pmsg, OUT OPTIONAL UINT* puCmdID )
  180. {
  181.     if( WM_KEYDOWN == pmsg->message || WM_KEYUP == pmsg->message )
  182.     {
  183.         #define TESTKEYSTATE(vk)   ((GetKeyState(vk) & 0x8000)!=0)
  184.         BYTE fVirt = FVIRTKEY;
  185.     
  186.         if( TESTKEYSTATE( VK_CONTROL ) )
  187.             fVirt |= FCONTROL;
  188.         else if( TESTKEYSTATE( VK_SHIFT ) )
  189.             fVirt |= FSHIFT;
  190.         else if( TESTKEYSTATE( VK_MENU ) )
  191.             fVirt |= FALT;
  192.         return SHQueryRawAccelerator( hcaAcc, fVirt, fVirt, pmsg->wParam, puCmdID );
  193.     }
  194.     return FALSE;
  195. }
  196. STDAPI SHSetThreadRef(IUnknown *punk)
  197. {
  198.     TlsSetValue(g_tlsThreadRef, punk);
  199.     return S_OK;
  200. }
  201. STDAPI SHGetThreadRef(IUnknown **ppunk)
  202. {
  203.     *ppunk = (IUnknown *)TlsGetValue(g_tlsThreadRef);
  204.     if (*ppunk)
  205.     {
  206.         (*ppunk)->AddRef();
  207.         return S_OK;
  208.     }
  209.     return E_NOINTERFACE;
  210. }
  211. // call if you want to kick off an independant thread.. you don't want handles back, or ids
  212. // and if the create fails, call synchronously
  213. typedef struct
  214. {
  215.     LPTHREAD_START_ROUTINE pfnMain;
  216.     LPTHREAD_START_ROUTINE pfnSync;
  217.     HANDLE hSync;
  218.     void *pvData;
  219.     DWORD dwFlags;
  220.     IUnknown *punkThreadRef;
  221.     IUnknown *punkProcessRef;
  222. } PRIVCREATETHREADDATA;
  223. DWORD CALLBACK WrapperThreadProc(void *pv)
  224. {
  225.     // make a copy of the input buffer, this is sitting on the calling threads stack
  226.     // once we signal him his copy will be invalid
  227.     PRIVCREATETHREADDATA rgCreate = *((PRIVCREATETHREADDATA *)pv);
  228.     HRESULT hrInit;
  229.     if (rgCreate.dwFlags & CTF_COINIT)
  230.         hrInit = SHCoInitialize();
  231.     // call the synchronous ThreadProc while the other thread is waiting on hSync
  232.     if (rgCreate.pfnSync)
  233.         rgCreate.pfnSync(rgCreate.pvData);
  234.     SetEvent(rgCreate.hSync);   // release the main thread..
  235.     // call the main thread proc
  236.     DWORD dwRes = rgCreate.pfnMain(rgCreate.pvData);
  237.     if (rgCreate.punkThreadRef)
  238.         rgCreate.punkThreadRef->Release();
  239.     if (rgCreate.punkProcessRef)
  240.         rgCreate.punkProcessRef->Release();
  241.     if (rgCreate.dwFlags & CTF_COINIT)
  242.         SHCoUninitialize(hrInit);
  243.     return dwRes;
  244. }
  245. // Call if you want to kick off an independent thread and
  246. // you don't care about the handle or thread ID.
  247. //
  248. // If the create fails, call synchronously.
  249. //
  250. // optionally call a secondary callback when the thread
  251. // is created.
  252. // returns: 
  253. //      TRUE if the thread was created
  254. STDAPI_(BOOL) SHCreateThread(
  255.     LPTHREAD_START_ROUTINE pfnThreadProc,
  256.     void *pvData,
  257.     DWORD dwFlags,                          // CTF_*
  258.     LPTHREAD_START_ROUTINE pfnCallback)     OPTIONAL
  259. {
  260.     BOOL bRet = FALSE;
  261.     PRIVCREATETHREADDATA rgCreate = {0};  // can be on the stack since we sync the thread
  262.     ASSERT(dwFlags & CTF_INSIST ? pfnCallback == NULL : TRUE);  // can't have a sync if you insist
  263.     if (CTF_THREAD_REF & dwFlags)
  264.         SHGetThreadRef(&rgCreate.punkThreadRef);
  265.     if (CTF_PROCESS_REF & dwFlags)
  266.         _SHGetInstanceExplorer(&rgCreate.punkProcessRef);
  267.     rgCreate.pfnMain = pfnThreadProc;
  268.     rgCreate.pfnSync = pfnCallback;
  269.     rgCreate.pvData = pvData;
  270.     rgCreate.dwFlags = dwFlags;
  271.     rgCreate.hSync = CreateEvent(NULL, FALSE, FALSE, NULL);
  272.     if (rgCreate.hSync)
  273.     {
  274.         DWORD idThread;
  275.         HANDLE hThread = CreateThread(NULL, 0, WrapperThreadProc, &rgCreate, 0, &idThread);
  276.         if (hThread)
  277.         {
  278.             // BUGBUG: should this be infinite, or should it be say 20 seconds ?
  279.             WaitForSingleObject(rgCreate.hSync, INFINITE);
  280.             CloseHandle(hThread);
  281.             bRet = TRUE;
  282.         }
  283.         CloseHandle(rgCreate.hSync);
  284.     }
  285.     if (!bRet)
  286.     {
  287.         if (rgCreate.punkThreadRef)
  288.             rgCreate.punkThreadRef->Release();
  289.         if (rgCreate.punkProcessRef)
  290.             rgCreate.punkProcessRef->Release();
  291.         if (dwFlags & CTF_INSIST)
  292.         {
  293.             // failed to create another thread... call synchronously
  294.             ASSERT(pfnCallback == NULL);  // can't have a sync if you insist
  295.             pfnThreadProc(pvData);
  296.             bRet = TRUE;        // what should the return be here?
  297.         }
  298.     }
  299.     return bRet;
  300. }
  301. STDAPI_(BOOL) SHIsLowMemoryMachine(DWORD dwType)
  302. // Are we an 8 meg Win95 machine or 16 meg NT machine.
  303. // Back in the old days...
  304. {
  305. #ifdef UNIX
  306.     return 0; //due to GlobalMemoryStatus() always return 0 physical mem size.
  307. #else
  308.     static int fLowMem = -1;
  309.     if (ILMM_IE4 == dwType && fLowMem == -1)
  310.     {
  311.         MEMORYSTATUS ms;
  312.         GlobalMemoryStatus(&ms);
  313.         if (g_bRunningOnNT)
  314.             fLowMem = (ms.dwTotalPhys <= 16*1024*1024);
  315.         else
  316.             fLowMem = (ms.dwTotalPhys <= 8*1024*1024);
  317.     }
  318.     return fLowMem;
  319. #endif
  320. }
  321. #if 0
  322. // MultiByteToWideChar doesn't truncate if the buffer is too small.
  323. // these utils do.
  324. // returns:
  325. //      # of chars converted (WIDE chars) into out buffer (pwstr)
  326. int _AnsiToUnicode(UINT uiCP, LPCSTR pstr, LPWSTR pwstr, int cch)
  327. {
  328.     int cchDst = 0;
  329.     ASSERT(IS_VALID_STRING_PTRA(pstr, -1));
  330.     ASSERT(NULL == pwstr || IS_VALID_WRITE_BUFFER(pwstr, WCHAR, cch));
  331.     if (cch && pwstr)
  332.         pwstr[0] = 0;
  333. #if 0
  334.     switch (uiCP)
  335.     {
  336.         case 1200:                      // UCS-2 (Unicode)
  337.             uiCP = 65001;
  338.             // fall through;
  339.         case 65000:                     // UTF-7
  340.         case 65001:                     // UTF-8
  341.         {
  342.             INT cchSrc, cchSrcOriginal;
  343.             cchSrc = cchSrcOriginal = lstrlenA(pstr) + 1;
  344.             cchDst = cch;
  345.             if (SUCCEEDED(ConvertINetMultiByteToUnicode(NULL, uiCP, pstr,
  346.                 &cchSrc, pwstr, &cchDst)) &&
  347.                 cchSrc < cchSrcOriginal)
  348.             {
  349.                 LPWSTR pwsz = (LPWSTR)LocalAlloc(LPTR, cchDst * SIZEOF(WCHAR));
  350.                 if (pwsz)
  351.                 {
  352.                     if (SUCCEEDED(ConvertINetMultiByteToUnicode( NULL, uiCP, pstr,
  353.                         &cchSrcOriginal, pwsz, &cchDst )))
  354.                     {
  355.                         StrCpyNW( pwstr, pwsz, cch );
  356.                         cchDst = cch;
  357.                     }
  358.                     LocalFree(pwsz);
  359.                 }
  360.             }
  361.             break;
  362.         }
  363.         default:
  364. #endif
  365.             cchDst = MultiByteToWideChar(uiCP, 0, pstr, -1, pwstr, cch);
  366.             if (!cchDst) {
  367.                 // failed.
  368.                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  369.                     int cchNeeded = MultiByteToWideChar(uiCP, 0, pstr, -1, NULL, 0);
  370.                     if (cchNeeded) {
  371.                         LPWSTR pwsz = (LPWSTR)LocalAlloc(LPTR, cchNeeded * SIZEOF(WCHAR));
  372.                         if (pwsz) {
  373.                             cchDst = MultiByteToWideChar(uiCP, 0, pstr, -1, pwsz, cchNeeded);
  374.                             if (cchDst) {
  375.                                 StrCpyNW(pwstr, pwsz, cch);
  376.                                 cchDst = cch;
  377.                             }
  378.                             LocalFree(pwsz);
  379.                         }
  380.                     }
  381.                 }
  382.             }
  383. #if 0
  384.             break;
  385.     }
  386. #endif
  387.     return cchDst;
  388. }
  389. // returns:
  390. //      # of BYTES written to output buffer (pstr)
  391. int _UnicodeToAnsi(UINT uiCP, LPCWSTR pwstr, LPSTR pstr, int cch)
  392. {
  393.     int cchDst = 0;
  394.     ASSERT(IS_VALID_STRING_PTRW(pwstr, -1));
  395.     ASSERT(NULL == pstr || IS_VALID_WRITE_BUFFER(pstr, char, cch));
  396.     if (pstr && cch)
  397.         pstr[0] = 0;
  398. #if 0
  399.     switch (uiCP)
  400.     {
  401.         case 1200:                      // UCS-2 (Unicode)
  402.             uiCP = 65001;
  403.             // fall through
  404.         case 65000:                     // UTF-7
  405.         case 65001:                     // UTF-8
  406.         {
  407.             INT cchSrc, cchSrcOriginal;
  408.             cchSrc = cchSrcOriginal = lstrlenW(pwstr) + 1;
  409.             cchDst = cch;
  410.             if (SUCCEEDED(ConvertINetUnicodeToMultiByte(NULL, uiCP, pwstr,
  411.                 &cchSrc, pstr, &cchDst)) &&
  412.                 cchSrc < cchSrcOriginal)
  413.             {
  414.                 LPSTR psz = (LPSTR)LocalAlloc(LPTR, cchDst * SIZEOF(CHAR));
  415.                 if (psz)
  416.                 {
  417.                     if (SUCCEEDED(ConvertINetUnicodeToMultiByte(NULL, uiCP, pwstr,
  418.                         &cchSrcOriginal, psz, &cchDst)))
  419.                     {
  420.                         // lstrcpyn puts NULL at pstr[cch-1]
  421.                         // without considering if it'd cut in dbcs
  422.                         TruncateString(psz, cch);
  423.                         lstrcpynA(pstr, psz, cch);
  424.                         cchDst = cch;
  425.                     }
  426.                     LocalFree(psz);
  427.                 }
  428.             }
  429.             break;
  430.         }
  431.         default:
  432. #endif
  433.             cchDst = WideCharToMultiByte(uiCP, 0, pwstr, -1, pstr, cch, NULL, NULL);
  434.             if (!cchDst) {
  435.                 // failed.
  436.                 if (GetLastError() == ERROR_INSUFFICIENT_BUFFER) {
  437.                     int cchNeeded = WideCharToMultiByte(uiCP, 0, pwstr, -1, NULL, 0, NULL, NULL);
  438.                     if (cchNeeded > 0) {
  439.                         LPSTR psz = (LPSTR)LocalAlloc(LPTR, cchNeeded * SIZEOF(CHAR));
  440.                         if (psz) {
  441.                             cchDst = WideCharToMultiByte(uiCP, 0, pwstr, -1, psz, cchNeeded, NULL, NULL);
  442.                             if (cchDst) {
  443.                                 // lstrcpyn puts NULL at pstr[cch-1]
  444.                                 // without considering if it'd cut in dbcs
  445.                                 SHTruncateString(psz, cch);
  446.                                 lstrcpynA(pstr, psz, cch);
  447.                                 cchDst = cch;
  448.                             }
  449.                             LocalFree(psz);
  450.                         }
  451.                     }
  452.                 }
  453.             }
  454. #if 0
  455.             break;
  456.     }
  457. #endif
  458.     return cchDst;
  459. }
  460. #endif
  461. // SHTruncateString
  462. //
  463. // purpose: cut a string at the given length in dbcs safe manner.
  464. //          the string may be truncated at cch-2 if the sz[cch] points
  465. //          to a lead byte that would result in cutting in the middle
  466. //          of double byte character.
  467. //
  468. // The character at sz[cchBufferSize-1] is not consulted, so you
  469. // can call this after lstrcpyn (which forces sz[cchBufferSize-1]=0).
  470. //
  471. // If the source string is shorter than cchBufferSize-1 characters,
  472. // we fiddle some bytes that have no effect, in which case the return
  473. // value is random.
  474. //
  475. // update: made it faster for sbcs environment (5/26/97)
  476. //         now returns adjusted cch            (6/20/97)
  477. //
  478. STDAPI_(int) SHTruncateString(CHAR *sz, int cchBufferSize)
  479. {
  480.     if (!sz || cchBufferSize <= 0) return 0;
  481.     int cch = cchBufferSize - 1; // get index position to NULL out
  482.     LPSTR psz = &sz[cch];
  483.     while (psz >sz)
  484.     {
  485.         psz--;
  486.         if (!IsDBCSLeadByte(*psz))
  487.         {
  488.             // Found non-leadbyte for the first time.
  489.             // This is either a trail byte of double byte char
  490.             // or a single byte character we've first seen.
  491.             // Thus, the next pointer must be at either of a leadbyte
  492.             // or &sz[cch]
  493.             psz++;
  494.             break;
  495.         }
  496.     }
  497.     if (((&sz[cch] - psz) & 1) && cch > 0)
  498.     {
  499.         // we're truncating the string in the middle of dbcs
  500.         cch--;
  501.     }
  502.     sz[cch] = '';
  503.     return cch;
  504. }
  505. //
  506. //  Why do we use the unsafe version?
  507. //
  508. //  -   Unsafe is much faster.
  509. //
  510. //  -   The safe version isn't safe after all and serves only to mask
  511. //      existing bugs.  The situation the safe version "saves" is if
  512. //      two threads both try to atomicrelease the same object.  This
  513. //      means that at the same moment, both threads think the object
  514. //      is alive.  Change the timing slightly, and now one thread
  515. //      atomicreleases the object before the other one, so the other
  516. //      thread is now using an object after the first thread already
  517. //      atomicreleased it.  Bug.
  518. //
  519. STDAPI_(void) IUnknown_AtomicRelease(void **ppunk)
  520. {
  521. #if 1 // Unsafe
  522.     if (ppunk && *ppunk) {
  523.         IUnknown* punk = *(IUnknown**)ppunk;
  524.         *ppunk = NULL;
  525.         punk->Release();
  526.     }
  527. #else // Safe
  528.     if (ppunk) {
  529.         IUnknown* punk = (IUnknown *)InterlockedExchangePointer(ppunk, NULL);
  530.         if (punk) {
  531.             punk->Release();
  532.         }
  533.     }
  534. #endif
  535. }
  536. STDAPI ConnectToConnectionPoint(IUnknown* punkThis, REFIID riidEvent, BOOL fConnect, IUnknown* punkTarget, DWORD* pdwCookie, IConnectionPoint** ppcpOut)
  537. {
  538.     // We always need punkTarget, we only need punkThis on connect
  539.     if (!punkTarget || (fConnect && !punkThis))
  540.     {
  541.         return E_FAIL;
  542.     }
  543.     if (ppcpOut)
  544.         *ppcpOut = NULL;
  545.     HRESULT hr;
  546.     IConnectionPointContainer *pcpContainer;
  547.     // Let's now have the Browser Window give us notification when something happens.
  548.     if (SUCCEEDED(hr = punkTarget->QueryInterface(IID_IConnectionPointContainer, (void **)&pcpContainer)))
  549.     {
  550.         IConnectionPoint *pcp;
  551.         if(SUCCEEDED(hr = pcpContainer->FindConnectionPoint(riidEvent, &pcp)))
  552.         {
  553.             if(fConnect)
  554.             {
  555.                 // Add us to the list of people interested...
  556.                 hr = pcp->Advise(punkThis, pdwCookie);
  557.                 if (FAILED(hr))
  558.                     *pdwCookie = 0;
  559.             }
  560.             else
  561.             {
  562.                 // Remove us from the list of people interested...
  563.                 hr = pcp->Unadvise(*pdwCookie);
  564.                 *pdwCookie = 0;
  565.             }
  566.             if (ppcpOut && SUCCEEDED(hr))
  567.                 *ppcpOut = pcp;
  568.             else
  569.                 pcp->Release();
  570.         }
  571.         pcpContainer->Release();
  572.     }
  573.     return hr;
  574. }
  575. STDAPI IUnknown_QueryStatus(IUnknown *punk, const GUID *pguidCmdGroup,
  576.     ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  577. {
  578.     HRESULT hres = E_FAIL;
  579.     if (punk) {
  580.         IOleCommandTarget* pct;
  581.         hres = punk->QueryInterface(IID_IOleCommandTarget, (void **)&pct);
  582.         if (pct) {
  583.             hres = pct->QueryStatus(pguidCmdGroup, cCmds, rgCmds, pcmdtext);
  584.             pct->Release();
  585.         }
  586.     }
  587.     return hres;
  588. }
  589. STDAPI IUnknown_Exec(IUnknown* punk, const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  590. {
  591.     HRESULT hr = E_FAIL;
  592.     if (punk) 
  593.     {
  594.         IOleCommandTarget* pct;
  595.         hr = punk->QueryInterface(IID_PPV_ARG(IOleCommandTarget, &pct));
  596.         if (SUCCEEDED(hr)) 
  597.         {
  598.             hr = pct->Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvarargIn, pvarargOut);
  599.             pct->Release();
  600.         }
  601.     }
  602.     return hr;
  603. }
  604. STDAPI_(void) SHSetWindowBits(HWND hWnd, int iWhich, DWORD dwBits, DWORD dwValue)
  605. {
  606.     DWORD dwStyle;
  607.     DWORD dwNewStyle;
  608.     dwStyle = GetWindowLong(hWnd, iWhich);
  609.     dwNewStyle = ( dwStyle & ~dwBits ) | (dwValue & dwBits);
  610.     if (dwStyle != dwNewStyle) {
  611.         SetWindowLong(hWnd, iWhich, dwNewStyle);
  612.     }
  613. }
  614. // OpenRegStream API in SHELL32 stupidly returns an emtpy
  615. // stream if we ask for read-only, if the entry does not exist.
  616. // we need to detect that case.
  617. //
  618. const LARGE_INTEGER c_li0 = { 0, 0 };
  619. STDAPI_(BOOL) SHIsEmptyStream(IStream* pstm)
  620. {
  621. #ifdef DEBUG
  622.     // We always call this function when we open a new stream,
  623.     // so we should always be at the beginning of the stream.
  624.     //
  625.     // We need this assert for the <NT5 shell case.
  626.     //
  627.     ULARGE_INTEGER liStart;
  628.     pstm->Seek(c_li0, STREAM_SEEK_CUR, &liStart);
  629.     ASSERT(0==liStart.HighPart && 0==liStart.LowPart);
  630. #endif
  631.     STATSTG st;
  632.     if (SUCCEEDED(pstm->Stat(&st, STATFLAG_NONAME)))
  633.     {
  634.         if (st.cbSize.LowPart || st.cbSize.HighPart)
  635.             return FALSE;
  636.     }
  637.     else
  638.     {
  639.         // Win95 IStream code did not implement stat, so check
  640.         // emptiness by trying to read.
  641.         //
  642.         int iTmp;
  643.         if (SUCCEEDED(IStream_Read(pstm, &iTmp, SIZEOF(iTmp))))
  644.         {
  645.             // The stream is indeed present, seek back to start
  646.             //
  647.             pstm->Seek(c_li0, STREAM_SEEK_SET, NULL);
  648.             return FALSE; // not empty
  649.         }
  650.     }
  651.     return TRUE;
  652. }
  653. STDAPI_(void) SHSetParentHwnd(HWND hwnd, HWND hwndParent)
  654. {
  655.     HWND hwndOldParent = GetParent(hwnd);
  656.     if (hwndParent != hwndOldParent)
  657.     {
  658.         //
  659.         // Get the child flag correct!  If we don't do this and
  660.         // somebody calls DialogBox on us while we are parented to NULL
  661.         // and WS_CHILD, the desktop will be disabled, thereby causing
  662.         // all mouse hit-testing to fail systemwide.
  663.         // we also want to do this in the right order so the window
  664.         // manager does the correct attachthreadinput if required...
  665.         //
  666.         if (hwndParent)
  667.             SHSetWindowBits(hwnd, GWL_STYLE, WS_CHILD | WS_POPUP, WS_CHILD);
  668.         SetParent(hwnd, hwndParent);
  669.         if (!hwndParent)
  670.             SHSetWindowBits(hwnd, GWL_STYLE, WS_CHILD | WS_POPUP, WS_POPUP);
  671.         //
  672.         // (jbeda) USER32 doesn't mirror the UIS bits correctly when windows
  673.         //         are reparented.  They (mcostea) say that it would cause
  674.         //         compat problems.  So to work around this, when
  675.         //         we reparent, we grab the bits on the parent window
  676.         //         and mirror them to the child.
  677.         //
  678.         if (g_bRunningOnNT5OrHigher)
  679.         {
  680.             LRESULT lUIState;
  681.             lUIState = SendMessage(hwndParent, WM_QUERYUISTATE, 0, 0);
  682.             if (lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL))
  683.             {
  684.                 SendMessage( hwnd, WM_UPDATEUISTATE,
  685.                              MAKEWPARAM(UIS_SET, 
  686.                                lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL)), 0 );
  687.             }
  688.             if (~lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL))
  689.             {
  690.                 SendMessage( hwnd, WM_UPDATEUISTATE,
  691.                              MAKEWPARAM(UIS_CLEAR, 
  692.                                ~lUIState & (UISF_HIDEFOCUS | UISF_HIDEACCEL)), 0 );
  693.             }
  694.         }
  695.     }
  696. }
  697. // IsSameObject checks for OLE object identity.
  698. //
  699. STDAPI_(BOOL) SHIsSameObject(IUnknown* punk1, IUnknown* punk2)
  700. {
  701.     if (!punk1 || !punk2)
  702.     {
  703.         return FALSE;
  704.     }
  705.     else if (punk1 == punk2)
  706.     {
  707.         // Quick shortcut -- if they're the same pointer
  708.         // already then they must be the same object
  709.         //
  710.         return TRUE;
  711.     }
  712.     else
  713.     {
  714.         IUnknown* punkI1;
  715.         IUnknown* punkI2;
  716.         HRESULT hres;
  717.         // Some apps don't implement QueryInterface! (SecureFile)
  718.         if (SUCCEEDED(hres = punk1->QueryInterface(IID_IUnknown, (void **)&punkI1)))
  719.         {
  720.             punkI1->Release();
  721.             if (SUCCEEDED(hres = punk2->QueryInterface(IID_IUnknown, (void **)&punkI2)))
  722.                 punkI2->Release();
  723.         }
  724.         if (FAILED(hres))
  725.             return FALSE;
  726.         return (punkI1 == punkI2);
  727.     }
  728. }
  729. // pass the CLSID of the object you are about to bind to. this queries 
  730. // the bind context to see if that guy should be avoided 
  731. // this would be a good shlwapi service. 
  732. STDAPI_(BOOL) SHSkipJunction(IBindCtx *pbc, const CLSID *pclsid) 
  733.     IUnknown *punk; 
  734.     if (pbc && SUCCEEDED(pbc->GetObjectParam(STR_SKIP_BINDING_CLSID, &punk))) 
  735.     { 
  736.         CLSID clsid; 
  737.         BOOL bSkip = SUCCEEDED(IUnknown_GetClassID(punk, &clsid)) && IsEqualCLSID(clsid, *pclsid); 
  738.         punk->Release(); 
  739.         return bSkip; 
  740.     } 
  741.     return FALSE; 
  742. STDAPI IUnknown_GetWindow(IUnknown* punk, HWND* phwnd)
  743. {
  744.     HRESULT hres = E_FAIL;
  745.     *phwnd = NULL;
  746.     if (punk) 
  747.     {
  748.         IOleWindow* pow;
  749.         IInternetSecurityMgrSite* pisms;
  750.         IShellView* psv;
  751.         // How many ways are there to get a window?  Let me count the ways...
  752.         if (SUCCEEDED(hres = punk->QueryInterface(IID_IOleWindow, (void **)&pow)))
  753.         {
  754.             hres = pow->GetWindow(phwnd);
  755.             pow->Release();
  756.         }
  757.         else if (SUCCEEDED(hres = punk->QueryInterface(IID_IInternetSecurityMgrSite, (void **)&pisms)))
  758.         {
  759.             hres = pisms->GetWindow(phwnd);
  760.             pisms->Release();
  761.         }
  762.         else if (SUCCEEDED(hres = punk->QueryInterface(IID_IShellView, (void **)&psv)))
  763.         {
  764.             hres = psv->GetWindow(phwnd);
  765.             psv->Release();
  766.         }
  767.     }
  768.     return hres;
  769. }
  770. /*****************************************************************************
  771.     FUNCTION:   IUnknown_EnableModless
  772.     DESCRIPTION:
  773.         Several interfaces implement the ::EnableModeless() or equivalent methods.
  774.     This requires us to use a utility function to query the punk until one is
  775.     implemented and then use it.
  776. *****************************************************************************/
  777. HRESULT IUnknown_EnableModless(IUnknown * punk, BOOL fEnabled)
  778. {
  779.     HRESULT hr = E_FAIL;
  780.     if (punk)
  781.     {
  782.         IOleInPlaceActiveObject * poipao;
  783.         IInternetSecurityMgrSite * pisms;
  784.         IOleInPlaceFrame * poipf;
  785.         IShellBrowser * psb;
  786.         IDocHostUIHandler * pdhuh;
  787.         // How many ways are there to enable modless?  Let me count the ways...
  788.         if (SUCCEEDED(hr = punk->QueryInterface(IID_IOleInPlaceActiveObject, (void **)&poipao)))
  789.         {
  790.             hr = poipao->EnableModeless(fEnabled);
  791.             poipao->Release();
  792.         }
  793.         else if (SUCCEEDED(hr = punk->QueryInterface(IID_IInternetSecurityMgrSite, (void **)&pisms)))
  794.         {
  795.             hr = pisms->EnableModeless(fEnabled);
  796.             pisms->Release();
  797.         }
  798.         else if (SUCCEEDED(hr = punk->QueryInterface(IID_IOleInPlaceFrame, (void **)&poipf)))
  799.         {
  800.             hr = poipf->EnableModeless(fEnabled);
  801.             poipf->Release();
  802.         }
  803.         else if (SUCCEEDED(hr = punk->QueryInterface(IID_IShellBrowser, (void **)&psb)))
  804.         {
  805.             hr = psb->EnableModelessSB(fEnabled);
  806.             psb->Release();
  807.         }
  808.         else if (SUCCEEDED(hr = punk->QueryInterface(IID_IDocHostUIHandler, (void **)&pdhuh)))
  809.         {
  810.             hr = pdhuh->EnableModeless(fEnabled);
  811.             pdhuh->Release();
  812.         }
  813.     }
  814.     return hr;
  815. }
  816. STDAPI IUnknown_SetOwner(IUnknown* punk, IUnknown* punkOwner)
  817. {
  818.     HRESULT hres = S_OK;
  819.     if (punk) {
  820.         IShellService* pss;
  821.         // need to check for succeeded here instead of pss because
  822.         // some old win95 objects (like rnaui.dll) don't null out the in param
  823.         if (SUCCEEDED(punk->QueryInterface(IID_IShellService, (void **)&pss))) {
  824.             pss->SetOwner(punkOwner);
  825.             pss->Release();
  826.         }
  827.     }
  828.     return hres;
  829. }
  830. STDAPI IUnknown_SetSite(IUnknown *punk, IUnknown *punkSite)
  831. {
  832.     HRESULT hr = E_FAIL;
  833.     if (punk)
  834.     {
  835.         IObjectWithSite *pows;
  836.         hr = punk->QueryInterface(IID_IObjectWithSite, (void**)&pows);
  837.         if (SUCCEEDED(hr))
  838.         {
  839.             hr = pows->SetSite(punkSite);
  840.             ASSERT(SUCCEEDED(hr));
  841.             pows->Release();
  842.         }
  843.         else
  844.         {
  845.             IInternetSecurityManager * pism;
  846.             // The security guys should have used IObjectWithSite, but no....
  847.             hr = punk->QueryInterface(IID_IInternetSecurityManager, (void**)&pism);
  848.             if (SUCCEEDED(hr))
  849.             {
  850.                 hr = pism->SetSecuritySite((IInternetSecurityMgrSite *) punkSite);
  851.                 ASSERT(SUCCEEDED(hr));
  852.                 pism->Release();
  853.             }
  854.         }
  855.     }
  856.     return hr;
  857. }
  858. STDAPI IUnknown_GetSite(IUnknown *punk, REFIID riid, void **ppv)
  859. {
  860.     HRESULT hr = E_FAIL;
  861.     *ppv = NULL;
  862.     if (punk) {
  863.         IObjectWithSite *pows;
  864.         hr = punk->QueryInterface(IID_IObjectWithSite, (void**)&pows);
  865.         ASSERT(SUCCEEDED(hr) || pows == NULL);  // paranoia
  866.         if (SUCCEEDED(hr)) {
  867.             hr = pows->GetSite(riid, ppv);
  868.             ASSERT(SUCCEEDED(hr));
  869.             pows->Release();
  870.         }
  871.     }
  872.     return hr;
  873. }
  874. //
  875. //      GetUIVersion()
  876. //
  877. //  returns the version of shell32
  878. //  3 == win95 gold / NT4
  879. //  4 == IE4 Integ / win98
  880. //  5 == win2k
  881. //
  882. STDAPI_(UINT) GetUIVersion()
  883. {
  884.     static UINT s_uiShell32 = 0;
  885.     if (s_uiShell32 == 0)
  886.     {
  887.         HINSTANCE hinst = GetModuleHandle(TEXT("SHELL32.DLL"));
  888.         if (hinst)
  889.         {
  890.             DLLGETVERSIONPROC pfnGetVersion = (DLLGETVERSIONPROC)GetProcAddress(hinst, "DllGetVersion");
  891.             DLLVERSIONINFO dllinfo;
  892.             dllinfo.cbSize = sizeof(DLLVERSIONINFO);
  893.             if (pfnGetVersion && pfnGetVersion(&dllinfo) == NOERROR)
  894.                 s_uiShell32 = dllinfo.dwMajorVersion;
  895.             else
  896.                 s_uiShell32 = 3;
  897.         }
  898.     }
  899.     return s_uiShell32;
  900. }
  901. //***   IUnknown_GetClassID -- do punk->IPS::GetClassID
  902. STDAPI IUnknown_GetClassID(IUnknown *punk, CLSID *pclsid)
  903. {
  904.     HRESULT hres = E_FAIL;
  905.     ASSERT(punk);   // currently nobody does
  906.     if (punk)
  907.     {
  908.         IPersist *p;
  909.         hres = punk->QueryInterface(IID_PPV_ARG(IPersist, &p));
  910.         //  sometimes we can do this since they dont answer the
  911.         //  the QI for IPersist.  but we cant do it on NT4 plain
  912.         //  since the net hood faults if the psf is just a \server
  913.         if (FAILED(hres) && (GetUIVersion() > 3))
  914.         {
  915.             //
  916.             //  BUGBUGLEGACY - we have some issues here on downlevel implementations of IPersistFolder
  917.             //  so in order to protect ourselves from bad implementations we will wrap this in a simple
  918.             //  exception handler.
  919.             //
  920.             __try
  921.             {
  922.                 IPersistFolder *pf;
  923.                 hres = punk->QueryInterface(IID_PPV_ARG(IPersistFolder, &pf));
  924.                 p = pf;
  925.             }
  926.             __except (EXCEPTION_EXECUTE_HANDLER)
  927.             {
  928.                 hres = E_NOTIMPL;
  929.             }
  930.         }
  931.         if (SUCCEEDED(hres))
  932.         {
  933.             hres = p->GetClassID(pclsid);
  934.             p->Release();
  935.         }
  936.     }
  937.     
  938.     return hres;
  939. }
  940. STDAPI IUnknown_QueryService(IUnknown* punk, REFGUID guidService, REFIID riid, void **ppvOut)
  941. {
  942.     HRESULT hres;
  943.     IServiceProvider *psp = NULL;   // Protect against QS dorks.
  944. #ifdef DEBUG
  945.     psp = (IServiceProvider *) 0xFFFFFFFF;    // Make punk->QueryInterface() NULL this out
  946. #endif // DEBUG
  947.     *ppvOut = NULL;
  948.     if (!punk)
  949.         return E_FAIL;
  950.     hres = punk->QueryInterface(IID_IServiceProvider, (void **)&psp);
  951.     if (SUCCEEDED(hres) && EVAL(psp))
  952.     {
  953.         hres = psp->QueryService(guidService, riid, ppvOut);
  954.         psp->Release();
  955.     }
  956.     else
  957.     {
  958.         // If you hit this, punk didn't NULL psp in ::QueryInterface().
  959.         ASSERT(!psp);
  960.     }
  961.     return hres;
  962. }
  963. #if defined(DEBUG) && 0 // defined(NOTYET)
  964. //
  965. // IUnknown_IsCanonical checks if the interface is the canonical IUnknown
  966. // for the object.
  967. //
  968. //  S_OK    = yes it is
  969. //  S_FALSE = no it isn't
  970. //  error   = IUnknown implementation is buggy.
  971. //
  972. //  If you get an error back, it means that the IUnknown is incorrectly
  973. //  implemented, and you probably should avoid doing anything with it.
  974. //
  975. STDAPI_(HRESULT) IUnknown_IsCanonical(IUnknown *punk)
  976. {
  977.     IUnknown *punkT;
  978.     HRESULT hres = punk->QueryInterface(IID_IUnknown, (void **)&punkT);
  979.     if (EVAL(SUCCEEDED(hres))) {
  980.         punkT->Release();
  981.         if (punk == punkT) {
  982.             hres = S_OK;
  983.         } else {
  984.             hres = S_FALSE;
  985.         }
  986.     }
  987.     return hres;
  988. }
  989. #endif
  990. //
  991. //  QueryInterface that doesn't affect the refcount.  Use this when doing
  992. //  funny aggregation games.
  993. //
  994. //  In order for this QI/Release trick to work, the punkOuter must be the
  995. //  canonical IUnknown for the outer object.  It is the caller's
  996. //  responsibility to ensure this.
  997. //
  998. //  punkOuter  - The controlling unknown (must be canonical)
  999. //  punkTarget - The thing that receives the QI (must be controlled
  1000. //               by punkOuter)
  1001. //  riid       - The interface to get
  1002. //  ppvOut     - Where to put the result
  1003. //
  1004. //  On success, the interface is obtained from the punkTarget, and the
  1005. //  refcount generated by the QI is removed from the punkOuter.
  1006. //
  1007. //  If either punkOuter or punkTarget is NULL, we vacuously fail with
  1008. //  E_NOINTERFACE.
  1009. //
  1010. //  When querying from an outer to an inner, punkOuter is the outer, and
  1011. //  punkTarget is the inner.
  1012. //
  1013. //  When querying from an inner to an outer, punkOuter and punkTarget are
  1014. //  both the outer.
  1015. //
  1016. STDAPI 
  1017. SHWeakQueryInterface(IUnknown *punkOuter, IUnknown *punkTarget, REFIID riid, void **ppvOut)
  1018. {
  1019.     HRESULT hres;
  1020.     if (punkOuter && punkTarget) {
  1021. #if defined(DEBUG) && 0 // defined(NOTYET)
  1022.         // RaymondC hasn't yet fixed all our aggregatable classes, so this
  1023.         // assertion fires too often
  1024.         ASSERT(IUnknown_IsCanonical(punkOuter));
  1025. #endif
  1026.         hres = punkTarget->QueryInterface(riid, ppvOut);
  1027.         if (SUCCEEDED(hres)) {
  1028.             punkOuter->Release();
  1029.             hres = S_OK;
  1030.         } else {
  1031.             // Double-check that QI isn't buggy.
  1032.             ASSERT(*ppvOut == NULL);
  1033.         }
  1034.     } else {
  1035.         hres = E_NOINTERFACE;
  1036.     }
  1037.     if (FAILED(hres)) {
  1038.         *ppvOut = NULL;
  1039.     }
  1040.     return hres;
  1041. }
  1042. //
  1043. //  Release an interface that was obtained via SHWeakQueryInterface.
  1044. //
  1045. //  punkOuter - The controlling unknown
  1046. //  ppunk     - The IUnknown to release
  1047. //
  1048. STDAPI_(void)
  1049. SHWeakReleaseInterface(IUnknown *punkOuter, IUnknown **ppunk)
  1050. {
  1051.     if (*ppunk) {
  1052.         ASSERT(IS_VALID_CODE_PTR(punkOuter, IUnknown));
  1053.         punkOuter->AddRef();
  1054.         IUnknown_AtomicRelease((void **)ppunk);
  1055.     }
  1056. }
  1057. HRESULT IUnknown_SetOptions(IUnknown * punk, DWORD dwACLOptions)
  1058. {
  1059.     HRESULT hr = S_OK;
  1060.     IACList2 * pal2;
  1061.     hr = punk->QueryInterface(IID_IACList2, (void **)&pal2);
  1062.     if (SUCCEEDED(hr))
  1063.     {
  1064.         hr = pal2->SetOptions(dwACLOptions);
  1065.         pal2->Release();
  1066.     }
  1067.     return hr;
  1068. }
  1069. IUnknown * ACGetLists(DWORD dwFlags, DWORD dwACLOptions)
  1070. {
  1071.     IUnknown * punkACLMulti = NULL;
  1072.     HRESULT hr = CoCreateInstance(CLSID_ACLMulti, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&punkACLMulti);
  1073.     AssertMsg((CO_E_NOTINITIALIZED != hr), TEXT("SHAutoComplete() can not use AutoComplete because OleInitialize() was never called."));
  1074.     if (SUCCEEDED(hr))
  1075.     {
  1076.         IObjMgr * pomMulti;
  1077.         hr = punkACLMulti->QueryInterface(IID_IObjMgr, (void **)&pomMulti);
  1078.         if (SUCCEEDED(hr))
  1079.         {
  1080.             if (dwFlags & SHACF_URLMRU)
  1081.             {
  1082.                 // ADD The MRU List
  1083.                 IUnknown * punkACLMRU;
  1084.                 hr = CoCreateInstance(CLSID_ACLMRU, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&punkACLMRU);
  1085.                 if (SUCCEEDED(hr))
  1086.                 {
  1087.                     pomMulti->Append(punkACLMRU);
  1088.                     IUnknown_SetOptions(punkACLMRU, dwACLOptions);
  1089.                     punkACLMRU->Release();
  1090.                 }
  1091.             }
  1092.             if (dwFlags & SHACF_URLHISTORY)
  1093.             {
  1094.                 // ADD The History List
  1095.                 IUnknown * punkACLHist;
  1096.                 hr = CoCreateInstance(CLSID_ACLHistory, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&punkACLHist);
  1097.                 if (SUCCEEDED(hr))
  1098.                 {
  1099.                     pomMulti->Append(punkACLHist);
  1100.                     IUnknown_SetOptions(punkACLHist, dwACLOptions);
  1101.                     punkACLHist->Release();
  1102.                 }
  1103.             }
  1104.             if (dwFlags & SHACF_FILESYSTEM)
  1105.             {
  1106.                 // ADD The ISF List
  1107.                 IUnknown * punkACLISF;
  1108.                 hr = CoCreateInstance(CLSID_ACListISF, NULL, CLSCTX_INPROC_SERVER, IID_IUnknown, (void **)&punkACLISF);
  1109.                 if (SUCCEEDED(hr))
  1110.                 {
  1111.                     pomMulti->Append(punkACLISF);
  1112.                     IUnknown_SetOptions(punkACLISF, dwACLOptions);
  1113.                     punkACLISF->Release();
  1114.                 }
  1115.             }
  1116.             pomMulti->Release();
  1117.         }
  1118.     }
  1119.     return punkACLMulti;
  1120. }
  1121. #define SZ_REGKEY_AUTOCOMPLETE_TAB          TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\AutoComplete")
  1122. #define SZ_REGVALUE_AUTOCOMPLETE_TAB        TEXT("Always Use Tab")
  1123. #define BOOL_NOT_SET                        0x00000005
  1124. DWORD _UpdateAutoCompleteFlags(DWORD dwFlags, DWORD * pdwACLOptions)
  1125. {
  1126.     DWORD dwACOptions = 0;
  1127.     *pdwACLOptions = 0;
  1128.     if (!(SHACF_AUTOAPPEND_FORCE_OFF & dwFlags) &&
  1129.         ((SHACF_AUTOAPPEND_FORCE_ON & dwFlags) ||
  1130.         SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOAPPEND, FALSE, /*default:*/FALSE)))
  1131.     {
  1132.         dwACOptions |= ACO_AUTOAPPEND;
  1133.     }
  1134.     if (!(SHACF_AUTOSUGGEST_FORCE_OFF & dwFlags) &&
  1135.         ((SHACF_AUTOSUGGEST_FORCE_ON & dwFlags) ||
  1136.         SHRegGetBoolUSValue(REGSTR_PATH_AUTOCOMPLETE, REGSTR_VAL_USEAUTOSUGGEST, FALSE, /*default:*/TRUE)))
  1137.     {
  1138.         dwACOptions |= ACO_AUTOSUGGEST;
  1139.     }
  1140.     if (SHACF_USETAB & dwFlags)
  1141.         dwACOptions |= ACO_USETAB;
  1142.     if (SHACF_FILESYS_ONLY & dwFlags)
  1143.         *pdwACLOptions |= ACLO_FILESYSONLY;
  1144.     // Windows uses the TAB key to move between controls in a dialog.  UNIX and other
  1145.     // operating systems that use AutoComplete have traditionally used the TAB key to
  1146.     // iterate thru the AutoComplete possibilities.  We need to default to disable the
  1147.     // TAB key (ACO_USETAB) unless the caller specifically wants it.  We will also
  1148.     // turn it on 
  1149.     static BOOL s_fAlwaysUseTab = BOOL_NOT_SET;
  1150.     if (BOOL_NOT_SET == s_fAlwaysUseTab)
  1151.         s_fAlwaysUseTab = SHRegGetBoolUSValue(SZ_REGKEY_AUTOCOMPLETE_TAB, SZ_REGVALUE_AUTOCOMPLETE_TAB, FALSE, FALSE);
  1152.         
  1153.     if (s_fAlwaysUseTab)
  1154.         dwACOptions |= ACO_USETAB;
  1155.     return dwACOptions;
  1156. }
  1157. /****************************************************
  1158.     FUNCTION: SHAutoComplete
  1159.     DESCRIPTION:
  1160.         This function will have AutoComplete take over
  1161.     an editbox to help autocomplete DOS paths.
  1162.     Caller needs to have called CoInitialize() or OleInitialize()
  1163.     and cannot call CoUninit/OleUninit until after
  1164.     WM_DESTROY on hwndEdit.
  1165. ****************************************************/
  1166. STDAPI SHAutoComplete(HWND hwndEdit, DWORD dwFlags)
  1167. {
  1168.     IUnknown * punkACL = NULL;
  1169.     HRESULT hr = E_OUTOFMEMORY;
  1170.     DWORD dwACLOptions = 0;
  1171.     DWORD dwACOptions = _UpdateAutoCompleteFlags(dwFlags, &dwACLOptions);
  1172.     if (SHACF_DEFAULT == dwFlags)
  1173.         dwFlags = (SHACF_FILESYSTEM | SHACF_URLALL);
  1174.     punkACL = ACGetLists(dwFlags, dwACLOptions);
  1175.     if (punkACL)    // May fail on low memory.
  1176.     {
  1177.         IAutoComplete2 * pac;
  1178.         // Create the AutoComplete Object
  1179.         hr = CoCreateInstance(CLSID_AutoComplete, NULL, CLSCTX_INPROC_SERVER, IID_IAutoComplete2, (void **)&pac);
  1180.         if (SUCCEEDED(hr))   // May fail because of out of memory
  1181.         {
  1182.             if (SHPinDllOfCLSID(&CLSID_ACListISF) &&
  1183.                 SHPinDllOfCLSID(&CLSID_AutoComplete))
  1184.             {
  1185.                 hr = pac->Init(hwndEdit, punkACL, NULL, NULL);
  1186.                 EVAL(SUCCEEDED(pac->SetOptions(dwACOptions)));
  1187.             }
  1188.             else
  1189.             {
  1190.                 hr = E_FAIL;
  1191.             }
  1192.             pac->Release();
  1193.         }
  1194.         punkACL->Release();
  1195.     }
  1196.     return hr;
  1197. }
  1198. //***   IOleCommandTarget helpers {
  1199. #define ISPOW2(i)   (((i) & ~((i) - 1)) == (i))
  1200. //***   IsQSForward -- (how) should i forward an IOleCT::Exec/QS command?
  1201. // ENTRY/EXIT
  1202. //  nCmdID  the usual; plus special value -1 means just check pguidCmdGroup
  1203. //  hr      S_OK|n if recognized (see below); o.w. OLECMDERR_E_NOTSUPPORTED
  1204. //      S_OK|+1  down
  1205. //      S_OK|+2  broadcast down
  1206. //      S_OK|-1  up
  1207. //      S_OK|-2  broadcast up (unused?)
  1208. // NOTES
  1209. //  WARNING: we never touch anything but the 1st field of rgCmds, so
  1210. //  IsExecForward can (and does!) lie and pass us '(OLECMD *) &nCmdID'.
  1211. //
  1212. STDAPI IsQSForward(const GUID *pguidCmdGroup, int cCmds, OLECMD *pCmds)
  1213. {
  1214.     int octd = 0;
  1215.     ASSERT(OCTD_DOWN > 0 && OCTD_DOWNBROADCAST > OCTD_DOWN);
  1216.     ASSERT(ISPOW2(OCTD_DOWN) && ISPOW2(OCTD_DOWNBROADCAST));
  1217.     ASSERT(OCTD_UP < 0);
  1218.     if (pguidCmdGroup == NULL) {
  1219.         for ( ; cCmds > 0; pCmds++, cCmds--) {
  1220.             switch (pCmds->cmdID) {
  1221.             case OLECMDID_STOP:
  1222.             case OLECMDID_REFRESH:
  1223.             case OLECMDID_ENABLE_INTERACTION:
  1224.                 // down (broadcast)
  1225.                 octd |= OCTD_DOWNBROADCAST;
  1226.                 break;
  1227.             case OLECMDID_CUT:
  1228.             case OLECMDID_COPY:
  1229.             case OLECMDID_PASTE:
  1230.             case OLECMDID_SELECTALL:
  1231.                 // down (singleton)
  1232.                 octd |= OCTD_DOWN;
  1233.                 break;
  1234.             default:
  1235.                 octd |= +4;
  1236.                 break;
  1237.             }
  1238.         }
  1239.     }
  1240.     else if (IsEqualGUID(CGID_Explorer, *pguidCmdGroup))
  1241.     {
  1242.         for ( ; cCmds > 0; pCmds++, cCmds--)
  1243.         {
  1244.             switch (pCmds->cmdID)
  1245.             {
  1246.                 case  SBCMDID_FILEDELETE:
  1247.                 case  SBCMDID_FILEPROPERTIES:
  1248.                 case  SBCMDID_FILERENAME:
  1249.                 case  SBCMDID_CREATESHORTCUT:
  1250.                     octd |= OCTD_DOWN;
  1251.                     break;
  1252.             }
  1253.         }
  1254.     }
  1255. #ifdef DEBUG
  1256.     // make sure only one bit set
  1257.     if (!ISPOW2(octd)) {
  1258.         // e.g. if we have both down and broadcast guys, the caller
  1259.         // will have to be careful to have his IOleCT::QS forward them
  1260.         // separately and then merge them together.
  1261.         ASSERT(0);  // probably best for caller to do 2 separate calls
  1262.         TraceMsg(DM_WARNING, "ief: singleton/broadcast mixture");
  1263.     }
  1264. #endif
  1265.     if (octd == 0 || (octd & 4)) {
  1266.         // octd&4: if anyone is bogus, make them all be, to flesh out
  1267.         // bugs where the caller is passing us a mixture we can't handle
  1268.         return OLECMDERR_E_NOTSUPPORTED;
  1269.     }
  1270.     // aka (S_OK|octd)
  1271.     return MAKE_HRESULT(ERROR_SUCCESS, FACILITY_NULL, octd);
  1272. }
  1273. //***   MayQSForward -- forward IOleCT::QS if appropriate
  1274. //
  1275. STDAPI MayQSForward(IUnknown *punk, int iUpDown, const GUID *pguidCmdGroup,
  1276.     ULONG cCmds, OLECMD rgCmds[], OLECMDTEXT *pcmdtext)
  1277. {
  1278.     HRESULT hrTmp;
  1279.     hrTmp = IsQSForward(pguidCmdGroup, cCmds, rgCmds);
  1280.     if (SUCCEEDED(hrTmp)) {
  1281.         // we know how to forward
  1282.         if (HRESULT_CODE(hrTmp) > 0 && iUpDown > 0
  1283.           || HRESULT_CODE(hrTmp) < 0 && iUpDown < 0) {
  1284.             // punk pts in the right direction for nCmdID
  1285. #if 0
  1286.             // it's caller's bug if they have more than 1 kid
  1287.             // can't ASSERT though because broadcast to only child is o.k.
  1288.             if (HRESULT_CODE(hrTmp) > 1)
  1289.                 TraceMsg(DM_WARNING, "med: Exec broadcast to singleton");
  1290. #endif
  1291.             return IUnknown_QueryStatus(punk, pguidCmdGroup, cCmds, rgCmds,
  1292.                 pcmdtext);
  1293.         }
  1294.     }
  1295.     return OLECMDERR_E_NOTSUPPORTED;
  1296. }
  1297. //***   MayExecForward -- forward IOleCT::Exec if appropriate
  1298. // NOTES
  1299. //  should iUpDown be an int or an HRESULT?
  1300. STDAPI MayExecForward(IUnknown *punk, int iUpDown, const GUID *pguidCmdGroup, DWORD nCmdID, DWORD nCmdexecopt, VARIANTARG *pvarargIn, VARIANTARG *pvarargOut)
  1301. {
  1302.     HRESULT hrTmp;
  1303.     hrTmp = IsExecForward(pguidCmdGroup, nCmdID);
  1304.     if (SUCCEEDED(hrTmp)) {
  1305.         // we know how to forward
  1306.         if (HRESULT_CODE(hrTmp) > 0 && iUpDown > 0
  1307.           || HRESULT_CODE(hrTmp) < 0 && iUpDown < 0) {
  1308.             // punk pts in the right direction for nCmdID
  1309. #if 0
  1310.             // it's caller's bug if they have more than 1 kid
  1311.             // can't ASSERT though because broadcast to only child is o.k.
  1312.             if (HRESULT_CODE(hrTmp) > 1)
  1313.                 TraceMsg(DM_WARNING, "md: Exec broadcast to singleton));
  1314. #endif
  1315.             return IUnknown_Exec(punk, pguidCmdGroup, nCmdID, nCmdexecopt,
  1316.                 pvarargIn, pvarargOut);
  1317.         }
  1318.     }
  1319.     return OLECMDERR_E_NOTSUPPORTED;
  1320. }
  1321. STDAPI_(HMENU) SHLoadMenuPopup(HINSTANCE hinst, UINT id)
  1322. {
  1323.     HMENU hMenuSub = NULL;
  1324.     HMENU hMenu = LoadMenuWrapW(hinst, MAKEINTRESOURCEW(id));
  1325.     if (hMenu) {
  1326.         hMenuSub = GetSubMenu(hMenu, 0);
  1327.         if (hMenuSub) {
  1328.             RemoveMenu(hMenu, 0, MF_BYPOSITION);
  1329.         }
  1330.         DestroyMenuWrap(hMenu);
  1331.     }
  1332.     return hMenuSub;
  1333. }
  1334. //-----------------------------------------------------------------------------
  1335. typedef LRESULT (WINAPI *POSTORSENDMESSAGEPROC)(HWND, UINT, WPARAM, LPARAM);
  1336. struct propagatemsg
  1337. {
  1338.     UINT   uMsg;
  1339.     WPARAM wParam;
  1340.     LPARAM lParam;
  1341.     POSTORSENDMESSAGEPROC PostOrSendMessage;
  1342. };
  1343. BOOL CALLBACK PropagateCallback(HWND hwndChild, LPARAM lParam)
  1344. {
  1345.     struct propagatemsg *pmsg = (struct propagatemsg *)lParam;
  1346.     pmsg->PostOrSendMessage(hwndChild, pmsg->uMsg, pmsg->wParam, pmsg->lParam);
  1347.     return TRUE;
  1348. }
  1349. STDAPI_(void) SHPropagateMessage(HWND hwndParent, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL fSend)
  1350. {
  1351.     if (!hwndParent)
  1352.         return;
  1353.     struct propagatemsg msg;
  1354.     msg.uMsg = uMsg;
  1355.     msg.wParam = wParam;
  1356.     msg.lParam = lParam;
  1357.     if (fSend) {
  1358.         msg.PostOrSendMessage = IsWindowUnicode(hwndParent) ?
  1359.                                     SendMessageW : SendMessageA;
  1360.     } else {
  1361.         msg.PostOrSendMessage = (POSTORSENDMESSAGEPROC)
  1362.                                 (IsWindowUnicode(hwndParent) ?
  1363.                                     PostMessageW : PostMessageA);
  1364.     }
  1365.     EnumChildWindows(hwndParent, /*(WNDENUMPROC)*/PropagateCallback, (LPARAM)&msg);
  1366. }
  1367. LRESULT SHDefWindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1368. {
  1369.     if (IsWindowUnicode(hwnd)) {
  1370.         return DefWindowProcW(hwnd, uMsg, wParam, lParam);
  1371.     } else {
  1372.         return DefWindowProcA(hwnd, uMsg, wParam, lParam);
  1373.     }
  1374. }
  1375. // Returns the submenu of the given menu and ID.  Returns NULL if there
  1376. // is no submenu
  1377. STDAPI_(HMENU) SHGetMenuFromID(HMENU hmMain, UINT uID)
  1378. {
  1379.     HMENU hmenuRet = NULL;
  1380.     if (!hmMain)
  1381.         return NULL;
  1382.     MENUITEMINFO mii;
  1383.     mii.cbSize = SIZEOF(mii);
  1384.     mii.fMask = MIIM_SUBMENU;
  1385.     if (GetMenuItemInfo(hmMain, uID, FALSE, &mii))
  1386.         hmenuRet = mii.hSubMenu;
  1387.     return hmenuRet;
  1388. }
  1389. STDAPI_(int) SHMenuIndexFromID(HMENU hm, UINT id)
  1390. {
  1391.     for (int index = GetMenuItemCount(hm)-1; index>=0; index--)
  1392.     {
  1393.         // We have to use GetMenuItemInfo and not the simpler GetMenuItemID
  1394.         // because GetMenuItemID does not support submenus (grr).
  1395.         MENUITEMINFO mii;
  1396.         mii.cbSize = SIZEOF(MENUITEMINFO);
  1397.         mii.fMask = MIIM_ID;
  1398.         mii.cch = 0;        // just in case
  1399.         if (GetMenuItemInfo(hm, (UINT)index, TRUE, &mii)
  1400.             && (mii.wID == id))
  1401.         {
  1402.            break;
  1403.         }
  1404.     }
  1405.     return(index);
  1406. }
  1407. STDAPI_(void) SHRemoveAllSubMenus(HMENU hmenu)
  1408. {
  1409.     int cItems = GetMenuItemCount(hmenu);
  1410.     int i;
  1411.     for (i=cItems-1; i>=0; i--)
  1412.     {
  1413.         if (GetSubMenu(hmenu, i))
  1414.             RemoveMenu(hmenu, i, MF_BYPOSITION);
  1415.     }
  1416. }
  1417. STDAPI_(void) SHEnableMenuItem(HMENU hmenu, UINT id, BOOL fEnable)
  1418. {
  1419.     EnableMenuItem(hmenu, id, fEnable ?
  1420.         (MF_BYCOMMAND | MF_ENABLED) : ( MF_BYCOMMAND| MF_GRAYED));
  1421. }
  1422. STDAPI_(void) SHCheckMenuItem(HMENU hmenu, UINT id, BOOL fChecked)
  1423. {
  1424.     CheckMenuItem(hmenu, id,
  1425.                   fChecked ? (MF_BYCOMMAND | MF_CHECKED) : (MF_BYCOMMAND | MF_UNCHECKED));
  1426. }
  1427. //
  1428. //  IStream 'saner/simpler' Wrappers that dont use exactly the same params, and have simpler
  1429. //  output.  closer mirroring the way we use them.
  1430. //
  1431. // NOTES
  1432. //  'saner' means that it only returns SUCCEEDED when it reads everything
  1433. //  you asked for.  'simpler' means no 'pcbRead' param.
  1434. STDAPI IStream_Read(IStream *pstm, void *pv, ULONG cb)
  1435. {
  1436.     ASSERT(pstm);
  1437.     HRESULT hr;
  1438.     ULONG cbRead;
  1439.     hr = pstm->Read(pv, cb, &cbRead);
  1440.     if (SUCCEEDED(hr) && cbRead != cb) {
  1441.         hr = E_FAIL;
  1442.     }
  1443.     return hr;
  1444. }
  1445. STDAPI IStream_Write(IStream *pstm, LPCVOID pvIn, ULONG cbIn)
  1446. {
  1447.     ASSERT(pstm);
  1448.     DWORD cb;
  1449.     HRESULT hr = pstm->Write(pvIn, cbIn, &cb);
  1450.     if(SUCCEEDED(hr) && cbIn != cb)
  1451.         hr = E_FAIL;
  1452.     return hr;
  1453. }
  1454. STDAPI IStream_Reset(IStream *pstm)
  1455. {
  1456.     return pstm->Seek(c_li0, STREAM_SEEK_SET, NULL);
  1457. }
  1458. STDAPI IStream_Size(IStream *pstm, ULARGE_INTEGER *pui)
  1459. {
  1460.     ASSERT(pstm);
  1461.     ASSERT(pui);
  1462.     HRESULT hr;
  1463.     STATSTG st = {0};
  1464.     // BUGBUG: What if IStream::Stat is not implemented?
  1465.     // Win95/NT4/IE4's IStream had this problem.  Fixed
  1466.     // for NT5...
  1467.     //
  1468.     if (SUCCEEDED(hr = pstm->Stat(&st, STATFLAG_NONAME)))
  1469.     {
  1470.         *pui = st.cbSize;
  1471.     }
  1472.     return hr;
  1473. }
  1474. STDAPI_(BOOL) SHRegisterClassA(const WNDCLASSA* pwc)
  1475. {
  1476.     WNDCLASSA wc;
  1477.     if (!GetClassInfoA(pwc->hInstance, pwc->lpszClassName, &wc)) {
  1478.         return RegisterClassA(pwc);
  1479.     }
  1480.     return TRUE;
  1481. }
  1482. //
  1483. //  Warning!  This uses RegisterClassWrap, which means that if we
  1484. //  are an ANSI-only platform, your window class will be registered
  1485. //  as **ANSI**, not as UNICODE.  You window procedure needs to call
  1486. //  IsWindowUnicode() to determine how to interpret incoming string
  1487. //  parameters.
  1488. //
  1489. STDAPI_(BOOL) SHRegisterClassW(const WNDCLASSW* pwc)
  1490. {
  1491.     WNDCLASSW wc;
  1492.     if (!GetClassInfoWrapW(pwc->hInstance, pwc->lpszClassName, &wc)) {
  1493.         return RegisterClassWrapW(pwc);
  1494.     }
  1495.     return TRUE;
  1496. }
  1497. //
  1498. //  SHUnregisterClasses unregisters an array of class names.
  1499. //
  1500. STDAPI_(void) SHUnregisterClassesA(HINSTANCE hinst, const LPCSTR *rgpszClasses, UINT cpsz)
  1501. {
  1502.     UINT i;
  1503.     for (i = 0; i < cpsz; i++) {
  1504.         WNDCLASSA wc;
  1505.         if (GetClassInfoA(hinst, rgpszClasses[i], &wc)) {
  1506.             UnregisterClassA(rgpszClasses[i], hinst);
  1507.         }
  1508.     }
  1509. }
  1510. STDAPI_(void) SHUnregisterClassesW(HINSTANCE hinst, const LPCWSTR *rgpszClasses, UINT cpsz)
  1511. {
  1512.     UINT i;
  1513.     for (i = 0; i < cpsz; i++) {
  1514.         WNDCLASSW wc;
  1515.         if (GetClassInfoWrapW(hinst, rgpszClasses[i], &wc)) {
  1516.             UnregisterClassWrapW(rgpszClasses[i], hinst);
  1517.         }
  1518.     }
  1519. }
  1520. //
  1521. // This structure is used by the UNICODE version of this function. So, the pointers point to
  1522. // wide characters strings.
  1523. typedef struct tagMBCINFOW {  // Used only between the routine and its DlgProc
  1524.     UINT    uType;
  1525.     LPCWSTR pwszText;
  1526.     LPCWSTR pwszTitle;
  1527.     LPCWSTR pwszRegPath;
  1528.     LPCWSTR pwszRegVal;
  1529.     
  1530.     DLGPROC pUserDlgProc;
  1531.     void * pUserData;
  1532. } MBCINFOW, *LPMBCINFOW;
  1533. void _MoveDlgItem(HDWP hdwp, HWND hDlg, int nItem, int x, int y)
  1534. {
  1535.     RECT rc;
  1536.     HWND hwnd = GetDlgItem(hDlg, nItem);
  1537.     GetClientRect(hwnd, &rc);
  1538.     MapWindowPoints(hwnd, hDlg, (LPPOINT) &rc, 2);
  1539.     DeferWindowPos(hdwp, hwnd, 0, rc.left + x, rc.top + y, 0, 0,
  1540.         SWP_NOZORDER | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE);
  1541. }
  1542. void _AddIcon(HWND hDlg, LPRECT prcNextChild, UINT uType)
  1543. {
  1544.     HICON hic;
  1545.     switch (uType & MB_ICONMASK)
  1546.     {
  1547.         case MB_ICONHAND :
  1548.             hic = LoadIcon(NULL, IDI_HAND);
  1549.             break;
  1550.         case MB_ICONQUESTION :
  1551.             hic = LoadIcon(NULL, IDI_QUESTION);
  1552.             break;
  1553.         case MB_ICONEXCLAMATION :
  1554.             hic = LoadIcon(NULL, IDI_EXCLAMATION);
  1555.             break;
  1556.         case MB_ICONINFORMATION:
  1557.             hic = LoadIcon(NULL, IDI_INFORMATION);
  1558.             break;
  1559.         default:
  1560.             hic = NULL;
  1561.     }
  1562.     if (hic)
  1563.     {
  1564.         prcNextChild->left += GetSystemMetrics(SM_CXICON) + 10;
  1565.         SendDlgItemMessage(hDlg, IDC_MBC_ICON, STM_SETIMAGE, (WPARAM) IMAGE_ICON, (LPARAM) hic);
  1566.     }
  1567. }
  1568. void _RecalcWindowHeight(HWND hWnd, LPMBCINFOW lpmbci)
  1569. {
  1570.     HDC hdc = GetDC(hWnd);
  1571.     RECT rc;
  1572.     HWND hwndText = GetDlgItem(hWnd,IDC_MBC_TEXT);
  1573.     HDWP hdwp;
  1574.     int iHeightDelta, cx, cxIcon;
  1575.     HFONT hFontSave;
  1576.     hFontSave = (HFONT)SelectObject(hdc, GetWindowFont(hwndText));
  1577.     // Get the starting rect of the text area (for the width)
  1578.     GetClientRect(hwndText, &rc);
  1579.     MapWindowPoints(hwndText, hWnd, (LPPOINT) &rc, 2);
  1580.     // See if we need to add an icon, slide rc over if we do
  1581.     cxIcon = RECTWIDTH(rc);
  1582.     _AddIcon(hWnd, &rc, lpmbci->uType);
  1583.     cxIcon = RECTWIDTH(rc) - cxIcon;
  1584.     // Calc how high the static text area needs to be, given the above width
  1585.     iHeightDelta = RECTHEIGHT(rc);
  1586.     cx = RECTWIDTH(rc);
  1587.     //Note: We need to call the Wide version of DrawText here!
  1588.     DrawTextWrapW(hdc, lpmbci->pwszText, -1, &rc, DT_CALCRECT | DT_LEFT | DT_WORDBREAK);
  1589.     iHeightDelta = RECTHEIGHT(rc) - iHeightDelta;
  1590.     cx = RECTWIDTH(rc) - cx; // Should only change for really long words w/o spaces
  1591.     if (cx < 0)
  1592.         cx = 0;
  1593.     if (hFontSave)
  1594.         SelectObject(hdc, hFontSave);
  1595.     ReleaseDC(hWnd, hdc);
  1596.     hdwp = BeginDeferWindowPos(6);
  1597.     DeferWindowPos(hdwp, hwndText, 0, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_NOACTIVATE);
  1598.     _MoveDlgItem(hdwp, hWnd, IDC_MBC_CHECK, -cxIcon, iHeightDelta);
  1599.     _MoveDlgItem(hdwp, hWnd, IDCANCEL, cx, iHeightDelta);
  1600.     _MoveDlgItem(hdwp, hWnd, IDOK, cx, iHeightDelta);
  1601.     _MoveDlgItem(hdwp, hWnd, IDYES, cx, iHeightDelta);
  1602.     _MoveDlgItem(hdwp, hWnd, IDNO, cx, iHeightDelta);
  1603.     EndDeferWindowPos(hdwp);
  1604.     GetWindowRect(hWnd, &rc);
  1605.     SetWindowPos(hWnd, 0, rc.left - (cx/2), rc.top - (iHeightDelta/2), RECTWIDTH(rc)+cx, RECTHEIGHT(rc)+iHeightDelta, SWP_NOZORDER | SWP_NOACTIVATE);
  1606.     return;
  1607. }
  1608. void HideAndDisableWindow(HWND hwnd)
  1609. {
  1610.     ShowWindow(hwnd, SW_HIDE);
  1611.     EnableWindow(hwnd, FALSE);
  1612. }
  1613. //
  1614. // NOTE: This dialog proc is always UNICODE since both SHMessageBoxCheckA/W thunk to UNICODE and
  1615. //       use this procedure.
  1616. //
  1617. BOOL_PTR CALLBACK MessageBoxCheckDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1618. {
  1619.     switch (uMsg)
  1620.     {
  1621.         // we only handle the WM_INITDIALOG so that we can resize the dialog
  1622.         // approprately
  1623.         case WM_INITDIALOG:
  1624.         {
  1625.             LPMBCINFOW lpmbci = (LPMBCINFOW)lParam;
  1626.             HWND hwndYES = GetDlgItem(hDlg, IDYES);
  1627.             HWND hwndNO = GetDlgItem(hDlg, IDNO);
  1628.             HWND hwndCANCEL = GetDlgItem(hDlg, IDCANCEL);
  1629.             HWND hwndOK = GetDlgItem(hDlg, IDOK);
  1630.             _RecalcWindowHeight(hDlg, lpmbci);
  1631.             //Note: We need to call the Wide version of SetDlgItemText() here.
  1632.             SetDlgItemTextWrapW(hDlg,IDC_MBC_TEXT,lpmbci->pwszText);
  1633.             if (lpmbci->pwszTitle)
  1634.                 SetWindowTextWrapW(hDlg,lpmbci->pwszTitle);
  1635.             if ((lpmbci->uType & MB_TYPEMASK) == MB_OKCANCEL)
  1636.             {
  1637.                 SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
  1638.                 HideAndDisableWindow(hwndYES);
  1639.                 HideAndDisableWindow(hwndNO);
  1640.                 SetFocus(hwndOK);
  1641.             }
  1642.             else if ((lpmbci->uType & MB_TYPEMASK) == MB_OK)
  1643.             {
  1644.                 RECT rc;
  1645.                 SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
  1646.                 HideAndDisableWindow(hwndYES);
  1647.                 HideAndDisableWindow(hwndNO);
  1648.                 HideAndDisableWindow(hwndCANCEL);
  1649.                 if (EVAL(GetClientRect(hwndCANCEL, &rc)))
  1650.                 {
  1651.                     MapWindowPoints(hwndCANCEL, hDlg, (LPPOINT) &rc, 2);
  1652.                     EVAL(SetWindowPos(hwndOK, hDlg, rc.left, rc.top, RECTWIDTH(rc), RECTHEIGHT(rc), SWP_NOZORDER | SWP_SHOWWINDOW));
  1653.                 }
  1654.                 SetFocus(hwndOK);
  1655.             }
  1656.             else // MB_YESNO
  1657.             {
  1658.                 SendMessage(hDlg, DM_SETDEFID, IDYES, 0);
  1659.                 HideAndDisableWindow(hwndOK);
  1660.                 HideAndDisableWindow(hwndCANCEL);
  1661.                 SetFocus(hwndYES);
  1662.             }
  1663.             return (FALSE); // we set the focus, so return false
  1664.         }
  1665.     }
  1666.     
  1667.     // didnt handle this message
  1668.     return FALSE;
  1669. }
  1670. //
  1671. // NOTE: The MessageBoxCheckExDlgProc is both UNICODE and ANSI since it dosent really do any string
  1672. //       stuff. Our UNICODE/ANSI-ness is determined by our caller.
  1673. //
  1674. BOOL_PTR CALLBACK MessageBoxCheckExDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  1675. {
  1676.     MBCINFOW* pmbci = NULL;
  1677.     HWND hwndCheckBox = GetDlgItem(hDlg, IDC_MESSAGEBOXCHECKEX);
  1678.     if (uMsg == WM_INITDIALOG)
  1679.     {
  1680.         pmbci = (MBCINFOW*)lParam;
  1681.         // we have to have this control or we're hopeless
  1682.         if(!hwndCheckBox)
  1683.         {
  1684.             AssertMsg(FALSE, "MessageBoxCheckEx dialog templates must have a control whos ID is IDC_MESSAGEBOXCHECKEX!!");
  1685.             EndDialog(hDlg, 0);
  1686.         }
  1687.         
  1688.         // we use the checkbox to hang our data off of, since the caller
  1689.         // might want to use hDlg to hang its data off of.
  1690.         SetWindowPtr(hwndCheckBox, GWLP_USERDATA, pmbci);
  1691.     }
  1692.     else
  1693.     {
  1694.         pmbci = (MBCINFOW*)GetWindowPtr(hwndCheckBox, GWLP_USERDATA);
  1695.     }
  1696.     // we get a few messages before we get the WM_INITDIALOG (such as WM_SETFONT)
  1697.     // and until we get the WM_INITDIALOG we dont have our pmbci pointer, we just
  1698.     // return false
  1699.     if (!pmbci)
  1700.         return FALSE;
  1701.     // now check to see if we have a user specified dlg proc
  1702.     if (pmbci->pUserDlgProc)
  1703.     {
  1704.         // for the messages below, we simply return what the "real" dialog proc
  1705.         // said since they do NOT return TRUE/FALSE (eg handled or not handled).
  1706.         if (uMsg == WM_CTLCOLORMSGBOX      ||
  1707.             uMsg == WM_CTLCOLOREDIT        ||
  1708.             uMsg == WM_CTLCOLORLISTBOX     ||
  1709.             uMsg == WM_CTLCOLORBTN         ||
  1710.             uMsg == WM_CTLCOLORDLG         ||
  1711.             uMsg == WM_CTLCOLORSCROLLBAR   ||
  1712.             uMsg == WM_CTLCOLORSTATIC      ||
  1713.             uMsg == WM_COMPAREITEM         ||
  1714.             uMsg == WM_VKEYTOITEM          ||
  1715.             uMsg == WM_CHARTOITEM          ||
  1716.             uMsg == WM_QUERYDRAGICON       ||
  1717.             uMsg == WM_INITDIALOG)
  1718.         {
  1719.             return pmbci->pUserDlgProc(hDlg, uMsg, wParam, (uMsg == WM_INITDIALOG) ? (LPARAM)(pmbci->pUserData) : lParam);
  1720.         }
  1721.         if ((pmbci->pUserDlgProc(hDlg, uMsg, wParam, lParam) != FALSE) &&
  1722.             (uMsg != WM_DESTROY))
  1723.         {
  1724.             // the "real" dialog proc handled it so we are done, except we always
  1725.             // need to handle the WM_DESTROY message so we can check the state of
  1726.             // the checkbox in order to set the registry key accordingly.
  1727.             return TRUE;
  1728.         }
  1729.     }
  1730.     switch (uMsg)
  1731.     {
  1732.         case WM_CLOSE:
  1733.             wParam = IDCANCEL;
  1734.             // fall through
  1735.         case WM_COMMAND:
  1736.         {
  1737.             switch (LOWORD(wParam))
  1738.             {
  1739.                 case IDOK:
  1740.                 case IDYES:
  1741.                 case IDCANCEL:
  1742.                 case IDNO:
  1743.                     EndDialog(hDlg, (int) LOWORD(wParam));
  1744.                     break;
  1745.             }
  1746.             break;
  1747.         }
  1748.         case WM_DESTROY:
  1749.             if (IsDlgButtonChecked(hDlg, IDC_MESSAGEBOXCHECKEX) == BST_CHECKED)
  1750.             {
  1751.                 // Note: we need to call the Wide version of this function,
  1752.                 // since our pmbci is always UNICODE
  1753.                 SHRegSetUSValueW(pmbci->pwszRegPath, pmbci->pwszRegVal, REG_SZ,
  1754.                                  L"no", sizeof(L"no"), SHREGSET_HKCU);
  1755.             }
  1756.             break;
  1757.     }
  1758.     return FALSE;
  1759. }
  1760. // MessageBoxCheckW puts up a simple dialog box, with a checkbox to control if the
  1761. // dialog is to be shown again (eg gives you "dont show me this dlg again" functionality).
  1762. //
  1763. // Call as you would MessageBox, but with three additional parameters:
  1764. // The value to return if they checked "never again", and the reg path and value
  1765. // to store whether it gets shown again.
  1766. // MessageBoxCheckEx allows the user to specify a template that will be used along with
  1767. // a dialog proc that will be called. The only must for the dialog template is that it has
  1768. // to have a checkbox control with ID IDC_MESSAGEBOXCHECKEX (this is the "dont show me this
  1769. // again" checkbox).
  1770. // 
  1771. // The default template looks something like the following:
  1772. //
  1773. //        DLG_MESSAGEBOXCHECK DIALOG DISCARDABLE  0, 0, 210, 55
  1774. //        STYLE DS_MODALFRAME | DS_NOIDLEMSG | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_VISIBLE
  1775. //        CAPTION "Error!"
  1776. //        FONT 8, "MS Shell Dlg"
  1777. //        BEGIN
  1778. //            ICON            0, IDC_MBC_ICON,5,5,18,20
  1779. //            LTEXT           "",IDC_MBC_TEXT,5,5,200,8
  1780. //            CONTROL         "&In the future, do not show me this dialog box",
  1781. //                             IDC_MESSAGEBOXCHECKEX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,5,20,155,10
  1782. //            PUSHBUTTON      "OK",IDOK,95,35,50,14
  1783. //            PUSHBUTTON      "Cancel",IDCANCEL,150,35,50,14
  1784. //        END
  1785. //
  1786. // This function is fully implemented in UNICODE and the ANSI version of this function is thunked
  1787. // to this UNICODE version.
  1788. //
  1789. STDAPI_(int) SHMessageBoxCheckW(HWND hwnd, LPCWSTR pwszText, LPCWSTR pwszTitle, UINT uType, int iDefault, LPCWSTR pwszRegVal)
  1790. {
  1791.     // BUGBUG -- browseui(unicode) uses this
  1792.     MBCINFOW mbci;
  1793.     // check first to see if the "dont show me this again" is set
  1794.     if (!SHRegGetBoolUSValueW(REGSTR_PATH_MESSAGEBOXCHECKW, pwszRegVal, FALSE, /*default:*/TRUE))
  1795.         return iDefault;
  1796.     ASSERT(((uType & MB_TYPEMASK) == MB_OKCANCEL) || 
  1797.            ((uType & MB_TYPEMASK) == MB_YESNO) ||
  1798.            ((uType & MB_TYPEMASK) == MB_OK));
  1799.     ASSERT(pwszText != NULL);
  1800.     mbci.pwszText = pwszText;
  1801.     mbci.pwszTitle = pwszTitle;
  1802.     mbci.pwszRegPath = REGSTR_PATH_MESSAGEBOXCHECKW;
  1803.     mbci.pwszRegVal = pwszRegVal;
  1804.     mbci.uType = uType;
  1805.     mbci.pUserData = (LPVOID) &mbci;
  1806.     mbci.pUserDlgProc = MessageBoxCheckDlgProc;
  1807.     // we use the MessageBoxCheckExDlgProc as the main dlg proc, and allow the MessageBoxCheckDlgProc
  1808.     // to be the "user" dlg proc
  1809.     return (int)DialogBoxParamWrapW(HINST_THISDLL, MAKEINTRESOURCEW(DLG_MESSAGEBOXCHECK),
  1810.                                     hwnd, MessageBoxCheckExDlgProc, (LPARAM)&mbci);
  1811. }
  1812. //
  1813. //  This function simply thunks to the UNICODE version.
  1814. //
  1815. //
  1816. STDAPI_(int) SHMessageBoxCheckA(HWND hwnd, LPCSTR pszText, LPCSTR pszTitle, UINT uType, int iDefault, LPCSTR pszRegVal)
  1817. {
  1818.     LPWSTR  lpwszText = NULL, lpwszTitle = NULL;
  1819.     int     iTextBuffSize = 0, iTitleBuffSize = 0;
  1820.     WCHAR   wszRegVal[REGSTR_MAX_VALUE_LENGTH];
  1821.     // check first to see if the "dont show me this again" is set
  1822.     if (!SHRegGetBoolUSValueA(REGSTR_PATH_MESSAGEBOXCHECKA, pszRegVal, FALSE, /*default:*/TRUE))
  1823.         return iDefault;
  1824.     // Since there is no MAX possible size for these strings, we dynamically allocate them.
  1825.     // Convert the input params into UNICODE.
  1826.     if(!(lpwszText = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(iTextBuffSize = lstrlen(pszText)+1))))
  1827.         goto End_MsgBoxCheck;
  1828.     if(!(lpwszTitle = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR)*(iTitleBuffSize = lstrlen(pszTitle)+1))))
  1829.         goto End_MsgBoxCheck;
  1830.     // Conver the Ansi strings into Unicode strings.
  1831.     SHAnsiToUnicode(pszText, lpwszText, iTextBuffSize);
  1832.     SHAnsiToUnicode(pszTitle, lpwszTitle, iTitleBuffSize);
  1833.     SHAnsiToUnicode(pszRegVal, wszRegVal, ARRAYSIZE(wszRegVal));
  1834.     // Call the UNICODE version of this function.
  1835.     iDefault = SHMessageBoxCheckW(hwnd, lpwszText, lpwszTitle, uType, iDefault, wszRegVal);
  1836.     // Clean up and return.
  1837. End_MsgBoxCheck:
  1838.     if(lpwszText)
  1839.         LocalFree((HANDLE)lpwszText);
  1840.     if(lpwszTitle)
  1841.         LocalFree((HANDLE)lpwszTitle);
  1842.     return iDefault;
  1843. }
  1844. //
  1845. // This function calls directly to the helper function
  1846. //
  1847. STDAPI_(int) SHMessageBoxCheckExW(HWND hwnd, HINSTANCE hinst, LPCWSTR pwszTemplateName, DLGPROC pDlgProc, void *pData,
  1848.                                   int iDefault, LPCWSTR pwszRegVal)
  1849. {
  1850.     MBCINFOW mbci = {0};
  1851.     // check first to see if the "dont show me this again" is set
  1852.     if (!SHRegGetBoolUSValueW(REGSTR_PATH_MESSAGEBOXCHECKW, pwszRegVal, FALSE, /*default:*/TRUE))
  1853.         return iDefault;
  1854.     mbci.pwszRegPath = REGSTR_PATH_MESSAGEBOXCHECKW;
  1855.     mbci.pwszRegVal = pwszRegVal;
  1856.     mbci.pUserDlgProc = pDlgProc;
  1857.     mbci.pUserData = pData;
  1858.     // call the UNICODE function, since the users dlg proc (if it exists) is UNICODE
  1859.     return (int)DialogBoxParamWrapW(hinst, pwszTemplateName, hwnd, MessageBoxCheckExDlgProc, (LPARAM)&mbci);
  1860. }
  1861. //
  1862. // This function thunks the strings and calls the helper function
  1863. //
  1864. STDAPI_(int) SHMessageBoxCheckExA(HWND hwnd, HINSTANCE hinst, LPCSTR pszTemplateName, DLGPROC pDlgProc, void *pData, 
  1865.                                   int iDefault, LPCSTR pszRegVal)
  1866. {
  1867.     WCHAR   wszRegVal[REGSTR_MAX_VALUE_LENGTH];
  1868.     MBCINFOW mbci = {0};
  1869.     // check first to see if the "dont show me this again" is set
  1870.     if (!SHRegGetBoolUSValueA(REGSTR_PATH_MESSAGEBOXCHECKA, pszRegVal, FALSE, /*default:*/TRUE))
  1871.         return iDefault;
  1872.     // Conver the Ansi strings into Unicode strings.
  1873.     SHAnsiToUnicode(pszRegVal, wszRegVal, ARRAYSIZE(wszRegVal));
  1874.     mbci.pwszRegPath = REGSTR_PATH_MESSAGEBOXCHECKW; // the MBCINFOW is always UNICODE
  1875.     mbci.pwszRegVal = wszRegVal;
  1876.     mbci.pUserDlgProc = pDlgProc;
  1877.     mbci.pUserData = pData;
  1878.     // call the ANSI function since the users dlg proc (if it exists) is ANSI
  1879.     iDefault = (int)DialogBoxParamA(hinst, pszTemplateName, hwnd, MessageBoxCheckExDlgProc, (LPARAM)&mbci);
  1880.     return iDefault;
  1881. }
  1882. // "I'm sorry, Dave, I can't let you do that."
  1883. LWSTDAPI_(void) SHRestrictedMessageBox(HWND hwnd)
  1884. {
  1885.     ShellMessageBoxWrapW(MLGetHinst(),
  1886.                          hwnd, 
  1887.                          MAKEINTRESOURCEW(IDS_RESTRICTIONS), 
  1888.                          MAKEINTRESOURCEW(IDS_RESTRICTIONSTITLE), 
  1889.                          MB_OK | MB_ICONSTOP);
  1890. }
  1891. // in:
  1892. //      pdrop       thing to drop on
  1893. //      pdataobj    thing we are dropping
  1894. //      grfKeyState to force certain operations
  1895. //      ppt         [optional] point where the drop happens (screen coords)
  1896. //
  1897. // in/out
  1898. //      pdwEffect   [optional] effects allowed and returns what was performed.
  1899. STDAPI SHSimulateDrop(IDropTarget *pdrop, IDataObject *pdtobj, DWORD grfKeyState,
  1900.                       const POINTL *ppt, DWORD *pdwEffect)
  1901. {
  1902.     POINTL pt;
  1903.     DWORD dwEffect;
  1904.     if (!ppt)
  1905.     {
  1906.         ppt = &pt;
  1907.         pt.x = 0;
  1908.         pt.y = 0;
  1909.     }
  1910.     if (!pdwEffect)
  1911.     {
  1912.         pdwEffect = &dwEffect;
  1913.         dwEffect = DROPEFFECT_LINK | DROPEFFECT_MOVE | DROPEFFECT_COPY;
  1914.     }
  1915.     DWORD dwEffectSave = *pdwEffect;    // drag enter returns the default effect
  1916.     HRESULT hr = pdrop->DragEnter(pdtobj, grfKeyState, *ppt, pdwEffect);
  1917.     if (*pdwEffect)
  1918.     {
  1919.         *pdwEffect = dwEffectSave;      // do Drop with the full set of bits
  1920.         hr = pdrop->Drop(pdtobj, grfKeyState, *ppt, pdwEffect);
  1921.     }
  1922.     else
  1923.     {
  1924.         pdrop->DragLeave();
  1925.         hr = S_FALSE;     // HACK? S_FALSE DragEnter said no
  1926.     }
  1927.     return hr;
  1928. }
  1929. STDAPI SHLoadFromPropertyBag(IUnknown* punk, IPropertyBag* ppg)
  1930. {
  1931.     IPersistPropertyBag* pppg;
  1932.     HRESULT hres = punk->QueryInterface(IID_IPersistPropertyBag, (void **)&pppg);
  1933.     if (SUCCEEDED(hres))
  1934.     {
  1935.         hres = pppg->Load(ppg, NULL);
  1936.         pppg->Release();
  1937.     }
  1938.     return hres;
  1939. }
  1940. //***   IUnknown_TranslateAcceleratorOCS -- do punk->IOCS::TranslateAccelerator
  1941. STDAPI IUnknown_TranslateAcceleratorOCS(IUnknown *punk, LPMSG lpMsg, DWORD grfMods)
  1942. {
  1943.     HRESULT hr = E_FAIL;
  1944.     IOleControlSite *pocs;
  1945.     if (punk) {
  1946.         hr = punk->QueryInterface(IID_IOleControlSite, (void**)&pocs);
  1947.         if (SUCCEEDED(hr)) {
  1948.             hr = pocs->TranslateAccelerator(lpMsg, grfMods);
  1949.             pocs->Release();
  1950.         }
  1951.     }
  1952.     return hr;
  1953. }
  1954. STDAPI IUnknown_OnFocusOCS(IUnknown *punk, BOOL fGotFocus)
  1955. {
  1956.     HRESULT hr = E_FAIL;
  1957.     IOleControlSite *pocs;
  1958.     if (punk) {
  1959.         hr = punk->QueryInterface(IID_IOleControlSite, (void**)&pocs);
  1960.         if (SUCCEEDED(hr)) {
  1961.             hr = pocs->OnFocus(fGotFocus);
  1962.             pocs->Release();
  1963.         }
  1964.     }
  1965.     return hr;
  1966. }
  1967. STDAPI IUnknown_HandleIRestrict(IUnknown * punk, const GUID * pguidID, DWORD dwRestrictAction, VARIANT * pvarArgs, DWORD * pdwRestrictionResult)
  1968. {
  1969.     HRESULT hr = E_INVALIDARG;
  1970.     IRestrict * pr;
  1971.     if (!EVAL(pdwRestrictionResult))
  1972.         return hr;
  1973.     hr = IUnknown_QueryService(punk, SID_SRestrictionHandler, IID_IRestrict, (void**)&pr);
  1974.     if (SUCCEEDED(hr))
  1975.     {
  1976.         hr = pr->IsRestricted(pguidID, dwRestrictAction, pvarArgs, pdwRestrictionResult);
  1977.         pr->Release();
  1978.     }
  1979.     return hr;
  1980. }
  1981. /*----------------------------------------------------------
  1982. Purpose: Get color resolution of the current display
  1983. */
  1984. STDAPI_(UINT) SHGetCurColorRes(void)
  1985. {
  1986.     HDC hdc;
  1987.     UINT uColorRes;
  1988.     hdc = GetDC(NULL);
  1989.     uColorRes = GetDeviceCaps(hdc, PLANES) * GetDeviceCaps(hdc, BITSPIXEL);
  1990.     ReleaseDC(NULL, hdc);
  1991.     return uColorRes;
  1992. }
  1993. //
  1994. // If a folder returns QIF_DONTEXPANDFODLER from QueryInfo, the folder should
  1995. // not get expanded.  This is used by menu code to not expand channel folders.
  1996. //
  1997. STDAPI_(BOOL) SHIsExpandableFolder(IShellFolder *psf, LPCITEMIDLIST pidl)
  1998. {
  1999.     ASSERT(psf);
  2000.     ASSERT(pidl);
  2001.     BOOL fRet = TRUE;
  2002.     IQueryInfo* pqi;
  2003.     if (SUCCEEDED(psf->GetUIObjectOf(NULL, 1, &pidl, IID_IQueryInfo, NULL,
  2004.                   (void **)&pqi)))
  2005.     {
  2006.         ASSERT(pqi);
  2007.         DWORD dwFlags;
  2008.         if (SUCCEEDED(pqi->GetInfoFlags(&dwFlags)))
  2009.         {
  2010.             fRet = !(dwFlags & QIF_DONTEXPANDFOLDER);
  2011.         }
  2012.         pqi->Release();
  2013.     }
  2014.     return fRet;
  2015. }
  2016. STDAPI_(DWORD) SHWaitForSendMessageThread(HANDLE hThread, DWORD dwTimeout)
  2017. {
  2018.     MSG msg;
  2019.     DWORD dwRet;
  2020.     DWORD dwEnd = GetTickCount() + dwTimeout;
  2021.     // We will attempt to wait up to dwTimeout for the thread to
  2022.     // terminate
  2023.     do 
  2024.     {
  2025.         dwRet = MsgWaitForMultipleObjects(1, &hThread, FALSE,
  2026.                 dwTimeout, QS_SENDMESSAGE);
  2027.         if (dwRet == (WAIT_OBJECT_0 + 1))
  2028.         {
  2029.             // There must be a pending SendMessage from either the
  2030.             // thread we are killing or some other thread/process besides
  2031.             // this one.  Do a PeekMessage to process the pending
  2032.             // SendMessage and try waiting again
  2033.             PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
  2034.             // Calculate if we have any more time left in the timeout to
  2035.             // wait on.
  2036.             if (dwTimeout != INFINITE)
  2037.             {
  2038.                 dwTimeout = dwEnd - GetTickCount();
  2039.                 if ((long)dwTimeout <= 0)
  2040.                 {
  2041.                     // No more time left, fail with WAIT_TIMEOUT
  2042.                     dwRet = WAIT_TIMEOUT;
  2043.                 }
  2044.             }
  2045.         }
  2046.         // dwRet == WAIT_OBJECT_0 || dwRet == WAIT_FAILED
  2047.         // The thread must have exited, so we are happy
  2048.         //
  2049.         // dwRet == WAIT_TIMEOUT
  2050.         // The thread is taking too long to finish, so just
  2051.         // return and let the caller kill it
  2052.     } while (dwRet == (WAIT_OBJECT_0 + 1));
  2053.     return(dwRet);
  2054. }
  2055. // Max length of the File Type name in registry under CLASSES ROOT
  2056. #define MAX_FILETYPE_NAME       256
  2057. STDAPI_(BOOL) SHVerbExistsNA(LPCSTR szExtension, LPCSTR pszVerb, LPSTR pszCommand, DWORD cchCommand)
  2058. {
  2059.     BOOL fRet = FALSE;
  2060.     HKEY hKey;
  2061.     VDATEINPUTBUF(pszCommand, CHAR, cchCommand);
  2062.     if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szExtension, 0, KEY_READ, &hKey)) {
  2063.         DWORD dwType, dwcbData;
  2064.         TCHAR szVerb[MAX_PATH + MAX_FILETYPE_NAME] = { NULL };
  2065.         TCHAR szDefaultName[MAX_FILETYPE_NAME];
  2066.         dwcbData = SIZEOF(szDefaultName);
  2067.         if ( ERROR_SUCCESS == RegQueryValueEx(hKey, NULL, NULL, &dwType, (LPBYTE)&szDefaultName, &dwcbData))
  2068.         {
  2069.             HKEY hKey2;
  2070.             wnsprintf(szVerb, SIZECHARS(szVerb), TEXT("%s\shell\%s\command"), szDefaultName, pszVerb);
  2071.             if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_CLASSES_ROOT, szVerb, 0, KEY_READ, &hKey2)) {
  2072.                 if (pszCommand) {
  2073.                     pszCommand[0] = 0;
  2074.                     dwcbData = cchCommand;
  2075.                     if (ERROR_SUCCESS == RegQueryValueEx(hKey2, NULL, NULL, &dwType, (LPBYTE)pszCommand, &dwcbData))
  2076.                     {
  2077.                         fRet = TRUE;
  2078.                     }
  2079.                 } else
  2080.                     fRet = TRUE;
  2081.                 RegCloseKey(hKey2);
  2082.             }
  2083.         }
  2084.         RegCloseKey(hKey);
  2085.     }
  2086.     return fRet;
  2087. }
  2088. STDAPI_(void) SHFillRectClr(HDC hdc, LPRECT prc, COLORREF clr)
  2089. {
  2090.     COLORREF clrSave = SetBkColor(hdc, clr);
  2091.     ExtTextOut(hdc,0,0,ETO_OPAQUE,prc,NULL,0,NULL);
  2092.     SetBkColor(hdc, clrSave);
  2093. }
  2094. //***   SearchMapInt -- map int->int
  2095. //
  2096. STDAPI_(int) SHSearchMapInt(const int *src, const int *dst, int cnt, int val)
  2097. {
  2098.     for (; cnt > 0; cnt--, src++, dst++) {
  2099.         if (*src == val)
  2100.             return *dst;
  2101.     }
  2102.     return -1;
  2103. }
  2104. STDAPI_(void) IUnknown_Set(IUnknown ** ppunk, IUnknown * punk)
  2105. {
  2106.     ASSERT(ppunk);
  2107.     if (*ppunk != punk)
  2108.     {
  2109.         IUnknown_AtomicRelease((VOID**)ppunk);
  2110.         if (punk)
  2111.         {
  2112.             punk->AddRef();
  2113.             *ppunk = punk;
  2114.         }
  2115.     }
  2116. }
  2117. /*----------------------------------------------------------
  2118.   Purpose: Removes the first '&' from a string, and returns
  2119.           the character after it.
  2120.           BUGBUG -
  2121.           To be compatible with USER, we need to convert "&&" to
  2122.           a single "&" and not consider it a mnemonic.
  2123. */
  2124. STDAPI_(CHAR) SHStripMneumonicA(LPSTR pszMenu)
  2125. {
  2126.     ASSERT(pszMenu);
  2127.     LPSTR pszAmp = StrChr(pszMenu,TEXT('&'));
  2128.     CHAR cMneumonic = pszAmp? *CharNext(pszAmp): pszMenu[0];  //Return the Char.
  2129.     while(pszAmp && *pszAmp)
  2130.     {
  2131.         *pszAmp = *CharNext(pszAmp);
  2132.         pszAmp = CharNext(pszAmp);
  2133.     }
  2134.     return cMneumonic;
  2135. }
  2136. /*----------------------------------------------------------
  2137.   Purpose: Removes the first '&' from a string, and returns
  2138.           the character after it.
  2139. */
  2140. STDAPI_(WCHAR) SHStripMneumonicW(LPWSTR pszMenu)
  2141. {
  2142.     ASSERT(pszMenu);
  2143.     LPWSTR pszAmp = StrChrW(pszMenu, L'&');
  2144.     WCHAR cMneumonic = (pszAmp ? pszAmp[1]: pszMenu[0]);  //Return the Char.
  2145.     while(pszAmp && *pszAmp)
  2146.     {
  2147.         pszAmp[0] = pszAmp[1];
  2148.         pszAmp++;
  2149.     }
  2150.     return cMneumonic;
  2151. }
  2152. // don't use IsChild.  that walks down all children.
  2153. // faster to walk up the parent chain
  2154. //***   IsChildOrSelf -- is hwnd either a child of or equal to hwndParent?
  2155. // NOTES
  2156. //  HasFocus      is IsChildOrSelf(hwnd, GetFocus())
  2157. //  IsWindowOwner is IsChildOrSelf(hwnd, hwndOwner)
  2158. //  n.b. hwnd==0 is special and yields FALSE.  this is presumbably what
  2159. //  one wants for both HasFocus and IsWindowOwner.
  2160. //
  2161. // NOTE: S_OK means TRUE, S_FALSE meanse FALSE
  2162. //
  2163. STDAPI SHIsChildOrSelf(HWND hwndParent, HWND hwnd)
  2164. {
  2165.     // SHDOCVW likes to pass hwndParent == NULL.  Oops.
  2166.     // SHDOCVW even likes to pass hwndParent == hwnd == NULL.  Double oops.
  2167.     if (hwndParent == NULL || hwnd == NULL) {
  2168.         return S_FALSE;
  2169.     }
  2170.     // Old code here used to walk the GetParent chain which is bogus
  2171.     // because GetParent will return the Window Owner when there is
  2172.     // no parent window. Bug 63233 got bit by this -- a dialog box
  2173.     // has no parent but it's owned by the window at the top of
  2174.     // the hwndParent chain. Since GetParent returns the window owner
  2175.     // if there is no parent, we'd incorrectly think we should translate
  2176.     // this message. I switched this to calling IsChild directly. Note:
  2177.     // in asking around it appears that this function was written
  2178.     // because of the mistaken assumption that IsChild was implemented
  2179.     // in a perf-unfriendly way. [mikesh 15 Oct 97]
  2180.     //
  2181.     return ((hwndParent == hwnd) || IsChild(hwndParent, hwnd)) ? S_OK : S_FALSE;
  2182. }
  2183. STDAPI IContextMenu_Invoke(IContextMenu* pcm, HWND hwndOwner, LPCSTR pVerb, UINT fFlags)
  2184. {
  2185.     HRESULT hres = S_OK;
  2186.     if (pcm)
  2187.     {
  2188.         UINT idCmd = 0;
  2189.         DECLAREWAITCURSOR;
  2190.         SetWaitCursor();
  2191.         HMENU hmenu = NULL;
  2192.         CMINVOKECOMMANDINFO ici = {
  2193.             SIZEOF(CMINVOKECOMMANDINFO),
  2194.             0,
  2195.             hwndOwner,
  2196.             NULL,
  2197.             NULL, NULL,
  2198.             SW_NORMAL,
  2199.         };
  2200.         if (!IS_INTRESOURCE(pVerb)) {
  2201. #ifdef UNICODE
  2202.             ici.lpVerbW = pVerb;
  2203.             ici.lpVerb = makeansi(pVerb);
  2204.             ici.fMask |= CMIC_MASK_UNICODE;
  2205. #else
  2206.             ici.lpVerb = pVerb;
  2207. #endif
  2208.         } else {
  2209.             hmenu = CreatePopupMenu();
  2210.             if (hmenu)
  2211.             {
  2212.                 fFlags |= CMF_DEFAULTONLY;
  2213. #define CMD_ID_FIRST 1
  2214.                 pcm->QueryContextMenu(hmenu, 0, CMD_ID_FIRST, 0xFFFF, fFlags);
  2215.                 idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
  2216.                 if (-1 != idCmd)
  2217.                 {
  2218.                     ici.lpVerb = (LPSTR)MAKEINTRESOURCE(idCmd - CMD_ID_FIRST);
  2219.                 }
  2220.             }
  2221.         }
  2222.         // need to reset it so that user won't blow off the app starting  cursor
  2223.         // also so that if we won't leave the wait cursor up when we're not waiting
  2224.         // (like in a prop sheet or something that has a message loop
  2225.         ResetWaitCursor();
  2226.         // can't just check verb because it could be 0 if idCmd == CMD_ID_FIRST
  2227.         if ((-1 != idCmd) || ici.lpVerb) 
  2228.         {
  2229.             if (!hwndOwner)
  2230.                 ici.fMask |= CMIC_MASK_FLAG_NO_UI;
  2231.             pcm->InvokeCommand(&ici);
  2232.             hres = (HRESULT)1;
  2233.         }
  2234.         if (hmenu)
  2235.             DestroyMenu(hmenu);
  2236.     }
  2237.     return hres;
  2238. }
  2239. //
  2240. // SetDefaultDialogFont
  2241. //
  2242. // purpose: set font to the given control of the dialog
  2243. //          with platform's default character set so that
  2244. //          user can see whatever string in the native
  2245. //          language on the platform.
  2246. //
  2247. // in:      hDlg - the parent window handle of the given control
  2248. //          idCtl - ID of the control
  2249. //
  2250. // note:    this will store created font with window property
  2251. //          so that we can destroy it later.
  2252. //
  2253. const TCHAR c_szPropDlgFont[]       = TEXT("PropDlgFont");
  2254. STDAPI_(void) SHSetDefaultDialogFont(HWND hDlg, int idCtl)
  2255. {
  2256.     HFONT hfont;
  2257.     HFONT hfontDefault;
  2258.     LOGFONT lf;
  2259.     LOGFONT lfDefault;
  2260.     hfont = GetWindowFont(hDlg);
  2261.     GetObject(hfont, sizeof(LOGFONT), &lf);
  2262.     SystemParametersInfo(SPI_GETICONTITLELOGFONT, sizeof(LOGFONT), &lfDefault, 0);
  2263.     
  2264.     if (lfDefault.lfCharSet == lf.lfCharSet)
  2265.     {
  2266.         // if the dialog already has correct character set
  2267.         // don't do anything.
  2268.         return;
  2269.     }
  2270.     // If we already have hfont created, use it.
  2271.     if(!(hfontDefault = (HFONT)GetProp(hDlg, c_szPropDlgFont)))
  2272.     {
  2273.         // assign the same height of the dialog font
  2274.         lfDefault.lfHeight = lf.lfHeight;
  2275.         if (!(hfontDefault=CreateFontIndirect(&lfDefault)))
  2276.         {
  2277.             // restore back in failure
  2278.             hfontDefault = hfont;
  2279.         }
  2280.         if (hfontDefault != hfont)
  2281.             SetProp(hDlg, c_szPropDlgFont, hfontDefault);
  2282.     }
  2283.     
  2284.     SetWindowFont(GetDlgItem(hDlg, idCtl), hfontDefault, FALSE);
  2285. }
  2286. //
  2287. // RemoveDefaultDialogFont
  2288. //
  2289. // purpose: Destroy the font we used to set gui default font
  2290. //          Also removes the window property used to store the font.
  2291. //
  2292. // in:      hDlg - the parent window handle of the given control
  2293. //
  2294. // note:
  2295. STDAPI_(void) SHRemoveDefaultDialogFont(HWND hDlg)
  2296. {
  2297.     HFONT hfont;
  2298.     if(hfont = (HFONT)GetProp(hDlg, c_szPropDlgFont))
  2299.     {
  2300.         DeleteObject(hfont);
  2301.         RemoveProp(hDlg, c_szPropDlgFont);
  2302.     }
  2303. }
  2304. // NOTE: since this is a worker window it probably doesn't care about
  2305. // system messages that are ansi/unicode, so only support an ansi version.
  2306. // If the pfnWndProc cares, it can thunk the messages. (Do this because
  2307. // Win95 doesn't support RegisterClassW.)
  2308. HWND SHCreateWorkerWindowA(WNDPROC pfnWndProc, HWND hwndParent, DWORD dwExStyle, DWORD dwFlags, HMENU hmenu, void * p)
  2309. {
  2310.     WNDCLASSA wc = {0};
  2311.     wc.lpfnWndProc      = DefWindowProcA;
  2312.     wc.cbWndExtra       = SIZEOF(void *);
  2313.     wc.hInstance        = HINST_THISDLL;
  2314.     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
  2315.     wc.hbrBackground    = (HBRUSH) (COLOR_BTNFACE + 1);
  2316.     wc.lpszClassName    = "WorkerA";
  2317.     dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
  2318.     SHRegisterClassA(&wc);
  2319.     HWND hwnd = CreateWindowExA(dwExStyle, "WorkerA", NULL, dwFlags,
  2320.                                   0, 0, 0, 0, hwndParent,
  2321.                                   (HMENU)hmenu, HINST_THISDLL, NULL);
  2322.     if (hwnd) {
  2323.         SetWindowPtr(hwnd, 0, p);
  2324.         if (pfnWndProc)
  2325.             SetWindowPtr(hwnd, GWLP_WNDPROC, pfnWndProc);
  2326.     }
  2327.     return hwnd;
  2328. }
  2329. // WARNING: since this is a worker window it probably doesn't care about
  2330. // system messages that are ansi/unicode, default to an ansi version on Win95.
  2331. //
  2332. // this forces callers to be aware of the fact that they are getting into
  2333. // a mess if they want compatibility with Win95.
  2334. //
  2335. // If the pfnWndProc cares, it can thunk the messages. (Do this because
  2336. // Win95 doesn't support RegisterClassW.)
  2337. //
  2338. HWND SHCreateWorkerWindowW(WNDPROC pfnWndProc, HWND hwndParent, DWORD dwExStyle, DWORD dwFlags, HMENU hmenu, void * p)
  2339. {
  2340.     //  must default to ANSI on win95
  2341.     if (!g_bRunningOnNT)
  2342.     {
  2343.         return SHCreateWorkerWindowA(pfnWndProc, hwndParent, dwExStyle, dwFlags, hmenu, p);
  2344.     }
  2345.     WNDCLASSW wc = {0};
  2346.     wc.lpfnWndProc      = DefWindowProcW;
  2347.     wc.cbWndExtra       = SIZEOF(void *);
  2348.     wc.hInstance        = HINST_THISDLL;
  2349.     wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
  2350.     wc.hbrBackground    = (HBRUSH) (COLOR_BTNFACE + 1);
  2351.     wc.lpszClassName    = L"WorkerW";
  2352.     dwExStyle |= IS_BIDI_LOCALIZED_SYSTEM() ? dwExStyleRTLMirrorWnd : 0L;
  2353.     SHRegisterClassW(&wc);
  2354.     HWND hwnd = CreateWindowExW(dwExStyle, L"WorkerW", NULL, dwFlags,
  2355.                                   0, 0, 0, 0, hwndParent,
  2356.                                   (HMENU)hmenu, HINST_THISDLL, NULL);
  2357.     if (hwnd) {
  2358.         SetWindowPtr(hwnd, 0, p);
  2359.         // Note: Must explicitly use W version to avoid charset thunks
  2360.         if (pfnWndProc)
  2361.             SetWindowLongPtrW(hwnd, GWLP_WNDPROC, (LONG_PTR)pfnWndProc);
  2362.     }
  2363.     return hwnd;
  2364. }
  2365. #pragma warning(disable:4035)   // no return value
  2366. #undef SHInterlockedCompareExchange
  2367. STDAPI_(void *) SHInterlockedCompareExchange(void **ppDest, void *pExch, void *pComp)
  2368. {
  2369. #if defined(_X86_)
  2370.     _asm {
  2371.         mov     ecx,ppDest
  2372.         mov     edx,pExch
  2373.         mov     eax,pComp
  2374.         lock    cmpxchg [ecx],edx
  2375.     }
  2376. #else
  2377.     return InterlockedCompareExchangePointer(ppDest, pExch, pComp);
  2378. #endif
  2379. }
  2380. #pragma warning(default:4035)
  2381. #define REGSTR_PATH_POLICIESW    L"Software\Microsoft\Windows\CurrentVersion\Policies"
  2382. STDAPI_(DWORD) SHRestrictionLookup(INT rest, LPCWSTR pszBaseKey, const SHRESTRICTIONITEMS *pRestrictions,
  2383.                                    DWORD* pdwRestrictionItemValues)
  2384. {
  2385.     int i;
  2386.     DWORD dw = 0;
  2387.     //
  2388.     // Loop through the restrictions
  2389.     //
  2390.     for (i=0; pRestrictions[i].pszKey; i++)
  2391.     {
  2392.         if (rest == pRestrictions[i].iFlag)
  2393.         {
  2394.             dw = pdwRestrictionItemValues[i];
  2395.             // Has this restriction been initialized yet?
  2396.             //
  2397.             if (dw == -1)
  2398.             {
  2399.                 dw = SHGetRestriction(pszBaseKey, pRestrictions[i].pszKey, pRestrictions[i].pszValue);
  2400.                 pdwRestrictionItemValues[i] = dw;
  2401.             }
  2402.             // Got the restriction we needed. Get out of here.
  2403.             break;
  2404.         }
  2405.     }
  2406.     return dw;
  2407. }
  2408. STDAPI_(DWORD) SHGetRestriction(LPCWSTR pszBaseKey, LPCWSTR pszGroup, LPCWSTR pszRestriction)
  2409. {
  2410.     // Make sure the string is long enough to hold longest one...
  2411.     COMPILETIME_ASSERT(MAX_PATH > ARRAYSIZE(REGSTR_PATH_POLICIESW) + 40); // PathCombine *assumes* MAX_PATH
  2412.     WCHAR szSubKey[MAX_PATH];
  2413.     DWORD dwSize, dwType;
  2414.     // A sensible default
  2415.     DWORD dw = 0;
  2416.     //
  2417.     // This restriction hasn't been read yet.
  2418.     //
  2419.     if (!pszBaseKey) {
  2420.         pszBaseKey = REGSTR_PATH_POLICIESW;
  2421.     }
  2422. #ifndef UNIX
  2423.     PathCombineW(szSubKey, pszBaseKey, pszGroup);
  2424. #else
  2425.     wsprintfW(szSubKey, L"%s\%s", pszBaseKey, pszGroup);
  2426. #endif
  2427.     // Check local machine first and let it override what the
  2428.     // HKCU policy has done.
  2429.     dwSize = sizeof(dw);
  2430.     if (ERROR_SUCCESS != SHGetValueW(HKEY_LOCAL_MACHINE,
  2431.                                      szSubKey, pszRestriction,
  2432.                                      &dwType, &dw, &dwSize))
  2433.     {
  2434.         // Check current user if we didn't find anything for the local machine.
  2435.         dwSize = sizeof(dw);
  2436.         SHGetValueW(HKEY_CURRENT_USER,
  2437.                     szSubKey, pszRestriction,
  2438.                     &dwType, &dw, &dwSize);
  2439.     }
  2440.     return dw;
  2441. }
  2442. //  WhichPlatform
  2443. //      Determine if we're running on integrated shell or browser-only.
  2444. STDAPI_(UINT) WhichPlatform(void)
  2445. {
  2446.     HINSTANCE hinst;
  2447.     // Cache this info
  2448.     static UINT uInstall = PLATFORM_UNKNOWN;
  2449.     if (uInstall != PLATFORM_UNKNOWN)
  2450.         return uInstall;
  2451.     // Not all callers are linked to SHELL32.DLL, so we must use LoadLibrary.
  2452.     hinst = LoadLibraryA("SHELL32.DLL");
  2453.     if (hinst)
  2454.     {
  2455.         DWORD fValue;
  2456.         DWORD cbSize = sizeof(fValue);
  2457.         HKEY hKey;
  2458.         LONG lRes;
  2459.         // NOTE: GetProcAddress always takes ANSI strings!
  2460.         DLLGETVERSIONPROC pfnGetVersion =
  2461.             (DLLGETVERSIONPROC)GetProcAddress(hinst, "DllGetVersion");
  2462.         uInstall = (NULL != pfnGetVersion) ? PLATFORM_INTEGRATED : PLATFORM_BROWSERONLY;
  2463.         // check that the registry reflects the right value... (this is so iexplore can check efficiently)
  2464.         lRes = RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\Microsoft\Internet Explorer"),
  2465.                             0, KEY_READ | KEY_WRITE, &hKey );
  2466.         if ( lRes == ERROR_SUCCESS )
  2467.         {
  2468.             lRes = RegQueryValueEx( hKey, REGVAL_INTEGRATEDBROWSER,
  2469.                                     NULL, NULL,
  2470.                                     (LPBYTE) &fValue, &cbSize );
  2471.             if ( lRes == ERROR_SUCCESS && uInstall == PLATFORM_BROWSERONLY )
  2472.             {
  2473.                 // remove the value, we are now Browser only release
  2474.                 RegDeleteValue( hKey, REGVAL_INTEGRATEDBROWSER);
  2475.             }
  2476.             else if ( lRes != ERROR_SUCCESS && uInstall == PLATFORM_INTEGRATED )
  2477.             {
  2478.                 // install the RegValue, we are integrated browser mode...
  2479.                 fValue = TRUE;
  2480.                 cbSize = sizeof( fValue );
  2481.                 RegSetValueEx( hKey, REGVAL_INTEGRATEDBROWSER,
  2482.                                (DWORD) NULL, REG_DWORD,
  2483.                                (LPBYTE) &fValue, cbSize );
  2484.                 // ignore the failure, if the key is not present, shdocvw will be loaded and this
  2485.                 // function called anyway....
  2486.             }
  2487.             RegCloseKey( hKey );
  2488.         }
  2489.         else
  2490.         {
  2491.             // a machine without our regKey,
  2492.             TraceMsg(TF_ERROR, "WhichPlatform: failed to open 'HKLM\Software\Microsoft\Internet Explorer'");
  2493.         }
  2494.         FreeLibrary(hinst);
  2495.     }
  2496.     return uInstall;
  2497. }
  2498. // Tray notification window class
  2499. CHAR const c_szTrayNotificationClass[] = WNDCLASS_TRAYNOTIFY;
  2500. BOOL DoRegisterGlobalHotkey(WORD wOldHotkey, WORD wNewHotkey,
  2501.                             LPCSTR pcszPath, LPCWSTR pcwszPath)
  2502. {
  2503.     BOOL bResult;
  2504.     HWND hwndTray;
  2505.     CHAR szPath[MAX_PATH];
  2506.     ASSERT((NULL != pcszPath) || (NULL != pcwszPath));
  2507.     hwndTray = FindWindowA(c_szTrayNotificationClass, 0);
  2508.     if (hwndTray)
  2509.     {
  2510.         if (wOldHotkey)
  2511.         {
  2512.             SendMessage(hwndTray, WMTRAY_SCUNREGISTERHOTKEY, wOldHotkey, 0);
  2513.             TraceMsg(TF_FUNC, "RegisterGlobalHotkey(): Unregistered old hotkey %#04x.", wOldHotkey);
  2514.         }
  2515.         if (wNewHotkey)
  2516.         {
  2517.             //  If not running on NT and we have a widestr, need to convert to ansi
  2518.             if ((!g_bRunningOnNT) && (NULL == pcszPath))
  2519.             {
  2520.                 SHUnicodeToAnsi(pcwszPath, szPath, ARRAYSIZE(szPath));
  2521.                 pcszPath = szPath;
  2522.             }
  2523.             // Aargh: The W95/browser only mode sends a string, other platforms sends
  2524.             // Atom...
  2525.             if (!g_bRunningOnNT && (WhichPlatform() == PLATFORM_IE3))
  2526.             {
  2527.                 SendMessage(hwndTray, WMTRAY_SCREGISTERHOTKEY, wNewHotkey, (LPARAM)pcszPath);
  2528.             }
  2529.             else
  2530.             {
  2531.                 ATOM atom = (NULL != pcszPath) ?
  2532.                                 GlobalAddAtomA(pcszPath) :
  2533.                                 GlobalAddAtomW(pcwszPath);
  2534.                 ASSERT(atom);
  2535.                 if (atom)
  2536.                 {
  2537.                     SendMessage(hwndTray, WMTRAY_SCREGISTERHOTKEY, wNewHotkey, (LPARAM)atom);
  2538.                     GlobalDeleteAtom(atom);
  2539.                 }
  2540.             }
  2541.             TraceMsg(TF_FUNC, "RegisterGlobalHotkey(): Registered new hotkey %#04x.",wNewHotkey);
  2542.         }
  2543.         bResult = TRUE;
  2544.     }
  2545.     else
  2546.     {
  2547.         bResult = FALSE;
  2548.         TraceMsgA(TF_WARNING, "RegisterGlobalHotkey(): Unable to find Tray window of class %s to notify.",
  2549.                   c_szTrayNotificationClass);
  2550.     }
  2551.     return(bResult);
  2552. }
  2553. BOOL
  2554. RegisterGlobalHotkeyW(
  2555.     WORD wOldHotkey,
  2556.     WORD wNewHotkey,
  2557.     LPCWSTR pcwszPath)
  2558. {
  2559.     ASSERT(IsValidPathW(pcwszPath));
  2560.     return DoRegisterGlobalHotkey(wOldHotkey, wNewHotkey, NULL, pcwszPath);
  2561. }
  2562. BOOL
  2563. RegisterGlobalHotkeyA(
  2564.     WORD wOldHotkey,
  2565.     WORD wNewHotkey,
  2566.     LPCSTR pcszPath)
  2567. {
  2568.     ASSERT(IsValidPathA(pcszPath));
  2569.     return DoRegisterGlobalHotkey(wOldHotkey, wNewHotkey, pcszPath, NULL);
  2570. }
  2571. typedef UINT (__stdcall * PFNGETSYSTEMWINDOWSDIRECTORYW)(LPWSTR pwszBuffer, UINT cchBuff);
  2572. UINT WINAPI
  2573. SHGetSystemWindowsDirectoryW(LPWSTR lpBuffer, UINT uSize)
  2574. {
  2575.     if (g_bRunningOnNT)
  2576.     {
  2577.         static PFNGETSYSTEMWINDOWSDIRECTORYW s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)-1;
  2578.         if (((PFNGETSYSTEMWINDOWSDIRECTORYW)-1) == s_pfn)
  2579.         {
  2580.             HINSTANCE hinst = GetModuleHandle(TEXT("KERNEL32.DLL"));
  2581.             ASSERT(NULL != hinst);  //  YIKES!
  2582.             if (hinst)
  2583.                 s_pfn = (PFNGETSYSTEMWINDOWSDIRECTORYW)GetProcAddress(hinst, "GetSystemWindowsDirectoryW");
  2584.             else
  2585.                 s_pfn = NULL;
  2586.         }
  2587.         if (s_pfn)
  2588.         {
  2589.             // we use the new API so we dont get lied to by hydra
  2590.             return s_pfn(lpBuffer, uSize);
  2591.         }
  2592.         else
  2593.         {
  2594.             GetSystemDirectoryW(lpBuffer, uSize);
  2595.             PathRemoveFileSpecW(lpBuffer);
  2596.             return lstrlenW(lpBuffer);
  2597.         }
  2598.     }
  2599.     else
  2600.     {
  2601.         return GetWindowsDirectoryWrapW(lpBuffer, uSize);
  2602.     }
  2603. }
  2604. UINT WINAPI
  2605. SHGetSystemWindowsDirectoryA(LPSTR lpBuffer, UINT uSize)
  2606. {
  2607.     if (g_bRunningOnNT)
  2608.     {
  2609.         WCHAR wszPath[MAX_PATH];
  2610.         SHGetSystemWindowsDirectoryW(wszPath, ARRAYSIZE(wszPath));
  2611.         return SHUnicodeToAnsi(wszPath, lpBuffer, uSize);
  2612.     }
  2613.     else
  2614.     {
  2615.         return GetWindowsDirectoryA(lpBuffer, uSize);
  2616.     }
  2617. }
  2618. typedef struct {
  2619.     SHDLGPROC pfnDlgProc;
  2620.     VOID* pData;
  2621. } SHDIALOGDATA;
  2622. BOOL_PTR DialogBoxProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
  2623. {
  2624.     SHDIALOGDATA* pdd = (SHDIALOGDATA*)GetWindowPtr(hwnd, DWLP_USER);
  2625.     if (uMsg == WM_INITDIALOG) {
  2626.         pdd = (SHDIALOGDATA*)lParam;
  2627.         SetWindowPtr(hwnd, DWLP_USER, pdd);
  2628.         lParam = (LPARAM)pdd->pData;
  2629.     }
  2630.     if (pdd && pdd->pfnDlgProc) {
  2631.         // Must return bResult instead of unconditional TRUE because it
  2632.         // might be a WM_CTLCOLOR message.
  2633.         BOOL_PTR bResult = pdd->pfnDlgProc(pdd->pData, hwnd, uMsg, wParam, lParam);
  2634.         if (bResult)
  2635.             return bResult;
  2636.     }
  2637.     switch (uMsg) {
  2638.     case WM_INITDIALOG:
  2639.         return TRUE;
  2640.     case WM_COMMAND:
  2641.     {
  2642.         int id = GET_WM_COMMAND_ID(wParam, lParam);
  2643.         HWND hwndCtrl = GetDlgItem(hwnd, id);
  2644.         if ((id != IDHELP) && SendMessage(hwndCtrl, WM_GETDLGCODE, 0, 0) & (DLGC_DEFPUSHBUTTON | DLGC_UNDEFPUSHBUTTON)) {
  2645.             EndDialog(hwnd, id);
  2646.             return TRUE;
  2647.         }
  2648.         break;
  2649.     }
  2650.     }
  2651.     return FALSE;
  2652. }
  2653. STDAPI_(INT_PTR) SHDialogBox(HINSTANCE hInstance, LPCWSTR lpTemplateName,
  2654.     HWND hwndParent, SHDLGPROC lpDlgFunc, VOID* lpData)
  2655. {
  2656.     SHDIALOGDATA dd;
  2657.     dd.pfnDlgProc = lpDlgFunc;
  2658.     dd.pData = lpData;
  2659.     // we currently only support resource id #'s
  2660.     ASSERT(IS_INTRESOURCE(lpTemplateName));
  2661.     return DialogBoxParam(hInstance, (LPCTSTR)lpTemplateName, hwndParent, DialogBoxProc, (LPARAM)&dd);
  2662. }
  2663. //---------------------------------------------------------------------------
  2664. #define CMD_ID_FIRST    1
  2665. #define CMD_ID_LAST     0x7fff
  2666. STDAPI SHInvokeDefaultCommand(HWND hwnd, IShellFolder* psf, LPCITEMIDLIST pidlItem)
  2667. {
  2668.     return SHInvokeCommand(hwnd, psf, pidlItem, NULL);
  2669. }
  2670. STDAPI SHInvokeCommand(HWND hwnd, IShellFolder* psf, LPCITEMIDLIST pidlItem, LPCSTR lpVerb)
  2671. {
  2672.     HRESULT hres = E_FAIL;
  2673.     if (psf)
  2674.     {
  2675.         IContextMenu *pcm;
  2676.         if (SUCCEEDED(psf->GetUIObjectOf(hwnd, 1, &pidlItem, IID_IContextMenu, 0, (void**)&pcm)))
  2677.         {
  2678.             if (pcm)
  2679.             {
  2680.                 HMENU hmenu = CreatePopupMenu();
  2681.                 if (hmenu)
  2682.                 {
  2683.                     if (SUCCEEDED(pcm->QueryContextMenu(hmenu, 0, CMD_ID_FIRST, CMD_ID_LAST,
  2684.                                     lpVerb ? 0 : CMF_DEFAULTONLY))) 
  2685.                     {
  2686.                         UINT idCmd = -1;
  2687.                         if (lpVerb == NULL)
  2688.                         {
  2689.                             idCmd = GetMenuDefaultItem(hmenu, MF_BYCOMMAND, 0);
  2690.                             if ((UINT)-1 != idCmd)
  2691.                                 lpVerb = MAKEINTRESOURCE(idCmd - CMD_ID_FIRST);
  2692.                         }
  2693.                         // if idCmd == 0, then lpVerb would be Zero. So we need to check to
  2694.                         // see if idCmd is not -1.
  2695.                         if (lpVerb || idCmd != (UINT)-1)
  2696.                         {
  2697.                             CMINVOKECOMMANDINFO ici = { 0 };
  2698.                             ici.cbSize = SIZEOF(CMINVOKECOMMANDINFO);
  2699.                             ici.hwnd = hwnd;
  2700.                             ici.lpVerb = lpVerb;
  2701.                             ici.nShow = SW_NORMAL;
  2702.                             hres = pcm->InvokeCommand((LPCMINVOKECOMMANDINFO)&ici);
  2703.                         }
  2704.                     }
  2705.                     DestroyMenu(hmenu);
  2706.                 }
  2707.                 // Release our use of the context menu
  2708.                 pcm->Release();
  2709.             }
  2710.         }
  2711.     }
  2712.     return hres;
  2713. }
  2714. int MessageBoxHelper(HINSTANCE hInst, HWND hwnd, IUnknown *punkEnableModless, LPCWSTR pwzMessage, UINT idTitle, UINT nFlags)
  2715. {
  2716.     WCHAR wzTitle[MAX_PATH];
  2717.     UINT uiResult;
  2718.     EVAL(LoadStringWrapW(hInst, idTitle, wzTitle, ARRAYSIZE(wzTitle)));
  2719.     if (hwnd)
  2720.         IUnknown_EnableModless(punkEnableModless, TRUE);
  2721.     uiResult = MessageBoxWrapW(hwnd, pwzMessage, wzTitle, nFlags);
  2722.     if (hwnd)
  2723.         IUnknown_EnableModless(punkEnableModless, TRUE);
  2724.     return uiResult;
  2725. }
  2726. int MessageBoxDiskHelper(HINSTANCE hInst, HWND hwnd, IUnknown *punkEnableModless, UINT idMessage, UINT idTitle, UINT nFlags, BOOL fDrive, DWORD dwDrive)
  2727. {
  2728.     WCHAR wzMessage[MAX_PATH];
  2729.     EVAL(LoadStringWrapW(hInst, idMessage, wzMessage, ARRAYSIZE(wzMessage)));
  2730.     if (fDrive)
  2731.     {
  2732.         WCHAR wzTemp[MAX_PATH];
  2733.         wnsprintfW(wzTemp, ARRAYSIZE(wzTemp), wzMessage, dwDrive);
  2734.         StrCpyNW(wzMessage, wzTemp, ARRAYSIZE(wzMessage));
  2735.     }
  2736.     return MessageBoxHelper(hInst, hwnd, punkEnableModless, wzMessage, idTitle, nFlags);
  2737. }
  2738. BOOL DoMediaPrompt(HWND hwnd, IUnknown *punkEnableModless, int nDrive, LPCWSTR pwzDrive, BOOL fOfferToFormat, DWORD dwError, UINT wFunc, BOOL * pfRetry)
  2739. {
  2740.     BOOL fDiskHasMedia = TRUE;  // Assume yes
  2741.     *pfRetry = FALSE;
  2742.     TraceMsg(TF_FUNC, "DOS Extended error %X", dwError);
  2743.     // BUGBUG, flash (ROM?) drives return a different error code here
  2744.     // that we need to map to not formatted, talk to robwi...
  2745.     // Is it true that it's not ready or we can't format it?
  2746.     if ((dwError == ERROR_NOT_READY) || !fOfferToFormat)
  2747.     {
  2748.         // Yes, so do the disk insert w/o offering to format.
  2749.         fDiskHasMedia = FALSE;
  2750.         // drive not ready (no disk in the drive)
  2751.         if (hwnd &&
  2752.             (IDRETRY == MessageBoxDiskHelper(HINST_THISDLL, hwnd, punkEnableModless, IDS_DRIVENOTREADY, (IDS_FILEERROR + wFunc),
  2753.                             (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_RETRYCANCEL), TRUE, (DWORD)(nDrive + TEXT('A')))))
  2754.         {
  2755.             *pfRetry = TRUE;    // The user wants to try again, bless their heart.
  2756.         }
  2757.         else
  2758.         {
  2759.             // The user was informed that media isn't present and they basically
  2760.             // informed us to cancel the operation.
  2761.             *pfRetry = FALSE;
  2762.         }
  2763.     }
  2764.     else if ((dwError == ERROR_GEN_FAILURE) ||
  2765.         (dwError == ERROR_UNRECOGNIZED_MEDIA) ||
  2766.         (dwError == ERROR_UNRECOGNIZED_VOLUME))
  2767.     {
  2768.         // general failue (disk not formatted)
  2769.         if (hwnd &&
  2770.             (IDYES == MessageBoxDiskHelper(HINST_THISDLL, hwnd, punkEnableModless, IDS_UNFORMATTED, (IDS_FILEERROR + wFunc),
  2771.                         (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_YESNO), TRUE, (DWORD)(nDrive + TEXT('A')))))
  2772.         {
  2773.             if (hwnd)
  2774.                 IUnknown_EnableModless(punkEnableModless, FALSE);
  2775.             UINT uiFormat = _SHFormatDrive(hwnd, nDrive, SHFMT_ID_DEFAULT, 0);
  2776.             if (hwnd)
  2777.                 IUnknown_EnableModless(punkEnableModless, TRUE);
  2778.             switch (uiFormat)
  2779.             {
  2780.             case SHFMT_CANCEL:
  2781.                 *pfRetry = FALSE;
  2782.                 fDiskHasMedia = FALSE;
  2783.                 break;
  2784.             case SHFMT_ERROR:
  2785.             case SHFMT_NOFORMAT:
  2786.                 fDiskHasMedia = FALSE;  // We still don't have a formatted drive
  2787.                 if (hwnd)
  2788.                 {
  2789.                     MessageBoxDiskHelper(HINST_THISDLL, hwnd, punkEnableModless, IDS_NOFMT, (IDS_FILEERROR + wFunc), 
  2790.                                 (MB_SETFOREGROUND | MB_ICONEXCLAMATION | MB_OK), TRUE, (DWORD)(nDrive + TEXT('A')));
  2791.                     *pfRetry = TRUE;
  2792.                 }
  2793.                 else
  2794.                     *pfRetry = FALSE;   // If we can't display UI, no need to try again.
  2795.                 break;
  2796.             default:
  2797.                 // Disk should now be formatted, verify
  2798.                 *pfRetry = TRUE;
  2799.                 fDiskHasMedia = TRUE;
  2800.                 break;
  2801.             }
  2802.         }
  2803.         else
  2804.         {
  2805.             *pfRetry = FALSE;   // If we can't display UI, or no need to try again.
  2806.             fDiskHasMedia = FALSE;  // The user either wasn't given the option of formatting or decided not to format.
  2807.         }
  2808.     }
  2809.     else
  2810.     {
  2811.         if (hwnd)
  2812.         {
  2813.             MessageBoxDiskHelper(HINST_THISDLL, hwnd, punkEnableModless, IDS_NOSUCHDRIVE, (IDS_FILEERROR + wFunc),
  2814.                         (MB_SETFOREGROUND | MB_ICONHAND), TRUE, (DWORD)(nDrive + TEXT('A')));
  2815.             *pfRetry = FALSE;
  2816.             fDiskHasMedia = FALSE;
  2817.         }
  2818.         else
  2819.         {
  2820.             *pfRetry = FALSE;
  2821.             fDiskHasMedia = FALSE;
  2822.         }
  2823.     }
  2824.     return fDiskHasMedia;
  2825. }
  2826. BOOL CheckDiskForMedia(HWND hwnd, IUnknown *punkEnableModless, int nDrive, LPCWSTR pwzDrive, UINT wFunc, BOOL * pfRetry)
  2827. {
  2828.     BOOL fDiskHasMedia = TRUE;  // Assume yes because of the fall thru case. (Path Exists)
  2829.     *pfRetry = FALSE;   // If we fall thru and the destination path exists, don't retry.
  2830.     // BUGBUG, we need to do the find first here instead of GetCurrentDirectory()
  2831.     // because redirected devices (network, cdrom) do not actually hit the disk
  2832.     // on the GetCurrentDirectory() call (dos busted)
  2833.     if (DRIVE_CDROM == _RealDriveType(nDrive, FALSE))   // Is it a CD-ROM Drive?
  2834.     {
  2835.         // Is the CD not in and the caller wants UI?
  2836.         if (!PathFileExistsW(pwzDrive) && hwnd)
  2837.             fDiskHasMedia = DoMediaPrompt(hwnd, punkEnableModless, nDrive, pwzDrive, wFunc, FALSE, GetLastError(), pfRetry);
  2838.     }
  2839.     else
  2840.     {
  2841.         int iIsNet;
  2842.         // Is this some kind of net drive?
  2843.         if ((_DriveType(nDrive) != DRIVE_FIXED) && (FALSE != (iIsNet = _IsNetDrive(nDrive))))
  2844.         {
  2845.             // Yes, so see if the connection still exists.
  2846.             if (iIsNet == 1)
  2847.             {
  2848.                 // Yes, it exists so we are done.
  2849.                 *pfRetry = FALSE;
  2850.                 fDiskHasMedia = TRUE;
  2851.             }
  2852.             else
  2853.             {
  2854.                 // No, so try to restore the connection.
  2855.                 DWORD dwError = WNetRestoreConnectionWrapW(hwnd, pwzDrive);
  2856.                 if (dwError != WN_SUCCESS)
  2857.                 {
  2858.                     // Restoring the connection failed, so prepare to tell the 
  2859.                     // caller the bad news and then display UI to the user if appropriate.
  2860.                     *pfRetry = FALSE;
  2861.                     fDiskHasMedia = TRUE;
  2862.                     if (!(dwError == WN_CANCEL || dwError == ERROR_CONTINUE) && hwnd)
  2863.                     {
  2864.                         WCHAR wzMessage[128];
  2865.                         WNetGetLastErrorWrapW(&dwError, wzMessage, ARRAYSIZE(wzMessage), NULL, 0);
  2866.                         IUnknown_EnableModless(punkEnableModless, FALSE);   // Cover me, I'm going to do UI
  2867.                         MessageBoxHelper(HINST_THISDLL, hwnd, punkEnableModless, wzMessage, (IDS_FILEERROR + wFunc),
  2868.                                         (MB_OK | MB_ICONEXCLAMATION | MB_SETFOREGROUND));
  2869.                         IUnknown_EnableModless(punkEnableModless, TRUE);
  2870.                     }
  2871.                 }
  2872.                 else
  2873.                 {
  2874.                     // Restoring the connection worked.
  2875.                     *pfRetry = FALSE;
  2876.                     fDiskHasMedia = TRUE;
  2877.                 }
  2878.             }
  2879.         }
  2880.         else
  2881.         {
  2882.             // No, so see if it's a floppy or unformatted drive.
  2883.             // Is the destination reachable?
  2884.             if (!PathFileExistsW(pwzDrive))
  2885.             {
  2886.                 // No so ask the user about formatting or inserting the media.
  2887.                 fDiskHasMedia = DoMediaPrompt(hwnd, punkEnableModless, nDrive, pwzDrive, TRUE, GetLastError(), wFunc, pfRetry);
  2888.             }
  2889.             else
  2890.             {
  2891.                 ASSERT(FALSE == *pfRetry);      // Make sure the defaults are still true.
  2892.                 ASSERT(TRUE == fDiskHasMedia);
  2893.             }
  2894.         }
  2895.     }
  2896.     return fDiskHasMedia;
  2897. }
  2898. // FUNCTION: SHCheckDiskForMedia
  2899. //
  2900. // DESCRIPTION:
  2901. // note: this has the side effect of setting the
  2902. // current drive to the new disk if it is successful
  2903. //
  2904. // The default impl being ansi sucks, but we need to 
  2905. // see if the unicode versions of the WNet APIs are impl on Win95.
  2906. //
  2907. // PARAMETERS:
  2908. // hwnd - NULL means no UI will be displayed.  Non-NULL means
  2909. // punkEnableModless - Make caller modal during UI. (OPTIONAL)
  2910. // pszPath - Path that needs verification.
  2911. // wFunc - Type of operation (FO_MOVE, FO_COPY, FO_DELETE, FO_RENAME - shellapi.h)
  2912. //
  2913. // Keep the return value a strict TRUE/FALSE because some callers rely on it.
  2914. BOOL SHCheckDiskForMediaW(HWND hwnd, IUnknown *punkEnableModless, LPCWSTR pwzPath, UINT wFunc)
  2915. {
  2916.     BOOL fDiskHasMedia = FALSE;  // Assume yes
  2917.     int nDrive = PathGetDriveNumberW(pwzPath);
  2918.     ASSERT(nDrive != -1);       // should not get a UNC here
  2919.     if (nDrive != -1)   // not supported on UNCs
  2920.     {
  2921.         WCHAR wzDrive[10];
  2922.         PathBuildRootW(wzDrive, nDrive);
  2923.         BOOL fKeepRetrying;
  2924.         do
  2925.         {
  2926.             fDiskHasMedia = CheckDiskForMedia(hwnd, punkEnableModless, nDrive, wzDrive, wFunc, &fKeepRetrying);          
  2927.         }
  2928.         while (fKeepRetrying);
  2929.     }
  2930.     return fDiskHasMedia;
  2931. }
  2932. BOOL SHCheckDiskForMediaA(HWND hwnd, IUnknown *punkEnableModless, LPCSTR pszPath, UINT wFunc)
  2933. {
  2934.     WCHAR wzPath[MAX_PATH];
  2935.     SHAnsiToUnicode(pszPath, wzPath, ARRAYSIZE(wzPath));
  2936.     return SHCheckDiskForMediaW(hwnd, punkEnableModless, wzPath, wFunc);
  2937. }
  2938. HRESULT _FaultInIEFeature(HWND hwnd, uCLSSPEC *pclsspec, QUERYCONTEXT *pQ, DWORD dwFlags);
  2939. struct HELPCONT_FILE 
  2940. {
  2941.     const   CHAR *pszFile;
  2942.     int     nLength;
  2943. } g_helpConts[] =
  2944. {
  2945. { "iexplore.chm", ARRAYSIZE("iexplore.chm") - 1 },
  2946. { "iexplore.hlp", ARRAYSIZE("iexplore.hlp") - 1 },
  2947. { "update.chm", ARRAYSIZE("update.chm") - 1 },
  2948. { "update.cnt", ARRAYSIZE("update.cnt") - 1 },
  2949. { "users.chm", ARRAYSIZE("users.chm") - 1 },
  2950. { "users.hlp", ARRAYSIZE("users.hlp") - 1 },
  2951. { "accessib.chm", ARRAYSIZE("accessib.chm") - 1 },
  2952. { "ieeula.chm", ARRAYSIZE("ieeula.chm") - 1 },
  2953. { "iesupp.chm", ARRAYSIZE("iesupp.chm") - 1 },
  2954. { "msnauth.hlp", ARRAYSIZE("msnauth.hlp") - 1 },
  2955. { "ratings.chm", ARRAYSIZE("ratings.chm") - 1 },
  2956. { "ratings.hlp", ARRAYSIZE("ratings.hlp") - 1 }
  2957. };
  2958. HRESULT _JITHelpFileA(HWND hwnd, LPCSTR pszPath)
  2959. {
  2960.     if (!pszPath)
  2961.         return S_OK;
  2962.     HRESULT hr = S_OK;
  2963.     BOOL bMustJIT = FALSE;
  2964.     CHAR *pszFile = PathFindFileName(pszPath);
  2965.   
  2966.     for (int i = 0; i < ARRAYSIZE(g_helpConts); i++)
  2967.     {
  2968.         if (StrCmpNIA(g_helpConts[i].pszFile, pszFile, g_helpConts[i].nLength) == 0)
  2969.         {
  2970.             bMustJIT = TRUE;
  2971.             break;
  2972.         }
  2973.     }
  2974.     if (bMustJIT)
  2975.     {
  2976.         uCLSSPEC ucs;
  2977.         QUERYCONTEXT qc = { 0 };
  2978.         
  2979.         ucs.tyspec = TYSPEC_CLSID;
  2980.         ucs.tagged_union.clsid = CLSID_IEHelp;
  2981.         hr = _FaultInIEFeature(hwnd, &ucs, &qc, FIEF_FLAG_FORCE_JITUI);
  2982.     }
  2983.     return hr;
  2984. }
  2985. HRESULT _JITHelpFileW(HWND hwnd, LPCWSTR pwszFile)
  2986. {
  2987.     if (!pwszFile)
  2988.         return S_OK;
  2989.     CHAR szFile[MAX_PATH];
  2990.     SHUnicodeToAnsi(pwszFile, szFile, ARRAYSIZE(szFile));
  2991.     return _JITHelpFileA(hwnd, szFile);
  2992. }
  2993. BOOL _JITSetLastError(HRESULT hr)
  2994. {
  2995.     DWORD err;
  2996.     
  2997.     if (HRESULT_FACILITY(hr) == FACILITY_WIN32)
  2998.     {
  2999.         err = HRESULT_CODE(hr);
  3000.     }
  3001.     else if (hr == E_ACCESSDENIED)
  3002.     {
  3003.         err = ERROR_ACCESS_DENIED;
  3004.     }
  3005.     else
  3006.     {
  3007.         err = ERROR_FILE_NOT_FOUND;
  3008.     }
  3009.     SetLastError(err);
  3010.     return FALSE;
  3011. }
  3012. HWND SHHtmlHelpOnDemandW(HWND hwnd, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage, BOOL bUseML)
  3013. {
  3014.     return SUCCEEDED(_JITHelpFileW(hwnd, pszFile)) ?
  3015.                 (bUseML ? MLHtmlHelpW(hwnd, pszFile, uCommand, dwData, dwCrossCodePage) : 
  3016.                           HtmlHelpW(hwnd, pszFile, uCommand, dwData)) :
  3017.                 NULL;
  3018. }
  3019. HWND SHHtmlHelpOnDemandA(HWND hwnd, LPCSTR pszFile, UINT uCommand, DWORD_PTR dwData, DWORD dwCrossCodePage, BOOL bUseML)
  3020. {
  3021.     return SUCCEEDED(_JITHelpFileA(hwnd, pszFile)) ?
  3022.                 (bUseML ? MLHtmlHelpA(hwnd, pszFile, uCommand, dwData, dwCrossCodePage) : 
  3023.                           HtmlHelpA(hwnd, pszFile, uCommand, dwData)) :
  3024.                 NULL;
  3025. }
  3026. BOOL SHWinHelpOnDemandW(HWND hwnd, LPCWSTR pszFile, UINT uCommand, DWORD_PTR dwData, BOOL bUseML)
  3027. {
  3028.     HRESULT hr;
  3029.     return SUCCEEDED(hr = _JITHelpFileW(hwnd, pszFile)) ?
  3030.                 (bUseML ? MLWinHelpW(hwnd, pszFile, uCommand, dwData) : 
  3031.                           WinHelpWrapW(hwnd,pszFile, uCommand, dwData)) :
  3032.                 _JITSetLastError(hr);
  3033. }
  3034. BOOL SHWinHelpOnDemandA(HWND hwnd, LPCSTR pszFile, UINT uCommand, DWORD_PTR dwData, BOOL bUseML)
  3035. {
  3036.     HRESULT hr;
  3037.     return SUCCEEDED(hr = _JITHelpFileA(hwnd, pszFile)) ?
  3038.                 (bUseML ? MLWinHelpA(hwnd, pszFile, uCommand, dwData) : 
  3039.                           WinHelpA(hwnd,pszFile, uCommand, dwData)) :
  3040.                 _JITSetLastError(hr);
  3041. }
  3042. /*****************************************************************************
  3043.     FUNCTION: SHPersistDataObject
  3044.     DESCRIPTION:
  3045.         This funciton exists for IDataObjects that don't want OLE to use the
  3046.     default IDataObject implementation if OleFlushClipboard is called.
  3047.     How to use:
  3048.     1. This function should be called when the IDataObject::GetData() method
  3049.        is called with (FORMATETC.cfFormat ==
  3050.        RegisterClipboardFormat(CFSTR_PERSISTEDDATAOBJECT)).
  3051.     2. OleFlushClipboard copies pMedium to it's own implementation of IDataObject
  3052.        which doesn't work with the lindex parameter of FORMATETC or for private interfaces.
  3053.     3. OLE or the IDropTarget calls SHLoadPersistedDataObject().  The first
  3054.        param will be OLE's IDataObject impl, and the second param (out param)
  3055.        will be the original IDataObject.  The new IDataObject will contains
  3056.        the original state as long as it correctly implemented IPersistStream.
  3057.     PARAMETERS:
  3058.         pdoToPersist - This is the original IDataObject that implements IPersistStream.
  3059.         pMedium - This is contain the persisted state of this object.
  3060.                   CFSTR_PERSISTEDDATAOBJECT can be used to read the data.
  3061. *****************************************************************************/
  3062. #define SIZE_PERSISTDATAOBJECT  (10 * 1024)
  3063. STDAPI SHPersistDataObject(/*IN*/ IDataObject * pdoToPersist, /*OUT*/ STGMEDIUM * pMedium)
  3064. {
  3065.     HRESULT hr = E_NOTIMPL;
  3066.     // We shipped IE 5.0 RTM with this and SHLoadPersistedDataObject().  We removed
  3067.     // the code after the OLE32.DLL guys moved the functionality into ole32.dll.
  3068.     // See the "OleClipboardPersistOnFlush" clipboard format.
  3069.     return hr;
  3070. }
  3071. /*****************************************************************************
  3072.     FUNCTION: SHLoadPersistedDataObject
  3073.     DESCRIPTION:
  3074.         This funciton exists for IDataObjects that don't want OLE to use the
  3075.     default IDataObject implementation if OleFlushClipboard is called.
  3076.     How to use:
  3077.     1. SHPersistDataObject() was called when the IDataObject::GetData() method
  3078.        is called with (FORMATETC.cfFormat == RegisterClipboardFormat(CFSTR_PERSISTEDDATAOBJECT)).
  3079.     2. OleFlushClipboard copies pMedium to it's own implementation of IDataObject
  3080.        which doesn't work with the lindex parameter of FORMATETC or for private interfaces.
  3081.     3. OLE or the IDropTarget calls SHLoadPersistedDataObject().  The first
  3082.        param will be OLE's IDataObject impl, and the second param (out param)
  3083.        will be the original IDataObject.  The new IDataObject will contains
  3084.        the original state as long as it correctly implemented IPersistStream.
  3085.     PARAMETERS:
  3086.         pdo - This is OLE's IDataObject.
  3087.         ppdoToPersist - This is the original IDataObject or equal to pdo if
  3088.                         un-serializing the object didn't work.  It always has
  3089.                         it's own ref.
  3090. *****************************************************************************/
  3091. STDAPI SHLoadPersistedDataObject(/*IN*/ IDataObject * pdo, /*OUT*/ IDataObject ** ppdoToPersist)
  3092. {
  3093.     // See SHPersistDataObject() for details
  3094.     return pdo->QueryInterface(IID_IDataObject, (void **) ppdoToPersist);
  3095. }
  3096. #ifndef SMTO_NOTIMEOUTIFNOTHUNG
  3097. #define SMTO_NOTIMEOUTIFNOTHUNG 0x0008
  3098. #endif
  3099. LWSTDAPI_(LRESULT) SHSendMessageBroadcastA(UINT uMsg, WPARAM wParam, LPARAM lParam)
  3100. {
  3101.     ULONG_PTR lres = 0;
  3102.     DWORD dwFlags = SMTO_ABORTIFHUNG;
  3103.     if (g_bRunningOnNT5OrHigher)
  3104.         dwFlags |= SMTO_NOTIMEOUTIFNOTHUNG;
  3105.     SendMessageTimeoutA(HWND_BROADCAST, uMsg, wParam, lParam, dwFlags, 30 * 1000, &lres);
  3106.     return (LRESULT) lres;
  3107. }
  3108. LWSTDAPI_(LRESULT) SHSendMessageBroadcastW(UINT uMsg, WPARAM wParam, LPARAM lParam)
  3109. {
  3110.     ULONG_PTR lres = 0;
  3111.     DWORD dwFlags = SMTO_ABORTIFHUNG;
  3112.     if (g_bRunningOnNT5OrHigher)
  3113.         dwFlags |= SMTO_NOTIMEOUTIFNOTHUNG;
  3114.     SendMessageTimeoutWrapW(HWND_BROADCAST, uMsg, wParam, lParam, dwFlags, 30 * 1000, &lres);
  3115.     return (LRESULT) lres;
  3116. }
  3117. #ifdef UNIX
  3118. /*****************************************************************************
  3119.     IEUNIX : Wrapper to get rid of undefined symbol on UNIX.
  3120. *****************************************************************************/
  3121. STDAPI_(BOOL) SHCreateThreadPriv(
  3122.     LPTHREAD_START_ROUTINE pfnThreadProc,
  3123.     void *pvData,
  3124.     DWORD dwFlags,                          // CTF_*
  3125.     LPTHREAD_START_ROUTINE pfnCallback)     OPTIONAL
  3126. {
  3127.     return SHCreateThread(pfnThreadProc, pvData, dwFlags, pfnCallback);
  3128. }
  3129. #endif
  3130. #define MODULE_NAME_SIZE    128
  3131. #define MODULE_VERSION_SIZE  15
  3132. //
  3133. //  If version is NULL, then we do it for all versions of the app.
  3134. //
  3135. //  If version begins with MAJORVERSION, then we check only the major version.
  3136. //  (CH_MAJORVERSION is the char version of same.)
  3137. //
  3138. #define MAJORVERSION TEXT("1")
  3139. #define CH_MAJORVERSION TEXT('1')
  3140. typedef struct tagAPPCOMPAT
  3141. {
  3142.     LPCTSTR pszModule;
  3143.     LPCTSTR pszVersion;
  3144.     DWORD  dwFlags;
  3145. } APPCOMPAT, *LPAPPCOMPAT;
  3146. typedef struct tagAPPCLASS
  3147. {
  3148.     LPCTSTR pstzWndClass;
  3149.     DWORD   dwFlags;
  3150. } APPCLASS, *LPAPPCLASS;
  3151. typedef struct tagWNDDAT
  3152. {
  3153.     const APPCLASS *rgAppClass;
  3154.     DWORD      cAppClass;
  3155.     DWORD      dwPid;
  3156.     int        irgFound;
  3157. } WNDDAT, *LPWNDDAT;
  3158. BOOL CALLBACK EnumWnd (HWND hwnd, LPARAM lParam)
  3159. {
  3160.     TCHAR sz[256];
  3161.     DWORD dwPid;
  3162.     int cch;
  3163.     LPWNDDAT pwd = (LPWNDDAT) lParam;
  3164.     if (GetClassName (hwnd, sz, ARRAYSIZE(sz)))
  3165.     {
  3166.         cch = lstrlen (sz);
  3167.         for (DWORD irg = 0; irg < pwd->cAppClass; irg++)
  3168.         {
  3169.             ASSERT(lstrlen(&(pwd->rgAppClass[irg].pstzWndClass[1])) == (int) pwd->rgAppClass[irg].pstzWndClass[0]);
  3170.             if (lstrncmp (sz, &(pwd->rgAppClass[irg].pstzWndClass[1]),
  3171.                  min(cch, (int) pwd->rgAppClass[irg].pstzWndClass[0])) == 0)
  3172.             {
  3173.                 GetWindowThreadProcessId(hwnd, &dwPid);
  3174.                 if (dwPid == pwd->dwPid)
  3175.                 {
  3176.                     pwd->irgFound = irg;
  3177.                     return FALSE;
  3178.                 }
  3179.             }
  3180.         }
  3181.     }
  3182.     return TRUE;
  3183. }
  3184. BOOL _IsAppCompatVersion(LPTSTR szModulePath, LPCTSTR pszVersionMatch)
  3185. {
  3186.     if (pszVersionMatch == NULL)            // Wildcard - match all versions
  3187.     {
  3188.         return TRUE;
  3189.     }
  3190.     else
  3191.     {
  3192.         CHAR  chBuffer[4096]; // hopefully this is enough... Star Office 5 requires 3172
  3193.         TCHAR* pszVersion = NULL;
  3194.         UINT  cb;
  3195.         DWORD  dwHandle;
  3196.         // get module version here!
  3197.         //
  3198.         //  Some apps use codepage 0x04E4 (1252 = CP_USASCII) and some use
  3199.         //  codepage 0x04B0 (1200 = CP_UNICODE).
  3200.         //
  3201.         // ...and then Star Office 5.00 uses 0407 instead of 0409.
  3202.         //
  3203.         cb = GetFileVersionInfoSize(szModulePath, &dwHandle);
  3204.         if (cb <= ARRAYSIZE(chBuffer) &&
  3205.             GetFileVersionInfo(szModulePath, dwHandle, ARRAYSIZE(chBuffer), (void *)chBuffer) &&
  3206.             (VerQueryValue((void *)chBuffer, TEXT("\StringFileInfo\040904E4\ProductVersion"), (void **) &pszVersion, &cb) ||
  3207.              VerQueryValue((void *)chBuffer, TEXT("\StringFileInfo\040704E4\ProductVersion"), (void **) &pszVersion, &cb) ||
  3208.              VerQueryValue((void *)chBuffer, TEXT("\StringFileInfo\040904B0\ProductVersion"), (void **) &pszVersion, &cb)))
  3209.         {
  3210.             DWORD_PTR cchCmp = 0;
  3211.             if (pszVersionMatch[0] == CH_MAJORVERSION)
  3212.             {
  3213.                 // Truncate at the first comma or period
  3214.                 LPTSTR pszTemp = StrChr(pszVersion, TEXT(','));
  3215.                 if (pszTemp)
  3216.                     *pszTemp = 0;
  3217.                 pszTemp = StrChr(pszVersion, TEXT('.'));
  3218.                 if (pszTemp)
  3219.                     *pszTemp = 0;
  3220.                 pszVersionMatch++;
  3221.             }
  3222.             else
  3223.             {
  3224.                 TCHAR *pch = StrChr(pszVersionMatch, TEXT('*'));
  3225.                 if (pch)
  3226.                 {
  3227.                     cchCmp = pch - pszVersionMatch;
  3228.                 }
  3229.             }
  3230.             if ((cchCmp && StrCmpNI(pszVersion, pszVersionMatch, (int)cchCmp) == 0)
  3231.             || lstrcmpi(pszVersion, pszVersionMatch) == 0)
  3232.             {
  3233.                 DebugMsg(TF_ALWAYS, TEXT("%s ver %s - compatibility hacks enabled"), PathFindFileName(szModulePath), pszVersion);
  3234.                 return TRUE;
  3235.             }
  3236.         }
  3237.     }
  3238.     return FALSE;
  3239. }
  3240. typedef struct {
  3241.     DWORD flag;
  3242.     LPCTSTR psz;
  3243. } FLAGMAP;
  3244. DWORD _GetMappedFlags(HKEY hk, const FLAGMAP *pmaps, DWORD cmaps)
  3245. {
  3246.     DWORD dwRet = 0;
  3247.     for (DWORD i = 0; i < cmaps; i++)
  3248.     {
  3249.         if (NOERROR == SHGetValue(hk, NULL, pmaps[i].psz, NULL, NULL, NULL))
  3250.             dwRet |= pmaps[i].flag;
  3251.     }
  3252.     return dwRet;
  3253. }
  3254. #define ACFMAPPING(acf)     {ACF_##acf, TEXT(#acf)}
  3255. DWORD _GetRegistryCompatFlags(LPTSTR pszModulePath)
  3256. {
  3257.     DWORD dwRet = 0;
  3258.     LPCTSTR pszModule = PathFindFileName(pszModulePath);
  3259.     TCHAR sz[MAX_PATH];
  3260.     HKEY hk;
  3261.     wnsprintf(sz, ARRAYSIZE(sz), TEXT("SOFTWARE\Microsoft\Windows\CurrentVersion\ShellCompatibility\Applications\%s"), pszModule);
  3262.     
  3263.     if (NOERROR == RegOpenKeyEx(HKEY_LOCAL_MACHINE, sz, 0, KEY_QUERY_VALUE, &hk))
  3264.     {   
  3265.         LPCTSTR pszVersion = NULL;
  3266.         DWORD dw = SIZEOF(sz);
  3267.         if (NOERROR == SHGetValue(hk, NULL, TEXT("Version"), NULL, sz, &dw))
  3268.             pszVersion = sz;
  3269.             
  3270.         if (_IsAppCompatVersion(pszModulePath, pszVersion))
  3271.         {
  3272.             static const FLAGMAP rgAcfMaps[] = {
  3273.                 ACFMAPPING(CONTEXTMENU),
  3274.                 ACFMAPPING(CORELINTERNETENUM),
  3275.                 ACFMAPPING(OLDCREATEVIEWWND),
  3276.                 ACFMAPPING(WIN95DEFVIEW),
  3277.                 ACFMAPPING(DOCOBJECT),
  3278.                 ACFMAPPING(FLUSHNOWAITALWAYS),
  3279.                 ACFMAPPING(MYCOMPUTERFIRST),
  3280.                 ACFMAPPING(OLDREGITEMGDN),
  3281.                 ACFMAPPING(LOADCOLUMNHANDLER),
  3282.                 ACFMAPPING(ANSI), 
  3283.                 ACFMAPPING(STAROFFICE5PRINTER),
  3284.                 ACFMAPPING(NOVALIDATEFSIDS),
  3285.                 };
  3286.             dwRet = _GetMappedFlags(hk, rgAcfMaps, ARRAYSIZE(rgAcfMaps));
  3287.         }
  3288.         RegCloseKey(hk);
  3289.     }
  3290.     return dwRet;
  3291. }
  3292. DWORD SHGetAppCompatFlags (DWORD dwFlagsNeeded)
  3293. {
  3294.     static BOOL  bInitialized = FALSE;
  3295.     static DWORD dwCachedProcessFlags = 0;
  3296.     static const APPCOMPAT aAppCompat[] = 
  3297.     {
  3298.         {TEXT("WPWIN7.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM},
  3299.         {TEXT("PRWIN70.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM},
  3300.         {TEXT("PS80.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3301.         {TEXT("QPW.EXE"), TEXT("7.0"), ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDCREATEVIEWWND | ACF_OLDREGITEMGDN},
  3302.         {TEXT("QFINDER.EXE"), NULL, ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3303.         {TEXT("PFIM80.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3304.         {TEXT("UA80.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3305.         {TEXT("PDXWIN32.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3306.         {TEXT("SITEBUILDER.EXE"), NULL, ACF_CONTEXTMENU | ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3307.         {TEXT("HOTDOG4.EXE"), NULL, ACF_DOCOBJECT},
  3308.         {TEXT("RNAAPP.EXE"), NULL, ACF_FLUSHNOWAITALWAYS},
  3309.         //
  3310.         //  PDEXPLO.EXE version "2, 0, 2, 0" requires ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST
  3311.         //  PDEXPLO.EXE version "1, 0, 0, 0" requires ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST
  3312.         //  PDEXPLO.EXE version "3, 0, 0, 1" requires                   ACF_MYCOMPUTERFIRST
  3313.         //  PDEXPLO.EXE version "3, 0, 3, 0" requires                   ACF_MYCOMPUTERFIRST
  3314.         //
  3315.         //  So I'm just going to key off the major versions.
  3316.         //
  3317.         {TEXT("PDEXPLO.EXE"), MAJORVERSION TEXT("2"), ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST},
  3318.         {TEXT("PDEXPLO.EXE"), MAJORVERSION TEXT("1"), ACF_CONTEXTMENU | ACF_MYCOMPUTERFIRST},
  3319.         {TEXT("PDEXPLO.EXE"), MAJORVERSION TEXT("3"), ACF_MYCOMPUTERFIRST | ACF_OLDREGITEMGDN},
  3320.         // SIZEMGR.EXE is part of the PowerDesk 98 suite, so we also key off
  3321.         // only the major version
  3322.         {TEXT("SIZEMGR.EXE"), MAJORVERSION TEXT("3"), ACF_OLDCREATEVIEWWND | ACF_OLDREGITEMGDN},
  3323.         {TEXT("SMARTCTR.EXE"), TEXT("96.0"), ACF_CONTEXTMENU},
  3324.         // new programs, old bugs
  3325.         {TEXT("WPWIN8.EXE"), NULL, ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3326.         {TEXT("PRWIN8.EXE"), NULL, ACF_CORELINTERNETENUM | ACF_OLDREGITEMGDN},
  3327.         {TEXT("UE32.EXE"), TEXT("2.00.0.0"), ACF_OLDREGITEMGDN},
  3328.         {TEXT("PP70.EXE"),NULL, ACF_LOADCOLUMNHANDLER},
  3329.         {TEXT("PP80.EXE"),NULL, ACF_LOADCOLUMNHANDLER},
  3330.         {TEXT("PS80.EXE"),NULL, ACF_OLDREGITEMGDN},
  3331.         {TEXT("ABCMM.EXE"),NULL,ACF_LOADCOLUMNHANDLER},
  3332.         // We've found versions 8.0.0.153 and 8.0.0.227, so just use 8.*
  3333.         {TEXT("QPW.EXE"), MAJORVERSION TEXT("8"), ACF_CORELINTERNETENUM | ACF_OLDCREATEVIEWWND},
  3334.         {TEXT("CORELDRW.EXE"), MAJORVERSION TEXT("7"), ACF_OLDREGITEMGDN},
  3335.         {TEXT("FILLER51.EXE"), NULL, ACF_OLDREGITEMGDN},
  3336.         
  3337.         //For Win95 and Win98
  3338.         {TEXT("AUTORUN.EXE"), TEXT("4.10.1998"),ACF_ANSI},
  3339.         {TEXT("AUTORUN.EXE"), TEXT("4.00.950"),ACF_ANSI},
  3340.         //Powerpoint
  3341.         {TEXT("POWERPNT.EXE"), MAJORVERSION TEXT("8"), ACF_WIN95SHLEXEC},
  3342.    
  3343.         //Money 99
  3344.         {TEXT("MSMONEY.EXE"), MAJORVERSION TEXT("7"), ACF_WIN95SHLEXEC},
  3345.         //Star Office 5.0
  3346.         {TEXT("soffice.EXE"), MAJORVERSION TEXT("5"), ACF_STAROFFICE5PRINTER},
  3347.         // All of the "Corel WordPerfect Office 2000" suite apps need ACF_WIN95DEFVIEW. Since the shipping
  3348.         // version (9.0.0.528) as well as their SR1 release (9.0.0.588) are both broken, we key off 
  3349.         // of the major version
  3350.         {TEXT("WPWIN9.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3351.         {TEXT("QPW.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3352.         {TEXT("PRWIN9.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3353.         {TEXT("DAD9.EXE"), MAJORVERSION TEXT("9"), ACF_WIN95DEFVIEW},
  3354.     };
  3355.     static const APPCLASS aAppClass[] =
  3356.     {
  3357.             // note that the strings here are stz's....
  3358.         {TEXT("x9""bosa_sdm_"),                           ACF_APPISOFFICE | ACF_FOLDERSCUTASLINK},
  3359.         {TEXT("x18""File Open Message Window"),           ACF_APPISOFFICE | ACF_FOLDERSCUTASLINK},
  3360.     };
  3361.     if (dwFlagsNeeded & (ACF_PERPROCESSFLAGS))
  3362.     {
  3363.         if (!bInitialized)
  3364.         {    
  3365.           //
  3366.           //  Do this only for old apps.
  3367.           //
  3368.           //  Once an app marks itself as NT5-compatible, we stop doing
  3369.           //  NT4/Win5 app hacks for it.
  3370.           //
  3371.             if (GetProcessVersion(0) < MAKELONG(0, 5))
  3372.             {
  3373.     
  3374.                 TCHAR  szModulePath[MODULE_NAME_SIZE];
  3375.                 TCHAR* pszModuleName;
  3376.                 int i;
  3377.         
  3378.                 GetModuleFileName(GetModuleHandle(NULL), szModulePath, ARRAYSIZE(szModulePath));
  3379.                 pszModuleName = PathFindFileName(szModulePath);
  3380.         
  3381.                 if (pszModuleName)
  3382.                 {
  3383.                     for (i=0; i < ARRAYSIZE(aAppCompat); i++)
  3384.                     {
  3385.                         if (lstrcmpi(aAppCompat[i].pszModule, pszModuleName) == 0)
  3386.                         {
  3387.                             if (_IsAppCompatVersion(szModulePath, aAppCompat[i].pszVersion))
  3388.                             {
  3389.                                 dwCachedProcessFlags = aAppCompat[i].dwFlags;
  3390.                                 break;
  3391.                             }
  3392.                         }
  3393.                     }
  3394.                     dwCachedProcessFlags |= _GetRegistryCompatFlags(szModulePath);
  3395.                 }
  3396.             }
  3397.             bInitialized = TRUE;
  3398.         }
  3399.     }
  3400.     if ((dwFlagsNeeded & ACF_PERCALLFLAGS) &&
  3401.         !(dwCachedProcessFlags & ACF_KNOWPERPROCESS))
  3402.     {
  3403.         WNDDAT wd;
  3404.         wd.dwPid = GetCurrentProcessId();
  3405.         wd.irgFound = -1;
  3406.         wd.rgAppClass = aAppClass;
  3407.         wd.cAppClass = ARRAYSIZE(aAppClass);
  3408.         EnumWindows (EnumWnd, (LPARAM) &wd);
  3409.         if (wd.irgFound > -1)
  3410.         {
  3411.             dwCachedProcessFlags |= (aAppClass[wd.irgFound].dwFlags);
  3412.         }
  3413.         dwCachedProcessFlags |= ACF_KNOWPERPROCESS;
  3414.     }
  3415.     
  3416.     return dwCachedProcessFlags; 
  3417. }