home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / sdk / winui / mdi / regmpad / regdb.c < prev    next >
C/C++ Source or Header  |  1997-10-04  |  36KB  |  1,094 lines

  1. // RegDB.c -- Implements the semantics of the registry interface for this
  2. //            application. The visible interfaces and data structures are
  3. //            defined in RegDB.h.
  4.  
  5. /*
  6.  *
  7.                                 Overview
  8.                                 --------
  9.  
  10. The NT Registry is an object database consisting of keys and values. Keys
  11. have names and may contain other keys and values. A value is a name paired
  12. with a data object and a data type. The keys in the registry are analogous
  13. to the directories in a file system. In that vein the values are analogous
  14. to files.
  15.  
  16. Access to a key and its associated set of values is mediated by a key handle.
  17. Four key handles are given as predefined constants. Those handles correspond
  18. to the roots of key trees which have special signifigance. Handles for the
  19. other keys in the registry database may be constructed via the Registry's
  20. Open and Create interfaces using an existing key handle and a relative path
  21. string.
  22.  
  23. The predefined key handles are:
  24.  
  25.  
  26.     HKEY_LOCAL_MACHINE -- This handle refers to a tree of keys and values
  27.                           which characterize the state of the local machine.
  28.                           It contains state information global to everyone
  29.                           who uses the machine.
  30.  
  31.     HKEY_CLASSES_ROOT  -- This handle refers to a subtree within
  32.                           HKEY_LOCAL_MACHINE. It defines the associations
  33.                           between file extensions and document types as well
  34.                           as the command strings for shell and DDE/OLE actions.
  35.  
  36.     HKEY_USERS         -- This handle refers to a tree of information about
  37.                           the people who use this machine. The top level of
  38.                           the tree consists of a .DEFAULT key and one or more
  39.                           entries for specific people. The specific entries
  40.                           are created dynamically and are initially based on
  41.                           the content of the .DEFAULT key. The key names for
  42.                           the specific entries are SIDs which define the
  43.                           permissions given to the corresponding people.
  44.  
  45.     HKEY_CURRENT_USER  -- This handle refers to a specific user key within
  46.                           HKEY_USERS. It denotes the information tree for
  47.                           the currently active user id.
  48.  
  49.  
  50.                           Application Conventions
  51.                           -----------------------
  52.  
  53. Applications need to manipulate three of the above key trees. At installation
  54. time an application should adjust HKEY_CLASSES_ROOT to define the documents
  55. which it handles together with their file extensions and its shell and
  56. DDE/OLE command strings. At the same time it needs to add information global
  57. to all users to the HKEY_USERS\.DEFAULT key.
  58.  
  59. Subsequently an application will need to place per-user information in the
  60. HKEY_CURRENT_USER subtree. That information will include preferences as well
  61. as historical information such as lists of recently opened files.
  62.  
  63. The conventions appropriate to the HKEY_CLASSES_ROOT will not be described
  64. or demonstrated in this sample application. The focus here will be on
  65. HKEY_USERS\.DEFAULT and HKEY_CURRENT_USER subtrees.
  66.  
  67. Within both of those subtrees information related to version 2.5 of the
  68. Bazooka application published by Trey Software will be clustered in the
  69. subkey:
  70.  
  71.       software\"Trey Software"\Bazooka\2.5
  72.  
  73. and in general applications will use a path with the structure
  74.  
  75.       software \ <company name> \ <application name> \ <version number>
  76.  
  77. to access their state information.
  78.  
  79. After an application has been installed almost all registry changes will
  80. involve HKEY_CURRENT_USER and will concern a specific user's preferences
  81. or history.
  82.  
  83.  */
  84.  
  85. #include "multipad.h"
  86.  
  87. // #include <windows.H>
  88. // #include <winbase.h>
  89. #include <malloc.h>
  90. #include "regdb.h"
  91.  
  92. HKEY hkGlobal  = NULL;  // Key Handle for global registry data
  93. HKEY hkPerUser = NULL;  // Key Handle for per-user registry data
  94.  
  95. BOOL fTextWrapDefault = FALSE; // Set from registry data.
  96.  
  97. HANDLE hmtxRegGlobal  = NULL; // Mutex for serializing Local Machine Data.
  98. HANDLE hmtxRegPerUser = NULL; // Mutex for serializing Current User Data
  99.  
  100. BOOL RunningAsAdministrator()
  101. {
  102.    BOOL  fAdmin;
  103.    HANDLE htkThread;
  104.    TOKEN_GROUPS *ptg = NULL;
  105.    DWORD cbTokenGroups;
  106.    DWORD iGroup;
  107.    SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;
  108.    PSID psidAdmin;
  109.  
  110.    // This function returns TRUE if the user identifier associated with this
  111.    // process is a member of the the Administrators group.
  112.  
  113.    // First we must open a handle to the access token for this thread.
  114.  
  115.    if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &htkThread))
  116.       if (GetLastError() == ERROR_NO_TOKEN)
  117.       {
  118.          // If the thread does not have an access token, we'll examine the
  119.          // access token associated with the process.
  120.  
  121.          if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htkThread))
  122.          return FALSE;
  123.       }
  124.       else return FALSE;
  125.  
  126.    // Then we must query the size of the group information associated with
  127.    // the token. Note that we expect a FALSE result from GetTokenInformation
  128.    // because we've given it a NULL buffer. On exit cbTokenGroups will tell
  129.    // the size of the group information.
  130.  
  131.    if (GetTokenInformation(htkThread, TokenGroups, NULL, 0, &cbTokenGroups))
  132.       return FALSE;
  133.  
  134.    // Here we verify that GetTokenInformation failed for lack of a large
  135.    // enough buffer.
  136.  
  137.    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  138.       return FALSE;
  139.  
  140.    // Now we allocate a buffer for the group information.
  141.    // Since _alloca allocates on the stack, we don't have
  142.    // to explicitly deallocate it. That happens automatically
  143.    // when we exit this function.
  144.  
  145.    if (!(ptg= _alloca(cbTokenGroups))) return FALSE;
  146.  
  147.    // Now we ask for the group information again.
  148.    // This may fail if an administrator has added this account
  149.    // to an additional group between our first call to
  150.    // GetTokenInformation and this one.
  151.  
  152.    if (!GetTokenInformation(htkThread, TokenGroups, ptg, cbTokenGroups,
  153.                                        &cbTokenGroups
  154.                            )
  155.       )
  156.       return FALSE;
  157.  
  158.    // Now we must create a System Identifier for the Admin group.
  159.  
  160.    if (!AllocateAndInitializeSid
  161.           (&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
  162.                                    DOMAIN_ALIAS_RID_ADMINS,
  163.                                    0, 0, 0, 0, 0, 0,
  164.                                    &psidAdmin
  165.           )
  166.       )
  167.       return FALSE;
  168.  
  169.    // Finally we'll iterate through the list of groups for this access
  170.    // token looking for a match against the SID we created above.
  171.  
  172.    fAdmin= FALSE;
  173.  
  174.    for (iGroup= 0; iGroup < ptg->GroupCount; iGroup++)
  175.       if (EqualSid(ptg->Groups[iGroup].Sid, psidAdmin))
  176.       {
  177.          fAdmin= TRUE;
  178.  
  179.          break;
  180.       }
  181.  
  182.    // Before we exit we must explicity deallocate the SID
  183.    // we created.
  184.  
  185.    FreeSid(psidAdmin);
  186.  
  187.    return(fAdmin);
  188. }
  189.  
  190.  
  191. BOOL InstallApp(PSZ pszPathBuff)
  192. {
  193.    // This function attempts to install global data for this
  194.    // application in the HKEY_LOCAL_MACHINE portion of the
  195.    // registry database.
  196.  
  197.    // The parameter pszPathBuff refers to a null terminated
  198.    // string which defines where the new key should be located
  199.    // in the LOCAL_MACHINE tree.
  200.  
  201.    // We requires that the current user have administrative
  202.    // privileges. That requirement insures that the owner
  203.    // tag for the global registry entries will be the
  204.    // Administrator group and not a particular user id.
  205.  
  206.    // We also assume that hmtxRegGlobal is held when this function is called.
  207.  
  208.    // First we'll see whether this user has admin privileges...
  209.    // Only administrators can install this application...
  210.  
  211.    if (!RunningAsAdministrator())
  212.    {
  213.       MPError(hwndFrame, MB_OK | MB_ICONHAND, IDS_CANTINSTALL, NULL);
  214.  
  215.       return FALSE;
  216.    }
  217.  
  218.    // Then we bring up a dialog to get the global configuration information
  219.    // we'll be storing in the HKEY_LOCAL_MACHINE tree. The dialog proc
  220.    // will call StoreAppConfig with that configuration data.
  221.  
  222.    if (!DialogBoxParam(hInst, MAKEINTRESOURCE(DLG_INSTALL),
  223.                        hwndFrame, InstallDlgProc,
  224.                        (LPARAM) pszPathBuff
  225.                       )
  226.       ) return FALSE;
  227. }
  228.  
  229. BOOL StoreAppConfig(HWND hwnd, PSZ pszPathBuff, PSZ pszInstallName,
  230.                     PSZ pszInstallOrg, BOOL fTextWrapDefault
  231.                    )
  232. {
  233.    // This function attempts to install global data for this
  234.    // application in the HKEY_LOCAL_MACHINE portion of the
  235.    // registry database. It is called from InstallDlgProc.
  236.  
  237.    // The parameter hwnd denotes the window associated with this
  238.    // call to StoreAppConfig. It's used with the calls to MPError
  239.    // below.
  240.  
  241.    // The parameter pszPathBuff refers to a null terminated
  242.    // string which defines where the new key should be located
  243.    // in the LOCAL_MACHINE tree.
  244.  
  245.    // The pszInstallName and pszInstallOrg parameters are text strings
  246.    // which denote the person and the organization which has installed
  247.    // this app in the HKEY_LOCAL_MACHINE portion of the registry.
  248.  
  249.    // The fTextWrapDefault is a boolean value which will be stored
  250.    // in the DEFAULT subkey. Values in the DEFAULT subkey are copied
  251.    // into the HKEY_PER_USER area during user initialization. (See
  252.    // the InitUser function below.)
  253.  
  254.    // We assume that hmtxRegGlobal is held when this function is called.
  255.  
  256.    HKEY  hkGlobal     = NULL;
  257.    HKEY  hkDefaults   = NULL;
  258.    PSID  psidAdmins   = NULL;
  259.    PSID  psidEveryone = NULL;
  260.    PACL  paclKey      = NULL;
  261.  
  262.    BOOL  fInstalled   = FALSE;
  263.  
  264.    long  lResult;
  265.    DWORD dwDisposition;
  266.  
  267.    BYTE  abEmptyStringSet[2];
  268.  
  269.    SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;
  270.    SID_IDENTIFIER_AUTHORITY  WorldSidAuthority= SECURITY_WORLD_SID_AUTHORITY;
  271.  
  272.    SECURITY_ATTRIBUTES sa;
  273.    SECURITY_DESCRIPTOR sdPermissions;
  274.    // Next we'll setup the security attributes we're going to
  275.    // use with the application's global key.
  276.  
  277.    sa.nLength              = sizeof(SECURITY_ATTRIBUTES);
  278.    sa.bInheritHandle       = FALSE;
  279.    sa.lpSecurityDescriptor = &sdPermissions;
  280.  
  281.    // Here we're creating a System Identifier (SID) to represent
  282.    // the Admin group.
  283.  
  284.    if (!AllocateAndInitializeSid
  285.           (&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
  286.                                    DOMAIN_ALIAS_RID_ADMINS,
  287.                                    0, 0, 0, 0, 0, 0,
  288.                                    &psidAdmins
  289.           )
  290.       )
  291.       goto security_failure;
  292.  
  293.    // Now we'll construct a System Identifier which represents
  294.    // all users.
  295.  
  296.    if (!AllocateAndInitializeSid
  297.           (&WorldSidAuthority, 1, SECURITY_WORLD_RID,
  298.            0, 0, 0, 0, 0, 0, 0,
  299.            &psidEveryone
  300.           )
  301.       )
  302.       goto security_failure;
  303.  
  304.    if (!InitializeSecurityDescriptor(&sdPermissions,
  305.                                      SECURITY_DESCRIPTOR_REVISION1
  306.                                     )
  307.       )
  308.       goto security_failure;
  309.  
  310.    // We want the admin group to own this key.
  311.  
  312.    if (!SetSecurityDescriptorOwner(&sdPermissions, psidAdmins, 0))
  313.       goto security_failure;
  314.  
  315.    // Finally we must allocate and construct the discretionary
  316.    // access control list (DACL) for the key.
  317.  
  318.    // Note that _alloca allocates memory on the stack frame
  319.    // which will automatically be deallocated when this routine
  320.    // exits.
  321.  
  322.    if (!(paclKey= (PACL) _alloca(ACL_BUFFER_SIZE)))
  323.       goto memory_limited;
  324.  
  325.    if (!InitializeAcl(paclKey, ACL_BUFFER_SIZE, ACL_REVISION2))
  326.       goto security_failure;
  327.  
  328.    // Our DACL will contain two access control entries (ACEs). One which allows
  329.    // members of the Admin group complete access to the key, and one which gives
  330.    // read-only access to everyone.
  331.  
  332.    if (!AddAccessAllowedAce(paclKey, ACL_REVISION2, KEY_ALL_ACCESS, psidAdmins))
  333.       goto security_failure;
  334.  
  335.    if (!AddAccessAllowedAce(paclKey, ACL_REVISION2, KEY_READ, psidEveryone))
  336.       goto security_failure;
  337.  
  338.    // We must bind this DACL to the security descriptor...
  339.  
  340.    if (!SetSecurityDescriptorDacl(&sdPermissions, TRUE, paclKey, FALSE))
  341.       goto security_failure;
  342.  
  343.    // Now we'll attempt to create the key with the security attributes...
  344.  
  345.    lResult= RegCreateKeyEx(HKEY_LOCAL_MACHINE, pszPathBuff, 0,
  346.                            "Application Global Data", REG_OPTION_NON_VOLATILE,
  347.                            KEY_ALL_ACCESS,
  348.                            &sa, &hkGlobal, &dwDisposition
  349.                           );
  350.  
  351.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  352.  
  353.    // Usually the disposition value will indicate that we've created a
  354.    // new key. Sometimes it may instead state that we've opened an existing
  355.    // key. This can happen when installation is incomplete and interrupted,
  356.    // say by loss of electrical power.
  357.  
  358.    if (   dwDisposition != REG_CREATED_NEW_KEY
  359.        && dwDisposition != REG_OPENED_EXISTING_KEY
  360.       ) goto registry_access_error;
  361.  
  362.    // Now we'll add two values to the global key.
  363.  
  364.    // These values are simple strings which identify the name and
  365.    // organization associated with this installation.
  366.  
  367.    lResult= RegSetValueEx(hkGlobal, KEY_VALUE_INSTALL_NAME, 0, REG_SZ,
  368.                                     pszInstallName, strlen(pszInstallName)+1
  369.                          );
  370.  
  371.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  372.  
  373.    lResult= RegSetValueEx(hkGlobal, KEY_VALUE_INSTALL_ORG, 0, REG_SZ,
  374.                                     pszInstallOrg, strlen(pszInstallOrg)+1
  375.                          );
  376.  
  377.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  378.  
  379.    // We've created the global key. Now we must create the "Defaults" subkey
  380.    // and set its value(s).
  381.  
  382.    lResult= RegCreateKeyEx(hkGlobal, DEFAULTS_PATH, 0,
  383.                            "Defaults for Per-User Data",
  384.                            REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS,
  385.                            &sa, &hkDefaults, &dwDisposition
  386.                           );
  387.  
  388.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  389.  
  390.    // Usually the disposition value will indicate that we've created a
  391.    // new key. Sometimes it may instead state that we've opened an existing
  392.    // key. This can happen when installation is incomplete and interrupted,
  393.    // say by loss of electrical power.
  394.  
  395.    if (   dwDisposition != REG_CREATED_NEW_KEY
  396.        && dwDisposition != REG_OPENED_EXISTING_KEY
  397.       ) goto registry_access_error;
  398.  
  399.    // Now we'll add a collection of values to the Defaults subkey.
  400.    // When per-user data is constructed for a particular userid, these
  401.    // values will define the initial state of the per-user data.
  402.  
  403.    // In this demo application we store two items -- a word-wrap flag
  404.    // and a file name set.
  405.  
  406.    lResult= RegSetValueEx(hkDefaults, WORD_WRAP_DEFAULT, 0, REG_DWORD,
  407.                           (LPBYTE) &fTextWrapDefault,
  408.                           sizeof(fTextWrapDefault)
  409.                          );
  410.  
  411.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  412.  
  413.    abEmptyStringSet[0]= '\0';
  414.    abEmptyStringSet[1]= '\0';
  415.  
  416.    lResult= RegSetValueEx(hkDefaults, LAST_FILE_SET, 0, REG_MULTI_SZ,
  417.                           (LPBYTE) &abEmptyStringSet, 2
  418.                          );
  419.  
  420.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  421.  
  422.    // Finally, we'll force our registry data out to disk via the
  423.    // flush key api:
  424.  
  425.    lResult= RegFlushKey(hkGlobal);
  426.  
  427.    // Then we'll write out the REG_INSTALLED flag. Note that its
  428.    // value is unimportant. Only its existence matters.
  429.  
  430.    fInstalled= TRUE;
  431.  
  432.    lResult= RegSetValueEx(hkGlobal, REG_INSTALLED, 0, REG_DWORD,
  433.                           (LPBYTE) &fInstalled, sizeof(fInstalled)
  434.                          );
  435.  
  436.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  437.  
  438.    RegCloseKey(hkGlobal);
  439.  
  440.    RegCloseKey(hkDefaults);
  441.  
  442.    FreeSid(psidAdmins);
  443.  
  444.    return TRUE;
  445.  
  446. registry_access_error:
  447.  
  448.    MPError(hwnd, MB_OK | MB_ICONHAND, IDS_REG_ACCESS_ERROR, NULL);
  449.  
  450.    // We've constructed some, but not all of the global key state.
  451.    // So we must remove any keys we created. The Defaults key must
  452.    // be deleted first before the Global key can be deleted.
  453.  
  454.    if (hkDefaults) RegDeleteKey(hkGlobal, DEFAULTS_PATH);
  455.  
  456.    if (hkGlobal) RegDeleteKey(HKEY_LOCAL_MACHINE, pszPathBuff);
  457.  
  458.    goto clean_up_after_failure;
  459.  
  460. memory_limited:
  461.  
  462.    MPError(hwnd, MB_OK | MB_ICONHAND, IDS_MEMORY_LIMITED, NULL);
  463.    goto clean_up_after_failure;
  464.  
  465. security_failure:
  466.  
  467.    MPError(hwnd, MB_OK | MB_ICONHAND, IDS_SECURITY_FAIL_I, NULL);
  468.  
  469. clean_up_after_failure:
  470.  
  471.    if (psidAdmins  ) FreeSid(psidAdmins  );
  472.    if (psidEveryone) FreeSid(psidEveryone);
  473.  
  474.    return FALSE;
  475. }
  476.  
  477. PSID GetCurrentUserInfo()
  478. {
  479.    // This function returns security information about the person who owns
  480.    // this thread.
  481.  
  482.    HANDLE htkThread;
  483.  
  484.    TOKEN_USER *ptu;
  485.    DWORD      cbtu;
  486.  
  487.    TOKEN_GROUPS *ptg = NULL;
  488.    SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;
  489.  
  490.    // First we must open a handle to the access token for this thread.
  491.  
  492.    if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, FALSE, &htkThread))
  493.       if (GetLastError() == ERROR_NO_TOKEN)
  494.       {
  495.          // If the thread does not have an access token, we'll examine the
  496.          // access token associated with the process.
  497.  
  498.          if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &htkThread))
  499.          return NULL;
  500.       }
  501.       else return NULL;
  502.  
  503.  
  504.    if (GetTokenInformation(htkThread, TokenUser, NULL, 0, &cbtu))
  505.       return NULL;
  506.  
  507.    // Here we verify that GetTokenInformation failed for lack of a large
  508.    // enough buffer.
  509.  
  510.    if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
  511.       return NULL;
  512.  
  513.    // Now we allocate a buffer for the group information.
  514.    // Since _alloca allocates on the stack, we don't have
  515.    // to explicitly deallocate it. That happens automatically
  516.    // when we exit this function.
  517.  
  518.    if (!(ptu= LocalAlloc(LPTR, cbtu))) return NULL;
  519.  
  520.    // Now we ask for the user information again.
  521.    // This may fail if an administrator has changed SID information
  522.    // for this user.
  523.  
  524.    if (!GetTokenInformation(htkThread, TokenUser, ptu, cbtu, &cbtu))
  525.       return FALSE;
  526.  
  527. // if (GetTokenInformation(htkThread, TokenUser, &tu, sizeof(tu), &cbtu))
  528. //    return NULL;
  529.  
  530.    return ptu;
  531. }
  532.  
  533. BOOL InitUser(HKEY hkGlobal, PSZ pszPathBuff)
  534. {
  535.    // This function sets up the per-user key area for a new user.
  536.    // It will be called the very first time a user runs the application.
  537.  
  538.    // The initial per-user information is copied over from a set of defaults
  539.    // stored in the global key area.
  540.  
  541.    // We assume that hkGlobal is a registry key for the global area
  542.    // used by this application. We assume pszPathBuff defines where
  543.    // the per-user data should be stored in the CURRENT_USER tree.
  544.  
  545.    // We also assume that hmtxRegPerUser is held when this function is called.
  546.  
  547.    HKEY hkPerUser  = NULL;
  548.    HKEY hkDefaults = NULL;
  549.  
  550.    BOOL fInstalled= FALSE;
  551.  
  552.    LONG lResult;
  553.  
  554.    DWORD dwType, cbData;
  555.  
  556.    PSZ pszFileList;
  557.  
  558.    DWORD dwDisposition;
  559.  
  560.    TOKEN_USER *ptu = NULL;
  561.  
  562.    PSID psidUser   = NULL,
  563.         psidAdmins = NULL;
  564.  
  565.    PACL  paclKey = NULL;
  566.  
  567.    BOOL fWordWrap;
  568.    PSZ  pszFileSet = NULL;
  569.  
  570.    SID_IDENTIFIER_AUTHORITY SystemSidAuthority= SECURITY_NT_AUTHORITY;
  571.  
  572.    SECURITY_ATTRIBUTES sa;
  573.    SECURITY_DESCRIPTOR sdPermissions;
  574.  
  575.  
  576.    // First we'll setup the security attributes we're going to
  577.    // use with the application's global key.
  578.  
  579.    sa.nLength              = sizeof(SECURITY_ATTRIBUTES);
  580.    sa.bInheritHandle       = FALSE;
  581.    sa.lpSecurityDescriptor = &sdPermissions;
  582.  
  583.    // Here we're creating a System Identifier (SID) to represent
  584.    // the Admin group.
  585.  
  586.    if (!AllocateAndInitializeSid
  587.           (&SystemSidAuthority, 2, SECURITY_BUILTIN_DOMAIN_RID,
  588.                                    DOMAIN_ALIAS_RID_ADMINS,
  589.                                    0, 0, 0, 0, 0, 0,
  590.                                    &psidAdmins
  591.           )
  592.       )
  593.       goto security_failure;
  594.  
  595.    // We also need a SID for the current user.
  596.  
  597.    if (   !(ptu= GetCurrentUserInfo())
  598.        || !(psidUser= ptu->User.Sid)
  599.       ) goto security_failure;
  600.  
  601.    if (!InitializeSecurityDescriptor(&sdPermissions,
  602.                                      SECURITY_DESCRIPTOR_REVISION1
  603.                                     )
  604.       )
  605.       goto security_failure;
  606.  
  607.    // We want the current user to own this key.
  608.  
  609.    if (!SetSecurityDescriptorOwner(&sdPermissions, psidUser, 0))
  610.       goto security_failure;
  611.  
  612.    // Finally we must allocate and construct the discretionary
  613.    // access control list (DACL) for the key.
  614.  
  615.    // Note that _alloca allocates memory on the stack frame
  616.    // which will automatically be deallocated when this routine
  617.    // exits.
  618.  
  619.    if (!(paclKey= (PACL) _alloca(ACL_BUFFER_SIZE)))
  620.       goto memory_limited;
  621.  
  622.    if (!InitializeAcl(paclKey, ACL_BUFFER_SIZE, ACL_REVISION2))
  623.       goto security_failure;
  624.  
  625.    // Our DACL will two access control entries (ACEs). The first ACE
  626.    // provides full access to the current user. The second ACE gives
  627.    // the Admin group full access. By default all other users will have
  628.    // no access to the key.
  629.  
  630.    // The reason for admin access is to allow an administrator to
  631.    // run special utilties to cleanup inconsistencies and disasters
  632.    // in the per-user data area.
  633.  
  634.    if (!AddAccessAllowedAce(paclKey, ACL_REVISION2, KEY_ALL_ACCESS, psidUser))
  635.       goto security_failure;
  636.  
  637.    if (!AddAccessAllowedAce(paclKey, ACL_REVISION2, KEY_ALL_ACCESS, psidAdmins))
  638.       goto security_failure;
  639.  
  640.    // We must bind this DACL to the security descriptor...
  641.  
  642.    if (!SetSecurityDescriptorDacl(&sdPermissions, TRUE, paclKey, FALSE))
  643.       goto security_failure;
  644.  
  645.    // Now we'll attempt to create the key with the security attributes...
  646.  
  647.    lResult= RegCreateKeyEx(HKEY_CURRENT_USER, pszPathBuff, 0,
  648.                            "Application Per-User Data", REG_OPTION_NON_VOLATILE,
  649.                            KEY_ALL_ACCESS,
  650.                            &sa, &hkPerUser, &dwDisposition
  651.                           );
  652.  
  653.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  654.  
  655.    // Usually the disposition value will indicate that we've created a
  656.    // new key. Sometimes it may instead state that we've opened an existing
  657.    // key. This can happen when installation is incomplete and interrupted,
  658.    // say by loss of electrical power.
  659.  
  660.    if (   dwDisposition != REG_CREATED_NEW_KEY
  661.        && dwDisposition != REG_OPENED_EXISTING_KEY
  662.       ) goto registry_access_error;
  663.  
  664.    // Now we must open the Defaults subkey in the global area.
  665.  
  666.    lResult= RegOpenKeyEx(hkGlobal, DEFAULTS_PATH, 0, KEY_READ, &hkDefaults);
  667.  
  668.    if (lResult != ERROR_SUCCESS)
  669.    {
  670.       // Can't open the "Defaults" subkey for read access.
  671.       // This shouldn't happen normally. The algorithmic sequence for
  672.       // installing this application and then creating user profile
  673.       // data should prevent it.
  674.  
  675.       // It is possible for someone running RegEdit32 to delete the key,
  676.       // however.
  677.  
  678.       goto registry_damage_error;
  679.    }
  680.  
  681.    // Now we'll copy two default values to the per-user key:
  682.    //
  683.    //   -- a word-wrap flag
  684.    //   -- a list of file names
  685.  
  686.    cbData= sizeof(fWordWrap);
  687.  
  688.    lResult= RegQueryValueEx(hkDefaults, WORD_WRAP_DEFAULT, NULL,
  689.                             &dwType, (LPBYTE) &fWordWrap, &cbData
  690.                            );
  691.  
  692.    if (   lResult != ERROR_SUCCESS
  693.        || dwType  != REG_DWORD
  694.       ) goto registry_damage_error;
  695.  
  696.  
  697.    lResult= RegSetValueEx(hkPerUser, WORD_WRAP_DEFAULT, 0, REG_DWORD,
  698.                           (LPBYTE) &fWordWrap, sizeof(fWordWrap)
  699.                          );
  700.  
  701.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  702.  
  703.    cbData= 0;
  704.  
  705.    lResult= RegQueryValueEx(hkDefaults, LAST_FILE_SET, NULL, &dwType,
  706.                             NULL, &cbData
  707.                            );
  708.  
  709.    if (   lResult != ERROR_SUCCESS
  710.        || dwType != REG_MULTI_SZ
  711.       )
  712.      goto registry_damage_error;
  713.  
  714.    pszFileList= (PSZ) _alloca(cbData);
  715.  
  716.    if (!pszFileList) goto memory_limited;
  717.  
  718.    lResult= RegQueryValueEx(hkDefaults, LAST_FILE_SET, NULL, &dwType,
  719.                             (LPBYTE) pszFileList, &cbData
  720.                            );
  721.  
  722.    if (lResult != ERROR_SUCCESS) goto registry_damage_error;
  723.  
  724.    lResult= RegSetValueEx(hkPerUser, LAST_FILE_SET, 0, REG_MULTI_SZ,
  725.                           (LPBYTE) pszFileList, cbData
  726.                          );
  727.  
  728.    if (lResult != ERROR_SUCCESS) goto registry_access_error;
  729.  
  730.    // Now we force our registry subtree out to disk
  731.    // via the flush key api:
  732.  
  733.    lResult= RegFlushKey(hkPerUser);
  734.  
  735.    // Finally we store the REG_INSTALLED value in the registry to
  736.    // indicate that installation has completed. Note that its value
  737.    // is irrelevant. Only its presence or absence is meaningful.
  738.  
  739.    fInstalled= TRUE;
  740.  
  741.    lResult= RegSetValueEx(hkPerUser, REG_INSTALLED, 0, REG_DWORD,
  742.                           (LPBYTE) &fInstalled, sizeof(fInstalled)
  743.                          );
  744.  
  745.    RegCloseKey(hkPerUser);
  746.    RegCloseKey(hkDefaults);
  747.  
  748.    FreeSid(psidAdmins);
  749.  
  750.    LocalFree(ptu);
  751.  
  752.    return(TRUE);
  753.  
  754. registry_damage_error:
  755.  
  756.    // We'll display a warning that the registry info has
  757.    // been damaged.
  758.  
  759.    MPError(hwndFrame, MB_OK | MB_ICONHAND, IDS_MUTEX_LOGIC_ERR, NULL);
  760.  
  761.    // Then we discard the REG_INSTALLED flag to insure that a reinstallation
  762.    // can proceed.
  763.  
  764.    RegDeleteValue(hkGlobal, REG_INSTALLED);
  765.  
  766.    goto clean_up_registry_keys;
  767.  
  768. registry_access_error:
  769.  
  770.    MPError(hwndFrame, MB_OK | MB_ICONHAND, IDS_REG_ACCESS_ERROR, NULL);
  771.  
  772. clean_up_registry_keys:
  773.  
  774.    // We've constructed some, but not all of the global key state.
  775.    // So we must remove any keys we created. The Defaults key must
  776.    // be deleted first before the Global key can be deleted.
  777.  
  778.    if (hkPerUser) RegDeleteKey(HKEY_CURRENT_USER, pszPathBuff);
  779.  
  780.    if (hkDefaults) RegCloseKey(hkDefaults);
  781.  
  782.    goto clean_up_after_failure;
  783.  
  784. memory_limited:
  785.  
  786.    MPError(hwndFrame, MB_OK | MB_ICONHAND, IDS_MEMORY_LIMITED, NULL);
  787.    goto clean_up_after_failure;
  788.  
  789. security_failure:
  790.  
  791.    MPError(hwndFrame, MB_OK | MB_ICONHAND, IDS_SECURITY_FAIL_I, NULL);
  792.  
  793. clean_up_after_failure:
  794.  
  795.    if (psidAdmins) FreeSid(psidAdmins  );
  796.    if (ptu       ) LocalFree(psidUser);
  797.  
  798.    return FALSE;
  799. }
  800.  
  801. BOOL CreateAppKeys()
  802. {
  803.    long lResult;
  804.    BOOL fSuccess;
  805.    BYTE abPathBuffer[MAX_PATH];
  806.    BYTE abMutexName [MAX_PATH];
  807.  
  808.    BOOL  fInstalled= FALSE;
  809.    DWORD dwType, cbData;
  810.  
  811.    // Here we're constructing registry key handles for global data and
  812.    // per-user data. The global data is kept in the HKEY_LOCAL_MACHINE
  813.    // tree, and the per-user data is kept in the HKEY_CURRENT_USER tree.
  814.    // For both trees the data for this program is kept in the same
  815.    // relative location.
  816.  
  817.    // The registry handles are returned in the global variables hkGlobal
  818.    // and hkPerUser. In addition two global mutex handles (hmtxRegGlobal
  819.    // and hmtxRegPerUser are created. The mutexes are used to serialize
  820.    // registry accesses among instances of this application at start-up.
  821.    // That serialization is necessary to insure that a complete registry
  822.    // environment is present when the application starts.
  823.  
  824.    // Note that individual registry reads and writes do not need to be
  825.    // serialized -- only collections of reads and writes which must be
  826.    // consistent with each other.
  827.  
  828.    // Note all so the use of the registry value REG_INSTALLED. It is the
  829.    // last value written to the HKEY_LOCAL_MACHINE and HKEY_CURRENT_USER
  830.    // registry tree during the installation sequence. Whenever this application
  831.    // starts, it looks for REG_INSTALLED as a sign that the registry has
  832.    // been properly setup. If it doesn't find it, we assume that either
  833.    // setup hasn't been done or has been done incompletely.
  834.  
  835.    // First we'll construct a relative path to the keys we're going to
  836.    // open. The path has the structure:
  837.  
  838.    //    Software \ <Company Name> \ <Application Name> \ <Version Number>
  839.  
  840.    wsprintf(abPathBuffer, "Software\\%s\\%s\\%s",
  841.             COMPANY_NAME, APPLICATION_NAME, VERSION_NUMBER
  842.            );
  843.  
  844.    // Since two instance of this application could be running simultaneously,
  845.    // we use a named mutext to serialize registry accesses.
  846.  
  847.    wsprintf(abMutexName, "Software/%s/%s/%s/Globals",
  848.             COMPANY_NAME, APPLICATION_NAME, VERSION_NUMBER
  849.            );
  850.  
  851.    if (!(hmtxRegGlobal= CreateMutex(NULL, FALSE, abMutexName))) goto failure_exit;
  852.  
  853.    wsprintf(abMutexName, "Software/%s/%s/%s/PerUser",
  854.             COMPANY_NAME, APPLICATION_NAME, VERSION_NUMBER
  855.            );
  856.  
  857.    if (!(hmtxRegPerUser= CreateMutex(NULL, FALSE, abMutexName))) goto failure_exit;
  858.  
  859.  
  860.    // First we'll attempt to open a key to the global data...
  861.  
  862.    for (lResult= ~ERROR_SUCCESS; lResult != ERROR_SUCCESS; )
  863.    {
  864.       // We serialize the code in this loop via hmtxRegGlobal.
  865.  
  866.       lResult= WaitForSingleObject(hmtxRegGlobal, 0xFFFFFFFF);
  867.  
  868.       if (   lResult != WAIT_ABANDONED
  869.           && lResult != WAIT_OBJECT_0
  870.          ) goto failure_exit;
  871.  
  872.       lResult= RegOpenKeyEx(HKEY_LOCAL_MACHINE, abPathBuffer, 0, KEY_READ,
  873.                                                 &hkGlobal
  874.                            );
  875.  
  876.       // Note that we also look for the REG_INSTALLED flag.
  877.  
  878.       cbData= sizeof(fInstalled);
  879.  
  880.       if (   ERROR_SUCCESS != lResult
  881.           || ERROR_SUCCESS != RegQueryValueEx(hkGlobal, REG_INSTALLED, 0,
  882.                                               &dwType, (LPBYTE) &fInstalled,
  883.                                               &cbData
  884.                                              )
  885.          )
  886.       {
  887.          hkGlobal= NULL;
  888.  
  889.          // If we can't open the global key, this probably means that the
  890.          // application hasn't been installed yet. So we'll try to install it.
  891.  
  892.          // If the installation succeeds, we'll try opening the global key
  893.          // again. Otherwise we'll just fail.
  894.  
  895.          fSuccess= InstallApp(abPathBuffer);
  896.  
  897.          ReleaseMutex(hmtxRegGlobal);
  898.  
  899.          if (fSuccess) continue;
  900.          else goto failure_exit;
  901.       }
  902.  
  903.       ReleaseMutex(hmtxRegGlobal);
  904.    }
  905.  
  906.    for (lResult= ~ERROR_SUCCESS; lResult != ERROR_SUCCESS; )
  907.    {
  908.       // We serialize the code in this loop via hmtxRegPerUser.
  909.  
  910.       lResult= WaitForSingleObject(hmtxRegPerUser, 0xFFFFFFFF);
  911.  
  912.       if (   lResult != WAIT_ABANDONED
  913.           && lResult != WAIT_OBJECT_0
  914.          ) goto failure_exit;
  915.  
  916.       // Now we'll try to open an handle to the per-user key for this user and
  917.       // this application.
  918.  
  919.       lResult= RegOpenKeyEx(HKEY_CURRENT_USER, abPathBuffer, 0, KEY_ALL_ACCESS,
  920.                                                &hkPerUser
  921.                            );
  922.  
  923.       // Note that we also look for the REG_INSTALLED flag.
  924.  
  925.       cbData= sizeof(fInstalled);
  926.  
  927.       if (   ERROR_SUCCESS != lResult
  928.           || ERROR_SUCCESS != RegQueryValueEx(hkPerUser, REG_INSTALLED, 0,
  929.                                               &dwType, (LPBYTE) &fInstalled,
  930.                                               &cbData
  931.                                              )
  932.          )
  933.       {
  934.          // The per-user open call failed. We infer that this is the first
  935.          // time this user has invoked this application. So next we'll try
  936.          // to initial a per-user area for them.
  937.  
  938.          hkPerUser= NULL;
  939.  
  940.          fSuccess= InitUser(hkGlobal, abPathBuffer);
  941.  
  942.          ReleaseMutex(hmtxRegPerUser);
  943.  
  944.          if (fSuccess) continue;
  945.          else goto failure_exit;
  946.       }
  947.  
  948.       ReleaseMutex(hmtxRegPerUser);
  949.    }
  950.  
  951.    return TRUE;
  952.  
  953. failure_exit:
  954.  
  955.    // When we're exiting because of a failure, we must clean up
  956.    // by closing any handles we've created along the way.
  957.  
  958.    if (hmtxRegGlobal) CloseHandle(hmtxRegGlobal);
  959.  
  960.    if (hmtxRegPerUser) CloseHandle(hmtxRegPerUser);
  961.  
  962.    if (hkGlobal) RegCloseKey(hkGlobal);
  963.  
  964.    return FALSE;
  965. }
  966.  
  967. BOOL LoadConfiguration()
  968. {
  969.    // This routine loads the per-user configuration data.
  970.  
  971.    // Two items are kept as configuration data:
  972.    //
  973.    //   fTextWrapDefault -- a BOOL which defines whether the text windows
  974.    //                       fold text at the right edge of the window.
  975.    //
  976.    //   A REG_MULTI_SZ list of file names.
  977.    //
  978.    //     This list represents the files which were open at the end of the
  979.    //     last RegMPad session. We'll attempt to reopen those files.
  980.  
  981.    LONG  lResult, cbData;
  982.    DWORD dwType;
  983.    PSZ   pszFileList= NULL;
  984.  
  985.    CHAR *pc;
  986.  
  987.    cbData= sizeof(fTextWrapDefault);
  988.  
  989.    lResult= RegQueryValueEx(hkPerUser, WORD_WRAP_DEFAULT, NULL,
  990.                             &dwType, (LPBYTE) &fTextWrapDefault, &cbData
  991.                            );
  992.  
  993.    if (lResult != ERROR_SUCCESS) return FALSE;
  994.  
  995.    cbData= 0;
  996.  
  997.    lResult= RegQueryValueEx(hkPerUser, LAST_FILE_SET, NULL, &dwType,
  998.                             NULL, &cbData
  999.                            );
  1000.  
  1001.    if (   lResult != ERROR_SUCCESS
  1002.        || dwType  != REG_MULTI_SZ
  1003.       ) return FALSE;
  1004.  
  1005.    pszFileList= (PSZ) _alloca(cbData);
  1006.  
  1007.    if (!pszFileList) return FALSE;
  1008.  
  1009.    lResult= RegQueryValueEx(hkPerUser, LAST_FILE_SET, NULL, &dwType,
  1010.                             (LPBYTE) pszFileList, &cbData
  1011.                            );
  1012.  
  1013.    if (lResult != ERROR_SUCCESS) return FALSE;
  1014.  
  1015.    for (pc= pszFileList; *pc; )
  1016.    {
  1017.       if (!AlreadyOpen(pc)) AddFile(pc);
  1018.  
  1019.       for (; *pc++; );
  1020.    }
  1021.  
  1022.    return TRUE;
  1023. }
  1024.  
  1025. BOOL SaveConfiguration()
  1026. {
  1027.    // This routine saves the per-user configuration data to the registry.
  1028.    // That data consists of two items:
  1029.    //
  1030.    //   fTextWrapDefault -- a BOOL which defines whether the text windows
  1031.    //                       fold text at the right edge of the window.
  1032.    //
  1033.    //   A REG_MULTI_SZ list of file names.
  1034.    //
  1035.    //     This list represents the files which were open at the end of the
  1036.    //     last RegMPad session.
  1037.  
  1038.    LONG  lResult;
  1039.    HWND  hwnd;
  1040.    PBYTE pb, pbNext;
  1041.    LONG  cb, cbTotal;
  1042.  
  1043.    lResult= RegSetValueEx(hkPerUser, WORD_WRAP_DEFAULT, 0, REG_DWORD,
  1044.                           (CONST LPBYTE) &fTextWrapDefault,
  1045.                           sizeof(fTextWrapDefault)
  1046.                          );
  1047.  
  1048.    if (lResult != ERROR_SUCCESS) return FALSE;
  1049.  
  1050.    for (hwnd= GetWindow(hwndMDIClient, GW_CHILD), cbTotal= 1;
  1051.         hwnd;
  1052.         hwnd= GetWindow(hwnd, GW_HWNDNEXT)
  1053.        )
  1054.    {
  1055.       // Skip if this is an icon title window...
  1056.  
  1057.       if (GetWindow(hwnd, GW_OWNER)) continue;
  1058.  
  1059.       if (GetWindowWord(hwnd, GWW_UNTITLED)) continue;
  1060.  
  1061.       cbTotal += GetWindowTextLength(hwnd) + 1;
  1062.    }
  1063.  
  1064.    pb= (PBYTE) _alloca(cbTotal);
  1065.  
  1066.    if (!pb) return FALSE;
  1067.  
  1068.    for (hwnd= GetWindow(hwndMDIClient, GW_CHILD), pbNext= pb;
  1069.         hwnd;
  1070.         hwnd= GetWindow(hwnd, GW_HWNDNEXT)
  1071.        )
  1072.    {
  1073.       // Skip if this is an icon title window...
  1074.  
  1075.       if (GetWindow(hwnd, GW_OWNER)) continue;
  1076.  
  1077.       if (GetWindowWord(hwnd, GWW_UNTITLED)) continue;
  1078.  
  1079.       cb= GetWindowTextLength(hwnd);
  1080.  
  1081.       GetWindowText(hwnd, pbNext, cb+1);
  1082.  
  1083.       pbNext+= cb+1;
  1084.    }
  1085.  
  1086.    *pbNext= 0;
  1087.  
  1088.    lResult= RegSetValueEx(hkPerUser, LAST_FILE_SET, 0, REG_MULTI_SZ,
  1089.                           pb, cbTotal
  1090.                          );
  1091.  
  1092.    return (lResult == ERROR_SUCCESS)? TRUE : FALSE;
  1093. }
  1094.