7 #include <conio.h> /* this is where Open Watcom hides the outp() etc. functions */
17 #include <hw/cpu/cpu.h>
18 #include <hw/dos/dos.h>
19 #include <hw/dos/doswin.h>
20 #include <hw/dos/dosntvdm.h>
22 /* DEBUG: Flush out calls that aren't there */
24 # define int86 ___EVIL___
25 # define int386 ___EVIL___
26 # define ntvdm_RegisterModule ___EVIL___
27 # define ntvdm_UnregisterModule ___EVIL___
28 # define _dos_getvect ___EVIL___
29 # define _dos_setvect ___EVIL___
32 const char *windows_version_method = NULL;
35 * 0 = not running under Windows
36 * 1 = running under Windows */
37 const char *windows_emulation_comment_str = NULL;
38 uint8_t windows_emulation = 0;
39 uint16_t windows_version = 0; /* NOTE: 0 for Windows NT */
40 uint8_t windows_mode = 0;
41 uint8_t windows_init = 0;
43 const char *windows_mode_strs[WINDOWS_MAX] = {
52 /* TESTING (whether or not it correctly detects the presence of Windows):
53 * Note that in some columns the API returns insufficient information and the
54 * API has to use it's best guess on the correct value, which might be
55 * inaccurate or wrong (marked: GUESSES).
57 * For Windows NT/2000/XP/Vista/7 tests, the code does not have any way of
58 * knowing (yet) which version of the NT kernel is involved, so the best
59 * it can offer is "I am running under NT" (marked as ONLY NT)
61 * OS, shell, configuration & mode Detects Correct mode Correct version
62 * Microsoft Windows 3.0 (DOSBox 0.74)
63 * 386 Enhanced Mode YES YES YES
64 * 286 Standard Mode YES GUESSES YES
65 * 8086 Real Mode YES GUESSES YES
66 * Microsoft Windows 3.1 (DOSBox 0.74)
67 * 386 Enhanced Mode YES YES YES
68 * 286 Standard Mode YES YES YES
69 * Microsoft Windows 3.11 (DOSBox 0.74)
70 * 386 Enhanced Mode YES YES YES*
71 * 286 Standard Mode YES YES YES*
72 * * = Despite being v3.11 it still reports itself as v3.1
73 * Microsoft Windows 95 (4.00.950) (DOS 7.00) (Qemu)
74 * Normal YES YES* YES (4.0)
75 * Safe mode YES YES* YES (4.0)
76 * Prevent DOS apps detecting Windows NO - -
77 * * = Reports self as "enhanced mode" which is really the only mode supported
78 * Microsoft Windows 95 OSR2.5 (4.00.950 C) (DOS 7.10) (Qemu)
79 * Normal YES YES YES (4.0)
80 * Safe mode YES YES YES (4.0)
81 * Microsoft Windows 95 SP1 (4.00.950a) (DOS 7.00) (Qemu)
82 * Normal YES YES YES (4.0)
83 * Safe mode YES YES YES (4.0)
84 * Microsoft Windows 98 (4.10.1998) (DOS 7.10) (Qemu)
85 * Normal YES YES YES (4.10)
86 * Safe mode YES YES YES (4.10)
87 * Microsoft Windows 98 SE (4.10.2222 A) (DOS 7.10) (Qemu)
88 * Normal YES YES YES (4.10)
89 * Safe mode YES YES YES (4.10)
90 * Microsoft Windows ME (4.90.3000) (DOS 8.00) (Qemu)
91 * Normal YES YES YES (4.90)
92 * Safe mode YES YES YES (4.90)
93 * Microsoft Windows 2000 Professional (5.00.2195) (VirtualBox)
94 * Normal YES N/A ONLY NT
95 * Microsoft Windows XP Professional (5.1.2600) (VirtualBox)
96 * Normal YES N/A ONLY NT
97 * Microsoft Windows XP Professional SP1 (5.1.2600) (VirtualBox)
98 * Normal YES N/A ONLY NT
99 * Microsoft Windows XP Professional SP2 (5.1.2600) (VirtualBox)
100 * Normal YES N/A ONLY NT
101 * Microsoft Windows XP Professional SP3 (5.1.2600) (VirtualBox)
102 * Normal YES N/A ONLY NT
105 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 32
106 /* it's nice to know if we're running under WINE (and therefore possibly Linux)
107 * as opposed to real Windows, so that we can adjust our techniques accordingly.
108 * I doubt for example that WINE would support Windows NT's NTVDM.EXE BOP codes,
109 * or that our Win9x compatible VxD enumeration would know not to try enumerating
111 void win32_probe_for_wine() { /* Probing for WINE from the Win32 layer */
114 ntdll = LoadLibrary("NTDLL.DLL");
116 const char *(__stdcall *p)() = (const char *(__stdcall *)())GetProcAddress(ntdll,"wine_get_version");
118 windows_emulation = WINEMU_WINE;
119 windows_emulation_comment_str = p(); /* and the function apparently returns a string */
124 #elif defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
125 void win16_probe_for_wine() { /* Probing for WINE from the Win16 layer */
128 if (!genthunk32_init()) return;
129 if (genthunk32w_ntdll == 0) return;
131 callw = __GetProcAddress32W(genthunk32w_ntdll,"wine_get_version");
132 if (callw == 0) return;
134 retv = __CallProcEx32W(CPEX_DEST_STDCALL/*nothing to convert*/,0/*0 param*/,callw);
135 if (retv == 0) return;
137 windows_emulation = WINEMU_WINE;
139 /* NTS: We assume that WINE, just like real Windows, will not move or relocate
140 * NTDLL.DLL and will not move the string it just returned. */
141 /* TODO: You need a function the host program can call to free the selector
142 * you allocated here, in case it wants to reclaim resources */
146 sel = AllocSelector(myds);
148 /* the selector is directed at the string, then retained in this
149 * code as a direct FAR pointer to WINE's version string */
150 SetSelectorBase(sel,retv);
151 SetSelectorLimit(sel,0xFFF); /* WINE's version string is rarely longer than 14 chars */
152 windows_emulation_comment_str = MK_FP(sel,0);
158 int detect_windows() {
159 #if defined(TARGET_WINDOWS)
160 # if TARGET_MSDOS == 32
162 /* Windows 3.0/3.1 with Win386 */
166 windows_emulation = 0;
168 windows_mode = WINDOWS_ENHANCED; /* most likely scenario is Windows 3.1 386 enhanced mode */
171 windows_version_method = "GetVersion";
172 windows_version = (LOBYTE(LOWORD(raw)) << 8) | HIBYTE(LOWORD(raw));
173 /* NTS: Microsoft documents GetVersion() as leaving bit 31 unset for NT, bit 31 set for Win32s and Win 9x/ME.
174 * But that's not what the Win16 version of the function does! */
176 if (dos_version == 0) probe_dos();
178 /* Windows 3.1/9x/ME */
180 if (raw & WF_PMODE) {
181 if (raw & WF_ENHANCED)
182 windows_mode = WINDOWS_ENHANCED;
183 else/* if (raw & WF_STANDARD)*/
184 windows_mode = WINDOWS_STANDARD;
187 windows_mode = WINDOWS_REAL;
190 /* NTS: All 32-bit Windows systems since Windows NT 3.51 and Windows 95 return
191 * major=3 minor=95 when Win16 applications query the version number. The only
192 * exception to that rule is Windows NT 3.1, which returns major=3 minor=10,
193 * the same version number returned by Windows 3.1. */
194 if (windows_mode == WINDOWS_ENHANCED &&
195 (dos_version >= 0x510 && dos_version <= 0x57f)/* MS-DOS v5.50 */ &&
196 (windows_version == 0x035F /* Windows NT 4/2000/XP/Vista/7/8 */ ||
197 windows_version == 0x030A /* Windows NT 3.1/3.5x */)) {
198 /* if the real DOS version is 5.50 then we're under NT */
199 windows_mode = WINDOWS_NT;
202 switch (dos_version>>8) {
203 case 10: /* OS/2 1.x */
204 case 20: /* OS/2 2.x (low=0), and OS/2 Warp 3 (low=30), and OS/2 Warp 4 (low=40) */
205 windows_version_method = "Deduce from DOS version";
206 windows_version = dos_version;
207 windows_mode = WINDOWS_OS2;
211 # elif TARGET_WINDOWS >= 40 || defined(WINNT)
212 /* Windows 95/98/ME/XP/2000/NT/etc. and Windows NT builds: We don't need to do anything.
213 * The fact we're running means Windows is present */
214 /* TODO: Clarify which Windows: Are we running under NT? or 9X/ME? What version? */
218 windows_emulation = 0;
220 memset(&ovi,0,sizeof(ovi));
221 ovi.dwOSVersionInfoSize = sizeof(ovi);
224 windows_version_method = "GetVersionEx";
225 windows_version = (ovi.dwMajorVersion << 8) | ovi.dwMinorVersion;
226 if (ovi.dwPlatformId == VER_PLATFORM_WIN32_NT)
227 windows_mode = WINDOWS_NT;
229 windows_mode = WINDOWS_ENHANCED; /* Windows 3.1 Win32s, or Windows 95/98/ME */
231 win32_probe_for_wine();
233 # elif TARGET_WINDOWS == 31
234 /* Windows 3.1 with Win32s target build. Note that such programs run in the Win32 layer on Win95/98/ME/NT/2000/etc. */
235 /* TODO: Clarify which Windows, using the limited set of functions available under Win32s, or perhaps using GetProcAddress
236 * to use the later GetVersionEx() functions offered by Win95/98/NT/etc. */
240 windows_emulation = 0;
242 windows_mode = WINDOWS_ENHANCED; /* Assume Windows 3.1 386 Enhanced Mode. This 32-bit app couldn't run otherwise */
245 windows_version_method = "GetVersion";
246 windows_version = (LOBYTE(LOWORD(raw)) << 8) | HIBYTE(LOWORD(raw));
247 if (!(raw & 0x80000000UL)) { /* FIXME: Does this actually work? */
248 /* Windows NT/2000/XP/etc */
249 windows_mode = WINDOWS_NT;
252 /* TODO: GetProcAddress() GetVersionEx() and get the REAL version number from Windows */
254 win32_probe_for_wine();
257 # error Unknown 32-bit Windows variant
259 # elif TARGET_MSDOS == 16
260 # if TARGET_WINDOWS >= 30
261 /* Windows 3.0/3.1, what we then want to know is what mode we're running under: Real? Standard? Enhanced?
262 * The API function GetWinFlags() only exists in 3.0 and higher, it doesn't exist under 2.x */
267 windows_emulation = 0;
269 windows_mode = WINDOWS_ENHANCED; /* most likely scenario is Windows 3.1 386 enhanced mode */
272 windows_version_method = "GetVersion";
273 windows_version = (LOBYTE(LOWORD(raw)) << 8) | HIBYTE(LOWORD(raw));
274 /* NTS: Microsoft documents GetVersion() as leaving bit 31 unset for NT, bit 31 set for Win32s and Win 9x/ME.
275 * But that's not what the Win16 version of the function does! */
277 if (dos_version == 0) probe_dos();
279 /* Windows 3.1/9x/ME */
281 if (raw & WF_PMODE) {
282 if (raw & WF_ENHANCED)
283 windows_mode = WINDOWS_ENHANCED;
284 else/* if (raw & WF_STANDARD)*/
285 windows_mode = WINDOWS_STANDARD;
288 windows_mode = WINDOWS_REAL;
291 /* NTS: All 32-bit Windows systems since Windows NT 3.51 and Windows 95 return
292 * major=3 minor=95 when Win16 applications query the version number. The only
293 * exception to that rule is Windows NT 3.1, which returns major=3 minor=10,
294 * the same version number returned by Windows 3.1. */
295 if (windows_mode == WINDOWS_ENHANCED &&
296 (dos_version >= 0x510 && dos_version <= 0x57f)/* MS-DOS v5.50 */ &&
297 (windows_version == 0x035F /* Windows NT 4/2000/XP/Vista/7/8 */ ||
298 windows_version == 0x030A /* Windows NT 3.1/3.5x */)) {
299 /* if the real DOS version is 5.50 then we're under NT */
300 windows_mode = WINDOWS_NT;
303 switch (dos_version>>8) {
304 case 10: /* OS/2 1.x */
305 case 20: /* OS/2 2.x (low=0), and OS/2 Warp 3 (low=30), and OS/2 Warp 4 (low=40) */
306 windows_version_method = "Deduce from DOS version";
307 windows_version = dos_version;
308 windows_mode = WINDOWS_OS2;
312 /* If we're running under Windows 9x/ME or Windows NT/2000 we can thunk our way into
313 * the Win32 world and call various functions to get a more accurate picture of the
314 * Windows system we are running on */
315 /* NTS: Under Windows NT 3.51 or later this technique is the only way to get the
316 * true system version number. The Win16 GetVersion() will always return
317 * some backwards compatible version number except for NT 3.1:
320 * +==========================
331 if (genthunk32_init() && genthunk32w_kernel32_GetVersionEx != 0) {
334 memset(&osv,0,sizeof(osv));
335 osv.dwOSVersionInfoSize = sizeof(osv);
336 if (__CallProcEx32W(CPEX_DEST_STDCALL | 1/* convert param 1*/,
337 1/*1 param*/,genthunk32w_kernel32_GetVersionEx,
338 (DWORD)((void far*)(&osv))) != 0UL) {
339 windows_version_method = "GetVersionEx [16->32 CallProcEx32W]";
340 windows_version = (osv.dwMajorVersion << 8) | osv.dwMinorVersion;
341 if (osv.dwPlatformId == 2/*VER_PLATFORM_WIN32_NT*/)
342 windows_mode = WINDOWS_NT;
344 windows_mode = WINDOWS_ENHANCED;
348 win16_probe_for_wine();
350 # elif TARGET_WINDOWS >= 20
351 /* Windows 2.x or higher. Use GetProcAddress() to locate GetWinFlags() if possible, else assume real mode
352 * and find some other way to detect if we're running under the 286 or 386 enhanced versions of Windows 2.11 */
356 windows_version = 0x200;
357 windows_mode = WINDOWS_REAL;
358 windows_version_method = "Assuming";
361 /* Windows 1.x. No GetProcAddress, no GetWinFlags. Assume Real mode. */
362 /* TODO: How exactly DO you get the Windows version in 1.1? */
365 windows_version = 0x101; /* Assume 1.1 */
366 windows_mode = WINDOWS_REAL;
367 windows_version_method = "Assuming";
371 # error Unknown Windows bit target
373 #elif defined(TARGET_OS2)
374 /* OS/2 16-bit or 32-bit. Obviously as something compiled for OS/2, we're running under OS/2 */
376 windows_version_method = "I'm an OS/2 program, therefore the environment is OS/2";
377 windows_version = dos_version;
378 windows_mode = WINDOWS_OS2;
382 /* MS-DOS 16-bit or 32-bit. MS-DOS applications must try various obscure interrupts to detect whether Windows is running */
383 /* TODO: How can we detect whether we're running under OS/2? */
391 switch (dos_version>>8) {
392 case 10: /* OS/2 1.x */
393 case 20: /* OS/2 2.x (low=0), and OS/2 Warp 3 (low=30), and OS/2 Warp 4 (low=40) */
394 windows_version_method = "Deduce from DOS version";
395 windows_version = dos_version;
396 windows_mode = WINDOWS_OS2;
400 if (windows_version == 0) {
402 #if TARGET_MSDOS == 32
403 int386(0x2F,®s,®s);
405 int86(0x2F,®s,®s);
407 if (regs.w.ax == 0x0000 && regs.w.bx >= 0x300 && regs.w.bx <= 0x700) { /* Windows 3.1 */
408 windows_version = regs.w.bx;
410 case 0x0002: windows_mode = WINDOWS_STANDARD; break;
411 case 0x0003: windows_mode = WINDOWS_ENHANCED; break;
412 default: windows_version = 0; break;
415 if (windows_mode != 0)
416 windows_version_method = "INT 2Fh AX=160Ah";
420 if (windows_version == 0) {
422 #if TARGET_MSDOS == 32
423 int386(0x2F,®s,®s);
425 int86(0x2F,®s,®s);
427 if (regs.w.ax == 0x0000) { /* Windows 3.0 or DOSSHELL in real or standard mode */
428 /* FIXME: Okay... if DOSSHELL triggers this test how do I tell between Windows and DOSSHELL? */
429 /* Also, this call does NOT work when Windows 3.0 is in enhanced mode, and for Real and Standard modes
430 * does not tell us which mode is active.
432 * As far as I can tell there really is no way to differentiate whether it is running in Real or
433 * Standard mode, because on a 286 there is no "virtual 8086" mode. The only way Windows can run
434 * DOS level code is to thunk back into real mode. So for all purposes whatsoever, we might as well
435 * say that we're running in Windows Real mode because during that time slice we have complete control
437 windows_version = 0x300;
438 windows_mode = WINDOWS_REAL;
439 windows_version_method = "INT 2Fh AX=4680h";
443 if (windows_version == 0) {
445 #if TARGET_MSDOS == 32
446 int386(0x2F,®s,®s);
448 int86(0x2F,®s,®s);
450 if (regs.h.al == 1 || regs.h.al == 0xFF) { /* Windows 2.x/386 */
451 windows_version = 0x200;
452 windows_mode = WINDOWS_ENHANCED;
454 else if (regs.h.al == 3 || regs.h.al == 4) {
455 windows_version = (regs.h.al << 8) + regs.h.ah;
456 windows_mode = WINDOWS_ENHANCED; /* Windows 3.0 */
459 if (windows_mode != 0)
460 windows_version_method = "INT 2Fh AX=1600h";
463 if (windows_version == 0 && windows_mode == WINDOWS_NONE) {
464 /* well... if the above fails, but the "true" DOS version is 5.50, then we're running under Windows NT */
465 /* NOTE: Every copy of NT/2000/XP/Vista I have reports 5.50, but assuming that will continue is stupid.
466 * Microsoft is free to change that someday. */
467 if (dos_version == 0) probe_dos();
468 if (dos_version >= 0x510 && dos_version <= 0x57f) { /* FIXME: If I recall Windows NT really does stick to v5.50, so this range check should be changed into == 5.50 */
469 windows_mode = WINDOWS_NT;
471 windows_version_method = "Assuming from DOS version number";
475 /* now... if this is Windows NT, the next thing we can do is use NTVDM.EXE's
476 * BOP opcodes to load a "helper" DLL that allows us to call into Win32 */
477 # if defined(NTVDM_CLIENT) && !defined(TARGET_WINDOWS)
478 if (windows_mode == WINDOWS_NT && ntvdm_dosntast_init()) {
479 /* OK. Ask for the version number */
482 memset(&ovi,0,sizeof(ovi));
483 ovi.dwOSVersionInfoSize = sizeof(ovi);
484 if (ntvdm_dosntast_getversionex(&ovi)) {
485 windows_version_method = "GetVersionEx [NTVDM.EXE + DOSNTAST.VDD]";
486 windows_version = (ovi.dwMajorVersion << 8) | ovi.dwMinorVersion;
487 if (ovi.dwPlatformId == 2/*VER_PLATFORM_WIN32_NT*/)
488 windows_mode = WINDOWS_NT;
490 windows_mode = WINDOWS_ENHANCED;
497 return (windows_mode != WINDOWS_NONE);
500 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 32 && !defined(WIN386)
501 /* API for exploiting the QT_Thunk Win32 -> Win16 thunking offered by Windows 9x/ME */
502 unsigned char win9x_qt_thunk_probed = 0;
503 unsigned char win9x_qt_thunk_available = 0;
505 void (__stdcall *QT_Thunk)() = NULL;
506 DWORD (__stdcall *LoadLibrary16)(LPSTR lpszLibFileName) = NULL;
507 VOID (__stdcall *FreeLibrary16)(DWORD dwInstance) = NULL;
508 HGLOBAL16 (__stdcall *GlobalAlloc16)(UINT flags,DWORD size) = NULL;
509 HGLOBAL16 (__stdcall *GlobalFree16)(HGLOBAL16 handle) = NULL;
510 DWORD (__stdcall *GlobalLock16)(HGLOBAL16 handle) = NULL;
511 BOOL (__stdcall *GlobalUnlock16)(HGLOBAL16 handle) = NULL;
512 VOID (__stdcall *GlobalUnfix16)(HGLOBAL16 handle) = NULL;
513 VOID (__stdcall *GlobalFix16)(HGLOBAL16 handle) = NULL;
514 DWORD (__stdcall *GetProcAddress16)(DWORD dwInstance, LPSTR lpszProcName) = NULL;
515 DWORD win9x_kernel_win16 = 0;
516 DWORD win9x_user_win16 = 0;
518 int Win9xQT_ThunkInit() {
519 if (!win9x_qt_thunk_probed) {
520 Win32OrdinalLookupInfo nfo;
523 win9x_qt_thunk_probed = 1;
524 win9x_qt_thunk_available = 0;
526 if (dos_version == 0) probe_dos();
527 if (windows_mode == 0) detect_windows();
528 if (windows_mode != WINDOWS_ENHANCED) return 0; /* This does not work under Windows NT */
529 if (windows_version < 0x400) return 0; /* This does not work on Windows 3.1 Win32s (FIXME: Are you sure?) */
531 /* This hack relies on undocumented Win16 support routines hidden in KERNEL32.DLL.
532 * They're so super seekret, Microsoft won't even let us get to them through GetProcAddress() */
533 kern32 = GetModuleHandle("KERNEL32.DLL");
534 if (windows_emulation == WINEMU_WINE) {
535 /* FIXME: Direct ordinal lookup doesn't work. Returned
536 * addresses point to invalid regions of KERNEL32.DLL.
537 * I doubt WINE is even putting a PE-compatible image
540 * WINE does allow us to GetProcAddress ordinals
541 * (unlike Windows 9x which blocks it) but I'm not
542 * really sure the returned functions are anything
543 * like the Windows 9x equivalent. If we assume they
544 * are, this code seems unable to get the address of
545 * KRNL386.EXE's "GETVERSION" function.
547 * So basically WINE's Windows 9x emulation is more
548 * like Windows XP's Application Compatability modes
549 * than any serious attempt at pretending to be
550 * Windows 9x. And the entry points may well be
551 * stubs or other random functions in the same way
552 * that ordinal 35 is unrelated under Windows XP. */
555 else if (Win32GetOrdinalLookupInfo(kern32,&nfo)) {
556 GlobalFix16 = (void*)Win32GetOrdinalAddress(&nfo,27);
557 GlobalLock16 = (void*)Win32GetOrdinalAddress(&nfo,25);
558 GlobalFree16 = (void*)Win32GetOrdinalAddress(&nfo,31);
559 LoadLibrary16 = (void*)Win32GetOrdinalAddress(&nfo,35);
560 FreeLibrary16 = (void*)Win32GetOrdinalAddress(&nfo,36);
561 GlobalAlloc16 = (void*)Win32GetOrdinalAddress(&nfo,24);
562 GlobalUnfix16 = (void*)Win32GetOrdinalAddress(&nfo,28);
563 GlobalUnlock16 = (void*)Win32GetOrdinalAddress(&nfo,26);
564 GetProcAddress16 = (void*)Win32GetOrdinalAddress(&nfo,37);
565 QT_Thunk = (void*)GetProcAddress(kern32,"QT_Thunk");
571 GlobalUnfix16 = NULL;
572 LoadLibrary16 = NULL;
573 FreeLibrary16 = NULL;
574 GlobalAlloc16 = NULL;
575 GlobalUnlock16 = NULL;
576 GetProcAddress16 = NULL;
580 if (LoadLibrary16 && FreeLibrary16 && GetProcAddress16 && QT_Thunk) {
581 /* Prove the API works by loading a reference to KERNEL */
582 win9x_kernel_win16 = LoadLibrary16("KERNEL");
583 if (win9x_kernel_win16 != 0) {
584 win9x_qt_thunk_available = 1;
587 win9x_user_win16 = LoadLibrary16("USER");
591 return win9x_qt_thunk_available;
594 void Win9xQT_ThunkFree() {
595 if (win9x_kernel_win16) {
596 FreeLibrary16(win9x_kernel_win16);
597 win9x_kernel_win16 = 0;
602 #if defined(TARGET_WINDOWS) && TARGET_MSDOS == 16
603 unsigned char ToolHelpProbed = 0;
604 HMODULE ToolHelpDLL = 0;
605 BOOL (PASCAL FAR *__TimerCount)(TIMERINFO FAR *t) = NULL;
606 BOOL (PASCAL FAR *__InterruptUnRegister)(HTASK htask) = NULL;
607 BOOL (PASCAL FAR *__InterruptRegister)(HTASK htask,FARPROC callback) = NULL;
610 if (!ToolHelpProbed) {
615 /* BUGFIX: In case TOOLHELP.DLL is missing (such as when running under Windows 3.0)
616 * this prevents sudden interruption by a "Cannot find TOOLHELP.DLL" error
618 oldMode = SetErrorMode(SEM_FAILCRITICALERRORS|SEM_NOOPENFILEERRORBOX);
619 ToolHelpDLL = LoadLibrary("TOOLHELP.DLL");
620 SetErrorMode(oldMode);
621 if (ToolHelpDLL != 0) {
622 __TimerCount = (void far*)GetProcAddress(ToolHelpDLL,"TIMERCOUNT");
623 __InterruptRegister = (void far*)GetProcAddress(ToolHelpDLL,"INTERRUPTREGISTER");
624 __InterruptUnRegister = (void far*)GetProcAddress(ToolHelpDLL,"INTERRUPTUNREGISTER");
628 return (ToolHelpDLL != 0) && (__TimerCount != NULL) && (__InterruptRegister != NULL) &&
629 (__InterruptUnRegister != NULL);
632 void ToolHelpFree() {
634 FreeLibrary(ToolHelpDLL);
637 __InterruptUnRegister = NULL;
638 __InterruptRegister = NULL;