Added a per-drive FailReadOnly flag, and removed the global
[wine/gsoc_dplay.git] / misc / registry.c
blob66754d21fa15f01d7321c2518e0bef3bc17e5891
1 /*
2 * Registry Functions
4 * Copyright 1996 Marcus Meissner
5 * Copyright 1998 Matthew Becker
6 * Copyright 1999 Sylvain St-Germain
8 * December 21, 1997 - Kevin Cozens
9 * Fixed bugs in the _w95_loadreg() function. Added extra information
10 * regarding the format of the Windows '95 registry files.
12 * NOTES
13 * When changing this file, please re-run the regtest program to ensure
14 * the conditions are handled properly.
16 * TODO
17 * Security access
18 * Option handling
19 * Time for RegEnumKey*, RegQueryInfoKey*
22 #include "config.h"
24 #include <stdlib.h>
25 #include <string.h>
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <ctype.h>
29 #include <errno.h>
30 #ifdef HAVE_SYS_ERRNO_H
31 #include <sys/errno.h>
32 #endif
33 #include <sys/types.h>
34 #include <fcntl.h>
35 #include <sys/fcntl.h>
36 #include <sys/stat.h>
37 #include <assert.h>
38 #include <time.h>
39 #include "windef.h"
40 #include "winbase.h"
41 #include "wine/winbase16.h"
42 #include "wine/winestring.h"
43 #include "winerror.h"
44 #include "file.h"
45 #include "heap.h"
46 #include "debugtools.h"
47 #include "options.h"
48 #include "winreg.h"
49 #include "server.h"
50 #include "services.h"
52 DEFAULT_DEBUG_CHANNEL(reg);
54 static void REGISTRY_Init(void);
55 /* FIXME: following defines should be configured global ... */
57 /* NOTE: do not append a /. linux' mkdir() WILL FAIL if you do that */
58 #define WINE_PREFIX "/.wine"
59 #define SAVE_USERS_DEFAULT ETCDIR"/wine.userreg"
60 #define SAVE_LOCAL_MACHINE_DEFAULT ETCDIR"/wine.systemreg"
62 /* relative in ~user/.wine/ : */
63 #define SAVE_CURRENT_USER "user.reg"
64 #define SAVE_LOCAL_USERS_DEFAULT "wine.userreg"
65 #define SAVE_LOCAL_MACHINE "system.reg"
67 #define KEY_REGISTRY "Software\\The WINE team\\WINE\\Registry"
68 #define VAL_SAVEUPDATED "SaveOnlyUpdatedKeys"
71 /* what valuetypes do we need to convert? */
72 #define UNICONVMASK ((1<<REG_SZ)|(1<<REG_MULTI_SZ)|(1<<REG_EXPAND_SZ))
75 static void *xmalloc( size_t size )
77 void *res;
79 res = malloc (size ? size : 1);
80 if (res == NULL) {
81 WARN("Virtual memory exhausted.\n");
82 exit (1);
84 return res;
88 * QUESTION
89 * Are these doing the same as HEAP_strdupAtoW and HEAP_strdupWtoA?
90 * If so, can we remove them?
91 * ANSWER
92 * No, the memory handling functions are called very often in here,
93 * just replacing them by HeapAlloc(SystemHeap,...) makes registry
94 * loading 100 times slower. -MM
96 static LPWSTR strdupA2W(LPCSTR src)
98 if(src) {
99 LPWSTR dest=xmalloc(2*strlen(src)+2);
100 lstrcpyAtoW(dest,src);
101 return dest;
103 return NULL;
106 LPWSTR strcvtA2W(LPCSTR src, int nchars)
109 LPWSTR dest = xmalloc (2 * nchars + 2);
111 lstrcpynAtoW(dest,src,nchars+1);
112 return dest;
117 /******************************************************************************
118 * REGISTRY_Init [Internal]
119 * Registry initialisation, allocates some default keys.
121 static void REGISTRY_Init(void) {
122 HKEY hkey;
123 char buf[200];
125 TRACE("(void)\n");
127 RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
128 RegCloseKey(hkey);
130 /* This was an Open, but since it is called before the real registries
131 are loaded, it was changed to a Create - MTB 980507*/
132 RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
133 RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
134 RegCloseKey(hkey);
136 /* \\SOFTWARE\\Microsoft\\Window NT\\CurrentVersion
137 * CurrentVersion
138 * CurrentBuildNumber
139 * CurrentType
140 * string RegisteredOwner
141 * string RegisteredOrganization
144 /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
145 * string SysContact
146 * string SysLocation
147 * SysServices
149 if (-1!=gethostname(buf,200)) {
150 RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
151 RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
152 RegCloseKey(hkey);
157 /************************ SAVE Registry Function ****************************/
159 #define REGISTRY_SAVE_VERSION 0x00000001
161 /* Registry saveformat:
162 * If you change it, increase above number by 1, which will flush
163 * old registry database files.
165 * Global:
166 * "WINE REGISTRY Version %d"
167 * subkeys....
168 * Subkeys:
169 * keyname
170 * valuename=lastmodified,type,data
171 * ...
172 * subkeys
173 * ...
174 * keyname,valuename,stringdata:
175 * the usual ascii characters from 0x00-0xff (well, not 0x00)
176 * and \uXXXX as UNICODE value XXXX with XXXX>0xff
177 * ( "=\\\t" escaped in \uXXXX form.)
178 * type,lastmodified:
179 * int
181 * FIXME: doesn't save 'class' (what does it mean anyway?), nor flags.
183 * [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry]
184 * SaveOnlyUpdatedKeys=yes
187 /* Same as RegSaveKey but with Unix pathnames */
188 static void save_key( HKEY hkey, const char *filename )
190 struct save_registry_request *req = get_req_buffer();
191 int count = 0;
192 DWORD ret;
193 HANDLE handle;
194 char *p;
195 char *name = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + 20 );
197 if (!name) return;
198 strcpy( name, filename );
199 if ((p = strrchr( name, '/' ))) p++;
200 else p = name;
202 for (;;)
204 sprintf( p, "reg%04x.tmp", count++ );
205 handle = FILE_CreateFile( name, GENERIC_WRITE, 0, NULL,
206 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, -1, TRUE );
207 if (handle != INVALID_HANDLE_VALUE) break;
208 if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) break;
211 if (handle != INVALID_HANDLE_VALUE)
213 req->hkey = hkey;
214 req->file = handle;
215 ret = server_call_noerr( REQ_SAVE_REGISTRY );
216 CloseHandle( handle );
217 if (ret) unlink( name );
218 else if (rename( name, filename ) == -1)
220 ERR( "Failed to move %s to %s: ", name, filename );
221 perror( "rename" );
222 unlink( name );
225 else ERR( "Failed to save registry to %s, err %ld\n", name, GetLastError() );
227 HeapFree( GetProcessHeap(), 0, name );
231 /******************************************************************************
232 * SHELL_SaveRegistryBranch [Internal]
234 * Saves main registry branch specified by hkey.
236 static void SHELL_SaveRegistryBranch(HKEY hkey)
238 char *fn, *home;
240 /* Find out what to save to, get from config file */
241 BOOL writeToHome = PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1);
242 BOOL writeToAlt = PROFILE_GetWineIniBool("registry","WritetoAltRegistries",1);
244 /* FIXME: does this check apply to all keys written below ? */
245 if (!(home = getenv( "HOME" )))
246 ERR("Failed to get homedirectory of UID %ld.\n",(long) getuid());
248 /* HKEY_LOCAL_MACHINE contains the HKEY_CLASSES_ROOT branch */
249 if (hkey == HKEY_CLASSES_ROOT) hkey = HKEY_LOCAL_MACHINE;
251 switch (hkey)
253 case HKEY_CURRENT_USER:
254 fn = xmalloc( MAX_PATHNAME_LEN );
255 if (writeToAlt && PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
256 fn, MAX_PATHNAME_LEN - 1))
257 save_key( HKEY_CURRENT_USER, fn );
258 free (fn);
260 if (home && writeToHome)
262 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
263 strlen(SAVE_CURRENT_USER) + 2 );
264 strcpy(fn,home);
265 strcat(fn,WINE_PREFIX);
267 /* create the directory. don't care about errorcodes. */
268 mkdir(fn,0755); /* drwxr-xr-x */
269 strcat(fn,"/"SAVE_CURRENT_USER);
270 save_key( HKEY_CURRENT_USER, fn );
271 free(fn);
273 break;
274 case HKEY_LOCAL_MACHINE:
275 /* Try first saving according to the defined location in .winerc */
276 fn = xmalloc ( MAX_PATHNAME_LEN);
277 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltLocalMachineFile", "",
278 fn, MAX_PATHNAME_LEN - 1))
279 save_key( HKEY_LOCAL_MACHINE, fn );
280 free (fn);
282 if (home && writeToHome)
284 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
285 strlen(SAVE_LOCAL_MACHINE) + 2);
286 strcpy(fn,home);
287 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
288 save_key( HKEY_LOCAL_MACHINE, fn );
289 free(fn);
291 break;
292 case HKEY_USERS:
293 fn = xmalloc( MAX_PATHNAME_LEN );
294 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltUserFile", "",
295 fn, MAX_PATHNAME_LEN - 1))
296 save_key( HKEY_USERS, fn );
297 free (fn);
299 if (home && writeToHome)
301 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
302 strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
303 strcpy(fn,home);
304 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
305 save_key( HKEY_USERS, fn );
306 free(fn);
308 break;
309 default:
310 ERR("unknown/invalid key handle !\n");
311 break;
316 /******************************************************************************
317 * SHELL_SaveRegistry [Internal]
319 void SHELL_SaveRegistry( void )
321 struct set_registry_levels_request *req = get_req_buffer();
322 char buf[4];
323 HKEY hkey;
324 int all, version;
326 TRACE("(void)\n");
328 all=0;
329 if (RegOpenKeyA(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS)
331 strcpy(buf,"yes");
333 else
335 DWORD len,junk,type;
337 len=4;
338 if ((ERROR_SUCCESS!=RegQueryValueExA( hkey,
339 VAL_SAVEUPDATED,
340 &junk,
341 &type,
342 buf,
343 &len)) || (type!=REG_SZ))
345 strcpy(buf,"yes");
347 RegCloseKey(hkey);
350 if (lstrcmpiA(buf,"yes")) all = 1;
352 version = PROFILE_GetWineIniBool( "registry", "UseNewFormat", 1 ) ? 2 : 1;
353 /* set saving level (0 for saving everything, 1 for saving only modified keys) */
354 req->current = 1;
355 req->saving = !all;
356 req->version = version;
357 server_call( REQ_SET_REGISTRY_LEVELS );
359 SHELL_SaveRegistryBranch(HKEY_CURRENT_USER);
360 SHELL_SaveRegistryBranch(HKEY_LOCAL_MACHINE);
361 SHELL_SaveRegistryBranch(HKEY_USERS);
364 /* Periodic save callback */
365 static void CALLBACK periodic_save( ULONG_PTR dummy )
367 SHELL_SaveRegistry();
370 /************************ LOAD Registry Function ****************************/
374 /******************************************************************************
375 * _find_or_add_key [Internal]
377 static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
379 HKEY subkey;
380 if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
381 if (keyname) free( keyname );
382 return subkey;
385 /******************************************************************************
386 * _find_or_add_value [Internal]
388 static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len )
390 RegSetValueExW( hkey, name, 0, type, data, len );
391 if (name) free( name );
392 if (data) free( data );
396 /******************************************************************************
397 * _wine_read_line [Internal]
399 * reads a line including dynamically enlarging the readbuffer and throwing
400 * away comments
402 static int _wine_read_line( FILE *F, char **buf, int *len )
404 char *s,*curread;
405 int mylen,curoff;
407 curread = *buf;
408 mylen = *len;
409 **buf = '\0';
410 while (1) {
411 while (1) {
412 s=fgets(curread,mylen,F);
413 if (s==NULL)
414 return 0; /* EOF */
415 if (NULL==(s=strchr(curread,'\n'))) {
416 /* buffer wasn't large enough */
417 curoff = strlen(*buf);
418 curread = realloc(*buf,*len*2);
419 if(curread == NULL) {
420 WARN("Out of memory");
421 return 0;
423 *buf = curread;
424 curread+= curoff;
425 mylen = *len; /* we filled up the buffer and
426 * got new '*len' bytes to fill
428 *len = *len * 2;
429 } else {
430 *s='\0';
431 break;
434 /* throw away comments */
435 if (**buf=='#' || **buf==';') {
436 curread = *buf;
437 mylen = *len;
438 continue;
440 if (s) /* got end of line */
441 break;
443 return 1;
447 /******************************************************************************
448 * _wine_read_USTRING [Internal]
450 * converts a char* into a UNICODE string (up to a special char)
451 * and returns the position exactly after that string
453 static char* _wine_read_USTRING( char *buf, LPWSTR *str )
455 char *s;
456 LPWSTR ws;
458 /* read up to "=" or "\0" or "\n" */
459 s = buf;
460 *str = (LPWSTR)xmalloc(2*strlen(buf)+2);
461 ws = *str;
462 while (*s && (*s!='\n') && (*s!='=')) {
463 if (*s!='\\')
464 *ws++=*((unsigned char*)s++);
465 else {
466 s++;
467 if (!*s) {
468 /* Dangling \ ... may only happen if a registry
469 * write was short. FIXME: What do to?
471 break;
473 if (*s=='\\') {
474 *ws++='\\';
475 s++;
476 continue;
478 if (*s!='u') {
479 WARN("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
480 *ws++='\\';
481 *ws++=*s++;
482 } else {
483 char xbuf[5];
484 int wc;
486 s++;
487 memcpy(xbuf,s,4);xbuf[4]='\0';
488 if (!sscanf(xbuf,"%x",&wc))
489 WARN("Strange escape sequence %s found in |%s|\n",xbuf,buf);
490 s+=4;
491 *ws++ =(unsigned short)wc;
495 *ws = 0;
496 return s;
500 /******************************************************************************
501 * _wine_loadsubkey [Internal]
503 * NOTES
504 * It seems like this is returning a boolean. Should it?
506 * RETURNS
507 * Success: 1
508 * Failure: 0
510 static int _wine_loadsubkey( FILE *F, HKEY hkey, int level, char **buf, int *buflen )
512 HKEY subkey;
513 int i;
514 char *s;
515 LPWSTR name;
517 TRACE("(%p,%x,%d,%s,%d)\n", F, hkey, level, debugstr_a(*buf), *buflen);
519 /* Good. We already got a line here ... so parse it */
520 subkey = 0;
521 while (1) {
522 i=0;s=*buf;
523 while (*s=='\t') {
524 s++;
525 i++;
527 if (i>level) {
528 if (!subkey) {
529 WARN("Got a subhierarchy without resp. key?\n");
530 return 0;
532 if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen))
533 if (!_wine_read_line(F,buf,buflen))
534 goto done;
535 continue;
538 /* let the caller handle this line */
539 if (i<level || **buf=='\0')
540 goto done;
542 /* it can be: a value or a keyname. Parse the name first */
543 s=_wine_read_USTRING(s,&name);
545 /* switch() default: hack to avoid gotos */
546 switch (0) {
547 default:
548 if (*s=='\0') {
549 if (subkey) RegCloseKey( subkey );
550 subkey=_find_or_add_key(hkey,name);
551 } else {
552 LPBYTE data;
553 int len,lastmodified,type;
555 if (*s!='=') {
556 WARN("Unexpected character: %c\n",*s);
557 break;
559 s++;
560 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
561 WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
562 break;
564 /* skip the 2 , */
565 s=strchr(s,',');s++;
566 s=strchr(s,',');
567 if (!s++) {
568 WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
569 break;
571 if (type == REG_SZ || type == REG_EXPAND_SZ) {
572 s=_wine_read_USTRING(s,(LPWSTR*)&data);
573 len = lstrlenW((LPWSTR)data)*2+2;
574 } else {
575 len=strlen(s)/2;
576 data = (LPBYTE)xmalloc(len+1);
577 for (i=0;i<len;i++) {
578 data[i]=0;
579 if (*s>='0' && *s<='9')
580 data[i]=(*s-'0')<<4;
581 if (*s>='a' && *s<='f')
582 data[i]=(*s-'a'+'\xa')<<4;
583 if (*s>='A' && *s<='F')
584 data[i]=(*s-'A'+'\xa')<<4;
585 s++;
586 if (*s>='0' && *s<='9')
587 data[i]|=*s-'0';
588 if (*s>='a' && *s<='f')
589 data[i]|=*s-'a'+'\xa';
590 if (*s>='A' && *s<='F')
591 data[i]|=*s-'A'+'\xa';
592 s++;
595 _find_or_add_value(hkey,name,type,data,len);
598 /* read the next line */
599 if (!_wine_read_line(F,buf,buflen))
600 goto done;
602 done:
603 if (subkey) RegCloseKey( subkey );
604 return 1;
608 /******************************************************************************
609 * _wine_loadsubreg [Internal]
611 static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
613 int ver;
614 char *buf;
615 int buflen;
617 buf=xmalloc(10);buflen=10;
618 if (!_wine_read_line(F,&buf,&buflen)) {
619 free(buf);
620 return 0;
622 if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
623 free(buf);
624 return 0;
626 if (ver!=REGISTRY_SAVE_VERSION) {
627 if (ver == 2) /* new version */
629 HANDLE file;
630 if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
631 FILE_ATTRIBUTE_NORMAL, -1, TRUE )) != INVALID_HANDLE_VALUE)
633 struct load_registry_request *req = get_req_buffer();
634 req->hkey = hkey;
635 req->file = file;
636 req->name[0] = 0;
637 server_call( REQ_LOAD_REGISTRY );
638 CloseHandle( file );
640 free( buf );
641 return 1;
643 else
645 TRACE("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
646 free(buf);
647 return 0;
650 if (!_wine_read_line(F,&buf,&buflen)) {
651 free(buf);
652 return 0;
654 if (!_wine_loadsubkey(F,hkey,0,&buf,&buflen)) {
655 free(buf);
656 return 0;
658 free(buf);
659 return 1;
663 /******************************************************************************
664 * _wine_loadreg [Internal]
666 static void _wine_loadreg( HKEY hkey, char *fn )
668 FILE *F;
670 TRACE("(%x,%s)\n",hkey,debugstr_a(fn));
672 F = fopen(fn,"rb");
673 if (F==NULL) {
674 WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
675 return;
677 _wine_loadsubreg(F,hkey,fn);
678 fclose(F);
681 /* NT REGISTRY LOADER */
683 #ifdef HAVE_SYS_MMAN_H
684 # include <sys/mman.h>
685 #endif
687 #ifndef MAP_FAILED
688 #define MAP_FAILED ((LPVOID)-1)
689 #endif
691 #define NT_REG_BLOCK_SIZE 0x1000
693 #define NT_REG_HEADER_BLOCK_ID 0x66676572 /* regf */
694 #define NT_REG_POOL_BLOCK_ID 0x6E696268 /* hbin */
695 #define NT_REG_KEY_BLOCK_ID 0x6b6e /* nk */
696 #define NT_REG_VALUE_BLOCK_ID 0x6b76 /* vk */
698 /* subblocks of nk */
699 #define NT_REG_HASH_BLOCK_ID 0x666c /* lf */
700 #define NT_REG_NOHASH_BLOCK_ID 0x696c /* li */
701 #define NT_REG_RI_BLOCK_ID 0x6972 /* ri */
703 #define NT_REG_KEY_BLOCK_TYPE 0x20
704 #define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c
706 typedef struct
708 DWORD id; /* 0x66676572 'regf'*/
709 DWORD uk1; /* 0x04 */
710 DWORD uk2; /* 0x08 */
711 FILETIME DateModified; /* 0x0c */
712 DWORD uk3; /* 0x14 */
713 DWORD uk4; /* 0x18 */
714 DWORD uk5; /* 0x1c */
715 DWORD uk6; /* 0x20 */
716 DWORD RootKeyBlock; /* 0x24 */
717 DWORD BlockSize; /* 0x28 */
718 DWORD uk7[116];
719 DWORD Checksum; /* at offset 0x1FC */
720 } nt_regf;
722 typedef struct
724 DWORD blocksize;
725 BYTE data[1];
726 } nt_hbin_sub;
728 typedef struct
730 DWORD id; /* 0x6E696268 'hbin' */
731 DWORD off_prev;
732 DWORD off_next;
733 DWORD uk1;
734 DWORD uk2; /* 0x10 */
735 DWORD uk3; /* 0x14 */
736 DWORD uk4; /* 0x18 */
737 DWORD size; /* 0x1C */
738 nt_hbin_sub hbin_sub; /* 0x20 */
739 } nt_hbin;
742 * the value_list consists of offsets to the values (vk)
744 typedef struct
746 WORD SubBlockId; /* 0x00 0x6B6E */
747 WORD Type; /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
748 FILETIME writetime; /* 0x04 */
749 DWORD uk1; /* 0x0C */
750 DWORD parent_off; /* 0x10 Offset of Owner/Parent key */
751 DWORD nr_subkeys; /* 0x14 number of sub-Keys */
752 DWORD uk8; /* 0x18 */
753 DWORD lf_off; /* 0x1C Offset of the sub-key lf-Records */
754 DWORD uk2; /* 0x20 */
755 DWORD nr_values; /* 0x24 number of values */
756 DWORD valuelist_off; /* 0x28 Offset of the Value-List */
757 DWORD off_sk; /* 0x2c Offset of the sk-Record */
758 DWORD off_class; /* 0x30 Offset of the Class-Name */
759 DWORD uk3; /* 0x34 */
760 DWORD uk4; /* 0x38 */
761 DWORD uk5; /* 0x3c */
762 DWORD uk6; /* 0x40 */
763 DWORD uk7; /* 0x44 */
764 WORD name_len; /* 0x48 name-length */
765 WORD class_len; /* 0x4a class-name length */
766 char name[1]; /* 0x4c key-name */
767 } nt_nk;
769 typedef struct
771 DWORD off_nk; /* 0x00 */
772 DWORD name; /* 0x04 */
773 } hash_rec;
775 typedef struct
777 WORD id; /* 0x00 0x666c */
778 WORD nr_keys; /* 0x06 */
779 hash_rec hash_rec[1];
780 } nt_lf;
783 list of subkeys without hash
785 li --+-->nk
787 +-->nk
789 typedef struct
791 WORD id; /* 0x00 0x696c */
792 WORD nr_keys;
793 DWORD off_nk[1];
794 } nt_li;
797 this is a intermediate node
799 ri --+-->li--+-->nk
801 | +-->nk
803 +-->li--+-->nk
805 +-->nk
807 typedef struct
809 WORD id; /* 0x00 0x6972 */
810 WORD nr_li; /* 0x02 number off offsets */
811 DWORD off_li[1]; /* 0x04 points to li */
812 } nt_ri;
814 typedef struct
816 WORD id; /* 0x00 'vk' */
817 WORD nam_len;
818 DWORD data_len;
819 DWORD data_off;
820 DWORD type;
821 WORD flag;
822 WORD uk1;
823 char name[1];
824 } nt_vk;
826 LPSTR _strdupnA( LPCSTR str, int len )
828 LPSTR ret;
830 if (!str) return NULL;
831 ret = malloc( len + 1 );
832 lstrcpynA( ret, str, len );
833 ret[len] = 0x00;
834 return ret;
837 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level);
838 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk);
839 static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level);
843 * gets a value
845 * vk->flag:
846 * 0 value is a default value
847 * 1 the value has a name
849 * vk->data_len
850 * len of the whole data block
851 * - reg_sz (unicode)
852 * bytes including the terminating \0 = 2*(number_of_chars+1)
853 * - reg_dword, reg_binary:
854 * if highest bit of data_len is set data_off contains the value
856 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk)
858 WCHAR name [256];
859 DWORD ret;
860 BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
862 if(vk->id != NT_REG_VALUE_BLOCK_ID) goto error;
864 lstrcpynAtoW(name, vk->name, vk->nam_len+1);
866 ret = RegSetValueExW( hkey, (vk->flag & 0x00000001) ? name : NULL, 0, vk->type,
867 (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata,
868 (vk->data_len & 0x7fffffff) );
869 if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
870 return TRUE;
871 error:
872 ERR_(reg)("unknown block found (0x%04x), please report!\n", vk->id);
873 return FALSE;
877 * get the subkeys
879 * this structure contains the hash of a keyname and points to all
880 * subkeys
882 * exception: if the id is 'il' there are no hash values and every
883 * dword is a offset
885 static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level)
887 int i;
889 if (lf->id == NT_REG_HASH_BLOCK_ID)
891 if (subkeys != lf->nr_keys) goto error1;
893 for (i=0; i<lf->nr_keys; i++)
895 if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), level)) goto error;
898 else if (lf->id == NT_REG_NOHASH_BLOCK_ID)
900 nt_li * li = (nt_li*)lf;
901 if (subkeys != li->nr_keys) goto error1;
903 for (i=0; i<li->nr_keys; i++)
905 if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+li->off_nk[i]+4), level)) goto error;
908 else if (lf->id == NT_REG_RI_BLOCK_ID) /* ri */
910 nt_ri * ri = (nt_ri*)lf;
911 int li_subkeys = 0;
913 /* count all subkeys */
914 for (i=0; i<ri->nr_li; i++)
916 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
917 if(li->id != NT_REG_NOHASH_BLOCK_ID) goto error2;
918 li_subkeys += li->nr_keys;
921 /* check number */
922 if (subkeys != li_subkeys) goto error1;
924 /* loop through the keys */
925 for (i=0; i<ri->nr_li; i++)
927 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
928 if (!_nt_parse_lf(hkey, base, li->nr_keys, (nt_lf*)li, level)) goto error;
931 else
933 goto error2;
935 return TRUE;
937 error2: ERR("unknown node id 0x%04x, please report!\n", lf->id);
938 return TRUE;
940 error1: ERR_(reg)("registry file corrupt! (inconsistent number of subkeys)\n");
941 return FALSE;
943 error: ERR_(reg)("error reading lf block\n");
944 return FALSE;
947 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
949 char * name;
950 int i;
951 DWORD * vl;
952 HKEY subkey = hkey;
954 if(nk->SubBlockId != NT_REG_KEY_BLOCK_ID)
956 ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId);
957 goto error;
960 if((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) &&
961 (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID))
963 ERR_(reg)("registry file corrupt!\n");
964 goto error;
967 /* create the new key */
968 if(level <= 0)
970 name = _strdupnA( nk->name, nk->name_len+1);
971 if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; }
972 free(name);
975 /* loop through the subkeys */
976 if (nk->nr_subkeys)
978 nt_lf * lf = (nt_lf*)(base+nk->lf_off+4);
979 if (!_nt_parse_lf(subkey, base, nk->nr_subkeys, lf, level-1)) goto error1;
982 /* loop trough the value list */
983 vl = (DWORD *)(base+nk->valuelist_off+4);
984 for (i=0; i<nk->nr_values; i++)
986 nt_vk * vk = (nt_vk*)(base+vl[i]+4);
987 if (!_nt_parse_vk(subkey, base, vk)) goto error1;
990 RegCloseKey(subkey);
991 return TRUE;
993 error1: RegCloseKey(subkey);
994 error: return FALSE;
997 /* end nt loader */
999 /* windows 95 registry loader */
1001 /* SECTION 1: main header
1003 * once at offset 0
1005 #define W95_REG_CREG_ID 0x47455243
1007 typedef struct
1009 DWORD id; /* "CREG" = W95_REG_CREG_ID */
1010 DWORD version; /* ???? 0x00010000 */
1011 DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
1012 DWORD uk2; /* 0x0c */
1013 WORD rgdb_num; /* 0x10 # of RGDB-blocks */
1014 WORD uk3;
1015 DWORD uk[3];
1016 /* rgkn */
1017 } _w95creg;
1019 /* SECTION 2: Directory information (tree structure)
1021 * once on offset 0x20
1023 * structure: [rgkn][dke]* (repeat till rgkn->size is reached)
1025 #define W95_REG_RGKN_ID 0x4e4b4752
1027 typedef struct
1029 DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
1030 DWORD size; /* Size of the RGKN-block */
1031 DWORD root_off; /* Rel. Offset of the root-record */
1032 DWORD uk[5];
1033 } _w95rgkn;
1035 /* Disk Key Entry Structure
1037 * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the
1038 * hive itself. It looks the same like other keys. Even the ID-number can
1039 * be any value.
1041 * The "hash"-value is a value representing the key's name. Windows will not
1042 * search for the name, but for a matching hash-value. if it finds one, it
1043 * will compare the actual string info, otherwise continue with the next key.
1044 * To calculate the hash initialize a D-Word with 0 and add all ASCII-values
1045 * of the string which are smaller than 0x80 (128) to this D-Word.
1047 * If you want to modify key names, also modify the hash-values, since they
1048 * cannot be found again (although they would be displayed in REGEDIT)
1049 * End of list-pointers are filled with 0xFFFFFFFF
1051 * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
1052 * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
1053 * structure) and reading another RGDB_section.
1055 * there is a one to one relationship between dke and dkh
1057 /* key struct, once per key */
1058 typedef struct
1060 DWORD x1; /* Free entry indicator(?) */
1061 DWORD hash; /* sum of bytes of keyname */
1062 DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
1063 DWORD prevlvl; /* offset of previous key */
1064 DWORD nextsub; /* offset of child key */
1065 DWORD next; /* offset of sibling key */
1066 WORD nrLS; /* id inside the rgdb block */
1067 WORD nrMS; /* number of the rgdb block */
1068 } _w95dke;
1070 /* SECTION 3: key information, values and data
1072 * structure:
1073 * section: [blocks]* (repeat creg->rgdb_num times)
1074 * blocks: [rgdb] [subblocks]* (repeat till block size reached )
1075 * subblocks: [dkh] [dkv]* (repeat dkh->values times )
1077 * An interesting relationship exists in RGDB_section. The value at offset
1078 * 10 equals the value at offset 4 minus the value at offset 8. I have no
1079 * idea at the moment what this means. (Kevin Cozens)
1082 /* block header, once per block */
1083 #define W95_REG_RGDB_ID 0x42444752
1085 typedef struct
1087 DWORD id; /* 0x00 'rgdb' = W95_REG_RGDB_ID */
1088 DWORD size; /* 0x04 */
1089 DWORD uk1; /* 0x08 */
1090 DWORD uk2; /* 0x0c */
1091 DWORD uk3; /* 0x10 */
1092 DWORD uk4; /* 0x14 */
1093 DWORD uk5; /* 0x18 */
1094 DWORD uk6; /* 0x1c */
1095 /* dkh */
1096 } _w95rgdb;
1098 /* Disk Key Header structure (RGDB part), once per key */
1099 typedef struct
1101 DWORD nextkeyoff; /* 0x00 offset to next dkh*/
1102 WORD nrLS; /* 0x04 id inside the rgdb block */
1103 WORD nrMS; /* 0x06 number of the rgdb block */
1104 DWORD bytesused; /* 0x08 */
1105 WORD keynamelen; /* 0x0c len of name */
1106 WORD values; /* 0x0e number of values */
1107 DWORD xx1; /* 0x10 */
1108 char name[1]; /* 0x14 */
1109 /* dkv */ /* 0x14 + keynamelen */
1110 } _w95dkh;
1112 /* Disk Key Value structure, once per value */
1113 typedef struct
1115 DWORD type; /* 0x00 */
1116 DWORD x1; /* 0x04 */
1117 WORD valnamelen; /* 0x08 length of name, 0 is default key */
1118 WORD valdatalen; /* 0x0A length of data */
1119 char name[1]; /* 0x0c */
1120 /* raw data */ /* 0x0c + valnamelen */
1121 } _w95dkv;
1123 /******************************************************************************
1124 * _w95_lookup_dkh [Internal]
1126 * seeks the dkh belonging to a dke
1128 static _w95dkh * _w95_lookup_dkh (_w95creg *creg, int nrLS, int nrMS)
1130 _w95rgdb * rgdb;
1131 _w95dkh * dkh;
1132 int i;
1134 rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off); /* get the beginning of the rgdb datastore */
1135 assert (creg->rgdb_num > nrMS); /* check: requested block < last_block) */
1137 /* find the right block */
1138 for(i=0; i<nrMS ;i++)
1140 assert(rgdb->id == W95_REG_RGDB_ID); /* check the magic */
1141 rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size); /* find next block */
1144 dkh = (_w95dkh*)(rgdb + 1); /* first sub block within the rgdb */
1148 if(nrLS==dkh->nrLS ) return dkh;
1149 dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */
1150 } while ((char *)dkh < ((char*)rgdb+rgdb->size));
1152 return NULL;
1155 /******************************************************************************
1156 * _w95_parse_dkv [Internal]
1158 static int _w95_parse_dkv (
1159 HKEY hkey,
1160 _w95dkh * dkh,
1161 int nrLS,
1162 int nrMS )
1164 _w95dkv * dkv;
1165 int i;
1166 DWORD ret;
1167 char * name;
1169 /* first value block */
1170 dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
1172 /* loop trought the values */
1173 for (i=0; i< dkh->values; i++)
1175 name = _strdupnA(dkv->name, dkv->valnamelen+1);
1176 ret = RegSetValueExA(hkey, name, 0, dkv->type, &(dkv->name[dkv->valnamelen]),dkv->valdatalen);
1177 if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
1178 free (name);
1180 /* next value */
1181 dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
1183 return TRUE;
1186 /******************************************************************************
1187 * _w95_parse_dke [Internal]
1189 static int _w95_parse_dke(
1190 HKEY hkey,
1191 _w95creg * creg,
1192 _w95rgkn *rgkn,
1193 _w95dke * dke,
1194 int level )
1196 _w95dkh * dkh;
1197 HKEY hsubkey = hkey;
1198 char * name;
1199 int ret = FALSE;
1201 /* get start address of root key block */
1202 if (!dke) dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
1204 /* special root key */
1205 if (dke->nrLS == 0xffff || dke->nrMS==0xffff) /* eg. the root key has no name */
1207 /* parse the one subkey*/
1208 if (dke->nextsub != 0xffffffff)
1210 return _w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level);
1212 /* has no sibling keys */
1213 goto error;
1216 /* search subblock */
1217 if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS)))
1219 fprintf(stderr, "dke pointing to missing dkh !\n");
1220 goto error;
1223 if ( level <= 0 )
1225 /* walk sibling keys */
1226 if (dke->next != 0xffffffff )
1228 if (!_w95_parse_dke(hkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next), level)) goto error;
1231 /* create subkey and insert values */
1232 name = _strdupnA( dkh->name, dkh->keynamelen+1);
1233 if (RegCreateKeyA(hkey, name, &hsubkey)) { free(name); goto error; }
1234 free(name);
1235 if (!_w95_parse_dkv(hsubkey, dkh, dke->nrLS, dke->nrMS)) goto error1;
1238 /* next sub key */
1239 if (dke->nextsub != 0xffffffff)
1241 if (!_w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level-1)) goto error1;
1244 ret = TRUE;
1245 error1: if (hsubkey != hkey) RegCloseKey(hsubkey);
1246 error: return ret;
1248 /* end windows 95 loader */
1250 /******************************************************************************
1251 * NativeRegLoadKey [Internal]
1253 * Loads a native registry file (win95/nt)
1254 * hkey root key
1255 * fn filename
1256 * level number of levels to cut away (eg. ".Default" in user.dat)
1258 * this function intentionally uses unix file functions to make it possible
1259 * to move it to a seperate registry helper programm
1261 static int NativeRegLoadKey( HKEY hkey, char* fn, int level )
1263 int fd = 0;
1264 struct stat st;
1265 DOS_FULL_NAME full_name;
1266 int ret = FALSE;
1267 void * base;
1269 if (!DOSFS_GetFullName( fn, 0, &full_name )) return FALSE;
1271 /* map the registry into the memory */
1272 if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE;
1273 if ((fstat(fd, &st) == -1)) goto error;
1274 if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error;
1276 switch (*(LPDWORD)base)
1278 /* windows 95 'creg' */
1279 case W95_REG_CREG_ID:
1281 _w95creg * creg;
1282 _w95rgkn * rgkn;
1283 creg = base;
1284 TRACE_(reg)("Loading win95 registry '%s' '%s'\n",fn, full_name.long_name);
1286 /* load the header (rgkn) */
1287 rgkn = (_w95rgkn*)(creg + 1);
1288 if (rgkn->id != W95_REG_RGKN_ID)
1290 ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
1291 goto error1;
1294 ret = _w95_parse_dke(hkey, creg, rgkn, NULL, level);
1296 break;
1297 /* nt 'regf'*/
1298 case NT_REG_HEADER_BLOCK_ID:
1300 nt_regf * regf;
1301 nt_hbin * hbin;
1302 nt_hbin_sub * hbin_sub;
1303 nt_nk* nk;
1305 TRACE_(reg)("Loading nt registry '%s' '%s'\n",fn, full_name.long_name);
1307 /* start block */
1308 regf = base;
1310 /* hbin block */
1311 hbin = (nt_hbin *) ((char *) base + 0x1000);
1312 if (hbin->id != NT_REG_POOL_BLOCK_ID)
1314 ERR_(reg)( "%s hbin block invalid\n", fn);
1315 goto error1;
1318 /* hbin_sub block */
1319 hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
1320 if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k'))
1322 ERR_(reg)( "%s hbin_sub block invalid\n", fn);
1323 goto error1;
1326 /* nk block */
1327 nk = (nt_nk*)&(hbin_sub->data[0]);
1328 if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE)
1330 ERR_(reg)( "%s special nk block not found\n", fn);
1331 goto error1;
1334 ret = _nt_parse_nk (hkey, (char *) base + 0x1000, nk, level);
1336 break;
1337 default:
1339 ERR("unknown signature in registry file %s.\n",fn);
1340 goto error1;
1343 if(!ret) ERR("error loading registry file %s\n", fn);
1344 error1: munmap(base, st.st_size);
1345 error: close(fd);
1346 return ret;
1349 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1351 reghack - windows 3.11 registry data format demo program.
1353 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1354 a combined hash table and tree description, and finally a text table.
1356 The header is obvious from the struct header. The taboff1 and taboff2
1357 fields are always 0x20, and their usage is unknown.
1359 The 8-byte entry table has various entry types.
1361 tabent[0] is a root index. The second word has the index of the root of
1362 the directory.
1363 tabent[1..hashsize] is a hash table. The first word in the hash entry is
1364 the index of the key/value that has that hash. Data with the same
1365 hash value are on a circular list. The other three words in the
1366 hash entry are always zero.
1367 tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1368 entry: dirent and keyent/valent. They are identified by context.
1369 tabent[freeidx] is the first free entry. The first word in a free entry
1370 is the index of the next free entry. The last has 0 as a link.
1371 The other three words in the free list are probably irrelevant.
1373 Entries in text table are preceeded by a word at offset-2. This word
1374 has the value (2*index)+1, where index is the referring keyent/valent
1375 entry in the table. I have no suggestion for the 2* and the +1.
1376 Following the word, there are N bytes of data, as per the keyent/valent
1377 entry length. The offset of the keyent/valent entry is from the start
1378 of the text table to the first data byte.
1380 This information is not available from Microsoft. The data format is
1381 deduced from the reg.dat file by me. Mistakes may
1382 have been made. I claim no rights and give no guarantees for this program.
1384 Tor Sjøwall, tor@sn.no
1387 /* reg.dat header format */
1388 struct _w31_header {
1389 char cookie[8]; /* 'SHCC3.10' */
1390 unsigned long taboff1; /* offset of hash table (??) = 0x20 */
1391 unsigned long taboff2; /* offset of index table (??) = 0x20 */
1392 unsigned long tabcnt; /* number of entries in index table */
1393 unsigned long textoff; /* offset of text part */
1394 unsigned long textsize; /* byte size of text part */
1395 unsigned short hashsize; /* hash size */
1396 unsigned short freeidx; /* free index */
1399 /* generic format of table entries */
1400 struct _w31_tabent {
1401 unsigned short w0, w1, w2, w3;
1404 /* directory tabent: */
1405 struct _w31_dirent {
1406 unsigned short sibling_idx; /* table index of sibling dirent */
1407 unsigned short child_idx; /* table index of child dirent */
1408 unsigned short key_idx; /* table index of key keyent */
1409 unsigned short value_idx; /* table index of value valent */
1412 /* key tabent: */
1413 struct _w31_keyent {
1414 unsigned short hash_idx; /* hash chain index for string */
1415 unsigned short refcnt; /* reference count */
1416 unsigned short length; /* length of string */
1417 unsigned short string_off; /* offset of string in text table */
1420 /* value tabent: */
1421 struct _w31_valent {
1422 unsigned short hash_idx; /* hash chain index for string */
1423 unsigned short refcnt; /* reference count */
1424 unsigned short length; /* length of string */
1425 unsigned short string_off; /* offset of string in text table */
1428 /* recursive helper function to display a directory tree */
1429 void
1430 __w31_dumptree( unsigned short idx,
1431 unsigned char *txt,
1432 struct _w31_tabent *tab,
1433 struct _w31_header *head,
1434 HKEY hkey,
1435 time_t lastmodified,
1436 int level
1438 struct _w31_dirent *dir;
1439 struct _w31_keyent *key;
1440 struct _w31_valent *val;
1441 HKEY subkey = 0;
1442 static char tail[400];
1444 while (idx!=0) {
1445 dir=(struct _w31_dirent*)&tab[idx];
1447 if (dir->key_idx) {
1448 key = (struct _w31_keyent*)&tab[dir->key_idx];
1450 memcpy(tail,&txt[key->string_off],key->length);
1451 tail[key->length]='\0';
1452 /* all toplevel entries AND the entries in the
1453 * toplevel subdirectory belong to \SOFTWARE\Classes
1455 if (!level && !lstrcmpA(tail,".classes")) {
1456 __w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
1457 idx=dir->sibling_idx;
1458 continue;
1460 if (subkey) RegCloseKey( subkey );
1461 if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
1462 /* only add if leaf node or valued node */
1463 if (dir->value_idx!=0||dir->child_idx==0) {
1464 if (dir->value_idx) {
1465 val=(struct _w31_valent*)&tab[dir->value_idx];
1466 memcpy(tail,&txt[val->string_off],val->length);
1467 tail[val->length]='\0';
1468 RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
1471 } else {
1472 TRACE("strange: no directory key name, idx=%04x\n", idx);
1474 __w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
1475 idx=dir->sibling_idx;
1477 if (subkey) RegCloseKey( subkey );
1481 /******************************************************************************
1482 * _w31_loadreg [Internal]
1484 void _w31_loadreg(void) {
1485 HFILE hf;
1486 struct _w31_header head;
1487 struct _w31_tabent *tab;
1488 unsigned char *txt;
1489 int len;
1490 OFSTRUCT ofs;
1491 BY_HANDLE_FILE_INFORMATION hfinfo;
1492 time_t lastmodified;
1494 TRACE("(void)\n");
1496 hf = OpenFile("reg.dat",&ofs,OF_READ);
1497 if (hf==HFILE_ERROR)
1498 return;
1500 /* read & dump header */
1501 if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
1502 ERR("reg.dat is too short.\n");
1503 _lclose(hf);
1504 return;
1506 if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1507 ERR("reg.dat has bad signature.\n");
1508 _lclose(hf);
1509 return;
1512 len = head.tabcnt * sizeof(struct _w31_tabent);
1513 /* read and dump index table */
1514 tab = xmalloc(len);
1515 if (len!=_lread(hf,tab,len)) {
1516 ERR("couldn't read %d bytes.\n",len);
1517 free(tab);
1518 _lclose(hf);
1519 return;
1522 /* read text */
1523 txt = xmalloc(head.textsize);
1524 if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
1525 ERR("couldn't seek to textblock.\n");
1526 free(tab);
1527 free(txt);
1528 _lclose(hf);
1529 return;
1531 if (head.textsize!=_lread(hf,txt,head.textsize)) {
1532 ERR("textblock too short (%d instead of %ld).\n",len,head.textsize);
1533 free(tab);
1534 free(txt);
1535 _lclose(hf);
1536 return;
1539 if (!GetFileInformationByHandle(hf,&hfinfo)) {
1540 ERR("GetFileInformationByHandle failed?.\n");
1541 free(tab);
1542 free(txt);
1543 _lclose(hf);
1544 return;
1546 lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1547 __w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
1548 free(tab);
1549 free(txt);
1550 _lclose(hf);
1551 return;
1554 /**********************************************************************************
1555 * SetLoadLevel [Internal]
1557 * set level to 0 for loading system files
1558 * set level to 1 for loading user files
1560 static void SetLoadLevel(int level)
1562 struct set_registry_levels_request *req = get_req_buffer();
1564 req->current = level;
1565 req->saving = 0;
1566 req->version = 1;
1567 server_call( REQ_SET_REGISTRY_LEVELS );
1570 /**********************************************************************************
1571 * SHELL_LoadRegistry [Internal]
1573 #define REG_DONTLOAD -1
1574 #define REG_WIN31 0
1575 #define REG_WIN95 1
1576 #define REG_WINNT 2
1578 void SHELL_LoadRegistry( void )
1580 char *fn, *home;
1581 HKEY hkey;
1582 char windir[MAX_PATHNAME_LEN];
1583 char path[MAX_PATHNAME_LEN];
1584 int systemtype = REG_WIN31;
1586 TRACE("(void)\n");
1588 if (!CLIENT_IsBootThread()) return; /* already loaded */
1590 REGISTRY_Init();
1591 SetLoadLevel(0);
1593 GetWindowsDirectoryA( windir, MAX_PATHNAME_LEN );
1595 if (PROFILE_GetWineIniBool( "Registry", "LoadWindowsRegistryFiles", 1))
1597 /* test %windir%/system32/config/system --> winnt */
1598 strcpy(path, windir);
1599 strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
1600 if(GetFileAttributesA(path) != -1)
1602 systemtype = REG_WINNT;
1604 else
1606 /* test %windir%/system.dat --> win95 */
1607 strcpy(path, windir);
1608 strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1609 if(GetFileAttributesA(path) != -1)
1611 systemtype = REG_WIN95;
1615 if ((systemtype==REG_WINNT)
1616 && (! PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)))
1618 MESSAGE("When you are running with a native NT directory specify\n");
1619 MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n");
1620 MESSAGE("registry (LoadWindowsRegistryFiles=N)\n");
1621 systemtype = REG_DONTLOAD;
1624 else
1626 /* only wine registry */
1627 systemtype = REG_DONTLOAD;
1630 switch (systemtype)
1632 case REG_WIN31:
1633 _w31_loadreg();
1634 break;
1636 case REG_WIN95:
1637 /* Load windows 95 entries */
1638 NativeRegLoadKey(HKEY_LOCAL_MACHINE, "C:\\system.1st", 0);
1640 strcpy(path, windir);
1641 strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1642 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1644 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
1646 /* user specific user.dat */
1647 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1648 if (!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
1650 MESSAGE("can't load win95 user-registry %s\n", path);
1651 MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
1653 /* default user.dat */
1654 if (!RegCreateKeyA(HKEY_USERS, ".Default", &hkey))
1656 strcpy(path, windir);
1657 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1658 NativeRegLoadKey(hkey, path, 1);
1659 RegCloseKey(hkey);
1662 else
1664 /* global user.dat */
1665 strcpy(path, windir);
1666 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1667 NativeRegLoadKey(HKEY_CURRENT_USER, path, 1);
1669 break;
1671 case REG_WINNT:
1672 /* default user.dat */
1673 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
1675 strncat(path, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1676 if(!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
1678 MESSAGE("can't load NT user-registry %s\n", path);
1679 MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
1683 /* default user.dat */
1684 if (!RegCreateKeyA(HKEY_USERS, ".Default", &hkey))
1686 strcpy(path, windir);
1687 strncat(path, "\\system32\\config\\default", MAX_PATHNAME_LEN - strlen(path) - 1);
1688 NativeRegLoadKey(hkey, path, 1);
1689 RegCloseKey(hkey);
1693 * FIXME
1694 * map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
1697 strcpy(path, windir);
1698 strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
1699 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1701 strcpy(path, windir);
1702 strncat(path, "\\system32\\config\\software", MAX_PATHNAME_LEN - strlen(path) - 1);
1703 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1705 strcpy(path, windir);
1706 strncat(path, "\\system32\\config\\sam", MAX_PATHNAME_LEN - strlen(path) - 1);
1707 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1709 strcpy(path, windir);
1710 strncat(path, "\\system32\\config\\security", MAX_PATHNAME_LEN - strlen(path) - 1);
1711 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1713 /* this key is generated when the nt-core booted successfully */
1714 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey))
1715 RegCloseKey(hkey);
1716 break;
1717 } /* switch */
1719 if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
1722 * Load the global HKU hive directly from sysconfdir
1724 _wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT );
1727 * Load the global machine defaults directly form sysconfdir
1729 _wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
1732 SetLoadLevel(1);
1735 * Load the user saved registries
1737 if (!(home = getenv( "HOME" )))
1738 WARN("Failed to get homedirectory of UID %ld.\n",(long) getuid());
1739 else if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1))
1742 * Load user's personal versions of global HKU/.Default keys
1744 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX) +
1745 strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
1746 strcpy(fn, home);
1747 strcat(fn, WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1748 _wine_loadreg( HKEY_USERS, fn );
1749 free(fn);
1751 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) + strlen(SAVE_CURRENT_USER)+2);
1752 strcpy(fn, home);
1753 strcat(fn, WINE_PREFIX"/"SAVE_CURRENT_USER);
1754 _wine_loadreg( HKEY_CURRENT_USER, fn );
1755 free(fn);
1758 * Load HKLM, attempt to get the registry location from the config
1759 * file first, if exist, load and keep going.
1761 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_MACHINE)+2);
1762 strcpy(fn,home);
1763 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1764 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1765 free(fn);
1769 * Load HKCU, get the registry location from the config
1770 * file, if exist, load and keep going.
1772 if (PROFILE_GetWineIniBool ( "registry", "LoadAltRegistryFiles", 1))
1774 fn = xmalloc( MAX_PATHNAME_LEN );
1775 if ( PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
1776 fn, MAX_PATHNAME_LEN - 1))
1778 _wine_loadreg( HKEY_CURRENT_USER, fn );
1780 free (fn);
1782 * Load HKU, get the registry location from the config
1783 * file, if exist, load and keep going.
1785 fn = xmalloc ( MAX_PATHNAME_LEN );
1786 if ( PROFILE_GetWineIniString ( "registry", "AltUserFile", "",
1787 fn, MAX_PATHNAME_LEN - 1))
1789 _wine_loadreg( HKEY_USERS, fn );
1791 free (fn);
1793 * Load HKLM, get the registry location from the config
1794 * file, if exist, load and keep going.
1796 fn = xmalloc ( MAX_PATHNAME_LEN );
1797 if (PROFILE_GetWineIniString ( "registry", "AltLocalMachineFile", "",
1798 fn, MAX_PATHNAME_LEN - 1))
1800 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1802 free (fn);
1807 /* start the periodic saving timer */
1808 void SHELL_InitRegistrySaving(void)
1810 int save_timeout;
1812 if (!CLIENT_IsBootThread()) return;
1814 if ((save_timeout = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 )))
1816 SERVICE_AddTimer( save_timeout * 1000000, periodic_save, 0 );
1821 /********************* API FUNCTIONS ***************************************/
1826 /******************************************************************************
1827 * RegFlushKey [KERNEL.227] [ADVAPI32.143]
1828 * Immediately writes key to registry.
1829 * Only returns after data has been written to disk.
1831 * FIXME: does it really wait until data is written ?
1833 * PARAMS
1834 * hkey [I] Handle of key to write
1836 * RETURNS
1837 * Success: ERROR_SUCCESS
1838 * Failure: Error code
1840 DWORD WINAPI RegFlushKey( HKEY hkey )
1842 FIXME( "(%x): stub\n", hkey );
1843 return ERROR_SUCCESS;
1846 /******************************************************************************
1847 * RegConnectRegistryW [ADVAPI32.128]
1849 * PARAMS
1850 * lpMachineName [I] Address of name of remote computer
1851 * hHey [I] Predefined registry handle
1852 * phkResult [I] Address of buffer for remote registry handle
1854 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey,
1855 LPHKEY phkResult )
1857 TRACE("(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
1859 if (!lpMachineName || !*lpMachineName) {
1860 /* Use the local machine name */
1861 return RegOpenKey16( hKey, "", phkResult );
1864 FIXME("Cannot connect to %s\n",debugstr_w(lpMachineName));
1865 return ERROR_BAD_NETPATH;
1869 /******************************************************************************
1870 * RegConnectRegistryA [ADVAPI32.127]
1872 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, LPHKEY reskey )
1874 DWORD ret;
1875 LPWSTR machineW = strdupA2W(machine);
1876 ret = RegConnectRegistryW( machineW, hkey, reskey );
1877 free(machineW);
1878 return ret;
1882 /******************************************************************************
1883 * RegGetKeySecurity [ADVAPI32.144]
1884 * Retrieves a copy of security descriptor protecting the registry key
1886 * PARAMS
1887 * hkey [I] Open handle of key to set
1888 * SecurityInformation [I] Descriptor contents
1889 * pSecurityDescriptor [O] Address of descriptor for key
1890 * lpcbSecurityDescriptor [I/O] Address of size of buffer and description
1892 * RETURNS
1893 * Success: ERROR_SUCCESS
1894 * Failure: Error code
1896 LONG WINAPI RegGetKeySecurity( HKEY hkey,
1897 SECURITY_INFORMATION SecurityInformation,
1898 PSECURITY_DESCRIPTOR pSecurityDescriptor,
1899 LPDWORD lpcbSecurityDescriptor )
1901 TRACE("(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
1902 lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1904 /* FIXME: Check for valid SecurityInformation values */
1906 if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
1907 return ERROR_INSUFFICIENT_BUFFER;
1909 FIXME("(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
1910 pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1912 return ERROR_SUCCESS;
1916 /******************************************************************************
1917 * RegNotifyChangeKeyValue [ADVAPI32.???]
1919 * PARAMS
1920 * hkey [I] Handle of key to watch
1921 * fWatchSubTree [I] Flag for subkey notification
1922 * fdwNotifyFilter [I] Changes to be reported
1923 * hEvent [I] Handle of signaled event
1924 * fAsync [I] Flag for asynchronous reporting
1926 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree,
1927 DWORD fdwNotifyFilter, HANDLE hEvent,
1928 BOOL fAsync )
1930 FIXME("(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter,
1931 hEvent,fAsync);
1932 return ERROR_SUCCESS;
1936 /******************************************************************************
1937 * RegUnLoadKeyW [ADVAPI32.173]
1939 * PARAMS
1940 * hkey [I] Handle of open key
1941 * lpSubKey [I] Address of name of subkey to unload
1943 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
1945 FIXME("(%x,%s): stub\n",hkey, debugstr_w(lpSubKey));
1946 return ERROR_SUCCESS;
1950 /******************************************************************************
1951 * RegUnLoadKeyA [ADVAPI32.172]
1953 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1955 LONG ret;
1956 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1957 ret = RegUnLoadKeyW( hkey, lpSubKeyW );
1958 if(lpSubKeyW) free(lpSubKeyW);
1959 return ret;
1963 /******************************************************************************
1964 * RegSetKeySecurity [ADVAPI32.167]
1966 * PARAMS
1967 * hkey [I] Open handle of key to set
1968 * SecurityInfo [I] Descriptor contents
1969 * pSecurityDesc [I] Address of descriptor for key
1971 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
1972 PSECURITY_DESCRIPTOR pSecurityDesc )
1974 TRACE("(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
1976 /* It seems to perform this check before the hkey check */
1977 if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
1978 (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
1979 (SecurityInfo & DACL_SECURITY_INFORMATION) ||
1980 (SecurityInfo & SACL_SECURITY_INFORMATION)) {
1981 /* Param OK */
1982 } else
1983 return ERROR_INVALID_PARAMETER;
1985 if (!pSecurityDesc)
1986 return ERROR_INVALID_PARAMETER;
1988 FIXME(":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
1990 return ERROR_SUCCESS;
1994 /******************************************************************************
1995 * RegRestoreKeyW [ADVAPI32.164]
1997 * PARAMS
1998 * hkey [I] Handle of key where restore begins
1999 * lpFile [I] Address of filename containing saved tree
2000 * dwFlags [I] Optional flags
2002 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
2004 TRACE("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
2006 /* It seems to do this check before the hkey check */
2007 if (!lpFile || !*lpFile)
2008 return ERROR_INVALID_PARAMETER;
2010 FIXME("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
2012 /* Check for file existence */
2014 return ERROR_SUCCESS;
2018 /******************************************************************************
2019 * RegRestoreKeyA [ADVAPI32.163]
2021 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
2023 LONG ret;
2024 LPWSTR lpFileW = strdupA2W(lpFile);
2025 ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
2026 if(lpFileW) free(lpFileW);
2027 return ret;
2031 /******************************************************************************
2032 * RegReplaceKeyW [ADVAPI32.162]
2034 * PARAMS
2035 * hkey [I] Handle of open key
2036 * lpSubKey [I] Address of name of subkey
2037 * lpNewFile [I] Address of filename for file with new data
2038 * lpOldFile [I] Address of filename for backup file
2040 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
2041 LPCWSTR lpOldFile )
2043 FIXME("(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey),
2044 debugstr_w(lpNewFile),debugstr_w(lpOldFile));
2045 return ERROR_SUCCESS;
2049 /******************************************************************************
2050 * RegReplaceKeyA [ADVAPI32.161]
2052 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
2053 LPCSTR lpOldFile )
2055 LONG ret;
2056 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
2057 LPWSTR lpNewFileW = strdupA2W(lpNewFile);
2058 LPWSTR lpOldFileW = strdupA2W(lpOldFile);
2059 ret = RegReplaceKeyW( hkey, lpSubKeyW, lpNewFileW, lpOldFileW );
2060 free(lpOldFileW);
2061 free(lpNewFileW);
2062 free(lpSubKeyW);
2063 return ret;
2071 /* 16-bit functions */
2073 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
2074 * some programs. Do not remove those cases. -MM
2076 static inline void fix_win16_hkey( HKEY *hkey )
2078 if (*hkey == 0 || *hkey == 1) *hkey = HKEY_CLASSES_ROOT;
2081 /******************************************************************************
2082 * RegEnumKey16 [KERNEL.216] [SHELL.7]
2084 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
2086 fix_win16_hkey( &hkey );
2087 return RegEnumKeyA( hkey, index, name, name_len );
2090 /******************************************************************************
2091 * RegOpenKey16 [KERNEL.217] [SHELL.1]
2093 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
2095 fix_win16_hkey( &hkey );
2096 return RegOpenKeyA( hkey, name, retkey );
2099 /******************************************************************************
2100 * RegCreateKey16 [KERNEL.218] [SHELL.2]
2102 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
2104 fix_win16_hkey( &hkey );
2105 return RegCreateKeyA( hkey, name, retkey );
2108 /******************************************************************************
2109 * RegDeleteKey16 [KERNEL.219] [SHELL.4]
2111 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
2113 fix_win16_hkey( &hkey );
2114 return RegDeleteKeyA( hkey, name );
2117 /******************************************************************************
2118 * RegCloseKey16 [KERNEL.220] [SHELL.3]
2120 DWORD WINAPI RegCloseKey16( HKEY hkey )
2122 fix_win16_hkey( &hkey );
2123 return RegCloseKey( hkey );
2126 /******************************************************************************
2127 * RegSetValue16 [KERNEL.221] [SHELL.5]
2129 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
2131 fix_win16_hkey( &hkey );
2132 return RegSetValueA( hkey, name, type, data, count );
2135 /******************************************************************************
2136 * RegDeleteValue16 [KERNEL.222]
2138 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
2140 fix_win16_hkey( &hkey );
2141 return RegDeleteValueA( hkey, name );
2144 /******************************************************************************
2145 * RegEnumValue16 [KERNEL.223]
2147 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
2148 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
2150 fix_win16_hkey( &hkey );
2151 return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
2154 /******************************************************************************
2155 * RegQueryValue16 [KERNEL.224] [SHELL.6]
2157 * NOTES
2158 * Is this HACK still applicable?
2160 * HACK
2161 * The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
2162 * mask out the high 16 bit. This (not so much incidently) hopefully fixes
2163 * Aldus FH4)
2165 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
2167 fix_win16_hkey( &hkey );
2168 if (count) *count &= 0xffff;
2169 return RegQueryValueA( hkey, name, data, count );
2172 /******************************************************************************
2173 * RegQueryValueEx16 [KERNEL.225]
2175 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
2176 LPBYTE data, LPDWORD count )
2178 fix_win16_hkey( &hkey );
2179 return RegQueryValueExA( hkey, name, reserved, type, data, count );
2182 /******************************************************************************
2183 * RegSetValueEx16 [KERNEL.226]
2185 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
2186 CONST BYTE *data, DWORD count )
2188 fix_win16_hkey( &hkey );
2189 return RegSetValueExA( hkey, name, reserved, type, data, count );