Do not erase local files on uninstall if install dir does not exist.
[wine/multimedia.git] / misc / registry.c
blob9b0556ab29a7d8532248c0baca0b1bf1922165b2
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 );
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;
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 /* set saving level (0 for saving everything, 1 for saving only modified keys) */
353 req->current = 1;
354 req->saving = !all;
355 req->version = PROFILE_GetWineIniBool( "registry", "UseNewFormat", 0 ) ? 2 : 1;
356 server_call( REQ_SET_REGISTRY_LEVELS );
358 SHELL_SaveRegistryBranch(HKEY_CURRENT_USER);
359 SHELL_SaveRegistryBranch(HKEY_LOCAL_MACHINE);
360 SHELL_SaveRegistryBranch(HKEY_USERS);
363 /* Periodic save callback */
364 static void CALLBACK periodic_save( ULONG_PTR dummy )
366 SHELL_SaveRegistry();
369 /************************ LOAD Registry Function ****************************/
373 /******************************************************************************
374 * _find_or_add_key [Internal]
376 static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
378 HKEY subkey;
379 if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
380 if (keyname) free( keyname );
381 return subkey;
384 /******************************************************************************
385 * _find_or_add_value [Internal]
387 static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len )
389 RegSetValueExW( hkey, name, 0, type, data, len );
390 if (name) free( name );
391 if (data) free( data );
395 /******************************************************************************
396 * _wine_read_line [Internal]
398 * reads a line including dynamically enlarging the readbuffer and throwing
399 * away comments
401 static int _wine_read_line( FILE *F, char **buf, int *len )
403 char *s,*curread;
404 int mylen,curoff;
406 curread = *buf;
407 mylen = *len;
408 **buf = '\0';
409 while (1) {
410 while (1) {
411 s=fgets(curread,mylen,F);
412 if (s==NULL)
413 return 0; /* EOF */
414 if (NULL==(s=strchr(curread,'\n'))) {
415 /* buffer wasn't large enough */
416 curoff = strlen(*buf);
417 curread = realloc(*buf,*len*2);
418 if(curread == NULL) {
419 WARN("Out of memory");
420 return 0;
422 *buf = curread;
423 curread+= curoff;
424 mylen = *len; /* we filled up the buffer and
425 * got new '*len' bytes to fill
427 *len = *len * 2;
428 } else {
429 *s='\0';
430 break;
433 /* throw away comments */
434 if (**buf=='#' || **buf==';') {
435 curread = *buf;
436 mylen = *len;
437 continue;
439 if (s) /* got end of line */
440 break;
442 return 1;
446 /******************************************************************************
447 * _wine_read_USTRING [Internal]
449 * converts a char* into a UNICODE string (up to a special char)
450 * and returns the position exactly after that string
452 static char* _wine_read_USTRING( char *buf, LPWSTR *str )
454 char *s;
455 LPWSTR ws;
457 /* read up to "=" or "\0" or "\n" */
458 s = buf;
459 *str = (LPWSTR)xmalloc(2*strlen(buf)+2);
460 ws = *str;
461 while (*s && (*s!='\n') && (*s!='=')) {
462 if (*s!='\\')
463 *ws++=*((unsigned char*)s++);
464 else {
465 s++;
466 if (!*s) {
467 /* Dangling \ ... may only happen if a registry
468 * write was short. FIXME: What do to?
470 break;
472 if (*s=='\\') {
473 *ws++='\\';
474 s++;
475 continue;
477 if (*s!='u') {
478 WARN("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
479 *ws++='\\';
480 *ws++=*s++;
481 } else {
482 char xbuf[5];
483 int wc;
485 s++;
486 memcpy(xbuf,s,4);xbuf[4]='\0';
487 if (!sscanf(xbuf,"%x",&wc))
488 WARN("Strange escape sequence %s found in |%s|\n",xbuf,buf);
489 s+=4;
490 *ws++ =(unsigned short)wc;
494 *ws = 0;
495 return s;
499 /******************************************************************************
500 * _wine_loadsubkey [Internal]
502 * NOTES
503 * It seems like this is returning a boolean. Should it?
505 * RETURNS
506 * Success: 1
507 * Failure: 0
509 static int _wine_loadsubkey( FILE *F, HKEY hkey, int level, char **buf, int *buflen )
511 HKEY subkey;
512 int i;
513 char *s;
514 LPWSTR name;
516 TRACE("(%p,%x,%d,%s,%d)\n", F, hkey, level, debugstr_a(*buf), *buflen);
518 /* Good. We already got a line here ... so parse it */
519 subkey = 0;
520 while (1) {
521 i=0;s=*buf;
522 while (*s=='\t') {
523 s++;
524 i++;
526 if (i>level) {
527 if (!subkey) {
528 WARN("Got a subhierarchy without resp. key?\n");
529 return 0;
531 if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen))
532 if (!_wine_read_line(F,buf,buflen))
533 goto done;
534 continue;
537 /* let the caller handle this line */
538 if (i<level || **buf=='\0')
539 goto done;
541 /* it can be: a value or a keyname. Parse the name first */
542 s=_wine_read_USTRING(s,&name);
544 /* switch() default: hack to avoid gotos */
545 switch (0) {
546 default:
547 if (*s=='\0') {
548 if (subkey) RegCloseKey( subkey );
549 subkey=_find_or_add_key(hkey,name);
550 } else {
551 LPBYTE data;
552 int len,lastmodified,type;
554 if (*s!='=') {
555 WARN("Unexpected character: %c\n",*s);
556 break;
558 s++;
559 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
560 WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
561 break;
563 /* skip the 2 , */
564 s=strchr(s,',');s++;
565 s=strchr(s,',');
566 if (!s++) {
567 WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
568 break;
570 if (type == REG_SZ || type == REG_EXPAND_SZ) {
571 s=_wine_read_USTRING(s,(LPWSTR*)&data);
572 len = lstrlenW((LPWSTR)data)*2+2;
573 } else {
574 len=strlen(s)/2;
575 data = (LPBYTE)xmalloc(len+1);
576 for (i=0;i<len;i++) {
577 data[i]=0;
578 if (*s>='0' && *s<='9')
579 data[i]=(*s-'0')<<4;
580 if (*s>='a' && *s<='f')
581 data[i]=(*s-'a'+'\xa')<<4;
582 if (*s>='A' && *s<='F')
583 data[i]=(*s-'A'+'\xa')<<4;
584 s++;
585 if (*s>='0' && *s<='9')
586 data[i]|=*s-'0';
587 if (*s>='a' && *s<='f')
588 data[i]|=*s-'a'+'\xa';
589 if (*s>='A' && *s<='F')
590 data[i]|=*s-'A'+'\xa';
591 s++;
594 _find_or_add_value(hkey,name,type,data,len);
597 /* read the next line */
598 if (!_wine_read_line(F,buf,buflen))
599 goto done;
601 done:
602 if (subkey) RegCloseKey( subkey );
603 return 1;
607 /******************************************************************************
608 * _wine_loadsubreg [Internal]
610 static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
612 int ver;
613 char *buf;
614 int buflen;
616 buf=xmalloc(10);buflen=10;
617 if (!_wine_read_line(F,&buf,&buflen)) {
618 free(buf);
619 return 0;
621 if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
622 free(buf);
623 return 0;
625 if (ver!=REGISTRY_SAVE_VERSION) {
626 if (ver == 2) /* new version */
628 HANDLE file;
629 if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
630 FILE_ATTRIBUTE_NORMAL, -1 )) != INVALID_HANDLE_VALUE)
632 struct load_registry_request *req = get_req_buffer();
633 req->hkey = hkey;
634 req->file = file;
635 req->name[0] = 0;
636 server_call( REQ_LOAD_REGISTRY );
637 CloseHandle( file );
639 free( buf );
640 return 1;
642 else
644 TRACE("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
645 free(buf);
646 return 0;
649 if (!_wine_read_line(F,&buf,&buflen)) {
650 free(buf);
651 return 0;
653 if (!_wine_loadsubkey(F,hkey,0,&buf,&buflen)) {
654 free(buf);
655 return 0;
657 free(buf);
658 return 1;
662 /******************************************************************************
663 * _wine_loadreg [Internal]
665 static void _wine_loadreg( HKEY hkey, char *fn )
667 FILE *F;
669 TRACE("(%x,%s)\n",hkey,debugstr_a(fn));
671 F = fopen(fn,"rb");
672 if (F==NULL) {
673 WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
674 return;
676 _wine_loadsubreg(F,hkey,fn);
677 fclose(F);
680 /* NT REGISTRY LOADER */
682 #ifdef HAVE_SYS_MMAN_H
683 # include <sys/mman.h>
684 #endif
686 #ifndef MAP_FAILED
687 #define MAP_FAILED ((LPVOID)-1)
688 #endif
690 #define NT_REG_BLOCK_SIZE 0x1000
692 #define NT_REG_HEADER_BLOCK_ID 0x66676572 /* regf */
693 #define NT_REG_POOL_BLOCK_ID 0x6E696268 /* hbin */
694 #define NT_REG_KEY_BLOCK_ID 0x6b6e /* nk */
695 #define NT_REG_VALUE_BLOCK_ID 0x6b76 /* vk */
697 /* subblocks of nk */
698 #define NT_REG_HASH_BLOCK_ID 0x666c /* lf */
699 #define NT_REG_NOHASH_BLOCK_ID 0x696c /* li */
700 #define NT_REG_RI_BLOCK_ID 0x6972 /* ri */
702 #define NT_REG_KEY_BLOCK_TYPE 0x20
703 #define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c
705 typedef struct
707 DWORD id; /* 0x66676572 'regf'*/
708 DWORD uk1; /* 0x04 */
709 DWORD uk2; /* 0x08 */
710 FILETIME DateModified; /* 0x0c */
711 DWORD uk3; /* 0x14 */
712 DWORD uk4; /* 0x18 */
713 DWORD uk5; /* 0x1c */
714 DWORD uk6; /* 0x20 */
715 DWORD RootKeyBlock; /* 0x24 */
716 DWORD BlockSize; /* 0x28 */
717 DWORD uk7[116];
718 DWORD Checksum; /* at offset 0x1FC */
719 } nt_regf;
721 typedef struct
723 DWORD blocksize;
724 BYTE data[1];
725 } nt_hbin_sub;
727 typedef struct
729 DWORD id; /* 0x6E696268 'hbin' */
730 DWORD off_prev;
731 DWORD off_next;
732 DWORD uk1;
733 DWORD uk2; /* 0x10 */
734 DWORD uk3; /* 0x14 */
735 DWORD uk4; /* 0x18 */
736 DWORD size; /* 0x1C */
737 nt_hbin_sub hbin_sub; /* 0x20 */
738 } nt_hbin;
741 * the value_list consists of offsets to the values (vk)
743 typedef struct
745 WORD SubBlockId; /* 0x00 0x6B6E */
746 WORD Type; /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
747 FILETIME writetime; /* 0x04 */
748 DWORD uk1; /* 0x0C */
749 DWORD parent_off; /* 0x10 Offset of Owner/Parent key */
750 DWORD nr_subkeys; /* 0x14 number of sub-Keys */
751 DWORD uk8; /* 0x18 */
752 DWORD lf_off; /* 0x1C Offset of the sub-key lf-Records */
753 DWORD uk2; /* 0x20 */
754 DWORD nr_values; /* 0x24 number of values */
755 DWORD valuelist_off; /* 0x28 Offset of the Value-List */
756 DWORD off_sk; /* 0x2c Offset of the sk-Record */
757 DWORD off_class; /* 0x30 Offset of the Class-Name */
758 DWORD uk3; /* 0x34 */
759 DWORD uk4; /* 0x38 */
760 DWORD uk5; /* 0x3c */
761 DWORD uk6; /* 0x40 */
762 DWORD uk7; /* 0x44 */
763 WORD name_len; /* 0x48 name-length */
764 WORD class_len; /* 0x4a class-name length */
765 char name[1]; /* 0x4c key-name */
766 } nt_nk;
768 typedef struct
770 DWORD off_nk; /* 0x00 */
771 DWORD name; /* 0x04 */
772 } hash_rec;
774 typedef struct
776 WORD id; /* 0x00 0x666c */
777 WORD nr_keys; /* 0x06 */
778 hash_rec hash_rec[1];
779 } nt_lf;
782 list of subkeys without hash
784 li --+-->nk
786 +-->nk
788 typedef struct
790 WORD id; /* 0x00 0x696c */
791 WORD nr_keys;
792 DWORD off_nk[1];
793 } nt_li;
796 this is a intermediate node
798 ri --+-->li--+-->nk
800 | +-->nk
802 +-->li--+-->nk
804 +-->nk
806 typedef struct
808 WORD id; /* 0x00 0x6972 */
809 WORD nr_li; /* 0x02 number off offsets */
810 DWORD off_li[1]; /* 0x04 points to li */
811 } nt_ri;
813 typedef struct
815 WORD id; /* 0x00 'vk' */
816 WORD nam_len;
817 DWORD data_len;
818 DWORD data_off;
819 DWORD type;
820 WORD flag;
821 WORD uk1;
822 char name[1];
823 } nt_vk;
825 LPSTR _strdupnA( LPCSTR str, int len )
827 LPSTR ret;
829 if (!str) return NULL;
830 ret = malloc( len + 1 );
831 lstrcpynA( ret, str, len );
832 ret[len] = 0x00;
833 return ret;
836 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level);
837 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk);
838 static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level);
842 * gets a value
844 * vk->flag:
845 * 0 value is a default value
846 * 1 the value has a name
848 * vk->data_len
849 * len of the whole data block
850 * - reg_sz (unicode)
851 * bytes including the terminating \0 = 2*(number_of_chars+1)
852 * - reg_dword, reg_binary:
853 * if highest bit of data_len is set data_off contains the value
855 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk)
857 WCHAR name [256];
858 DWORD ret;
859 BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
861 if(vk->id != NT_REG_VALUE_BLOCK_ID) goto error;
863 lstrcpynAtoW(name, vk->name, vk->nam_len+1);
865 ret = RegSetValueExW( hkey, (vk->flag & 0x00000001) ? name : NULL, 0, vk->type,
866 (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata,
867 (vk->data_len & 0x7fffffff) );
868 if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
869 return TRUE;
870 error:
871 ERR_(reg)("unknown block found (0x%04x), please report!\n", vk->id);
872 return FALSE;
876 * get the subkeys
878 * this structure contains the hash of a keyname and points to all
879 * subkeys
881 * exception: if the id is 'il' there are no hash values and every
882 * dword is a offset
884 static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level)
886 int i;
888 if (lf->id == NT_REG_HASH_BLOCK_ID)
890 if (subkeys != lf->nr_keys) goto error1;
892 for (i=0; i<lf->nr_keys; i++)
894 if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), level)) goto error;
897 else if (lf->id == NT_REG_NOHASH_BLOCK_ID)
899 nt_li * li = (nt_li*)lf;
900 if (subkeys != li->nr_keys) goto error1;
902 for (i=0; i<li->nr_keys; i++)
904 if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+li->off_nk[i]+4), level)) goto error;
907 else if (lf->id == NT_REG_RI_BLOCK_ID) /* ri */
909 nt_ri * ri = (nt_ri*)lf;
910 int li_subkeys = 0;
912 /* count all subkeys */
913 for (i=0; i<ri->nr_li; i++)
915 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
916 if(li->id != NT_REG_NOHASH_BLOCK_ID) goto error2;
917 li_subkeys += li->nr_keys;
920 /* check number */
921 if (subkeys != li_subkeys) goto error1;
923 /* loop through the keys */
924 for (i=0; i<ri->nr_li; i++)
926 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
927 if (!_nt_parse_lf(hkey, base, li->nr_keys, (nt_lf*)li, level)) goto error;
930 else
932 goto error2;
934 return TRUE;
936 error2: ERR("unknown node id 0x%04x, please report!\n", lf->id);
937 return TRUE;
939 error1: ERR_(reg)("registry file corrupt! (inconsistent number of subkeys)\n");
940 return FALSE;
942 error: ERR_(reg)("error reading lf block\n");
943 return FALSE;
946 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
948 char * name;
949 int i;
950 DWORD * vl;
951 HKEY subkey = hkey;
953 if(nk->SubBlockId != NT_REG_KEY_BLOCK_ID)
955 ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId);
956 goto error;
959 if((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) &&
960 (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID))
962 ERR_(reg)("registry file corrupt!\n");
963 goto error;
966 /* create the new key */
967 if(level <= 0)
969 name = _strdupnA( nk->name, nk->name_len+1);
970 if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; }
971 free(name);
974 /* loop through the subkeys */
975 if (nk->nr_subkeys)
977 nt_lf * lf = (nt_lf*)(base+nk->lf_off+4);
978 if (!_nt_parse_lf(subkey, base, nk->nr_subkeys, lf, level-1)) goto error1;
981 /* loop trough the value list */
982 vl = (DWORD *)(base+nk->valuelist_off+4);
983 for (i=0; i<nk->nr_values; i++)
985 nt_vk * vk = (nt_vk*)(base+vl[i]+4);
986 if (!_nt_parse_vk(subkey, base, vk)) goto error1;
989 RegCloseKey(subkey);
990 return TRUE;
992 error1: RegCloseKey(subkey);
993 error: return FALSE;
996 /* end nt loader */
998 /* windows 95 registry loader */
1000 /* SECTION 1: main header
1002 * once at offset 0
1004 #define W95_REG_CREG_ID 0x47455243
1006 typedef struct
1008 DWORD id; /* "CREG" = W95_REG_CREG_ID */
1009 DWORD version; /* ???? 0x00010000 */
1010 DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
1011 DWORD uk2; /* 0x0c */
1012 WORD rgdb_num; /* 0x10 # of RGDB-blocks */
1013 WORD uk3;
1014 DWORD uk[3];
1015 /* rgkn */
1016 } _w95creg;
1018 /* SECTION 2: Directory information (tree structure)
1020 * once on offset 0x20
1022 * structure: [rgkn][dke]* (repeat till rgkn->size is reached)
1024 #define W95_REG_RGKN_ID 0x4e4b4752
1026 typedef struct
1028 DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
1029 DWORD size; /* Size of the RGKN-block */
1030 DWORD root_off; /* Rel. Offset of the root-record */
1031 DWORD uk[5];
1032 } _w95rgkn;
1034 /* Disk Key Entry Structure
1036 * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the
1037 * hive itself. It looks the same like other keys. Even the ID-number can
1038 * be any value.
1040 * The "hash"-value is a value representing the key's name. Windows will not
1041 * search for the name, but for a matching hash-value. if it finds one, it
1042 * will compare the actual string info, otherwise continue with the next key.
1043 * To calculate the hash initialize a D-Word with 0 and add all ASCII-values
1044 * of the string which are smaller than 0x80 (128) to this D-Word.
1046 * If you want to modify key names, also modify the hash-values, since they
1047 * cannot be found again (although they would be displayed in REGEDIT)
1048 * End of list-pointers are filled with 0xFFFFFFFF
1050 * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
1051 * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
1052 * structure) and reading another RGDB_section.
1054 * there is a one to one relationship between dke and dkh
1056 /* key struct, once per key */
1057 typedef struct
1059 DWORD x1; /* Free entry indicator(?) */
1060 DWORD hash; /* sum of bytes of keyname */
1061 DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
1062 DWORD prevlvl; /* offset of previous key */
1063 DWORD nextsub; /* offset of child key */
1064 DWORD next; /* offset of sibling key */
1065 WORD nrLS; /* id inside the rgdb block */
1066 WORD nrMS; /* number of the rgdb block */
1067 } _w95dke;
1069 /* SECTION 3: key information, values and data
1071 * structure:
1072 * section: [blocks]* (repeat creg->rgdb_num times)
1073 * blocks: [rgdb] [subblocks]* (repeat till block size reached )
1074 * subblocks: [dkh] [dkv]* (repeat dkh->values times )
1076 * An interesting relationship exists in RGDB_section. The value at offset
1077 * 10 equals the value at offset 4 minus the value at offset 8. I have no
1078 * idea at the moment what this means. (Kevin Cozens)
1081 /* block header, once per block */
1082 #define W95_REG_RGDB_ID 0x42444752
1084 typedef struct
1086 DWORD id; /* 0x00 'rgdb' = W95_REG_RGDB_ID */
1087 DWORD size; /* 0x04 */
1088 DWORD uk1; /* 0x08 */
1089 DWORD uk2; /* 0x0c */
1090 DWORD uk3; /* 0x10 */
1091 DWORD uk4; /* 0x14 */
1092 DWORD uk5; /* 0x18 */
1093 DWORD uk6; /* 0x1c */
1094 /* dkh */
1095 } _w95rgdb;
1097 /* Disk Key Header structure (RGDB part), once per key */
1098 typedef struct
1100 DWORD nextkeyoff; /* 0x00 offset to next dkh*/
1101 WORD nrLS; /* 0x04 id inside the rgdb block */
1102 WORD nrMS; /* 0x06 number of the rgdb block */
1103 DWORD bytesused; /* 0x08 */
1104 WORD keynamelen; /* 0x0c len of name */
1105 WORD values; /* 0x0e number of values */
1106 DWORD xx1; /* 0x10 */
1107 char name[1]; /* 0x14 */
1108 /* dkv */ /* 0x14 + keynamelen */
1109 } _w95dkh;
1111 /* Disk Key Value structure, once per value */
1112 typedef struct
1114 DWORD type; /* 0x00 */
1115 DWORD x1; /* 0x04 */
1116 WORD valnamelen; /* 0x08 length of name, 0 is default key */
1117 WORD valdatalen; /* 0x0A length of data */
1118 char name[1]; /* 0x0c */
1119 /* raw data */ /* 0x0c + valnamelen */
1120 } _w95dkv;
1122 /******************************************************************************
1123 * _w95_lookup_dkh [Internal]
1125 * seeks the dkh belonging to a dke
1127 static _w95dkh * _w95_lookup_dkh (_w95creg *creg, int nrLS, int nrMS)
1129 _w95rgdb * rgdb;
1130 _w95dkh * dkh;
1131 int i;
1133 rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off); /* get the beginning of the rgdb datastore */
1134 assert (creg->rgdb_num > nrMS); /* check: requested block < last_block) */
1136 /* find the right block */
1137 for(i=0; i<nrMS ;i++)
1139 assert(rgdb->id == W95_REG_RGDB_ID); /* check the magic */
1140 rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size); /* find next block */
1143 dkh = (_w95dkh*)(rgdb + 1); /* first sub block within the rgdb */
1147 if(nrLS==dkh->nrLS ) return dkh;
1148 dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */
1149 } while ((char *)dkh < ((char*)rgdb+rgdb->size));
1151 return NULL;
1154 /******************************************************************************
1155 * _w95_parse_dkv [Internal]
1157 static int _w95_parse_dkv (
1158 HKEY hkey,
1159 _w95dkh * dkh,
1160 int nrLS,
1161 int nrMS )
1163 _w95dkv * dkv;
1164 int i;
1165 DWORD ret;
1166 char * name;
1168 /* first value block */
1169 dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
1171 /* loop trought the values */
1172 for (i=0; i< dkh->values; i++)
1174 name = _strdupnA(dkv->name, dkv->valnamelen+1);
1175 ret = RegSetValueExA(hkey, name, 0, dkv->type, &(dkv->name[dkv->valnamelen]),dkv->valdatalen);
1176 if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
1177 free (name);
1179 /* next value */
1180 dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
1182 return TRUE;
1185 /******************************************************************************
1186 * _w95_parse_dke [Internal]
1188 static int _w95_parse_dke(
1189 HKEY hkey,
1190 _w95creg * creg,
1191 _w95rgkn *rgkn,
1192 _w95dke * dke,
1193 int level )
1195 _w95dkh * dkh;
1196 HKEY hsubkey = hkey;
1197 char * name;
1198 int ret = FALSE;
1200 /* get start address of root key block */
1201 if (!dke) dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
1203 /* special root key */
1204 if (dke->nrLS == 0xffff || dke->nrMS==0xffff) /* eg. the root key has no name */
1206 /* parse the one subkey*/
1207 if (dke->nextsub != 0xffffffff)
1209 return _w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level);
1211 /* has no sibling keys */
1212 goto error;
1215 /* search subblock */
1216 if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS)))
1218 fprintf(stderr, "dke pointing to missing dkh !\n");
1219 goto error;
1222 if ( level <= 0 )
1224 /* walk sibling keys */
1225 if (dke->next != 0xffffffff )
1227 if (!_w95_parse_dke(hkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next), level)) goto error;
1230 /* create subkey and insert values */
1231 name = _strdupnA( dkh->name, dkh->keynamelen+1);
1232 if (RegCreateKeyA(hkey, name, &hsubkey)) { free(name); goto error; }
1233 free(name);
1234 if (!_w95_parse_dkv(hsubkey, dkh, dke->nrLS, dke->nrMS)) goto error1;
1237 /* next sub key */
1238 if (dke->nextsub != 0xffffffff)
1240 if (!_w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level-1)) goto error1;
1243 ret = TRUE;
1244 error1: if (hsubkey != hkey) RegCloseKey(hsubkey);
1245 error: return ret;
1247 /* end windows 95 loader */
1249 /******************************************************************************
1250 * NativeRegLoadKey [Internal]
1252 * Loads a native registry file (win95/nt)
1253 * hkey root key
1254 * fn filename
1255 * level number of levels to cut away (eg. ".Default" in user.dat)
1257 * this function intentionally uses unix file functions to make it possible
1258 * to move it to a seperate registry helper programm
1260 static int NativeRegLoadKey( HKEY hkey, char* fn, int level )
1262 int fd = 0;
1263 struct stat st;
1264 DOS_FULL_NAME full_name;
1265 int ret = FALSE;
1266 void * base;
1268 if (!DOSFS_GetFullName( fn, 0, &full_name )) return FALSE;
1270 /* map the registry into the memory */
1271 if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE;
1272 if ((fstat(fd, &st) == -1)) goto error;
1273 if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error;
1275 switch (*(LPDWORD)base)
1277 /* windows 95 'creg' */
1278 case W95_REG_CREG_ID:
1280 _w95creg * creg;
1281 _w95rgkn * rgkn;
1282 creg = base;
1283 TRACE_(reg)("Loading win95 registry '%s' '%s'\n",fn, full_name.long_name);
1285 /* load the header (rgkn) */
1286 rgkn = (_w95rgkn*)(creg + 1);
1287 if (rgkn->id != W95_REG_RGKN_ID)
1289 ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
1290 goto error1;
1293 ret = _w95_parse_dke(hkey, creg, rgkn, NULL, level);
1295 break;
1296 /* nt 'regf'*/
1297 case NT_REG_HEADER_BLOCK_ID:
1299 nt_regf * regf;
1300 nt_hbin * hbin;
1301 nt_hbin_sub * hbin_sub;
1302 nt_nk* nk;
1304 TRACE_(reg)("Loading nt registry '%s' '%s'\n",fn, full_name.long_name);
1306 /* start block */
1307 regf = base;
1309 /* hbin block */
1310 hbin = (nt_hbin *) ((char *) base + 0x1000);
1311 if (hbin->id != NT_REG_POOL_BLOCK_ID)
1313 ERR_(reg)( "%s hbin block invalid\n", fn);
1314 goto error1;
1317 /* hbin_sub block */
1318 hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
1319 if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k'))
1321 ERR_(reg)( "%s hbin_sub block invalid\n", fn);
1322 goto error1;
1325 /* nk block */
1326 nk = (nt_nk*)&(hbin_sub->data[0]);
1327 if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE)
1329 ERR_(reg)( "%s special nk block not found\n", fn);
1330 goto error1;
1333 ret = _nt_parse_nk (hkey, (char *) base + 0x1000, nk, level);
1335 break;
1336 default:
1338 ERR("unknown signature in registry file %s.\n",fn);
1339 goto error1;
1342 if(!ret) ERR("error loading registry file %s\n", fn);
1343 error1: munmap(base, st.st_size);
1344 error: close(fd);
1345 return ret;
1348 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1350 reghack - windows 3.11 registry data format demo program.
1352 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1353 a combined hash table and tree description, and finally a text table.
1355 The header is obvious from the struct header. The taboff1 and taboff2
1356 fields are always 0x20, and their usage is unknown.
1358 The 8-byte entry table has various entry types.
1360 tabent[0] is a root index. The second word has the index of the root of
1361 the directory.
1362 tabent[1..hashsize] is a hash table. The first word in the hash entry is
1363 the index of the key/value that has that hash. Data with the same
1364 hash value are on a circular list. The other three words in the
1365 hash entry are always zero.
1366 tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1367 entry: dirent and keyent/valent. They are identified by context.
1368 tabent[freeidx] is the first free entry. The first word in a free entry
1369 is the index of the next free entry. The last has 0 as a link.
1370 The other three words in the free list are probably irrelevant.
1372 Entries in text table are preceeded by a word at offset-2. This word
1373 has the value (2*index)+1, where index is the referring keyent/valent
1374 entry in the table. I have no suggestion for the 2* and the +1.
1375 Following the word, there are N bytes of data, as per the keyent/valent
1376 entry length. The offset of the keyent/valent entry is from the start
1377 of the text table to the first data byte.
1379 This information is not available from Microsoft. The data format is
1380 deduced from the reg.dat file by me. Mistakes may
1381 have been made. I claim no rights and give no guarantees for this program.
1383 Tor Sjøwall, tor@sn.no
1386 /* reg.dat header format */
1387 struct _w31_header {
1388 char cookie[8]; /* 'SHCC3.10' */
1389 unsigned long taboff1; /* offset of hash table (??) = 0x20 */
1390 unsigned long taboff2; /* offset of index table (??) = 0x20 */
1391 unsigned long tabcnt; /* number of entries in index table */
1392 unsigned long textoff; /* offset of text part */
1393 unsigned long textsize; /* byte size of text part */
1394 unsigned short hashsize; /* hash size */
1395 unsigned short freeidx; /* free index */
1398 /* generic format of table entries */
1399 struct _w31_tabent {
1400 unsigned short w0, w1, w2, w3;
1403 /* directory tabent: */
1404 struct _w31_dirent {
1405 unsigned short sibling_idx; /* table index of sibling dirent */
1406 unsigned short child_idx; /* table index of child dirent */
1407 unsigned short key_idx; /* table index of key keyent */
1408 unsigned short value_idx; /* table index of value valent */
1411 /* key tabent: */
1412 struct _w31_keyent {
1413 unsigned short hash_idx; /* hash chain index for string */
1414 unsigned short refcnt; /* reference count */
1415 unsigned short length; /* length of string */
1416 unsigned short string_off; /* offset of string in text table */
1419 /* value tabent: */
1420 struct _w31_valent {
1421 unsigned short hash_idx; /* hash chain index for string */
1422 unsigned short refcnt; /* reference count */
1423 unsigned short length; /* length of string */
1424 unsigned short string_off; /* offset of string in text table */
1427 /* recursive helper function to display a directory tree */
1428 void
1429 __w31_dumptree( unsigned short idx,
1430 unsigned char *txt,
1431 struct _w31_tabent *tab,
1432 struct _w31_header *head,
1433 HKEY hkey,
1434 time_t lastmodified,
1435 int level
1437 struct _w31_dirent *dir;
1438 struct _w31_keyent *key;
1439 struct _w31_valent *val;
1440 HKEY subkey = 0;
1441 static char tail[400];
1443 while (idx!=0) {
1444 dir=(struct _w31_dirent*)&tab[idx];
1446 if (dir->key_idx) {
1447 key = (struct _w31_keyent*)&tab[dir->key_idx];
1449 memcpy(tail,&txt[key->string_off],key->length);
1450 tail[key->length]='\0';
1451 /* all toplevel entries AND the entries in the
1452 * toplevel subdirectory belong to \SOFTWARE\Classes
1454 if (!level && !lstrcmpA(tail,".classes")) {
1455 __w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
1456 idx=dir->sibling_idx;
1457 continue;
1459 if (subkey) RegCloseKey( subkey );
1460 if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
1461 /* only add if leaf node or valued node */
1462 if (dir->value_idx!=0||dir->child_idx==0) {
1463 if (dir->value_idx) {
1464 val=(struct _w31_valent*)&tab[dir->value_idx];
1465 memcpy(tail,&txt[val->string_off],val->length);
1466 tail[val->length]='\0';
1467 RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
1470 } else {
1471 TRACE("strange: no directory key name, idx=%04x\n", idx);
1473 __w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
1474 idx=dir->sibling_idx;
1476 if (subkey) RegCloseKey( subkey );
1480 /******************************************************************************
1481 * _w31_loadreg [Internal]
1483 void _w31_loadreg(void) {
1484 HFILE hf;
1485 struct _w31_header head;
1486 struct _w31_tabent *tab;
1487 unsigned char *txt;
1488 int len;
1489 OFSTRUCT ofs;
1490 BY_HANDLE_FILE_INFORMATION hfinfo;
1491 time_t lastmodified;
1493 TRACE("(void)\n");
1495 hf = OpenFile("reg.dat",&ofs,OF_READ);
1496 if (hf==HFILE_ERROR)
1497 return;
1499 /* read & dump header */
1500 if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
1501 ERR("reg.dat is too short.\n");
1502 _lclose(hf);
1503 return;
1505 if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1506 ERR("reg.dat has bad signature.\n");
1507 _lclose(hf);
1508 return;
1511 len = head.tabcnt * sizeof(struct _w31_tabent);
1512 /* read and dump index table */
1513 tab = xmalloc(len);
1514 if (len!=_lread(hf,tab,len)) {
1515 ERR("couldn't read %d bytes.\n",len);
1516 free(tab);
1517 _lclose(hf);
1518 return;
1521 /* read text */
1522 txt = xmalloc(head.textsize);
1523 if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
1524 ERR("couldn't seek to textblock.\n");
1525 free(tab);
1526 free(txt);
1527 _lclose(hf);
1528 return;
1530 if (head.textsize!=_lread(hf,txt,head.textsize)) {
1531 ERR("textblock too short (%d instead of %ld).\n",len,head.textsize);
1532 free(tab);
1533 free(txt);
1534 _lclose(hf);
1535 return;
1538 if (!GetFileInformationByHandle(hf,&hfinfo)) {
1539 ERR("GetFileInformationByHandle failed?.\n");
1540 free(tab);
1541 free(txt);
1542 _lclose(hf);
1543 return;
1545 lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1546 __w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
1547 free(tab);
1548 free(txt);
1549 _lclose(hf);
1550 return;
1553 /**********************************************************************************
1554 * SetLoadLevel [Internal]
1556 * set level to 0 for loading system files
1557 * set level to 1 for loading user files
1559 static void SetLoadLevel(int level)
1561 struct set_registry_levels_request *req = get_req_buffer();
1563 req->current = level;
1564 req->saving = 0;
1565 req->version = 1;
1566 server_call( REQ_SET_REGISTRY_LEVELS );
1569 /**********************************************************************************
1570 * SHELL_LoadRegistry [Internal]
1572 #define REG_DONTLOAD -1
1573 #define REG_WIN31 0
1574 #define REG_WIN95 1
1575 #define REG_WINNT 2
1577 void SHELL_LoadRegistry( void )
1579 char *fn, *home;
1580 HKEY hkey;
1581 char windir[MAX_PATHNAME_LEN];
1582 char path[MAX_PATHNAME_LEN];
1583 int systemtype = REG_WIN31;
1585 TRACE("(void)\n");
1587 if (!CLIENT_IsBootThread()) return; /* already loaded */
1589 REGISTRY_Init();
1590 SetLoadLevel(0);
1592 GetWindowsDirectoryA( windir, MAX_PATHNAME_LEN );
1594 if (PROFILE_GetWineIniBool( "Registry", "LoadWindowsRegistryFiles", 1))
1596 /* test %windir%/system32/config/system --> winnt */
1597 strcpy(path, windir);
1598 strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
1599 if(GetFileAttributesA(path) != -1)
1601 systemtype = REG_WINNT;
1603 else
1605 /* test %windir%/system.dat --> win95 */
1606 strcpy(path, windir);
1607 strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1608 if(GetFileAttributesA(path) != -1)
1610 systemtype = REG_WIN95;
1614 if ((systemtype==REG_WINNT)
1615 && (! PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)))
1617 MESSAGE("When you are running with a native NT directory specify\n");
1618 MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n");
1619 MESSAGE("registry (LoadWindowsRegistryFiles=N)\n");
1620 systemtype = REG_DONTLOAD;
1623 else
1625 /* only wine registry */
1626 systemtype = REG_DONTLOAD;
1629 switch (systemtype)
1631 case REG_WIN31:
1632 _w31_loadreg();
1633 break;
1635 case REG_WIN95:
1636 /* Load windows 95 entries */
1637 NativeRegLoadKey(HKEY_LOCAL_MACHINE, "C:\\system.1st", 0);
1639 strcpy(path, windir);
1640 strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1641 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1643 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
1645 /* user specific user.dat */
1646 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1647 if (!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
1649 MESSAGE("can't load win95 user-registry %s\n", path);
1650 MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
1652 /* default user.dat */
1653 if (!RegCreateKeyA(HKEY_USERS, ".Default", &hkey))
1655 strcpy(path, windir);
1656 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1657 NativeRegLoadKey(hkey, path, 1);
1658 RegCloseKey(hkey);
1661 else
1663 /* global user.dat */
1664 strcpy(path, windir);
1665 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1666 NativeRegLoadKey(HKEY_CURRENT_USER, path, 1);
1668 break;
1670 case REG_WINNT:
1671 /* default user.dat */
1672 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
1674 strncat(path, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1675 if(!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
1677 MESSAGE("can't load NT user-registry %s\n", path);
1678 MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
1682 /* default user.dat */
1683 if (!RegCreateKeyA(HKEY_USERS, ".Default", &hkey))
1685 strcpy(path, windir);
1686 strncat(path, "\\system32\\config\\default", MAX_PATHNAME_LEN - strlen(path) - 1);
1687 NativeRegLoadKey(hkey, path, 1);
1688 RegCloseKey(hkey);
1692 * FIXME
1693 * map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
1696 strcpy(path, windir);
1697 strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
1698 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1700 strcpy(path, windir);
1701 strncat(path, "\\system32\\config\\software", MAX_PATHNAME_LEN - strlen(path) - 1);
1702 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1704 strcpy(path, windir);
1705 strncat(path, "\\system32\\config\\sam", MAX_PATHNAME_LEN - strlen(path) - 1);
1706 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1708 strcpy(path, windir);
1709 strncat(path, "\\system32\\config\\security", MAX_PATHNAME_LEN - strlen(path) - 1);
1710 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1712 /* this key is generated when the nt-core booted successfully */
1713 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey))
1714 RegCloseKey(hkey);
1715 break;
1716 } /* switch */
1718 if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
1721 * Load the global HKU hive directly from sysconfdir
1723 _wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT );
1726 * Load the global machine defaults directly form sysconfdir
1728 _wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
1731 SetLoadLevel(1);
1734 * Load the user saved registries
1736 if (!(home = getenv( "HOME" )))
1737 WARN("Failed to get homedirectory of UID %ld.\n",(long) getuid());
1738 else if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1))
1741 * Load user's personal versions of global HKU/.Default keys
1743 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX) +
1744 strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
1745 strcpy(fn, home);
1746 strcat(fn, WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1747 _wine_loadreg( HKEY_USERS, fn );
1748 free(fn);
1750 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) + strlen(SAVE_CURRENT_USER)+2);
1751 strcpy(fn, home);
1752 strcat(fn, WINE_PREFIX"/"SAVE_CURRENT_USER);
1753 _wine_loadreg( HKEY_CURRENT_USER, fn );
1754 free(fn);
1757 * Load HKLM, attempt to get the registry location from the config
1758 * file first, if exist, load and keep going.
1760 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_MACHINE)+2);
1761 strcpy(fn,home);
1762 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1763 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1764 free(fn);
1768 * Load HKCU, get the registry location from the config
1769 * file, if exist, load and keep going.
1771 if (PROFILE_GetWineIniBool ( "registry", "LoadAltRegistryFiles", 1))
1773 fn = xmalloc( MAX_PATHNAME_LEN );
1774 if ( PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
1775 fn, MAX_PATHNAME_LEN - 1))
1777 _wine_loadreg( HKEY_CURRENT_USER, fn );
1779 free (fn);
1781 * Load HKU, get the registry location from the config
1782 * file, if exist, load and keep going.
1784 fn = xmalloc ( MAX_PATHNAME_LEN );
1785 if ( PROFILE_GetWineIniString ( "registry", "AltUserFile", "",
1786 fn, MAX_PATHNAME_LEN - 1))
1788 _wine_loadreg( HKEY_USERS, fn );
1790 free (fn);
1792 * Load HKLM, get the registry location from the config
1793 * file, if exist, load and keep going.
1795 fn = xmalloc ( MAX_PATHNAME_LEN );
1796 if (PROFILE_GetWineIniString ( "registry", "AltLocalMachineFile", "",
1797 fn, MAX_PATHNAME_LEN - 1))
1799 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1801 free (fn);
1806 /* start the periodic saving timer */
1807 void SHELL_InitRegistrySaving(void)
1809 int save_timeout;
1811 if (!CLIENT_IsBootThread()) return;
1813 if ((save_timeout = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 )))
1815 SERVICE_AddTimer( save_timeout * 1000000, periodic_save, 0 );
1820 /********************* API FUNCTIONS ***************************************/
1825 /******************************************************************************
1826 * RegFlushKey [KERNEL.227] [ADVAPI32.143]
1827 * Immediately writes key to registry.
1828 * Only returns after data has been written to disk.
1830 * FIXME: does it really wait until data is written ?
1832 * PARAMS
1833 * hkey [I] Handle of key to write
1835 * RETURNS
1836 * Success: ERROR_SUCCESS
1837 * Failure: Error code
1839 DWORD WINAPI RegFlushKey( HKEY hkey )
1841 FIXME( "(%x): stub\n", hkey );
1842 return ERROR_SUCCESS;
1845 /******************************************************************************
1846 * RegConnectRegistry32W [ADVAPI32.128]
1848 * PARAMS
1849 * lpMachineName [I] Address of name of remote computer
1850 * hHey [I] Predefined registry handle
1851 * phkResult [I] Address of buffer for remote registry handle
1853 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey,
1854 LPHKEY phkResult )
1856 TRACE("(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
1858 if (!lpMachineName || !*lpMachineName) {
1859 /* Use the local machine name */
1860 return RegOpenKey16( hKey, "", phkResult );
1863 FIXME("Cannot connect to %s\n",debugstr_w(lpMachineName));
1864 return ERROR_BAD_NETPATH;
1868 /******************************************************************************
1869 * RegConnectRegistry32A [ADVAPI32.127]
1871 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, LPHKEY reskey )
1873 DWORD ret;
1874 LPWSTR machineW = strdupA2W(machine);
1875 ret = RegConnectRegistryW( machineW, hkey, reskey );
1876 free(machineW);
1877 return ret;
1881 /******************************************************************************
1882 * RegGetKeySecurity [ADVAPI32.144]
1883 * Retrieves a copy of security descriptor protecting the registry key
1885 * PARAMS
1886 * hkey [I] Open handle of key to set
1887 * SecurityInformation [I] Descriptor contents
1888 * pSecurityDescriptor [O] Address of descriptor for key
1889 * lpcbSecurityDescriptor [I/O] Address of size of buffer and description
1891 * RETURNS
1892 * Success: ERROR_SUCCESS
1893 * Failure: Error code
1895 LONG WINAPI RegGetKeySecurity( HKEY hkey,
1896 SECURITY_INFORMATION SecurityInformation,
1897 PSECURITY_DESCRIPTOR pSecurityDescriptor,
1898 LPDWORD lpcbSecurityDescriptor )
1900 TRACE("(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
1901 lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1903 /* FIXME: Check for valid SecurityInformation values */
1905 if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
1906 return ERROR_INSUFFICIENT_BUFFER;
1908 FIXME("(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
1909 pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1911 return ERROR_SUCCESS;
1915 /******************************************************************************
1916 * RegNotifyChangeKeyValue [ADVAPI32.???]
1918 * PARAMS
1919 * hkey [I] Handle of key to watch
1920 * fWatchSubTree [I] Flag for subkey notification
1921 * fdwNotifyFilter [I] Changes to be reported
1922 * hEvent [I] Handle of signaled event
1923 * fAsync [I] Flag for asynchronous reporting
1925 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree,
1926 DWORD fdwNotifyFilter, HANDLE hEvent,
1927 BOOL fAsync )
1929 FIXME("(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter,
1930 hEvent,fAsync);
1931 return ERROR_SUCCESS;
1935 /******************************************************************************
1936 * RegUnLoadKey32W [ADVAPI32.173]
1938 * PARAMS
1939 * hkey [I] Handle of open key
1940 * lpSubKey [I] Address of name of subkey to unload
1942 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
1944 FIXME("(%x,%s): stub\n",hkey, debugstr_w(lpSubKey));
1945 return ERROR_SUCCESS;
1949 /******************************************************************************
1950 * RegUnLoadKey32A [ADVAPI32.172]
1952 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1954 LONG ret;
1955 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1956 ret = RegUnLoadKeyW( hkey, lpSubKeyW );
1957 if(lpSubKeyW) free(lpSubKeyW);
1958 return ret;
1962 /******************************************************************************
1963 * RegSetKeySecurity [ADVAPI32.167]
1965 * PARAMS
1966 * hkey [I] Open handle of key to set
1967 * SecurityInfo [I] Descriptor contents
1968 * pSecurityDesc [I] Address of descriptor for key
1970 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
1971 PSECURITY_DESCRIPTOR pSecurityDesc )
1973 TRACE("(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
1975 /* It seems to perform this check before the hkey check */
1976 if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
1977 (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
1978 (SecurityInfo & DACL_SECURITY_INFORMATION) ||
1979 (SecurityInfo & SACL_SECURITY_INFORMATION)) {
1980 /* Param OK */
1981 } else
1982 return ERROR_INVALID_PARAMETER;
1984 if (!pSecurityDesc)
1985 return ERROR_INVALID_PARAMETER;
1987 FIXME(":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
1989 return ERROR_SUCCESS;
1993 /******************************************************************************
1994 * RegRestoreKey32W [ADVAPI32.164]
1996 * PARAMS
1997 * hkey [I] Handle of key where restore begins
1998 * lpFile [I] Address of filename containing saved tree
1999 * dwFlags [I] Optional flags
2001 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
2003 TRACE("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
2005 /* It seems to do this check before the hkey check */
2006 if (!lpFile || !*lpFile)
2007 return ERROR_INVALID_PARAMETER;
2009 FIXME("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
2011 /* Check for file existence */
2013 return ERROR_SUCCESS;
2017 /******************************************************************************
2018 * RegRestoreKey32A [ADVAPI32.163]
2020 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
2022 LONG ret;
2023 LPWSTR lpFileW = strdupA2W(lpFile);
2024 ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
2025 if(lpFileW) free(lpFileW);
2026 return ret;
2030 /******************************************************************************
2031 * RegReplaceKey32W [ADVAPI32.162]
2033 * PARAMS
2034 * hkey [I] Handle of open key
2035 * lpSubKey [I] Address of name of subkey
2036 * lpNewFile [I] Address of filename for file with new data
2037 * lpOldFile [I] Address of filename for backup file
2039 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
2040 LPCWSTR lpOldFile )
2042 FIXME("(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey),
2043 debugstr_w(lpNewFile),debugstr_w(lpOldFile));
2044 return ERROR_SUCCESS;
2048 /******************************************************************************
2049 * RegReplaceKey32A [ADVAPI32.161]
2051 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
2052 LPCSTR lpOldFile )
2054 LONG ret;
2055 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
2056 LPWSTR lpNewFileW = strdupA2W(lpNewFile);
2057 LPWSTR lpOldFileW = strdupA2W(lpOldFile);
2058 ret = RegReplaceKeyW( hkey, lpSubKeyW, lpNewFileW, lpOldFileW );
2059 free(lpOldFileW);
2060 free(lpNewFileW);
2061 free(lpSubKeyW);
2062 return ret;
2070 /* 16-bit functions */
2072 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
2073 * some programs. Do not remove those cases. -MM
2075 static inline void fix_win16_hkey( HKEY *hkey )
2077 if (*hkey == 0 || *hkey == 1) *hkey = HKEY_CLASSES_ROOT;
2080 /******************************************************************************
2081 * RegEnumKey16 [KERNEL.216] [SHELL.7]
2083 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
2085 fix_win16_hkey( &hkey );
2086 return RegEnumKeyA( hkey, index, name, name_len );
2089 /******************************************************************************
2090 * RegOpenKey16 [KERNEL.217] [SHELL.1]
2092 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
2094 fix_win16_hkey( &hkey );
2095 return RegOpenKeyA( hkey, name, retkey );
2098 /******************************************************************************
2099 * RegCreateKey16 [KERNEL.218] [SHELL.2]
2101 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
2103 fix_win16_hkey( &hkey );
2104 return RegCreateKeyA( hkey, name, retkey );
2107 /******************************************************************************
2108 * RegDeleteKey16 [KERNEL.219] [SHELL.4]
2110 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
2112 fix_win16_hkey( &hkey );
2113 return RegDeleteKeyA( hkey, name );
2116 /******************************************************************************
2117 * RegCloseKey16 [KERNEL.220] [SHELL.3]
2119 DWORD WINAPI RegCloseKey16( HKEY hkey )
2121 fix_win16_hkey( &hkey );
2122 return RegCloseKey( hkey );
2125 /******************************************************************************
2126 * RegSetValue16 [KERNEL.221] [SHELL.5]
2128 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
2130 fix_win16_hkey( &hkey );
2131 return RegSetValueA( hkey, name, type, data, count );
2134 /******************************************************************************
2135 * RegDeleteValue16 [KERNEL.222]
2137 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
2139 fix_win16_hkey( &hkey );
2140 return RegDeleteValueA( hkey, name );
2143 /******************************************************************************
2144 * RegEnumValue16 [KERNEL.223]
2146 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
2147 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
2149 fix_win16_hkey( &hkey );
2150 return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
2153 /******************************************************************************
2154 * RegQueryValue16 [KERNEL.224] [SHELL.6]
2156 * NOTES
2157 * Is this HACK still applicable?
2159 * HACK
2160 * The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
2161 * mask out the high 16 bit. This (not so much incidently) hopefully fixes
2162 * Aldus FH4)
2164 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
2166 fix_win16_hkey( &hkey );
2167 if (count) *count &= 0xffff;
2168 return RegQueryValueA( hkey, name, data, count );
2171 /******************************************************************************
2172 * RegQueryValueEx16 [KERNEL.225]
2174 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
2175 LPBYTE data, LPDWORD count )
2177 fix_win16_hkey( &hkey );
2178 return RegQueryValueExA( hkey, name, reserved, type, data, count );
2181 /******************************************************************************
2182 * RegSetValueEx16 [KERNEL.226]
2184 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
2185 CONST BYTE *data, DWORD count )
2187 fix_win16_hkey( &hkey );
2188 return RegSetValueExA( hkey, name, reserved, type, data, count );