Moved command-line option handling out of the X11 driver.
[wine.git] / misc / registry.c
blob93306689d7c8db9692db190dee90b51cc96dae52
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 "xmalloc.h"
48 #include "options.h"
49 #include "winreg.h"
50 #include "server.h"
51 #include "services.h"
53 DEFAULT_DEBUG_CHANNEL(reg);
55 static void REGISTRY_Init(void);
56 /* FIXME: following defines should be configured global ... */
58 /* NOTE: do not append a /. linux' mkdir() WILL FAIL if you do that */
59 #define WINE_PREFIX "/.wine"
60 #define SAVE_USERS_DEFAULT ETCDIR"/wine.userreg"
61 #define SAVE_LOCAL_MACHINE_DEFAULT ETCDIR"/wine.systemreg"
63 /* relative in ~user/.wine/ : */
64 #define SAVE_CURRENT_USER "user.reg"
65 #define SAVE_LOCAL_USERS_DEFAULT "wine.userreg"
66 #define SAVE_LOCAL_MACHINE "system.reg"
68 #define KEY_REGISTRY "Software\\The WINE team\\WINE\\Registry"
69 #define VAL_SAVEUPDATED "SaveOnlyUpdatedKeys"
72 /* what valuetypes do we need to convert? */
73 #define UNICONVMASK ((1<<REG_SZ)|(1<<REG_MULTI_SZ)|(1<<REG_EXPAND_SZ))
78 * QUESTION
79 * Are these doing the same as HEAP_strdupAtoW and HEAP_strdupWtoA?
80 * If so, can we remove them?
81 * ANSWER
82 * No, the memory handling functions are called very often in here,
83 * just replacing them by HeapAlloc(SystemHeap,...) makes registry
84 * loading 100 times slower. -MM
86 static LPWSTR strdupA2W(LPCSTR src)
88 if(src) {
89 LPWSTR dest=xmalloc(2*strlen(src)+2);
90 lstrcpyAtoW(dest,src);
91 return dest;
93 return NULL;
96 LPWSTR strcvtA2W(LPCSTR src, int nchars)
99 LPWSTR dest = xmalloc (2 * nchars + 2);
101 lstrcpynAtoW(dest,src,nchars+1);
102 return dest;
107 /******************************************************************************
108 * REGISTRY_Init [Internal]
109 * Registry initialisation, allocates some default keys.
111 static void REGISTRY_Init(void) {
112 HKEY hkey;
113 char buf[200];
115 TRACE("(void)\n");
117 RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
118 RegCloseKey(hkey);
120 /* This was an Open, but since it is called before the real registries
121 are loaded, it was changed to a Create - MTB 980507*/
122 RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
123 RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
124 RegCloseKey(hkey);
126 /* \\SOFTWARE\\Microsoft\\Window NT\\CurrentVersion
127 * CurrentVersion
128 * CurrentBuildNumber
129 * CurrentType
130 * string RegisteredOwner
131 * string RegisteredOrganization
134 /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
135 * string SysContact
136 * string SysLocation
137 * SysServices
139 if (-1!=gethostname(buf,200)) {
140 RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
141 RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
142 RegCloseKey(hkey);
147 /************************ SAVE Registry Function ****************************/
149 #define REGISTRY_SAVE_VERSION 0x00000001
151 /* Registry saveformat:
152 * If you change it, increase above number by 1, which will flush
153 * old registry database files.
155 * Global:
156 * "WINE REGISTRY Version %d"
157 * subkeys....
158 * Subkeys:
159 * keyname
160 * valuename=lastmodified,type,data
161 * ...
162 * subkeys
163 * ...
164 * keyname,valuename,stringdata:
165 * the usual ascii characters from 0x00-0xff (well, not 0x00)
166 * and \uXXXX as UNICODE value XXXX with XXXX>0xff
167 * ( "=\\\t" escaped in \uXXXX form.)
168 * type,lastmodified:
169 * int
171 * FIXME: doesn't save 'class' (what does it mean anyway?), nor flags.
173 * [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry]
174 * SaveOnlyUpdatedKeys=yes
177 /* Same as RegSaveKey but with Unix pathnames */
178 static void save_key( HKEY hkey, const char *filename )
180 struct save_registry_request *req = get_req_buffer();
181 int count = 0;
182 DWORD ret;
183 HANDLE handle;
184 char *p;
185 char *name = HeapAlloc( GetProcessHeap(), 0, strlen(filename) + 20 );
187 if (!name) return;
188 strcpy( name, filename );
189 if ((p = strrchr( name, '/' ))) p++;
190 else p = name;
192 for (;;)
194 sprintf( p, "reg%04x.tmp", count++ );
195 handle = FILE_CreateFile( name, GENERIC_WRITE, 0, NULL,
196 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, -1 );
197 if (handle != INVALID_HANDLE_VALUE) break;
198 if ((ret = GetLastError()) != ERROR_ALREADY_EXISTS) break;
201 if (handle != INVALID_HANDLE_VALUE)
203 req->hkey = hkey;
204 req->file = handle;
205 ret = server_call_noerr( REQ_SAVE_REGISTRY );
206 CloseHandle( handle );
207 if (ret) unlink( name );
208 else if (rename( name, filename ) == -1)
210 ERR( "Failed to move %s to %s: ", name, filename );
211 perror( "rename" );
212 unlink( name );
215 else ERR( "Failed to save registry to %s, err %ld\n", name, GetLastError() );
217 HeapFree( GetProcessHeap(), 0, name );
221 /******************************************************************************
222 * SHELL_SaveRegistryBranch [Internal]
224 * Saves main registry branch specified by hkey.
226 static void SHELL_SaveRegistryBranch(HKEY hkey)
228 char *fn, *home;
230 /* Find out what to save to, get from config file */
231 BOOL writeToHome = PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1);
232 BOOL writeToAlt = PROFILE_GetWineIniBool("registry","WritetoAltRegistries",1);
234 /* FIXME: does this check apply to all keys written below ? */
235 if (!(home = getenv( "HOME" )))
236 ERR("Failed to get homedirectory of UID %ld.\n",(long) getuid());
238 /* HKEY_LOCAL_MACHINE contains the HKEY_CLASSES_ROOT branch */
239 if (hkey == HKEY_CLASSES_ROOT) hkey = HKEY_LOCAL_MACHINE;
241 switch (hkey)
243 case HKEY_CURRENT_USER:
244 fn = xmalloc( MAX_PATHNAME_LEN );
245 if (writeToAlt && PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
246 fn, MAX_PATHNAME_LEN - 1))
247 save_key( HKEY_CURRENT_USER, fn );
248 free (fn);
250 if (home && writeToHome)
252 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
253 strlen(SAVE_CURRENT_USER) + 2 );
254 strcpy(fn,home);
255 strcat(fn,WINE_PREFIX);
257 /* create the directory. don't care about errorcodes. */
258 mkdir(fn,0755); /* drwxr-xr-x */
259 strcat(fn,"/"SAVE_CURRENT_USER);
260 save_key( HKEY_CURRENT_USER, fn );
261 free(fn);
263 break;
264 case HKEY_LOCAL_MACHINE:
265 /* Try first saving according to the defined location in .winerc */
266 fn = xmalloc ( MAX_PATHNAME_LEN);
267 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltLocalMachineFile", "",
268 fn, MAX_PATHNAME_LEN - 1))
269 save_key( HKEY_LOCAL_MACHINE, fn );
270 free (fn);
272 if (home && writeToHome)
274 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
275 strlen(SAVE_LOCAL_MACHINE) + 2);
276 strcpy(fn,home);
277 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
278 save_key( HKEY_LOCAL_MACHINE, fn );
279 free(fn);
281 break;
282 case HKEY_USERS:
283 fn = xmalloc( MAX_PATHNAME_LEN );
284 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltUserFile", "",
285 fn, MAX_PATHNAME_LEN - 1))
286 save_key( HKEY_USERS, fn );
287 free (fn);
289 if (home && writeToHome)
291 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
292 strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
293 strcpy(fn,home);
294 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
295 save_key( HKEY_USERS, fn );
296 free(fn);
298 break;
299 default:
300 ERR("unknown/invalid key handle !\n");
301 break;
306 /******************************************************************************
307 * SHELL_SaveRegistry [Internal]
309 void SHELL_SaveRegistry( void )
311 struct set_registry_levels_request *req = get_req_buffer();
312 char buf[4];
313 HKEY hkey;
314 int all;
316 TRACE("(void)\n");
318 all=0;
319 if (RegOpenKeyA(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS)
321 strcpy(buf,"yes");
323 else
325 DWORD len,junk,type;
327 len=4;
328 if ((ERROR_SUCCESS!=RegQueryValueExA( hkey,
329 VAL_SAVEUPDATED,
330 &junk,
331 &type,
332 buf,
333 &len)) || (type!=REG_SZ))
335 strcpy(buf,"yes");
337 RegCloseKey(hkey);
340 if (lstrcmpiA(buf,"yes")) all = 1;
342 /* set saving level (0 for saving everything, 1 for saving only modified keys) */
343 req->current = 1;
344 req->saving = !all;
345 req->version = PROFILE_GetWineIniBool( "registry", "UseNewFormat", 0 ) ? 2 : 1;
346 server_call( REQ_SET_REGISTRY_LEVELS );
348 SHELL_SaveRegistryBranch(HKEY_CURRENT_USER);
349 SHELL_SaveRegistryBranch(HKEY_LOCAL_MACHINE);
350 SHELL_SaveRegistryBranch(HKEY_USERS);
353 /* Periodic save callback */
354 static void CALLBACK periodic_save( ULONG_PTR dummy )
356 SHELL_SaveRegistry();
359 /************************ LOAD Registry Function ****************************/
363 /******************************************************************************
364 * _find_or_add_key [Internal]
366 static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
368 HKEY subkey;
369 if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
370 if (keyname) free( keyname );
371 return subkey;
374 /******************************************************************************
375 * _find_or_add_value [Internal]
377 static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len )
379 RegSetValueExW( hkey, name, 0, type, data, len );
380 if (name) free( name );
381 if (data) free( data );
385 /******************************************************************************
386 * _wine_read_line [Internal]
388 * reads a line including dynamically enlarging the readbuffer and throwing
389 * away comments
391 static int _wine_read_line( FILE *F, char **buf, int *len )
393 char *s,*curread;
394 int mylen,curoff;
396 curread = *buf;
397 mylen = *len;
398 **buf = '\0';
399 while (1) {
400 while (1) {
401 s=fgets(curread,mylen,F);
402 if (s==NULL)
403 return 0; /* EOF */
404 if (NULL==(s=strchr(curread,'\n'))) {
405 /* buffer wasn't large enough */
406 curoff = strlen(*buf);
407 *buf = xrealloc(*buf,*len*2);
408 curread = *buf + curoff;
409 mylen = *len; /* we filled up the buffer and
410 * got new '*len' bytes to fill
412 *len = *len * 2;
413 } else {
414 *s='\0';
415 break;
418 /* throw away comments */
419 if (**buf=='#' || **buf==';') {
420 curread = *buf;
421 mylen = *len;
422 continue;
424 if (s) /* got end of line */
425 break;
427 return 1;
431 /******************************************************************************
432 * _wine_read_USTRING [Internal]
434 * converts a char* into a UNICODE string (up to a special char)
435 * and returns the position exactly after that string
437 static char* _wine_read_USTRING( char *buf, LPWSTR *str )
439 char *s;
440 LPWSTR ws;
442 /* read up to "=" or "\0" or "\n" */
443 s = buf;
444 *str = (LPWSTR)xmalloc(2*strlen(buf)+2);
445 ws = *str;
446 while (*s && (*s!='\n') && (*s!='=')) {
447 if (*s!='\\')
448 *ws++=*((unsigned char*)s++);
449 else {
450 s++;
451 if (!*s) {
452 /* Dangling \ ... may only happen if a registry
453 * write was short. FIXME: What do to?
455 break;
457 if (*s=='\\') {
458 *ws++='\\';
459 s++;
460 continue;
462 if (*s!='u') {
463 WARN("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
464 *ws++='\\';
465 *ws++=*s++;
466 } else {
467 char xbuf[5];
468 int wc;
470 s++;
471 memcpy(xbuf,s,4);xbuf[4]='\0';
472 if (!sscanf(xbuf,"%x",&wc))
473 WARN("Strange escape sequence %s found in |%s|\n",xbuf,buf);
474 s+=4;
475 *ws++ =(unsigned short)wc;
479 *ws = 0;
480 return s;
484 /******************************************************************************
485 * _wine_loadsubkey [Internal]
487 * NOTES
488 * It seems like this is returning a boolean. Should it?
490 * RETURNS
491 * Success: 1
492 * Failure: 0
494 static int _wine_loadsubkey( FILE *F, HKEY hkey, int level, char **buf, int *buflen )
496 HKEY subkey;
497 int i;
498 char *s;
499 LPWSTR name;
501 TRACE("(%p,%x,%d,%s,%d)\n", F, hkey, level, debugstr_a(*buf), *buflen);
503 /* Good. We already got a line here ... so parse it */
504 subkey = 0;
505 while (1) {
506 i=0;s=*buf;
507 while (*s=='\t') {
508 s++;
509 i++;
511 if (i>level) {
512 if (!subkey) {
513 WARN("Got a subhierarchy without resp. key?\n");
514 return 0;
516 if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen))
517 if (!_wine_read_line(F,buf,buflen))
518 goto done;
519 continue;
522 /* let the caller handle this line */
523 if (i<level || **buf=='\0')
524 goto done;
526 /* it can be: a value or a keyname. Parse the name first */
527 s=_wine_read_USTRING(s,&name);
529 /* switch() default: hack to avoid gotos */
530 switch (0) {
531 default:
532 if (*s=='\0') {
533 if (subkey) RegCloseKey( subkey );
534 subkey=_find_or_add_key(hkey,name);
535 } else {
536 LPBYTE data;
537 int len,lastmodified,type;
539 if (*s!='=') {
540 WARN("Unexpected character: %c\n",*s);
541 break;
543 s++;
544 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
545 WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
546 break;
548 /* skip the 2 , */
549 s=strchr(s,',');s++;
550 s=strchr(s,',');
551 if (!s++) {
552 WARN("Haven't understood possible value in |%s|, skipping.\n",*buf);
553 break;
555 if (type == REG_SZ || type == REG_EXPAND_SZ) {
556 s=_wine_read_USTRING(s,(LPWSTR*)&data);
557 len = lstrlenW((LPWSTR)data)*2+2;
558 } else {
559 len=strlen(s)/2;
560 data = (LPBYTE)xmalloc(len+1);
561 for (i=0;i<len;i++) {
562 data[i]=0;
563 if (*s>='0' && *s<='9')
564 data[i]=(*s-'0')<<4;
565 if (*s>='a' && *s<='f')
566 data[i]=(*s-'a'+'\xa')<<4;
567 if (*s>='A' && *s<='F')
568 data[i]=(*s-'A'+'\xa')<<4;
569 s++;
570 if (*s>='0' && *s<='9')
571 data[i]|=*s-'0';
572 if (*s>='a' && *s<='f')
573 data[i]|=*s-'a'+'\xa';
574 if (*s>='A' && *s<='F')
575 data[i]|=*s-'A'+'\xa';
576 s++;
579 _find_or_add_value(hkey,name,type,data,len);
582 /* read the next line */
583 if (!_wine_read_line(F,buf,buflen))
584 goto done;
586 done:
587 if (subkey) RegCloseKey( subkey );
588 return 1;
592 /******************************************************************************
593 * _wine_loadsubreg [Internal]
595 static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
597 int ver;
598 char *buf;
599 int buflen;
601 buf=xmalloc(10);buflen=10;
602 if (!_wine_read_line(F,&buf,&buflen)) {
603 free(buf);
604 return 0;
606 if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
607 free(buf);
608 return 0;
610 if (ver!=REGISTRY_SAVE_VERSION) {
611 if (ver == 2) /* new version */
613 HANDLE file;
614 if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
615 FILE_ATTRIBUTE_NORMAL, -1 )) != INVALID_HANDLE_VALUE)
617 struct load_registry_request *req = get_req_buffer();
618 req->hkey = hkey;
619 req->file = file;
620 req->name[0] = 0;
621 server_call( REQ_LOAD_REGISTRY );
622 CloseHandle( file );
624 free( buf );
625 return 1;
627 else
629 TRACE("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
630 free(buf);
631 return 0;
634 if (!_wine_read_line(F,&buf,&buflen)) {
635 free(buf);
636 return 0;
638 if (!_wine_loadsubkey(F,hkey,0,&buf,&buflen)) {
639 free(buf);
640 return 0;
642 free(buf);
643 return 1;
647 /******************************************************************************
648 * _wine_loadreg [Internal]
650 static void _wine_loadreg( HKEY hkey, char *fn )
652 FILE *F;
654 TRACE("(%x,%s)\n",hkey,debugstr_a(fn));
656 F = fopen(fn,"rb");
657 if (F==NULL) {
658 WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
659 return;
661 _wine_loadsubreg(F,hkey,fn);
662 fclose(F);
665 /* NT REGISTRY LOADER */
667 #ifdef HAVE_SYS_MMAN_H
668 # include <sys/mman.h>
669 #endif
671 #ifndef MAP_FAILED
672 #define MAP_FAILED ((LPVOID)-1)
673 #endif
675 #define NT_REG_BLOCK_SIZE 0x1000
677 #define NT_REG_HEADER_BLOCK_ID 0x66676572 /* regf */
678 #define NT_REG_POOL_BLOCK_ID 0x6E696268 /* hbin */
679 #define NT_REG_KEY_BLOCK_ID 0x6b6e /* nk */
680 #define NT_REG_VALUE_BLOCK_ID 0x6b76 /* vk */
682 /* subblocks of nk */
683 #define NT_REG_HASH_BLOCK_ID 0x666c /* lf */
684 #define NT_REG_NOHASH_BLOCK_ID 0x696c /* li */
685 #define NT_REG_RI_BLOCK_ID 0x6972 /* ri */
687 #define NT_REG_KEY_BLOCK_TYPE 0x20
688 #define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c
690 typedef struct
692 DWORD id; /* 0x66676572 'regf'*/
693 DWORD uk1; /* 0x04 */
694 DWORD uk2; /* 0x08 */
695 FILETIME DateModified; /* 0x0c */
696 DWORD uk3; /* 0x14 */
697 DWORD uk4; /* 0x18 */
698 DWORD uk5; /* 0x1c */
699 DWORD uk6; /* 0x20 */
700 DWORD RootKeyBlock; /* 0x24 */
701 DWORD BlockSize; /* 0x28 */
702 DWORD uk7[116];
703 DWORD Checksum; /* at offset 0x1FC */
704 } nt_regf;
706 typedef struct
708 DWORD blocksize;
709 BYTE data[1];
710 } nt_hbin_sub;
712 typedef struct
714 DWORD id; /* 0x6E696268 'hbin' */
715 DWORD off_prev;
716 DWORD off_next;
717 DWORD uk1;
718 DWORD uk2; /* 0x10 */
719 DWORD uk3; /* 0x14 */
720 DWORD uk4; /* 0x18 */
721 DWORD size; /* 0x1C */
722 nt_hbin_sub hbin_sub; /* 0x20 */
723 } nt_hbin;
726 * the value_list consists of offsets to the values (vk)
728 typedef struct
730 WORD SubBlockId; /* 0x00 0x6B6E */
731 WORD Type; /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
732 FILETIME writetime; /* 0x04 */
733 DWORD uk1; /* 0x0C */
734 DWORD parent_off; /* 0x10 Offset of Owner/Parent key */
735 DWORD nr_subkeys; /* 0x14 number of sub-Keys */
736 DWORD uk8; /* 0x18 */
737 DWORD lf_off; /* 0x1C Offset of the sub-key lf-Records */
738 DWORD uk2; /* 0x20 */
739 DWORD nr_values; /* 0x24 number of values */
740 DWORD valuelist_off; /* 0x28 Offset of the Value-List */
741 DWORD off_sk; /* 0x2c Offset of the sk-Record */
742 DWORD off_class; /* 0x30 Offset of the Class-Name */
743 DWORD uk3; /* 0x34 */
744 DWORD uk4; /* 0x38 */
745 DWORD uk5; /* 0x3c */
746 DWORD uk6; /* 0x40 */
747 DWORD uk7; /* 0x44 */
748 WORD name_len; /* 0x48 name-length */
749 WORD class_len; /* 0x4a class-name length */
750 char name[1]; /* 0x4c key-name */
751 } nt_nk;
753 typedef struct
755 DWORD off_nk; /* 0x00 */
756 DWORD name; /* 0x04 */
757 } hash_rec;
759 typedef struct
761 WORD id; /* 0x00 0x666c */
762 WORD nr_keys; /* 0x06 */
763 hash_rec hash_rec[1];
764 } nt_lf;
767 list of subkeys without hash
769 li --+-->nk
771 +-->nk
773 typedef struct
775 WORD id; /* 0x00 0x696c */
776 WORD nr_keys;
777 DWORD off_nk[1];
778 } nt_li;
781 this is a intermediate node
783 ri --+-->li--+-->nk
785 | +-->nk
787 +-->li--+-->nk
789 +-->nk
791 typedef struct
793 WORD id; /* 0x00 0x6972 */
794 WORD nr_li; /* 0x02 number off offsets */
795 DWORD off_li[1]; /* 0x04 points to li */
796 } nt_ri;
798 typedef struct
800 WORD id; /* 0x00 'vk' */
801 WORD nam_len;
802 DWORD data_len;
803 DWORD data_off;
804 DWORD type;
805 WORD flag;
806 WORD uk1;
807 char name[1];
808 } nt_vk;
810 LPSTR _strdupnA( LPCSTR str, int len )
812 LPSTR ret;
814 if (!str) return NULL;
815 ret = malloc( len + 1 );
816 lstrcpynA( ret, str, len );
817 ret[len] = 0x00;
818 return ret;
821 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level);
822 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk);
823 static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level);
827 * gets a value
829 * vk->flag:
830 * 0 value is a default value
831 * 1 the value has a name
833 * vk->data_len
834 * len of the whole data block
835 * - reg_sz (unicode)
836 * bytes including the terminating \0 = 2*(number_of_chars+1)
837 * - reg_dword, reg_binary:
838 * if highest bit of data_len is set data_off contains the value
840 static int _nt_parse_vk(HKEY hkey, char * base, nt_vk * vk)
842 WCHAR name [256];
843 DWORD ret;
844 BYTE * pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
846 if(vk->id != NT_REG_VALUE_BLOCK_ID) goto error;
848 lstrcpynAtoW(name, vk->name, vk->nam_len+1);
850 ret = RegSetValueExW( hkey, (vk->flag & 0x00000001) ? name : NULL, 0, vk->type,
851 (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata,
852 (vk->data_len & 0x7fffffff) );
853 if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
854 return TRUE;
855 error:
856 ERR_(reg)("unknown block found (0x%04x), please report!\n", vk->id);
857 return FALSE;
861 * get the subkeys
863 * this structure contains the hash of a keyname and points to all
864 * subkeys
866 * exception: if the id is 'il' there are no hash values and every
867 * dword is a offset
869 static int _nt_parse_lf(HKEY hkey, char * base, int subkeys, nt_lf * lf, int level)
871 int i;
873 if (lf->id == NT_REG_HASH_BLOCK_ID)
875 if (subkeys != lf->nr_keys) goto error1;
877 for (i=0; i<lf->nr_keys; i++)
879 if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), level)) goto error;
882 else if (lf->id == NT_REG_NOHASH_BLOCK_ID)
884 nt_li * li = (nt_li*)lf;
885 if (subkeys != li->nr_keys) goto error1;
887 for (i=0; i<li->nr_keys; i++)
889 if (!_nt_parse_nk(hkey, base, (nt_nk*)(base+li->off_nk[i]+4), level)) goto error;
892 else if (lf->id == NT_REG_RI_BLOCK_ID) /* ri */
894 nt_ri * ri = (nt_ri*)lf;
895 int li_subkeys = 0;
897 /* count all subkeys */
898 for (i=0; i<ri->nr_li; i++)
900 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
901 if(li->id != NT_REG_NOHASH_BLOCK_ID) goto error2;
902 li_subkeys += li->nr_keys;
905 /* check number */
906 if (subkeys != li_subkeys) goto error1;
908 /* loop through the keys */
909 for (i=0; i<ri->nr_li; i++)
911 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
912 if (!_nt_parse_lf(hkey, base, li->nr_keys, (nt_lf*)li, level)) goto error;
915 else
917 goto error2;
919 return TRUE;
921 error2: ERR("unknown node id 0x%04x, please report!\n", lf->id);
922 return TRUE;
924 error1: ERR_(reg)("registry file corrupt! (inconsistent number of subkeys)\n");
925 return FALSE;
927 error: ERR_(reg)("error reading lf block\n");
928 return FALSE;
931 static int _nt_parse_nk(HKEY hkey, char * base, nt_nk * nk, int level)
933 char * name;
934 int i;
935 DWORD * vl;
936 HKEY subkey = hkey;
938 if(nk->SubBlockId != NT_REG_KEY_BLOCK_ID)
940 ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId);
941 goto error;
944 if((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) &&
945 (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID))
947 ERR_(reg)("registry file corrupt!\n");
948 goto error;
951 /* create the new key */
952 if(level <= 0)
954 name = _strdupnA( nk->name, nk->name_len+1);
955 if(RegCreateKeyA( hkey, name, &subkey )) { free(name); goto error; }
956 free(name);
959 /* loop through the subkeys */
960 if (nk->nr_subkeys)
962 nt_lf * lf = (nt_lf*)(base+nk->lf_off+4);
963 if (!_nt_parse_lf(subkey, base, nk->nr_subkeys, lf, level-1)) goto error1;
966 /* loop trough the value list */
967 vl = (DWORD *)(base+nk->valuelist_off+4);
968 for (i=0; i<nk->nr_values; i++)
970 nt_vk * vk = (nt_vk*)(base+vl[i]+4);
971 if (!_nt_parse_vk(subkey, base, vk)) goto error1;
974 RegCloseKey(subkey);
975 return TRUE;
977 error1: RegCloseKey(subkey);
978 error: return FALSE;
981 /* end nt loader */
983 /* windows 95 registry loader */
985 /* SECTION 1: main header
987 * once at offset 0
989 #define W95_REG_CREG_ID 0x47455243
991 typedef struct
993 DWORD id; /* "CREG" = W95_REG_CREG_ID */
994 DWORD version; /* ???? 0x00010000 */
995 DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
996 DWORD uk2; /* 0x0c */
997 WORD rgdb_num; /* 0x10 # of RGDB-blocks */
998 WORD uk3;
999 DWORD uk[3];
1000 /* rgkn */
1001 } _w95creg;
1003 /* SECTION 2: Directory information (tree structure)
1005 * once on offset 0x20
1007 * structure: [rgkn][dke]* (repeat till rgkn->size is reached)
1009 #define W95_REG_RGKN_ID 0x4e4b4752
1011 typedef struct
1013 DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
1014 DWORD size; /* Size of the RGKN-block */
1015 DWORD root_off; /* Rel. Offset of the root-record */
1016 DWORD uk[5];
1017 } _w95rgkn;
1019 /* Disk Key Entry Structure
1021 * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the
1022 * hive itself. It looks the same like other keys. Even the ID-number can
1023 * be any value.
1025 * The "hash"-value is a value representing the key's name. Windows will not
1026 * search for the name, but for a matching hash-value. if it finds one, it
1027 * will compare the actual string info, otherwise continue with the next key.
1028 * To calculate the hash initialize a D-Word with 0 and add all ASCII-values
1029 * of the string which are smaller than 0x80 (128) to this D-Word.
1031 * If you want to modify key names, also modify the hash-values, since they
1032 * cannot be found again (although they would be displayed in REGEDIT)
1033 * End of list-pointers are filled with 0xFFFFFFFF
1035 * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
1036 * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
1037 * structure) and reading another RGDB_section.
1039 * there is a one to one relationship between dke and dkh
1041 /* key struct, once per key */
1042 typedef struct
1044 DWORD x1; /* Free entry indicator(?) */
1045 DWORD hash; /* sum of bytes of keyname */
1046 DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
1047 DWORD prevlvl; /* offset of previous key */
1048 DWORD nextsub; /* offset of child key */
1049 DWORD next; /* offset of sibling key */
1050 WORD nrLS; /* id inside the rgdb block */
1051 WORD nrMS; /* number of the rgdb block */
1052 } _w95dke;
1054 /* SECTION 3: key information, values and data
1056 * structure:
1057 * section: [blocks]* (repeat creg->rgdb_num times)
1058 * blocks: [rgdb] [subblocks]* (repeat till block size reached )
1059 * subblocks: [dkh] [dkv]* (repeat dkh->values times )
1061 * An interesting relationship exists in RGDB_section. The value at offset
1062 * 10 equals the value at offset 4 minus the value at offset 8. I have no
1063 * idea at the moment what this means. (Kevin Cozens)
1066 /* block header, once per block */
1067 #define W95_REG_RGDB_ID 0x42444752
1069 typedef struct
1071 DWORD id; /* 0x00 'rgdb' = W95_REG_RGDB_ID */
1072 DWORD size; /* 0x04 */
1073 DWORD uk1; /* 0x08 */
1074 DWORD uk2; /* 0x0c */
1075 DWORD uk3; /* 0x10 */
1076 DWORD uk4; /* 0x14 */
1077 DWORD uk5; /* 0x18 */
1078 DWORD uk6; /* 0x1c */
1079 /* dkh */
1080 } _w95rgdb;
1082 /* Disk Key Header structure (RGDB part), once per key */
1083 typedef struct
1085 DWORD nextkeyoff; /* 0x00 offset to next dkh*/
1086 WORD nrLS; /* 0x04 id inside the rgdb block */
1087 WORD nrMS; /* 0x06 number of the rgdb block */
1088 DWORD bytesused; /* 0x08 */
1089 WORD keynamelen; /* 0x0c len of name */
1090 WORD values; /* 0x0e number of values */
1091 DWORD xx1; /* 0x10 */
1092 char name[1]; /* 0x14 */
1093 /* dkv */ /* 0x14 + keynamelen */
1094 } _w95dkh;
1096 /* Disk Key Value structure, once per value */
1097 typedef struct
1099 DWORD type; /* 0x00 */
1100 DWORD x1; /* 0x04 */
1101 WORD valnamelen; /* 0x08 length of name, 0 is default key */
1102 WORD valdatalen; /* 0x0A length of data */
1103 char name[1]; /* 0x0c */
1104 /* raw data */ /* 0x0c + valnamelen */
1105 } _w95dkv;
1107 /******************************************************************************
1108 * _w95_lookup_dkh [Internal]
1110 * seeks the dkh belonging to a dke
1112 static _w95dkh * _w95_lookup_dkh (_w95creg *creg, int nrLS, int nrMS)
1114 _w95rgdb * rgdb;
1115 _w95dkh * dkh;
1116 int i;
1118 rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off); /* get the beginning of the rgdb datastore */
1119 assert (creg->rgdb_num > nrMS); /* check: requested block < last_block) */
1121 /* find the right block */
1122 for(i=0; i<nrMS ;i++)
1124 assert(rgdb->id == W95_REG_RGDB_ID); /* check the magic */
1125 rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size); /* find next block */
1128 dkh = (_w95dkh*)(rgdb + 1); /* first sub block within the rgdb */
1132 if(nrLS==dkh->nrLS ) return dkh;
1133 dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */
1134 } while ((char *)dkh < ((char*)rgdb+rgdb->size));
1136 return NULL;
1139 /******************************************************************************
1140 * _w95_parse_dkv [Internal]
1142 static int _w95_parse_dkv (
1143 HKEY hkey,
1144 _w95dkh * dkh,
1145 int nrLS,
1146 int nrMS )
1148 _w95dkv * dkv;
1149 int i;
1150 DWORD ret;
1151 char * name;
1153 /* first value block */
1154 dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
1156 /* loop trought the values */
1157 for (i=0; i< dkh->values; i++)
1159 name = _strdupnA(dkv->name, dkv->valnamelen+1);
1160 ret = RegSetValueExA(hkey, name, 0, dkv->type, &(dkv->name[dkv->valnamelen]),dkv->valdatalen);
1161 if (ret) ERR("RegSetValueEx failed (0x%08lx)\n", ret);
1162 free (name);
1164 /* next value */
1165 dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
1167 return TRUE;
1170 /******************************************************************************
1171 * _w95_parse_dke [Internal]
1173 static int _w95_parse_dke(
1174 HKEY hkey,
1175 _w95creg * creg,
1176 _w95rgkn *rgkn,
1177 _w95dke * dke,
1178 int level )
1180 _w95dkh * dkh;
1181 HKEY hsubkey = hkey;
1182 char * name;
1183 int ret = FALSE;
1185 /* get start address of root key block */
1186 if (!dke) dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
1188 /* special root key */
1189 if (dke->nrLS == 0xffff || dke->nrMS==0xffff) /* eg. the root key has no name */
1191 /* parse the one subkey*/
1192 if (dke->nextsub != 0xffffffff)
1194 return _w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level);
1196 /* has no sibling keys */
1197 goto error;
1200 /* search subblock */
1201 if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS)))
1203 fprintf(stderr, "dke pointing to missing dkh !\n");
1204 goto error;
1207 if ( level <= 0 )
1209 /* walk sibling keys */
1210 if (dke->next != 0xffffffff )
1212 if (!_w95_parse_dke(hkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next), level)) goto error;
1215 /* create subkey and insert values */
1216 name = _strdupnA( dkh->name, dkh->keynamelen+1);
1217 if (RegCreateKeyA(hkey, name, &hsubkey)) { free(name); goto error; }
1218 free(name);
1219 if (!_w95_parse_dkv(hsubkey, dkh, dke->nrLS, dke->nrMS)) goto error1;
1222 /* next sub key */
1223 if (dke->nextsub != 0xffffffff)
1225 if (!_w95_parse_dke(hsubkey, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub), level-1)) goto error1;
1228 ret = TRUE;
1229 error1: if (hsubkey != hkey) RegCloseKey(hsubkey);
1230 error: return ret;
1232 /* end windows 95 loader */
1234 /******************************************************************************
1235 * NativeRegLoadKey [Internal]
1237 * Loads a native registry file (win95/nt)
1238 * hkey root key
1239 * fn filename
1240 * level number of levels to cut away (eg. ".Default" in user.dat)
1242 * this function intentionally uses unix file functions to make it possible
1243 * to move it to a seperate registry helper programm
1245 static int NativeRegLoadKey( HKEY hkey, char* fn, int level )
1247 int fd = 0;
1248 struct stat st;
1249 DOS_FULL_NAME full_name;
1250 int ret = FALSE;
1251 void * base;
1253 if (!DOSFS_GetFullName( fn, 0, &full_name )) return FALSE;
1255 /* map the registry into the memory */
1256 if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return FALSE;
1257 if ((fstat(fd, &st) == -1)) goto error;
1258 if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error;
1260 switch (*(LPDWORD)base)
1262 /* windows 95 'creg' */
1263 case W95_REG_CREG_ID:
1265 _w95creg * creg;
1266 _w95rgkn * rgkn;
1267 creg = base;
1268 TRACE_(reg)("Loading win95 registry '%s' '%s'\n",fn, full_name.long_name);
1270 /* load the header (rgkn) */
1271 rgkn = (_w95rgkn*)(creg + 1);
1272 if (rgkn->id != W95_REG_RGKN_ID)
1274 ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
1275 goto error1;
1278 ret = _w95_parse_dke(hkey, creg, rgkn, NULL, level);
1280 break;
1281 /* nt 'regf'*/
1282 case NT_REG_HEADER_BLOCK_ID:
1284 nt_regf * regf;
1285 nt_hbin * hbin;
1286 nt_hbin_sub * hbin_sub;
1287 nt_nk* nk;
1289 TRACE_(reg)("Loading nt registry '%s' '%s'\n",fn, full_name.long_name);
1291 /* start block */
1292 regf = base;
1294 /* hbin block */
1295 hbin = (nt_hbin *) ((char *) base + 0x1000);
1296 if (hbin->id != NT_REG_POOL_BLOCK_ID)
1298 ERR_(reg)( "%s hbin block invalid\n", fn);
1299 goto error1;
1302 /* hbin_sub block */
1303 hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
1304 if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k'))
1306 ERR_(reg)( "%s hbin_sub block invalid\n", fn);
1307 goto error1;
1310 /* nk block */
1311 nk = (nt_nk*)&(hbin_sub->data[0]);
1312 if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE)
1314 ERR_(reg)( "%s special nk block not found\n", fn);
1315 goto error1;
1318 ret = _nt_parse_nk (hkey, (char *) base + 0x1000, nk, level);
1320 break;
1321 default:
1323 ERR("unknown signature in registry file %s.\n",fn);
1324 goto error1;
1327 if(!ret) ERR("error loading registry file %s\n", fn);
1328 error1: munmap(base, st.st_size);
1329 error: close(fd);
1330 return ret;
1333 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1335 reghack - windows 3.11 registry data format demo program.
1337 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1338 a combined hash table and tree description, and finally a text table.
1340 The header is obvious from the struct header. The taboff1 and taboff2
1341 fields are always 0x20, and their usage is unknown.
1343 The 8-byte entry table has various entry types.
1345 tabent[0] is a root index. The second word has the index of the root of
1346 the directory.
1347 tabent[1..hashsize] is a hash table. The first word in the hash entry is
1348 the index of the key/value that has that hash. Data with the same
1349 hash value are on a circular list. The other three words in the
1350 hash entry are always zero.
1351 tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1352 entry: dirent and keyent/valent. They are identified by context.
1353 tabent[freeidx] is the first free entry. The first word in a free entry
1354 is the index of the next free entry. The last has 0 as a link.
1355 The other three words in the free list are probably irrelevant.
1357 Entries in text table are preceeded by a word at offset-2. This word
1358 has the value (2*index)+1, where index is the referring keyent/valent
1359 entry in the table. I have no suggestion for the 2* and the +1.
1360 Following the word, there are N bytes of data, as per the keyent/valent
1361 entry length. The offset of the keyent/valent entry is from the start
1362 of the text table to the first data byte.
1364 This information is not available from Microsoft. The data format is
1365 deduced from the reg.dat file by me. Mistakes may
1366 have been made. I claim no rights and give no guarantees for this program.
1368 Tor Sjøwall, tor@sn.no
1371 /* reg.dat header format */
1372 struct _w31_header {
1373 char cookie[8]; /* 'SHCC3.10' */
1374 unsigned long taboff1; /* offset of hash table (??) = 0x20 */
1375 unsigned long taboff2; /* offset of index table (??) = 0x20 */
1376 unsigned long tabcnt; /* number of entries in index table */
1377 unsigned long textoff; /* offset of text part */
1378 unsigned long textsize; /* byte size of text part */
1379 unsigned short hashsize; /* hash size */
1380 unsigned short freeidx; /* free index */
1383 /* generic format of table entries */
1384 struct _w31_tabent {
1385 unsigned short w0, w1, w2, w3;
1388 /* directory tabent: */
1389 struct _w31_dirent {
1390 unsigned short sibling_idx; /* table index of sibling dirent */
1391 unsigned short child_idx; /* table index of child dirent */
1392 unsigned short key_idx; /* table index of key keyent */
1393 unsigned short value_idx; /* table index of value valent */
1396 /* key tabent: */
1397 struct _w31_keyent {
1398 unsigned short hash_idx; /* hash chain index for string */
1399 unsigned short refcnt; /* reference count */
1400 unsigned short length; /* length of string */
1401 unsigned short string_off; /* offset of string in text table */
1404 /* value tabent: */
1405 struct _w31_valent {
1406 unsigned short hash_idx; /* hash chain index for string */
1407 unsigned short refcnt; /* reference count */
1408 unsigned short length; /* length of string */
1409 unsigned short string_off; /* offset of string in text table */
1412 /* recursive helper function to display a directory tree */
1413 void
1414 __w31_dumptree( unsigned short idx,
1415 unsigned char *txt,
1416 struct _w31_tabent *tab,
1417 struct _w31_header *head,
1418 HKEY hkey,
1419 time_t lastmodified,
1420 int level
1422 struct _w31_dirent *dir;
1423 struct _w31_keyent *key;
1424 struct _w31_valent *val;
1425 HKEY subkey = 0;
1426 static char tail[400];
1428 while (idx!=0) {
1429 dir=(struct _w31_dirent*)&tab[idx];
1431 if (dir->key_idx) {
1432 key = (struct _w31_keyent*)&tab[dir->key_idx];
1434 memcpy(tail,&txt[key->string_off],key->length);
1435 tail[key->length]='\0';
1436 /* all toplevel entries AND the entries in the
1437 * toplevel subdirectory belong to \SOFTWARE\Classes
1439 if (!level && !lstrcmpA(tail,".classes")) {
1440 __w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
1441 idx=dir->sibling_idx;
1442 continue;
1444 if (subkey) RegCloseKey( subkey );
1445 if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
1446 /* only add if leaf node or valued node */
1447 if (dir->value_idx!=0||dir->child_idx==0) {
1448 if (dir->value_idx) {
1449 val=(struct _w31_valent*)&tab[dir->value_idx];
1450 memcpy(tail,&txt[val->string_off],val->length);
1451 tail[val->length]='\0';
1452 RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
1455 } else {
1456 TRACE("strange: no directory key name, idx=%04x\n", idx);
1458 __w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
1459 idx=dir->sibling_idx;
1461 if (subkey) RegCloseKey( subkey );
1465 /******************************************************************************
1466 * _w31_loadreg [Internal]
1468 void _w31_loadreg(void) {
1469 HFILE hf;
1470 struct _w31_header head;
1471 struct _w31_tabent *tab;
1472 unsigned char *txt;
1473 int len;
1474 OFSTRUCT ofs;
1475 BY_HANDLE_FILE_INFORMATION hfinfo;
1476 time_t lastmodified;
1478 TRACE("(void)\n");
1480 hf = OpenFile("reg.dat",&ofs,OF_READ);
1481 if (hf==HFILE_ERROR)
1482 return;
1484 /* read & dump header */
1485 if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
1486 ERR("reg.dat is too short.\n");
1487 _lclose(hf);
1488 return;
1490 if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1491 ERR("reg.dat has bad signature.\n");
1492 _lclose(hf);
1493 return;
1496 len = head.tabcnt * sizeof(struct _w31_tabent);
1497 /* read and dump index table */
1498 tab = xmalloc(len);
1499 if (len!=_lread(hf,tab,len)) {
1500 ERR("couldn't read %d bytes.\n",len);
1501 free(tab);
1502 _lclose(hf);
1503 return;
1506 /* read text */
1507 txt = xmalloc(head.textsize);
1508 if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
1509 ERR("couldn't seek to textblock.\n");
1510 free(tab);
1511 free(txt);
1512 _lclose(hf);
1513 return;
1515 if (head.textsize!=_lread(hf,txt,head.textsize)) {
1516 ERR("textblock too short (%d instead of %ld).\n",len,head.textsize);
1517 free(tab);
1518 free(txt);
1519 _lclose(hf);
1520 return;
1523 if (!GetFileInformationByHandle(hf,&hfinfo)) {
1524 ERR("GetFileInformationByHandle failed?.\n");
1525 free(tab);
1526 free(txt);
1527 _lclose(hf);
1528 return;
1530 lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1531 __w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
1532 free(tab);
1533 free(txt);
1534 _lclose(hf);
1535 return;
1538 /**********************************************************************************
1539 * SetLoadLevel [Internal]
1541 * set level to 0 for loading system files
1542 * set level to 1 for loading user files
1544 static void SetLoadLevel(int level)
1546 struct set_registry_levels_request *req = get_req_buffer();
1548 req->current = level;
1549 req->saving = 0;
1550 req->version = 1;
1551 server_call( REQ_SET_REGISTRY_LEVELS );
1554 /**********************************************************************************
1555 * SHELL_LoadRegistry [Internal]
1557 #define REG_DONTLOAD -1
1558 #define REG_WIN31 0
1559 #define REG_WIN95 1
1560 #define REG_WINNT 2
1562 void SHELL_LoadRegistry( void )
1564 char *fn, *home;
1565 HKEY hkey;
1566 char windir[MAX_PATHNAME_LEN];
1567 char path[MAX_PATHNAME_LEN];
1568 int systemtype = REG_WIN31;
1570 TRACE("(void)\n");
1572 if (!CLIENT_IsBootThread()) return; /* already loaded */
1574 REGISTRY_Init();
1575 SetLoadLevel(0);
1577 GetWindowsDirectoryA( windir, MAX_PATHNAME_LEN );
1579 if (PROFILE_GetWineIniBool( "Registry", "LoadWindowsRegistryFiles", 1))
1581 /* test %windir%/system32/config/system --> winnt */
1582 strcpy(path, windir);
1583 strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
1584 if(GetFileAttributesA(path) != -1)
1586 systemtype = REG_WINNT;
1588 else
1590 /* test %windir%/system.dat --> win95 */
1591 strcpy(path, windir);
1592 strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1593 if(GetFileAttributesA(path) != -1)
1595 systemtype = REG_WIN95;
1599 if ((systemtype==REG_WINNT)
1600 && (! PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)))
1602 MESSAGE("When you are running with a native NT directory specify\n");
1603 MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n");
1604 MESSAGE("registry (LoadWindowsRegistryFiles=N)\n");
1605 systemtype = REG_DONTLOAD;
1608 else
1610 /* only wine registry */
1611 systemtype = REG_DONTLOAD;
1614 switch (systemtype)
1616 case REG_WIN31:
1617 _w31_loadreg();
1618 break;
1620 case REG_WIN95:
1621 /* Load windows 95 entries */
1622 NativeRegLoadKey(HKEY_LOCAL_MACHINE, "C:\\system.1st", 0);
1624 strcpy(path, windir);
1625 strncat(path, "\\system.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1626 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1628 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
1630 /* user specific user.dat */
1631 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1632 if (!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
1634 MESSAGE("can't load win95 user-registry %s\n", path);
1635 MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
1637 /* default user.dat */
1638 if (!RegCreateKeyA(HKEY_USERS, ".Default", &hkey))
1640 strcpy(path, windir);
1641 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1642 NativeRegLoadKey(hkey, path, 1);
1643 RegCloseKey(hkey);
1646 else
1648 /* global user.dat */
1649 strcpy(path, windir);
1650 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1651 NativeRegLoadKey(HKEY_CURRENT_USER, path, 1);
1653 break;
1655 case REG_WINNT:
1656 /* default user.dat */
1657 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN))
1659 strncat(path, "\\ntuser.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1660 if(!NativeRegLoadKey( HKEY_CURRENT_USER, path, 1 ))
1662 MESSAGE("can't load NT user-registry %s\n", path);
1663 MESSAGE("check wine.conf, section [Wine], value 'Profile'\n");
1667 /* default user.dat */
1668 if (!RegCreateKeyA(HKEY_USERS, ".Default", &hkey))
1670 strcpy(path, windir);
1671 strncat(path, "\\system32\\config\\default", MAX_PATHNAME_LEN - strlen(path) - 1);
1672 NativeRegLoadKey(hkey, path, 1);
1673 RegCloseKey(hkey);
1677 * FIXME
1678 * map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
1681 strcpy(path, windir);
1682 strncat(path, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(path) - 1);
1683 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1685 strcpy(path, windir);
1686 strncat(path, "\\system32\\config\\software", MAX_PATHNAME_LEN - strlen(path) - 1);
1687 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1689 strcpy(path, windir);
1690 strncat(path, "\\system32\\config\\sam", MAX_PATHNAME_LEN - strlen(path) - 1);
1691 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1693 strcpy(path, windir);
1694 strncat(path, "\\system32\\config\\security", MAX_PATHNAME_LEN - strlen(path) - 1);
1695 NativeRegLoadKey(HKEY_LOCAL_MACHINE, path, 0);
1697 /* this key is generated when the nt-core booted successfully */
1698 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey))
1699 RegCloseKey(hkey);
1700 break;
1701 } /* switch */
1703 if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
1706 * Load the global HKU hive directly from sysconfdir
1708 _wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT );
1711 * Load the global machine defaults directly form sysconfdir
1713 _wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
1716 SetLoadLevel(1);
1719 * Load the user saved registries
1721 if (!(home = getenv( "HOME" )))
1722 WARN("Failed to get homedirectory of UID %ld.\n",(long) getuid());
1723 else if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1))
1726 * Load user's personal versions of global HKU/.Default keys
1728 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX) +
1729 strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
1730 strcpy(fn, home);
1731 strcat(fn, WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1732 _wine_loadreg( HKEY_USERS, fn );
1733 free(fn);
1735 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) + strlen(SAVE_CURRENT_USER)+2);
1736 strcpy(fn, home);
1737 strcat(fn, WINE_PREFIX"/"SAVE_CURRENT_USER);
1738 _wine_loadreg( HKEY_CURRENT_USER, fn );
1739 free(fn);
1742 * Load HKLM, attempt to get the registry location from the config
1743 * file first, if exist, load and keep going.
1745 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_MACHINE)+2);
1746 strcpy(fn,home);
1747 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1748 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1749 free(fn);
1753 * Load HKCU, get the registry location from the config
1754 * file, if exist, load and keep going.
1756 if (PROFILE_GetWineIniBool ( "registry", "LoadAltRegistryFiles", 1))
1758 fn = xmalloc( MAX_PATHNAME_LEN );
1759 if ( PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
1760 fn, MAX_PATHNAME_LEN - 1))
1762 _wine_loadreg( HKEY_CURRENT_USER, fn );
1764 free (fn);
1766 * Load HKU, get the registry location from the config
1767 * file, if exist, load and keep going.
1769 fn = xmalloc ( MAX_PATHNAME_LEN );
1770 if ( PROFILE_GetWineIniString ( "registry", "AltUserFile", "",
1771 fn, MAX_PATHNAME_LEN - 1))
1773 _wine_loadreg( HKEY_USERS, fn );
1775 free (fn);
1777 * Load HKLM, get the registry location from the config
1778 * file, if exist, load and keep going.
1780 fn = xmalloc ( MAX_PATHNAME_LEN );
1781 if (PROFILE_GetWineIniString ( "registry", "AltLocalMachineFile", "",
1782 fn, MAX_PATHNAME_LEN - 1))
1784 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1786 free (fn);
1791 /* start the periodic saving timer */
1792 void SHELL_InitRegistrySaving(void)
1794 int save_timeout;
1796 if (!CLIENT_IsBootThread()) return;
1798 if ((save_timeout = PROFILE_GetWineIniInt( "registry", "PeriodicSave", 0 )))
1800 SERVICE_AddTimer( save_timeout * 1000000, periodic_save, 0 );
1805 /********************* API FUNCTIONS ***************************************/
1810 /******************************************************************************
1811 * RegFlushKey [KERNEL.227] [ADVAPI32.143]
1812 * Immediately writes key to registry.
1813 * Only returns after data has been written to disk.
1815 * FIXME: does it really wait until data is written ?
1817 * PARAMS
1818 * hkey [I] Handle of key to write
1820 * RETURNS
1821 * Success: ERROR_SUCCESS
1822 * Failure: Error code
1824 DWORD WINAPI RegFlushKey( HKEY hkey )
1826 FIXME( "(%x): stub\n", hkey );
1827 return ERROR_SUCCESS;
1830 /******************************************************************************
1831 * RegConnectRegistry32W [ADVAPI32.128]
1833 * PARAMS
1834 * lpMachineName [I] Address of name of remote computer
1835 * hHey [I] Predefined registry handle
1836 * phkResult [I] Address of buffer for remote registry handle
1838 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey,
1839 LPHKEY phkResult )
1841 TRACE("(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
1843 if (!lpMachineName || !*lpMachineName) {
1844 /* Use the local machine name */
1845 return RegOpenKey16( hKey, "", phkResult );
1848 FIXME("Cannot connect to %s\n",debugstr_w(lpMachineName));
1849 return ERROR_BAD_NETPATH;
1853 /******************************************************************************
1854 * RegConnectRegistry32A [ADVAPI32.127]
1856 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, LPHKEY reskey )
1858 DWORD ret;
1859 LPWSTR machineW = strdupA2W(machine);
1860 ret = RegConnectRegistryW( machineW, hkey, reskey );
1861 free(machineW);
1862 return ret;
1866 /******************************************************************************
1867 * RegGetKeySecurity [ADVAPI32.144]
1868 * Retrieves a copy of security descriptor protecting the registry key
1870 * PARAMS
1871 * hkey [I] Open handle of key to set
1872 * SecurityInformation [I] Descriptor contents
1873 * pSecurityDescriptor [O] Address of descriptor for key
1874 * lpcbSecurityDescriptor [I/O] Address of size of buffer and description
1876 * RETURNS
1877 * Success: ERROR_SUCCESS
1878 * Failure: Error code
1880 LONG WINAPI RegGetKeySecurity( HKEY hkey,
1881 SECURITY_INFORMATION SecurityInformation,
1882 PSECURITY_DESCRIPTOR pSecurityDescriptor,
1883 LPDWORD lpcbSecurityDescriptor )
1885 TRACE("(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
1886 lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1888 /* FIXME: Check for valid SecurityInformation values */
1890 if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
1891 return ERROR_INSUFFICIENT_BUFFER;
1893 FIXME("(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
1894 pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1896 return ERROR_SUCCESS;
1900 /******************************************************************************
1901 * RegNotifyChangeKeyValue [ADVAPI32.???]
1903 * PARAMS
1904 * hkey [I] Handle of key to watch
1905 * fWatchSubTree [I] Flag for subkey notification
1906 * fdwNotifyFilter [I] Changes to be reported
1907 * hEvent [I] Handle of signaled event
1908 * fAsync [I] Flag for asynchronous reporting
1910 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree,
1911 DWORD fdwNotifyFilter, HANDLE hEvent,
1912 BOOL fAsync )
1914 FIXME("(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter,
1915 hEvent,fAsync);
1916 return ERROR_SUCCESS;
1920 /******************************************************************************
1921 * RegUnLoadKey32W [ADVAPI32.173]
1923 * PARAMS
1924 * hkey [I] Handle of open key
1925 * lpSubKey [I] Address of name of subkey to unload
1927 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
1929 FIXME("(%x,%s): stub\n",hkey, debugstr_w(lpSubKey));
1930 return ERROR_SUCCESS;
1934 /******************************************************************************
1935 * RegUnLoadKey32A [ADVAPI32.172]
1937 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1939 LONG ret;
1940 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1941 ret = RegUnLoadKeyW( hkey, lpSubKeyW );
1942 if(lpSubKeyW) free(lpSubKeyW);
1943 return ret;
1947 /******************************************************************************
1948 * RegSetKeySecurity [ADVAPI32.167]
1950 * PARAMS
1951 * hkey [I] Open handle of key to set
1952 * SecurityInfo [I] Descriptor contents
1953 * pSecurityDesc [I] Address of descriptor for key
1955 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
1956 PSECURITY_DESCRIPTOR pSecurityDesc )
1958 TRACE("(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
1960 /* It seems to perform this check before the hkey check */
1961 if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
1962 (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
1963 (SecurityInfo & DACL_SECURITY_INFORMATION) ||
1964 (SecurityInfo & SACL_SECURITY_INFORMATION)) {
1965 /* Param OK */
1966 } else
1967 return ERROR_INVALID_PARAMETER;
1969 if (!pSecurityDesc)
1970 return ERROR_INVALID_PARAMETER;
1972 FIXME(":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
1974 return ERROR_SUCCESS;
1978 /******************************************************************************
1979 * RegRestoreKey32W [ADVAPI32.164]
1981 * PARAMS
1982 * hkey [I] Handle of key where restore begins
1983 * lpFile [I] Address of filename containing saved tree
1984 * dwFlags [I] Optional flags
1986 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
1988 TRACE("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
1990 /* It seems to do this check before the hkey check */
1991 if (!lpFile || !*lpFile)
1992 return ERROR_INVALID_PARAMETER;
1994 FIXME("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
1996 /* Check for file existence */
1998 return ERROR_SUCCESS;
2002 /******************************************************************************
2003 * RegRestoreKey32A [ADVAPI32.163]
2005 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
2007 LONG ret;
2008 LPWSTR lpFileW = strdupA2W(lpFile);
2009 ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
2010 if(lpFileW) free(lpFileW);
2011 return ret;
2015 /******************************************************************************
2016 * RegReplaceKey32W [ADVAPI32.162]
2018 * PARAMS
2019 * hkey [I] Handle of open key
2020 * lpSubKey [I] Address of name of subkey
2021 * lpNewFile [I] Address of filename for file with new data
2022 * lpOldFile [I] Address of filename for backup file
2024 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
2025 LPCWSTR lpOldFile )
2027 FIXME("(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey),
2028 debugstr_w(lpNewFile),debugstr_w(lpOldFile));
2029 return ERROR_SUCCESS;
2033 /******************************************************************************
2034 * RegReplaceKey32A [ADVAPI32.161]
2036 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
2037 LPCSTR lpOldFile )
2039 LONG ret;
2040 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
2041 LPWSTR lpNewFileW = strdupA2W(lpNewFile);
2042 LPWSTR lpOldFileW = strdupA2W(lpOldFile);
2043 ret = RegReplaceKeyW( hkey, lpSubKeyW, lpNewFileW, lpOldFileW );
2044 free(lpOldFileW);
2045 free(lpNewFileW);
2046 free(lpSubKeyW);
2047 return ret;
2055 /* 16-bit functions */
2057 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
2058 * some programs. Do not remove those cases. -MM
2060 static inline void fix_win16_hkey( HKEY *hkey )
2062 if (*hkey == 0 || *hkey == 1) *hkey = HKEY_CLASSES_ROOT;
2065 /******************************************************************************
2066 * RegEnumKey16 [KERNEL.216] [SHELL.7]
2068 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
2070 fix_win16_hkey( &hkey );
2071 return RegEnumKeyA( hkey, index, name, name_len );
2074 /******************************************************************************
2075 * RegOpenKey16 [KERNEL.217] [SHELL.1]
2077 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
2079 fix_win16_hkey( &hkey );
2080 return RegOpenKeyA( hkey, name, retkey );
2083 /******************************************************************************
2084 * RegCreateKey16 [KERNEL.218] [SHELL.2]
2086 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
2088 fix_win16_hkey( &hkey );
2089 return RegCreateKeyA( hkey, name, retkey );
2092 /******************************************************************************
2093 * RegDeleteKey16 [KERNEL.219] [SHELL.4]
2095 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
2097 fix_win16_hkey( &hkey );
2098 return RegDeleteKeyA( hkey, name );
2101 /******************************************************************************
2102 * RegCloseKey16 [KERNEL.220] [SHELL.3]
2104 DWORD WINAPI RegCloseKey16( HKEY hkey )
2106 fix_win16_hkey( &hkey );
2107 return RegCloseKey( hkey );
2110 /******************************************************************************
2111 * RegSetValue16 [KERNEL.221] [SHELL.5]
2113 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
2115 fix_win16_hkey( &hkey );
2116 return RegSetValueA( hkey, name, type, data, count );
2119 /******************************************************************************
2120 * RegDeleteValue16 [KERNEL.222]
2122 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
2124 fix_win16_hkey( &hkey );
2125 return RegDeleteValueA( hkey, name );
2128 /******************************************************************************
2129 * RegEnumValue16 [KERNEL.223]
2131 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
2132 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
2134 fix_win16_hkey( &hkey );
2135 return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
2138 /******************************************************************************
2139 * RegQueryValue16 [KERNEL.224] [SHELL.6]
2141 * NOTES
2142 * Is this HACK still applicable?
2144 * HACK
2145 * The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
2146 * mask out the high 16 bit. This (not so much incidently) hopefully fixes
2147 * Aldus FH4)
2149 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
2151 fix_win16_hkey( &hkey );
2152 if (count) *count &= 0xffff;
2153 return RegQueryValueA( hkey, name, data, count );
2156 /******************************************************************************
2157 * RegQueryValueEx16 [KERNEL.225]
2159 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
2160 LPBYTE data, LPDWORD count )
2162 fix_win16_hkey( &hkey );
2163 return RegQueryValueExA( hkey, name, reserved, type, data, count );
2166 /******************************************************************************
2167 * RegSetValueEx16 [KERNEL.226]
2169 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
2170 CONST BYTE *data, DWORD count )
2172 fix_win16_hkey( &hkey );
2173 return RegSetValueExA( hkey, name, reserved, type, data, count );