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

Windows Kernel

Development Platform:

Visual C++

  1. /***************************************************************************
  2.  *  dll.c
  3.  *
  4.  *  Standard DLL entry-point functions 
  5.  *
  6.  ***************************************************************************/
  7. #include "shellprv.h"
  8. #include <ntverp.h>
  9. #include <advpub.h>         // For REGINSTALL
  10. #include "fstreex.h"
  11. #include "ids.h"
  12. #ifdef WINNT
  13. #define INSTALL_WEBFOLDERS 1
  14. #include <msi.h>            // For OfficeWebFolders_Install()
  15. void OfficeWebFolders_Install(void);
  16. #endif // WINNT
  17. extern "C" STDAPI_(void) Control_FillCache_RunDLL( HWND hwndStub, HINSTANCE hAppInstance, LPSTR pszCmdLine, int nCmdShow );
  18. // DllGetVersion - New for IE 4.0 shell integrated mode
  19. //
  20. // All we have to do is declare this puppy and CCDllGetVersion does the rest
  21. //
  22. DLLVER_DUALBINARY(VER_PRODUCTVERSION_DW, VER_PRODUCTBUILD_QFE);
  23. HRESULT CallRegInstall(LPCSTR szSection)
  24. {
  25.     HRESULT hr = E_FAIL;
  26.     HINSTANCE hinstAdvPack = LoadLibrary(TEXT("ADVPACK.DLL"));
  27.     if (hinstAdvPack)
  28.     {
  29.         REGINSTALL pfnri = (REGINSTALL)GetProcAddress(hinstAdvPack, "RegInstall");
  30.         if (pfnri)
  31.         {
  32.             char szShdocvwPath[MAX_PATH];
  33.             STRENTRY seReg[] = {
  34.                 { "SHDOCVW_PATH", szShdocvwPath },
  35. #ifdef WINNT                
  36.                 { "25", "%SystemRoot%" },
  37.                 { "11", "%SystemRoot%\system32" },
  38. #endif
  39.             };
  40.             STRTABLE stReg = { ARRAYSIZE(seReg), seReg };
  41.             // Get the location of shdocvw.dll
  42. #ifdef WINNT
  43.             lstrcpyA(szShdocvwPath, "%SystemRoot%\system32");
  44. #else
  45.             GetSystemDirectory(szShdocvwPath, SIZECHARS(szShdocvwPath));
  46. #endif
  47.             PathAppendA(szShdocvwPath, "shdocvw.dll");
  48.           
  49.             hr = pfnri(g_hinst, szSection, &stReg);
  50.         }
  51.         // since we only do this from DllInstall() don't load and unload advpack over and over
  52.         // FreeLibrary(hinstAdvPack);
  53.     }
  54.     return hr;
  55. }
  56. BOOL UnregisterTypeLibrary(const CLSID* piidLibrary)
  57. {
  58.     TCHAR szScratch[GUIDSTR_MAX];
  59.     HKEY hk;
  60.     BOOL f = FALSE;
  61.     // convert the libid into a string.
  62.     //
  63.     SHStringFromGUID(*piidLibrary, szScratch, ARRAYSIZE(szScratch));
  64.     if (RegOpenKey(HKEY_CLASSES_ROOT, TEXT("TypeLib"), &hk) == ERROR_SUCCESS) 
  65.     {
  66.         f = RegDeleteKey(hk, szScratch);
  67.         RegCloseKey(hk);
  68.     }
  69.     
  70.     return f;
  71. }
  72. HRESULT Shell32RegTypeLib(void)
  73. {
  74.     TCHAR szPath[MAX_PATH];
  75.     WCHAR wszPath[MAX_PATH];
  76.     // Load and register our type library.
  77.     //
  78.     GetModuleFileName(HINST_THISDLL, szPath, ARRAYSIZE(szPath));
  79.     SHTCharToUnicode(szPath, wszPath, ARRAYSIZE(wszPath));
  80.     ITypeLib *pTypeLib;
  81.     HRESULT hr = LoadTypeLib(wszPath, &pTypeLib);
  82.     if (SUCCEEDED(hr))
  83.     {
  84.         // call the unregister type library as we had some old junk that
  85.         // was registered by a previous version of OleAut32, which is now causing
  86.         // the current version to not work on NT...
  87.         UnregisterTypeLibrary(&LIBID_Shell32);
  88.         hr = RegisterTypeLib(pTypeLib, wszPath, NULL);
  89.         if (FAILED(hr))
  90.         {
  91.             TraceMsg(TF_WARNING, "SHELL32: RegisterTypeLib failed (%x)", hr);
  92.         }
  93.         pTypeLib->Release();
  94.     }
  95.     else
  96.     {
  97.         TraceMsg(TF_WARNING, "SHELL32: LoadTypeLib failed (%x)", hr);
  98.     }
  99.     return hr;
  100. }
  101. STDAPI CreateShowDesktopOnQuickLaunch()
  102. {
  103.     // delete the "_Current Item" key used for tip rotation in welcome.exe on every upgrade
  104.     HKEY hkey;
  105.     if ( ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER, TEXT("Software\Microsoft\Windows NT\CurrentVersion\Setup\Welcome"), 0, MAXIMUM_ALLOWED, &hkey ) )
  106.     {
  107.        RegDeleteValue(hkey, TEXT("_Current Item"));
  108.        RegCloseKey(hkey);
  109.     }
  110.     // create the "Show Desktop" icon in the quick launch tray
  111.     TCHAR szPath[MAX_PATH];
  112.     if ( SHGetSpecialFolderPath(NULL, szPath, CSIDL_APPDATA, TRUE) )
  113.     {
  114.         TCHAR szQuickLaunch[MAX_PATH];
  115.         LoadString(g_hinst, IDS_QUICKLAUNCH, szQuickLaunch, ARRAYSIZE(szQuickLaunch));
  116.         if ( PathAppend( szPath, szQuickLaunch ) )
  117.         {
  118.             WritePrivateProfileSection( TEXT("Shell"), TEXT("Command=2IconFile=explorer.exe,3"), szPath );
  119.             WritePrivateProfileSection( TEXT("Taskbar"), TEXT("Command=ToggleDesktop"), szPath );
  120.             return S_OK;
  121.         }
  122.     }
  123.     return E_FAIL;
  124. }
  125. STDAPI Favorites_Install(BOOL bInstall);
  126. STDAPI RecentDocs_Install(BOOL bInstall);
  127. STDAPI CDeskHtmlProp_RegUnReg(BOOL bInstall);
  128. #ifdef WINNT
  129. STDAPI_(BOOL) ApplyRegistrySecurity();
  130. #endif
  131. STDAPI_(void) FixPlusIcons();
  132. STDAPI_(void) CleanupFileSystem();
  133. #define KEEP_FAILURE(hrSum, hrLast) if (FAILED(hrLast)) hrSum = hrLast;
  134. STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine)
  135. {
  136.     HRESULT hrTemp, hr = S_OK;
  137.     // 99/05/03 vtan: If you're reading this section then you are considering
  138.     // adding code to the registration/installation of shell32.dll. There are
  139.     // now 3 schemes to accomplish this task:
  140.     // 1. IE4UINIT.EXE
  141.     //      Check HKLMSoftwareMicrosoftActive SetupInstalled Components{89820200-ECBD-11cf-8B85-00AA005B4383}
  142.     //      This says that if there is a new version of IE5 to launch ie4uinit.exe.
  143.     //      You can add code to this executable if you wish. You need to enlist in
  144.     //      the setup project on \trangoslmadd using "ieenlist setup"
  145.     // 2. REGSVR32.EXE /n /i:U shell32.dll
  146.     //      Check HKLMSoftwareMicrosoftActive SetupInstalled Components{89820200-ECBD-11cf-8B85-00AA005B4340}
  147.     //      This is executed using the same scheme as IE4UINIT.EXE except that the
  148.     //      executable used is regsvr32.exe with a command line passed to
  149.     //      shell32!DllInstall. Add your code in the section for "U" below.
  150.     //      If you put the code in the section which is NOT "U" then your
  151.     //      code is executed at GUI setup and any changes you make to HKCU
  152.     //      go into the default user (template). Be careful when putting
  153.     //      things here as winlogon.exe (or some other process) may put
  154.     //      your changes into a user profile unconditionally.
  155.     // 3. HIVEUSD.INX
  156.     //      Checks NT build numbers and does command based on the previous build
  157.     //      number and the current build number only executing the changes between
  158.     //      the builds. If you wish to add something using this method, currently
  159.     //      you have to enlist in the setup project on \rastamanntwin using
  160.     //      "enlist -fgs \rastamanntwin -p setup". To find hiveusd.inx go to
  161.     //      ntprivatesetupinfwin4inf. Add the build number which the delta
  162.     //      is required and a command to launch %SystemRoot%System32shmgrate.exe
  163.     //      with one or two parameters. The first parameter tells what command to
  164.     //      execute. The second parameter is optional. shmgrate.exe then finds
  165.     //      shell32.dll and calls shell32!FirstUserLogon. Code here is for upgrading
  166.     //      HKCU user profiles from one NT build to another NT build.
  167.     //      Code is executed in the process context shmgrate.exe and is executed
  168.     //      at a time where no UI is possible. Always use HKLMSoftwareMicrosoft
  169.     //      Windows NTCurrentVersionImage File Execution OptionsDebugger with
  170.     //      "-d".
  171.     // Schemes 1 and 2 work on either Win9x or WinNT but have the sometimes
  172.     // unwanted side effect of ALWAYS getting executed on version upgrades.
  173.     // Scheme 3 only gets executed on build number deltas. Because schemes 1
  174.     // and 2 are always executed, if a user changes (or deletes) that setting
  175.     // it will always get put back. Not so with scheme 3.
  176.     // Ideally, the best solution is have an internal shell32 build number
  177.     // delta scheme which determines the from build and the to build and does
  178.     // a similar mechanism to what hiveusd.inx and shmgrate.exe do. This
  179.     // would probably involve either a common installation function (such as
  180.     // FirstUserLogon()) which is called differently from Win9x and WinNT or
  181.     // common functions to do the upgrade and two entry points (such as
  182.     // FirstUserLogonNT() and FirstUserLogonWin9X().
  183.     if (bInstall)
  184.     {
  185. #ifdef WINNT
  186.         NT_PRODUCT_TYPE type = NtProductWinNt;
  187.         RtlGetNtProductType(&type);
  188. #endif
  189.         // "U" means it's the per user install call
  190.         if (!StrCmpIW(pszCmdLine, L"U"))
  191.         {
  192.             // NOTE: Code in this segment get run during first login.  We want first
  193.             // login to be as quick as possible so try to minimize this section.
  194.             // Put per-user install stuff here.  Any HKCU registration
  195.             // done here is suspect.  (If you are setting defaults, do
  196.             // so in HKLM and use the SHRegXXXUSValue functions.)
  197.             // WARNING: we get called by the ie4unit.exe (ieunit.inf) scheme:
  198.             //      %11%shell32.dll,NI,U
  199.             // this happens per user, to test this code "regsvr32 /n /i:U shell32.dll"
  200. #ifdef INSTALL_WEBFOLDERS
  201.             // Install the Office WebFolders shell namespace extension per user.
  202.             OfficeWebFolders_Install();
  203. #endif
  204.             // do the work to Install/Uninstall the favorites directory shellext...
  205.             hrTemp = Favorites_Install(bInstall);
  206.             KEEP_FAILURE(hrTemp, hr);
  207.             // do the work to Install/Uninstall the Recent Docs Folder
  208.             hrTemp = RecentDocs_Install(bInstall);
  209.             KEEP_FAILURE(hrTemp, hr);
  210.             hrTemp = CreateShowDesktopOnQuickLaunch();
  211.             KEEP_FAILURE(hrTemp, hr);
  212.             
  213.             // populate the control panel cache
  214.             Control_FillCache_RunDLL(NULL, NULL, NULL, SW_HIDE);
  215.         }
  216.         else
  217.         {
  218.             // Delete any old registration entries, then add the new ones.
  219.             // Keep ADVPACK.DLL loaded across multiple calls to RegInstall.
  220.             // (The inf engine doesn't guarantee DelReg/AddReg order, that's
  221.             // why we explicitly unreg and reg here.)
  222.             //
  223.             hrTemp = CallRegInstall("RegDll");
  224.             KEEP_FAILURE(hrTemp, hr);
  225. #ifdef WINNT
  226.             // I suppose we should call out NT-only registrations, just in case
  227.             // we ever have to ship a win9x based shell again
  228.             hrTemp = CallRegInstall("RegDllNT");
  229.             KEEP_FAILURE(hrTemp, hr);
  230.             // If we are on NT server, do additional stuff
  231.             if (type != NtProductWinNt)
  232.             {
  233.                 hrTemp = CallRegInstall("RegDllNTServer");
  234.                 KEEP_FAILURE(hrTemp, hr);
  235.             }
  236. #endif
  237.             // This is apparently the only way to get setup to remove all the registry backup
  238.             // for old names no longer in use...
  239.             hrTemp = CallRegInstall("CleanupOldRollback1");
  240.             KEEP_FAILURE(hrTemp, hr);
  241.             hrTemp = CallRegInstall("CleanupOldRollback2");
  242.             KEEP_FAILURE(hrTemp, hr);
  243.             // REVIEW (ToddB): Move this to DllRegisterServer.
  244.             hrTemp = Shell32RegTypeLib();
  245.             KEEP_FAILURE(hrTemp, hr);
  246. #ifdef WINNT
  247.             ApplyRegistrySecurity();
  248. #endif
  249.             FixPlusIcons();
  250.             CleanupFileSystem();
  251.         }
  252.     }
  253.     else
  254.     {
  255.         // We only need one unreg call since all our sections share
  256.         // the same backup information
  257.         hrTemp = CallRegInstall("UnregDll");
  258.         KEEP_FAILURE(hrTemp, hr);
  259.         UnregisterTypeLibrary(&LIBID_Shell32);
  260.     }
  261.     CDeskHtmlProp_RegUnReg(bInstall);
  262.     
  263.     return hr;
  264. }
  265. STDAPI DllRegisterServer(void)
  266. {
  267.     // NT5 setup calls this so it is now safe to put code here.
  268.     return S_OK;
  269. }
  270. STDAPI DllUnregisterServer(void)
  271. {
  272.     return S_OK;
  273. }
  274. //  Migration/Upgrade functions put here for want of a better place
  275. //  See DllInstall() for an explanation of usage.
  276. BOOL    Clone (LPCTSTR pszSourcePath, LPCTSTR pszTargetPath, LPCTSTR pszName)
  277. {
  278.     TCHAR   szSourcePath[MAX_PATH], szTargetPath[MAX_PATH];
  279.     lstrcpy(szSourcePath, pszSourcePath);
  280.     lstrcpy(szTargetPath, pszTargetPath);
  281.     PathAppend(szSourcePath, pszName);
  282.     PathAppend(szTargetPath, pszName);
  283.     return(CopyFile(szSourcePath, szTargetPath, TRUE));
  284. }
  285. STDAPI  CopySampleJPG (void)
  286. {
  287.     TCHAR   szCurrentUserMyPicturesPath[MAX_PATH],
  288.             szDefaultUserMyPicturesPath[MAX_PATH];
  289.     // Here's the logic for copying the "Sample.jpg" file on upgrade.
  290.     // 1. Get the CSIDL_MYPICUTRES path by using shell32!SHGetFolderPath passing (HANDLE)-1
  291.     //    as the hToken meaning "Default User".
  292.     // 2. Copy the file
  293.     // mydocs!PerUserInit is invoked to setup the desktop.ini and do all the other work
  294.     // required to make this correct.
  295.     if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYPICTURES, NULL, SHGFP_TYPE_CURRENT, szCurrentUserMyPicturesPath)) &&
  296.         SUCCEEDED(SHGetFolderPath(NULL, CSIDL_MYPICTURES, (HANDLE)-1, SHGFP_TYPE_CURRENT, szDefaultUserMyPicturesPath)))
  297.     {
  298.         TCHAR   szMyDocsDLLPath[MAX_PATH];
  299.         Clone(szDefaultUserMyPicturesPath, szCurrentUserMyPicturesPath, TEXT("Sample.jpg"));
  300.         if (GetSystemDirectory(szMyDocsDLLPath, ARRAYSIZE(szMyDocsDLLPath)) != 0)
  301.         {
  302.             HINSTANCE   hInstMyDocs;
  303.             PathAppend(szMyDocsDLLPath, TEXT("mydocs.dll"));
  304.             hInstMyDocs = LoadLibrary(szMyDocsDLLPath);
  305.             if (hInstMyDocs != NULL)
  306.             {
  307.                 typedef void    (*PFNPerUserInit) (void);
  308.                 PFNPerUserInit  pfnPerUserInit;
  309.                 pfnPerUserInit = (PFNPerUserInit)GetProcAddress(hInstMyDocs, "PerUserInit");
  310.                 if (pfnPerUserInit != NULL)
  311.                     pfnPerUserInit();
  312.                 FreeLibrary(hInstMyDocs);
  313.             }
  314.         }
  315.     }
  316.     return(S_OK);
  317. }
  318. void    CopyRegistryValues (HKEY hKeyBaseSource, LPCTSTR pszSource, HKEY hKeyBaseTarget, LPCTSTR pszTarget)
  319. {
  320.     DWORD   dwDisposition, dwMaxValueNameSize, dwMaxValueDataSize;
  321.     HKEY    hKeySource, hKeyTarget;
  322.     hKeySource = hKeyTarget = NULL;
  323.     if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyBaseSource,
  324.                                        pszSource,
  325.                                        0,
  326.                                        KEY_READ,
  327.                                        &hKeySource)) &&
  328.         (ERROR_SUCCESS == RegCreateKeyEx(hKeyBaseTarget,
  329.                                          pszTarget,
  330.                                          0,
  331.                                          TEXT(""),
  332.                                          REG_OPTION_NON_VOLATILE,
  333.                                          KEY_ALL_ACCESS,
  334.                                          NULL,
  335.                                          &hKeyTarget,
  336.                                          &dwDisposition)) &&
  337.         (ERROR_SUCCESS == RegQueryInfoKey(hKeySource,
  338.                                           NULL,
  339.                                           NULL,
  340.                                           NULL,
  341.                                           NULL,
  342.                                           NULL,
  343.                                           NULL,
  344.                                           NULL,
  345.                                           &dwMaxValueNameSize,
  346.                                           &dwMaxValueDataSize,
  347.                                           NULL,
  348.                                           NULL)))
  349.     {
  350.         TCHAR   *pszValueName;
  351.         void    *pValueData;
  352.         pszValueName = reinterpret_cast<TCHAR*>(LocalAlloc(LMEM_FIXED, ++dwMaxValueNameSize * sizeof(TCHAR)));
  353.         if (pszValueName != NULL)
  354.         {
  355.             pValueData = LocalAlloc(LMEM_FIXED, dwMaxValueDataSize);
  356.             if (pValueData != NULL)
  357.             {
  358.                 DWORD   dwIndex, dwType, dwValueNameSize, dwValueDataSize;
  359.                 dwIndex = 0;
  360.                 dwValueNameSize = dwMaxValueNameSize;
  361.                 dwValueDataSize = dwMaxValueDataSize;
  362.                 while (ERROR_SUCCESS == RegEnumValue(hKeySource,
  363.                                                      dwIndex,
  364.                                                      pszValueName,
  365.                                                      &dwValueNameSize,
  366.                                                      NULL,
  367.                                                      &dwType,
  368.                                                      reinterpret_cast<LPBYTE>(pValueData),
  369.                                                      &dwValueDataSize))
  370.                 {
  371.                     RegSetValueEx(hKeyTarget,
  372.                                   pszValueName,
  373.                                   0,
  374.                                   dwType,
  375.                                   reinterpret_cast<LPBYTE>(pValueData),
  376.                                   dwValueDataSize);
  377.                     ++dwIndex;
  378.                     dwValueNameSize = dwMaxValueNameSize;
  379.                     dwValueDataSize = dwMaxValueDataSize;
  380.                 }
  381.                 LocalFree(pValueData);
  382.             }
  383.             LocalFree(pszValueName);
  384.         }
  385.     }
  386. }
  387. STDAPI  MergeDesktopAndNormalStreams (void)
  388. {
  389.     static  const   TCHAR   scszBaseRegistryLocation[] = TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer");
  390.     static  const   int     sciMaximumStreams = 128;
  391.     static  const   TCHAR   sccOldMRUListBase = TEXT('a');
  392.     // Upgrade from NT4 (classic shell) to Windows 2000 (integrated shell)
  393.     // This involves TWO major changes and one minor change:
  394.     //    1. Merging DesktopStreamMRU and StreamMRU
  395.     //    2. Upgrading the MRUList to MRUListEx
  396.     //    3. Leaving the old settings alone for the roaming user profile scenario
  397.     // This also involves special casing the users desktop PIDL because this is
  398.     // stored as an absolute path PIDL in DesktopStream and needs to be stored
  399.     // in StreamsDesktop instead.
  400.     // The conversion is performed in-situ and simultaneously.
  401.     // 1. Open all the keys we are going to need to do the conversion.
  402.     HKEY    hKeyBase, hKeyDesktopStreamMRU, hKeyDesktopStreams, hKeyStreamMRU, hKeyStreams;
  403.     hKeyBase = hKeyDesktopStreamMRU = hKeyDesktopStreams = hKeyStreamMRU = hKeyStreams = NULL;
  404.     if ((ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,
  405.                                        scszBaseRegistryLocation,
  406.                                        0,
  407.                                        KEY_ALL_ACCESS,
  408.                                        &hKeyBase)) &&
  409.         (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase,
  410.                                        TEXT("DesktopStreamMRU"),
  411.                                        0,
  412.                                        KEY_ALL_ACCESS,
  413.                                        &hKeyDesktopStreamMRU)) &&
  414.         (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase,
  415.                                        TEXT("DesktopStreams"),
  416.                                        0,
  417.                                        KEY_ALL_ACCESS,
  418.                                        &hKeyDesktopStreams)) &&
  419.         (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase,
  420.                                        TEXT("StreamMRU"),
  421.                                        0,
  422.                                        KEY_ALL_ACCESS,
  423.                                        &hKeyStreamMRU)) &&
  424.         (ERROR_SUCCESS == RegOpenKeyEx(hKeyBase,
  425.                                        TEXT("Streams"),
  426.                                        0,
  427.                                        KEY_ALL_ACCESS,
  428.                                        &hKeyStreams)) &&
  429.     // 2. Determine whether this upgrade is needed at all. If the presence of
  430.     // StreamMRUMRUListEx is detected then stop.
  431.         (ERROR_SUCCESS != RegQueryValueEx(hKeyStreamMRU,
  432.                                          TEXT("MRUListEx"),
  433.                                          NULL,
  434.                                          NULL,
  435.                                          NULL,
  436.                                          NULL)))
  437.     {
  438.         DWORD   *pdwMRUListEx, *pdwMRUListExBase;
  439.         pdwMRUListExBase = pdwMRUListEx = reinterpret_cast<DWORD*>(LocalAlloc(LPTR, sciMaximumStreams * sizeof(DWORD) * 2));
  440.         if (pdwMRUListEx != NULL)
  441.         {
  442.             DWORD   dwLastFreeSlot, dwMRUListSize, dwType;
  443.             TCHAR   *pszMRUList, szMRUList[sciMaximumStreams];
  444.             // 3. Read the StreamMRUMRUList, iterate thru this list
  445.             // and convert as we go.
  446.             dwLastFreeSlot = 0;
  447.             dwMRUListSize = sizeof(szMRUList);
  448.             if (ERROR_SUCCESS == RegQueryValueEx(hKeyStreamMRU,
  449.                                                  TEXT("MRUList"),
  450.                                                  NULL,
  451.                                                  &dwType,
  452.                                                  reinterpret_cast<LPBYTE>(szMRUList),
  453.                                                  &dwMRUListSize))
  454.             {
  455.                 pszMRUList = szMRUList;
  456.                 while (*pszMRUList != TEXT(''))
  457.                 {
  458.                     DWORD   dwValueDataSize;
  459.                     TCHAR   szValue[16];
  460.                     // Read the PIDL information based on the letter in
  461.                     // the MRUList.
  462.                     szValue[0] = *pszMRUList++;
  463.                     szValue[1] = TEXT('');
  464.                     if (ERROR_SUCCESS == RegQueryValueEx(hKeyStreamMRU,
  465.                                                          szValue,
  466.                                                          NULL,
  467.                                                          NULL,
  468.                                                          NULL,
  469.                                                          &dwValueDataSize))
  470.                     {
  471.                         DWORD   dwValueType;
  472.                         void    *pValueData;
  473.                         pValueData = LocalAlloc(LMEM_FIXED, dwValueDataSize);
  474.                         if ((pValueData != NULL) &&
  475.                             (ERROR_SUCCESS == RegQueryValueEx(hKeyStreamMRU,
  476.                                                               szValue,
  477.                                                               NULL,
  478.                                                               &dwValueType,
  479.                                                               reinterpret_cast<LPBYTE>(pValueData),
  480.                                                               &dwValueDataSize)))
  481.                         {
  482.                             // Allocate a new number in the MRUListEx for the PIDL.
  483.                             *pdwMRUListEx = szValue[0] - sccOldMRUListBase;
  484.                             wsprintf(szValue, TEXT("%d"), *pdwMRUListEx++);
  485.                             ++dwLastFreeSlot;
  486.                             RegSetValueEx(hKeyStreamMRU,
  487.                                           szValue,
  488.                                           NULL,
  489.                                           dwValueType,
  490.                                           reinterpret_cast<LPBYTE>(pValueData),
  491.                                           dwValueDataSize);
  492.                             LocalFree(pValueData);
  493.                         }
  494.                     }
  495.                 }
  496.             }
  497.             // 4. Read the DesktopStreamMRUMRUList, iterate thru this
  498.             // this and append to the new MRUListEx that is being
  499.             // created as well as copying both the PIDL in DesktopStreamMRU
  500.             // and the view information in DesktopStreams.
  501.             dwMRUListSize = sizeof(szMRUList);
  502.             if (ERROR_SUCCESS == RegQueryValueEx(hKeyDesktopStreamMRU,
  503.                                                  TEXT("MRUList"),
  504.                                                  NULL,
  505.                                                  &dwType,
  506.                                                  reinterpret_cast<LPBYTE>(szMRUList),
  507.                                                  &dwMRUListSize))
  508.             {
  509.                 bool    fConvertedEmptyPIDL;
  510.                 TCHAR   szDesktopDirectoryPath[MAX_PATH];
  511.                 fConvertedEmptyPIDL = false;
  512.                 SHGetFolderPath(NULL, CSIDL_DESKTOPDIRECTORY, NULL, SHGFP_TYPE_CURRENT, szDesktopDirectoryPath);
  513.                 pszMRUList = szMRUList;
  514.                 while (*pszMRUList != TEXT(''))
  515.                 {
  516.                     DWORD   dwValueDataSize;
  517.                     TCHAR   szSource[16];
  518.                     // Read the PIDL information based on the letter in
  519.                     // the MRUList.
  520.                     szSource[0] = *pszMRUList++;
  521.                     szSource[1] = TEXT('');
  522.                     if (ERROR_SUCCESS == RegQueryValueEx(hKeyDesktopStreamMRU,
  523.                                                          szSource,
  524.                                                          NULL,
  525.                                                          NULL,
  526.                                                          NULL,
  527.                                                          &dwValueDataSize))
  528.                     {
  529.                         DWORD   dwValueType;
  530.                         void    *pValueData;
  531.                         pValueData = LocalAlloc(LMEM_FIXED, dwValueDataSize);
  532.                         if ((pValueData != NULL) &&
  533.                             (ERROR_SUCCESS == RegQueryValueEx(hKeyDesktopStreamMRU,
  534.                                                               szSource,
  535.                                                               NULL,
  536.                                                               &dwValueType,
  537.                                                               reinterpret_cast<LPBYTE>(pValueData),
  538.                                                               &dwValueDataSize)))
  539.                         {
  540.                             TCHAR   szTarget[16], szStreamPath[MAX_PATH];
  541.                             if ((SHGetPathFromIDList(reinterpret_cast<LPCITEMIDLIST>(pValueData), szStreamPath) != 0) &&
  542.                                 (0 == lstrcmpi(szStreamPath, szDesktopDirectoryPath)))
  543.                             {
  544.                                 if (!fConvertedEmptyPIDL)
  545.                                 {
  546.                                     // 99/05/24 #343721 vtan: Prefer the desktop relative PIDL
  547.                                     // (empty PIDL) when given a choice of two PIDLs that refer
  548.                                     // to the desktop. The old absolute PIDL is from SP3 and
  549.                                     // earlier days. The new relative PIDL is from SP4 and
  550.                                     // later days. An upgraded SP3 -> SP4 -> SPx -> Windows
  551.                                     // 2000 system will possibly have old absolute PIDLs.
  552.                                     // Check for the empty PIDL. If this is encountered already
  553.                                     // then don't process this stream.
  554.                                     fConvertedEmptyPIDL = ILIsEmpty(reinterpret_cast<LPCITEMIDLIST>(pValueData));
  555.                                     wsprintf(szSource, TEXT("%d"), szSource[0] - sccOldMRUListBase);
  556.                                     CopyRegistryValues(hKeyDesktopStreams, szSource, hKeyStreams, TEXT("Desktop"));
  557.                                 }
  558.                             }
  559.                             else
  560.                             {
  561.                                 // Allocate a new number in the MRUListEx for the PIDL.
  562.                                 *pdwMRUListEx++ = dwLastFreeSlot;
  563.                                 wsprintf(szTarget, TEXT("%d"), dwLastFreeSlot++);
  564.                                 if (ERROR_SUCCESS == RegSetValueEx(hKeyStreamMRU,
  565.                                                                    szTarget,
  566.                                                                    NULL,
  567.                                                                    dwValueType,
  568.                                                                    reinterpret_cast<LPBYTE>(pValueData),
  569.                                                                    dwValueDataSize))
  570.                                 {
  571.                                     // Copy the view information from DesktopStreams to Streams
  572.                                     wsprintf(szSource, TEXT("%d"), szSource[0] - sccOldMRUListBase);
  573.                                     CopyRegistryValues(hKeyDesktopStreams, szSource, hKeyStreams, szTarget);
  574.                                 }
  575.                             }
  576.                             LocalFree(pValueData);
  577.                         }
  578.                     }
  579.                 }
  580.             }
  581.             *pdwMRUListEx++ = static_cast<DWORD>(-1);
  582.             RegSetValueEx(hKeyStreamMRU,
  583.                           TEXT("MRUListEx"),
  584.                           NULL,
  585.                           REG_BINARY,
  586.                           reinterpret_cast<LPCBYTE>(pdwMRUListExBase),
  587.                           ++dwLastFreeSlot * sizeof(DWORD));
  588.             LocalFree(reinterpret_cast<HLOCAL>(pdwMRUListExBase));
  589.         }
  590.     }
  591.     if (hKeyStreams != NULL)
  592.         RegCloseKey(hKeyStreams);
  593.     if (hKeyStreamMRU != NULL)
  594.         RegCloseKey(hKeyStreamMRU);
  595.     if (hKeyDesktopStreams != NULL)
  596.         RegCloseKey(hKeyDesktopStreams);
  597.     if (hKeyDesktopStreamMRU != NULL)
  598.         RegCloseKey(hKeyDesktopStreamMRU);
  599.     if (hKeyBase != NULL)
  600.         RegCloseKey(hKeyBase);
  601.     return(S_OK);
  602. }
  603. static  const   int     s_ciMaximumNumericString = 32;
  604. int     GetRegistryStringValueAsInteger (HKEY hKey, LPCTSTR pszValue, int iDefaultValue)
  605. {
  606.     int     iResult;
  607.     DWORD   dwType, dwStringSize;
  608.     TCHAR   szString[s_ciMaximumNumericString];
  609.     dwStringSize = sizeof(szString);
  610.     if (ERROR_SUCCESS == RegQueryValueEx(hKey,
  611.                                          pszValue,
  612.                                          NULL,
  613.                                          &dwType,
  614.                                          reinterpret_cast<LPBYTE>(szString),
  615.                                          &dwStringSize) && (dwType == REG_SZ))
  616.     {
  617.         iResult = StrToInt(szString);
  618.     }
  619.     else
  620.     {
  621.         iResult = iDefaultValue;
  622.     }
  623.     return(iResult);
  624. }
  625. void    SetRegistryIntegerAsStringValue (HKEY hKey, LPCTSTR pszValue, int iValue)
  626. {
  627.     TCHAR   szString[s_ciMaximumNumericString];
  628.     wnsprintf(szString, ARRAYSIZE(szString), TEXT("%d"), iValue);
  629.     TW32(RegSetValueEx(hKey,
  630.                        pszValue,
  631.                        0,
  632.                        REG_SZ,
  633.                        reinterpret_cast<LPBYTE>(szString),
  634.                        (lstrlen(szString) + sizeof('')) * sizeof(TCHAR)));
  635. }
  636. STDAPI  MoveAndAdjustIconMetrics (void)
  637. {
  638.     // 99/06/06 #309198 vtan: The following comes from hiveusd.inx which is
  639.     // where this functionality used to be executed. It used to consist of
  640.     // simple registry deletion and addition. This doesn't work on upgrade
  641.     // when the user has large icons (Shell Icon Size == 48).
  642.     // In this case that metric must be moved and the new values adjusted
  643.     // so that the metric is preserved should the user then decide to turn
  644.     // off large icons.
  645.     // To restore old functionality, remove the entry in hiveusd.inx at
  646.     // build 1500 which is where this function is invoked and copy the
  647.     // old text back in.
  648. /*
  649.     HKR,"1508Hive2","Action",0x00010001,3
  650.     HKR,"1508Hive2","KeyName",0000000000,"Control PanelDesktopWindowMetrics"
  651.     HKR,"1508Hive2","Value",0000000000,"75"
  652.     HKR,"1508Hive2","ValueName",0000000000,"IconSpacing"
  653.     HKR,"1508Hive3","Action",0x00010001,3
  654.     HKR,"1508Hive3","KeyName",0000000000,"Control PanelDesktopWindowMetrics"
  655.     HKR,"1508Hive3","Value",0000000000,"1"
  656.     HKR,"1508Hive3","ValueName",0000000000,"IconTitleWrap"
  657. */
  658.     // Icon metric keys have moved from HKCUControl PanelDesktopIcon*
  659.     // to HKCUControl PanelDesktopWindowMetricsIcon* but only 3 values
  660.     // should be moved. These are "IconSpacing", "IconTitleWrap" and
  661.     // "IconVerticalSpacing". This code is executed before the deletion
  662.     // entry in hiveusd.inx so that it can get the values before they
  663.     // are deleted. The addition section has been remove (it's above).
  664.     static  const   TCHAR   s_cszIconSpacing[] = TEXT("IconSpacing");
  665.     static  const   TCHAR   s_cszIconTitleWrap[] = TEXT("IconTitleWrap");
  666.     static  const   TCHAR   s_cszIconVerticalSpacing[] = TEXT("IconVerticalSpacing");
  667.     static  const   int     s_ciStandardOldIconSpacing = 75;
  668.     static  const   int     s_ciStandardNewIconSpacing = -1125;
  669.     HKEY    hKeyDesktop, hKeyWindowMetrics;
  670.     hKeyDesktop = hKeyWindowMetrics = NULL;
  671.     if ((ERROR_SUCCESS == RegOpenKeyEx(HKEY_CURRENT_USER,
  672.                                        TEXT("Control Panel\Desktop"),
  673.                                        0,
  674.                                        KEY_ALL_ACCESS,
  675.                                        &hKeyDesktop)) &&
  676.         (ERROR_SUCCESS == RegOpenKeyEx(hKeyDesktop,
  677.                                        TEXT("WindowMetrics"),
  678.                                        0,
  679.                                        KEY_ALL_ACCESS,
  680.                                        &hKeyWindowMetrics)))
  681.     {
  682.         int     iIconSpacing, iIconTitleWrap, iIconVerticalSpacing;
  683.         // 1. Read the values that we wish the move and adjust.
  684.         iIconSpacing = GetRegistryStringValueAsInteger(hKeyDesktop, s_cszIconSpacing, s_ciStandardOldIconSpacing);
  685.         iIconTitleWrap = GetRegistryStringValueAsInteger(hKeyDesktop, s_cszIconTitleWrap, 1);
  686.         iIconVerticalSpacing = GetRegistryStringValueAsInteger(hKeyDesktop, s_cszIconVerticalSpacing, s_ciStandardOldIconSpacing);
  687.         // 2. Perform the adjustment.
  688.         iIconSpacing = s_ciStandardNewIconSpacing * iIconSpacing / s_ciStandardOldIconSpacing;
  689.         iIconVerticalSpacing = s_ciStandardNewIconSpacing * iIconVerticalSpacing / s_ciStandardOldIconSpacing;
  690.         // 3. Write the values back out in the new (moved) location.
  691.         SetRegistryIntegerAsStringValue(hKeyWindowMetrics, s_cszIconSpacing, iIconSpacing);
  692.         SetRegistryIntegerAsStringValue(hKeyWindowMetrics, s_cszIconTitleWrap, iIconTitleWrap);
  693.         SetRegistryIntegerAsStringValue(hKeyWindowMetrics, s_cszIconVerticalSpacing, iIconVerticalSpacing);
  694.         // 4. Let winlogon continue processing hiveusd.inx and delete the
  695.         // old entries in the process. We already created the new entries
  696.         // and that has been removed from hiveusd.inx.
  697.     }
  698.     if (hKeyWindowMetrics != NULL)
  699.         TW32(RegCloseKey(hKeyWindowMetrics));
  700.     if (hKeyDesktop != NULL)
  701.         TW32(RegCloseKey(hKeyDesktop));
  702.     return(S_OK);
  703. }
  704. extern  "C"     STDAPI  FirstUserLogon (LPCSTR pcszCommand, LPCSTR pcszOptionalArguments)
  705. {
  706.     enum
  707.     {
  708.         kCommandCopySampleJPG,
  709.         kCommandMergeDesktopAndNormalStreams,
  710.         kCommandMoveAndAdjustIconMetrics
  711.     };
  712.     typedef struct
  713.     {
  714.         LPCSTR  pcszCommand;
  715.         int     iCommand;
  716.     } tCommandElement;
  717.     tCommandElement     sCommands[] =
  718.     {
  719.         { "CopySampleJPG",                  kCommandCopySampleJPG                  },
  720.         { "MergeDesktopAndNormalStreams",   kCommandMergeDesktopAndNormalStreams   },
  721.         { "MoveAndAdjustIconMetrics",       kCommandMoveAndAdjustIconMetrics       },
  722.     };
  723.     HRESULT     hResult;
  724.     int         i;
  725.     // Match what shmgrate.exe passed us and execute the command.
  726.     // Only use the optional argument if required. Note this is
  727.     // done ANSI because the original command line is ANSI from
  728.     // shmgrate.exe.
  729.     for (i = 0; (i < ARRAYSIZE(sCommands)) && (lstrcmpA(pcszCommand, sCommands[i].pcszCommand) != 0); ++i)
  730.         ;
  731.     switch (sCommands[i].iCommand)
  732.     {
  733.         case kCommandCopySampleJPG:
  734.             hResult = CopySampleJPG();
  735.             break;
  736.         case kCommandMergeDesktopAndNormalStreams:
  737.             hResult = MergeDesktopAndNormalStreams();
  738.             break;
  739.         case kCommandMoveAndAdjustIconMetrics:
  740.             hResult = MoveAndAdjustIconMetrics();
  741.             break;
  742.         default:
  743.             hResult = E_FAIL;
  744.             break;
  745.     }
  746.     return(hResult);
  747. }
  748. #ifdef INSTALL_WEBFOLDERS
  749. //
  750. // WebFolders namespace extension installation.
  751. // This is the code that initially installs the Office WebFolders 
  752. // shell namespace extension on the computer.  Code in shmgrate.exe
  753. // (see privatewindowsshellmigrate) performs per-user
  754. // web folders registration duties.
  755. //
  756. typedef UINT (WINAPI * PFNMSIINSTALLPRODUCT)(LPCTSTR, LPCTSTR);
  757. typedef INSTALLUILEVEL (WINAPI * PFNMSISETINTERNALUI)(INSTALLUILEVEL, HWND *);
  758. #define GETPROC(var, hmod, ptype, fn)  ptype var = (ptype)GetProcAddress(hmod, fn)
  759. #define API_MSISETINTERNALUI  "MsiSetInternalUI"
  760. #ifdef UNICODE
  761. #   define API_MSIINSTALLPRODUCT "MsiInstallProductW"
  762. #else
  763. #   define API_MSIINSTALLPRODUCT "MsiInstallProductA"
  764. #endif
  765. typedef struct _WEBFOLDER_INSTALL_RETRY_STRUCT {
  766.     HMODULE hmod;
  767.     PFNMSISETINTERNALUI pfnMsiSetInternalUI;
  768.     PFNMSIINSTALLPRODUCT pfnMsiInstallProduct;
  769. } WEBFOLDER_INSTALL_RETRY_STRUCT;
  770.    
  771. void WebFolder_Install_RetryThreadProc(WEBFOLDER_INSTALL_RETRY_STRUCT * pwirs)
  772. {
  773.     ASSERT(pwirs);
  774.     ASSERT(pwirs->hmod);
  775.     ASSERT(pwirs->pfnMsiSetInternalUI);
  776.     ASSERT(pwirs->pfnMsiInstallProduct);
  777.     TCHAR szPath[MAX_PATH];
  778.     UINT uRet = 0;
  779.     INSTALLUILEVEL oldUILevel = pwirs->pfnMsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
  780.     do {
  781.         GetSystemDirectory(szPath, ARRAYSIZE(szPath));
  782.         PathAppend(szPath, TEXT("webfldrs.msi"));
  783.         uRet = pwirs->pfnMsiInstallProduct(szPath, TEXT(""));
  784.     } while (uRet == ERROR_INSTALL_ALREADY_RUNNING);
  785.     pwirs->pfnMsiSetInternalUI(oldUILevel, NULL);
  786.     DllRelease();
  787.     FreeLibrary(pwirs->hmod);
  788.     LocalFree(pwirs);
  789. }
  790. void OfficeWebFolders_Install(void)
  791. {
  792.     HMODULE hmod = LoadLibrary(TEXT("msi.dll"));
  793.     if (hmod)
  794.     {
  795.         BOOL bFreeLib = FALSE;
  796.         GETPROC(pfnMsiSetInternalUI,  hmod, PFNMSISETINTERNALUI,  API_MSISETINTERNALUI);
  797.         GETPROC(pfnMsiInstallProduct, hmod, PFNMSIINSTALLPRODUCT, API_MSIINSTALLPRODUCT);
  798.         if (pfnMsiSetInternalUI && pfnMsiInstallProduct)
  799.         {
  800.             TCHAR szPath[MAX_PATH];
  801.             GetSystemDirectory(szPath, ARRAYSIZE(szPath));
  802.             PathAppend(szPath, TEXT("webfldrs.msi"));
  803.             //
  804.             // Use "silent" install mode.  No UI.
  805.             //
  806.             INSTALLUILEVEL oldUILevel = pfnMsiSetInternalUI(INSTALLUILEVEL_NONE, NULL);
  807.             //
  808.             // Install the web folders MSI package.
  809.             //
  810.             if (pfnMsiInstallProduct(szPath, TEXT("")) == ERROR_INSTALL_ALREADY_RUNNING)
  811.             {
  812.                 WEBFOLDER_INSTALL_RETRY_STRUCT * pwirs = (WEBFOLDER_INSTALL_RETRY_STRUCT *)LocalAlloc(LPTR, SIZEOF(WEBFOLDER_INSTALL_RETRY_STRUCT));
  813.                 if (pwirs)
  814.                 {
  815.                     DWORD thid;     // Not used but we have to pass something in
  816.                     pwirs->hmod = hmod;
  817.                     pwirs->pfnMsiSetInternalUI = pfnMsiSetInternalUI;
  818.                     pwirs->pfnMsiInstallProduct = pfnMsiInstallProduct;
  819.                     DllAddRef();
  820.                     HANDLE hthreadWorker = CreateThread(NULL, 0,
  821.                         (LPTHREAD_START_ROUTINE)WebFolder_Install_RetryThreadProc,
  822.                         (LPVOID)pwirs, CREATE_SUSPENDED, &thid);
  823.                     
  824.                     if (hthreadWorker)
  825.                     {
  826.                         // Demote the priority so it doesn't interfere with the
  827.                         SetThreadPriority(hthreadWorker, THREAD_PRIORITY_BELOW_NORMAL);
  828.                         ResumeThread(hthreadWorker);
  829.                         bFreeLib = FALSE;
  830.                     }
  831.                     else
  832.                     {
  833.                         DllRelease();
  834.                         LocalFree(pwirs);
  835.                     }
  836.                 }
  837.             }
  838.             
  839.             pfnMsiSetInternalUI(oldUILevel, NULL);
  840.         }
  841.         if (bFreeLib)
  842.             FreeLibrary(hmod);
  843.     }
  844. }
  845. #endif // INSTALL_WEBFOLDERS
  846. #ifdef WINNT
  847. // now is the time on sprockets when we lock down the registry
  848. STDAPI_(BOOL) ApplyRegistrySecurity()
  849. {
  850.     BOOL fSuccess = FALSE;      // assume failure
  851.     SECURITY_DESCRIPTOR* psd;
  852.     SHELL_USER_PERMISSION supEveryone;
  853.     SHELL_USER_PERMISSION supSystem;
  854.     SHELL_USER_PERMISSION supAdministrators;
  855.     PSHELL_USER_PERMISSION aPerms[3] = {&supEveryone, &supSystem, &supAdministrators};
  856.     // we want the "Everyone" to have read access
  857.     supEveryone.susID = susEveryone;
  858.     supEveryone.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
  859.     supEveryone.dwAccessMask = KEY_READ;
  860.     supEveryone.fInherit = TRUE;
  861.     supEveryone.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
  862.     supEveryone.dwInheritAccessMask = GENERIC_READ;
  863.     // we want the "SYSTEM" to have full control
  864.     supSystem.susID = susSystem;
  865.     supSystem.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
  866.     supSystem.dwAccessMask = KEY_ALL_ACCESS;
  867.     supSystem.fInherit = TRUE;
  868.     supSystem.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
  869.     supSystem.dwInheritAccessMask = GENERIC_ALL;
  870.     // we want the "Administrators" to have full control
  871.     supAdministrators.susID = susAdministrators;
  872.     supAdministrators.dwAccessType = ACCESS_ALLOWED_ACE_TYPE;
  873.     supAdministrators.dwAccessMask = KEY_ALL_ACCESS;
  874.     supAdministrators.fInherit = TRUE;
  875.     supAdministrators.dwInheritMask = (OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE);
  876.     supAdministrators.dwInheritAccessMask = GENERIC_ALL;
  877.     psd = GetShellSecurityDescriptor(aPerms, ARRAYSIZE(aPerms));
  878.     if (psd)
  879.     {
  880.         HKEY hkLMBitBucket;
  881.         if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, TEXT("Software\Microsoft\Windows\CurrentVersion\Explorer\BitBucket"), 0, KEY_ALL_ACCESS, &hkLMBitBucket) == ERROR_SUCCESS)
  882.         {
  883.             if (RegSetKeySecurity(hkLMBitBucket, DACL_SECURITY_INFORMATION, psd) == ERROR_SUCCESS)
  884.             {
  885.                 // victory is mine!
  886.                 fSuccess = TRUE;
  887.             }
  888.             RegCloseKey(hkLMBitBucket);
  889.         }
  890.         LocalFree(psd);
  891.     }
  892.     return fSuccess;
  893. }
  894. #endif // WINNT
  895. CComModule _Module;
  896. BEGIN_OBJECT_MAP(ObjectMap)
  897.     // nothing in here, use clsobj.c class table instead
  898. END_OBJECT_MAP()
  899. // ATL DllMain, needed to support our ATL classes that depend on _Module
  900. // REVIEW: confirm that _Module is really needed
  901. STDAPI_(BOOL) ATL_DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID /*lpReserved*/)
  902. {
  903.     if (dwReason == DLL_PROCESS_ATTACH)
  904.     {
  905.         _Module.Init(ObjectMap, hInstance);
  906.     }
  907.     else if (dwReason == DLL_PROCESS_DETACH)
  908.     {
  909.         _Module.Term();
  910.     }
  911.     return TRUE;    // ok
  912. }