Send WM_CTLCOLOREDIT not WM_CTLCOLORSTATIC messages to parent of a
[wine] / misc / version.c
1 /*
2  * Windows and DOS version functions
3  *
4  * Copyright 1997 Alexandre Julliard
5  * Copyright 1997 Marcus Meissner
6  * Copyright 1998 Patrik Stridvall
7  * Copyright 1998 Andreas Mohr
8  */
9
10 #include <string.h>
11 #include <stdlib.h>
12 #include "winbase.h"
13 #include "winuser.h"
14 #include "wine/winbase16.h"
15 #include "process.h"
16 #include "options.h"
17 #include "debugtools.h"
18 #include "neexe.h"
19 #include "winversion.h"
20 #include "winerror.h"
21
22 DEFAULT_DEBUG_CHANNEL(ver)
23
24 typedef struct
25 {
26     LONG             getVersion16; 
27     LONG             getVersion32;
28     OSVERSIONINFOA getVersionEx;
29 } VERSION_DATA;
30
31
32 /* FIXME: compare values below with original and fix */
33 static VERSION_DATA VersionData[NB_WINDOWS_VERSIONS] =
34 {
35     /* WIN31 */
36     {
37         MAKELONG( 0x0a03, 0x0616 ), /* DOS 6.22 */
38         MAKELONG( 0x0a03, 0x8000 ),
39         {
40             sizeof(OSVERSIONINFOA), 3, 10, 0,
41             VER_PLATFORM_WIN32s, "Win32s 1.3" 
42         }
43     },
44     /* WIN95 */
45     {
46         0x07005F03,
47         0xC0000004,
48         {
49             sizeof(OSVERSIONINFOA), 4, 0, 0x40003B6,
50             VER_PLATFORM_WIN32_WINDOWS, "Win95"
51         }
52     },
53     /* WIN98 */
54     {
55         0x07005F03,     /* FIXME: need DOS value from real Win98 */
56         0xC0000A04,
57         {
58             sizeof(OSVERSIONINFOA), 4, 10, 0x40A07CE,
59             VER_PLATFORM_WIN32_WINDOWS, "Win98"
60         }
61     },
62     /* NT351 */
63     {
64         0x05000A03,
65         0x04213303,
66         {
67             sizeof(OSVERSIONINFOA), 3, 51, 0x421,
68             VER_PLATFORM_WIN32_NT, "Service Pack 2"
69         }
70     },
71     /* NT40 */
72     {
73         0x05000A03,
74         0x05650004,
75         {
76             sizeof(OSVERSIONINFOA), 4, 0, 0x565,
77             VER_PLATFORM_WIN32_NT, "Service Pack 3"
78         }
79     }
80 };
81
82 static const char *WinVersionNames[NB_WINDOWS_VERSIONS] =
83 {
84     "win31",
85     "win95",
86     "win98",
87     "nt351",
88     "nt40"
89 };
90
91 /* if one of the following dlls is importing ntdll the windows
92 version autodetection switches wine to unicode (nt 3.51 or 4.0) */
93 static char * special_dlls[] =
94 {
95         "COMDLG32",
96         "COMCTL32",
97         "SHELL32",
98         "USER32",
99         "OLE32",
100         "RPCRT4",
101         NULL
102 };
103
104 /* the current version has not been autodetected but forced via cmdline */
105 static BOOL versionForced = FALSE;
106 static WINDOWS_VERSION defaultWinVersion = WIN31;
107
108 /**********************************************************************
109  *         VERSION_ParseWinVersion
110  */
111 void VERSION_ParseWinVersion( const char *arg )
112 {
113     int i;
114     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
115     {
116         if (!strcmp( WinVersionNames[i], arg ))
117         {
118             defaultWinVersion = (WINDOWS_VERSION)i;
119             versionForced = TRUE;
120             return;
121         }
122     }
123     MESSAGE("Invalid winver value '%s' specified.\n", arg );
124     MESSAGE("Valid versions are:" );
125     for (i = 0; i < NB_WINDOWS_VERSIONS; i++)
126         MESSAGE(" '%s'%c", WinVersionNames[i],
127             (i == NB_WINDOWS_VERSIONS - 1) ? '\n' : ',' );
128 }
129
130
131 /**********************************************************************
132  *         VERSION_ParseDosVersion
133  */
134 void VERSION_ParseDosVersion( const char *arg )
135 {
136     int hi, lo;
137     if (sscanf( arg, "%d.%d", &hi, &lo ) == 2)
138     {
139         VersionData[WIN31].getVersion16 =
140             MAKELONG(LOWORD(VersionData[WIN31].getVersion16),
141                      (hi<<8) + lo);
142     }
143     else
144         fprintf( stderr, "-dosver: Wrong version format. Use \"-dosver x.xx\"\n");
145 }
146
147 /**********************************************************************
148  *      VERSION_GetSystemDLLVersion
149  *
150  * This function tryes to figure out if a given (native) dll comes from
151  * win95/98 or winnt. Since all values in the OptionalHeader are not a 
152  * usable hint, we test if a dll imports the ntdll.
153  * This is at least working for all system-dlls like comctl32, comdlg32 and
154  * shell32.
155  * If you have a better idea to figure this out...
156  */
157 DWORD VERSION_GetSystemDLLVersion (WINE_MODREF * wm)
158 {
159         PE_MODREF * pem = &(wm->binfmt.pe);
160         
161         if (pem->pe_import)
162         {
163           IMAGE_IMPORT_DESCRIPTOR * pe_imp;
164
165           for ( pe_imp = pem->pe_import; pe_imp->Name; pe_imp++)
166           {
167             char * name = (char*)((unsigned int)wm->module+(unsigned int)(pe_imp->Name));
168             TRACE("%s\n", name);
169             
170             if (!lstrncmpiA(name, "ntdll", 5))
171             {
172               if (3 == PE_HEADER(wm->module)->OptionalHeader.MajorOperatingSystemVersion)
173                 return NT351;
174               else
175                 return NT40;
176             }
177           }
178         }
179         return WIN95;
180 }
181 /**********************************************************************
182  *      VERSION_GetLinkedDllVersion
183  *
184  * Some version data (not reliable!):
185  * linker/OS/image/subsys
186  *
187  * x.xx/1.00/0.00/3.10  Win32s          (any version ?)
188  * 2.39/1.00/0.00/3.10  Win32s          freecell.exe (any version)
189  * 2.50/1.00/4.00/4.00  Win32s 1.30     winhlp32.exe    
190  * 2.60/3.51/3.51/3.51  NT351SP5        system dlls 
191  * 2.60/3.51/3.51/4.00  NT351SP5        comctl32 dll
192  * 2.xx/1.00/0.00/4.00  Win95           system files
193  * x.xx/4.00/0.00/4.00  Win95           most applications
194  * 3.10/4.00/0.00/4.00  Win98           notepad
195  * x.xx/5.00/5.00/4.00  Win98           system dlls
196  * x.xx/4.00/4.00/4.00  NT 4            most apps
197  * 5.12/5.00/5.00/4.00  NT4+IE5         comctl32.dll
198  * 5.12/5.00/5.00/4.00  Win98           calc
199  * x.xx/5.00/5.00/4.00  win95/win98/NT4 IE5 files
200  */
201 DWORD VERSION_GetLinkedDllVersion(PDB *pdb)
202 {
203         WINE_MODREF *wm;
204         DWORD WinVersion = NB_WINDOWS_VERSIONS;
205         PIMAGE_OPTIONAL_HEADER ophd;
206
207         if (!pdb->exe_modref)
208         {
209           /* winn311 progs only link to user32 */
210           if (pdb->modref_list && pdb->modref_list->next)
211             return WIN95;
212           return WIN31;
213         }
214         /* First check the native dlls provided. These have to be
215         from one windows version */
216         for ( wm = pdb->modref_list; wm; wm=wm->next )
217         {
218           ophd = &(PE_HEADER(wm->module)->OptionalHeader);
219
220           TRACE("%s: %02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
221             wm->modname,
222             ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
223             ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
224             ophd->MajorImageVersion, ophd->MinorImageVersion,
225             ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
226
227           /* test if it a external dll */
228           if ( !(wm->flags & WINE_MODREF_INTERNAL) )
229           {
230             int i;
231             for (i = 0; special_dlls[i]; i++)
232             {
233               /* test if it a special dll */
234               if (!lstrncmpiA(wm->modname, special_dlls[i], strlen(special_dlls[i]) ))
235               {
236                 DWORD DllVersion = VERSION_GetSystemDLLVersion(wm);
237                 if (WinVersion == NB_WINDOWS_VERSIONS) 
238                 {
239                   WinVersion = DllVersion;
240                 }
241                 else
242                 {
243                   if (WinVersion != DllVersion)
244                   {
245                     ERR("You mixed system dlls from different windows versions! Expect a chrash!\n");
246                     return WIN31; /* this may let the exe exiting */
247                   }
248                 }
249                 break;
250               }
251             }
252           }
253         }
254         
255         if(WinVersion != NB_WINDOWS_VERSIONS) return WinVersion;
256         
257         /* we are using no external system dlls, look at the exe */
258         ophd = &(PE_HEADER(pdb->exe_modref->module)->OptionalHeader);
259         
260         TRACE("-%s: %02x.%02x/%02x.%02x/%02x.%02x/%02x.%02x\n",
261             pdb->exe_modref->modname,
262             ophd->MajorLinkerVersion, ophd->MinorLinkerVersion,
263             ophd->MajorOperatingSystemVersion, ophd->MinorOperatingSystemVersion,
264             ophd->MajorImageVersion, ophd->MinorImageVersion,
265             ophd->MajorSubsystemVersion, ophd->MinorSubsystemVersion);
266
267         /* special nt 3.51 */
268         if (3 == ophd->MajorOperatingSystemVersion && 51 == ophd->MinorOperatingSystemVersion)
269         {
270             return NT351;
271         }
272
273         /* the MajorSubsystemVersion is the only usable singn */
274         if (ophd->MajorSubsystemVersion < 4)
275         {
276           if ( ophd->MajorOperatingSystemVersion == 1 
277             && ophd->MinorOperatingSystemVersion == 0)
278           {
279             return WIN31; /* win32s */
280           }
281           
282           if (ophd->Subsystem == IMAGE_SUBSYSTEM_WINDOWS_CUI)
283             return NT351; /* FIXME: NT 3.1, not tested */
284           else
285             return WIN95;
286         }       
287
288         return WIN95;
289 }
290
291 /**********************************************************************
292  *         VERSION_GetVersion
293  *
294  * WARNING !!!
295  * Don't call this function too early during the Wine init,
296  * as pdb->exe_modref (required by VERSION_GetImageVersion()) might still
297  * be NULL in such cases, which causes the winver to ALWAYS be detected
298  * as WIN31.
299  * And as we cache the winver once it has been determined, this is bad.
300  * This can happen much easier than you might think, as this function
301  * is called by EVERY GetVersion*() API !
302  *
303  */
304 WINDOWS_VERSION VERSION_GetVersion(void)
305 {
306         PDB *pdb = PROCESS_Current();
307     
308         if (versionForced) /* user has overridden any sensible checks */
309           return defaultWinVersion;
310
311         if (pdb->winver == 0xffff) /* to be determined */
312         {
313           pdb->winver = VERSION_GetLinkedDllVersion(pdb);
314           TRACE("Autodetected: %s\n", VERSION_GetVersionName());
315         }
316
317         return pdb->winver;
318 }
319
320 /**********************************************************************
321  *         VERSION_AppWinVer
322  * Returns the window version in case Wine emulates a later version
323  * of windows then the application expects.
324  * 
325  * In a number of cases when windows runs an application that was
326  * designed for an earlier windows version, windows reverts
327  * to "old" behaviour of that earlier version.
328  * 
329  * An example is a disabled  edit control that needs to be painted. 
330  * Old style behaviour is to send a WM_CTLCOLOREDIT message. This was 
331  * changed in Win95, NT4.0 by a WM_CTLCOLORSTATIC message _only_ for 
332  * applications with an expected version 0f 4.0 or higher.
333  * 
334  */
335 DWORD VERSION_AppWinVer(void)
336 {
337     WINDOWS_VERSION ver = VERSION_GetVersion();
338     DWORD dwEmulatedVersion=MAKELONG( VersionData[ver].getVersionEx.dwMinorVersion, 
339                     VersionData[ver].getVersionEx.dwMajorVersion);
340     /* fixme: this may not be 100% correct; see discussion on the
341      * wine developer list in Nov 1999 */
342     DWORD dwProcVersion = GetProcessVersion(0);
343     return dwProcVersion < dwEmulatedVersion ? dwProcVersion : dwEmulatedVersion; 
344 }
345
346
347 /**********************************************************************
348  *         VERSION_GetVersionName
349  */
350 char *VERSION_GetVersionName()
351 {
352   WINDOWS_VERSION ver = VERSION_GetVersion();
353   switch(ver)
354     {
355     case WIN31:
356       return "Windows 3.1";
357     case WIN95:  
358       return "Windows 95";
359     case WIN98:
360       return "Windows 98";
361     case NT351:
362       return "Windows NT 3.51";
363     case NT40:
364       return "Windows NT 4.0";
365     default:
366       FIXME("Windows version %d not named",ver);
367       return "Windows <Unknown>";
368     }
369 }
370
371 /***********************************************************************
372  *         GetVersion16   (KERNEL.3)
373  */
374 LONG WINAPI GetVersion16(void)
375 {
376     WINDOWS_VERSION ver = VERSION_GetVersion();
377     return VersionData[ver].getVersion16;
378 }
379
380
381 /***********************************************************************
382  *         GetVersion32   (KERNEL32.427)
383  */
384 LONG WINAPI GetVersion(void)
385 {
386     WINDOWS_VERSION ver = VERSION_GetVersion();
387     return VersionData[ver].getVersion32;
388 }
389
390
391 /***********************************************************************
392  *         GetVersionEx16   (KERNEL.149)
393  */
394 BOOL16 WINAPI GetVersionEx16(OSVERSIONINFO16 *v)
395 {
396     WINDOWS_VERSION ver = VERSION_GetVersion();
397     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFO16))
398     {
399         WARN("wrong OSVERSIONINFO size from app");
400         SetLastError(ERROR_INSUFFICIENT_BUFFER);
401         return FALSE;
402     }
403     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
404     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
405     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
406     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
407     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
408     return TRUE;
409 }
410
411
412 /***********************************************************************
413  *         GetVersionEx32A   (KERNEL32.428)
414  */
415 BOOL WINAPI GetVersionExA(OSVERSIONINFOA *v)
416 {
417     WINDOWS_VERSION ver = VERSION_GetVersion();
418     if (v->dwOSVersionInfoSize != sizeof(OSVERSIONINFOA))
419     {
420         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d)",
421                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOA));
422         SetLastError(ERROR_INSUFFICIENT_BUFFER);
423         return FALSE;
424     }
425     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
426     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
427     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
428     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
429     strcpy( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
430     return TRUE;
431 }
432
433
434 /***********************************************************************
435  *         GetVersionEx32W   (KERNEL32.429)
436  */
437 BOOL WINAPI GetVersionExW(OSVERSIONINFOW *v)
438 {
439     WINDOWS_VERSION ver = VERSION_GetVersion();
440
441     if (v->dwOSVersionInfoSize!=sizeof(OSVERSIONINFOW))
442     {
443         WARN("wrong OSVERSIONINFO size from app (got: %ld, expected: %d)",
444                         v->dwOSVersionInfoSize, sizeof(OSVERSIONINFOW));
445         SetLastError(ERROR_INSUFFICIENT_BUFFER);
446         return FALSE;
447     }
448     v->dwMajorVersion = VersionData[ver].getVersionEx.dwMajorVersion;
449     v->dwMinorVersion = VersionData[ver].getVersionEx.dwMinorVersion;
450     v->dwBuildNumber  = VersionData[ver].getVersionEx.dwBuildNumber;
451     v->dwPlatformId   = VersionData[ver].getVersionEx.dwPlatformId;
452     lstrcpyAtoW( v->szCSDVersion, VersionData[ver].getVersionEx.szCSDVersion );
453     return TRUE;
454 }
455
456
457 /***********************************************************************
458  *          GetWinFlags   (KERNEL.132)
459  */
460 DWORD WINAPI GetWinFlags16(void)
461 {
462   static const long cpuflags[5] =
463     { WF_CPU086, WF_CPU186, WF_CPU286, WF_CPU386, WF_CPU486 };
464   SYSTEM_INFO si;
465   OSVERSIONINFOA ovi;
466   DWORD result;
467
468   GetSystemInfo(&si);
469
470   /* There doesn't seem to be any Pentium flag.  */
471   result = cpuflags[MIN (si.wProcessorLevel, 4)];
472
473   switch(Options.mode)
474   {
475   case MODE_STANDARD:
476       result |= WF_STANDARD | WF_PMODE | WF_80x87;
477       break;
478
479   case MODE_ENHANCED:
480       result |= WF_ENHANCED | WF_PMODE | WF_80x87 | WF_PAGING;
481       break;
482
483   default:
484       ERR("Unknown mode set? This shouldn't happen. Check GetWinFlags()!\n");
485       break;
486   }
487   if (si.wProcessorLevel >= 4) result |= WF_HASCPUID;
488   ovi.dwOSVersionInfoSize = sizeof(ovi);
489   GetVersionExA(&ovi);
490   if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
491       result |= WF_WIN32WOW; /* undocumented WF_WINNT */
492   return result;
493 }
494
495
496 /***********************************************************************
497  *          GetWinDebugInfo   (KERNEL.355)
498  */
499 BOOL16 WINAPI GetWinDebugInfo16(WINDEBUGINFO *lpwdi, UINT16 flags)
500 {
501     FIXME("(%8lx,%d): stub returning 0\n",
502           (unsigned long)lpwdi, flags);
503     /* 0 means not in debugging mode/version */
504     /* Can this type of debugging be used in wine ? */
505     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
506     return 0;
507 }
508
509
510 /***********************************************************************
511  *          SetWinDebugInfo   (KERNEL.356)
512  */
513 BOOL16 WINAPI SetWinDebugInfo16(WINDEBUGINFO *lpwdi)
514 {
515     FIXME("(%8lx): stub returning 0\n", (unsigned long)lpwdi);
516     /* 0 means not in debugging mode/version */
517     /* Can this type of debugging be used in wine ? */
518     /* Constants: WDI_OPTIONS WDI_FILTER WDI_ALLOCBREAK */
519     return 0;
520 }
521
522
523 /***********************************************************************
524  *           DebugFillBuffer                    (KERNEL.329)
525  *
526  * TODO:
527  * Should fill lpBuffer only if DBO_BUFFERFILL has been set by SetWinDebugInfo()
528  */
529 void WINAPI DebugFillBuffer(LPSTR lpBuffer, WORD wBytes)
530 {
531         memset(lpBuffer, DBGFILL_BUFFER, wBytes);
532 }
533
534 /***********************************************************************
535  *           DiagQuery                          (KERNEL.339)
536  *
537  * returns TRUE if Win called with "/b" (bootlog.txt)
538  */
539 BOOL16 WINAPI DiagQuery16()
540 {
541         /* perhaps implement a Wine "/b" command line flag sometime ? */
542         return FALSE;
543 }
544
545 /***********************************************************************
546  *           DiagOutput                         (KERNEL.340)
547  *
548  * writes a debug string into <windir>\bootlog.txt
549  */
550 void WINAPI DiagOutput16(LPCSTR str)
551 {
552         /* FIXME */
553         DPRINTF("DIAGOUTPUT:%s\n", debugstr_a(str));
554 }
555
556 /***********************************************************************
557  *           OaBuildVersion           [OLEAUT32.170]
558  */
559 UINT WINAPI OaBuildVersion()
560 {
561     WINDOWS_VERSION ver = VERSION_GetVersion();
562
563     FIXME("Please report to a.mohr@mailto.de if you get version error messages !\n");
564     switch(VersionData[ver].getVersion32)
565     {
566         case 0x80000a03: /* Win 3.1 */
567                 return 0x140fd1; /* from Win32s 1.1e */
568         case 0xc0000004: /* Win 95 */
569          case 0xc0000a04: /* Win 98: verified same as Win95 */
570                 return 0x1e10a9; /* some older version: 0x0a0bd3 */
571         case 0x04213303: /* NT 3.51 */
572                 FIXME("NT 3.51 version value unknown !\n");
573                 return 0x1e10a9; /* value borrowed from Win95 */
574         case 0x05650004: /* NT 4.0 */
575                 return 0x141016;
576         default:
577                 return 0x0;
578     }
579 }
580 /***********************************************************************
581  *        VERSION_OsIsUnicode   [internal]
582  *
583  * NOTES
584  *   some functions getting sometimes LPSTR sometimes LPWSTR...
585  *
586  */
587 BOOL VERSION_OsIsUnicode(void)
588 {
589     switch(VERSION_GetVersion())
590     {
591     case NT351:
592     case NT40:
593         return TRUE;
594     default:
595         return FALSE;
596     }
597 }