Реализация DI в PHP
Jason-Webb 13.05.2025
Когда я начинал писать свой первый крупный PHP-проект, моя архитектура напоминала запутаный клубок спагетти. Классы создавали другие классы внутри себя, зависимости жостко прописывались в коде, а о. . .
Обработка изображений в реальном времени на C# с OpenCV
stackOverflow 13.05.2025
Объединение библиотеки компьютерного зрения OpenCV с современным языком программирования C# создаёт симбиоз, который открывает доступ к впечатляющему набору возможностей. Ключевое преимущество этого. . .
POCO, ACE, Loki и другие продвинутые C++ библиотеки
NullReferenced 13.05.2025
В C++ разработки существует такое обилие библиотек, что порой кажется, будто ты заблудился в дремучем лесу. И среди этого многообразия POCO (Portable Components) – как маяк для тех, кто ищет. . .
Паттерны проектирования GoF на C#
UnmanagedCoder 13.05.2025
Вы наверняка сталкивались с ситуациями, когда код разрастается до неприличных размеров, а его поддержка становится настоящим испытанием. Именно в такие моменты на помощь приходят паттерны Gang of. . .
Создаем CLI приложение на Python с Prompt Toolkit
py-thonny 13.05.2025
Современные командные интерфейсы давно перестали быть черно-белыми текстовыми программами, которые многие помнят по старым операционным системам. CLI сегодня – это мощные, интуитивные и даже. . .
Конвейеры ETL с Apache Airflow и Python
AI_Generated 13.05.2025
ETL-конвейеры – это набор процессов, отвечающих за извлечение данных из различных источников (Extract), их преобразование в нужный формат (Transform) и загрузку в целевое хранилище (Load). . . .
Выполнение асинхронных задач в Python с asyncio
py-thonny 12.05.2025
Современный мир программирования похож на оживлённый мегаполис – тысячи процессов одновременно требуют внимания, ресурсов и времени. В этих джунглях операций возникают ситуации, когда программа. . .
Работа с gRPC сервисами на C#
UnmanagedCoder 12.05.2025
gRPC (Google Remote Procedure Call) — открытый высокопроизводительный RPC-фреймворк, изначально разработанный компанией Google. Он отличается от традиционых REST-сервисов как минимум тем, что. . .
CQRS (Command Query Responsibility Segregation) на Java
Javaican 12.05.2025
CQRS — Command Query Responsibility Segregation, или разделение ответственности команд и запросов. Суть этого архитектурного паттерна проста: операции чтения данных (запросы) отделяются от операций. . .
Шаблоны и приёмы реализации DDD на C#
stackOverflow 12.05.2025
Когда я впервые погрузился в мир Domain-Driven Design, мне показалось, что это очередная модная методология, которая скоро канет в лету. Однако годы практики убедили меня в обратном. DDD — не просто. . .
Это перевод How to detect programmatically whether you are running on 64-bit Windows. Автор: Реймонд Чен.
Чтобы программно определить, запущены ли вы на 64-разрядной Windows, вы можете использовать функцию IsWow64Process
, которая показывает, не запущен ли ваш 32-разрядный процесс в режиме эмуляции.
Но как вы программно определите, не запущен ли ваш 64-битный процесс на 64-битной Windows? Легко.
function Is64BitProcessRunningOn64BitWindows: Boolean; begin Result := True; end;
Тот факт, что ваша 64-разрядная программа работает, и означает, что вы запущены на 64-разрядной Windows! Если бы вы были на 32-разрядной машине, ваш код не смог бы запуститься.
Это всё равно, что спрашивать: «включен ли компьютер?». Если бы он был выключен, ваша программа не смогла бы задать вопрос.
Конечно же, если вам нужен единый исходный текст для компиляции и в 32-разрядную программу и в 64-разрядную, то вам нужно сделать немного больше работы:
function Is64BitWindows: Boolean; var IsWow64Process: function(hProcess: THandle; out Wow64Process: Bool): Bool; stdcall; Wow64Process: Bool; begin {$IF Defined(CPU64)} Result := True; // 64-битная программа запускается только на Win64 {$ELSEIF Defined(CPU16)} Result := False; // Win64 не поддерживает 16-разрядные приложения {$ELSE} // 32-битные программы могут работать и на 32-разрядной и на 64-разрядной Windows // так что этот вопрос требует дальнейшего исследования IsWow64Process := GetProcAddress(GetModuleHandle(Kernel32), 'IsWow64Process'); Wow64Process := False; if Assigned(IsWow64Process) then Wow64Process := IsWow64Process(GetCurrentProcess, Wow64Process) and Wow64Process; Result := Wow64Process; {$IFEND} end;
Я добавил ветку для 16-битных программ на случай, если вы достаточно сумасшедший, чтобы писать их сегодня.
Примечание переводчика: поскольку в Delphi пока нет 64-разрядного компилятора, то не известно, как будут называться соответствующие директивы. Поэтому я взял директивы от FreePascal.
Форум программистов Vingrad
Модераторы: Poseidon, Snowy, bems, MetalFan |
Поиск: |
Получить версию Windows и ее разрядность, Как получить ее разрядность |
Опции темы |
ZBugz |
|
||
Опытный Профиль
Репутация: 6
|
Всем привет.
Это сообщение отредактировал(а) ZBugz — 21.9.2011, 20:17 |
||
|
|||
bems |
|
||
Эксперт Профиль Репутация: 31
|
ХЕ2 еще не пробовал, но должно работать Это сообщение отредактировал(а) bems — 22.9.2011, 10:27 ——————— Обижено школьников: 8 |
||
|
|||
Snowy |
|
||
Эксперт Профиль
Репутация: 192
|
ZBugz, код рабочий. Нужно только заменить GetVersionExA на GetVersionExW — юникод же! bems, код работает без проблем, как в 32, так и в 64 |
||
|
|||
ZBugz |
|
||
Опытный Профиль
Репутация: 6
|
О, спасибо. Оба работают. От BEMS понравилось что компактно Спасибо всем |
||
|
|||
Правила форума «Delphi: Общие вопросы» | |
Запрещается! 1. Публиковать ссылки на вскрытые компоненты 2. Обсуждать взлом компонентов и делиться вскрытыми компонентами
Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, Snowy, MetalFan, bems, Poseidon, Rrader. |
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей) |
0 Пользователей: |
« Предыдущая тема | Delphi: Общие вопросы | Следующая тема » |
#1
Luthfi
- Programming Language:PHP, Delphi/Object Pascal, Pascal, Transact-SQL
- Learning:C, Java, PHP
Posted 10 November 2010 — 11:04 PM
As 64 bit processors is getting more and more popular, 64 bit operating system is also becoming more popular. Well, you need 64 bit operating systems to get full advantage of the sophisticated 64bit processors. No wonder that more and more people also chose to install 64 bit operating system. On the other hand, for most programs switching from 32bit to 64bit does not give significant benefit over the hassle in converting process. Given that 32 bit programs run just fine under 64bit, and still get slight improvement in speed and throughput, makes converting to 64 bit is not really an urgent matter.
But no matter how good the 64bit OS is handling 32bit process, there is still some difference from true 32bit OS. And this is a must to keep the system running stable and clean. For Windows for example, this includes transparent registry and directory redirection. Thus making us have to be able to detect whether we are running under 64bit OS or not to anticipate this difference which maybe significantly affect our programs.
This article will discuss about how to detect if our program/process is running under Windows 64bit OS. The theory could be used in any programming language, but sample implementation will be done with Delphi.
The Theory
To check if the operating system is 64-bit we can use windows API IsWow64Process. This API is contained in kernel32.dll. Initially this api was not available in kernel32.dll of Windows 32 bit. So in those Windows simply detecting the presence of this api in kernel32.dll was enough for detection. Unfortunately in newer Windows versions (perhaps in Vista and 7, Microsoft was a bit vague on this) have IsWow64Process in their kernel32.dll, even in their 32 bit versions. This means now we have to actually run the api against our process for detection.
The possibility that IsWow64Process is missing in kernel32.dll is making static linking out of the question. We need dynamic linking or our application will not be able to run under Windows which missing IsWow64Process in their kernel32.dll.
Things also get slightly more complicated by the fact that some older Windows (like Windows 2000) does not have kernel32.dll. Therefore initial checking for the presence of this library is required.
So our steps should be:
- Assume that we are not running under Windows 64bit.
- Load kernel32.dll library, if failed simply return since we are running under older Windows which does not have kernel32.dll, which means it’s definitely not 64 bit.
- Load windows api IsWow64Process, if failed we simply jus return since we are definitely running under Windows 32 bit.
- Execute IsWow64Process against our own process, and use the returned value to see if we are a 32 bit process running under Windows 64 bit OS.
For details on IsWow64Process you can visit: IsWow64Process Function (Windows)
Delphi Implementation
function IsWindows64: Boolean; type TIsWow64Process = function(AHandle:THandle; var AIsWow64: BOOL): BOOL; stdcall; var vKernel32Handle: DWORD; vIsWow64Process: TIsWow64Process; vIsWow64 : BOOL; begin // 1) assume that we are not running under Windows 64 bit Result := False; // 2) Load kernel32.dll library vKernel32Handle := LoadLibrary('kernel32.dll'); if (vKernel32Handle = 0) then Exit; // Loading kernel32.dll was failed, just return try // 3) Load windows api IsWow64Process @vIsWow64Process := GetProcAddress(vKernel32Handle, 'IsWow64Process'); if not Assigned(vIsWow64Process) then Exit; // Loading IsWow64Process was failed, just return // 4) Execute IsWow64Process against our own process vIsWow64 := False; if (vIsWow64Process(GetCurrentProcess, vIsWow64)) then Result := vIsWow64; // use the returned value finally FreeLibrary(vKernel32Handle); // unload the library end; end;
Edited by LuthfiHakim, 11 November 2010 — 09:32 AM.
-
0
- Back to top
Contents
Introduction
There are times when you need to know information about the version of Windows your program is running on. For example:
- when reporting bugs;
- when wishing to take different actions on different operating systems.
The Windows API provides some version information that is common to all 32 bit Windows operating systems. Delphi’s run time library (RTL) makes access to this information very straightforward. We will begin our exploration by seeing how to get the information from the Windows API. Next we will look at Delphi’s RTL support and begin the development of a static class that uses this information to provide easy access to OS information.
Having reviewed the basic information we will then examine additional information provided by the operating system on later Windows NT OSs. We will use this information to enhance Delphi’s RTL support for NT and to extend our static class.
Since the additional information we get from Windows is only available on later NT OSs, we will see how to get similar information for early NT OSs by accessing the registry. We will enhance our static class to be able to utilise the information from the registry.
The last enhancement of our static class will be made when we look at how to detect further information about Windows XP. In particular we will find out how to detect Windows Media Center and Tablet editions and how to learn if a program is running on 64 bit Windows.
Our exploration begins by discussing the basic information about the OS that is available on all Windows systems.
Common Windows API OS Information
Windows provides the GetVersionEx API function that fills a structure with information describing the version of the underlying operating system. This function works for all 32 bit versions of Windows. In Delphi the structure is defined in the Windows unit as TOSVersionInfo – see Listing 1 below.
1type
2
3 _OSVERSIONINFOA = record
4 dwOSVersionInfoSize: DWORD;
5 dwMajorVersion: DWORD;
6 dwMinorVersion: DWORD;
7 dwBuildNumber: DWORD;
8 dwPlatformId: DWORD;
9 szCSDVersion: array[0..127] of AnsiChar;
10 end;
11
12 _OSVERSIONINFO = _OSVERSIONINFOA;
13 TOSVersionInfoA = _OSVERSIONINFOA;
14
15 TOSVersionInfo = TOSVersionInfoA;
Listing 1
The structure’s fields are explained in Table 1:
Fields of TOSVersionInfo
Field | Description |
---|---|
dwOSVersionInfoSize | Size of the structure in bytes. |
dwMajorVersion |
Major version of the operating system. Possible values are:
|
dwMinorVersion |
Minor version of the operating system. Possible values are:
|
dwBuildNumber | Build number of the operating system. On Windows 9x, the low order word contains the build number and the high order word contains the major and minor version numbers. |
dwPlatformId |
Operating system platform. This member can take one of the following values:
|
szCSDVersion |
Buffer containing a null terminated string identifying any installed service pack. On Windows NT OSs this string is the actual service pack name while on Windows 9x it is a single letter code that is interpreted as follows:
|
Table 1
Listing 2 shows how to use GetVersionEx to fill a TOSVersionInfo structure with operating system information:
1var
2 OSVI: TOSVersionInfo;
3begin
4
5 FillChar(OSVI, SizeOf(OSVI), 0);
6 OSVI.dwOSVersionInfoSize := SizeOf(OSVI);
7 if not GetVersionEx(OSVI) then
8 raise Exception.Create('Error calling GetVersionEx');
9
10end;
Listing 2
Here we zero the TOSVersionInfo structure then set the dwOSVersionInfoSize field to the size of the structure – a common Windows API idiom. We then call GetVersionEx, raising an exception if the function returns false. If the function returns true then the information stored in the structure is valid.
We would normally go on to use the fields of TOSVersionInfo in our program. However, Delphi steps in to make this process easier, as can be seen below.
Delphi RTL Support
Delphi’s SysUtils unit provides some global variables that make accessing basic operating system version information very straightforward, saving the need to call GetVersionEx ourselves. The variables provide the same information as the TOSVersionInfo structure, as can be seen in Table 2.
Delphi’s Win32XXX Global Variables
Variable | Field | Notes |
---|---|---|
Win32Platform | dwPlatformId | Operating system platform. Can take one of the VER_PLATFORM_* values noted in Table 1. |
Win32MajorVersion | dwMajorVersion | Major version of the operating system. |
Win32MinorVersion | dwMinorVersion | Minor version of the operating system. |
Win32BuildNumber | dwBuildNumber | Build number of the operating system. On Windows 9x systems the high order word is zeroed. |
Win32CSDVersion | szCSDVersion | Buffer containing a null terminated string identifying any installed service pack. For more information see Table 1. |
Table 2
Delphi initializes the global variables in the SysUtils unit’s initialization section. It does this by calling the InitPlatformId procedure which is shown in Listing 3 below:
1procedure InitPlatformId;
2var
3 OSVersionInfo: TOSVersionInfo;
4begin
5 OSVersionInfo.dwOSVersionInfoSize := SizeOf(OSVersionInfo);
6 if GetVersionEx(OSVersionInfo) then
7 with OSVersionInfo do
8 begin
9 Win32Platform := dwPlatformId;
10 Win32MajorVersion := dwMajorVersion;
11 Win32MinorVersion := dwMinorVersion;
12 if Win32Platform = VER_PLATFORM_WIN32_WINDOWS then
13 Win32BuildNumber := dwBuildNumber and $FFFF
14 else
15 Win32BuildNumber := dwBuildNumber;
16 Win32CSDVersion := szCSDVersion;
17 end;
18end;
Listing 3
Setting up TOSVersionInfo and calling GetVersionEx should be familiar from our previous discussion. Once Delphi has the version information, the global variables are simply assigned the value of the associated TOSVersionInfo field. The only exception to this is with Win32BuildNumber which, as noted in Table 2, has its high word zeroed in Windows 9x systems.
Platform
Listing 3 detects the platform by testing Win32Platform. We will learn more about detecting the platform below.
OS Version Information Class: Version 1
Having learned about the basic Windows and Delphi support for getting OS version information we are now ready to create a new static class, TOSInfo, that exposes OS version information in a user friendly way. We will use only the information provided by Delphi’s global variables in this class’s methods.
We begin by starting a new unit, UOSInfo.pas
, and declaring a new, empty, class in the unit’s interface section:
1type
2 TOSInfo = class(TObject)
3 public
4 end;
Listing 4
In the following subsections we will add methods to the class.
Finding the OS Platform
The most basic distinction between Windows operating systems is that between platforms. We can find the platform by examining Delphi’s Win32Platform global variable. The value of the variable will be one of the integer constants described in the dwPlatformId entry in Table 1. These constants are defined in Delphi’s Windows unit.
Our class will have two methods to test the platform – one to detect Windows 95 (IsWin9x) and one to test for the NT platform (IsWinNT). Since Delphi programs will not run on Win32s we will not provide a method to detect that platform.
Testing platforms
Users should not assume that when IsWin9x returns false we must have an NT platform – to be sure the IsWinNT method should be tested instead and vice versa.
We can add the appropriate declarations to TOSInfo‘s interface section and implement the methods as in Listing 5:
1class function TOSInfo.IsWin9x: Boolean;
2begin
3 Result := (Win32Platform = VER_PLATFORM_WIN32_WINDOWS);
4end;
5
6class function TOSInfo.IsWinNT: Boolean;
7begin
8 Result := (Win32Platform = VER_PLATFORM_WIN32_NT);
9end;
Listing 5
OS Product
Now we have code that can check the platform we can move on to detecting which operating system product our applications are running on. There are various combinations of major and minor version numbers that, along with the platform, determine the product. These are summarised in Table 3.
Mapping of OS version numbers to Windows products
Major Version |
Minor Version |
Windows 9x Platform |
Windows NT Platform |
---|---|---|---|
4 | 0 | Windows 95 | Windows NT 4 |
4 | 10 | Windows 98 | — |
4 | 90 | Windows Me | — |
5 | 0 | — | Windows 2000 |
5 | 1 | — | Windows XP |
5 | 2 | — | Windows Server 2003 |
Table 3
We will design our new method, Product, to return an enumerated value that indicates the underlying operating system. We define this enumeration in the interface section of UOSInfo.pas
as follows:
1type
2 TOSProduct = (
3 osUnknown,
4 osWin95,
5 osWin98,
6 osWinMe,
7 osWinNT4,
8 osWin2000,
9 osWinXP,
10 osWinServer2003
11 );
Listing 6
Now we can add our Product method to TOSInfo. The method’s implementation is shown in Listing 7:
1class function TOSInfo.Product: TOSProduct;
2begin
3 Result := osUnknown;
4 if IsWin9x then
5 begin
6 case Win32MajorVersion of
7 4:
8 begin
9 case Win32MinorVersion of
10 0: Result := osWin95;
11 10: Result := osWin98;
12 90: Result := osWinMe;
13 end;
14 end;
15 end;
16 end
17 else if IsWinNT then
18 begin
19 case Win32MajorVersion of
20 4:
21 begin
22 case Win32MinorVersion of
23 0: Result := osWinNT4;
24 end;
25 end;
26 5:
27 begin
28 case Win32MinorVersion of
29 0: Result := osWin2000;
30 1: Result := osWinXP;
31 2: Result := osWinServer2003;
32 end;
33 end;
34 end;
35 end;
36end;
Listing 7
This method is quite simple. It first sets an «unknown» return value in case the method fails to find the OS. We then take different branches according to the platform. Within each branch we test for valid combinations of major and minor versions, per Table 3, and return the value representing the OS.
Build Number
Once we know which product we’re dealing with we may also want to know its build number. We will therefore add a BuildNumber method to our class. Its implementation is provided in Listing 8. It simply returns the value of Delphi’s Win32BuildNumber variable. (Remember, Delphi took care of masking out the most significant word when on the Windows 9x platform, so we don’t need to do that here).
1class function TOSInfo.BuildNumber: Integer;
2begin
3 Result := Win32BuildNumber;
4end;
Listing 8
Service Pack
The final method we will implement here is ServicePack. Earlier, in Table 1, it was noted that Windows provides the full name of NT platform service packs but only supplies a code letter on the Windows 9x platform. Listing 9 shows how we use Delphi’s Win32CSDVersion variable to help us implement the method.
1class function TOSInfo.ServicePack: string;
2begin
3 Result := '';
4 if IsWin9x then
5 begin
6 if Win32CSDVersion <> '' then
7 begin
8 case Product of
9 osWin95:
10 if UpCase(Win32CSDVersion[1]) in ['B', 'C'] then
11 Result := 'OSR2';
12 osWin98:
13 if UpCase(Win32CSDVersion[1]) = 'A' then
14 Result := 'SE';
15 end;
16 end;
17 end
18 else if IsWinNT then
19 Result := Win32CSDVersion;
20end;
Listing 9
This method begins by assuming there is no service pack installed, so it sets the default result to the empty string. We then detect the platform. For Windows 9x we look for the characters ‘A’, ‘B’ or ‘C’ in Win32CSDVersion and return the required service pack name if they are found. For the NT platform we simply return the value of Win32CSDVersion since this will either be the empty string or will contain the service pack name in full.
NT 4 Complications
This is not quite the whole story – this method won’t detect Windows NT 4 Service Pack 6a. We’ll address this problem later in the article.
We have now got all the information we can by using the Windows API that is common to all versions of Windows. It’s time to move on to investigate further information that is specific to certain Windows platforms and OS versions.
Extended Windows API OS Information
So far we can detect the name of an OS product, but for the NT platform that’s not enough. Think for a moment about Windows XP – there are both the Home and Professional editions. For Windows 2000 it’s similarly complicated – we have both server (e.g. Advanced Server) and workstation (Professional) editions.
TOSVersionInfoEx
So, how do we get to this information? We make a start by examining the information provided by an extension of TOSVersionInfo named, unsurprisingly, TOSVersionInfoEx. Listing 10 shows a Pascal declaration of the structure. Note that Delphi does not declare it, so we need to include the definition in the interface section of UOSInfo.pas
.
1type
2 TOSVersionInfoEx = packed record
3 dwOSVersionInfoSize: DWORD;
4 dwMajorVersion: DWORD;
5 dwMinorVersion: DWORD;
6 dwBuildNumber: DWORD;
7 dwPlatformId: DWORD;
8 szCSDVersion: array[0..127] of AnsiChar;
9 wServicePackMajor: WORD;
10 wServicePackMinor: WORD;
11 wSuiteMask: WORD;
12 wProductType: Byte;
13 wReserved: Byte;
14 end;
Listing 10
We can see that the first six fields of TOSVersionInfoEx are the same as TOSVersionInfo (explained in Table 1). The new structure simply adds fields to the end of the old structure. Table 4 explains the purpose of the new fields:
Additional fields of TOSVersionInfoEx
Field | Description |
---|---|
wServicePackMajor | Major version number of the latest Service Pack installed on the system. If no Service Pack has been installed, the value is zero. |
wServicePackMinor | Minor version number of the latest Service Pack installed on the system. |
wSuiteMask |
Bit mask that identifies the product suites available on the system. This member can be a combination of the numerous VER_SUITE_* values defined by Microsoft. The values we will use here are:
|
wProductType |
Additional information about the operating system product. This member can take one of the following values:
|
wReserved | Reserved for future use. |
Table 4
Delphi defines neither the VER_NT_* nor VER_SUITE_* constants discussed in Table 4, so we must add the definitions to our UOSInfo unit’s interface section. Listing 11 shows the required definitions. Note that the listing declares all documented VER_SUITE_* flags, not just those described in Table 4.
1const
2
3 VER_NT_WORKSTATION = $0000001;
4 VER_NT_DOMAIN_CONTROLLER = $0000002;
5 VER_NT_SERVER = $0000003;
6
7
8 VER_SUITE_SMALLBUSINESS = $00000001;
9 VER_SUITE_ENTERPRISE = $00000002;
10 VER_SUITE_BACKOFFICE = $00000004;
11 VER_SUITE_COMMUNICATIONS = $00000008;
12 VER_SUITE_TERMINAL = $00000010;
13 VER_SUITE_SMALLBUSINESS_RESTRICTED = $00000020;
14 VER_SUITE_EMBEDDEDNT = $00000040;
15 VER_SUITE_DATACENTER = $00000080;
16 VER_SUITE_SINGLEUSERTS = $00000100;
17 VER_SUITE_PERSONAL = $00000200;
18 VER_SUITE_SERVERAPPLIANCE = $00000400;
19 VER_SUITE_BLADE = VER_SUITE_SERVERAPPLIANCE;
Listing 11
So, we’ve got a new structure that gives us extra information to play with, but how to we get the information from Windows?
Populating TOSVersionInfoEx
TOSVersionInfoEx is only supported on some NT platforms (those after NT 4 Service Pack 5) and not at all on the Windows 9x platform. As a result we have to check that we can use it. Here’s an apparent paradox: how do we check what OS we are using before calling the code that checks the OS?
The answer is quite simple, if a little convoluted. We call GetVersionEx() passing it a TOSVersionInfoEx instead of a TOSVersionInfo structure. If that call fails we have to pass GetVersionEx() a TOSVersionInfo structure instead. In actual fact we pass TOSVersionInfoEx again with the dwOSVersionInfoSize field set to the size of a TOSVersionInfo structure. This works because the first fields of TOSVersionInfoEx are the same as TOSVersionInfo.
Listing 12 shows how this is done. On OSs that do not support TOSVersionInfoEx, only the first six fields will be completed. The IsExtended variable is true if the whole of the structure is populated and false if not.
1var
2 POSV: POSVersionInfo;
3 IsExtended: Boolean;
4begin
5
6
7 FillChar(OSV, SizeOf(OSV), 0);
8
9
10
11 {$TYPEDADDRESS OFF}
12 POSVI := @OSV;
13 {$TYPEDADDRESS ON}
14
15 OSV.dwOSVersionInfoSize := SizeOf(TOSVersionInfoEx);
16 IsExtended := GetVersionEx(POSV^);
17 if not IsExtended then
18 begin
19
20 OSV.dwOSVersionInfoSize := SizeOf(TOSVersionInfo);
21 if not GetVersionEx(POSV^) then
22
23 raise Exception.Create('Can''t get OS info');
24 end;
25
26end;
Listing 12
Let us examine the code in detail. We first zero the TOSVersionInfoEx record then set its dwOSVersionInfoSize field to the size of the extended structure. Then we call the GetVersionEx() API function. If this function fails we reset the dwOSVersionInfoSize field to the size of a TOSVersionInfo structure and try again. This call should succeed. If it fails we raise an exception.
Now the Windows unit defines GetVersionEx() to accept a TOSVersionInfo parameter rather than one of type TOSVersionInfoEx. Because of Delphi’s strong typing we have to be a little underhand to get GetVersionEx() to accept our TOSVersionInfoEx parameter. This is accomplished by taking the address of the TOSVersionInfoEx record and casting it to a pointer to a TOSVersionInfo structure. Then we de-reference the pointer when passing to GetVersionEx(). Dirty but it works!
In the above code the IsExtended variable enables us to remember whether the structure contains extended information or not. Another method of checking is to read the structure size stored in the dwOSVersionInfoSize field – it will be SizeOf(TOSVersionInfoEx) if we have extended information and SizeOf(TOSVersionInfo) if not.
We now have some additional information at our disposal with which to describe later NT platform operating systems. Rather than using this directly, we will follow Delphi’s example and provide some global variables to store the extended OS version information. Later we will adapt our TOSInfo class to use these new global variables.
Extending Delphi’s RTL Support
Recall that Delphi’s Win32XXX global variables are set to the values of the fields of TOSVersionInfo when the program starts up. So it makes sense to provide more global variables that get their values from the additional fields of TOSVersionInfoEx. We will also provide a Boolean variable that records whether the extended information is available. The new variables are described in Table 5.
Additional Win32XXX global variables
Variable | Field | Notes |
---|---|---|
Win32ServicePackMajor | wServicePackMajor | Major version of any installed service pack or 0 if no such pack. Default value 0. |
Win32ServicePackMinor | wServicePackMinor | Minor version of any installed service pack. Default value 0. |
Win32SuiteMask | wSuiteMask | Bit flags that identify the product suites available on the system. Valid bit flags are defined by the VER_SUITE_XXX constants declared in Listing 11. Default value 0. |
Win32ProductType | wProductType | Additional information about the operating system. Possible values are given by the VER_NT_XXX constants declared in Listing 11. Default value 0. |
Win32HaveExInfo | – N/a – | Flag true if we have extended operation system version information and false if not. When this flag is false the variables above have no meaning and are zeroed. |
Table 5
We will declare the new global variables in the interface section of UOSInfo as shown in Listing 13.
1var
2 Win32HaveExInfo: Boolean = False;
3 Win32ServicePackMajor: Integer = 0;
4 Win32ServicePackMinor: Integer = 0;
5 Win32SuiteMask: Integer = 0;
6 Win32ProductType: Integer = 0;
Listing 13
All that remains to do is to try to get the extended information at start up and to store the required values in the global variables. Using the SysUtils InitPlatformId procedure as an example we will add a routine named InitPlatfornIdEx to UOSInfo‘s implementation section and call it in the initialization section. Listing 14 gives the required code.
1procedure InitPlatformIdEx;
2var
3 OSVI: TOSVersionInfoEx;
4 POSVI: POSVersionInfo;
5begin
6 FillChar(OSVI, SizeOf(OSVI), 0);
7 {$TYPEDADDRESS OFF}
8 POSVI := @OSVI;
9 {$TYPEDADDRESS ON}
10 OSVI.dwOSVersionInfoSize := SizeOf(TOSVersionInfoEx);
11 Win32HaveExInfo := GetVersionEx(POSVI^);
12 if Win32HaveExInfo then
13 begin
14 Win32ServicePackMajor := OSVI.wServicePackMajor;
15 Win32ServicePackMinor := OSVI.wServicePackMinor;
16 Win32SuiteMask := OSVI.wSuiteMask;
17 Win32ProductType := OSVI.wProductType;
18 end;
19end;
20
21
22
23initialization
24
25InitPlatformIdEx;
26
27end.
Listing 14
This routine works in a similar same way to that presented in Listing 12. The main difference is that we don’t try to call GetVersionEx with TOSVersionInfo if the call with TOSVersionInfoEx fails. This is not necessary since Delphi has already recorded the information that TOSVersionInfo provides. We simply use Win32HaveExInfo to record whether GetVersionEx succeeds and set the remaining global variables if so.
OS Version Information Class: Version 2
Now that we’ve explored how to get the additional OS information from the Windows API we are ready to extend our OS version information class accordingly.
Service Pack Versions
We will begin by adding two new parameterless class methods – Win32ServicePackMajor and Win32ServicePackMinor – and implementing them as shown in Listing 15. The methods return the major and minor service pack versions for supported Windows NT operating systems or 0 if the OS is not supported or has no service pack applied. As can be seen in the listing, the methods simply return the value of the corresponding global variable that we defined in the previous section.
1class function TOSInfo.ServicePackMajor: Integer;
2begin
3 Result := Win32ServicePackMajor;
4end;
5
6class function TOSInfo.ServicePackMinor: Integer;
7begin
8 Result := Win32ServicePackMinor;
9end;
Listing 15
These methods do not work for NT4 service pack 5 and earlier – the ServicePack method should be checked in these cases.
Product Type
Now we turn our attention to the information stored in the Win32ProductType variable. We have the following possibilities:
- The variable is not valid, either because we have an NT platform OS that doesn’t support the extended OS info, or because we have a non-NT platform.
- We have a supported NT platform in which case the variable informs us whether we have a workstation, server or domain controller system.
We will define a class method to distinguish these possibilities. The method will return an enumerated value of type TOSProductType that we will define in our unit’s interface section as:
1type
2 TOSProductType = (
3 ptNA,
4 ptUnknown,
5 ptNTWorkstation,
6 ptNTServer,
7 ptNTDomainController
8 );
Listing 16
Listing 17 shows the implementation of the new method, which should have its prototype added to the class declaration.
1class function TOSInfo.ProductType: TOSProductType;
2begin
3 if IsWinNT then
4 begin
5 case Win32ProductType of
6 VER_NT_WORKSTATION: Result := ptNTWorkstation;
7 VER_NT_SERVER: Result := ptNTServer;
8 VER_NT_DOMAIN_CONTROLLER: Result := ptNTDomainController;
9 else Result := ptUnknown;
10 end;
11 end
12 else
13 Result := ptNA;
14end;
Listing 17
We first check to see if we have an NT platform OS and return ptNA if not. If we have an NT platform OS then we check the value of the Win32ProductType global against its possible values and return the associated value from TOSProductType if a match is found. Where the value is unrecognised we return ptUnknown. You may have noticed that we don’t use Win32HaveExInfo to check whether extended OS information is supported. This is because Win32ProductType is set to zero in this event, causing all the case clauses to fail and ptUnknown to be returned.
Checking for Server OSs
We may also find it useful to be able to quickly check whether the operating system is a server system. To do this we add a new class method named IsServer to TOSInfo. Listing 18 defines the method.
1class function TOSInfo.IsServer: Boolean;
2begin
3 Result := ProductType in [ptNTServer, ptNTDomainController];
4end;
Listing 18
The method is defined in terms of the ProductType method – it simply checks if ProductType returns a value that represents an NT server OS.
Warning
IsServer will always return false if we have an NT4 server that has service pack 5 or lower. The solution of this problem will be discussed later in the article.
Finding the OS Edition
As we have already noted, each NT operating system version is sub-divided into various «editions». We will now extend TOSInfo to be able to detect and describe these editions. We do this by defining a new class method – Edition – that returns a string that describes the edition. The method will return the empty string if the edition is not known or if we are not using the NT platform. Listing 19 shows the method’s implementation.
1class function TOSInfo.Edition: string;
2begin
3 Result := '';
4 if IsWinNT then
5 begin
6 if Win32HaveExInfo then
7 begin
8
9 if IsServer then
10 begin
11
12 case Product of
13 osWinNT4:
14 begin
15 if CheckSuite(VER_SUITE_ENTERPRISE) then
16 Result := 'Server 4.0, Enterprise Edition'
17 else
18 Result := 'Server 4.0';
19 end;
20 osWin2000:
21 begin
22 if CheckSuite(VER_SUITE_DATACENTER) then
23 Result := 'Datacenter Server'
24 else if CheckSuite(VER_SUITE_ENTERPRISE) then
25 Result := 'Advanced Server'
26 else
27 Result := 'Standard Edition';
28 end;
29 osWinServer2003:
30 begin
31 if CheckSuite(VER_SUITE_DATACENTER) then
32 Result := 'DataCenter Edition'
33 else if CheckSuite(VER_SUITE_ENTERPRISE) then
34 Result := 'Enterprise Edition'
35 else if CheckSuite(VER_SUITE_BLADE) then
36 Result := 'Web Edition'
37 else
38 Result := 'Standard Edition';
39 end;
40 end;
41 end
42 else
43 begin
44
45 case Product of
46 osWinNT4:
47 Result := 'Workstation 4.0';
48 osWin2000:
49 Result := 'Professional';
50 osWinXP:
51 begin
52 if CheckSuite(VER_SUITE_PERSONAL) then
53 Result := 'Home Edition'
54 else
55 Result := 'Professional';
56 end;
57 end;
58 end;
59 end;
60 end;
61end;
Listing 19
We first set the result to the empty string in case we fail to find any information about the edition. We then check that we have an NT operating system – we are done if not. Next we check that we have extended OS information available by examining Win32HaveExInfo. Again we have nothing more to do if this variable is false.
The rest of the method depends on whether we have a server OS or a workstation version. We proceed similarly in either case. First we determine the OS product. For some workstations this is all the information we need to determine the edition (for example the only Windows 2000 workstation is the Professional Edition). In other cases we then need to check the operating system suite bitmask (stored in Win32SuiteMask) to see which VER_SUITE_* constants it contains. This bitmask gives us the appropriate edition.
Once again, this method will not work for NT4 service pack 5 and earlier. An empty string will be returned by Edition in this case. We will fix this problem later in the article where we will modify the method to work when Win32HaveExInfo is false.
To help keep Edition‘s code tidy we define a private helper method – CheckSuite – to examine the Win32SuiteMask bitmask. We pass CheckSuite the required bit flag and it returns true if Win32SuiteMask contains the flag. CheckSuite is defined in Listing 20.
1class function TOSInfo.CheckSuite(const Suite: Integer): Boolean;
2begin
3 Result := Win32SuiteMask and Suite <> 0;
4end;
Listing 20
This completes our discussion of extended OS information available from Windows. In the next section we will look at getting more information about early NT 4 operating systems from the registry.
Getting Information About NT 4 from the Registry
It has been noted above that the extended operation system information provided by the Windows API is not available in versions of NT 4 prior to Service Pack 6. In this section we will look at how to overcome this problem by reading the required information from the registry.
There are two pieces of relevant information we can get from the registry:
- The NT product type.
- Whether NT 4 Service Pack 6 or 6a is installed.
Product Type
Let us first look at how to get the product type. Windows stores information about the edition of the NT4 OS in the registry under this key:
HKLM\SYSTEM\CurrentControlSet\Control\ProductOptions
The key’s ProductName value stores a code that indicates the product type. Values and their interpretation are shown in Table 6.
NT4 Registry Entries
Code | Edition |
---|---|
WINNT |
Workstation |
LANMANNT |
Server |
SERVERNT |
Advanced Server |
Table 6
Service Pack 6a
We detect whether NT Service Pack 6a is installed by checking that Win32CSDVersion contains the string ‘Service Pack 6’ and then checking for the existence of the registry key:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Hotfix\Q246009
If the key exists we have Service Pack 6a. Arcane, isn’t it?
OS Version Information Class: Version 3
Having seen what information we can find from the registry, and how to get it, we can update our operating system information class to work correctly with earlier versions of Windows NT 4.
First we will define a couple of new private static methods to get the required information from the registry and then we will update various public methods.
Registry Access Methods
We will create two new private methods that access the registry: GetNT4ProductType to read the product type and IsNT4SP6a to check for NT 4 Service Pack 6a.
GetNT4ProductType will read the product type from the registry and return the equivalent TOSProductType code. Before doing this we will need to define a new TOSProductType value – ptNTAdvancedServer to represent NT 4 Advanced Server. Listing 21 shows the revised declaration of TOSProductType.
1type
2 TOSProductType = (
3 ptNA,
4 ptUnknown,
5 ptNTWorkstation,
6 ptNTServer,
7 ptNTDomainController,
8 ptNTAdvancedServer
9 );
Listing 21
Having extended TOSProductType we can now implement GetNT4ProductType as per Listing 22.
1class function TOSInfo.GetNT4ProductType: TOSProductType;
2var
3 Reg: TRegistry;
4 ProductType: string;
5begin
6 Result := ptUnknown;
7 Reg := TRegistry.Create;
8 try
9 Reg.RootKey := HKEY_LOCAL_MACHINE;
10 if Reg.OpenKeyReadOnly(
11 'SYSTEM\CurrentControlSet\Control\ProductOptions'
12 ) then
13 begin
14 ProductType := Reg.ReadString('ProductType');
15 if SameText(ProductType, 'WINNT') then
16 Result := ptNTWorkstation
17 else if SameText(ProductType, 'LANMANNT') then
18 Result := ptNTServer
19 else if SameText(ProductType, 'SERVERNT') then
20 Result := ptNTAdvancedServer;
21 Reg.CloseKey;
22 end;
23 finally
24 Reg.Free;
25 end;
26end;
Listing 22
We first set a default result of ptUnknown in case we can’t read an expected value from the registry. Then we create a TRegistry object via which we access the registry. Next we open the key described in the previous section and read the ProductType value. Finally the product type code is mapped onto an equivalent TOSProductType value. Remember that Table 6 lists the possible values of the ProductType registry entry.
Now we can look at the definition of IsNT4SP6a. See Listing 23.
1class function TOSInfo.IsNT4SP6a: Boolean;
2var
3 Reg: TRegistry;
4begin
5 if (Product = osWinNT4)
6 and SameText(Win32CSDVersion, 'Service Pack 6') then
7 begin
8
9
10 Reg := TRegistry.Create;
11 try
12 Reg.RootKey := HKEY_LOCAL_MACHINE;
13 Result := Reg.KeyExists(
14 'SOFTWARE\Microsoft\Windows NT\CurrentVersion\Hotfix\Q246009'
15 );
16 finally
17 Reg.Free;
18 end;
19 end
20 else
21
22 Result := False;
23end;
Listing 23
First we check whether we have Windows NT 4 and that Service Pack 6 is being reported by Delphi’s Win32CSDVersion global variable. Then we create a TRegistry object and use it to check for the presence of the registry key that indicates Service Pack 6a is present. We return true if the key is present and false otherwise.
Now we have the registry access methods we need it’s time to move on to modify some of TOSInfo’s public methods to make use of the registry information.
Updated Public Methods
We need to update four public methods to take account of the new information we have about NT 4. They are:
- ProductType – to get the NT 4 product types from the registry.
- IsServer – to take account of the new ptNTAdvancedServer product type.
- Edition – to describe the NT 4 editions that we learn about from the registry.
- ServicePack – to include detection of NT 4 service pack 6a.
We begin with ProductType. Listing 24 shows the revised method in full.
1class function TOSInfo.ProductType: TOSProductType;
2begin
3 if IsWinNT then
4 begin
5 if Win32HaveExInfo then
6 begin
7 case Win32ProductType of
8 VER_NT_WORKSTATION: Result := ptNTWorkstation;
9 VER_NT_SERVER: Result := ptNTServer;
10 VER_NT_DOMAIN_CONTROLLER: Result := ptNTDomainController;
11 else Result := ptUnknown;
12 end;
13 end
14 else
15 Result := GetNT4ProductType;
16 end
17 else
18 Result := ptNA;
19end;
Listing 24
We now check if Win32HaveExInfo is true and proceed as before if so. When extended version information is not present we know we must look in the registry for the product type, which we do by calling the new GetNT4ProductType private method.
Next up is IsServer. As Listing 25 shows, all we need to do is add ptNTAdvancedServer to the list of server product types that are tested.
1class function TOSInfo.IsServer: Boolean;
2begin
3 Result := ProductType in
4 [ptNTServer, ptNTDomainController, ptNTAdvancedServer];
5end;
Listing 25
The changes we must make to the Edition method are show in Listing 26. For the sake of brevity the listing shows only the required changes and the surrounding context.
1class function TOSInfo.Edition: string;
2begin
3 Result := '';
4 if IsWinNT then
5 begin
6 if Win32HaveExInfo then
7 begin
8
9 end
10 else
11 begin
12
13 case GetNT4ProductType of
14 ptNTWorkstation: Result := 'WorkStation';
15 ptNTServer: Result := 'Server';
16 ptNTAdvancedServer: Result := 'Advanced Server'
17 end;
18 if Result <> '' then
19 Result := Result + Format(
20 ' %d.%d', [Win32MajorVersion, Win32MinorVersion]
21 );
22 end;
23 end;
24end;
Listing 26
Taking our lead from Listing 24, we add an else clause that executes when Win32HaveExInfo returns false on an NT platform OS. The code in the else clause gets the product type from the registry by calling GetNT4ProductType and then returns a description of the returned product type. We then append the major and minor versions of the OS to the description.
The last method to be modified is ServicePack. Listing 27 shows the changes. Only the code relating to the NT platform is changed, so the existing Windows 9x code is omitted from the listing.
1class function TOSInfo.ServicePack: string;
2begin
3 Result := '';
4 if IsWin9x then
5 begin
6
7 end
8 else if IsWinNT then
9 begin
10 if IsNT4SP6a then
11 Result := 'Service Pack 6a'
12 else
13 Result := Win32CSDVersion;
14 end;
15end;
Listing 27
All that needs to be done after testing for the Windows NT platform is to call IsNT4SP6a to check for NT 4’s service pack 6a. If SP6a is found, we return ‘Service Pack 6a’, otherwise the value of the Win32CSDVersion variable is returned as before.
We have now completed our review of NT 4 version information that can be read from the registry. In the next section we will find out more about different versions of Windows XP.
More XP Related OS Information
We now turn our attention to providing a little more information about Windows XP editions. XP comes in several flavours, some of which we have already dealt with and others that we haven’t examined yet. In particular we will see how to detect:
- XP Media Centre Edition
- XP Tablet Edition
- 64 bit versions of XP
XP Media Center and Tablet Editions
Surprisingly, we find out about these systems using the GetSystemMetrics() API call by passing the SM_MEDIACENTER or SM_TABLETPC flags to the function. A non-zero return from GetSystemMetrics() indicates the specified edition is present.
64 Bit Versions of XP
With the advent of 64 bit versions of Windows we may need to detect if our application is running on such an operating system.
Any application compiled with current versions of Delphi (up to Delphi 2006 at the time of writing) is 32 bit. 64 bit Windows runs 32 bit applications in the WOW64 subsystem. So, we need a way to find out if our application is running in this subsystem. Windows XP provides a function to 32 bit applications named IsWow64Process that provides this information.
IsWow64Process is not available to OSs earlier than Windows XP. Consequently we must link to this function dynamically. Static linking would cause our application to fail on all OSs but XP.
OS Version Information Class: Version 4
Having explored how we can detect XP Media Center and Tablet editions and find if we are running on 64 bit Windows, we can now update our TOSInfo static class to take account of these XP specific issues.
Supporting XP Media Center and Tablet Editions
We will add two new public static methods to TOSInfo that detect XP Media Center and Tablet editions – IsMediaCenter and IsTablet. We will also modify the Edition method to detect these editions.
Listing 28 shows the implementation of the new IsMediaCenter and IsTablet methods. They simply call GetSystemMetrics with the appropriate flag.
1class function TOSInfo.IsMediaCenter: Boolean;
2begin
3 Result := GetSystemMetrics(SM_MEDIACENTER) <> 0;
4end;
5
6class function TOSInfo.IsTablet: Boolean;
7begin
8 Result := GetSystemMetrics(SM_TABLETPC) <> 0;
9end;
Listing 28
Unfortunately Delphi does not define SM_MEDIACENTER and SM_TABLETPC, so we will need to define these in our code. We may as well make these publicly available, so add the constants to the interface section of UOSInfo.pas
as shown in Listing 29.
1const
2 SM_MEDIACENTER = 87;
3 SM_TABLETPC = 86;
Listing 29
Armed with the two new IsMediaCenter and IsTablet methods we can now modify TOSInfo.Edition to detect XP Media Center and Tablet editions. Listing 30 shows the changes that need to be made to the method.
1class function TOSInfo.Edition: string;
2begin
3 Result := '';
4 if IsWinNT then
5 begin
6 if Win32HaveExInfo then
7 begin
8
9 if IsServer then
10 begin
11
12 end
13 else
14 begin
15
16 case Product of
17
18 osWinXP:
19 begin
20 if IsMediaCenter then
21 Result := 'Media Center Edition'
22 else if IsTablet then
23 Result := 'Tablet PC Edition'
24 else if CheckSuite(VER_SUITE_PERSONAL) then
25 Result := 'Home Edition'
26 else
27 Result := 'Professional';
28 end;
29 end;
30 end;
31 end
32 else
33 begin
34
35 end;
36 end;
37end;
Listing 30
Here we simply update the Windows XP workstation section of the code to test for Media Center and Tablet editions. If we find neither we check for XP Home and Professional as before.
Detecting 64 Bit Windows
Our approach will be to create a new method of our TOSInfo static class – IsWOW64 – that is a wrapper round the IsWow64Process API function we discussed above. The new method, shown in Listing 31, returns true when running on 64 bit windows and false otherwise.
1class function TOSInfo.IsWOW64: Boolean;
2type
3 TIsWow64Process = function(
4 Handle: THandle;
5 var Res: BOOL
6 ): BOOL; stdcall;
7var
8 IsWow64Result: BOOL;
9 IsWow64Process: TIsWow64Process;
10begin
11
12 IsWow64Process := GetProcAddress(
13 GetModuleHandle('kernel32'), 'IsWow64Process'
14 );
15 if Assigned(IsWow64Process) then
16 begin
17
18 if not IsWow64Process(GetCurrentProcess, IsWow64Result) then
19 raise Exception.Create('Bad process handle');
20
21 Result := IsWow64Result;
22 end
23 else
24
25 Result := False;
26end;
Listing 31
TIsWow64Process prototypes the IsWow64Process API function. The return value of this function indicates whether the it succeeded or failed – it does not indicate whether the WOW64 subsystem is present. In fact the function’s Res parameter is used for this purpose – it is set true if running on WOW64 and false otherwise.
Our method tries to load IsWow64Process from kernel32.dll
and stores a pointer to the function in the IsWow64Process local variable. If we succeed in loading the function we call it, passing the current process handle and storing the function’s output value in IsWow64Result. In the unlikely event that the function fails (i.e. returns false) we raise an exception. Otherwise the value of IsWow64Result is returned.
Should we fail to load IsWow64Process we know that we are running on an OS prior to Windows XP that does not have a 64 bit support. Therefore we return false.
Our examination of how to get operating system version information is now complete.
Demo code
A demo program to accompany this article can be found in the delphidabbler/article-demos
Git repository on GitHub.
You can view the code in the article-23
sub-directory. Alternatively download a zip file containing all the demos by going to the repository’s landing page and clicking the Clone or download button and selecting Download ZIP.
See the demo’s README.md file for details.
This source code is merely a proof of concept and is intended only to illustrate this article. It is not designed for use in its current form in finished applications. The code is provided on an «AS IS» basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
The demo is open source. See the demo’s LICENSE.md file for licensing details.
System Information Unit
You may also wish to look at my System Information Unit that contains similar code to that presented in this article.
Feedback
I hope you found this article useful.
If you have any observations, comments, or have found any errors there are two places you can report them.
- For anything to do with the article content, but not the downloadable demo code, please use this website’s Issues page on GitHub. Make sure you mention that the issue relates to «article #23».
- For bugs in the demo code see the
article-demo
project’sREADME.md
file for details of how to report them.