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 * This library is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU Lesser General Public
14 * License as published by the Free Software Foundation; either
15 * version 2.1 of the License, or (at your option) any later version.
17 * This library is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 * Lesser General Public License for more details.
22 * You should have received a copy of the GNU Lesser General Public
23 * License along with this library; if not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
27 * When changing this file, please re-run the regtest program to ensure
28 * the conditions are handled properly.
33 * Time for RegEnumKey*, RegQueryInfoKey*
37 #include "wine/port.h"
46 #include <sys/types.h>
49 #ifdef HAVE_SYS_MMAN_H
50 # include <sys/mman.h>
57 #include "wine/winbase16.h"
58 #include "wine/library.h"
59 #include "wine/server.h"
60 #include "wine/unicode.h"
63 #include "wine/debug.h"
65 WINE_DEFAULT_DEBUG_CHANNEL(reg);
67 /* FIXME: following defines should be configured global */
68 #define SAVE_GLOBAL_REGBRANCH_USER_DEFAULT ETCDIR"/wine.userreg"
69 #define SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE ETCDIR"/wine.systemreg"
71 /* relative in ~user/.wine/ : */
72 #define SAVE_LOCAL_REGBRANCH_CURRENT_USER "user.reg"
73 #define SAVE_LOCAL_REGBRANCH_USER_DEFAULT "userdef.reg"
74 #define SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE "system.reg"
76 /* _xmalloc [Internal] */
77 static void *_xmalloc( size_t size )
81 res = malloc (size ? size : 1);
83 WARN("Virtual memory exhausted.\n");
89 /* _strdupnA [Internal] */
90 static LPSTR _strdupnA(LPCSTR str,size_t len)
94 if (!str) return NULL;
95 ret = _xmalloc( len + 1 );
96 memcpy( ret, str, len );
101 /* convert ansi string to unicode [Internal] */
102 static LPWSTR _strdupnAtoW(LPCSTR strA,size_t lenA)
107 if (!strA) return NULL;
108 lenW = MultiByteToWideChar(CP_ACP,0,strA,lenA,NULL,0);
109 ret = _xmalloc(lenW*sizeof(WCHAR)+sizeof(WCHAR));
110 MultiByteToWideChar(CP_ACP,0,strA,lenA,ret,lenW);
115 /* dump a Unicode string with proper escaping [Internal] */
116 /* FIXME: this code duplicates server/unicode.c */
117 static int _dump_strW(const WCHAR *str,size_t len,FILE *f,char escape[2])
119 static const char escapes[32] = ".......abtnvfr.............e....";
124 for (; len; str++, len--)
126 if (pos > buffer + sizeof(buffer) - 8)
128 fwrite( buffer, pos - buffer, 1, f );
129 count += pos - buffer;
132 if (*str > 127) /* hex escape */
134 if (len > 1 && str[1] < 128 && isxdigit((char)str[1]))
135 pos += sprintf( pos, "\\x%04x", *str );
137 pos += sprintf( pos, "\\x%x", *str );
140 if (*str < 32) /* octal or C escape */
142 if (!*str && len == 1) continue; /* do not output terminating NULL */
143 if (escapes[*str] != '.')
144 pos += sprintf( pos, "\\%c", escapes[*str] );
145 else if (len > 1 && str[1] >= '0' && str[1] <= '7')
146 pos += sprintf( pos, "\\%03o", *str );
148 pos += sprintf( pos, "\\%o", *str );
151 if (*str == '\\' || *str == escape[0] || *str == escape[1]) *pos++ = '\\';
154 fwrite( buffer, pos - buffer, 1, f );
155 count += pos - buffer;
159 /* convert ansi string to unicode and dump with proper escaping [Internal] */
160 static int _dump_strAtoW(LPCSTR strA,size_t len,FILE *f,char escape[2])
165 if (strA == NULL) return 0;
166 strW = _strdupnAtoW(strA,len);
167 ret = _dump_strW(strW,len,f,escape);
173 /* FIXME: this code duplicates server/registry.c */
175 WCHAR *nameW; /* value name */
176 int type; /* value type */
177 size_t len; /* value data length in bytes */
178 void *data; /* pointer to value data */
181 /* dump a value to a text file */
182 /* FIXME: this code duplicates server/registry.c */
183 static void _dump_value(struct key_value *value,FILE *f)
187 if (value->nameW[0]) {
189 count = 1 + _dump_strW(value->nameW,strlenW(value->nameW),f,"\"\"");
190 count += fprintf( f, "\"=" );
192 else count = fprintf( f, "@=" );
194 switch(value->type) {
198 if (value->type != REG_SZ) fprintf( f, "str(%d):", value->type );
200 if (value->data) _dump_strW(value->data,value->len/sizeof(WCHAR),f,"\"\"");
204 if (value->len == sizeof(DWORD)) {
206 memcpy( &dw, value->data, sizeof(DWORD) );
207 fprintf( f, "dword:%08lx", dw );
210 /* else fall through */
212 if (value->type == REG_BINARY) count += fprintf( f, "hex:" );
213 else count += fprintf( f, "hex(%x):", value->type );
214 for (i = 0; i < value->len; i++) {
215 count += fprintf( f, "%02x", *((unsigned char *)value->data + i) );
216 if (i < value->len-1) {
219 fprintf( f, "\\\n " );
229 /******************************************************************/
230 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
232 reghack - windows 3.11 registry data format demo program.
234 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
235 a combined hash table and tree description, and finally a text table.
237 The header is obvious from the struct header. The taboff1 and taboff2
238 fields are always 0x20, and their usage is unknown.
240 The 8-byte entry table has various entry types.
242 tabent[0] is a root index. The second word has the index of the root of
244 tabent[1..hashsize] is a hash table. The first word in the hash entry is
245 the index of the key/value that has that hash. Data with the same
246 hash value are on a circular list. The other three words in the
247 hash entry are always zero.
248 tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
249 entry: dirent and keyent/valent. They are identified by context.
250 tabent[freeidx] is the first free entry. The first word in a free entry
251 is the index of the next free entry. The last has 0 as a link.
252 The other three words in the free list are probably irrelevant.
254 Entries in text table are preceded by a word at offset-2. This word
255 has the value (2*index)+1, where index is the referring keyent/valent
256 entry in the table. I have no suggestion for the 2* and the +1.
257 Following the word, there are N bytes of data, as per the keyent/valent
258 entry length. The offset of the keyent/valent entry is from the start
259 of the text table to the first data byte.
261 This information is not available from Microsoft. The data format is
262 deduced from the reg.dat file by me. Mistakes may
263 have been made. I claim no rights and give no guarantees for this program.
265 Tor Sjøwall, tor@sn.no
268 /* reg.dat header format */
270 char cookie[8]; /* 'SHCC3.10' */
271 unsigned long taboff1; /* offset of hash table (??) = 0x20 */
272 unsigned long taboff2; /* offset of index table (??) = 0x20 */
273 unsigned long tabcnt; /* number of entries in index table */
274 unsigned long textoff; /* offset of text part */
275 unsigned long textsize; /* byte size of text part */
276 unsigned short hashsize; /* hash size */
277 unsigned short freeidx; /* free index */
280 /* generic format of table entries */
282 unsigned short w0, w1, w2, w3;
285 /* directory tabent: */
287 unsigned short sibling_idx; /* table index of sibling dirent */
288 unsigned short child_idx; /* table index of child dirent */
289 unsigned short key_idx; /* table index of key keyent */
290 unsigned short value_idx; /* table index of value valent */
295 unsigned short hash_idx; /* hash chain index for string */
296 unsigned short refcnt; /* reference count */
297 unsigned short length; /* length of string */
298 unsigned short string_off; /* offset of string in text table */
303 unsigned short hash_idx; /* hash chain index for string */
304 unsigned short refcnt; /* reference count */
305 unsigned short length; /* length of string */
306 unsigned short string_off; /* offset of string in text table */
309 /* recursive helper function to display a directory tree [Internal] */
310 void _w31_dumptree(unsigned short idx,unsigned char *txt,struct _w31_tabent *tab,struct _w31_header *head,HKEY hkey,time_t lastmodified, int level)
312 struct _w31_dirent *dir;
313 struct _w31_keyent *key;
314 struct _w31_valent *val;
316 static char tail[400];
319 dir=(struct _w31_dirent*)&tab[idx];
322 key = (struct _w31_keyent*)&tab[dir->key_idx];
324 memcpy(tail,&txt[key->string_off],key->length);
325 tail[key->length]='\0';
326 /* all toplevel entries AND the entries in the
327 * toplevel subdirectory belong to \SOFTWARE\Classes
329 if (!level && !strcmp(tail,".classes")) {
330 _w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
331 idx=dir->sibling_idx;
334 if (subkey) RegCloseKey( subkey );
335 if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
336 /* only add if leaf node or valued node */
337 if (dir->value_idx!=0||dir->child_idx==0) {
338 if (dir->value_idx) {
339 val=(struct _w31_valent*)&tab[dir->value_idx];
340 memcpy(tail,&txt[val->string_off],val->length);
341 tail[val->length]='\0';
342 RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
345 } else TRACE("strange: no directory key name, idx=%04x\n", idx);
346 _w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
347 idx=dir->sibling_idx;
349 if (subkey) RegCloseKey( subkey );
353 /******************************************************************************
354 * _w31_loadreg [Internal]
356 void _w31_loadreg(void)
359 struct _w31_header head;
360 struct _w31_tabent *tab;
364 BY_HANDLE_FILE_INFORMATION hfinfo;
369 hf = OpenFile("reg.dat",&ofs,OF_READ);
370 if (hf==HFILE_ERROR) return;
372 /* read & dump header */
373 if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
374 ERR("reg.dat is too short.\n");
378 if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
379 ERR("reg.dat has bad signature.\n");
384 len = head.tabcnt * sizeof(struct _w31_tabent);
385 /* read and dump index table */
387 if (len!=_lread(hf,tab,len)) {
388 ERR("couldn't read %d bytes.\n",len);
395 txt = _xmalloc(head.textsize);
396 if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
397 ERR("couldn't seek to textblock.\n");
403 if (head.textsize!=_lread(hf,txt,head.textsize)) {
404 ERR("textblock too short (%d instead of %ld).\n",len,head.textsize);
411 if (!GetFileInformationByHandle(hf,&hfinfo)) {
412 ERR("GetFileInformationByHandle failed?.\n");
418 lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
419 _w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
426 /***********************************************************************************/
427 /* windows 95 registry loader */
428 /***********************************************************************************/
430 /* SECTION 1: main header
434 #define W95_REG_CREG_ID 0x47455243
437 DWORD id; /* "CREG" = W95_REG_CREG_ID */
438 DWORD version; /* ???? 0x00010000 */
439 DWORD rgdb_off; /* 0x08 Offset of 1st RGDB-block */
440 DWORD uk2; /* 0x0c */
441 WORD rgdb_num; /* 0x10 # of RGDB-blocks */
447 /* SECTION 2: Directory information (tree structure)
449 * once on offset 0x20
451 * structure: [rgkn][dke]* (repeat till last_dke is reached)
453 #define W95_REG_RGKN_ID 0x4e4b4752
456 DWORD id; /*"RGKN" = W95_REG_RGKN_ID */
457 DWORD size; /* Size of the RGKN-block */
458 DWORD root_off; /* Rel. Offset of the root-record */
459 DWORD last_dke; /* Offset to last DKE ? */
463 /* Disk Key Entry Structure
465 * the 1st entry in a "usual" registry file is a nul-entry with subkeys: the
466 * hive itself. It looks the same like other keys. Even the ID-number can
469 * The "hash"-value is a value representing the key's name. Windows will not
470 * search for the name, but for a matching hash-value. if it finds one, it
471 * will compare the actual string info, otherwise continue with the next key.
472 * To calculate the hash initialize a D-Word with 0 and add all ASCII-values
473 * of the string which are smaller than 0x80 (128) to this D-Word.
475 * If you want to modify key names, also modify the hash-values, since they
476 * cannot be found again (although they would be displayed in REGEDIT)
477 * End of list-pointers are filled with 0xFFFFFFFF
479 * Disk keys are layed out flat ... But, sometimes, nrLS and nrMS are both
480 * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
481 * structure) and reading another RGDB_section.
483 * The last DKE (see field last_dke in _w95_rgkn) has only 3 DWORDs with
484 * 0x80000000 (EOL indicator ?) as x1, the hash value and 0xFFFFFFFF as x3.
485 * The remaining space between last_dke and the offset calculated from
486 * rgkn->size seems to be free for use for more dke:s.
487 * So it seems if more dke:s are added, they are added to that space and
488 * last_dke is grown, and in case that "free" space is out, the space
489 * gets grown and rgkn->size gets adjusted.
491 * there is a one to one relationship between dke and dkh
493 /* key struct, once per key */
495 DWORD x1; /* Free entry indicator(?) */
496 DWORD hash; /* sum of bytes of keyname */
497 DWORD x3; /* Root key indicator? usually 0xFFFFFFFF */
498 DWORD prevlvl; /* offset of previous key */
499 DWORD nextsub; /* offset of child key */
500 DWORD next; /* offset of sibling key */
501 WORD nrLS; /* id inside the rgdb block */
502 WORD nrMS; /* number of the rgdb block */
505 /* SECTION 3: key information, values and data
508 * section: [blocks]* (repeat creg->rgdb_num times)
509 * blocks: [rgdb] [subblocks]* (repeat till block size reached )
510 * subblocks: [dkh] [dkv]* (repeat dkh->values times )
512 * An interesting relationship exists in RGDB_section. The DWORD value
513 * at offset 0x10 equals the one at offset 0x04 minus the one at offset 0x08.
514 * I have no idea at the moment what this means. (Kevin Cozens)
517 /* block header, once per block */
518 #define W95_REG_RGDB_ID 0x42444752
521 DWORD id; /* 0x00 'RGDB' = W95_REG_RGDB_ID */
522 DWORD size; /* 0x04 */
523 DWORD uk1; /* 0x08 */
524 DWORD uk2; /* 0x0c */
525 DWORD uk3; /* 0x10 */
526 DWORD uk4; /* 0x14 */
527 DWORD uk5; /* 0x18 */
528 DWORD uk6; /* 0x1c */
532 /* Disk Key Header structure (RGDB part), once per key */
534 DWORD nextkeyoff; /* 0x00 offset to next dkh */
535 WORD nrLS; /* 0x04 id inside the rgdb block */
536 WORD nrMS; /* 0x06 number of the rgdb block */
537 DWORD bytesused; /* 0x08 */
538 WORD keynamelen; /* 0x0c len of name */
539 WORD values; /* 0x0e number of values */
540 DWORD xx1; /* 0x10 */
541 char name[1]; /* 0x14 */
542 /* dkv */ /* 0x14 + keynamelen */
545 /* Disk Key Value structure, once per value */
547 DWORD type; /* 0x00 */
549 WORD valnamelen; /* 0x08 length of name, 0 is default key */
550 WORD valdatalen; /* 0x0A length of data */
551 char name[1]; /* 0x0c */
552 /* raw data */ /* 0x0c + valnamelen */
555 /******************************************************************************
556 * _w95_lookup_dkh [Internal]
558 * seeks the dkh belonging to a dke
560 static _w95dkh *_w95_lookup_dkh(_w95creg *creg,int nrLS,int nrMS)
566 /* get the beginning of the rgdb datastore */
567 rgdb = (_w95rgdb*)((char*)creg+creg->rgdb_off);
569 /* check: requested block < last_block) */
570 if (creg->rgdb_num <= nrMS) {
571 ERR("registry file corrupt! requested block no. beyond end.\n");
575 /* find the right block */
576 for(i=0; i<nrMS ;i++) {
577 if(rgdb->id != W95_REG_RGDB_ID) { /* check the magic */
578 ERR("registry file corrupt! bad magic 0x%08lx\n", rgdb->id);
581 rgdb = (_w95rgdb*) ((char*)rgdb+rgdb->size); /* find next block */
584 dkh = (_w95dkh*)(rgdb + 1); /* first sub block within the rgdb */
587 if(nrLS==dkh->nrLS ) return dkh;
588 dkh = (_w95dkh*)((char*)dkh + dkh->nextkeyoff); /* find next subblock */
589 } while ((char *)dkh < ((char*)rgdb+rgdb->size));
595 /******************************************************************************
596 * _w95_dump_dkv [Internal]
598 static int _w95_dump_dkv(_w95dkh *dkh,int nrLS,int nrMS,FILE *f)
603 /* first value block */
604 dkv = (_w95dkv*)((char*)dkh+dkh->keynamelen+0x14);
606 /* loop through the values */
607 for (i=0; i< dkh->values; i++) {
608 struct key_value value;
611 value.nameW = _strdupnAtoW(dkv->name,dkv->valnamelen);
612 value.type = dkv->type;
613 value.len = dkv->valdatalen;
615 value.data = &(dkv->name[dkv->valnamelen]);
617 if ( (value.type==REG_SZ) || (value.type==REG_EXPAND_SZ) || (value.type==REG_MULTI_SZ) ) {
618 pdata = _strdupnAtoW(value.data,value.len);
621 if (pdata != NULL) value.data = pdata;
623 _dump_value(&value,f);
625 if (pdata != NULL) free(pdata);
628 dkv = (_w95dkv*)((char*)dkv+dkv->valnamelen+dkv->valdatalen+0x0c);
633 /******************************************************************************
634 * _w95_dump_dke [Internal]
636 static int _w95_dump_dke(LPSTR key_name,_w95creg *creg,_w95rgkn *rgkn,_w95dke *dke,FILE *f,int level)
639 LPSTR new_key_name = NULL;
641 /* special root key */
642 if (dke->nrLS == 0xffff || dke->nrMS==0xffff) /* eg. the root key has no name */
644 /* parse the one subkey */
645 if (dke->nextsub != 0xffffffff) return _w95_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub),f,level);
646 /* has no sibling keys */
650 /* search subblock */
651 if (!(dkh = _w95_lookup_dkh(creg, dke->nrLS, dke->nrMS))) {
652 ERR("dke pointing to missing dkh !\n");
657 /* create new subkey name */
658 new_key_name = _strdupnA(key_name,strlen(key_name)+dkh->keynamelen+1);
659 if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
660 strncat(new_key_name,dkh->name,dkh->keynamelen);
662 /* walk sibling keys */
663 if (dke->next != 0xffffffff ) {
664 if (!_w95_dump_dke(key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->next),f,level)) {
670 /* write the key path (something like [Software\\Microsoft\\..]) only if:
671 1) key has some values
672 2) key has no values and no subkeys
674 if (dkh->values > 0) {
675 /* there are some values */
677 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
679 if (!_w95_dump_dkv(dkh, dke->nrLS, dke->nrMS,f)) {
684 if ((dke->nextsub == 0xffffffff) && (dkh->values == 0)) {
685 /* no subkeys and no values */
687 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
690 } else new_key_name = _strdupnA(key_name,strlen(key_name));
693 if (dke->nextsub != 0xffffffff) {
694 if (!_w95_dump_dke(new_key_name, creg, rgkn, (_w95dke*)((char*)rgkn+dke->nextsub),f,level-1)) {
703 /* end windows 95 loader */
705 /***********************************************************************************/
706 /* windows NT registry loader */
707 /***********************************************************************************/
709 /* NT REGISTRY LOADER */
711 #ifdef HAVE_SYS_MMAN_H
712 # include <sys/mman.h>
716 #define MAP_FAILED ((LPVOID)-1)
719 #define NT_REG_BLOCK_SIZE 0x1000
721 #define NT_REG_HEADER_BLOCK_ID 0x66676572 /* regf */
722 #define NT_REG_POOL_BLOCK_ID 0x6E696268 /* hbin */
723 #define NT_REG_KEY_BLOCK_ID 0x6b6e /* nk */
724 #define NT_REG_VALUE_BLOCK_ID 0x6b76 /* vk */
726 /* subblocks of nk */
727 #define NT_REG_HASH_BLOCK_ID 0x666c /* lf */
728 #define NT_REG_NOHASH_BLOCK_ID 0x696c /* li */
729 #define NT_REG_RI_BLOCK_ID 0x6972 /* ri */
731 #define NT_REG_KEY_BLOCK_TYPE 0x20
732 #define NT_REG_ROOT_KEY_BLOCK_TYPE 0x2c
735 DWORD id; /* 0x66676572 'regf'*/
736 DWORD uk1; /* 0x04 */
737 DWORD uk2; /* 0x08 */
738 FILETIME DateModified; /* 0x0c */
739 DWORD uk3; /* 0x14 */
740 DWORD uk4; /* 0x18 */
741 DWORD uk5; /* 0x1c */
742 DWORD uk6; /* 0x20 */
743 DWORD RootKeyBlock; /* 0x24 */
744 DWORD BlockSize; /* 0x28 */
746 DWORD Checksum; /* at offset 0x1FC */
755 DWORD id; /* 0x6E696268 'hbin' */
759 DWORD uk2; /* 0x10 */
760 DWORD uk3; /* 0x14 */
761 DWORD uk4; /* 0x18 */
762 DWORD size; /* 0x1C */
763 nt_hbin_sub hbin_sub; /* 0x20 */
767 * the value_list consists of offsets to the values (vk)
770 WORD SubBlockId; /* 0x00 0x6B6E */
771 WORD Type; /* 0x02 for the root-key: 0x2C, otherwise 0x20*/
772 FILETIME writetime; /* 0x04 */
773 DWORD uk1; /* 0x0C */
774 DWORD parent_off; /* 0x10 Offset of Owner/Parent key */
775 DWORD nr_subkeys; /* 0x14 number of sub-Keys */
776 DWORD uk8; /* 0x18 */
777 DWORD lf_off; /* 0x1C Offset of the sub-key lf-Records */
778 DWORD uk2; /* 0x20 */
779 DWORD nr_values; /* 0x24 number of values */
780 DWORD valuelist_off; /* 0x28 Offset of the Value-List */
781 DWORD off_sk; /* 0x2c Offset of the sk-Record */
782 DWORD off_class; /* 0x30 Offset of the Class-Name */
783 DWORD uk3; /* 0x34 */
784 DWORD uk4; /* 0x38 */
785 DWORD uk5; /* 0x3c */
786 DWORD uk6; /* 0x40 */
787 DWORD uk7; /* 0x44 */
788 WORD name_len; /* 0x48 name-length */
789 WORD class_len; /* 0x4a class-name length */
790 char name[1]; /* 0x4c key-name */
794 DWORD off_nk; /* 0x00 */
795 DWORD name; /* 0x04 */
799 WORD id; /* 0x00 0x666c */
800 WORD nr_keys; /* 0x06 */
801 hash_rec hash_rec[1];
805 list of subkeys without hash
812 WORD id; /* 0x00 0x696c */
818 this is a intermediate node
829 WORD id; /* 0x00 0x6972 */
830 WORD nr_li; /* 0x02 number off offsets */
831 DWORD off_li[1]; /* 0x04 points to li */
835 WORD id; /* 0x00 'vk' */
849 * 0 value is a default value
850 * 1 the value has a name
853 * len of the whole data block
855 * bytes including the terminating \0 = 2*(number_of_chars+1)
856 * - reg_dword, reg_binary:
857 * if highest bit of data_len is set data_off contains the value
859 static int _nt_dump_vk(LPSTR key_name, char *base, nt_vk *vk,FILE *f)
861 BYTE *pdata = (BYTE *)(base+vk->data_off+4); /* start of data */
862 struct key_value value;
864 if (vk->id != NT_REG_VALUE_BLOCK_ID) {
865 ERR("unknown block found (0x%04x), please report!\n", vk->id);
869 value.nameW = _strdupnAtoW(vk->name,vk->nam_len);
870 value.type = vk->type;
871 value.len = (vk->data_len & 0x7fffffff);
872 value.data = (vk->data_len & 0x80000000) ? (LPBYTE)&(vk->data_off): pdata;
874 _dump_value(&value,f);
880 /* it's called from _nt_dump_lf() */
881 static int _nt_dump_nk(LPSTR key_name,char *base,nt_nk *nk,FILE *f,int level);
886 * this structure contains the hash of a keyname and points to all
889 * exception: if the id is 'il' there are no hash values and every
892 static int _nt_dump_lf(LPSTR key_name, char *base, int subkeys, nt_lf *lf, FILE *f, int level)
896 if (lf->id == NT_REG_HASH_BLOCK_ID) {
897 if (subkeys != lf->nr_keys) goto error1;
899 for (i=0; i<lf->nr_keys; i++)
900 if (!_nt_dump_nk(key_name, base, (nt_nk*)(base+lf->hash_rec[i].off_nk+4), f, level)) goto error;
901 } else if (lf->id == NT_REG_NOHASH_BLOCK_ID) {
902 nt_li * li = (nt_li*)lf;
903 if (subkeys != li->nr_keys) goto error1;
905 for (i=0; i<li->nr_keys; i++)
906 if (!_nt_dump_nk(key_name, base, (nt_nk*)(base+li->off_nk[i]+4), f, level)) goto error;
907 } else if (lf->id == NT_REG_RI_BLOCK_ID) { /* ri */
908 nt_ri * ri = (nt_ri*)lf;
911 /* count all subkeys */
912 for (i=0; i<ri->nr_li; i++) {
913 nt_li * li = (nt_li*)(base+ri->off_li[i]+4);
914 if(li->id != NT_REG_NOHASH_BLOCK_ID) goto error2;
915 li_subkeys += li->nr_keys;
919 if (subkeys != li_subkeys) goto error1;
921 /* loop through the keys */
922 for (i=0; i<ri->nr_li; i++) {
923 nt_li *li = (nt_li*)(base+ri->off_li[i]+4);
924 if (!_nt_dump_lf(key_name, base, li->nr_keys, (nt_lf*)li, f, level)) goto error;
931 if (lf->id == 0x686c)
932 FIXME("unknown Win XP node id 0x686c: do we need to add support for it ?\n");
934 ERR("unknown node id 0x%04x, please report!\n", lf->id);
938 ERR("registry file corrupt! (inconsistent number of subkeys)\n");
942 ERR("error reading lf block\n");
946 /* _nt_dump_nk [Internal] */
947 static int _nt_dump_nk(LPSTR key_name,char *base,nt_nk *nk,FILE *f,int level)
951 LPSTR new_key_name = NULL;
953 TRACE("%s\n", key_name);
955 if (nk->SubBlockId != NT_REG_KEY_BLOCK_ID) {
956 ERR("unknown node id 0x%04x, please report!\n", nk->SubBlockId);
960 if ((nk->Type!=NT_REG_ROOT_KEY_BLOCK_TYPE) && (((nt_nk*)(base+nk->parent_off+4))->SubBlockId != NT_REG_KEY_BLOCK_ID)) {
961 ERR("registry file corrupt!\n");
965 /* create the new key */
967 /* create new subkey name */
968 new_key_name = _strdupnA(key_name,strlen(key_name)+nk->name_len+1);
969 if (strcmp(new_key_name,"") != 0) strcat(new_key_name,"\\");
970 strncat(new_key_name,nk->name,nk->name_len);
972 /* write the key path (something like [Software\\Microsoft\\..]) only if:
973 1) key has some values
974 2) key has no values and no subkeys
976 if (nk->nr_values > 0) {
977 /* there are some values */
979 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
982 if ((nk->nr_subkeys == 0) && (nk->nr_values == 0)) {
983 /* no subkeys and no values */
985 _dump_strAtoW(new_key_name,strlen(new_key_name),f,"[]");
989 /* loop trough the value list */
990 vl = (DWORD *)(base+nk->valuelist_off+4);
991 for (n=0; n<nk->nr_values; n++) {
992 nt_vk * vk = (nt_vk*)(base+vl[n]+4);
993 if (!_nt_dump_vk(new_key_name, base, vk, f)) {
998 } else new_key_name = _strdupnA(key_name,strlen(key_name));
1000 /* loop through the subkeys */
1001 if (nk->nr_subkeys) {
1002 nt_lf *lf = (nt_lf*)(base+nk->lf_off+4);
1003 if (!_nt_dump_lf(new_key_name, base, nk->nr_subkeys, lf, f, level-1)) {
1015 /**********************************************************************************
1016 * _set_registry_levels [Internal]
1018 * set level to 0 for loading system files
1019 * set level to 1 for loading user files
1021 static void _set_registry_levels(int level,int saving,int period)
1023 SERVER_START_REQ( set_registry_levels )
1025 req->current = level;
1026 req->saving = saving;
1027 req->period = period;
1028 wine_server_call( req );
1033 /* _save_at_exit [Internal] */
1034 static void _save_at_exit(HKEY hkey,LPCSTR path)
1036 LPCSTR confdir = wine_get_config_dir();
1038 SERVER_START_REQ( save_registry_atexit )
1041 wine_server_add_data( req, confdir, strlen(confdir) );
1042 wine_server_add_data( req, path, strlen(path)+1 );
1043 wine_server_call( req );
1048 /* configure save files and start the periodic saving timer [Internal] */
1049 static void _init_registry_saving( HKEY hkey_users_default )
1055 all = !PROFILE_GetWineIniBool("registry","SaveOnlyUpdatedKeys",1);
1056 PROFILE_GetWineIniString( "registry", "PeriodicSave", "", buffer, sizeof(buffer) );
1057 if (buffer[0]) period = atoi(buffer);
1059 /* set saving level (0 for saving everything, 1 for saving only modified keys) */
1060 _set_registry_levels(1,!all,period*1000);
1062 if (PROFILE_GetWineIniBool("registry","WritetoHomeRegistryFiles",1))
1064 _save_at_exit(HKEY_CURRENT_USER,"/" SAVE_LOCAL_REGBRANCH_CURRENT_USER );
1065 _save_at_exit(HKEY_LOCAL_MACHINE,"/" SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE);
1066 _save_at_exit(hkey_users_default,"/" SAVE_LOCAL_REGBRANCH_USER_DEFAULT);
1071 /******************************************************************************
1072 * _allocate_default_keys [Internal]
1073 * Registry initialisation, allocates some default keys.
1075 static void _allocate_default_keys(void) {
1081 RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
1084 /* This was an Open, but since it is called before the real registries
1085 are loaded, it was changed to a Create - MTB 980507*/
1086 RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
1087 RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
1090 /* \\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion
1092 * CurrentBuildNumber
1094 * string RegisteredOwner
1095 * string RegisteredOrganization
1098 /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
1100 * string SysLocation
1103 if (-1!=gethostname(buf,200)) {
1104 RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
1105 RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
1109 RegCreateKeyA(HKEY_USERS,".Default",&hkey);
1113 #define REG_DONTLOAD -1
1118 /* return the type of native registry [Internal] */
1119 static int _get_reg_type(void)
1121 char windir[MAX_PATHNAME_LEN];
1122 char tmp[MAX_PATHNAME_LEN];
1123 int ret = REG_WIN31;
1125 GetWindowsDirectoryA(windir,MAX_PATHNAME_LEN);
1127 /* test %windir%/system32/config/system --> winnt */
1128 strcpy(tmp, windir);
1129 strncat(tmp, "\\system32\\config\\system", MAX_PATHNAME_LEN - strlen(tmp) - 1);
1130 if(GetFileAttributesA(tmp) != (DWORD)-1) {
1135 /* test %windir%/system.dat --> win95 */
1136 strcpy(tmp, windir);
1137 strncat(tmp, "\\system.dat", MAX_PATHNAME_LEN - strlen(tmp) - 1);
1138 if(GetFileAttributesA(tmp) != (DWORD)-1) {
1143 if ((ret == REG_WINNT) && (!PROFILE_GetWineIniString( "Wine", "Profile", "", tmp, MAX_PATHNAME_LEN))) {
1144 MESSAGE("When you are running with a native NT directory specify\n");
1145 MESSAGE("'Profile=<profiledirectory>' or disable loading of Windows\n");
1146 MESSAGE("registry (LoadWindowsRegistryFiles=N)\n");
1153 #define WINE_REG_VER_ERROR -1
1154 #define WINE_REG_VER_1 0
1155 #define WINE_REG_VER_2 1
1156 #define WINE_REG_VER_OLD 2
1157 #define WINE_REG_VER_UNKNOWN 3
1159 /* return the version of wine registry file [Internal] */
1160 static int _get_wine_registry_file_format_version(LPCSTR fn)
1166 if ((f=fopen(fn,"rt")) == NULL) {
1167 WARN("Couldn't open %s for reading: %s\n",fn,strerror(errno));
1168 return WINE_REG_VER_ERROR;
1171 if (fgets(tmp,50,f) == NULL) {
1172 WARN("Error reading %s: %s\n",fn,strerror(errno));
1174 return WINE_REG_VER_ERROR;
1178 if (sscanf(tmp,"WINE REGISTRY Version %d",&ver) != 1) return WINE_REG_VER_UNKNOWN;
1181 return WINE_REG_VER_1;
1184 return WINE_REG_VER_2;
1187 return WINE_REG_VER_UNKNOWN;
1191 /* load the registry file in wine format [Internal] */
1192 static void load_wine_registry(HKEY hkey,LPCSTR fn)
1196 file_format = _get_wine_registry_file_format_version(fn);
1197 switch (file_format) {
1199 case WINE_REG_VER_1:
1200 WARN("Unable to load registry file %s: old format which is no longer supported.\n",fn);
1203 case WINE_REG_VER_2: {
1205 if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
1206 FILE_ATTRIBUTE_NORMAL, 0, TRUE, DRIVE_UNKNOWN )))
1208 SERVER_START_REQ( load_registry )
1212 wine_server_call( req );
1215 CloseHandle( file );
1220 case WINE_REG_VER_UNKNOWN:
1221 WARN("Unable to load registry file %s: unknown format.\n",fn);
1224 case WINE_REG_VER_ERROR:
1229 /* generate and return the name of the tmp file and associated stream [Internal] */
1230 static LPSTR _get_tmp_fn(FILE **f)
1237 sprintf(ret,"/tmp/reg%lx%04x.tmp",(long)getpid(),count++);
1238 if ((tmp_fd = open(ret,O_CREAT | O_EXCL | O_WRONLY,0666)) != -1) break;
1239 if (errno != EEXIST) {
1240 ERR("Unexpected error while open() call: %s\n",strerror(errno));
1247 if ((*f = fdopen(tmp_fd,"w")) == NULL) {
1248 ERR("Unexpected error while fdopen() call: %s\n",strerror(errno));
1257 /* convert win95 native registry file to wine format [Internal] */
1258 static LPSTR _convert_win95_registry_to_wine_format(LPCSTR fn,int level)
1262 DOS_FULL_NAME full_name;
1269 _w95dke *dke, *root_dke;
1271 if (!DOSFS_GetFullName( fn, 0, &full_name )) return NULL;
1273 /* map the registry into the memory */
1274 if ((fd = open(full_name.long_name, O_RDONLY | O_NONBLOCK)) == -1) return NULL;
1275 if ((fstat(fd, &st) == -1)) goto error1;
1276 if (!st.st_size) goto error1;
1277 if ((base = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd, 0)) == MAP_FAILED) goto error1;
1279 /* control signature */
1280 if (*(LPDWORD)base != W95_REG_CREG_ID) {
1281 ERR("unable to load native win95 registry file %s: unknown signature.\n",fn);
1286 /* load the header (rgkn) */
1287 rgkn = (_w95rgkn*)(creg + 1);
1288 if (rgkn->id != W95_REG_RGKN_ID) {
1289 ERR("second IFF header not RGKN, but %lx\n", rgkn->id);
1292 if (rgkn->root_off != 0x20) {
1293 ERR("rgkn->root_off not 0x20, please report !\n");
1296 if (rgkn->last_dke > rgkn->size)
1298 ERR("registry file corrupt! last_dke > size!\n");
1301 /* verify last dke */
1302 dke = (_w95dke*)((char*)rgkn + rgkn->last_dke);
1303 if (dke->x1 != 0x80000000)
1305 ERR("last dke invalid !\n");
1308 if (rgkn->size > creg->rgdb_off)
1310 ERR("registry file corrupt! rgkn size > rgdb_off !\n");
1313 root_dke = (_w95dke*)((char*)rgkn + rgkn->root_off);
1314 if ( (root_dke->prevlvl != 0xffffffff) || (root_dke->next != 0xffffffff) )
1316 ERR("registry file corrupt! invalid root dke !\n");
1320 if ( (ret = _get_tmp_fn(&f)) == NULL) goto error;
1321 fprintf(f,"WINE REGISTRY Version 2");
1322 _w95_dump_dke("",creg,rgkn,root_dke,f,level);
1327 ERR("Unable to load native win95 registry file %s.\n",fn);
1328 ERR("Please report this.\n");
1329 ERR("Make a backup of the file, run a good reg cleaner program and try again!\n");
1332 munmap(base, st.st_size);
1338 /* convert winnt native registry file to wine format [Internal] */
1339 static LPSTR _convert_winnt_registry_to_wine_format(LPCSTR fn,int level)
1349 nt_hbin_sub *hbin_sub;
1354 hFile = CreateFileA( fn, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, 0 );
1355 if ( hFile == INVALID_HANDLE_VALUE ) return NULL;
1356 hMapping = CreateFileMappingA( hFile, NULL, PAGE_READONLY|SEC_COMMIT, 0, 0, NULL );
1357 if (!hMapping) goto error1;
1358 base = MapViewOfFile( hMapping, FILE_MAP_READ, 0, 0, 0 );
1359 CloseHandle( hMapping );
1360 if (!base) goto error1;
1362 /* control signature */
1363 if (*(LPDWORD)base != NT_REG_HEADER_BLOCK_ID) {
1364 ERR("unable to load native winnt registry file %s: unknown signature.\n",fn);
1372 hbin = (nt_hbin*)((char*) base + 0x1000);
1373 if (hbin->id != NT_REG_POOL_BLOCK_ID) {
1374 ERR( "hbin block invalid\n");
1378 /* hbin_sub block */
1379 hbin_sub = (nt_hbin_sub*)&(hbin->hbin_sub);
1380 if ((hbin_sub->data[0] != 'n') || (hbin_sub->data[1] != 'k')) {
1381 ERR( "hbin_sub block invalid\n");
1386 nk = (nt_nk*)&(hbin_sub->data[0]);
1387 if (nk->Type != NT_REG_ROOT_KEY_BLOCK_TYPE) {
1388 ERR( "special nk block not found\n");
1392 if ( (ret = _get_tmp_fn(&f)) == NULL) goto error;
1393 fprintf(f,"WINE REGISTRY Version 2");
1394 _nt_dump_nk("",(char*)base+0x1000,nk,f,level);
1398 UnmapViewOfFile( base );
1404 /* convert native registry to wine format and load it via server call [Internal] */
1405 static void _convert_and_load_native_registry(LPCSTR fn,HKEY hkey,int reg_type,int level)
1411 /* FIXME: following function doesn't really convert yet */
1412 tmp = _convert_winnt_registry_to_wine_format(fn,level);
1415 tmp = _convert_win95_registry_to_wine_format(fn,level);
1418 ERR("Don't know how to convert native 3.1 registry yet.\n");
1421 ERR("Unknown registry format parameter (%d)\n",reg_type);
1426 load_wine_registry(hkey,tmp);
1427 TRACE("File %s successfully converted to %s and loaded to registry.\n",fn,tmp);
1430 else WARN("Unable to convert %s (doesn't exist?)\n",fn);
1434 /* load all native windows registry files [Internal] */
1435 static void _load_windows_registry( HKEY hkey_users_default )
1438 char windir[MAX_PATHNAME_LEN];
1439 char path[MAX_PATHNAME_LEN];
1441 GetWindowsDirectoryA(windir,MAX_PATHNAME_LEN);
1443 reg_type = _get_reg_type();
1448 /* user specific ntuser.dat */
1449 if (PROFILE_GetWineIniString( "Wine", "Profile", "", path, MAX_PATHNAME_LEN)) {
1450 strcat(path,"\\ntuser.dat");
1451 _convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WINNT,1);
1454 /* default user.dat */
1455 if (hkey_users_default) {
1456 strcpy(path,windir);
1457 strcat(path,"\\system32\\config\\default");
1458 _convert_and_load_native_registry(path,hkey_users_default,REG_WINNT,1);
1463 * map HLM\System\ControlSet001 to HLM\System\CurrentControlSet
1466 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SYSTEM", &hkey)) {
1467 strcpy(path,windir);
1468 strcat(path,"\\system32\\config\\system");
1469 _convert_and_load_native_registry(path,hkey,REG_WINNT,1);
1473 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE, "SOFTWARE", &hkey)) {
1474 strcpy(path,windir);
1475 strcat(path,"\\system32\\config\\software");
1476 _convert_and_load_native_registry(path,hkey,REG_WINNT,1);
1480 strcpy(path,windir);
1481 strcat(path,"\\system32\\config\\sam");
1482 _convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WINNT,0);
1484 strcpy(path,windir);
1485 strcat(path,"\\system32\\config\\security");
1486 _convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WINNT,0);
1488 /* this key is generated when the nt-core booted successfully */
1489 if (!RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\Clone",&hkey)) RegCloseKey(hkey);
1494 _convert_and_load_native_registry("c:\\system.1st",HKEY_LOCAL_MACHINE,REG_WIN95,0);
1496 strcpy(path,windir);
1497 strcat(path,"\\system.dat");
1498 _convert_and_load_native_registry(path,HKEY_LOCAL_MACHINE,REG_WIN95,0);
1500 strcpy(path,windir);
1501 strcat(path,"\\classes.dat");
1502 _convert_and_load_native_registry(path,HKEY_CLASSES_ROOT,REG_WIN95,0);
1504 if (PROFILE_GetWineIniString("Wine","Profile","",path,MAX_PATHNAME_LEN)) {
1505 /* user specific user.dat */
1506 strncat(path, "\\user.dat", MAX_PATHNAME_LEN - strlen(path) - 1);
1507 _convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WIN95,1);
1509 /* default user.dat */
1510 if (hkey_users_default) {
1511 strcpy(path,windir);
1512 strcat(path,"\\user.dat");
1513 _convert_and_load_native_registry(path,hkey_users_default,REG_WIN95,1);
1516 strcpy(path,windir);
1517 strcat(path,"\\user.dat");
1518 _convert_and_load_native_registry(path,HKEY_CURRENT_USER,REG_WIN95,1);
1523 /* FIXME: here we should convert to *.reg file supported by server and call REQ_LOAD_REGISTRY, see REG_WIN95 case */
1528 TRACE("REG_DONTLOAD\n");
1532 ERR("switch: no match (%d)\n",reg_type);
1538 /* load global registry files (stored in /etc/wine) [Internal] */
1539 static void _load_global_registry(void)
1543 /* Load the global HKU hive directly from sysconfdir */
1544 load_wine_registry( HKEY_USERS, SAVE_GLOBAL_REGBRANCH_USER_DEFAULT );
1546 /* Load the global machine defaults directly from sysconfdir */
1547 load_wine_registry( HKEY_LOCAL_MACHINE, SAVE_GLOBAL_REGBRANCH_LOCAL_MACHINE );
1550 /* load home registry files (stored in ~/.wine) [Internal] */
1551 static void _load_home_registry( HKEY hkey_users_default )
1553 LPCSTR confdir = wine_get_config_dir();
1554 LPSTR tmp = _xmalloc(strlen(confdir)+20);
1556 strcpy(tmp,confdir);
1557 strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_USER_DEFAULT);
1558 load_wine_registry(hkey_users_default,tmp);
1560 strcpy(tmp,confdir);
1561 strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_CURRENT_USER);
1562 load_wine_registry(HKEY_CURRENT_USER,tmp);
1564 strcpy(tmp,confdir);
1565 strcat(tmp,"/" SAVE_LOCAL_REGBRANCH_LOCAL_MACHINE);
1566 load_wine_registry(HKEY_LOCAL_MACHINE,tmp);
1571 /* load all registry (native and global and home) */
1572 void SHELL_LoadRegistry( void )
1574 HKEY hkey_users_default;
1578 if (!CLIENT_IsBootThread()) return; /* already loaded */
1580 if (RegCreateKeyA(HKEY_USERS,".Default",&hkey_users_default))
1582 ERR("Cannot create HKEY_USERS/.Default\n" );
1586 _allocate_default_keys();
1587 _set_registry_levels(0,0,0);
1588 if (PROFILE_GetWineIniBool("Registry","LoadWindowsRegistryFiles",1))
1589 _load_windows_registry( hkey_users_default );
1590 if (PROFILE_GetWineIniBool("Registry","LoadGlobalRegistryFiles",1))
1591 _load_global_registry();
1592 _set_registry_levels(1,0,0);
1593 if (PROFILE_GetWineIniBool("Registry","LoadHomeRegistryFiles",1))
1594 _load_home_registry( hkey_users_default );
1595 _init_registry_saving( hkey_users_default );
1596 RegCloseKey(hkey_users_default);
1599 /***************************************************************************/
1601 /***************************************************************************/
1603 /******************************************************************************
1604 * RegFlushKey [ADVAPI32.@]
1605 * Immediately writes key to registry.
1606 * Only returns after data has been written to disk.
1608 * FIXME: does it really wait until data is written ?
1611 * hkey [I] Handle of key to write
1614 * Success: ERROR_SUCCESS
1615 * Failure: Error code
1617 DWORD WINAPI RegFlushKey( HKEY hkey )
1619 FIXME( "(%x): stub\n", hkey );
1620 return ERROR_SUCCESS;
1624 /******************************************************************************
1625 * RegUnLoadKeyA [ADVAPI32.@]
1627 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1629 FIXME("(%x,%s): stub\n",hkey, debugstr_a(lpSubKey));
1630 return ERROR_SUCCESS;
1634 /******************************************************************************
1635 * RegReplaceKeyA [ADVAPI32.@]
1637 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
1640 FIXME("(%x,%s,%s,%s): stub\n", hkey, debugstr_a(lpSubKey),
1641 debugstr_a(lpNewFile),debugstr_a(lpOldFile));
1642 return ERROR_SUCCESS;
1650 /* 16-bit functions */
1652 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
1653 * some programs. Do not remove those cases. -MM
1655 static inline void fix_win16_hkey( HKEY *hkey )
1657 if (*hkey == 0 || *hkey == (HKEY)1) *hkey = HKEY_CLASSES_ROOT;
1660 /******************************************************************************
1661 * RegEnumKey [KERNEL.216]
1662 * RegEnumKey [SHELL.7]
1664 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
1666 fix_win16_hkey( &hkey );
1667 return RegEnumKeyA( hkey, index, name, name_len );
1670 /******************************************************************************
1671 * RegOpenKey [KERNEL.217]
1672 * RegOpenKey [SHELL.1]
1674 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1676 fix_win16_hkey( &hkey );
1677 return RegOpenKeyA( hkey, name, retkey );
1680 /******************************************************************************
1681 * RegCreateKey [KERNEL.218]
1682 * RegCreateKey [SHELL.2]
1684 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1686 fix_win16_hkey( &hkey );
1687 return RegCreateKeyA( hkey, name, retkey );
1690 /******************************************************************************
1691 * RegDeleteKey [KERNEL.219]
1692 * RegDeleteKey [SHELL.4]
1694 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
1696 fix_win16_hkey( &hkey );
1697 return RegDeleteKeyA( hkey, name );
1700 /******************************************************************************
1701 * RegCloseKey [KERNEL.220]
1702 * RegCloseKey [SHELL.3]
1704 DWORD WINAPI RegCloseKey16( HKEY hkey )
1706 fix_win16_hkey( &hkey );
1707 return RegCloseKey( hkey );
1710 /******************************************************************************
1711 * RegSetValue [KERNEL.221]
1712 * RegSetValue [SHELL.5]
1714 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
1716 fix_win16_hkey( &hkey );
1717 return RegSetValueA( hkey, name, type, data, count );
1720 /******************************************************************************
1721 * RegDeleteValue [KERNEL.222]
1723 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
1725 fix_win16_hkey( &hkey );
1726 return RegDeleteValueA( hkey, name );
1729 /******************************************************************************
1730 * RegEnumValue [KERNEL.223]
1732 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
1733 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
1735 fix_win16_hkey( &hkey );
1736 return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
1739 /******************************************************************************
1740 * RegQueryValue [KERNEL.224]
1741 * RegQueryValue [SHELL.6]
1744 * Is this HACK still applicable?
1747 * The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
1748 * mask out the high 16 bit. This (not so much incidently) hopefully fixes
1751 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
1753 fix_win16_hkey( &hkey );
1754 if (count) *count &= 0xffff;
1755 return RegQueryValueA( hkey, name, data, count );
1758 /******************************************************************************
1759 * RegQueryValueEx [KERNEL.225]
1761 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
1762 LPBYTE data, LPDWORD count )
1764 fix_win16_hkey( &hkey );
1765 return RegQueryValueExA( hkey, name, reserved, type, data, count );
1768 /******************************************************************************
1769 * RegSetValueEx [KERNEL.226]
1771 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
1772 CONST BYTE *data, DWORD count )
1774 fix_win16_hkey( &hkey );
1775 if (!count && (type==REG_SZ)) count = strlen(data);
1776 return RegSetValueExA( hkey, name, reserved, type, data, count );
1779 /******************************************************************************
1780 * RegFlushKey [KERNEL.227]
1782 DWORD WINAPI RegFlushKey16( HKEY hkey )
1784 fix_win16_hkey( &hkey );
1785 return RegFlushKey( hkey );