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 #ifdef HAVE_SYS_ERRNO_H
30 #include <sys/errno.h>
32 #include <sys/types.h>
33 #include <sys/fcntl.h>
39 #include "wine/winbase16.h"
40 #include "wine/winestring.h"
44 #include "debugtools.h"
48 #include "winversion.h"
51 DEFAULT_DEBUG_CHANNEL(reg)
53 static void REGISTRY_Init(void);
54 /* FIXME: following defines should be configured global ... */
56 /* NOTE: do not append a /. linux' mkdir() WILL FAIL if you do that */
57 #define WINE_PREFIX "/.wine"
58 #define SAVE_USERS_DEFAULT ETCDIR"/wine.userreg"
59 #define SAVE_LOCAL_MACHINE_DEFAULT ETCDIR"/wine.systemreg"
61 /* relative in ~user/.wine/ : */
62 #define SAVE_CURRENT_USER "user.reg"
63 #define SAVE_LOCAL_USERS_DEFAULT "wine.userreg"
64 #define SAVE_LOCAL_MACHINE "system.reg"
66 #define KEY_REGISTRY "Software\\The WINE team\\WINE\\Registry"
67 #define VAL_SAVEUPDATED "SaveOnlyUpdatedKeys"
70 /* what valuetypes do we need to convert? */
71 #define UNICONVMASK ((1<<REG_SZ)|(1<<REG_MULTI_SZ)|(1<<REG_EXPAND_SZ))
77 * Are these doing the same as HEAP_strdupAtoW and HEAP_strdupWtoA?
78 * If so, can we remove them?
80 * No, the memory handling functions are called very often in here,
81 * just replacing them by HeapAlloc(SystemHeap,...) makes registry
82 * loading 100 times slower. -MM
84 static LPWSTR strdupA2W(LPCSTR src)
87 LPWSTR dest=xmalloc(2*strlen(src)+2);
88 lstrcpyAtoW(dest,src);
94 LPWSTR strcvtA2W(LPCSTR src, int nchars)
97 LPWSTR dest = xmalloc (2 * nchars + 2);
99 lstrcpynAtoW(dest,src,nchars+1);
106 /******************************************************************************
107 * REGISTRY_Init [Internal]
108 * Registry initialisation, allocates some default keys.
110 static void REGISTRY_Init(void) {
114 TRACE_(reg)("(void)\n");
116 RegCreateKeyA(HKEY_DYN_DATA,"PerfStats\\StatData",&hkey);
119 /* This was an Open, but since it is called before the real registries
120 are loaded, it was changed to a Create - MTB 980507*/
121 RegCreateKeyA(HKEY_LOCAL_MACHINE,"HARDWARE\\DESCRIPTION\\System",&hkey);
122 RegSetValueExA(hkey,"Identifier",0,REG_SZ,"SystemType WINE",strlen("SystemType WINE"));
125 /* \\SOFTWARE\\Microsoft\\Window NT\\CurrentVersion
129 * string RegisteredOwner
130 * string RegisteredOrganization
133 /* System\\CurrentControlSet\\Services\\SNMP\\Parameters\\RFC1156Agent
138 if (-1!=gethostname(buf,200)) {
139 RegCreateKeyA(HKEY_LOCAL_MACHINE,"System\\CurrentControlSet\\Control\\ComputerName\\ComputerName",&hkey);
140 RegSetValueExA(hkey,"ComputerName",0,REG_SZ,buf,strlen(buf)+1);
146 /************************ SAVE Registry Function ****************************/
148 #define REGISTRY_SAVE_VERSION 0x00000001
150 /* Registry saveformat:
151 * If you change it, increase above number by 1, which will flush
152 * old registry database files.
155 * "WINE REGISTRY Version %d"
159 * valuename=lastmodified,type,data
163 * keyname,valuename,stringdata:
164 * the usual ascii characters from 0x00-0xff (well, not 0x00)
165 * and \uXXXX as UNICODE value XXXX with XXXX>0xff
166 * ( "=\\\t" escaped in \uXXXX form.)
170 * FIXME: doesn't save 'class' (what does it mean anyway?), nor flags.
172 * [HKEY_CURRENT_USER\\Software\\The WINE team\\WINE\\Registry]
173 * SaveOnlyUpdatedKeys=yes
176 /* Same as RegSaveKey but with Unix pathnames */
177 static void save_key( HKEY hkey, const char *filename )
179 struct save_registry_request *req = get_req_buffer();
184 char *name = xmalloc( strlen(filename) + 10 );
185 char *p = strrchr( name, '/' );
191 sprintf( p, "reg%04x.tmp", count++ );
192 handle = FILE_CreateFile( name, GENERIC_WRITE, 0, NULL,
193 CREATE_NEW, FILE_ATTRIBUTE_NORMAL, -1 );
194 if (handle != INVALID_HANDLE_VALUE) break;
195 if ((ret = GetLastError()) != ERROR_FILE_EXISTS) return;
200 ret = server_call_noerr( REQ_SAVE_REGISTRY );
201 CloseHandle( handle );
202 if (ret) unlink( name );
203 else if (rename( name, filename ) == -1)
205 ERR( "Failed to move %s to %s: ", name, filename );
212 /******************************************************************************
213 * SHELL_SaveRegistryBranch [Internal]
215 * Saves main registry branch specified by hkey.
217 static void SHELL_SaveRegistryBranch(HKEY hkey)
221 /* Find out what to save to, get from config file */
222 BOOL writeToHome = PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1);
223 BOOL writeToAlt = PROFILE_GetWineIniBool("registry","WritetoAltRegistries",1);
225 /* FIXME: does this check apply to all keys written below ? */
226 if (!(home = getenv( "HOME" )))
227 ERR_(reg)("Failed to get homedirectory of UID %ld.\n",(long) getuid());
229 /* HKEY_LOCAL_MACHINE contains the HKEY_CLASSES_ROOT branch */
230 if (hkey == HKEY_CLASSES_ROOT) hkey = HKEY_LOCAL_MACHINE;
234 case HKEY_CURRENT_USER:
235 fn = xmalloc( MAX_PATHNAME_LEN );
236 if (writeToAlt && PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
237 fn, MAX_PATHNAME_LEN - 1))
238 save_key( HKEY_CURRENT_USER, fn );
241 if (home && writeToHome)
243 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
244 strlen(SAVE_CURRENT_USER) + 2 );
246 strcat(fn,WINE_PREFIX);
248 /* create the directory. don't care about errorcodes. */
249 mkdir(fn,0755); /* drwxr-xr-x */
250 strcat(fn,"/"SAVE_CURRENT_USER);
251 save_key( HKEY_CURRENT_USER, fn );
255 case HKEY_LOCAL_MACHINE:
256 /* Try first saving according to the defined location in .winerc */
257 fn = xmalloc ( MAX_PATHNAME_LEN);
258 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltLocalMachineFile", "",
259 fn, MAX_PATHNAME_LEN - 1))
260 save_key( HKEY_LOCAL_MACHINE, fn );
263 if (home && writeToHome)
265 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
266 strlen(SAVE_LOCAL_MACHINE) + 2);
268 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
269 save_key( HKEY_LOCAL_MACHINE, fn );
274 fn = xmalloc( MAX_PATHNAME_LEN );
275 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltUserFile", "",
276 fn, MAX_PATHNAME_LEN - 1))
277 save_key( HKEY_USERS, fn );
280 if (home && writeToHome)
282 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
283 strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
285 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
286 save_key( HKEY_USERS, fn );
291 ERR_(reg)("unknown/invalid key handle !\n");
297 /******************************************************************************
298 * SHELL_SaveRegistry [Internal]
300 void SHELL_SaveRegistry( void )
302 struct set_registry_levels_request *req = get_req_buffer();
307 TRACE_(reg)("(void)\n");
310 if (RegOpenKeyA(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS)
319 if ((ERROR_SUCCESS!=RegQueryValueExA( hkey,
324 &len)) || (type!=REG_SZ))
331 if (lstrcmpiA(buf,"yes")) all = 1;
333 /* set saving level (0 for saving everything, 1 for saving only modified keys) */
336 server_call( REQ_SET_REGISTRY_LEVELS );
338 SHELL_SaveRegistryBranch(HKEY_CURRENT_USER);
339 SHELL_SaveRegistryBranch(HKEY_LOCAL_MACHINE);
340 SHELL_SaveRegistryBranch(HKEY_USERS);
344 /************************ LOAD Registry Function ****************************/
348 /******************************************************************************
349 * _find_or_add_key [Internal]
351 static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
354 if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
355 if (keyname) free( keyname );
359 /******************************************************************************
360 * _find_or_add_value [Internal]
362 static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len )
364 RegSetValueExW( hkey, name, 0, type, data, len );
365 if (name) free( name );
366 if (data) free( data );
370 /******************************************************************************
371 * _wine_read_line [Internal]
373 * reads a line including dynamically enlarging the readbuffer and throwing
376 static int _wine_read_line( FILE *F, char **buf, int *len )
386 s=fgets(curread,mylen,F);
389 if (NULL==(s=strchr(curread,'\n'))) {
390 /* buffer wasn't large enough */
391 curoff = strlen(*buf);
392 *buf = xrealloc(*buf,*len*2);
393 curread = *buf + curoff;
394 mylen = *len; /* we filled up the buffer and
395 * got new '*len' bytes to fill
403 /* throw away comments */
404 if (**buf=='#' || **buf==';') {
409 if (s) /* got end of line */
416 /******************************************************************************
417 * _wine_read_USTRING [Internal]
419 * converts a char* into a UNICODE string (up to a special char)
420 * and returns the position exactly after that string
422 static char* _wine_read_USTRING( char *buf, LPWSTR *str )
427 /* read up to "=" or "\0" or "\n" */
429 *str = (LPWSTR)xmalloc(2*strlen(buf)+2);
431 while (*s && (*s!='\n') && (*s!='=')) {
433 *ws++=*((unsigned char*)s++);
437 /* Dangling \ ... may only happen if a registry
438 * write was short. FIXME: What do to?
448 WARN_(reg)("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
456 memcpy(xbuf,s,4);xbuf[4]='\0';
457 if (!sscanf(xbuf,"%x",&wc))
458 WARN_(reg)("Strange escape sequence %s found in |%s|\n",xbuf,buf);
460 *ws++ =(unsigned short)wc;
469 /******************************************************************************
470 * _wine_loadsubkey [Internal]
473 * It seems like this is returning a boolean. Should it?
479 static int _wine_loadsubkey( FILE *F, HKEY hkey, int level, char **buf, int *buflen )
486 TRACE_(reg)("(%p,%x,%d,%s,%d)\n", F, hkey, level, debugstr_a(*buf), *buflen);
488 /* Good. We already got a line here ... so parse it */
498 WARN_(reg)("Got a subhierarchy without resp. key?\n");
501 if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen))
502 if (!_wine_read_line(F,buf,buflen))
507 /* let the caller handle this line */
508 if (i<level || **buf=='\0')
511 /* it can be: a value or a keyname. Parse the name first */
512 s=_wine_read_USTRING(s,&name);
514 /* switch() default: hack to avoid gotos */
518 if (subkey) RegCloseKey( subkey );
519 subkey=_find_or_add_key(hkey,name);
522 int len,lastmodified,type;
525 WARN_(reg)("Unexpected character: %c\n",*s);
529 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
530 WARN_(reg)("Haven't understood possible value in |%s|, skipping.\n",*buf);
537 WARN_(reg)("Haven't understood possible value in |%s|, skipping.\n",*buf);
540 if (type == REG_SZ || type == REG_EXPAND_SZ) {
541 s=_wine_read_USTRING(s,(LPWSTR*)&data);
542 len = lstrlenW((LPWSTR)data)*2+2;
545 data = (LPBYTE)xmalloc(len+1);
546 for (i=0;i<len;i++) {
548 if (*s>='0' && *s<='9')
550 if (*s>='a' && *s<='f')
551 data[i]=(*s-'a'+'\xa')<<4;
552 if (*s>='A' && *s<='F')
553 data[i]=(*s-'A'+'\xa')<<4;
555 if (*s>='0' && *s<='9')
557 if (*s>='a' && *s<='f')
558 data[i]|=*s-'a'+'\xa';
559 if (*s>='A' && *s<='F')
560 data[i]|=*s-'A'+'\xa';
564 _find_or_add_value(hkey,name,type,data,len);
567 /* read the next line */
568 if (!_wine_read_line(F,buf,buflen))
572 if (subkey) RegCloseKey( subkey );
577 /******************************************************************************
578 * _wine_loadsubreg [Internal]
580 static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
586 buf=xmalloc(10);buflen=10;
587 if (!_wine_read_line(F,&buf,&buflen)) {
591 if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
595 if (ver!=REGISTRY_SAVE_VERSION) {
596 if (ver == 2) /* new version */
599 if ((file = FILE_CreateFile( fn, GENERIC_READ, 0, NULL, OPEN_EXISTING,
600 FILE_ATTRIBUTE_NORMAL, -1 )) != INVALID_HANDLE_VALUE)
602 struct load_registry_request *req = get_req_buffer();
606 server_call( REQ_LOAD_REGISTRY );
614 TRACE_(reg)("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
619 if (!_wine_read_line(F,&buf,&buflen)) {
623 if (!_wine_loadsubkey(F,hkey,0,&buf,&buflen)) {
632 /******************************************************************************
633 * _wine_loadreg [Internal]
635 static void _wine_loadreg( HKEY hkey, char *fn )
639 TRACE_(reg)("(%x,%s)\n",hkey,debugstr_a(fn));
643 WARN_(reg)("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
646 _wine_loadsubreg(F,hkey,fn);
650 /******************************************************************************
651 * _flush_registry [Internal]
653 * This function allow to flush section of the internal registry. It is mainly
654 * implements to fix a problem with the global HKU and the local HKU.
655 * Those two files are read to build the HKU\.Default branch to finaly copy
656 * this branch onto HKCU hive, once this is done, if we keep the HKU hive as is,
657 * all the global HKU are saved onto the user's personal version of HKU hive.
661 static void _flush_registry( HKEY hkey )
663 WCHAR name[MAX_PATH];
668 /* FIXME: we assume that deleting a key will move the other ones up, */
669 /* so that we can always use index 0 until there are no more keys */
670 if (RegEnumKeyW( hkey, 0, name, sizeof(name) ) != ERROR_SUCCESS) break;
671 if (RegOpenKeyW( hkey, name, &subkey ) != ERROR_SUCCESS) break;
672 _flush_registry( subkey );
673 if (RegDeleteKeyW( subkey, NULL ) != ERROR_SUCCESS) break;
674 RegCloseKey( subkey );
679 /******************************************************************************
680 * _copy_registry [Internal]
682 static void _copy_registry( HKEY from, HKEY to )
687 DWORD type, name_len, len;
688 static WCHAR name[MAX_PATH];
689 static BYTE data[2048];
696 name_len = sizeof(name);
697 if (RegEnumValueW( from, index++, name, &name_len,
698 NULL, &type, data, &len ) != ERROR_SUCCESS) break;
699 RegSetValueW( to, name, type, (LPCWSTR)data, len );
706 name_len = sizeof(name);
707 if (RegEnumKeyExW( from, index++, name, &name_len,
708 NULL, NULL, 0, &ft ) != ERROR_SUCCESS)
710 if (RegOpenKeyW( from, name, &subkey ) == ERROR_SUCCESS)
713 if (RegCreateKeyW( to, name, &newsub ) == ERROR_SUCCESS)
715 _copy_registry( subkey, newsub );
716 RegCloseKey( newsub );
718 RegCloseKey( subkey );
724 /* WINDOWS 95 REGISTRY LOADER */
726 * Structure of a win95 registry database.
730 * 8 : DWORD offset_of_RGDB_part
731 * 0C..0F: ? (someone fill in please)
732 * 10: WORD number of RGDB blocks
734 * 14: WORD always 0000?
735 * 16: WORD always 0001?
736 * 18..1F: ? (someone fill in please)
741 * 4 : DWORD offset to first RGDB section
742 * 8 : DWORD offset to the root record
743 * C..0x1B: ? (fill in)
744 * 0x20 ... offset_of_RGDB_part: Disk Key Entry structures
746 * Disk Key Entry Structure:
747 * 00: DWORD - Free entry indicator(?)
748 * 04: DWORD - Hash = sum of bytes of keyname
749 * 08: DWORD - Root key indicator? unknown, but usually 0xFFFFFFFF on win95 systems
750 * 0C: DWORD - disk address of PreviousLevel Key.
751 * 10: DWORD - disk address of Next Sublevel Key.
752 * 14: DWORD - disk address of Next Key (on same level).
753 * DKEP>18: WORD - Nr, Low Significant part.
754 * 1A: WORD - Nr, High Significant part.
756 * The disk address always points to the nr part of the previous key entry
757 * of the referenced key. Don't ask me why, or even if I got this correct
758 * from staring at 1kg of hexdumps. (DKEP)
760 * The High significant part of the structure seems to equal the number
761 * of the RGDB section. The low significant part is a unique ID within
764 * There are two minor corrections to the position of that structure.
765 * 1. If the address is xxx014 or xxx018 it will be aligned to xxx01c AND
766 * the DKE reread from there.
767 * 2. If the address is xxxFFx it will be aligned to (xxx+1)000.
768 * CPS - I have not experienced the above phenomenon in my registry files
772 * 04: DWORD offset to next RGDB section
774 * 0C: WORD always 000d?
775 * 0E: WORD RGDB block number
776 * 10: DWORD ? (equals value at offset 4 - value at offset 8)
781 * 00: DWORD nextkeyoffset - offset to the next disk key structure
782 * 08: WORD nrLS - low significant part of NR
783 * 0A: WORD nrHS - high significant part of NR
784 * 0C: DWORD bytesused - bytes used in this structure.
785 * 10: WORD name_len - length of name in bytes. without \0
786 * 12: WORD nr_of_values - number of values.
787 * 14: char name[name_len] - name string. No \0.
788 * 14+name_len: disk values
789 * nextkeyoffset: ... next disk key
792 * 00: DWORD type - value type (hmm, could be WORD too)
793 * 04: DWORD - unknown, usually 0
794 * 08: WORD namelen - length of Name. 0 means name=NULL
795 * 0C: WORD datalen - length of Data.
796 * 10: char name[namelen] - name, no \0
797 * 10+namelen: BYTE data[datalen] - data, without \0 if string
798 * 10+namelen+datalen: next values or disk key
800 * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
801 * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
802 * structure) and reading another RGDB_section.
803 * repeat until end of file.
805 * An interesting relationship exists in RGDB_section. The value at offset
806 * 10 equals the value at offset 4 minus the value at offset 8. I have no
807 * idea at the moment what this means. (Kevin Cozens)
809 * FIXME: this description needs some serious help, yes.
812 struct _w95keyvalue {
814 unsigned short datalen;
824 struct _w95keyvalue *values;
825 struct _w95key *prevlvl;
826 struct _w95key *nextsub;
827 struct _w95key *next;
841 /******************************************************************************
842 * _w95_processKey [Internal]
844 static HKEY _w95_processKey ( HKEY hkey, int nrLS, int nrMS, struct _w95_info *info )
847 /* Disk Key Header structure (RGDB part) */
849 unsigned long nextkeyoff;
852 unsigned long bytesused;
853 unsigned short keynamelen;
854 unsigned short values;
857 /* disk key values or nothing */
859 /* Disk Key Value structure */
863 unsigned short valnamelen;
864 unsigned short valdatalen;
865 /* valname, valdata */
871 char *rgdbdata = info->rgdbbuffer;
872 int nbytes = info->rgdbsize;
873 char *curdata = rgdbdata;
874 char *end = rgdbdata + nbytes;
876 char *next = rgdbdata;
882 if (strncmp(curdata, "RGDB", 4)) return 0;
884 memcpy(&off_next_rgdb,curdata+4,4);
885 next = curdata + off_next_rgdb;
886 nrgdb = (int) *((short *)curdata + 7);
888 } while (nrgdb != nrMS && (next < end));
890 /* curdata now points to the start of the right RGDB section */
893 #define XREAD(whereto,len) \
894 if ((curdata + len) <= end) {\
895 memcpy(whereto,curdata,len);\
900 while (curdata < next) {
901 struct dkh *xdkh = (struct dkh*)curdata;
903 bytesread += sizeof(dkh); /* FIXME... nextkeyoff? */
904 if (xdkh->nrLS == nrLS) {
905 memcpy(&dkh,xdkh,sizeof(dkh));
906 curdata += sizeof(dkh);
909 curdata += xdkh->nextkeyoff;
912 if (dkh.nrLS != nrLS) return 0;
914 if (nrgdb != dkh.nrMS)
917 assert((dkh.keynamelen<2) || curdata[0]);
918 subkey=_find_or_add_key(hkey,strcvtA2W(curdata, dkh.keynamelen));
919 curdata += dkh.keynamelen;
921 for (i=0;i< dkh.values; i++) {
927 XREAD(&dkv,sizeof(dkv));
929 name = strcvtA2W(curdata, dkv.valnamelen);
930 curdata += dkv.valnamelen;
932 if ((1 << dkv.type) & UNICONVMASK) {
933 data = (LPBYTE) strcvtA2W(curdata, dkv.valdatalen);
934 len = 2*(dkv.valdatalen + 1);
936 /* I don't think we want to NULL terminate all data */
937 data = xmalloc(dkv.valdatalen);
938 memcpy (data, curdata, dkv.valdatalen);
939 len = dkv.valdatalen;
942 curdata += dkv.valdatalen;
944 _find_or_add_value( subkey, name, dkv.type, data, len );
949 /******************************************************************************
950 * _w95_walkrgkn [Internal]
952 static void _w95_walkrgkn( HKEY prevkey, char *off,
953 struct _w95_info *info )
956 /* Disk Key Entry structure (RGKN part) */
960 unsigned long x3;/*usually 0xFFFFFFFF */
961 unsigned long prevlvl;
962 unsigned long nextsub;
966 } *dke = (struct dke *)off;
970 dke = (struct dke *) ((char *)info->rgknbuffer);
973 subkey = _w95_processKey(prevkey, dke->nrLS, dke->nrMS, info);
975 if (dke->nextsub != -1 &&
976 ((dke->nextsub - 0x20) < info->rgknsize)
977 && (dke->nextsub > 0x20)) {
979 _w95_walkrgkn(subkey ? subkey : prevkey, /* XXX <-- This is a hack*/
980 info->rgknbuffer + dke->nextsub - 0x20,
983 if (subkey) RegCloseKey( subkey );
985 if (dke->next != -1 &&
986 ((dke->next - 0x20) < info->rgknsize) &&
987 (dke->next > 0x20)) {
988 _w95_walkrgkn(prevkey,
989 info->rgknbuffer + dke->next - 0x20,
995 /******************************************************************************
996 * _w95_loadreg [Internal]
998 static void _w95_loadreg( char* fn, HKEY hkey )
1002 unsigned long where,version,rgdbsection,end;
1003 struct _w95_info info;
1005 BY_HANDLE_FILE_INFORMATION hfdinfo;
1007 TRACE_(reg)("Loading Win95 registry database '%s'\n",fn);
1008 hfd=OpenFile(fn,&ofs,OF_READ);
1009 if (hfd==HFILE_ERROR)
1012 if (4!=_lread(hfd,magic,4))
1014 if (strcmp(magic,"CREG")) {
1015 WARN_(reg)("%s is not a w95 registry.\n",fn);
1018 if (4!=_lread(hfd,&version,4))
1020 if (4!=_lread(hfd,&rgdbsection,4))
1022 if (-1==_llseek(hfd,0x20,SEEK_SET))
1024 if (4!=_lread(hfd,magic,4))
1026 if (strcmp(magic,"RGKN")) {
1027 WARN_(reg)("second IFF header not RGKN, but %s\n", magic);
1031 /* STEP 1: Keylink structures */
1032 if (-1==_llseek(hfd,0x40,SEEK_SET))
1037 info.rgknsize = end - where;
1038 info.rgknbuffer = (char*)xmalloc(info.rgknsize);
1039 if (info.rgknsize != _lread(hfd,info.rgknbuffer,info.rgknsize))
1042 if (!GetFileInformationByHandle(hfd,&hfdinfo))
1045 end = hfdinfo.nFileSizeLow;
1046 info.lastmodified = DOSFS_FileTimeToUnixTime(&hfdinfo.ftLastWriteTime,NULL);
1048 if (-1==_llseek(hfd,rgdbsection,SEEK_SET))
1051 info.rgdbbuffer = (char*)xmalloc(end-rgdbsection);
1052 info.rgdbsize = end - rgdbsection;
1054 if (info.rgdbsize !=_lread(hfd,info.rgdbbuffer,info.rgdbsize))
1058 _w95_walkrgkn(hkey, NULL, &info);
1060 free (info.rgdbbuffer);
1061 free (info.rgknbuffer);
1065 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1068 reghack - windows 3.11 registry data format demo program.
1070 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1071 a combined hash table and tree description, and finally a text table.
1073 The header is obvious from the struct header. The taboff1 and taboff2
1074 fields are always 0x20, and their usage is unknown.
1076 The 8-byte entry table has various entry types.
1078 tabent[0] is a root index. The second word has the index of the root of
1080 tabent[1..hashsize] is a hash table. The first word in the hash entry is
1081 the index of the key/value that has that hash. Data with the same
1082 hash value are on a circular list. The other three words in the
1083 hash entry are always zero.
1084 tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1085 entry: dirent and keyent/valent. They are identified by context.
1086 tabent[freeidx] is the first free entry. The first word in a free entry
1087 is the index of the next free entry. The last has 0 as a link.
1088 The other three words in the free list are probably irrelevant.
1090 Entries in text table are preceeded by a word at offset-2. This word
1091 has the value (2*index)+1, where index is the referring keyent/valent
1092 entry in the table. I have no suggestion for the 2* and the +1.
1093 Following the word, there are N bytes of data, as per the keyent/valent
1094 entry length. The offset of the keyent/valent entry is from the start
1095 of the text table to the first data byte.
1097 This information is not available from Microsoft. The data format is
1098 deduced from the reg.dat file by me. Mistakes may
1099 have been made. I claim no rights and give no guarantees for this program.
1101 Tor Sjøwall, tor@sn.no
1104 /* reg.dat header format */
1105 struct _w31_header {
1106 char cookie[8]; /* 'SHCC3.10' */
1107 unsigned long taboff1; /* offset of hash table (??) = 0x20 */
1108 unsigned long taboff2; /* offset of index table (??) = 0x20 */
1109 unsigned long tabcnt; /* number of entries in index table */
1110 unsigned long textoff; /* offset of text part */
1111 unsigned long textsize; /* byte size of text part */
1112 unsigned short hashsize; /* hash size */
1113 unsigned short freeidx; /* free index */
1116 /* generic format of table entries */
1117 struct _w31_tabent {
1118 unsigned short w0, w1, w2, w3;
1121 /* directory tabent: */
1122 struct _w31_dirent {
1123 unsigned short sibling_idx; /* table index of sibling dirent */
1124 unsigned short child_idx; /* table index of child dirent */
1125 unsigned short key_idx; /* table index of key keyent */
1126 unsigned short value_idx; /* table index of value valent */
1130 struct _w31_keyent {
1131 unsigned short hash_idx; /* hash chain index for string */
1132 unsigned short refcnt; /* reference count */
1133 unsigned short length; /* length of string */
1134 unsigned short string_off; /* offset of string in text table */
1138 struct _w31_valent {
1139 unsigned short hash_idx; /* hash chain index for string */
1140 unsigned short refcnt; /* reference count */
1141 unsigned short length; /* length of string */
1142 unsigned short string_off; /* offset of string in text table */
1145 /* recursive helper function to display a directory tree */
1147 __w31_dumptree( unsigned short idx,
1149 struct _w31_tabent *tab,
1150 struct _w31_header *head,
1152 time_t lastmodified,
1155 struct _w31_dirent *dir;
1156 struct _w31_keyent *key;
1157 struct _w31_valent *val;
1159 static char tail[400];
1162 dir=(struct _w31_dirent*)&tab[idx];
1165 key = (struct _w31_keyent*)&tab[dir->key_idx];
1167 memcpy(tail,&txt[key->string_off],key->length);
1168 tail[key->length]='\0';
1169 /* all toplevel entries AND the entries in the
1170 * toplevel subdirectory belong to \SOFTWARE\Classes
1172 if (!level && !lstrcmpA(tail,".classes")) {
1173 __w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
1174 idx=dir->sibling_idx;
1177 if (subkey) RegCloseKey( subkey );
1178 if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
1179 /* only add if leaf node or valued node */
1180 if (dir->value_idx!=0||dir->child_idx==0) {
1181 if (dir->value_idx) {
1182 val=(struct _w31_valent*)&tab[dir->value_idx];
1183 memcpy(tail,&txt[val->string_off],val->length);
1184 tail[val->length]='\0';
1185 RegSetValueA( subkey, NULL, REG_SZ, tail, 0 );
1189 TRACE_(reg)("strange: no directory key name, idx=%04x\n", idx);
1191 __w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
1192 idx=dir->sibling_idx;
1194 if (subkey) RegCloseKey( subkey );
1198 /******************************************************************************
1199 * _w31_loadreg [Internal]
1201 void _w31_loadreg(void) {
1203 struct _w31_header head;
1204 struct _w31_tabent *tab;
1208 BY_HANDLE_FILE_INFORMATION hfinfo;
1209 time_t lastmodified;
1211 TRACE_(reg)("(void)\n");
1213 hf = OpenFile("reg.dat",&ofs,OF_READ);
1214 if (hf==HFILE_ERROR)
1217 /* read & dump header */
1218 if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
1219 ERR_(reg)("reg.dat is too short.\n");
1223 if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1224 ERR_(reg)("reg.dat has bad signature.\n");
1229 len = head.tabcnt * sizeof(struct _w31_tabent);
1230 /* read and dump index table */
1232 if (len!=_lread(hf,tab,len)) {
1233 ERR_(reg)("couldn't read %d bytes.\n",len);
1240 txt = xmalloc(head.textsize);
1241 if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
1242 ERR_(reg)("couldn't seek to textblock.\n");
1248 if (head.textsize!=_lread(hf,txt,head.textsize)) {
1249 ERR_(reg)("textblock too short (%d instead of %ld).\n",len,head.textsize);
1256 if (!GetFileInformationByHandle(hf,&hfinfo)) {
1257 ERR_(reg)("GetFileInformationByHandle failed?.\n");
1263 lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1264 __w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
1272 /**********************************************************************************
1273 * SHELL_LoadRegistry [Internal]
1275 void SHELL_LoadRegistry( void )
1277 struct set_registry_levels_request *req = get_req_buffer();
1281 TRACE_(reg)("(void)\n");
1285 /* set level to 0 for loading system files */
1288 server_call( REQ_SET_REGISTRY_LEVELS );
1290 if (PROFILE_GetWineIniBool ("registry", "LoadWindowsRegistryFiles", 1))
1292 /* Load windows 3.1 entries */
1294 /* Load windows 95 entries */
1295 _w95_loadreg("C:\\system.1st", HKEY_LOCAL_MACHINE);
1296 _w95_loadreg("system.dat", HKEY_LOCAL_MACHINE);
1297 _w95_loadreg("user.dat", HKEY_USERS);
1300 if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
1303 * Load the global HKU hive directly from sysconfdir
1305 _wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT );
1308 * Load the global machine defaults directly form sysconfdir
1310 _wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
1313 /* set level to 1 for loading user files */
1316 server_call( REQ_SET_REGISTRY_LEVELS );
1319 * Load the user saved registries
1321 if (!(home = getenv( "HOME" )))
1322 WARN_(reg)("Failed to get homedirectory of UID %ld.\n",(long) getuid());
1323 else if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1))
1326 * Load user's personal versions of global HKU/.Default keys
1328 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX) +
1329 strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
1331 strcat(fn, WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1332 _wine_loadreg( HKEY_USERS, fn );
1335 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) + strlen(SAVE_CURRENT_USER)+2);
1337 strcat(fn, WINE_PREFIX"/"SAVE_CURRENT_USER);
1338 _wine_loadreg( HKEY_CURRENT_USER, fn );
1342 * Load HKLM, attempt to get the registry location from the config
1343 * file first, if exist, load and keep going.
1345 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_MACHINE)+2);
1347 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1348 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1353 * Load HKCU, get the registry location from the config
1354 * file, if exist, load and keep going.
1356 if (PROFILE_GetWineIniBool ( "registry", "LoadAltRegistryFiles", 1))
1358 fn = xmalloc( MAX_PATHNAME_LEN );
1359 if ( PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
1360 fn, MAX_PATHNAME_LEN - 1))
1362 _wine_loadreg( HKEY_CURRENT_USER, fn );
1366 * Load HKU, get the registry location from the config
1367 * file, if exist, load and keep going.
1369 fn = xmalloc ( MAX_PATHNAME_LEN );
1370 if ( PROFILE_GetWineIniString ( "registry", "AltUserFile", "",
1371 fn, MAX_PATHNAME_LEN - 1))
1373 _wine_loadreg( HKEY_USERS, fn );
1377 * Load HKLM, get the registry location from the config
1378 * file, if exist, load and keep going.
1380 fn = xmalloc ( MAX_PATHNAME_LEN );
1381 if (PROFILE_GetWineIniString ( "registry", "AltLocalMachineFile", "",
1382 fn, MAX_PATHNAME_LEN - 1))
1384 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1390 * Obtain the handle of the HKU\.Default key.
1391 * in order to copy HKU\.Default\* onto HKEY_CURRENT_USER
1393 if (RegCreateKeyA(HKEY_USERS,".Default",&hkey) != ERROR_SUCCESS)
1394 WARN_(reg)("Could not create global user default key\n");
1396 _copy_registry( hkey, HKEY_CURRENT_USER );
1400 * Since HKU is built from the global HKU and the local user HKU file we must
1401 * flush the HKU tree we have built at this point otherwise the part brought
1402 * in from the global HKU is saved into the local HKU. To avoid this
1403 * useless dupplication of HKU keys we reread the local HKU key.
1406 /* Allways flush the HKU hive and reload it only with user's personal HKU */
1407 _flush_registry( HKEY_USERS );
1409 /* Reload user's local HKU hive */
1410 if (home && PROFILE_GetWineIniBool ("registry","LoadHomeRegistryFiles",1))
1412 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX)
1413 + strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
1416 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1418 _wine_loadreg( HKEY_USERS, fn );
1424 * Make sure the update mode is there
1426 if (ERROR_SUCCESS==RegCreateKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey))
1428 DWORD junk,type,len;
1432 if (( RegQueryValueExA(
1438 &len) != ERROR_SUCCESS) || (type != REG_SZ))
1440 RegSetValueExA(hkey,VAL_SAVEUPDATED,0,REG_SZ,"yes",4);
1448 /********************* API FUNCTIONS ***************************************/
1453 /******************************************************************************
1454 * RegFlushKey [KERNEL.227] [ADVAPI32.143]
1455 * Immediately writes key to registry.
1456 * Only returns after data has been written to disk.
1458 * FIXME: does it really wait until data is written ?
1461 * hkey [I] Handle of key to write
1464 * Success: ERROR_SUCCESS
1465 * Failure: Error code
1467 DWORD WINAPI RegFlushKey( HKEY hkey )
1469 FIXME( "(%x): stub\n", hkey );
1470 return ERROR_SUCCESS;
1473 /******************************************************************************
1474 * RegConnectRegistry32W [ADVAPI32.128]
1477 * lpMachineName [I] Address of name of remote computer
1478 * hHey [I] Predefined registry handle
1479 * phkResult [I] Address of buffer for remote registry handle
1481 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey,
1484 TRACE_(reg)("(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
1486 if (!lpMachineName || !*lpMachineName) {
1487 /* Use the local machine name */
1488 return RegOpenKey16( hKey, "", phkResult );
1491 FIXME_(reg)("Cannot connect to %s\n",debugstr_w(lpMachineName));
1492 return ERROR_BAD_NETPATH;
1496 /******************************************************************************
1497 * RegConnectRegistry32A [ADVAPI32.127]
1499 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, LPHKEY reskey )
1502 LPWSTR machineW = strdupA2W(machine);
1503 ret = RegConnectRegistryW( machineW, hkey, reskey );
1509 /******************************************************************************
1510 * RegGetKeySecurity [ADVAPI32.144]
1511 * Retrieves a copy of security descriptor protecting the registry key
1514 * hkey [I] Open handle of key to set
1515 * SecurityInformation [I] Descriptor contents
1516 * pSecurityDescriptor [O] Address of descriptor for key
1517 * lpcbSecurityDescriptor [I/O] Address of size of buffer and description
1520 * Success: ERROR_SUCCESS
1521 * Failure: Error code
1523 LONG WINAPI RegGetKeySecurity( HKEY hkey,
1524 SECURITY_INFORMATION SecurityInformation,
1525 PSECURITY_DESCRIPTOR pSecurityDescriptor,
1526 LPDWORD lpcbSecurityDescriptor )
1528 TRACE_(reg)("(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
1529 lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1531 /* FIXME: Check for valid SecurityInformation values */
1533 if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
1534 return ERROR_INSUFFICIENT_BUFFER;
1536 FIXME_(reg)("(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
1537 pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1539 return ERROR_SUCCESS;
1543 /******************************************************************************
1544 * RegNotifyChangeKeyValue [ADVAPI32.???]
1547 * hkey [I] Handle of key to watch
1548 * fWatchSubTree [I] Flag for subkey notification
1549 * fdwNotifyFilter [I] Changes to be reported
1550 * hEvent [I] Handle of signaled event
1551 * fAsync [I] Flag for asynchronous reporting
1553 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree,
1554 DWORD fdwNotifyFilter, HANDLE hEvent,
1557 FIXME_(reg)("(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter,
1559 return ERROR_SUCCESS;
1563 /******************************************************************************
1564 * RegUnLoadKey32W [ADVAPI32.173]
1567 * hkey [I] Handle of open key
1568 * lpSubKey [I] Address of name of subkey to unload
1570 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
1572 FIXME_(reg)("(%x,%s): stub\n",hkey, debugstr_w(lpSubKey));
1573 return ERROR_SUCCESS;
1577 /******************************************************************************
1578 * RegUnLoadKey32A [ADVAPI32.172]
1580 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1583 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1584 ret = RegUnLoadKeyW( hkey, lpSubKeyW );
1585 if(lpSubKeyW) free(lpSubKeyW);
1590 /******************************************************************************
1591 * RegSetKeySecurity [ADVAPI32.167]
1594 * hkey [I] Open handle of key to set
1595 * SecurityInfo [I] Descriptor contents
1596 * pSecurityDesc [I] Address of descriptor for key
1598 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
1599 PSECURITY_DESCRIPTOR pSecurityDesc )
1601 TRACE_(reg)("(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
1603 /* It seems to perform this check before the hkey check */
1604 if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
1605 (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
1606 (SecurityInfo & DACL_SECURITY_INFORMATION) ||
1607 (SecurityInfo & SACL_SECURITY_INFORMATION)) {
1610 return ERROR_INVALID_PARAMETER;
1613 return ERROR_INVALID_PARAMETER;
1615 FIXME_(reg)(":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
1617 return ERROR_SUCCESS;
1621 /******************************************************************************
1622 * RegRestoreKey32W [ADVAPI32.164]
1625 * hkey [I] Handle of key where restore begins
1626 * lpFile [I] Address of filename containing saved tree
1627 * dwFlags [I] Optional flags
1629 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
1631 TRACE_(reg)("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
1633 /* It seems to do this check before the hkey check */
1634 if (!lpFile || !*lpFile)
1635 return ERROR_INVALID_PARAMETER;
1637 FIXME_(reg)("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
1639 /* Check for file existence */
1641 return ERROR_SUCCESS;
1645 /******************************************************************************
1646 * RegRestoreKey32A [ADVAPI32.163]
1648 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
1651 LPWSTR lpFileW = strdupA2W(lpFile);
1652 ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
1653 if(lpFileW) free(lpFileW);
1658 /******************************************************************************
1659 * RegReplaceKey32W [ADVAPI32.162]
1662 * hkey [I] Handle of open key
1663 * lpSubKey [I] Address of name of subkey
1664 * lpNewFile [I] Address of filename for file with new data
1665 * lpOldFile [I] Address of filename for backup file
1667 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
1670 FIXME_(reg)("(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey),
1671 debugstr_w(lpNewFile),debugstr_w(lpOldFile));
1672 return ERROR_SUCCESS;
1676 /******************************************************************************
1677 * RegReplaceKey32A [ADVAPI32.161]
1679 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
1683 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1684 LPWSTR lpNewFileW = strdupA2W(lpNewFile);
1685 LPWSTR lpOldFileW = strdupA2W(lpOldFile);
1686 ret = RegReplaceKeyW( hkey, lpSubKeyW, lpNewFileW, lpOldFileW );
1698 /* 16-bit functions */
1700 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
1701 * some programs. Do not remove those cases. -MM
1703 static inline void fix_win16_hkey( HKEY *hkey )
1705 if (*hkey == 0 || *hkey == 1) *hkey = HKEY_CLASSES_ROOT;
1708 /******************************************************************************
1709 * RegEnumKey16 [KERNEL.216] [SHELL.7]
1711 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
1713 fix_win16_hkey( &hkey );
1714 return RegEnumKeyA( hkey, index, name, name_len );
1717 /******************************************************************************
1718 * RegOpenKey16 [KERNEL.217] [SHELL.1]
1720 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1722 fix_win16_hkey( &hkey );
1723 return RegOpenKeyA( hkey, name, retkey );
1726 /******************************************************************************
1727 * RegCreateKey16 [KERNEL.218] [SHELL.2]
1729 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1731 fix_win16_hkey( &hkey );
1732 return RegCreateKeyA( hkey, name, retkey );
1735 /******************************************************************************
1736 * RegDeleteKey16 [KERNEL.219] [SHELL.4]
1738 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
1740 fix_win16_hkey( &hkey );
1741 return RegDeleteKeyA( hkey, name );
1744 /******************************************************************************
1745 * RegCloseKey16 [KERNEL.220] [SHELL.3]
1747 DWORD WINAPI RegCloseKey16( HKEY hkey )
1749 fix_win16_hkey( &hkey );
1750 return RegCloseKey( hkey );
1753 /******************************************************************************
1754 * RegSetValue16 [KERNEL.221] [SHELL.5]
1756 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
1758 fix_win16_hkey( &hkey );
1759 return RegSetValueA( hkey, name, type, data, count );
1762 /******************************************************************************
1763 * RegDeleteValue16 [KERNEL.222]
1765 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
1767 fix_win16_hkey( &hkey );
1768 return RegDeleteValueA( hkey, name );
1771 /******************************************************************************
1772 * RegEnumValue16 [KERNEL.223]
1774 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
1775 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
1777 fix_win16_hkey( &hkey );
1778 return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
1781 /******************************************************************************
1782 * RegQueryValue16 [KERNEL.224] [SHELL.6]
1785 * Is this HACK still applicable?
1788 * The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
1789 * mask out the high 16 bit. This (not so much incidently) hopefully fixes
1792 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
1794 fix_win16_hkey( &hkey );
1795 if (count) *count &= 0xffff;
1796 return RegQueryValueA( hkey, name, data, count );
1799 /******************************************************************************
1800 * RegQueryValueEx16 [KERNEL.225]
1802 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
1803 LPBYTE data, LPDWORD count )
1805 fix_win16_hkey( &hkey );
1806 return RegQueryValueExA( hkey, name, reserved, type, data, count );
1809 /******************************************************************************
1810 * RegSetValueEx16 [KERNEL.226]
1812 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
1813 CONST BYTE *data, DWORD count )
1815 fix_win16_hkey( &hkey );
1816 return RegSetValueExA( hkey, name, reserved, type, data, count );