|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.