REGDB.C
上传用户:bangxh
上传日期:2007-01-31
资源大小:42235k
文件大小:35k
源码类别:

Windows编程

开发平台:

Visual C++

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