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.
13 * When changing this file, please re-run the regtest program to ensure
14 * the conditions are handled properly.
19 * Time for RegEnumKey*, RegQueryInfoKey*
29 #include <sys/types.h>
35 #include "debugtools.h"
39 #ifdef HAVE_SYS_MMAN_H
40 # include <sys/mman.h>
45 DEFAULT_DEBUG_CHANNEL(reg);
47 /* FIXME: following defines should be configured global */
48 #define SAVE_GLOBAL_REGBRANCH_USER_DEFAULT ETCDIR"/wine.userreg"
49 #define SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE ETCDIR"/wine.systemreg"
51 /* relative in ~user/.wine/ : */
52 #define SAVE_LOCAL_REGBRANCH_CURRENT_USER "user.reg"
53 #define SAVE_LOCAL_REGBRANCH_USER_DEFAULT "userdef.reg"
54 #define SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE "system.reg"
56 /* _xmalloc [Internal] */
57 static void *_xmalloc( size_t size )
61 res = malloc (size ? size : 1);
63 WARN("Virtual memory exhausted.\n");
69 /* _strdupnA [Internal] */
70 static LPSTR _strdupnA(LPCSTR str,size_t len)
74 if (!str) return NULL;
75 ret = _xmalloc( len + 1 );
76 memcpy( ret, str, len );
81 /* convert ansi string to unicode [Internal] */
82 static LPWSTR _strdupnAtoW(LPCSTR strA,size_t lenA)
87 if (!strA) return NULL;
88 lenW = MultiByteToWideChar(CP_ACP,0,strA,lenA,NULL,0);
89 ret = _xmalloc(lenW*sizeof(WCHAR)+sizeof(WCHAR));
90 MultiByteToWideChar(CP_ACP,0,strA,lenA,ret,lenW);
95 /* dump a Unicode string with proper escaping [Internal] */
96 /* FIXME: this code duplicates server/unicode.c */
97 static int _dump_strW(const WCHAR *str,size_t len,FILE *f,char escape[2])
99 static const char escapes[32] = ".......abtnvfr.............e....";
104 for (; len; str++, len--)
106 if (pos > buffer + sizeof(buffer) - 8)
108 fwrite( buffer, pos - buffer, 1, f );
109 count += pos - buffer;
112 if (*str > 127) /* hex escape */
114 if (len > 1 && str[1] < 128 && isxdigit((char)str[1]))
115 pos += sprintf( pos, "\\x%04x", *str );
117 pos += sprintf( pos, "\\x%x", *str );
120 if (*str < 32) /* octal or C escape */
122 if (!*str && len == 1) continue; /* do not output terminating NULL */
123 if (escapes[*str] != '.')
124 pos += sprintf( pos, "\\%c", escapes[*str] );
125 else if (len > 1 && str[1] >= '0' && str[1] <= '7')
126 pos += sprintf( pos, "\\%03o", *str );
128 pos += sprintf( pos, "\\%o", *str );
131 if (*str == '\\' || *str == escape[0] || *str == escape[1]) *pos++ = '\\';
134 fwrite( buffer, pos - buffer, 1, f );
135 count += pos - buffer;
139 /* convert ansi string to unicode and dump with proper escaping [Internal] */
140 static int _dump_strAtoW(LPCSTR strA,size_t len,FILE *f,char escape[2])
145 if (strA == NULL) return 0;
146 strW = _strdupnAtoW(strA,len);
147 ret = _dump_strW(strW,len,f,escape);
153 /* FIXME: this code duplicates server/registry.c */
155 WCHAR *nameW; /* value name */
156 int type; /* value type */
157 size_t len; /* value data length in bytes */
158 void *data; /* pointer to value data */
161 /* dump a value to a text file */
162 /* FIXME: this code duplicates server/registry.c */
163 static void _dump_value(struct key_value *value,FILE *f)
167 if (value->nameW[0]) {
169 count = 1 + _dump_strW(value->nameW,strlenW(value->nameW),f,"\"\"");
170 count += fprintf( f, "\"=" );
172 else count = fprintf( f, "@=" );
174 switch(value->type) {
178 if (value->type != REG_SZ) fprintf( f, "str(%d):", value->type );
180 if (value->data) _dump_strW(value->data,value->len/sizeof(WCHAR),f,"\"\"");
184 if (value->len == sizeof(DWORD)) {
186 memcpy( &dw, value->data, sizeof(DWORD) );
187 fprintf( f, "dword:%08lx", dw );
190 /* else fall through */
192 if (value->type == REG_BINARY) count += fprintf( f, "hex:" );
193 else count += fprintf( f, "hex(%x):", value->type );
194 for (i = 0; i < value->len; i++) {
195 count += fprintf( f, "%02x", *((unsigned char *)value->data + i) );
196 if (i < value->len-1) {
199 fprintf( f, "\\\n " );
209 /******************************************************************/
210 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
212 reghack - windows 3.11 registry data format demo program.
214 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
215 a combined hash table and tree description, and finally a text table.
217 The header is obvious from the struct header. The taboff1 and taboff2
218 fields are always 0x20, and their usage is unknown.
220 The 8-byte entry table has various entry types.
222 tabent[0] is a root index. The second word has the index of the root of
224 tabent[1..hashsize] is a hash table. The first word in the hash entry is
225 the index of the key/value that has that hash. Data with the same
226 hash value are on a circular list. The other three words in the
227 hash entry are always zero.
228 tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
229 entry: dirent and keyent/valent. They are identified by context.
230 tabent[freeidx] is the first free entry. The first word in a free entry
231 is the index of the next free entry. The last has 0 as a link.
232 The other three words in the free list are probably irrelevant.
234 Entries in text table are preceded by a word at offset-2. This word
235 has the value (2*index)+1, where index is the referring keyent/valent
236 entry in the table. I have no suggestion for the 2* and the +1.
237 Following the word, there are N bytes of data, as per the keyent/valent
238 entry length. The offset of the keyent/valent entry is from the start
239 of the text table to the first data byte.
241 This information is not available from Microsoft. The data format is
242 deduced from the reg.dat file by me. Mistakes may
243 have been made. I claim no rights and give no guarantees for this program.
245 Tor Sjøwall, tor@sn.no
248 /* reg.dat header format */
250 char cookie[8]; /* 'SHCC3.10' */
251 unsigned long taboff1; /* offset of hash table (??) = 0x20 */
252 unsigned long taboff2; /* offset of index table (??) = 0x20 */
253 unsigned long tabcnt; /* number of entries in index table */
254 unsigned long textoff; /* offset of text part */
255 unsigned long textsize; /* byte size of text part */
256 unsigned short hashsize; /* hash size */
257 unsigned short freeidx; /* free index */
260 /* generic format of table entries */
262 unsigned short w0, w1, w2, w3;
265 /* directory tabent: */
267 unsigned short sibling_idx; /* table index of sibling dirent */
268 unsigned short child_idx; /* table index of child dirent */
269 unsigned short key_idx; /* table index of key keyent */
270 unsigned short value_idx; /* table index of value valent */
275 unsigned short hash_idx; /* hash chain index for string */
276 unsigned short refcnt; /* reference count */
277 unsigned short length; /* length of string */
278 unsigned short string_off; /* offset of string in text table */
283 unsigned short hash_idx; /* hash chain index for string */
284 unsigned short refcnt; /* reference count */
285 unsigned short length; /* length of string */
286 unsigned short string_off; /* offset of string in text table */
289 /* recursive helper function to display a directory tree [Internal] */
290 void _w31_dumptree(unsigned short idx,unsigned char *txt,struct _w31_tabent *tab,struct _w31_header *head,HKEY hkey,time_t lastmodified, int level)
292 struct _w31_dirent *dir;
293 struct _w31_keyent *key;
294 struct _w31_valent *val;
296 static char tail[400];
299 dir=(struct _w31_dirent*)&tab[idx];
302 key = (struct _w31_keyent*)&tab[dir->key_idx];
304 memcpy(tail,&txt[key->string_off],key->length);
305 tail[key->length]='\0';
306 /* all toplevel entries AND the entries in the
307 * toplevel subdirectory belong to \SOFTWARE\Classes
309 if (!level && !strcmp(tail,".classes")) {
310 _w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
311 idx=dir->sibling_idx;
314 if (subkey) RegCloseKey( subkey );
315 if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
316 /* only add if leaf node or valued node */
317 if (dir->value_idx!=0||dir->child_idx==0) {
318 if (dir->value_idx) {
319 val=(struct _w31_valent*)&tab[dir->value_idx];
320 memcpy(tail,&txt[val->string_off],val->length);
321 tail[val->length]='\0';
322 RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
325 } else TRACE("strange: no directory key name, idx=%04x\n", idx);
326 _w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
327 idx=dir->sibling_idx;
329 if (subkey) RegCloseKey( subkey );
333 /******************************************************************************
334 * _w31_loadreg [Internal]
336 void _w31_loadreg(void)
339 struct _w31_header head;
340 struct _w31_tabent *tab;
344 BY_HANDLE_FILE_INFORMATION hfinfo;
349 hf = OpenFile("reg.dat",&ofs,OF_READ);
350 if (hf==HFILE_ERROR) return;
352 /* read & dump header */
353 if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
354 ERR("reg.dat is too short.\n");
358 if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
359 ERR("reg.dat has bad signature.\n");
364 len = head.tabcnt * sizeof(struct _w31_tabent);
365 /* read and dump index table */
367 if (len!=_lread(hf,tab,len)) {
368 ERR("couldn't read %d bytes.\n",len);
375 txt = _xmalloc(head.textsize);
376 if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
377 ERR("couldn't seek to textblock.\n");
383 if (head.textsize!=_lread(hf,txt,head.textsize)) {
384 ERR("textblock too short (%d instead of %ld).\n",len,head.textsize);
391 if (!GetFileInformationByHandle(hf,&hfinfo)) {
392 ERR("GetFileInformationByHandle failed?.\n");
398 lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
399 _w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
406 /***********************************************************************************/
407 /* windows 95 registry loader */
408 /***********************************************************************************/
410 /* SECTION 1: main header
414 #define W95_REG_CREG_ID 0x47455243
417 DWORD id; /* "CREG" = W95_REG_CREG_ID */
418 DWORD version; /* ???? 0x00010000 */
419 DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
420 DWORD uk2; /* 0x0c */
421 WORD rgdb_num; /* 0x10 # of RGDB-blocks */
427 /* SECTION 2: Directory information (tree structure)
429 * once on offset 0x20
431 * structure: [rgkn][dke]* (repeat till last_dke is reached)
433 #define W95_REG_RGKN_ID 0x4e4b4752
436 DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
437 DWORD size; /* Size of the RGKN-block */
438 DWORD root_off; /* Rel. Offset of the root-record */
439 DWORD last_dke; /* Offset to last DKE ? */
443 /* Disk Key Entry Structure
445 * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the
446 * hive itself. It looks the same like other keys. Even the ID-number can
449 * The "hash"-value is a value representing the key's name. Windows will not
450 * search for the name, but for a matching hash-value. if it finds one, it
451 * will compare the actual string info, otherwise continue with the next key.
452 * To calculate the hash initialize a D-Word with 0 and add all ASCII-values
453 * of the string which are smaller than 0x80 (128) to this D-Word.
455 * If you want to modify key names, also modify the hash-values, since they
456 * cannot be found again (although they would be displayed in REGEDIT)
457 * End of list-pointers are filled with 0xFFFFFFFF
459 * Disk keys are layed out flat ... But, sometimes, nrLS and nrMS are both
460 * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
461 * structure) and reading another RGDB_section.
463 * The last DKE (see field last_dke in _w95_rgkn) has only 3 DWORDs with
464 * 0x80000000 (EOL indicator ?) as x1, the hash value and 0xFFFFFFFF as x3.
465 * The remaining space between last_dke and the offset calculated from
466 * rgkn->size seems to be free for use for more dke:s.
467 * So it seems if more dke:s are added, they are added to that space and
468 * last_dke is grown, and in case that "free" space is out, the space
469 * gets grown and rgkn->size gets adjusted.
471 * there is a one to one relationship between dke and dkh
473 /* key struct, once per key */
475 DWORD x1; /* Free entry indicator(?) */
476 DWORD hash; /* sum of bytes of keyname */
477 DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
478 DWORD prevlvl; /* offset of previous key */
479 DWORD nextsub; /* offset of child key */
480 DWORD next; /* offset of sibling key */
481 WORD nrLS; /* id inside the rgdb block */
482 WORD nrMS; /* number of the rgdb block */
485 /* SECTION 3: key information, values and data
488 * section: [blocks]* (repeat creg->rgdb_num times)
489 * blocks: [rgdb] [subblocks]* (repeat till block size reached )
490 * subblocks: [dkh] [dkv]* (repeat dkh->values times )
492 * An interesting relationship exists in RGDB_section. The DWORD value
493 * at offset 0x10 equals the one at offset 0x04 minus the one at offset 0x08.
494 * I have no idea at the moment what this means. (Kevin Cozens)
497 /* block header, once per block */
498 #define W95_REG_RGDB_ID 0x42444752
501 DWORD id; /* 0x00 'RGDB' = W95_REG_RGDB_ID */
502 DWORD size; /* 0x04 */
503 DWORD uk1; /* 0x08 */
504 DWORD uk2; /* 0x0c */
505 DWORD uk3; /* 0x10 */
506 DWORD uk4; /* 0x14 */
507 DWORD uk5; /* 0x18 */
508 DWORD uk6; /* 0x1c */
512 /* Disk Key Header structure (RGDB part), once per key */
514 DWORD nextkeyoff; /* 0x00 offset to next dkh */
515 WORD nrLS; /* 0x04 id inside the rgdb block */
516 WORD nrMS; /* 0x06 number of the rgdb block */
517 DWORD bytesused; /* 0x08 */
518 WORD keynamelen; /* 0x0c len of name */
519 WORD values; /* 0x0e number of values */
520 DWORD xx1; /* 0x10 */
521 char name[1]; /* 0x14 */
522 /* dkv */ /* 0x14 + keynamelen */
525 /* Disk Key Value structure, once per value */
527 DWORD type; /* 0x00 */
529 WORD valnamelen; /* 0x08 length of name, 0 is default key */
530 WORD valdatalen; /* 0x0A length of data */
531 char name[1]; /* 0x0c */
532 /* raw data */ /* 0x0c + valnamelen */
535 /******************************************************************************
536 * _w95_lookup_dkh [Internal]
538 * seeks the dkh belonging to a dke
540 static _w95dkh *_w95_lookup_dkh(_w95creg *creg,int nrLS,int nrMS)
546 /* get the beginning of the rgdb datastore */
547 rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off);
549 /* check: requested block < last_block) */
550 if (creg->rgdb_num <= nrMS) {
551 ERR("registry file corrupt! requested block no. beyond end.\n");
555 /* find the right block */
556 for(i=0; i<nrMS ;i++) {
557 if(rgdb->id != W95_REG_RGDB_ID) { /* check the magic */
558 ERR("registry file corrupt! bad magic 0x%08lx\n", rgdb->id);
561 rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size); /* find next block */
564 dkh = (_w95dkh*)(rgdb + 1); /* first sub block within the rgdb */
567 if(nrLS==dkh->nrLS ) return dkh;
568 dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */
569 } while ((char *)dkh < ((char*)rgdb+rgdb->size));
575 /******************************************************************************
576 * _w95_dump_dkv [Internal]
578 static int _w95_dump_dkv(_w95dkh *dkh,int nrLS,int nrMS,FILE *f)
583 /* first value block */
584 dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
586 /* loop trought the values */
587 for (i=0; i< dkh->values; i++) {
588 struct key_value value;
591 value.nameW = _strdupnAtoW(dkv->name,dkv->valnamelen);
592 value.type = dkv->type;
593 value.len = dkv->valdatalen;
595 value.data = &(dkv->name[dkv->valnamelen]);
597 if ( (value.type==REG_SZ) || (value.type==REG_EXPAND_SZ) || (value.type==REG_MULTI_SZ) ) {
598 pdata = _strdupnAtoW(value.data,value.len);
601 if (pdata != NULL) value.data = pdata;
603 _dump_value(&value,f);
605 if (pdata != NULL) free(pdata);
608 dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
613 /******************************************************************************
614 * _w95_dump_dke [Internal]
616 static int _w95_dump_dke(LPSTR key_name,_w95creg *creg,_w95rgkn *rgkn,_w95dke *dke,FILE *f,int level)
619 LPSTR new_key_name = NULL;
621 /* special root key */
622 if (dke->nrLS == 0xffff || dke->nrMS==0xffff) /* eg. the root key has no name */
624 /* parse the one subkey */
625 if (dke->nextsub != 0xffffffff) return _w95_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub),f,level);
626 /* has no sibling keys */
630 /* search subblock */
631 if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS))) {
632 ERR("dke pointing to missing dkh !\n");
637 /* create new subkey name */
638 new_key_name = _strdupnA(key_name,strlen(key_name)+dkh->keynamelen+1);
639 if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
640 strncat(new_key_name,dkh->name,dkh->keynamelen);
642 /* walk sibling keys */
643 if (dke->next != 0xffffffff ) {
644 if (!_w95_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next),f,level)) {
650 /* write the key path (something like [Software\\Microsoft\\..]) only if:
651 1) key has some values
652 2) key has no values and no subkeys
654 if (dkh->values > 0) {
655 /* there are some values */
657 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
659 if (!_w95_dump_dkv(dkh, dke->nrLS, dke->nrMS,f)) {
664 if ((dke->nextsub == 0xffffffff) && (dkh->values == 0)) {
665 /* no subkeys and no values */
667 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
670 } else new_key_name = _strdupnA(key_name,strlen(key_name));
673 if (dke->nextsub != 0xffffffff) {
674 if (!_w95_dump_dke(new_key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub),f,level-1)) {
683 /* end windows 95 loader */
685 /***********************************************************************************/
686 /* windows NT registry loader */
687 /***********************************************************************************/
689 /* NT REGISTRY LOADER */
691 #ifdef HAVE_SYS_MMAN_H
692 # include <sys/mman.h>
696 #define MAP_FAILED ((LPVOID)-1)
699 #define NT_REG_BLOCK_SIZE 0x1000
701 #define NT_REG_HEADER_BLOCK_ID 0x66676572 /* regf */
702 #define NT_REG_POOL_BLOCK_ID 0x6E696268 /* hbin */
703 #define NT_REG_KEY_BLOCK_ID 0x6b6e /* nk */
704 #define NT_REG_VALUE_BLOCK_ID 0x6b76 /* vk */
706 /* subblocks of nk */
707 #define NT_REG_HASH_BLOCK_ID 0x666c /* lf */
708 #define NT_REG_NOHASH_BLOCK_ID 0x696c /* li */
709 #define NT_REG_RI_BLOCK_ID 0x6972 /* ri */
711 #define NT_REG_KEY_BLOCK_TYPE 0x20
712 #define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c
715 DWORD id; /* 0x66676572 'regf'*/
716 DWORD uk1; /* 0x04 */
717 DWORD uk2; /* 0x08 */
718 FILETIME DateModified; /* 0x0c */
719 DWORD uk3; /* 0x14 */
720 DWORD uk4; /* 0x18 */
721 DWORD uk5; /* 0x1c */
722 DWORD uk6; /* 0x20 */
723 DWORD RootKeyBlock; /* 0x24 */
724 DWORD BlockSize; /* 0x28 */
726 DWORD Checksum; /* at offset 0x1FC */
735 DWORD id; /* 0x6E696268 'hbin' */
739 DWORD uk2; /* 0x10 */
740 DWORD uk3; /* 0x14 */
741 DWORD uk4; /* 0x18 */
742 DWORD size; /* 0x1C */
743 nt_hbin_sub hbin_sub; /* 0x20 */
747 * the value_list consists of offsets to the values (vk)
750 WORD SubBlockId; /* 0x00 0x6B6E */
751 WORD Type; /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
752 FILETIME writetime; /* 0x04 */
753 DWORD uk1; /* 0x0C */
754 DWORD parent_off; /* 0x10 Offset of Owner/Parent key */
755 DWORD nr_subkeys; /* 0x14 number of sub-Keys */
756 DWORD uk8; /* 0x18 */
757 DWORD lf_off; /* 0x1C Offset of the sub-key lf-Records */
758 DWORD uk2; /* 0x20 */
759 DWORD nr_values; /* 0x24 number of values */
760 DWORD valuelist_off; /* 0x28 Offset of the Value-List */
761 DWORD off_sk; /* 0x2c Offset of the sk-Record */
762 DWORD off_class; /* 0x30 Offset of the Class-Name */
763 DWORD uk3; /* 0x34 */
764 DWORD uk4; /* 0x38 */
765 DWORD uk5; /* 0x3c */
766 DWORD uk6; /* 0x40 */
767 DWORD uk7; /* 0x44 */
768 WORD name_len; /* 0x48 name-length */
769 WORD class_len; /* 0x4a class-name length */
770 char name[1]; /* 0x4c key-name */
774 DWORD off_nk; /* 0x00 */
775 DWORD name; /* 0x04 */
779 WORD id; /* 0x00 0x666c */
780 WORD nr_keys; /* 0x06 */
781 hash_rec hash_rec[1];
785 list of subkeys without hash
792 WORD id; /* 0x00 0x696c */
798 this is a intermediate node
809 WORD id; /* 0x00 0x6972 */
810 WORD nr_li; /* 0x02 number off offsets */
811 DWORD off_li[1]; /* 0x04 points to li */
815 WORD id; /* 0x00 'vk' */
829 * 0 value is a default value
830 * 1 the value has a name
833 * len of the whole data block
835 * bytes including the terminating \0 = 2*(number_of_chars+1)
836 * - reg_dword, reg_binary:
837 * if highest bit of data_len is set data_off contains the value
839 static int _nt_dump_vk(LPSTR key_name, char *base, nt_vk *vk,FILE *f)
841 BYTE *pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
842 struct key_value value;
844 if (vk->id != NT_REG_VALUE_BLOCK_ID) {
845 ERR("unknown block found (0x%04x), please report!\n", vk->id);
849 value.nameW = _strdupnAtoW(vk->name,vk->nam_len);
850 value.type = vk->type;
851 value.len = (vk->data_len & 0x7fffffff);
852 value.data = (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata;
854 _dump_value(&value,f);
860 /* it's called from _nt_dump_lf() */
861 static int _nt_dump_nk(LPSTR key_name,char *base,nt_nk *nk,FILE *f,int level);
866 * this structure contains the hash of a keyname and points to all
869 * exception: if the id is 'il' there are no hash values and every
872 static int _nt_dump_lf(LPSTR key_name, char *base, int subkeys, nt_lf *lf, FILE *f, int level)
876 if (lf->id == NT_REG_HASH_BLOCK_ID) {
877 if (subkeys != lf->nr_keys) goto error1;
879 for (i=0; i<lf->nr_keys; i++)
880 if (!_nt_dump_nk(key_name, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), f, level)) goto error;
881 } else if (lf->id == NT_REG_NOHASH_BLOCK_ID) {
882 nt_li * li = (nt_li*)lf;
883 if (subkeys != li->nr_keys) goto error1;
885 for (i=0; i<li->nr_keys; i++)
886 if (!_nt_dump_nk(key_name, base, (nt_nk*)(base+li->off_nk[i]+4), f, level)) goto error;
887 } else if (lf->id == NT_REG_RI_BLOCK_ID) { /* ri */
888 nt_ri * ri = (nt_ri*)lf;
891 /* count all subkeys */
892 for (i=0; i<ri->nr_li; i++) {
893 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
894 if(li->id != NT_REG_NOHASH_BLOCK_ID) goto error2;
895 li_subkeys += li->nr_keys;
899 if (subkeys != li_subkeys) goto error1;
901 /* loop through the keys */
902 for (i=0; i<ri->nr_li; i++) {
903 nt_li *li = (nt_li*)(base+ri->off_li[i]+4);
904 if (!_nt_dump_lf(key_name, base, li->nr_keys, (nt_lf*)li, f, level)) goto error;
911 ERR("unknown node id 0x%04x, please report!\n", lf->id);
915 ERR("registry file corrupt! (inconsistent number of subkeys)\n");
919 ERR("error reading lf block\n");
923 /* _nt_dump_nk [Internal] */
924 static int _nt_dump_nk(LPSTR key_name,char *base,nt_nk *nk,FILE *f,int level)
928 LPSTR new_key_name = NULL;
931 if (nk->SubBlockId != NT_REG_KEY_BLOCK_ID) {
932 ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId);
936 if ((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) && (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID)) {
937 ERR("registry file corrupt!\n");
941 /* create the new key */
943 /* create new subkey name */
944 new_key_name = _strdupnA(key_name,strlen(key_name)+nk->name_len+1);
945 if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
946 strncat(new_key_name,nk->name,nk->name_len);
948 /* write the key path (something like [Software\\Microsoft\\..]) only if:
949 1) key has some values
950 2) key has no values and no subkeys
952 if (nk->nr_values > 0) {
953 /* there are some values */
955 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
958 if ((nk->nr_subkeys == 0) && (nk->nr_values == 0)) {
959 /* no subkeys and no values */
961 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
965 /* loop trough the value list */
966 vl = (DWORD *)(base+nk->valuelist_off+4);
967 for (n=0; n<nk->nr_values; n++) {
968 nt_vk * vk = (nt_vk*)(base+vl[n]+4);
969 if (!_nt_dump_vk(new_key_name, base, vk, f)) {
974 } else new_key_name = _strdupnA(key_name,strlen(key_name));
976 /* loop through the subkeys */
977 if (nk->nr_subkeys) {
978 nt_lf *lf = (nt_lf*)(base+nk->lf_off+4);
979 if (!_nt_dump_lf(new_key_name, base, nk->nr_subkeys, lf, f, level-1)) {
991 /**********************************************************************************
992 * _set_registry_levels [Internal]
994 * set level to 0 for loading system files
995 * set level to 1 for loading user files
997 static void _set_registry_levels(int level,int saving,int period)
1001 struct set_registry_levels_request *req = server_alloc_req( sizeof(*req), 0 );
1003 req->current = level;
1004 req->saving = saving;
1005 req->period = period;
1006 server_call( REQ_SET_REGISTRY_LEVELS );
1011 /* _save_at_exit [Internal] */
1012 static void _save_at_exit(HKEY hkey,LPCSTR path)
1014 LPCSTR confdir = get_config_dir();
1015 size_t len = strlen(confdir) + strlen(path) + 2;
1017 if (len > REQUEST_MAX_VAR_SIZE) {
1018 ERR( "config dir '%s' too long\n", confdir );
1023 struct save_registry_atexit_request *req = server_alloc_req( sizeof(*req), len );
1024 sprintf( server_data_ptr(req), "%s/%s", confdir, path );
1026 server_call( REQ_SAVE_REGISTRY_ATEXIT );
1031 /* configure save files and start the periodic saving timer [Internal] */
1032 static void _init_registry_saving( HKEY hkey_users_default )
1037 all = PROFILE_GetWineIniBool("registry","SaveOnlyUpdatedKeys",1);
1038 period = PROFILE_GetWineIniInt("registry","PeriodicSave",0);
1040 /* set saving level (0 for saving everything, 1 for saving only modified keys) */
1041 _set_registry_levels(1,!all,period*1000);
1043 if (PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1))
1045 _save_at_exit(HKEY_CURRENT_USER,SAVE_LOCAL_REGBRANCH_CURRENT_USER );
1046 _save_at_exit(HKEY_LOCAL_MACHINE,SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE);
1047 _save_at_exit(hkey_users_default,SAVE_LOCAL_REGBRANCH_USER_DEFAULT);
1052 /******************************************************************************
1053 * _allocate_default_keys [Internal]
1054 * Registry initialisation, allocates some default keys.
1056 static void _allocate_default_keys(void) {
1062 RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
1065 /* This was an Open, but since it is called before the real registries
1066 are loaded, it was changed to a Create - MTB 980507*/
1067 RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
1068 RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
1071 /* \\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion
1073 * CurrentBuildNumber
1075 * string RegisteredOwner
1076 * string RegisteredOrganization
1079 /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
1081 * string SysLocation
1084 if (-1!=gethostname(buf,200)) {
1085 RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
1086 RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
1090 RegCreateKeyA(HKEY_USERS,".Default",&hkey);
1094 #define REG_DONTLOAD -1
1099 /* return the type of native registry [Internal] */
1100 static int _get_reg_type(void)
1102 char windir[MAX_PATHNAME_LEN];
1103 char tmp[MAX_PATHNAME_LEN];
1104 int ret = REG_WIN31;
1106 GetWindowsDirectoryA(windir,MAX_PATHNAME_LEN);
1108 /* test %windir%/system32/config/system --> winnt */
1109 strcpy(tmp, windir);
1110 strncat(tmp, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(tmp) - 1);
1111 if(GetFileAttributesA(tmp) != (DWORD)-1) {
1116 /* test %windir%/system.dat --> win95 */
1117 strcpy(tmp, windir);
1118 strncat(tmp, "\\system.dat", MAX_PATHNAME_LEN - strlen(tmp) - 1);
1119 if(GetFileAttributesA(tmp) != (DWORD)-1) {
1124 if ((ret == REG_WINNT) && (!PROFILE_GetWineIniString( "Wine", "Profile", "", tmp, MAX_PATHNAME_LEN))) {
1125 MESSAGE("When you are running with a native NT directory specify\n");
1126 MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n");
1127 MESSAGE("registry (LoadWindowsRegistryFiles=N)\n");
1134 #define WINE_REG_VER_ERROR -1
1135 #define WINE_REG_VER_1 0
1136 #define WINE_REG_VER_2 1
1137 #define WINE_REG_VER_OLD 2
1138 #define WINE_REG_VER_UNKNOWN 3
1140 /* return the version of wine registry file [Internal] */
1141 static int _get_wine_registry_file_format_version(LPCSTR fn)
1147 if ((f=fopen(fn,"rt")) == NULL) {
1148 WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno));
1149 return WINE_REG_VER_ERROR;
1152 if (fgets(tmp,50,f) == NULL) {
1153 WARN("Error reading %s: %s\n",fn,strerror(errno));
1155 return WINE_REG_VER_ERROR;
1159 if (sscanf(tmp,"WINE REGISTRY Version %d",&ver) != 1) return WINE_REG_VER_UNKNOWN;
1162 return WINE_REG_VER_1;
1165 return WINE_REG_VER_2;
1168 return WINE_REG_VER_UNKNOWN;
1172 /* load the registry file in wine format [Internal] */
1173 static void load_wine_registry(HKEY hkey,LPCSTR fn)
1177 file_format = _get_wine_registry_file_format_version(fn);
1178 switch (file_format) {
1180 case WINE_REG_VER_1:
1181 WARN("Unable to load registry file %s: old format which is no longer supported.\n",fn);
1184 case WINE_REG_VER_2: {
1186 if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
1187 FILE_ATTRIBUTE_NORMAL, -1, TRUE )) != INVALID_HANDLE_VALUE)
1191 struct load_registry_request *req = server_alloc_req( sizeof(*req), 0 );
1194 server_call( REQ_LOAD_REGISTRY );
1197 CloseHandle( file );
1202 case WINE_REG_VER_UNKNOWN:
1203 WARN("Unable to load registry file %s: unknown format.\n",fn);
1206 case WINE_REG_VER_ERROR:
1211 /* generate and return the name of the tmp file and associated stream [Internal] */
1212 static LPSTR _get_tmp_fn(FILE **f)
1219 sprintf(ret,"/tmp/reg%lx%04x.tmp",(long)getpid(),count++);
1220 if ((tmp_fd = open(ret,O_CREAT | O_EXCL | O_WRONLY,0666)) != -1) break;
1221 if (errno != EEXIST) {
1222 ERR("Unexpected error while open() call: %s\n",strerror(errno));
1229 if ((*f = fdopen(tmp_fd,"w")) == NULL) {
1230 ERR("Unexpected error while fdopen() call: %s\n",strerror(errno));
1239 /* convert win95 native registry file to wine format [Internal] */
1240 static LPSTR _convert_win95_registry_to_wine_format(LPCSTR fn,int level)
1244 DOS_FULL_NAME full_name;
1251 _w95dke *dke, *root_dke;
1253 if (!DOSFS_GetFullName( fn, 0, &full_name )) return NULL;
1255 /* map the registry into the memory */
1256 if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return NULL;
1257 if ((fstat(fd, &st) == -1)) goto error1;
1258 if (!st.st_size) goto error1;
1259 if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error1;
1261 /* control signature */
1262 if (*(LPDWORD)base != W95_REG_CREG_ID) {
1263 ERR("unable to load native win95 registry file %s: unknown signature.\n",fn);
1268 /* load the header (rgkn) */
1269 rgkn = (_w95rgkn*)(creg + 1);
1270 if (rgkn->id != W95_REG_RGKN_ID) {
1271 ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
1274 if (rgkn->root_off != 0x20) {
1275 ERR("rgkn->root_off not 0x20, please report !\n");
1278 if (rgkn->last_dke > rgkn->size)
1280 ERR("registry file corrupt! last_dke > size!\n");
1283 /* verify last dke */
1284 dke = (_w95dke*)((char*)rgkn + rgkn->last_dke);
1285 if (dke->x1 != 0x80000000)
1287 ERR("last dke invalid !\n");
1290 if (rgkn->size > creg->rgdb_off)
1292 ERR("registry file corrupt! rgkn size > rgdb_off !\n");
1295 root_dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
1296 if ( (root_dke->prevlvl != 0xffffffff) || (root_dke->next != 0xffffffff) )
1298 ERR("registry file corrupt! invalid root dke !\n");
1302 if ( (ret = _get_tmp_fn(&f)) == NULL) goto error;
1303 fprintf(f,"WINE REGISTRY Version 2");
1304 _w95_dump_dke("",creg,rgkn,root_dke,f,level);
1309 ERR("Unable to load native win95 registry file %s.\n",fn);
1310 ERR("Please report to a.mohr@mailto.de.\n");
1311 ERR("Make a backup of the file, run a good reg cleaner program and try again!\n");
1314 munmap(base, st.st_size);
1320 /* convert winnt native registry file to wine format [Internal] */
1321 static LPSTR _convert_winnt_registry_to_wine_format(LPCSTR fn,int level)
1325 DOS_FULL_NAME full_name;
1332 nt_hbin_sub *hbin_sub;
1335 if (!DOSFS_GetFullName( fn, 0, &full_name )) return NULL;
1337 /* map the registry into the memory */
1338 if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return NULL;
1339 if ((fstat(fd, &st) == -1)) goto error1;
1340 if (!st.st_size) goto error1;
1341 if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error1;
1343 /* control signature */
1344 if (*(LPDWORD)base != NT_REG_HEADER_BLOCK_ID) {
1345 ERR("unable to load native winnt registry file %s: unknown signature.\n",fn);
1353 hbin = (nt_hbin*)((char*) base + 0x1000);
1354 if (hbin->id != NT_REG_POOL_BLOCK_ID) {
1355 ERR( "hbin block invalid\n");
1359 /* hbin_sub block */
1360 hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
1361 if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k')) {
1362 ERR( "hbin_sub block invalid\n");
1367 nk = (nt_nk*)&(hbin_sub->data[0]);
1368 if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE) {
1369 ERR( "special nk block not found\n");
1373 if ( (ret = _get_tmp_fn(&f)) == NULL) goto error;
1374 fprintf(f,"WINE REGISTRY Version 2");
1375 _nt_dump_nk("",(char*)base+0x1000,nk,f,level);
1379 munmap(base,st.st_size);
1385 /* convert native native registry to wine format and load it via server call [Internal] */
1386 static void _convert_and_load_native_registry(LPCSTR fn,HKEY hkey,int reg_type,int level)
1392 /* FIXME: following function doesn't really convert yet */
1393 tmp = _convert_winnt_registry_to_wine_format(fn,level);
1396 tmp = _convert_win95_registry_to_wine_format(fn,level);
1399 ERR("Don't know how to convert native 3.1 registry yet.\n");
1402 ERR("Unknown registry format parameter (%d)\n",reg_type);
1407 load_wine_registry(hkey,tmp);
1408 TRACE("File %s successfuly converted to %s and loaded to registry.\n",fn,tmp);
1411 else WARN("Unable to convert %s (not exist?)\n",fn);
1415 /* load all native windows registry files [Internal] */
1416 static void _load_windows_registry( HKEY hkey_users_default )
1419 char windir[MAX_PATHNAME_LEN];
1420 char path[MAX_PATHNAME_LEN];
1422 GetWindowsDirectoryA(windir,MAX_PATHNAME_LEN);
1424 reg_type = _get_reg_type();
1429 /* user specific ntuser.dat */
1430 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)) {
1431 strcat(path,"\\ntuser.dat");
1432 _convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WINNT,1);
1435 /* default user.dat */
1436 if (hkey_users_default) {
1437 strcpy(path,windir);
1438 strcat(path,"\\system32\\config\\default");
1439 _convert_and_load_native_registry(path,hkey_users_default,REG_WINNT,1);
1444 * map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
1447 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SYSTEM", &hkey)) {
1448 strcpy(path,windir);
1449 strcat(path,"\\system32\\config\\system");
1450 _convert_and_load_native_registry(path,hkey,REG_WINNT,1);
1454 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE", &hkey)) {
1455 strcpy(path,windir);
1456 strcat(path,"\\system32\\config\\software");
1457 _convert_and_load_native_registry(path,hkey,REG_WINNT,1);
1461 strcpy(path,windir);
1462 strcat(path,"\\system32\\config\\sam");
1463 _convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WINNT,0);
1465 strcpy(path,windir);
1466 strcat(path,"\\system32\\config\\security");
1467 _convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WINNT,0);
1469 /* this key is generated when the nt-core booted successfully */
1470 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey)) RegCloseKey(hkey);
1475 _convert_and_load_native_registry("c:\\system.1st",HKEY_LOCAL_MACHINE,REG_WIN95,0);
1477 strcpy(path,windir);
1478 strcat(path,"\\system.dat");
1479 _convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WIN95,0);
1481 if (PROFILE_GetWineIniString("Wine","Profile","",path,MAX_PATHNAME_LEN)) {
1482 /* user specific user.dat */
1483 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1484 _convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WIN95,1);
1486 /* default user.dat */
1487 if (hkey_users_default) {
1488 strcpy(path,windir);
1489 strcat(path,"\\user.dat");
1490 _convert_and_load_native_registry(path,hkey_users_default,REG_WIN95,1);
1493 strcpy(path,windir);
1494 strcat(path,"\\user.dat");
1495 _convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WIN95,1);
1500 /* FIXME: here we should convert to *.reg file supported by server and call REQ_LOAD_REGISTRY, see REG_WIN95 case */
1505 TRACE("REG_DONTLOAD\n");
1509 ERR("switch: no match (%d)\n",reg_type);
1515 /* load global registry files (stored in /etc/wine) [Internal] */
1516 static void _load_global_registry(void)
1520 /* Load the global HKU hive directly from sysconfdir */
1521 load_wine_registry( HKEY_USERS, SAVE_GLOBAL_REGBRANCH_USER_DEFAULT );
1523 /* Load the global machine defaults directly from sysconfdir */
1524 load_wine_registry( HKEY_LOCAL_MACHINE, SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE );
1527 /* load home registry files (stored in ~/.wine) [Internal] */
1528 static void _load_home_registry( HKEY hkey_users_default )
1530 LPCSTR confdir = get_config_dir();
1531 LPSTR tmp = _xmalloc(strlen(confdir)+20);
1533 strcpy(tmp,confdir);
1534 strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_USER_DEFAULT);
1535 load_wine_registry(hkey_users_default,tmp);
1537 strcpy(tmp,confdir);
1538 strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_CURRENT_USER);
1539 load_wine_registry(HKEY_CURRENT_USER,tmp);
1541 strcpy(tmp,confdir);
1542 strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE);
1543 load_wine_registry(HKEY_LOCAL_MACHINE,tmp);
1548 /* load all registry (native and global and home) */
1549 void SHELL_LoadRegistry( void )
1551 HKEY hkey_users_default;
1555 if (!CLIENT_IsBootThread()) return; /* already loaded */
1557 if (!RegCreateKeyA(HKEY_USERS,".Default",&hkey_users_default)) hkey_users_default = 0;
1559 _allocate_default_keys();
1560 _set_registry_levels(0,0,0);
1561 if (PROFILE_GetWineIniBool("Registry","LoadWindowsRegistryFiles",1))
1562 _load_windows_registry( hkey_users_default );
1563 if (PROFILE_GetWineIniBool("Registry","LoadGlobalRegistryFiles",1))
1564 _load_global_registry();
1565 _set_registry_levels(1,0,0);
1566 if (PROFILE_GetWineIniBool("Registry","LoadHomeRegistryFiles",1))
1567 _load_home_registry( hkey_users_default );
1568 _init_registry_saving( hkey_users_default );
1569 RegCloseKey(hkey_users_default);
1572 /***************************************************************************/
1574 /***************************************************************************/
1576 /******************************************************************************
1577 * RegFlushKey [KERNEL.227] [ADVAPI32.143]
1578 * Immediately writes key to registry.
1579 * Only returns after data has been written to disk.
1581 * FIXME: does it really wait until data is written ?
1584 * hkey [I] Handle of key to write
1587 * Success: ERROR_SUCCESS
1588 * Failure: Error code
1590 DWORD WINAPI RegFlushKey( HKEY hkey )
1592 FIXME( "(%x): stub\n", hkey );
1593 return ERROR_SUCCESS;
1597 /******************************************************************************
1598 * RegUnLoadKeyA [ADVAPI32.172]
1600 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1602 FIXME("(%x,%s): stub\n",hkey, debugstr_a(lpSubKey));
1603 return ERROR_SUCCESS;
1607 /******************************************************************************
1608 * RegRestoreKeyW [ADVAPI32.164]
1611 * hkey [I] Handle of key where restore begins
1612 * lpFile [I] Address of filename containing saved tree
1613 * dwFlags [I] Optional flags
1615 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
1617 TRACE("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
1619 /* It seems to do this check before the hkey check */
1620 if (!lpFile || !*lpFile)
1621 return ERROR_INVALID_PARAMETER;
1623 FIXME("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
1625 /* Check for file existence */
1627 return ERROR_SUCCESS;
1631 /******************************************************************************
1632 * RegRestoreKeyA [ADVAPI32.163]
1634 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
1636 LPWSTR lpFileW = HEAP_strdupAtoW( GetProcessHeap(), 0, lpFile );
1637 LONG ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
1638 HeapFree( GetProcessHeap(), 0, lpFileW );
1643 /******************************************************************************
1644 * RegReplaceKeyA [ADVAPI32.161]
1646 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
1649 FIXME("(%x,%s,%s,%s): stub\n", hkey, debugstr_a(lpSubKey),
1650 debugstr_a(lpNewFile),debugstr_a(lpOldFile));
1651 return ERROR_SUCCESS;
1659 /* 16-bit functions */
1661 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
1662 * some programs. Do not remove those cases. -MM
1664 static inline void fix_win16_hkey( HKEY *hkey )
1666 if (*hkey == 0 || *hkey == 1) *hkey = HKEY_CLASSES_ROOT;
1669 /******************************************************************************
1670 * RegEnumKey16 [KERNEL.216] [SHELL.7]
1672 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
1674 fix_win16_hkey( &hkey );
1675 return RegEnumKeyA( hkey, index, name, name_len );
1678 /******************************************************************************
1679 * RegOpenKey16 [KERNEL.217] [SHELL.1]
1681 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1683 fix_win16_hkey( &hkey );
1684 return RegOpenKeyA( hkey, name, retkey );
1687 /******************************************************************************
1688 * RegCreateKey16 [KERNEL.218] [SHELL.2]
1690 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1692 fix_win16_hkey( &hkey );
1693 return RegCreateKeyA( hkey, name, retkey );
1696 /******************************************************************************
1697 * RegDeleteKey16 [KERNEL.219] [SHELL.4]
1699 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
1701 fix_win16_hkey( &hkey );
1702 return RegDeleteKeyA( hkey, name );
1705 /******************************************************************************
1706 * RegCloseKey16 [KERNEL.220] [SHELL.3]
1708 DWORD WINAPI RegCloseKey16( HKEY hkey )
1710 fix_win16_hkey( &hkey );
1711 return RegCloseKey( hkey );
1714 /******************************************************************************
1715 * RegSetValue16 [KERNEL.221] [SHELL.5]
1717 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
1719 fix_win16_hkey( &hkey );
1720 return RegSetValueA( hkey, name, type, data, count );
1723 /******************************************************************************
1724 * RegDeleteValue16 [KERNEL.222]
1726 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
1728 fix_win16_hkey( &hkey );
1729 return RegDeleteValueA( hkey, name );
1732 /******************************************************************************
1733 * RegEnumValue16 [KERNEL.223]
1735 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
1736 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
1738 fix_win16_hkey( &hkey );
1739 return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
1742 /******************************************************************************
1743 * RegQueryValue16 [KERNEL.224] [SHELL.6]
1746 * Is this HACK still applicable?
1749 * The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
1750 * mask out the high 16 bit. This (not so much incidently) hopefully fixes
1753 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
1755 fix_win16_hkey( &hkey );
1756 if (count) *count &= 0xffff;
1757 return RegQueryValueA( hkey, name, data, count );
1760 /******************************************************************************
1761 * RegQueryValueEx16 [KERNEL.225]
1763 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
1764 LPBYTE data, LPDWORD count )
1766 fix_win16_hkey( &hkey );
1767 return RegQueryValueExA( hkey, name, reserved, type, data, count );
1770 /******************************************************************************
1771 * RegSetValueEx16 [KERNEL.226]
1773 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
1774 CONST BYTE *data, DWORD count )
1776 fix_win16_hkey( &hkey );
1777 if (!count && (type==REG_SZ)) count = strlen(data);
1778 return RegSetValueExA( hkey, name, reserved, type, data, count );
1781 /******************************************************************************
1782 * RegFlushKey16 [KERNEL.227]
1784 DWORD WINAPI RegFlushKey16( HKEY hkey )
1786 fix_win16_hkey( &hkey );
1787 return RegFlushKey( hkey );