home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CD-ROM Magazin 1995 July/August
/
CDROMMAG.ISO
/
share
/
tool
/
winversi
/
win_ver.c
< prev
next >
Wrap
Text File
|
1995-04-17
|
83KB
|
2,194 lines
/*
Copyright (c) 1993-1995, UK, John M.Howells
CIS ID: 100031,353
A whole slew of terms such as Microsoft, MS, MS-DOS, Windows,
Win32, Win32s, Windows NT, Visual C++, Borland, etc., are
probably trademarks of someone or other.
If you have ever read any disclaimer about using the software at
your own risk, and such, then assume you read it here also.
Program to display the version of Windows on which it is run.
The program can be compiled for both 16-bit and 32-bit versions,
and although most of the code is unique to one version or the
other it's easier to keep it all together. And its operation is
quite crude, not registering a Window, and such, but displaying
the results in a Message Box.
On its own the program is not very useful, and the source is
presented here for the benefit of others writing programs that
need to know the version of Windows their programs are running
on, as it tries to collect together the many documents produced
by Microsoft, and other techniques, into a single working example.
You may think this is all way over the top, and so it is, but it
just shows how many ways I have so far found to determine the
version of Windows hosting a program.
It will report:
16-bit version: either Windows 3.1 'proper' or WOW (i.e. Windows on
Windows, the 16-bit emulation provided by Windows NT)
and Windows 95. if running on WOW it also reports the
version and build of the underlying OS. if running on
a system with Win32s it also reports the version and
build. On Windows NT it will try and report the service
pack identity. On a 16-bit host it will report the file
version of the USER.EXE file.
or
32-bit version: either Windows NT 'proper' or Windows 95 (aka Chicago)
or Win32s (the extension to allow 32-bit programs to
run on the 16-bit version of Windows 3.1x). if Windows
NT is the host OS whether it is the Workstation product
or a Server.
The source of the information is as follows:
Detect Windows-on-Windows Knowledgebase Articles Q92395
(as at 95-02-03) and Q96404
Detect presence of Win32s Knowledgebase Articles Q92395
from a 32-bit program (as at 95-02-03) and Q96404
Detect the Version of Win32s Win32s Programmer's Reference
from a 16-bit program
Detect other Version information Knowledgebase Article Q92395
(as at 95-02-03)
Detect version reported to a DOS program DPMI specification
Detect type and version of 32-bit host on-line help for GetVersionEx()
Detect version of underlying Windows NT 3.1 from information in NT 3.5 SDK CD
from 16-bit program \SCT\SAMPLES\INTEROP\README.TXT
& Knowledgebase Article Q114341
Detect version of underlying Windows NT 3.5 from information in NT 3.5 SDK CD
from 16-bit program \DOC\MISC\GENTHUNK.TXT
& Knowledgebase Article Q104009
Detect Windows for Workgroups is running Knowldegebase Article Q114470
Detect file version from USER.EXE for based on Knowledgebase Article
WFWG (&Win) 3.11 Q113892
Distinguish Windows NT Workstation Note from John Lawniczak of
from Windows NT Server Microsoft Developer Support
in the MSWIN32 forum on CIS
Determine Workgroups from WINVER.EXE My own guess (!) after seeing output
from the 16-bit CONFIGSP that it
called a "Windows Version String"
Determine Windows NT 3.1 Service Pack My own code, and examining which
key the WINMSD program uses
Detect if Win32s is running All my own work
Detect numeric Service Pack number All my own work
(CSD Version) for Windows NT 3.5
Detect Windows 95 from 16-bit mode Programmer's Guide for Windows 95
states Generic Thunk available.
According to Pietrek, in MSJ Extra (UK edition), October '94, Page 24,
in the Beta for Windows 95 [aka Windows 4.x, aka Chicago] GetVersion()
returns with the top two bits (31 and 30) set, so in a 32-bit program
code of the form:
DWORD version = GetVersion();
if( 0 == ( version & 0x80000000 ) ) // bit 31 not set
{
// it's NT
}
else
{
// it's not NT
if( ( version & 0xC0000000 ) == 0xC0000000 ) // bits 31 & 30 set
{
// it's Chicago
}
else
{
// it's Win32s
}
}
will detect the platform, but we do not use that here, because Pietrek
notes that the meaning of bit 30 may change [even though he then uses
this code to detect Win32s in the API Spy program in the December '94
issue (UK edition) of MSJ] and instead we use code recommeded by Robert
Hess of MS in the March 1994 issue of the MSDN newsletter, but only
after GetVersionEx() has failed.
When the program is running on a 16-bit version of Windows 3.1x or later
it uses the DOS interface to check the version of Windows. It does this
because Windows for Workgroups 3.11 tells lies (see MS Knowledgebase
articles Q106271 and Q113892), and reports that it is Windows 3.10, but
the DOS interface returns the correct version number. Even this does not
work on Windows 3.11 (*not* Windows for Workgroups 3.11), as both
interfaces, Windows and DOS, report 3.10, so the string from USER.EXE
is also obtained! Also, the recommended File Version from USER.EXE is
now reported.
<PERSONAL OPINION ON>
Development of this mixed-mode program is easier under the Borland environment,
in my opinion, because the GUI can be used under 16-bit and 32-bit Windows to
compile both 16-bit and 32-bit versions of the program, whereas the 32-bit GUI
from Microsoft only works under WIndows NT, and you have to resort to the
command-line tools to build a 32-bit version under 16-bit Windows (though even
that is not possible with Visual C++ 2) and you have to develop the program in
effectively four modes, since the results are different from 16-bit and 32-bit
versions of the program for the 16-bit and 32-bit OS's.
<PERSONAL OPINION OFF>
Modification History:
1.0 94-03-31 JMH Initial Version
1.1 94-04-28 JMH There are only two significant corrections. The first
is for a silly mistake in omitting to initialize the
bld_id string, though the original worked by accident!
The other correction is for a better check for Version
3.10 or later. The previous version checked for the
major version greater than 3 and the minor version
greater than 10, which is not *quite* correct. The
other changes are cosmetic. Changed the check to set up
bld_id so no 'else' is needed.
Corrected the spelling of 'both'. Changed the protected
mode check in the 16-bit function to check for 3.10
version (3.11?) from the use of a DPMI function to use
of the GetWinFlags() function.
Changed the 32-bit check for NT to use a macro, rather
than an embedded constant.
Defined a macro for the cases where the DOS version
check returns an 'unknown' result.
Added 'argsused' pragma for WinMain() when compiling
with Borland C++ 4.0.
Corrected spelling of 'Windows'!
Added the mode (Enhanced/Standard/Real) to the output
in 16-bit mode.
1.2 94-05-09 JMH Modified to report the underlying OS version number
when running on Windows NT. On the Windows NT 3.5
(aka Daytona) Beta (build 612) the WOW GetVersion
returns 3.10, presumably to kid 16-bit applications
that they are running on 3.10, in case they might
get confused if they saw 3.50 returned. Here the
underlying 32-bit version of GetVersion is called,
as described on the Daytona Beta 612 CD in the
\SCT\SAMPLES\INTEROP directory, to get the true
underlying Windows NT OS version and build.
1.3 94-08-10 JMH Modified to report the Build identity from USER.EXE
on Windows 3.10 and later. The Windows 3.11 OS/2 kicker
(*NOT* WFWG 3.11) still reports 3.10 to both Windows
and DOS programs that ask what version, but the build
string in USER.EXE actually says 3.11 correctly. Ain't
it clever! The program cannot analyze the string and
not print it out if it is the same as the version
reported by GetVersion(), as it contains (incorrectly
in my opinion) the string "3.1" and not "3.10". Also
changed the string for the "True" version report to
say "DOS reports ..." because who knows what may be
going on in future versions! Are there any more places
where a version number is made available? Rearranged
the output.
1.4 94-10-27 JMH Added the 32-bit GetVersionEx() interface to both
16-bit and 32-bit versions. In each case the program
does not assume that the function exists, so we have
to use LoadLibrary(), and so on, to find the function
and do run-time linking to it. The GetversionEx()
function was not present in Windows NT 3.1 or in
version 1.10 of Win32s, but is present in Windows
NT 3.5, and in Version 1.15 of Win32s.
Changed the 16-bit version so that it detects whether
or not the underlying OS supports the 32-bit thunk
interface when trying to work out whether we are on a
32-bit OS for getting underlying version information,
rather than relying on any flags. so that we *should*
be independent of any particular OS, though probably
it will only be valid on Windows NT.
Changed the flow of the code slightly so that for each
of the versions all output goes through a single call
to MessageBox().
Used conditional compilation to allow the 'basic'
version of the interfaces to be tested on systems that
support the Extended interface.
Changed so that the 16-bit version always reports the
DOS version, if available, when running on 16-bit host.
Altered the Base Address of the Microsoft-built 32-bit
version to 400000 (the Borland tools already have this
address as the default) to make the program a bit more
Chicago compatible.
Who would believe 'they' could invent so many ways of
finding a simple thing like a version number! Must
remember to get dates right this time!
1.5 94-11-06 JMH Slight change for compatibility with Visual C++ 2.0
(and presumably later versions of other tool suites),
to only define the OSVERSIONINFOA structure and its
associated VER_PLATFORM_WIN32... #defines when the
VER_PLATFORM_WIN32_NT macro is not already defined.
Added version number to Dialog Box text.
Added a header file for common version information.
1.6 95-01-01 JMH Changed name in output from Chicago to Windows 95.
Should have done this for previous version! Silly me!
Added code in 16-bit version to try and detect Windows
for Workgroups (see below), even when NT is the host,
though on NT the network always appears to be running
to a 16-bit program.
Changed the method used to report the 32-bit platform
name when GetVersionEx() exists so that cases other
than those recognized at the time of writing are
reported.
Minor changes to a couple of comments to try and
clarify the way things are done.
Corrected comment for origin of detection of underlying
version of NT 3.5, and alter both underlying detection
comments to reflect the final SDK issue.
Changed the name of the function pointer in the 16-bit
program that holds the address of the function that
gets the Win32s version information to more closely
match that of the actual function name, and slightly
simplified its use, as modern compilers don't have to
be told exactly when to use a pointer to call a function.
Changed field names in the WIN32SINFO structure to those
used in the Win32s Programmer's Reference.
Deleted pointer to extended version informnation in
16-bit and 32-bit code.
Added File Version information, which is the method that
is recommended by Microsoft to get the true version in
Knowledgebase article Q113892, to all the other stuff,
from USER.EXE for 16-bit and USER32.DLL for 32-bit.
Corrected a good many format strings to use %u for
version info not %d, as pedantically the numbers are
(D)WORD's, and therefore unsigned.
Added report of debug or retail version of Win32s to
16-bit version.
changed the name bld_id to dos_bld_id for clarity.
Corrected an error in the name of the Windows 95
definition for the platform.
Added a check for which version of Windows NT,
Workstation or Server, and made the 32-bit version
UNICODE/ASCII portable. NOTE, a UNICODE build does
not make any sense as a released version, since the
result would only run on Windows NT (and hardly
therefore qualifies as a portable 32-bit version
detector!) but it is done as a guide to those who
know their programs are going to run only on NT
and want to use UNICODE.
Changed slightly the local definition of
OSVERSIONINFO to make it UNICODE/ASCII portable.
I don't know the difference between the _UNICODE and
UNICODE defines, and when to use each, so if only
_UNICODE is defined the program defines UNICODE.
*NOTE* that I have only tested this code on Windows
NT Workstation 3.5 release version, which does *not*
have a string in the szCSDVersion field, so I *hope*
I have got that right, and the correct strings for a
Server. Note that the program does not distinguish
the names used for NT 3.1 (nothing and Advanced
Server) from those for NT 3.5 (Workstation and
Server), but uses the NT 3.5 names only. I cannot
find the Workstation/Server key and its value in the
NT 3.1 Resoruce Kit documentation to confirm the
values used here.
The 16-bit version does *not* try and get at the
registry to determine if the NT host is a Workstation
or a Server because the master key needed is not
readily available to a 16-bit program.
The projects do not use pre-compiled headers only
because several sets for Borland and MS gets rather
large.
Changed the output format for 32-bit versions so
that all now go <platform> <version> <build>
consistently.
Added the reasons for failure to the 16-bit Win32s
report if the GetWin32sInfo function is found but
returns a non-zero result.
Changed the name of WV_WINNT to WV_NO_WINNT to
properly reflect the meaning of the TRUE state of
the flag.
1.7 95-02-07 JMH Changed the 'wfwg_....' string pointers to 'far' to
overcome an apparent bug in the Borland C++ 4.5
compiler, which gets something wrong in the output
from the wsprintf() statement noted. However, it
does not take much to put it right, as almost any
reference to a string corrects the problem. The
point of change and the point where it went wrong
are noted by comments starting ++++. Fortunately
the Microsoft Visual C++ 1.51 compiler does not
mind if they are declared far or not.
In the 16-bit version when not running under
Windows NT add the code to extract a version string
(but *NOT* a part of the version resource) from the
WINVER.EXE file. Even for versions of WINVER.EXE
for Windows NT 3.1 and 3.5 (though they are not used
here) this always appears to be at offset 0x0207 in
the WINVER.EXE file, though this is only determined
by inspection! The program does a quick confidence
test, just in case!! This *appears* to be what the
16-bit version of MS' CONFIGSP program from the MSDN
CD does, but I have not tried to trace this through
to confirm it! However, on Windows 3.11 this reports
the version as 3.10, so while it may seem to be
good way of working out whether this is a Workgroups
installation it is *NO* good for the version. Who is
the twit in MS who decides these things? There is no
need to use this code when Windows NT is running
(though that could be said about getting the file
version from USER.EXE as well) as the program generic
thunks through to get the true underlying version.
Now that I have Service Pack 1 for Windows NT 3.5 I
can see that the szCSDVersion string was being
handled properly for both ASCII and UNICODE.
For 32-bit version leave the code to determine the
File Version behind but conditionally compile it
out of the build (but with a correction to report
the USER32.DLL file and not USER.EXE as before).
Corrected a minor, and extermely unlikely, bug in
the 16-bit code if the GetSystemDirectory were to
fail on the second call after the first call had
succeeded.
In the 16-bit version moved USER.EXE file version
detection so that it is not executed when Windows
NT is the host.
Added a pointer to the OSVERSIONINFO definition
and a function type for GetVersionEx in the 32-bit
code.
Made the UNICODE definition only apply to 32-bit
programs.
In the 16-bit output moved the DOS version up to
just after the version reported by GetVersion().
Output the Service Pack [aka CSD=Corrective
Service Diskette] number in the 32-bit version
of the program for the basic GetVersion interface.
This is the nearest that Windows NT 3.1 has to the
szCSDVersion string returned on Windows NT 3.5 in
the GetVersionEx() data structure. This should
only be needed on Windows NT 3.1, because the
extended interface does the job on NT 3.5, and
while this item does exist on NT 3.5 it is a
string when we are looking for the number used
on NT 3.1, so we allow for it being either type,
and for its absence, when the basic interface is
tested. Note that this entry in the registry is
not *quite* where GetVersionEx() gets its string
from, but this string in the registry is updated
to reflect the GetVersionEx() string when the
system boots up.
In the DPMI code that gets the version of Windows
that a DOS application sees clear the register
set structure to zeroes, just in case.
Changed 'temp' name to 'win32s_ver' for clarity.
Moved Windows for Workgroups check in the 16-bit
version so that it is only executed when 16-bit
Windows is the host as it always indicates that
the Windows for Workgroups network is running,
even on a stand-alone system with no network
whatsoever, when Windows NT is the host OS.
Corrected the code fragment in this comment, about
detecting Win32s/Chicago/NT, etc. as there was one
too few 0's in each constant.
Succumbed to the tempptation, and added even
more over the top code to get the type of Windows
NT host for a 16-bit program, using a Generic
Thunk to the underlying 32-bit functions that
access the Registry, requiring a slight change to
the 32-bit code. Note that for this we use the
CallProc32W function, rather than CallProcEx32W,
because the former works on both Windows NT 3.1,
while only Windows NT 3.5 supports the latter.
Because CallProc32W is a PASCAL function it is
not easy to define an appropriate prototype, so
there are actually three different versions of
it defined here, each taking a different number
of parameters, all of which are mapped by the
DEF file to the single CallProc32W function!
Added a function prototype for the Win32sInfo
function that get the Win32s version.
Changed 16-bit version to use STRICT compliance,
which required two module handles to be changed
from HANDLE to HINSTNACE.
Slightly changed access to the Windows NT
registry from the 16-bit version, and used it to
add further over the top code to add the service
pack data to the 16-bit version when Windows NT
is the host.
Split the change comments so that each change
has its own paragraph.
Changed WORKSTATION_... (KEY and VAL) to the more
appropriate PRODUCTTYPE_... (KEY and VAL).
Added a #define for the length of the string
returned from the registry for the type of the
Windows NT host.
1.8 95-03-15 JMH Cut definitions for OSVERSIONINFO as they are not
needed.
Added code to the 32-bit version to parse the
version from the Win32s.INI file if the GetVersionEx
function is not available. This is what happens on
version 1.10 and before, though I have never seen
version 1.0, and even Microsoft do not seem to know
where a copy was distributed. To get this to compile
for UNICODE (which is silly anyway!) required some
fiddling about with the header files and translated
function names, and the UNICODE version does not
compile on Borland C++ 4.5, but only the Win32s bit
which is silly as UNICODE anyway!
Added code to the 16-bit version to detect whether
Win32s is actually running. Clearly this makes no
sense in the 32-bit version! Presumably a program
that requires Win32s should only do an installation
when Win32s is not actually running, though if the
correct version of Win32s is already present the
Win32s bit of the installation can, of course, be
skipped.
Added numeric CSD Version to 16-bit and 32-bit code
when Windows NT is the host. This value does not exist
on Windows NT 3.1, where the CSD Version under the
CurrentVersion key is numeric. This should allow
an application that depends on a particular service
pack level to make sure that the system is already
configured appropriately.
minor rearrangement of code to always use reg32_access
function for access to registry. a different form of
the function is provided for each of 16-bit and 32-bit
programs.
1.9 95-04-18 JMH Changed 32-bit Visual C++ 2 project settings for
compatibility with the Visual C++ 2.1 subscription
release by adding 3.10 to the /subsystem:windows
flag so that Windows NT 3.1 can run the program.
Added the Windows 95 definition if the header files
have not already defined it, and removed conditional
compilation tests for it.
Used only bottom WORD of the GetVersionEx result for
the build number, as on Windows 95 (I've got a preview
copy at last - on 10th April 1995!) this appears to
put the version number into the top word, as on build
347 it contains 0x0400015B ( 4.0.347 ?? ).
Also, on Windows 95 the Generic Thunk interface works
(see Article 32 in the Windows 95 Programmer's Guide
from the Windows 95 SDK, though the GENTHUNK.TXT file
on the same CD still states that only Windows NT will
support Generic Thunks), so much to my surprise a
16-bit program on Windows 95 can get at the underlying
32-bit GetVersionEx and GetVersion functions. However,
under Windows 95 the build number is not present in the
GetVersion response, so modified the 16-bit output
preperation when the Generic Thunk works, so that under
Windows 95 the stuff that is exclusive to Windows NT is
not produced.
Changed the 16-bit program slightly so that it refers
to WOW (Windows On Windows) only when running on
Windows NT, otherwise just refer to Win16.
Added the DOS version number to the 16-bit program
running under Windows 95.
Further notes:
The results from the 16-bit version
===================================
Windows Windows Windows WFWG Windows Windows Windows
Version 3.10 3.11 3.11 NT 3.1 NT 3.5 95
File Version 3.10 3.11 3.11 3.10 3.10
GetVersion() 3.10 3.10 3.10 3.10 3.10 3.95
DOS 3.10 3.10 3.11
Build Id 3.10 3.11 3.11
WINVER.EXE string 3.10 3.10 3.11
Gen Thunk to GetVersion 3.10 3.50 4.00
Gen Thunk to GetVersionEx 3.10 3.50 4.00
It can be seen that (at the time of writing!) the most reliable method
for getting the correct version number of the host OS as binary data
(as opposed to the string provided by the Build ID) is to first check
for the availability of Generic Thunk support and then use the File
Version information from USER.EXE on the 16-bit versions of Windows,
as recommended in Q113892, and the Thunk layer where available, where
the GetVersion is simpler as it applies to all versions of Windows NT,
whereas the GetVersionEx thunk is only available on Windows NT 3.5,
so you have to be prepared to drop back to the GetVersion thunk if that
fails.
For the installation of Win32s programs should always use the 16-bit
function from a 16-bit program, as Win32s may not yet be installed.
The information returned appears to be a reliable indication of the
version of Win32s, and is consistent with the information returned
by the 32-bit family GerVersionEx function available in later versions.
Windows 95 (aka Chicago)
========================
This information represents the Preview version, build 347. Note,
that this is built with the commercially released tools and does
not need anything from the Win95 SDK.
In the 16-bit version of the program the Windows 95 check can use
the Generic Thunk interface. The documentation on the Windows NT SDK
indcated that the Generic Thunk interface would always be unique to
Windows NT (and that same document on the Windows 95 SDK CD still
indicates this). However, the Programmer's Guide on the Windows 95
SDK CD reports that the Generic Thunk interface is supported by
Windows 95, so the 16-bit program can use that interface to access
the 32-bit GetVersionEx or GetVersion functions. Note that if the
GetVersion interface is used the program can distinguish Windows NT
from Windows 95 by using the WF_WINNT bit in the value returned by
GetWinFlags.
In the 32-bit version the GetVersionEx function is available from
the Windows 95 host, so there is no problem there.
In both cases there is one minor problem with Windows 95, that the
build identity field returned in the GetVersionEx structure contains
(at least in the Preview build 347) 'rubbish' in the top word that
looks like the version number (04.00). In the registry there is a
DWORD version number that contains the same 'rubbish', so presumably
that is copied straight into the build field without removing the
version number part. Because of this the program uses only the low
WORD of the field when displaying the build number. Maybe this will
change for the final version.
Detecting Windows for Workgroups
================================
As of Version 1.6 the program does its best to detect whether it is
running under Windows for Workgroups. The program can easily detect
Windows for Workgroups on a networked system when the network is
running, as in Q114470, but this test is unreliable.
The problem is that Windows for Workgroups does not require a network,
and in such a mode there appears to be no obvious difference from a
basic (i.e. not Workgroup) Windows installation. The program looks for
the presence of WFWNET.DRV in the WINDOWS\SYSTEM directory as a further
indication of Windows for Workgroups, but this in itself is still a
very unreliable check. In the end we report what we find:
Workgroups found and running (because net detect says so)
Workgroups not found (net detect says no and no WFWNET.DRV found)
Workgroups found but not running (net detect says no but WFWNET.DRV found)
Note that the wording "Not found" is carefully chosen! It is intended
to indicate that just because it was "not found" does not mean this is
not a Windows for Workgroups system.
But if anybody reading this knows a more reliable test for Windows for
Workgroups ... other than the WINVER string, that is ...
In version 1.7 a check on a string from the WINVER.EXE file is included,
which contains the word "Workgroups" in the WFWG 3.11 version, but this
seems somewhat dubious, and I have no idea what is in the Windows for
Workgroups 3.10 string.
Again, if anybody knows a better Workgroups check ...
*/
#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ )
/* #define UNICODE /* uncomment this or define UNICODE on the command
line to create a UNICODE version of the 32-bit
program, though it's not of any practical use! */
#endif
#if defined( _UNICODE ) && !defined( UNICODE )
#define UNICODE /* just in case the alternate _UNICODE define is used */
#endif
#if defined( UNICODE ) && !defined( _UNICODE )
#define _UNICODE /* and the other way round! */
#endif
#define STRICT
#include <windows.h>
#include <stdlib.h> /* for _MAX_PATH */
#include <string.h> /* for strncpmi() and strstr() and _fmemset */
#if defined( _MSC_VER )
/* Borland C++ 4.5 does not have tchar.h (yet?),
so UNICODE version does not compile! */
#include <tchar.h>
#endif
#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ )
/* to get the Borland version to compile when not
using UNICODE define the translated name */
#if !defined( _tcstol )
#define _tcstol strtol
#endif
#endif /* of Borland translation */
#include "win_ver.h"
/* #define TEST_BASIC_IF /* remove comment starter at beginning of line
or define on command line to test basic 32-bit
I/F's rather than the extended I/F's */
/* #define INCLUDE_USER32_DLL /* remove comment starter at beginning of line
or define on command line to include the version
of USER32.DLL in the 32-bit version output */
#if !defined( TEXT ) /* have to do this for 16-bit compiles */
#define TCHAR char
#define PTCHAR char*
#define TEXT( str ) str
#endif
#define PROG_NAME TEXT( "Windows Version Program : V" ) \
TEXT( PROG_MAJOR_VERS_STRING ) TEXT( "." ) \
TEXT( PROG_MINOR_VERS_STRING )
/* some of the things are defined here because they are not
available in any of the header files that ship with some
of the standard C/C++ development environments currently
available, as presented here it will build with the retail
versions of Microsoft VC++ 1.5 or 1.51 or 1.52 (from the
VC++ 2.x packages) for 16-bit and VC++ 1.10 (aka 1.0)
32-bit edition or VC++ 2.0 or VC++ 2.1, and the Borland
C++ 4.02 or C++ 4.5 packages for both 16-bit and 32-bit. */
#define WF_WINNT 0x4000 /* flag for WOW in 16-bit Windows, 1 == WOW == NT */
#define WV_NO_WINNT 0x80000000 /* flag for not NT in 32-bit GetVersion(), 1 == not NT */
#define W32SYS "W32SYS.DLL" /* name of Win32s DLL */
#define WIN32SINF "GETWIN32SINFO" /* name of GetWin32sInfo function in W32SYS.DLL */
#define WIN32S_INI "\\SYSTEM\\WIN32S.INI"
/* name of Win32s.INI file in the Windows directory */
#define WIN32S_SECT "Win32s" /* section in Win32s.INI file */
#define WIN32S_KEY "Version" /* key in Win32s.INI file for version data */
#define KRNL386 "KRNL386.EXE" /* name of Krnl386 EXE */
#define LDLIB32W "LOADLIBRARYEX32W"
/* name of LoadLibraryEx32W function in KRNL386.EXE */
#define CALLPROCEX "_CALLPROCEX32W"/* name of CallProcEx32W function in Kernel32.DLL.
note that this does *not* exist on Windows NT 3.1,
only on Windows NT 3.5 and later. */
#define USER_EXE "USER.EXE" /* name of USER.EXE file */
#define BUILD_ID 516 /* number of Build string in USER.EXE */
#define USER32_DLL "USER32.DLL" /* name of USER32.DLL file on Windows NT */
#define KERNEL32 "KERNEL32.DLL" /* name of module that holds GetVersion(Ex) functions */
#define GETVERN "GetVersion" /* name of GerVersion function in Kernel32.DLL */
#define FILEINFO_KEY "\\" /* name of file info root block for getting file
version information */
#define VER_UNKNOWN 0xFFFF /* value when can't get version from DOS */
/* data for Windows for Workgroups network detection */
#define WFWG_NOT_FOUND 0 /* value when WFWG not found */
#define WFWG_FOUND 1 /* value when WFWG found but not running */
#define WFWG_RUNNING 2 /* value when WFWG found and running */
/* data for Workgroups detection from WINVER.EXE */
#define WINVER_EXE "WINVER.EXE" /* name of WINVER.EXE file */
#define WINVER_OFFSET 0x0207 /* offset of windows version string in that file */
#define WINVER_LENGTH 64 /* number of chars to read */
/* data for Service Pack detection on Windows NT 3.1 */
#define REG_CSD_KEY "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"
/* name of the registry entry for the CSD version */
#define REG_CSD_DATA "CSDVersion" /* name of CSD version data - only needed for NT 3.1 */
#define CSD_TO_SP( xx ) ( ( xx ) >> 8 ) /* to convert CSD Version number to the Service Pack
number. I have seen no documentation for this, so
maybe something like HIBYTE( LOWORD( xx ) ) should
be used, but I have not traced through the WINVER
from Windows NT 3.1 to see how it is worked out. */
/* data for the extended get version information */
#if !defined( UNICODE )
/* using ASCII version of extended interface */
# define GETVERNEX "GetVersionExA" /* name of GetVersionEx function in Kernel32.DLL,
note that this is in W32sCOMB.DLL in Win32s,
but asking for it in the KERNEL32.DLL module
nevertheless still works properly. */
#else /* UNICODE */
/* using UNICODE version of extended interface */
# define GETVERNEX "GetVersionExW" /* only sensible in Windows NT */
#endif
#define CSD_STR_SIZE 128 /* MS do not define a macro for this */
#if !defined( TEST_BASIC_IF ) /* declarations only relevent for new interface */
#if !defined( VER_PLATFORM_WIN32_NT ) /* if already defined don't do again. this is
defined in the headers for MS Visual C++ 2
and Borland C++ 4.5 but not in the earlier
versions, and not for 16-bit compilations. */
typedef struct _OSVERSIONINFO { /* Extended Vern Info from GetVersionEx() */
DWORD dwOSVersionInfoSize; /* set to size of structure */
DWORD dwMajorVersion; /* OS major version */
DWORD dwMinorVersion; /* OS minor version */
DWORD dwBuildNumber; /* OS build number */
DWORD dwPlatformId; /* OS platform type */
TCHAR szCSDVersion[ CSD_STR_SIZE ]; /* description string */
} OSVERSIONINFO, *LPOSVERSIONINFO;
/* the values returned in the PlatformID field of the Extended Version
information to indicate which version of the 32-bit platform is in use. */
#define VER_PLATFORM_WIN32s 0 /* Win32s on Windows 3.1x */
#define VER_PLATFORM_WIN32_WINDOWS 1 /* Win32 on Windows 95. */
#define VER_PLATFORM_WIN32_NT 2 /* Windows NT */
#endif /* of extended version info already defined */
#if !defined( VER_PLATFORM_WIN32_WINDOWS ) /* Windows 95 value for platform ID not
defined in the header files VC++ 2.0
or 2.1 or BC++ 4.5, so define it now */
#define VER_PLATFORM_WIN32_WINDOWS 1
#endif
#endif /* of testing full interface */
#if !defined( WIN32 ) && !defined( _WIN32 ) && !defined( __WIN32__ )
/* prototypes for functions that permit the program to
thunk from this 16-bit world to a 32-bit module.
the DEF file identifies these as in the Kernel,
and it does not matter if they are not, as they
will only be called if the earlier checks OK it */
DWORD FAR PASCAL LoadLibraryEx32W( LPCSTR, DWORD, DWORD );
BOOL FAR PASCAL FreeLibrary32W( DWORD );
DWORD FAR PASCAL GetProcAddress32W( DWORD, LPCSTR );
DWORD FAR PASCAL CallProc32W__0( DWORD, DWORD, DWORD ); /* NT 3.1, et seq */
#if !defined( TEST_BASIC_IF )
DWORD FAR CDECL CallProcEx32W( DWORD, DWORD, DWORD, ... ); /* NT 3.5, et seq */
#endif /* of testing only basic interface */
#endif /* of 16-bit Windows NT and Windows 95 generic thunk function prototypes */
#if __BORLANDC__ >= 0x0452
/* on Borland C++ 4.x exclude exception handling code */
void _ExceptInit( void )
{
;
}
#endif
#ifdef __BORLANDC__
#pragma argsused
#endif
/* the function that drives the whole thing */
int PASCAL WinMain( HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpszCmdLine,
int nCmdShow )
{
DWORD win_vers;
DWORD maj_vers;
DWORD min_vers;
TCHAR file_ver[ 128 ] = TEXT( "" ); /* file version string created from USER.??? */
TCHAR buff[ 1024 ]; /* results buffer */
int chrs; /* characters in results buffer so far */
extern PTCHAR nt_type( void ); /* function that works out type of Windows NT */
extern PTCHAR nt_csd_num( void ); /* function to get numeric CSD number as string */
extern BOOL reg32_access( PTCHAR, PTCHAR, LPDWORD, LPBYTE, LPDWORD );
/* function to read value from the registry */
#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ )
/* ====================================
program is being compiled for 32-bit
==================================== */
#define WIN_BITS "32"
/* by comparison with the 16-bit code the 32-bit code is relatively
straightforward! There are only two functions GetVersion() and
GetVersionEx(). The latter is available on version 3.5 of Windows NT
but not on version 3.1 of Windows NT, and on version 1.15 build 103
of Win32s, but not on version 1.10 build 88 of Win32s. Note that
when we find Win32s and report the underlying Windows version we do
*not* take any steps to determine the correct version number!!! */
#if !defined( TEST_BASIC_IF ) /* normally included in build - define for testing */
typedef BOOL ( WINAPI *GVEX_PTR )( LPOSVERSIONINFO );
/* type of ptr to GetVersionEx function */
HINSTANCE hKernel32; /* handle to 32-bit KERNEL32.DLL */
GVEX_PTR Get_Version_Ex; /* function ptr to GetVersionEx */
OSVERSIONINFO os_vn_inf = { sizeof( OSVERSIONINFO ) };
/* initialized Extended Version information */
#endif
#if defined( INCLUDE_USER32_DLL ) /* chop this out of the build for version 1.7
but leave the code behind */
#include <winver.h> /* not included by windows.h - different
name for 16-bit and 32-bit windows */
TCHAR user_name[ _MAX_PATH ]; /* constructed name of USER32.DLL file */
/* first try for platform independent stuff (though this will
silently fail on Win32s) this is for file version from the
USER32.DLL file. here errors are checked but not reported.
this is just to demonstrate the consistency, because
unlike 16-bit Windows the 32-bit GetVersion() function
always [so far on NT 3.1 and NT 3.5!] tells the truth.
UNICODE versions of the functions uses wide characters. */
if( GetSystemDirectory( user_name, _MAX_PATH ) != 0 )
{
DWORD dummy; /* ignored on 32-bit */
DWORD inf_size; /* how big is version info buffer */
PBYTE ver_buff; /* ptr to mem allocated for ver info buff */
VS_FIXEDFILEINFO *fixed_info; /* ptr to fixed info in ver info buff */
UINT fixed_size; /* number of bytes read for ver info */
/* add name of user32.dll to system directory. */
lstrcat( user_name, TEXT( "\\" ) TEXT( USER32_DLL ) );
if( ( inf_size = GetFileVersionInfoSize( user_name, &dummy ) ) != 0 )
{
/* how big is version info buffer required */
ver_buff = GlobalAlloc( GPTR, inf_size );
if( ver_buff != NULL )
{
/* and get the information */
if( GetFileVersionInfo( user_name, 0, inf_size, ver_buff ) != FALSE )
{
/* get ptr to fixed version information in version info buffer */
if( VerQueryValue( ver_buff, TEXT( FILEINFO_KEY ),
(PVOID *)&fixed_info, &fixed_size ) != 0 )
{
/* okay - add file version to build information */
wsprintf( file_ver, TEXT( "\n" ) TEXT( USER32_DLL ) TEXT( " File Version %lu.%lu" ),
HIWORD( fixed_info->dwFileVersionMS ),
LOWORD( fixed_info->dwFileVersionMS ) );
}
}
/* free memory for version buffer */
GlobalFree( ver_buff );
}
}
}
#endif /* of code chopped out of build for version 1.7 */
#if !defined( TEST_BASIC_IF ) /* code normally included in build - define for testing */
/* now try for extended version information - the function is
run-time linked so that we can use simple information if
the extended information is not available on this version
of the OS. Here normal 32-bit functions are used. the name
given to GetProcAddress *NEVER* uses wide characters, but
the name given to GetModuleHandle does - go figure. */
if( ( hKernel32 = GetModuleHandle( TEXT( KERNEL32 ) ) ) != NULL &&
( Get_Version_Ex = (GVEX_PTR)GetProcAddress( hKernel32, GETVERNEX ) ) != NULL )
{
/* got Extended Version function ptr so use it */
PTCHAR platform_type;
TCHAR unknown_str[ 32 ];
/* and get data */
Get_Version_Ex( &os_vn_inf );
/* set up name of 32-bit platform */
switch( os_vn_inf.dwPlatformId )
{
case VER_PLATFORM_WIN32_NT:
platform_type = TEXT( "Windows NT" );
break;
case VER_PLATFORM_WIN32s:
platform_type = TEXT( "Win32s" );
break;
case VER_PLATFORM_WIN32_WINDOWS:
platform_type = TEXT( "Windows 95" );
break;
default:
/* cater for unknown platform types */
wsprintf( unknown_str, TEXT( "Unknown type %lu" ), os_vn_inf.dwPlatformId );
platform_type = unknown_str;
}
/* here use only low word of build identity for Windows 95
compatiblity, as on build 347 it contains 0x0400015B */
chrs = wsprintf( buff, TEXT( "%s Version %u.%02u Build %u\n" )
TEXT( "%s" ),
platform_type,
os_vn_inf.dwMajorVersion,
os_vn_inf.dwMinorVersion,
LOWORD( os_vn_inf.dwBuildNumber ),
os_vn_inf.szCSDVersion
);
switch( os_vn_inf.dwPlatformId )
{
case VER_PLATFORM_WIN32s:
/* on Win32s append version of underlying 16-bit Windows */
win_vers = GetVersion();
chrs += wsprintf( buff + chrs, TEXT( "\n" )
TEXT( "running on\n" )
TEXT( "Windows Version %u.%02u" ),
LOBYTE( LOWORD( win_vers ) ),
HIBYTE( LOWORD( win_vers ) ) );
break;
case VER_PLATFORM_WIN32_NT:
/* on Windows NT report if workstation or some kind of
server and add the numeric Service Pack information */
chrs += wsprintf( buff + chrs, TEXT( "\n" )
TEXT( "Windows NT %s" )
TEXT( "%s" ),
nt_type(), nt_csd_num() );
break;
}
/* end of extended version code */
}
else
{
/* we could not get extended version information so go with basic */
#endif
/* GetVersion() always exists so it is compile-time linked */
win_vers = GetVersion();
/* save version information */
maj_vers = LOBYTE( LOWORD( win_vers ) );
min_vers = HIBYTE( LOWORD( win_vers ) );
if( ( win_vers & WV_NO_WINNT ) == 0 )
{
/* this is real Windows NT */
union
{
DWORD csd_no; /* CSD as number in NT 3.1 */
TCHAR csd_str[ CSD_STR_SIZE + 1 ]; /* CSD as string in NT 3.5 */
} csd_data; /* CSD as number or string */
DWORD csd_type; /* CSD value type */
DWORD csd_size = sizeof( csd_data ); /* CSD size in bytes -
even for UNICODE */
/* try and get the CSD Version (Service Pack) value from the Registry */
if( reg32_access( TEXT( REG_CSD_KEY ), TEXT( REG_CSD_DATA ),
&csd_type, (LPBYTE)&csd_data, &csd_size ) )
{
/* display depends on the data type from the Registry */
switch( csd_type )
{
case REG_DWORD:
/* this is Windows NT with numeric CSD version -
this is the code used on Windows NT 3.1 by the
basic interface - form a service pack string */
wsprintf( csd_data.csd_str, TEXT( "Service Pack %lu (CSD Version 0x%lX)\n" ),
CSD_TO_SP( csd_data.csd_no ), csd_data.csd_no );
break;
case REG_SZ:
/* this should only be used when testing the basic
interface on Windows NT 3.5 - add line terminator */
lstrcat( csd_data.csd_str, TEXT( "\n" ) );
break;
default:
/* this should never happen! */
lstrcpy( csd_data.csd_str, TEXT( "??\n" ) );
/* drop through and out */
}
/* form full Windows NT version string */
chrs = wsprintf( buff, TEXT( "Windows NT Version %u.%02u Build %u\n" )
TEXT( "%s" )
TEXT( "Windows NT %s" )
TEXT( "%s" ),
maj_vers, min_vers, HIWORD( win_vers ) & 0x7FFF,
csd_data.csd_str,
nt_type(), nt_csd_num() );
}
}
else
{
/* yes - I know this bit is idiotic as UNICODE!
and it does cause some grief. */
if( maj_vers < 4 )
{
/* this is Win32s running on 16-bit Windows 3.1x, the
program is running in 32-bit mode and the extended
information is not available but although we are
getting desperate before we bottle out we try for
information from the Win32s.INI file */
TCHAR file_name[ _MAX_PATH ]; /* space for full INI file name */
TCHAR ver_buff[ 32 ]; /* space for string from INI file */
PTCHAR ver_ptr = ver_buff; /* ptr to string from INI file */
LONG win32s_maj; /* Win32s major version */
LONG win32s_min; /* Win32s minor version */
LONG win32s_bld; /* Win32s build number */
/* the default string is empty so we can recognize the
case where it is returned - the documentation says
that if we do not supply a full path name then the
GetPrivateProfileString function should look in the
Windows directory, but it appears not to work here,
so we make the full name ourselves. the string is
parsed as we go along. the various parts of the
version number are handled as longs. note that we
use strtol (portably _tcstol) rather than atoi
(portably _ttoi) as it serves three purposes at once,
(1) it gets the number (2) it steps the pointer to
the next character of the string, (3) it allows us
to check [following from (2)] that exactly all the
characters expected were read as part of the number.
all three numbers must be followed by a full stop. */
if( 0 != GetWindowsDirectory( file_name, _MAX_PATH ) &&
0 != GetPrivateProfileString( TEXT( WIN32S_SECT ), TEXT( WIN32S_KEY ),
TEXT( "" ), ver_ptr, sizeof( ver_buff ) / sizeof( TCHAR ),
lstrcat( file_name, TEXT( WIN32S_INI ) ) ) &&
( win32s_maj = _tcstol( ver_ptr, &ver_ptr, 10 ), TEXT( '.' ) == *ver_ptr ) &&
( win32s_min = _tcstol( ++ver_ptr, &ver_ptr, 10 ), TEXT( '.' ) == *ver_ptr ) &&
( win32s_bld = _tcstol( ++ver_ptr, &ver_ptr, 10 ), TEXT( '.' ) == *ver_ptr ) )
{
/* we cannot tell if this is the debug or retail version,
but we warn the user the data has come from the INI file!
note that the version number is formatted as %ld.%ld rather
than %ld.%02ld because the version 1.10 INI file contains
version 1.1 and we must not report the identity as 1.01. */
chrs = wsprintf( buff, TEXT( "Win32s Version %ld.%ld Build %ld\n" )
TEXT( "(with data from WIN32S.INI file)\n" )
TEXT( "running on\n" )
TEXT( "Windows Version %u.%02u" ),
win32s_maj, win32s_min, win32s_bld,
maj_vers, min_vers );
}
else
{
/* the version number from the Win32s.INI file is no
good so we cannot easily determine the version of
Win32s as that info would have to come from a 16-bit
DLL - good innit. the program *could* work out the
version, but it would require a 16-bit DLL to thunk
to, and that just is not worth the effort. Bottle
out and tell the user to run the 16-bit version. */
chrs = wsprintf( buff, TEXT( " Win32s running on Version %u.%02u\n" )
TEXT( "\t of Windows\n" )
TEXT( "\n" )
TEXT( " Run 16-bit version for information\n" )
TEXT( " on Version of Win32s\n" )
TEXT( "and for additional 16-bit build information" ),
maj_vers, min_vers );
}
}
else
{
/* this is Windows 95 - aka Chicago - how did we ever get here as
GetVersionEx (or GetVersion!) should have already done its thing. */
chrs = wsprintf( buff, TEXT( "??? Windows 95 Version %u.%02u" ),
maj_vers, min_vers );
}
}
#if !defined( TEST_BASIC_IF )
}
#endif
/* however we got here add possibly empty file version string */
wsprintf( buff + chrs, TEXT( "%s" ), file_ver );
/* ==========================
end of 32-bit Windows code
========================== */
#else
/* ============================
start of 16-bit Windows code
============================ */
#if defined( UNICODE )
#error UNICODE for a 16-bit program ???
#endif
#include <ver.h> /* not included by windows.h - different
name for 16-bit and 32-bit windows */
#define WIN_BITS "16"
/* compared with the 32-bit code this is horrible, though ironically
it is tidier when Windows NT is the host. It tries for a host
32-bit OS. If that fails it proceeds with 16-bit detection,
looking for Win32s as well as checking for version numbers known
to DOS and the USER.EXE module, the file version from USER.EXE,
and the Windows string from the WINVER.EXE file. */
#if !defined( REG_DWORD ) /* we have to define these for 16-bit version */
#define REG_SZ 1
#define REG_DWORD 4
#endif
DWORD win_flags;
HINSTANCE hKrnl386; /* instance handle for Krnl386.EXE */
/* first do the bits that are host independent - simple GetVersion */
win_vers = GetVersion();
/* save basic version number */
maj_vers = LOBYTE( LOWORD( win_vers ) );
min_vers = HIBYTE( LOWORD( win_vers ) );
/* OK - done system independent stuff - now system dependent */
win_flags = GetWinFlags();
/* detect whether the underlying OS can support the functions
that would allow a 16-bit program to thunk to that underlying
32-bit OS. we don't need the function address, as this is
only to check that the function we need is present, so don't
bother to save it. the WF_WINNT check could be used, but by
ignoring that and doing it this way we are not dependant on
any particular flags to tell us which version of the OS we are
on. If the LoadLibraryEx32W function is present we use it,
[even though that almost certainly means only on Windows NT].
Well, on 10th April 1995 the previous statement in [ ] was
proved wrong when I finally got my hands on a Preview copy of
Windows 95, which *does* support the Generic Thunk stuff, so
not using the WF_WINNT flag proved to be the correct choice
way back in May 1995! Sheesh! note that neither the value from
the 16-bit GetVersion nor from the 16-bit GetWinFlags function
appears to indicate if this is running on Windows 95. */
if( /* win_flags & WF_WINNT - *NO* - the following works on Windows 95
where that flag is not set - testing this longer way is better */
( hKrnl386 = GetModuleHandle( KRNL386 ) ) != NULL &&
GetProcAddress( hKrnl386, LDLIB32W ) != NULL )
{
/* Program is running on Windows NT, or on some other system
(Windows 95? - I thought maybe not - until I found out to the
contrary on 10th April 1995) that supports LoadLibraryEx32W
so use it to get version data from the underlying 32-bit OS */
DWORD hKernel32; /* handle to 32-bit DLL while thunking */
DWORD Get_Version; /* ptr to 32-bit function that gets version. */
#if !defined( TEST_BASIC_IF )
OSVERSIONINFO os_vn_inf = { sizeof( OSVERSIONINFO ) };
/* initialized extended version information */
DWORD Get_Version_Ex; /* ptr to Extended Version function */
/* first try for extended version information - we go through
this so that we can use simple information if the extended
information is not available on this version of the OS, as
Windows NT 3.1 does not provide Extended Version information.
Here the program uses the thunk interface to call from the
16-bit version of the program 'through' to the 32-bit world.
the KRNL386 check is because the CallProcEx32W function did
not appear till Windows NT 3.5, so we can't use the extended
functions on Windows NT 3.1, and we don't want an unresolved
Dynalink message to stop the program, but we don't need to
save the address of the function. So, do checks and get data.
We assume that if the CallProcEx32W() function does not exist
then nor does the GetVersionEx() function.
The CallProcEx32W function passes 1 parameter that requires
address translation to the GetVersionEx() function.
if any check fails, or we fail to get data, we go on to try
for basic data.
Note, that it is *VITAL* that hKernel32 is obtained *FIRST*
so that is is *ALWAYS* done (the rest may not be!), as it
is assumed to be set up when getting the basic version. */
if( ( hKernel32 = LoadLibraryEx32W( KERNEL32, NULL, 0 ) ) != NULL &&
( Get_Version_Ex = GetProcAddress32W( hKernel32, GETVERNEX ) ) != NULL &&
GetProcAddress( hKrnl386, CALLPROCEX ) != NULL &&
CallProcEx32W( 1, 1, Get_Version_Ex, (OSVERSIONINFO FAR *)&os_vn_inf ) == TRUE )
{
char *platform_type;
char unknown_str[ 32 ];
/* free library when done. */
FreeLibrary32W( hKernel32 );
/* set up name of 32-bit platform though
unlikely to be other than Windows NT */
switch( os_vn_inf.dwPlatformId )
{
case VER_PLATFORM_WIN32_NT:
platform_type = "Windows NT";
break;
case VER_PLATFORM_WIN32s:
/* this will never happen, as Win32s
does not support Generic Thunks */
platform_type = "Win32s";
break;
case VER_PLATFORM_WIN32_WINDOWS:
platform_type = "Windows 95";
break;
default:
/* cater for unknown platform types */
wsprintf( unknown_str, "Unknown type %lu", os_vn_inf.dwPlatformId );
platform_type = unknown_str;
}
/* prepare output buffer and give user information */
chrs = wsprintf( buff, "%s reports OS Version %u.%2u\n"
"running on\n"
"%s Version %u.%02u Build %u\n"
"%s\n",
(LPCSTR)( win_flags & WF_WINNT ? "WOW (Win16)" : "Win16" ),
LOBYTE( win_vers ), HIBYTE( win_vers ),
(LPCSTR)platform_type,
(WORD)os_vn_inf.dwMajorVersion,
(WORD)os_vn_inf.dwMinorVersion,
(WORD)os_vn_inf.dwBuildNumber,
(LPCSTR)os_vn_inf.szCSDVersion );
switch( os_vn_inf.dwPlatformId )
{
case VER_PLATFORM_WIN32_NT:
/* if Windows NT is the host add the type and CSD version */
wsprintf( buff + chrs, "Windows NT %s"
"%s",
(LPCSTR)nt_type(), (LPCSTR)nt_csd_num() );
break;
case VER_PLATFORM_WIN32s:
/* if Wind32s add the DOS version number - should never happen */
case VER_PLATFORM_WIN32_WINDOWS:
/* if Windows 95 add the DOS version number */
wsprintf( buff + chrs, "with DOS version %d.%02d",
HIBYTE( HIWORD( win_vers ) ),
LOBYTE( HIWORD( win_vers ) ) );
break;
}
}
else
{
#else
/* if testing basic interfaces get library handle */
hKernel32 = LoadLibraryEx32W( KERNEL32, NULL, 0 );
#endif
/* failed to get extended information - try for basic.
under normal circumstances (i.e. when not testing)
this should only run if Windows NT 3.1 is the host. */
if( hKernel32 == NULL ||
( Get_Version = GetProcAddress32W( hKernel32, GETVERN ) ) == NULL )
{
/* somehow we failed to get a handle to KERNEL32.DLL, or the
address of the GetVersion function - this should never happen!!! */
wsprintf( buff, "Error\n"
"\n"
"No %s" KERNEL32 " module\n"
"\n"
"???? - How can this be - ????",
hKernel32 != NULL ? GETVERN " function in" : "" );
if( hKernel32 != NULL )
{
FreeLibrary32W( hKernel32 );
}
}
else
{
DWORD nt_vers; /* underlying OS version and build */
/* get OS version number reported by the 32-bit OS.
no parameters, so no translation. note that here the
CallProc32W function is used, as the CallProcEx32W
function does not exist on Windows NT 3.1, and we
want this program to work on all Windows NT versions. */
nt_vers = CallProc32W__0( Get_Version, 0, 0 );
/*free the library when done */
FreeLibrary32W( hKernel32 );
/* prepare output buffer and give user information */
chrs = wsprintf( buff, "%s reports OS Version %u.%2u\n"
"running on\n"
"Windows %s Version %u.%02u",
(LPCSTR)( win_flags & WF_WINNT ?
"WOW (Win16)" : "Win16" ),
(WORD)maj_vers, (WORD)min_vers,
(LPCSTR)( win_flags & WF_WINNT ?
"NT" : "95" ),
LOBYTE( LOWORD( nt_vers ) ),
HIBYTE( LOWORD( nt_vers ) ) );
if( win_flags & WF_WINNT )
{
/* if Windows NT is the host add the build number
(which is not present in the GetVersion response
on Windows 95) and the type and CSD version */
/* data for access to registry for Service Pack level */
union
{
DWORD csd_no; /* CSD as number in NT 3.1 */
char csd_str[ CSD_STR_SIZE + 1 ]; /* CSD as string in NT 3.5 */
} csd_data; /* CSD as number or string */
DWORD csd_type; /* CSD value type */
DWORD csd_size = sizeof( csd_data ); /* CSD size in bytes */
/* get service pack info from the Windows NT registry */
if( reg32_access( REG_CSD_KEY, REG_CSD_DATA,
&csd_type, (LPBYTE)&csd_data, &csd_size ) == TRUE )
{
/* display depends on the data type from the Registry */
switch( csd_type )
{
case REG_DWORD:
/* this is Windows NT with numeric CSD version -
this is the code used on Windows NT 3.1 by the
basic interface - form a service pack string */
wsprintf( csd_data.csd_str, "Service Pack %lu (CSD Version 0x%lX)\n",
CSD_TO_SP( csd_data.csd_no ) , csd_data.csd_no );
break;
case REG_SZ:
/* this should only be used when testing the basic
interface on Windows NT 3.5 - add line terminator */
lstrcat( csd_data.csd_str, "\n" );
break;
default:
/* this should never happen! */
lstrcpy( csd_data.csd_str, "??\n" );
/* drop through and out */
}
}
wsprintf( buff + chrs,
" Build %u\n"
"%s"
"Windows NT %s"
"%s",
HIWORD( nt_vers ) & 0x7FFF,
(LPCSTR)csd_data.csd_str,
(LPCSTR)nt_type(),
(LPCSTR)nt_csd_num() );
}
else
{
/* on Windows 95 (but never on Win32s!!)
add the DOS version number */
wsprintf( buff + chrs, "\n"
"with DOS version %d.%02d",
HIBYTE( HIWORD( win_vers ) ),
LOBYTE( HIWORD( win_vers ) ) );
}
}
#if !defined( TEST_BASIC_IF )
}
#endif
/* end of code for Windows NT as host */
}
else
{
/* Program is running on real 16-bit Windows 3.1x - here the program
can find out which version, if any, of Win32s is available, and
which particular build of Windows 3.1x this really is */
char file_name[ _MAX_PATH ]; /* constructed name of a file */
/* space for WINVER.EXE data */
char winver_buff[ WINVER_LENGTH + 1 ] = "";
/* string from WINVER.EXE file,
also used for result string */
/* data for Win32s */
typedef struct
{
BYTE bMajor; /* Win32s major version number */
BYTE bMinor; /* Win32s minor version number */
WORD wBuildNumber; /* Win32s build number */
BOOL fDebug; /* Win32s retail/debug flag */
} WIN32SINFO, far *LPWIN32SINFO;
typedef int ( WINAPI *GW32S_PTR )( LPWIN32SINFO );
/* type of ptr to GetWin32sInfo function */
HINSTANCE h_win32s; /* handle to Win32s library */
GW32S_PTR Get_Win32s_Info; /* address of Win32s version function */
WIN32SINFO win32s_info; /* the Win32s version information */
char win32s_ver[ 128 ]; /* filled with Win32s version - if any */
/* data for USER.EXE build string */
HINSTANCE h_userexe; /* handle to USER.EXE library */
char bld_temp[ 64 ] = ""; /* space for build string */
char bld_str[ 64 ] = ""; /* space for build result */
/* data for DOS build string */
char dos_bld_id[ 64 ] = ""; /* for DOS build information */
/* ++++ these are only declared 'far' to keep the
code working when the Borland C++ 4.5 compiler is used */
char far *wfwg_string;
char far *wfwg_spacer;
WORD win_for_wkg_check( void ); /* our function to check if this
is Windows for Workgroups */
WORD get_dos_win_ver( void ); /* our function to ask DOS what
version of Windows it thinks */
BOOL win32s_running( void ); /* our function to detect if
Win32s is actually running */
/* get file version information from USER.EXE file -
the only way of getting the true version number! */
if( GetSystemDirectory( file_name, _MAX_PATH ) != 0 )
{
/* this is slightly simplified since V1.6 since now that it
is not excecuted under Windows NT we do not have to worry
about the possible alternate directory for the file */
DWORD inf_handle; /* handle to version information in USER.EXE */
DWORD inf_size; /* how big is version info buffer */
LPBYTE ver_buff; /* ptr to mem allocated for ver info buff */
VS_FIXEDFILEINFO FAR *fixed_info; /* ptr to fixed info in ver info buff */
UINT fixed_size; /* number of bytes read for ver info */
/* add file name to directory */
lstrcat( file_name, "\\" USER_EXE );
/* see if there is a file with constructed name that contains version info */
if( ( inf_size = GetFileVersionInfoSize( file_name, &inf_handle ) ) != 0 )
{
HGLOBAL h_mem;
/* we have an appropriate file name and the size
of version info buffer - allocate space for it */
if( NULL != ( ver_buff = (LPBYTE)GlobalLock( h_mem = GlobalAlloc( GPTR, inf_size ) ) ) )
{
/* get version information */
if( GetFileVersionInfo( file_name, inf_handle, inf_size, ver_buff ) != 0 )
{
/* get ptr to version info in buffer */
if( VerQueryValue( ver_buff, FILEINFO_KEY,
(LPVOID FAR*)&fixed_info, &fixed_size ) != 0 )
{
wsprintf( file_ver, USER_EXE " File Version %u.%u\n",
HIWORD( fixed_info->dwFileVersionMS ),
LOWORD( fixed_info->dwFileVersionMS ) );
}
}
GlobalUnlock( h_mem );
GlobalFree( h_mem );
}
}
}
/* set up WFWG information */
switch( win_for_wkg_check() )
{
case WFWG_NOT_FOUND:
wfwg_string = "Not Found";
wfwg_spacer = " ";
break;
case WFWG_FOUND:
wfwg_string = "Found\n but Network not Running";
wfwg_spacer = " ";
break;
case WFWG_RUNNING:
wfwg_string = "Found\n and Network Running";
wfwg_spacer = " ";
break;
default:
wfwg_string =
wfwg_spacer = "";
}
/* get Windows version string from the WINVER.EXE file
which should be in the main Windows directory */
if( GetWindowsDirectory( file_name, _MAX_PATH ) != 0 )
{
HFILE h_winver;
/* add the file name to the Windows directory name */
lstrcat( file_name, "\\" WINVER_EXE );
/* see if we can open the file */
if( ( h_winver = _lopen( file_name, READ ) ) != HFILE_ERROR )
{
/* seek to start of string and read it in */
if( _llseek( h_winver, WINVER_OFFSET, SEEK_SET ) != HFILE_ERROR &&
_lread( h_winver, winver_buff, WINVER_LENGTH ) == WINVER_LENGTH )
{
/* make sure string is terminated */
winver_buff[ WINVER_LENGTH ] = '\0';
/* check that the string looks sensible - i.e. it starts with
"Windows" - using the standard C function for the compare
as the Windows function does not allow the definition of the
length, and see if the string contains "Workgroup" within it */
if( strncmp( winver_buff, "Windows", sizeof( "Windows" ) - 1 ) == 0 &&
strstr( winver_buff, "Workgroup" ) != NULL )
{
/* yup - string looks OK and Workgroup found in string */
lstrcpy( winver_buff, WINVER_EXE " indicates Workgroups\n" );
}
else
{
/* nope - blank out string */
winver_buff[ 0 ] = '\0';
}
}
/* tidy up */
_lclose( h_winver );
}
}
if( ( ( maj_vers << 8 ) + min_vers ) >= 0x030A )
{
/* reported version number is 3.10 or greater so
double check because Windows for Workgroups 3.11
and Windows 3.11 both report the version as 3.10 */
WORD dos_win_vers;
dos_win_vers = get_dos_win_ver();
if( dos_win_vers != VER_UNKNOWN )
{
/* setup DOS information */
wsprintf( dos_bld_id, " DOS reports Windows %u.%u\n",
HIBYTE( dos_win_vers ),
LOBYTE( dos_win_vers ) );
}
/* don't disable No File Found error box - we should *always* find USER.EXE! */
h_userexe = LoadLibrary( USER_EXE );
if( h_userexe > HINSTANCE_ERROR )
{
if( LoadString( h_userexe, BUILD_ID, bld_temp, sizeof( bld_temp ) ) != 0 )
{
wsprintf( bld_str, "USER.EXE build id Windows %s\n", (LPCSTR)bld_temp );
}
FreeLibrary( h_userexe );
}
}
/* disable "File Not Found" error box for
the case where Win32s is not installed */
SetErrorMode( SEM_NOOPENFILEERRORBOX );
/* now try and load Win32s library to find out
what (if any) version of Win32s is available */
h_win32s = LoadLibrary( W32SYS );
if( h_win32s > HINSTANCE_ERROR )
{
/* Win32s library loaded so Win32s does exist, find out if it is
running. note that though we have loaded the W32SYS library this
does *not* cause Win32s to run so we do get a correct result,
a 32-bit Win32s application has to be actually running. When
all Win32s programs have closed the test will return false.*/
BOOL win32s_state = win32s_running();
/* get address of Win32s info function */
Get_Win32s_Info = (GW32S_PTR)GetProcAddress( h_win32s, WIN32SINF );
if( Get_Win32s_Info )
{
int win32_stat;
/* Win32s Version 1.1 or later installed. there is no
need for the slightly more complex
(*Get_Win32s_Info)( ... )
form of call used in the Win32s Programmer's Reference
as modern compilers know to use a function ptr to call
a function when the construct is appropriate. */
if( ( win32_stat = Get_Win32s_Info( &win32s_info ) ) == 0 )
{
/* Win32s OK - report information */
wsprintf( win32s_ver, "\n Supporting Version %u.%u Build %u\n"
" of Win32s - %s version\n"
" Win32s %s running",
win32s_info.bMajor,
win32s_info.bMinor,
win32s_info.wBuildNumber,
(LPCSTR)( win32s_info.fDebug ? "Debug" : "Retail" ),
(LPCSTR)( win32s_state ? "is" : "not" ) );
}
else
{
/* Win32s failure */
static char *fmt = "\n Win32s VXD not %s";
switch( win32_stat )
{
case 1:
wsprintf( win32s_ver, fmt, (LPCSTR)"present" );
break;
case 2:
wsprintf( win32s_ver, fmt, (LPCSTR)"loaded\n because paging not enabled" );
break;
case 3:
wsprintf( win32s_ver, fmt, (LPCSTR)"loaded\n because running in Standard Mode" );
break;
default:
wsprintf( win32s_ver, "\n Win32s fail reason %d", win32_stat );
}
}
}
else
{
/* if no function then Win32s is version 1.0 */
wsprintf( win32s_ver, "\nSupporting Version 1.0\n"
"\tof Win32s"
" Win32s %s running",
(LPCSTR)( win32s_state ? "is" : "not" ) );
}
FreeLibrary( h_win32s );
}
else
{
/* no Win32s library */
wsprintf( win32s_ver, " Win32s not available" );
}
/* ++++ this compiles badly on BC++ 4.5 unless
the wfwg_... strings are declared far. */
wsprintf( buff, " Windows reports Version %u.%02u\n"
"%s in %s mode\n"
" on Version %u.%02u of DOS\n"
"%sWin for Workgroups %s\n"
"%s" /* WINVER.EXE Workgroups */
"%s\n" /* Win32s string */
"\n"
"%s" /* USER.EXE build id */
"%s" /* USER.EXE file version */
"%s", /* DOS build */
LOBYTE( LOWORD( win_vers ) ),
HIBYTE( LOWORD( win_vers ) ),
(LPCSTR)( ( win_flags & ( WF_ENHANCED | WF_STANDARD ) ) ?
"" : " " ),
(LPCSTR)( win_flags & WF_ENHANCED ? "Enhanced" :
win_flags & WF_STANDARD ? "Standard" : "Real" ),
HIBYTE( HIWORD( win_vers ) ),
LOBYTE( HIWORD( win_vers ) ),
(LPCSTR)wfwg_spacer, (LPCSTR)wfwg_string,
(LPCSTR)winver_buff,
(LPCSTR)win32s_ver,
(LPCSTR)bld_str,
(LPCSTR)file_ver,
(LPCSTR)dos_bld_id );
}
/* ==========================
end of 16-bit Windows code
========================== */
#endif
MessageBox( NULL, buff, PROG_NAME TEXT( " : " ) TEXT( WIN_BITS ) TEXT( "-bit" ),
MB_OK | MB_ICONINFORMATION );
return( FALSE );
}
#if defined( WIN32 ) || defined( _WIN32 ) || defined( __WIN32__ )
/* =========================================
support function only used in 32-bit mode
========================================= */
BOOL reg32_access( PTCHAR key, PTCHAR val,
LPDWORD data_type_ptr, LPBYTE data_ptr, LPDWORD data_size_ptr )
{
/* this function reads a single value held under the
HKEY_LOCAL_MACHINE key in the Windows NT registry */
HKEY reg_key;
BOOL ret_val = FALSE;
if( ERROR_SUCCESS == RegOpenKeyEx( HKEY_LOCAL_MACHINE, key, 0, KEY_READ, ®_key ) )
{
if( ERROR_SUCCESS == RegQueryValueEx( reg_key, val, 0,
data_type_ptr, data_ptr, data_size_ptr ) )
{
/* all OK */
ret_val = TRUE;
}
/* tidy up if we got key */
RegCloseKey( reg_key );
}
return( ret_val );
}
#else
/* ==========================================
support functions only used in 16-bit mode
========================================== */
#define MPX_INT 0x2f /* DOS Multiplex Interrupt */
#define MPX_WIN_VER 0x160A /* Get Windows Version */
#define DPMI_INT 0x31 /* DPMI Interrupt */
#define DPMI_REAL_INT 0x0300 /* DPMI call real-mode int */
#include <string.h>
/* this uses the DPMI interface to call real-mode INT 0x2F
to get Windows version as seen by a DOS program. If the
program directly uses INT 0x2F it gets a protected mode
interupt that does not support the required interface.
Paul A LeBlanc of Borland's TeamB on CIS's BCPPWIN forum
suggested the technuique of using a DPMI real-mode interrupt
for getting at the Windows version reported to a DOS program. */
WORD get_dos_win_ver( void )
{
/* DPMI Function 0x0300 (real-mode interrupt) register set */
struct real_regs
{
DWORD reg_edi;
DWORD reg_esi;
DWORD reg_ebp;
DWORD reg_reserved;
DWORD reg_ebx;
DWORD reg_edx;
DWORD reg_ecx;
DWORD reg_eax;
WORD reg_status;
WORD reg_es;
WORD reg_ds;
WORD reg_fs;
WORD reg_gs;
WORD reg_ip;
WORD reg_cs;
WORD reg_sp;
WORD reg_ss;
} real_regs;
static struct real_regs far *real_reg_ptr;
/* just make sure program is running in protected mode */
if( ( GetWinFlags() & WF_PMODE ) == 0 )
{
/* real mode - this should never happen! */
return( VER_UNKNOWN );
}
/* program is already running in protected mode,
so it is okay to make DPMI (INT 0x31) calls */
real_reg_ptr = &real_regs; /* init ptr to register set */
_fmemset( real_reg_ptr, 0, sizeof( real_regs ) );
/* clear register set */
real_regs.reg_eax = MPX_WIN_VER; /* MPX fn to get WIN version */
_asm {
les di, real_reg_ptr /* ptr to register set */
mov bx, MPX_INT /* INT 0x2f - flags zero */
mov cx, 0 /* 0 words to real-mode stack */
mov ax, DPMI_REAL_INT /* DPMI call real-mode int */
int DPMI_INT /* go to it */
jc dpmi_bad /* carry set on error */
}
return( LOWORD( real_regs.reg_ebx ) ); /* return word with WIN vers */
dpmi_bad:
return( VER_UNKNOWN ); /* version reported to DOS not known */
}
/* the next function tried to detect whether this is a
Windows for Workgroups installation */
/* things unique to the next function. These are included here
so that special Win for Workgroups SDK stuff is not needed.
the function prototype and the WNNC_... defines are also in
the Workgroups SDK WINNET.H file */
#include <stdlib.h>
#include <io.h>
#if !defined( WNNC_NET_TYPE ) /* if already defined don't do again */
/* the DEF file identifies this as in the User module */
WORD WINAPI WNetGetCaps( WORD );
#define WNNC_NET_TYPE 0x0002
#define WNNC_NET_MultiNet 0x8000
#define WNNC_SUBNET_WinWorkgroups 0x0004
#endif
#define ACC_EXIST 0 /* value for 'file exists' in access() */
WORD win_for_wkg_check( void )
{
WORD net_flags;
char wfwnet_name[ _MAX_PATH ];
/* check for network type */
net_flags = WNetGetCaps( WNNC_NET_TYPE );
/* check for mutiple-network bit */
if( net_flags & WNNC_NET_MultiNet )
{
/* check low byte for WFWG bit */
if( ( LOBYTE( net_flags ) ) & WNNC_SUBNET_WinWorkgroups )
{
/* net detect report Windows for Workgroups */
return( WFWG_RUNNING );
}
}
/* network not running - but this does not mean this is not
Windows for Workgroups. construct name and check it exists */
if( GetSystemDirectory( wfwnet_name, sizeof( wfwnet_name ) ) )
{
lstrcat( wfwnet_name, "\\wfwnet.drv" );
if( access( wfwnet_name, ACC_EXIST ) == 0 )
{
/* if file found this system is Windows for Workgroups */
return( WFWG_FOUND );
}
/* if file not found this is not Windows for Workgroups (probably!) */
}
return( WFWG_NOT_FOUND );
}
/* the next function detects if Win32s is actully running,
as opposed to being installed but not running */
#include <toolhelp.h>
#define WIN32S_MODULE "WIN32S16" /* name of Win32s module loaded when
Win32s is actually running */
BOOL win32s_running( void )
{
MODULEENTRY module_entry = { sizeof( MODULEENTRY ) };
/* if we can find module data then Win32s is running.
no need to dynamically link to the ModuleFindName
function as TOOLHELP.DLL should be installed on a
Windows 3.1 (or later) system supporting Win32s. */
if( ModuleFindName( &module_entry, WIN32S_MODULE ) != NULL )
{
return( TRUE );
}
/* if we get here no Win32s application is running */
return( FALSE );
}
/* the next function accesses the Windows NT Registry from a 16-bit program */
/* handle to Registry key pre-defined in 32-bit environment but not in 16-bit */
#define HKEY_LOCAL_MACHINE ( (DWORD)0x80000002UL )
/* and error return value - defined here to make sure we get correct 32-bit value */
#define ERROR_SUCCESS 0
/* more variations of the CallProc32W function.
these are all mapped to CallProc32W by the DEF file */
DWORD FAR PASCAL CallProc32W__1( DWORD,
DWORD, DWORD, DWORD ); /* 1 param */
DWORD FAR PASCAL CallProc32W__5( DWORD, DWORD, DWORD, DWORD, DWORD,
DWORD, DWORD, DWORD ); /* 5 params */
DWORD FAR PASCAL CallProc32W__6( DWORD, DWORD, DWORD, DWORD, DWORD, DWORD,
DWORD, DWORD, DWORD ); /* 6 params */
/* to define the number of params to the function
called - the last parameter to CallProc32W */
#define NUM_PARAMS( xx ) ( (DWORD)( xx ) )
/* a macro to set an address translation bit for the
CallProc32W function so that we can use the normal param
number even though the bits are used 'in reverse'. the
bits form the penultimate parameter to CallProc32W */
#define ADDR( parno, numpars ) ( (DWORD)1 << ( numpars - parno ) )
/* used as penultimate param if no parameters to function
called by CallProc32W require address translation */
#define NO_ADDR ( (DWORD)0 )
#define ADVAPI32 "ADVAPI32.DLL" /* module with 32-bit Reg... functions */
#define REGOPENKEYEX "RegOpenKeyExA" /* name of 32-bit ASCII RegOpenKeyEx() */
#define REGQUERYVALUEEX "RegQueryValueExA" /* name of 32-bit ASCII RegQueryValueEx() */
#define REGCLOSEKEY "RegCloseKey" /* name of 32-bit RegCloseKey */
#define REG_QUERY_VALUE 0x0001 /* manifest constant for ReqOpenKeyEx */
BOOL reg32_access( char *key, char *val, LPDWORD data_type, LPBYTE data, LPDWORD data_size )
{
/* this function reads a single value held under the
HKEY_LOCAL_MACHINE key in the Windows NT registry */
DWORD hAdvAPI32; /* handle to 32-bit ADVAPI32 module */
DWORD Reg_Open_Key_Ex; /* address in that of 32-bit RegOpenKeyEx function */
DWORD Reg_Query_Value_Ex; /* and address of 32-bit RegQueryValueEx function */
DWORD Reg_Close_Key; /* and address of 32-bit RegCloseKey function */
DWORD hKey; /* 32-bit key */
BOOL ret_val = FALSE; /* default return value */
if( ( hAdvAPI32 = LoadLibraryEx32W( ADVAPI32, NULL, 0 ) ) != NULL )
{
/* got 32-bit module handle OK - get 32-bit function addresses */
if( ( Reg_Open_Key_Ex = GetProcAddress32W( hAdvAPI32, REGOPENKEYEX ) ) != NULL &&
( Reg_Query_Value_Ex = GetProcAddress32W( hAdvAPI32, REGQUERYVALUEEX ) ) != NULL &&
( Reg_Close_Key = GetProcAddress32W( hAdvAPI32, REGCLOSEKEY ) ) != NULL &&
/* got 32-bit function addresses - access registry */
ERROR_SUCCESS == CallProc32W__5( /* open key */
HKEY_LOCAL_MACHINE, (DWORD)(LPSTR)key, 0,
REG_QUERY_VALUE, (DWORD)(LPDWORD)&hKey,
Reg_Open_Key_Ex,
ADDR( 2, 5 ) | ADDR( 5, 5 ),
NUM_PARAMS( 5 ) ) &&
ERROR_SUCCESS == CallProc32W__6( /* query value */
hKey, (DWORD)(LPSTR)val, 0,
(DWORD)data_type, (DWORD)data, (DWORD)data_size,
Reg_Query_Value_Ex,
ADDR( 2, 6 ) | ADDR( 4, 6 ) | ADDR( 5, 6 ) | ADDR( 6, 6 ),
NUM_PARAMS( 6 ) ) &&
ERROR_SUCCESS == CallProc32W__1( /* close key */
hKey,
Reg_Close_Key,
NO_ADDR,
NUM_PARAMS( 1 ) ) )
{
/* access OK */
ret_val = TRUE;
}
/* tidy up whether or not we got value */
FreeLibrary32W( hAdvAPI32 );
}
return( ret_val );
}
#endif
/* ======================================================
support functions used in 16-bit mode and 32-bit modes
====================================================== */
/* note that these functions use TCHAR to compile properly for UNICODE or not */
#define PRODUCTTYPE_KEY "System\\CurrentControlSet\\Control\\ProductOptions"
/* Registry key under HKEY_LOCAL_MACHINE that tells
us whether host OS is NT Workstaion or NT Server */
#define PRODUCTTYPE_VAL "ProductType" /* data item under that key with useful info. */
#define PRODUCTTYPE_LEN 16 /* space to allow for product type string */
#define NT_WORKSTATION "WinNT" /* value in Windows NT Registry that indicates
if this is Workstation, other entries (either
SERVERNT or LANMANNT) indicate server. */
#define NT_SERVER "ServerNT" /* value ... for NT Server */
#define NT_LANMAN "LanManNT" /* value ... for Lan Manager NT */
#define PRODUCTTYPE_CSD "CSDVersion" /* value in the Registry under the ProductOptions
key for numeric CSD Version. exists on Windows
NT 3.5 when a service pack is loaded but not on
Windows NT 3.1 where the CSD Version under the
CurrentVersion key is numeric. */
/* determine Windows NT type from string */
PTCHAR winnt_type( PTCHAR val_buff )
{
/* start compares with value for Workstation */
if( lstrcmpi( val_buff, TEXT( NT_WORKSTATION ) ) == 0 )
{
/* yes - workstation */
return( TEXT( "Workstation" ) );
}
/* no - must be a server of some kind */
if( lstrcmpi( val_buff, TEXT( NT_SERVER ) ) == 0 )
{
/* this is NT Server */
return( TEXT( "Server" ) );
}
/* not NT server - try LAN Manager */
if( lstrcmpi( val_buff, TEXT( NT_LANMAN ) ) == 0 )
{
/* this is LAN Manager on NT */
return( TEXT( "Lan Manager" ) );
}
/* oops - don't recognize the type */
return( TEXT( "( Unknown Type [Server?] )" ) );
}
/* the next two functions use reg32_access to be 16-bit/32-bit portable */
/* determines whether this is Windows NT Workstation or
Server, works on Windows NT 3.1 and Windows NT 3.5. */
PTCHAR nt_type( void )
{
TCHAR val_buff[ PRODUCTTYPE_LEN ];
DWORD val_buff_size = sizeof( val_buff ); /* even with UNICODE size is *ALWAYS* bytes */
DWORD val_type;
PTCHAR retstr = TEXT( "( Type Not Known )" ); /* default return string */
/* open the master key for the product type information - read value and check type */
if( reg32_access( TEXT( PRODUCTTYPE_KEY ), TEXT( PRODUCTTYPE_VAL ),
&val_type, (LPBYTE)val_buff, &val_buff_size ) &&
REG_SZ == val_type )
{
/* get type of Windows NT */
retstr = winnt_type( val_buff );
}
return( retstr );
}
/* function gets numeric service pack identity */
PTCHAR nt_csd_num( void )
{
DWORD csd_val;
DWORD csd_val_size = sizeof( csd_val );
DWORD csd_type;
static TCHAR retstr[ 64 ] = TEXT( "" ); /* default return string */
/* open the master key for the product type information - read value and check type */
if( reg32_access( TEXT( PRODUCTTYPE_KEY ), TEXT( PRODUCTTYPE_CSD ),
&csd_type, (LPBYTE)&csd_val, &csd_val_size ) &&
REG_DWORD == csd_type )
{
/* get type of Windows NT */
wsprintf( retstr, TEXT( "\nService Pack %lu (CSD Version 0x%lX)" ), CSD_TO_SP( csd_val ), csd_val );
}
return( retstr );
}