Annotation of mstools/samples/regmpad/regdb.c, revision 1.1.1.1

1.1       root        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:    HANDLE hkPerUser  = NULL;
                    548:    HANDLE 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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.