|
|
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.