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
178 /******************************************************************************
179 * SHELL_SaveRegistryBranch [Internal]
181 * Saves main registry branch specified by hkey.
183 static void SHELL_SaveRegistryBranch(HKEY hkey)
187 /* Find out what to save to, get from config file */
188 BOOL writeToHome = PROFILE_GetWineIniBool("registry","WritetoHomeRegistries",1);
189 BOOL writeToAlt = PROFILE_GetWineIniBool("registry","WritetoAltRegistries",1);
191 /* FIXME: does this check apply to all keys written below ? */
192 if (!(home = getenv( "HOME" )))
193 ERR_(reg)("Failed to get homedirectory of UID %ld.\n",(long) getuid());
195 /* HKEY_LOCAL_MACHINE contains the HKEY_CLASSES_ROOT branch */
196 if (hkey == HKEY_CLASSES_ROOT) hkey = HKEY_LOCAL_MACHINE;
200 case HKEY_CURRENT_USER:
201 fn = xmalloc( MAX_PATHNAME_LEN );
202 if (writeToAlt && PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
203 fn, MAX_PATHNAME_LEN - 1))
204 RegSaveKeyA( HKEY_CURRENT_USER, fn, NULL );
207 if (home && writeToHome)
209 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
210 strlen(SAVE_CURRENT_USER) + 2 );
212 strcat(fn,WINE_PREFIX);
214 /* create the directory. don't care about errorcodes. */
215 mkdir(fn,0755); /* drwxr-xr-x */
216 strcat(fn,"/"SAVE_CURRENT_USER);
217 RegSaveKeyA( HKEY_CURRENT_USER, fn, NULL );
221 case HKEY_LOCAL_MACHINE:
222 /* Try first saving according to the defined location in .winerc */
223 fn = xmalloc ( MAX_PATHNAME_LEN);
224 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltLocalMachineFile", "",
225 fn, MAX_PATHNAME_LEN - 1))
226 RegSaveKeyA( HKEY_LOCAL_MACHINE, fn, NULL );
229 if (home && writeToHome)
231 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
232 strlen(SAVE_LOCAL_MACHINE) + 2);
234 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
235 RegSaveKeyA( HKEY_LOCAL_MACHINE, fn, NULL );
240 fn = xmalloc( MAX_PATHNAME_LEN );
241 if (writeToAlt && PROFILE_GetWineIniString( "Registry", "AltUserFile", "",
242 fn, MAX_PATHNAME_LEN - 1))
243 RegSaveKeyA( HKEY_USERS, fn, NULL );
246 if (home && writeToHome)
248 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) +
249 strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
251 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
252 RegSaveKeyA( HKEY_USERS, fn, NULL );
257 ERR_(reg)("unknown/invalid key handle !\n");
263 /******************************************************************************
264 * SHELL_SaveRegistry [Internal]
266 void SHELL_SaveRegistry( void )
268 struct set_registry_levels_request *req = get_req_buffer();
273 TRACE_(reg)("(void)\n");
276 if (RegOpenKeyA(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey)!=ERROR_SUCCESS)
285 if ((ERROR_SUCCESS!=RegQueryValueExA( hkey,
290 &len)) || (type!=REG_SZ))
297 if (lstrcmpiA(buf,"yes")) all = 1;
299 /* set saving level (0 for saving everything, 1 for saving only modified keys) */
302 server_call( REQ_SET_REGISTRY_LEVELS );
304 SHELL_SaveRegistryBranch(HKEY_CURRENT_USER);
305 SHELL_SaveRegistryBranch(HKEY_LOCAL_MACHINE);
306 SHELL_SaveRegistryBranch(HKEY_USERS);
310 /************************ LOAD Registry Function ****************************/
314 /******************************************************************************
315 * _find_or_add_key [Internal]
317 static inline HKEY _find_or_add_key( HKEY hkey, LPWSTR keyname )
320 if (RegCreateKeyW( hkey, keyname, &subkey ) != ERROR_SUCCESS) subkey = 0;
321 if (keyname) free( keyname );
325 /******************************************************************************
326 * _find_or_add_value [Internal]
328 static void _find_or_add_value( HKEY hkey, LPWSTR name, DWORD type, LPBYTE data, DWORD len )
330 RegSetValueExW( hkey, name, 0, type, data, len );
331 if (name) free( name );
332 if (data) free( data );
336 /******************************************************************************
337 * _wine_read_line [Internal]
339 * reads a line including dynamically enlarging the readbuffer and throwing
342 static int _wine_read_line( FILE *F, char **buf, int *len )
352 s=fgets(curread,mylen,F);
355 if (NULL==(s=strchr(curread,'\n'))) {
356 /* buffer wasn't large enough */
357 curoff = strlen(*buf);
358 *buf = xrealloc(*buf,*len*2);
359 curread = *buf + curoff;
360 mylen = *len; /* we filled up the buffer and
361 * got new '*len' bytes to fill
369 /* throw away comments */
370 if (**buf=='#' || **buf==';') {
375 if (s) /* got end of line */
382 /******************************************************************************
383 * _wine_read_USTRING [Internal]
385 * converts a char* into a UNICODE string (up to a special char)
386 * and returns the position exactly after that string
388 static char* _wine_read_USTRING( char *buf, LPWSTR *str )
393 /* read up to "=" or "\0" or "\n" */
395 *str = (LPWSTR)xmalloc(2*strlen(buf)+2);
397 while (*s && (*s!='\n') && (*s!='=')) {
399 *ws++=*((unsigned char*)s++);
403 /* Dangling \ ... may only happen if a registry
404 * write was short. FIXME: What do to?
414 WARN_(reg)("Non unicode escape sequence \\%c found in |%s|\n",*s,buf);
422 memcpy(xbuf,s,4);xbuf[4]='\0';
423 if (!sscanf(xbuf,"%x",&wc))
424 WARN_(reg)("Strange escape sequence %s found in |%s|\n",xbuf,buf);
426 *ws++ =(unsigned short)wc;
435 /******************************************************************************
436 * _wine_loadsubkey [Internal]
439 * It seems like this is returning a boolean. Should it?
445 static int _wine_loadsubkey( FILE *F, HKEY hkey, int level, char **buf, int *buflen )
452 TRACE_(reg)("(%p,%x,%d,%s,%d)\n", F, hkey, level, debugstr_a(*buf), *buflen);
454 /* Good. We already got a line here ... so parse it */
464 WARN_(reg)("Got a subhierarchy without resp. key?\n");
467 if (!_wine_loadsubkey(F,subkey,level+1,buf,buflen))
468 if (!_wine_read_line(F,buf,buflen))
473 /* let the caller handle this line */
474 if (i<level || **buf=='\0')
477 /* it can be: a value or a keyname. Parse the name first */
478 s=_wine_read_USTRING(s,&name);
480 /* switch() default: hack to avoid gotos */
484 subkey=_find_or_add_key(hkey,name);
487 int len,lastmodified,type;
490 WARN_(reg)("Unexpected character: %c\n",*s);
494 if (2!=sscanf(s,"%d,%d,",&type,&lastmodified)) {
495 WARN_(reg)("Haven't understood possible value in |%s|, skipping.\n",*buf);
502 WARN_(reg)("Haven't understood possible value in |%s|, skipping.\n",*buf);
505 if (type == REG_SZ || type == REG_EXPAND_SZ) {
506 s=_wine_read_USTRING(s,(LPWSTR*)&data);
507 len = lstrlenW((LPWSTR)data)*2+2;
510 data = (LPBYTE)xmalloc(len+1);
511 for (i=0;i<len;i++) {
513 if (*s>='0' && *s<='9')
515 if (*s>='a' && *s<='f')
516 data[i]=(*s-'a'+'\xa')<<4;
517 if (*s>='A' && *s<='F')
518 data[i]=(*s-'A'+'\xa')<<4;
520 if (*s>='0' && *s<='9')
522 if (*s>='a' && *s<='f')
523 data[i]|=*s-'a'+'\xa';
524 if (*s>='A' && *s<='F')
525 data[i]|=*s-'A'+'\xa';
529 _find_or_add_value(hkey,name,type,data,len);
532 /* read the next line */
533 if (!_wine_read_line(F,buf,buflen))
540 /******************************************************************************
541 * _wine_loadsubreg [Internal]
543 static int _wine_loadsubreg( FILE *F, HKEY hkey, const char *fn )
549 buf=xmalloc(10);buflen=10;
550 if (!_wine_read_line(F,&buf,&buflen)) {
554 if (!sscanf(buf,"WINE REGISTRY Version %d",&ver)) {
558 if (ver!=REGISTRY_SAVE_VERSION) {
559 TRACE_(reg)("Old format (%d) registry found, ignoring it. (buf was %s).\n",ver,buf);
563 if (!_wine_read_line(F,&buf,&buflen)) {
567 if (!_wine_loadsubkey(F,hkey,0,&buf,&buflen)) {
576 /******************************************************************************
577 * _wine_loadreg [Internal]
579 static void _wine_loadreg( HKEY hkey, char *fn )
583 TRACE_(reg)("(%x,%s)\n",hkey,debugstr_a(fn));
587 WARN_(reg)("Couldn't open %s for reading: %s\n",fn,strerror(errno) );
590 _wine_loadsubreg(F,hkey,fn);
594 /******************************************************************************
595 * _flush_registry [Internal]
597 * This function allow to flush section of the internal registry. It is mainly
598 * implements to fix a problem with the global HKU and the local HKU.
599 * Those two files are read to build the HKU\.Default branch to finaly copy
600 * this branch onto HKCU hive, once this is done, if we keep the HKU hive as is,
601 * all the global HKU are saved onto the user's personal version of HKU hive.
605 static void _flush_registry( HKEY hkey )
607 WCHAR name[MAX_PATH];
612 /* FIXME: we assume that deleting a key will move the other ones up, */
613 /* so that we can always use index 0 until there are no more keys */
614 if (RegEnumKeyW( hkey, 0, name, sizeof(name) ) != ERROR_SUCCESS) break;
615 if (RegOpenKeyW( hkey, name, &subkey ) != ERROR_SUCCESS) break;
616 _flush_registry( subkey );
617 if (RegDeleteKeyW( subkey, NULL ) != ERROR_SUCCESS) break;
618 RegCloseKey( subkey );
623 /******************************************************************************
624 * _copy_registry [Internal]
626 static void _copy_registry( HKEY from, HKEY to )
631 DWORD type, name_len, len;
632 static WCHAR name[MAX_PATH];
633 static BYTE data[2048];
640 name_len = sizeof(name);
641 if (RegEnumValueW( from, index++, name, &name_len,
642 NULL, &type, data, &len ) != ERROR_SUCCESS) break;
643 RegSetValueW( to, name, type, (LPCWSTR)data, len );
650 name_len = sizeof(name);
651 if (RegEnumKeyExW( from, index++, name, &name_len,
652 NULL, NULL, 0, &ft ) != ERROR_SUCCESS)
654 if (RegOpenKeyW( from, name, &subkey ) == ERROR_SUCCESS)
657 if (RegCreateKeyW( to, name, &newsub ) == ERROR_SUCCESS)
659 _copy_registry( subkey, newsub );
660 RegCloseKey( newsub );
662 RegCloseKey( subkey );
668 /* WINDOWS 95 REGISTRY LOADER */
670 * Structure of a win95 registry database.
674 * 8 : DWORD offset_of_RGDB_part
675 * 0C..0F: ? (someone fill in please)
676 * 10: WORD number of RGDB blocks
678 * 14: WORD always 0000?
679 * 16: WORD always 0001?
680 * 18..1F: ? (someone fill in please)
685 * 4 : DWORD offset to first RGDB section
686 * 8 : DWORD offset to the root record
687 * C..0x1B: ? (fill in)
688 * 0x20 ... offset_of_RGDB_part: Disk Key Entry structures
690 * Disk Key Entry Structure:
691 * 00: DWORD - Free entry indicator(?)
692 * 04: DWORD - Hash = sum of bytes of keyname
693 * 08: DWORD - Root key indicator? unknown, but usually 0xFFFFFFFF on win95 systems
694 * 0C: DWORD - disk address of PreviousLevel Key.
695 * 10: DWORD - disk address of Next Sublevel Key.
696 * 14: DWORD - disk address of Next Key (on same level).
697 * DKEP>18: WORD - Nr, Low Significant part.
698 * 1A: WORD - Nr, High Significant part.
700 * The disk address always points to the nr part of the previous key entry
701 * of the referenced key. Don't ask me why, or even if I got this correct
702 * from staring at 1kg of hexdumps. (DKEP)
704 * The High significant part of the structure seems to equal the number
705 * of the RGDB section. The low significant part is a unique ID within
708 * There are two minor corrections to the position of that structure.
709 * 1. If the address is xxx014 or xxx018 it will be aligned to xxx01c AND
710 * the DKE reread from there.
711 * 2. If the address is xxxFFx it will be aligned to (xxx+1)000.
712 * CPS - I have not experienced the above phenomenon in my registry files
716 * 04: DWORD offset to next RGDB section
718 * 0C: WORD always 000d?
719 * 0E: WORD RGDB block number
720 * 10: DWORD ? (equals value at offset 4 - value at offset 8)
725 * 00: DWORD nextkeyoffset - offset to the next disk key structure
726 * 08: WORD nrLS - low significant part of NR
727 * 0A: WORD nrHS - high significant part of NR
728 * 0C: DWORD bytesused - bytes used in this structure.
729 * 10: WORD name_len - length of name in bytes. without \0
730 * 12: WORD nr_of_values - number of values.
731 * 14: char name[name_len] - name string. No \0.
732 * 14+name_len: disk values
733 * nextkeyoffset: ... next disk key
736 * 00: DWORD type - value type (hmm, could be WORD too)
737 * 04: DWORD - unknown, usually 0
738 * 08: WORD namelen - length of Name. 0 means name=NULL
739 * 0C: WORD datalen - length of Data.
740 * 10: char name[namelen] - name, no \0
741 * 10+namelen: BYTE data[datalen] - data, without \0 if string
742 * 10+namelen+datalen: next values or disk key
744 * Disk keys are layed out flat ... But, sometimes, nrLS and nrHS are both
745 * 0xFFFF, which means skipping over nextkeyoffset bytes (including this
746 * structure) and reading another RGDB_section.
747 * repeat until end of file.
749 * An interesting relationship exists in RGDB_section. The value at offset
750 * 10 equals the value at offset 4 minus the value at offset 8. I have no
751 * idea at the moment what this means. (Kevin Cozens)
753 * FIXME: this description needs some serious help, yes.
756 struct _w95keyvalue {
758 unsigned short datalen;
768 struct _w95keyvalue *values;
769 struct _w95key *prevlvl;
770 struct _w95key *nextsub;
771 struct _w95key *next;
785 /******************************************************************************
786 * _w95_processKey [Internal]
788 static HKEY _w95_processKey ( HKEY hkey, int nrLS, int nrMS, struct _w95_info *info )
791 /* Disk Key Header structure (RGDB part) */
793 unsigned long nextkeyoff;
796 unsigned long bytesused;
797 unsigned short keynamelen;
798 unsigned short values;
801 /* disk key values or nothing */
803 /* Disk Key Value structure */
807 unsigned short valnamelen;
808 unsigned short valdatalen;
809 /* valname, valdata */
815 char *rgdbdata = info->rgdbbuffer;
816 int nbytes = info->rgdbsize;
817 char *curdata = rgdbdata;
818 char *end = rgdbdata + nbytes;
820 char *next = rgdbdata;
826 if (strncmp(curdata, "RGDB", 4)) return 0;
828 memcpy(&off_next_rgdb,curdata+4,4);
829 next = curdata + off_next_rgdb;
830 nrgdb = (int) *((short *)curdata + 7);
832 } while (nrgdb != nrMS && (next < end));
834 /* curdata now points to the start of the right RGDB section */
837 #define XREAD(whereto,len) \
838 if ((curdata + len) <= end) {\
839 memcpy(whereto,curdata,len);\
844 while (curdata < next) {
845 struct dkh *xdkh = (struct dkh*)curdata;
847 bytesread += sizeof(dkh); /* FIXME... nextkeyoff? */
848 if (xdkh->nrLS == nrLS) {
849 memcpy(&dkh,xdkh,sizeof(dkh));
850 curdata += sizeof(dkh);
853 curdata += xdkh->nextkeyoff;
856 if (dkh.nrLS != nrLS) return 0;
858 if (nrgdb != dkh.nrMS)
861 assert((dkh.keynamelen<2) || curdata[0]);
862 subkey=_find_or_add_key(hkey,strcvtA2W(curdata, dkh.keynamelen));
863 curdata += dkh.keynamelen;
865 for (i=0;i< dkh.values; i++) {
871 XREAD(&dkv,sizeof(dkv));
873 name = strcvtA2W(curdata, dkv.valnamelen);
874 curdata += dkv.valnamelen;
876 if ((1 << dkv.type) & UNICONVMASK) {
877 data = (LPBYTE) strcvtA2W(curdata, dkv.valdatalen);
878 len = 2*(dkv.valdatalen + 1);
880 /* I don't think we want to NULL terminate all data */
881 data = xmalloc(dkv.valdatalen);
882 memcpy (data, curdata, dkv.valdatalen);
883 len = dkv.valdatalen;
886 curdata += dkv.valdatalen;
888 _find_or_add_value( subkey, name, dkv.type, data, len );
893 /******************************************************************************
894 * _w95_walkrgkn [Internal]
896 static void _w95_walkrgkn( HKEY prevkey, char *off,
897 struct _w95_info *info )
900 /* Disk Key Entry structure (RGKN part) */
904 unsigned long x3;/*usually 0xFFFFFFFF */
905 unsigned long prevlvl;
906 unsigned long nextsub;
910 } *dke = (struct dke *)off;
914 dke = (struct dke *) ((char *)info->rgknbuffer);
917 subkey = _w95_processKey(prevkey, dke->nrLS, dke->nrMS, info);
918 /* XXX <-- This is a hack*/
919 if (!subkey) subkey = prevkey;
921 if (dke->nextsub != -1 &&
922 ((dke->nextsub - 0x20) < info->rgknsize)
923 && (dke->nextsub > 0x20)) {
925 _w95_walkrgkn(subkey,
926 info->rgknbuffer + dke->nextsub - 0x20,
930 if (dke->next != -1 &&
931 ((dke->next - 0x20) < info->rgknsize) &&
932 (dke->next > 0x20)) {
933 _w95_walkrgkn(prevkey,
934 info->rgknbuffer + dke->next - 0x20,
940 /******************************************************************************
941 * _w95_loadreg [Internal]
943 static void _w95_loadreg( char* fn, HKEY hkey )
947 unsigned long where,version,rgdbsection,end;
948 struct _w95_info info;
950 BY_HANDLE_FILE_INFORMATION hfdinfo;
952 TRACE_(reg)("Loading Win95 registry database '%s'\n",fn);
953 hfd=OpenFile(fn,&ofs,OF_READ);
954 if (hfd==HFILE_ERROR)
957 if (4!=_lread(hfd,magic,4))
959 if (strcmp(magic,"CREG")) {
960 WARN_(reg)("%s is not a w95 registry.\n",fn);
963 if (4!=_lread(hfd,&version,4))
965 if (4!=_lread(hfd,&rgdbsection,4))
967 if (-1==_llseek(hfd,0x20,SEEK_SET))
969 if (4!=_lread(hfd,magic,4))
971 if (strcmp(magic,"RGKN")) {
972 WARN_(reg)("second IFF header not RGKN, but %s\n", magic);
976 /* STEP 1: Keylink structures */
977 if (-1==_llseek(hfd,0x40,SEEK_SET))
982 info.rgknsize = end - where;
983 info.rgknbuffer = (char*)xmalloc(info.rgknsize);
984 if (info.rgknsize != _lread(hfd,info.rgknbuffer,info.rgknsize))
987 if (!GetFileInformationByHandle(hfd,&hfdinfo))
990 end = hfdinfo.nFileSizeLow;
991 info.lastmodified = DOSFS_FileTimeToUnixTime(&hfdinfo.ftLastWriteTime,NULL);
993 if (-1==_llseek(hfd,rgdbsection,SEEK_SET))
996 info.rgdbbuffer = (char*)xmalloc(end-rgdbsection);
997 info.rgdbsize = end - rgdbsection;
999 if (info.rgdbsize !=_lread(hfd,info.rgdbbuffer,info.rgdbsize))
1003 _w95_walkrgkn(hkey, NULL, &info);
1005 free (info.rgdbbuffer);
1006 free (info.rgknbuffer);
1010 /* WINDOWS 31 REGISTRY LOADER, supplied by Tor Sjøwall, tor@sn.no */
1013 reghack - windows 3.11 registry data format demo program.
1015 The reg.dat file has 3 parts, a header, a table of 8-byte entries that is
1016 a combined hash table and tree description, and finally a text table.
1018 The header is obvious from the struct header. The taboff1 and taboff2
1019 fields are always 0x20, and their usage is unknown.
1021 The 8-byte entry table has various entry types.
1023 tabent[0] is a root index. The second word has the index of the root of
1025 tabent[1..hashsize] is a hash table. The first word in the hash entry is
1026 the index of the key/value that has that hash. Data with the same
1027 hash value are on a circular list. The other three words in the
1028 hash entry are always zero.
1029 tabent[hashsize..tabcnt] is the tree structure. There are two kinds of
1030 entry: dirent and keyent/valent. They are identified by context.
1031 tabent[freeidx] is the first free entry. The first word in a free entry
1032 is the index of the next free entry. The last has 0 as a link.
1033 The other three words in the free list are probably irrelevant.
1035 Entries in text table are preceeded by a word at offset-2. This word
1036 has the value (2*index)+1, where index is the referring keyent/valent
1037 entry in the table. I have no suggestion for the 2* and the +1.
1038 Following the word, there are N bytes of data, as per the keyent/valent
1039 entry length. The offset of the keyent/valent entry is from the start
1040 of the text table to the first data byte.
1042 This information is not available from Microsoft. The data format is
1043 deduced from the reg.dat file by me. Mistakes may
1044 have been made. I claim no rights and give no guarantees for this program.
1046 Tor Sjøwall, tor@sn.no
1049 /* reg.dat header format */
1050 struct _w31_header {
1051 char cookie[8]; /* 'SHCC3.10' */
1052 unsigned long taboff1; /* offset of hash table (??) = 0x20 */
1053 unsigned long taboff2; /* offset of index table (??) = 0x20 */
1054 unsigned long tabcnt; /* number of entries in index table */
1055 unsigned long textoff; /* offset of text part */
1056 unsigned long textsize; /* byte size of text part */
1057 unsigned short hashsize; /* hash size */
1058 unsigned short freeidx; /* free index */
1061 /* generic format of table entries */
1062 struct _w31_tabent {
1063 unsigned short w0, w1, w2, w3;
1066 /* directory tabent: */
1067 struct _w31_dirent {
1068 unsigned short sibling_idx; /* table index of sibling dirent */
1069 unsigned short child_idx; /* table index of child dirent */
1070 unsigned short key_idx; /* table index of key keyent */
1071 unsigned short value_idx; /* table index of value valent */
1075 struct _w31_keyent {
1076 unsigned short hash_idx; /* hash chain index for string */
1077 unsigned short refcnt; /* reference count */
1078 unsigned short length; /* length of string */
1079 unsigned short string_off; /* offset of string in text table */
1083 struct _w31_valent {
1084 unsigned short hash_idx; /* hash chain index for string */
1085 unsigned short refcnt; /* reference count */
1086 unsigned short length; /* length of string */
1087 unsigned short string_off; /* offset of string in text table */
1090 /* recursive helper function to display a directory tree */
1092 __w31_dumptree( unsigned short idx,
1094 struct _w31_tabent *tab,
1095 struct _w31_header *head,
1097 time_t lastmodified,
1100 struct _w31_dirent *dir;
1101 struct _w31_keyent *key;
1102 struct _w31_valent *val;
1104 static char tail[400];
1107 dir=(struct _w31_dirent*)&tab[idx];
1110 key = (struct _w31_keyent*)&tab[dir->key_idx];
1112 memcpy(tail,&txt[key->string_off],key->length);
1113 tail[key->length]='\0';
1114 /* all toplevel entries AND the entries in the
1115 * toplevel subdirectory belong to \SOFTWARE\Classes
1117 if (!level && !lstrcmpA(tail,".classes")) {
1118 __w31_dumptree(dir->child_idx,txt,tab,head,hkey,lastmodified,level+1);
1119 idx=dir->sibling_idx;
1122 if (RegCreateKeyA( hkey, tail, &subkey ) != ERROR_SUCCESS) subkey = 0;
1123 /* only add if leaf node or valued node */
1124 if (dir->value_idx!=0||dir->child_idx==0) {
1125 if (dir->value_idx) {
1126 val=(struct _w31_valent*)&tab[dir->value_idx];
1127 memcpy(tail,&txt[val->string_off],val->length);
1128 tail[val->length]='\0';
1129 RegSetValueA( hkey, NULL, REG_SZ, tail, 0 );
1133 TRACE_(reg)("strange: no directory key name, idx=%04x\n", idx);
1135 __w31_dumptree(dir->child_idx,txt,tab,head,subkey,lastmodified,level+1);
1136 idx=dir->sibling_idx;
1141 /******************************************************************************
1142 * _w31_loadreg [Internal]
1144 void _w31_loadreg(void) {
1146 struct _w31_header head;
1147 struct _w31_tabent *tab;
1151 BY_HANDLE_FILE_INFORMATION hfinfo;
1152 time_t lastmodified;
1154 TRACE_(reg)("(void)\n");
1156 hf = OpenFile("reg.dat",&ofs,OF_READ);
1157 if (hf==HFILE_ERROR)
1160 /* read & dump header */
1161 if (sizeof(head)!=_lread(hf,&head,sizeof(head))) {
1162 ERR_(reg)("reg.dat is too short.\n");
1166 if (memcmp(head.cookie, "SHCC3.10", sizeof(head.cookie))!=0) {
1167 ERR_(reg)("reg.dat has bad signature.\n");
1172 len = head.tabcnt * sizeof(struct _w31_tabent);
1173 /* read and dump index table */
1175 if (len!=_lread(hf,tab,len)) {
1176 ERR_(reg)("couldn't read %d bytes.\n",len);
1183 txt = xmalloc(head.textsize);
1184 if (-1==_llseek(hf,head.textoff,SEEK_SET)) {
1185 ERR_(reg)("couldn't seek to textblock.\n");
1191 if (head.textsize!=_lread(hf,txt,head.textsize)) {
1192 ERR_(reg)("textblock too short (%d instead of %ld).\n",len,head.textsize);
1199 if (!GetFileInformationByHandle(hf,&hfinfo)) {
1200 ERR_(reg)("GetFileInformationByHandle failed?.\n");
1206 lastmodified = DOSFS_FileTimeToUnixTime(&hfinfo.ftLastWriteTime,NULL);
1207 __w31_dumptree(tab[0].w1,txt,tab,&head,HKEY_CLASSES_ROOT,lastmodified,0);
1215 /**********************************************************************************
1216 * SHELL_LoadRegistry [Internal]
1218 void SHELL_LoadRegistry( void )
1220 struct set_registry_levels_request *req = get_req_buffer();
1224 TRACE_(reg)("(void)\n");
1228 /* set level to 0 for loading system files */
1231 server_call( REQ_SET_REGISTRY_LEVELS );
1233 if (PROFILE_GetWineIniBool ("registry", "LoadWindowsRegistryFiles", 1))
1235 /* Load windows 3.1 entries */
1237 /* Load windows 95 entries */
1238 _w95_loadreg("C:\\system.1st", HKEY_LOCAL_MACHINE);
1239 _w95_loadreg("system.dat", HKEY_LOCAL_MACHINE);
1240 _w95_loadreg("user.dat", HKEY_USERS);
1243 if (PROFILE_GetWineIniBool ("registry","LoadGlobalRegistryFiles", 1))
1246 * Load the global HKU hive directly from sysconfdir
1248 _wine_loadreg( HKEY_USERS, SAVE_USERS_DEFAULT );
1251 * Load the global machine defaults directly form sysconfdir
1253 _wine_loadreg( HKEY_LOCAL_MACHINE, SAVE_LOCAL_MACHINE_DEFAULT );
1256 /* set level to 1 for loading user files */
1259 server_call( REQ_SET_REGISTRY_LEVELS );
1262 * Load the user saved registries
1264 if (!(home = getenv( "HOME" )))
1265 WARN_(reg)("Failed to get homedirectory of UID %ld.\n",(long) getuid());
1266 else if (PROFILE_GetWineIniBool("registry", "LoadHomeRegistryFiles", 1))
1269 * Load user's personal versions of global HKU/.Default keys
1271 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX) +
1272 strlen(SAVE_LOCAL_USERS_DEFAULT)+2);
1274 strcat(fn, WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1275 _wine_loadreg( HKEY_USERS, fn );
1278 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX) + strlen(SAVE_CURRENT_USER)+2);
1280 strcat(fn, WINE_PREFIX"/"SAVE_CURRENT_USER);
1281 _wine_loadreg( HKEY_CURRENT_USER, fn );
1285 * Load HKLM, attempt to get the registry location from the config
1286 * file first, if exist, load and keep going.
1288 fn=(char*)xmalloc( strlen(home)+ strlen(WINE_PREFIX)+ strlen(SAVE_LOCAL_MACHINE)+2);
1290 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_MACHINE);
1291 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1296 * Load HKCU, get the registry location from the config
1297 * file, if exist, load and keep going.
1299 if (PROFILE_GetWineIniBool ( "registry", "LoadAltRegistryFiles", 1))
1301 fn = xmalloc( MAX_PATHNAME_LEN );
1302 if ( PROFILE_GetWineIniString( "registry", "AltCurrentUserFile", "",
1303 fn, MAX_PATHNAME_LEN - 1))
1305 _wine_loadreg( HKEY_CURRENT_USER, fn );
1309 * Load HKU, get the registry location from the config
1310 * file, if exist, load and keep going.
1312 fn = xmalloc ( MAX_PATHNAME_LEN );
1313 if ( PROFILE_GetWineIniString ( "registry", "AltUserFile", "",
1314 fn, MAX_PATHNAME_LEN - 1))
1316 _wine_loadreg( HKEY_USERS, fn );
1320 * Load HKLM, get the registry location from the config
1321 * file, if exist, load and keep going.
1323 fn = xmalloc ( MAX_PATHNAME_LEN );
1324 if (PROFILE_GetWineIniString ( "registry", "AltLocalMachineFile", "",
1325 fn, MAX_PATHNAME_LEN - 1))
1327 _wine_loadreg( HKEY_LOCAL_MACHINE, fn );
1333 * Obtain the handle of the HKU\.Default key.
1334 * in order to copy HKU\.Default\* onto HKEY_CURRENT_USER
1336 if (RegCreateKeyA(HKEY_USERS,".Default",&hkey) != ERROR_SUCCESS)
1337 WARN_(reg)("Could not create global user default key\n");
1339 _copy_registry( hkey, HKEY_CURRENT_USER );
1343 * Since HKU is built from the global HKU and the local user HKU file we must
1344 * flush the HKU tree we have built at this point otherwise the part brought
1345 * in from the global HKU is saved into the local HKU. To avoid this
1346 * useless dupplication of HKU keys we reread the local HKU key.
1349 /* Allways flush the HKU hive and reload it only with user's personal HKU */
1350 _flush_registry( HKEY_USERS );
1352 /* Reload user's local HKU hive */
1353 if (home && PROFILE_GetWineIniBool ("registry","LoadHomeRegistryFiles",1))
1355 fn=(char*)xmalloc( strlen(home) + strlen(WINE_PREFIX)
1356 + strlen(SAVE_LOCAL_USERS_DEFAULT) + 2);
1359 strcat(fn,WINE_PREFIX"/"SAVE_LOCAL_USERS_DEFAULT);
1361 _wine_loadreg( HKEY_USERS, fn );
1367 * Make sure the update mode is there
1369 if (ERROR_SUCCESS==RegCreateKey16(HKEY_CURRENT_USER,KEY_REGISTRY,&hkey))
1371 DWORD junk,type,len;
1375 if (( RegQueryValueExA(
1381 &len) != ERROR_SUCCESS) || (type != REG_SZ))
1383 RegSetValueExA(hkey,VAL_SAVEUPDATED,0,REG_SZ,"yes",4);
1391 /********************* API FUNCTIONS ***************************************/
1396 /******************************************************************************
1397 * RegFlushKey [KERNEL.227] [ADVAPI32.143]
1398 * Immediately writes key to registry.
1399 * Only returns after data has been written to disk.
1401 * FIXME: does it really wait until data is written ?
1404 * hkey [I] Handle of key to write
1407 * Success: ERROR_SUCCESS
1408 * Failure: Error code
1410 DWORD WINAPI RegFlushKey( HKEY hkey )
1412 FIXME( "(%x): stub\n", hkey );
1413 return ERROR_SUCCESS;
1416 /******************************************************************************
1417 * RegConnectRegistry32W [ADVAPI32.128]
1420 * lpMachineName [I] Address of name of remote computer
1421 * hHey [I] Predefined registry handle
1422 * phkResult [I] Address of buffer for remote registry handle
1424 LONG WINAPI RegConnectRegistryW( LPCWSTR lpMachineName, HKEY hKey,
1427 TRACE_(reg)("(%s,%x,%p): stub\n",debugstr_w(lpMachineName),hKey,phkResult);
1429 if (!lpMachineName || !*lpMachineName) {
1430 /* Use the local machine name */
1431 return RegOpenKey16( hKey, "", phkResult );
1434 FIXME_(reg)("Cannot connect to %s\n",debugstr_w(lpMachineName));
1435 return ERROR_BAD_NETPATH;
1439 /******************************************************************************
1440 * RegConnectRegistry32A [ADVAPI32.127]
1442 LONG WINAPI RegConnectRegistryA( LPCSTR machine, HKEY hkey, LPHKEY reskey )
1445 LPWSTR machineW = strdupA2W(machine);
1446 ret = RegConnectRegistryW( machineW, hkey, reskey );
1452 /******************************************************************************
1453 * RegGetKeySecurity [ADVAPI32.144]
1454 * Retrieves a copy of security descriptor protecting the registry key
1457 * hkey [I] Open handle of key to set
1458 * SecurityInformation [I] Descriptor contents
1459 * pSecurityDescriptor [O] Address of descriptor for key
1460 * lpcbSecurityDescriptor [I/O] Address of size of buffer and description
1463 * Success: ERROR_SUCCESS
1464 * Failure: Error code
1466 LONG WINAPI RegGetKeySecurity( HKEY hkey,
1467 SECURITY_INFORMATION SecurityInformation,
1468 PSECURITY_DESCRIPTOR pSecurityDescriptor,
1469 LPDWORD lpcbSecurityDescriptor )
1471 TRACE_(reg)("(%x,%ld,%p,%ld)\n",hkey,SecurityInformation,pSecurityDescriptor,
1472 lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1474 /* FIXME: Check for valid SecurityInformation values */
1476 if (*lpcbSecurityDescriptor < sizeof(SECURITY_DESCRIPTOR))
1477 return ERROR_INSUFFICIENT_BUFFER;
1479 FIXME_(reg)("(%x,%ld,%p,%ld): stub\n",hkey,SecurityInformation,
1480 pSecurityDescriptor,lpcbSecurityDescriptor?*lpcbSecurityDescriptor:0);
1482 return ERROR_SUCCESS;
1486 /******************************************************************************
1487 * RegNotifyChangeKeyValue [ADVAPI32.???]
1490 * hkey [I] Handle of key to watch
1491 * fWatchSubTree [I] Flag for subkey notification
1492 * fdwNotifyFilter [I] Changes to be reported
1493 * hEvent [I] Handle of signaled event
1494 * fAsync [I] Flag for asynchronous reporting
1496 LONG WINAPI RegNotifyChangeKeyValue( HKEY hkey, BOOL fWatchSubTree,
1497 DWORD fdwNotifyFilter, HANDLE hEvent,
1500 FIXME_(reg)("(%x,%i,%ld,%x,%i): stub\n",hkey,fWatchSubTree,fdwNotifyFilter,
1502 return ERROR_SUCCESS;
1506 /******************************************************************************
1507 * RegUnLoadKey32W [ADVAPI32.173]
1510 * hkey [I] Handle of open key
1511 * lpSubKey [I] Address of name of subkey to unload
1513 LONG WINAPI RegUnLoadKeyW( HKEY hkey, LPCWSTR lpSubKey )
1515 FIXME_(reg)("(%x,%s): stub\n",hkey, debugstr_w(lpSubKey));
1516 return ERROR_SUCCESS;
1520 /******************************************************************************
1521 * RegUnLoadKey32A [ADVAPI32.172]
1523 LONG WINAPI RegUnLoadKeyA( HKEY hkey, LPCSTR lpSubKey )
1526 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1527 ret = RegUnLoadKeyW( hkey, lpSubKeyW );
1528 if(lpSubKeyW) free(lpSubKeyW);
1533 /******************************************************************************
1534 * RegSetKeySecurity [ADVAPI32.167]
1537 * hkey [I] Open handle of key to set
1538 * SecurityInfo [I] Descriptor contents
1539 * pSecurityDesc [I] Address of descriptor for key
1541 LONG WINAPI RegSetKeySecurity( HKEY hkey, SECURITY_INFORMATION SecurityInfo,
1542 PSECURITY_DESCRIPTOR pSecurityDesc )
1544 TRACE_(reg)("(%x,%ld,%p)\n",hkey,SecurityInfo,pSecurityDesc);
1546 /* It seems to perform this check before the hkey check */
1547 if ((SecurityInfo & OWNER_SECURITY_INFORMATION) ||
1548 (SecurityInfo & GROUP_SECURITY_INFORMATION) ||
1549 (SecurityInfo & DACL_SECURITY_INFORMATION) ||
1550 (SecurityInfo & SACL_SECURITY_INFORMATION)) {
1553 return ERROR_INVALID_PARAMETER;
1556 return ERROR_INVALID_PARAMETER;
1558 FIXME_(reg)(":(%x,%ld,%p): stub\n",hkey,SecurityInfo,pSecurityDesc);
1560 return ERROR_SUCCESS;
1564 /******************************************************************************
1565 * RegRestoreKey32W [ADVAPI32.164]
1568 * hkey [I] Handle of key where restore begins
1569 * lpFile [I] Address of filename containing saved tree
1570 * dwFlags [I] Optional flags
1572 LONG WINAPI RegRestoreKeyW( HKEY hkey, LPCWSTR lpFile, DWORD dwFlags )
1574 TRACE_(reg)("(%x,%s,%ld)\n",hkey,debugstr_w(lpFile),dwFlags);
1576 /* It seems to do this check before the hkey check */
1577 if (!lpFile || !*lpFile)
1578 return ERROR_INVALID_PARAMETER;
1580 FIXME_(reg)("(%x,%s,%ld): stub\n",hkey,debugstr_w(lpFile),dwFlags);
1582 /* Check for file existence */
1584 return ERROR_SUCCESS;
1588 /******************************************************************************
1589 * RegRestoreKey32A [ADVAPI32.163]
1591 LONG WINAPI RegRestoreKeyA( HKEY hkey, LPCSTR lpFile, DWORD dwFlags )
1594 LPWSTR lpFileW = strdupA2W(lpFile);
1595 ret = RegRestoreKeyW( hkey, lpFileW, dwFlags );
1596 if(lpFileW) free(lpFileW);
1601 /******************************************************************************
1602 * RegReplaceKey32W [ADVAPI32.162]
1605 * hkey [I] Handle of open key
1606 * lpSubKey [I] Address of name of subkey
1607 * lpNewFile [I] Address of filename for file with new data
1608 * lpOldFile [I] Address of filename for backup file
1610 LONG WINAPI RegReplaceKeyW( HKEY hkey, LPCWSTR lpSubKey, LPCWSTR lpNewFile,
1613 FIXME_(reg)("(%x,%s,%s,%s): stub\n", hkey, debugstr_w(lpSubKey),
1614 debugstr_w(lpNewFile),debugstr_w(lpOldFile));
1615 return ERROR_SUCCESS;
1619 /******************************************************************************
1620 * RegReplaceKey32A [ADVAPI32.161]
1622 LONG WINAPI RegReplaceKeyA( HKEY hkey, LPCSTR lpSubKey, LPCSTR lpNewFile,
1626 LPWSTR lpSubKeyW = strdupA2W(lpSubKey);
1627 LPWSTR lpNewFileW = strdupA2W(lpNewFile);
1628 LPWSTR lpOldFileW = strdupA2W(lpOldFile);
1629 ret = RegReplaceKeyW( hkey, lpSubKeyW, lpNewFileW, lpOldFileW );
1641 /* 16-bit functions */
1643 /* 0 and 1 are valid rootkeys in win16 shell.dll and are used by
1644 * some programs. Do not remove those cases. -MM
1646 static inline void fix_win16_hkey( HKEY *hkey )
1648 if (*hkey == 0 || *hkey == 1) *hkey = HKEY_CLASSES_ROOT;
1651 /******************************************************************************
1652 * RegEnumKey16 [KERNEL.216] [SHELL.7]
1654 DWORD WINAPI RegEnumKey16( HKEY hkey, DWORD index, LPSTR name, DWORD name_len )
1656 fix_win16_hkey( &hkey );
1657 return RegEnumKeyA( hkey, index, name, name_len );
1660 /******************************************************************************
1661 * RegOpenKey16 [KERNEL.217] [SHELL.1]
1663 DWORD WINAPI RegOpenKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1665 fix_win16_hkey( &hkey );
1666 return RegOpenKeyA( hkey, name, retkey );
1669 /******************************************************************************
1670 * RegCreateKey16 [KERNEL.218] [SHELL.2]
1672 DWORD WINAPI RegCreateKey16( HKEY hkey, LPCSTR name, LPHKEY retkey )
1674 fix_win16_hkey( &hkey );
1675 return RegCreateKeyA( hkey, name, retkey );
1678 /******************************************************************************
1679 * RegDeleteKey16 [KERNEL.219] [SHELL.4]
1681 DWORD WINAPI RegDeleteKey16( HKEY hkey, LPCSTR name )
1683 fix_win16_hkey( &hkey );
1684 return RegDeleteKeyA( hkey, name );
1687 /******************************************************************************
1688 * RegCloseKey16 [KERNEL.220] [SHELL.3]
1690 DWORD WINAPI RegCloseKey16( HKEY hkey )
1692 fix_win16_hkey( &hkey );
1693 return RegCloseKey( hkey );
1696 /******************************************************************************
1697 * RegSetValue16 [KERNEL.221] [SHELL.5]
1699 DWORD WINAPI RegSetValue16( HKEY hkey, LPCSTR name, DWORD type, LPCSTR data, DWORD count )
1701 fix_win16_hkey( &hkey );
1702 return RegSetValueA( hkey, name, type, data, count );
1705 /******************************************************************************
1706 * RegDeleteValue16 [KERNEL.222]
1708 DWORD WINAPI RegDeleteValue16( HKEY hkey, LPSTR name )
1710 fix_win16_hkey( &hkey );
1711 return RegDeleteValueA( hkey, name );
1714 /******************************************************************************
1715 * RegEnumValue16 [KERNEL.223]
1717 DWORD WINAPI RegEnumValue16( HKEY hkey, DWORD index, LPSTR value, LPDWORD val_count,
1718 LPDWORD reserved, LPDWORD type, LPBYTE data, LPDWORD count )
1720 fix_win16_hkey( &hkey );
1721 return RegEnumValueA( hkey, index, value, val_count, reserved, type, data, count );
1724 /******************************************************************************
1725 * RegQueryValue16 [KERNEL.224] [SHELL.6]
1728 * Is this HACK still applicable?
1731 * The 16bit RegQueryValue doesn't handle selectorblocks anyway, so we just
1732 * mask out the high 16 bit. This (not so much incidently) hopefully fixes
1735 DWORD WINAPI RegQueryValue16( HKEY hkey, LPCSTR name, LPSTR data, LPDWORD count )
1737 fix_win16_hkey( &hkey );
1738 if (count) *count &= 0xffff;
1739 return RegQueryValueA( hkey, name, data, count );
1742 /******************************************************************************
1743 * RegQueryValueEx16 [KERNEL.225]
1745 DWORD WINAPI RegQueryValueEx16( HKEY hkey, LPCSTR name, LPDWORD reserved, LPDWORD type,
1746 LPBYTE data, LPDWORD count )
1748 fix_win16_hkey( &hkey );
1749 return RegQueryValueExA( hkey, name, reserved, type, data, count );
1752 /******************************************************************************
1753 * RegSetValueEx16 [KERNEL.226]
1755 DWORD WINAPI RegSetValueEx16( HKEY hkey, LPCSTR name, DWORD reserved, DWORD type,
1756 CONST BYTE *data, DWORD count )
1758 fix_win16_hkey( &hkey );
1759 return RegSetValueExA( hkey, name, reserved, type, data, count );