Como detectar a verdadeira versão do Windows?
Pergunta
Eu sei que posso chamar a função da API GetVersionEx Win32 para recuperar a versão do Windows.Na maioria dos casos, o valor retornado reflete a versão do meu Windows, mas às vezes não é assim.
Se um usuário executar meu aplicativo na camada de compatibilidade, GetVersionEx não reportará a versão real, mas a versão imposta pela camada de compatibilidade.Por exemplo, se eu estiver executando o Vista e executar meu programa no modo de compatibilidade "Windows NT 4", GetVersionEx não retornará a versão 6.0, mas a 4.0.
Existe uma maneira de contornar esse comportamento e obter a versão verdadeira do Windows?
Solução
A melhor abordagem que conheço é verificar se uma API específica é exportada de alguma DLL.Cada nova versão do Windows adiciona novas funções e, ao verificar a existência dessas funções, é possível saber em qual sistema operacional o aplicativo está sendo executado.Por exemplo, o Vista exporta GetLocaleInfoEx de kernel32.dll enquanto o Windows anterior não.
Para resumir a longa história, aqui está uma lista contendo apenas exportações de kernel32.dll.
> *function: implemented in* > GetLocaleInfoEx: Vista > GetLargePageMinimum: Vista, Server 2003 GetDLLDirectory: Vista, Server 2003, XP SP1 GetNativeSystemInfo: Vista, Server 2003, XP SP1, XP ReplaceFile: Vista, Server 2003, XP SP1, XP, 2000 OpenThread: Vista, Server 2003, XP SP1, XP, 2000, ME GetThreadPriorityBoost: Vista, Server 2003, XP SP1, XP, 2000, NT 4 IsDebuggerPresent: Vista, Server 2003, XP SP1, XP, 2000, ME, NT 4, 98 GetDiskFreeSpaceEx: Vista, Server 2003, XP SP1, XP, 2000, ME, NT 4, 98, 95 OSR2 ConnectNamedPipe: Vista, Server 2003, XP SP1, XP, 2000, NT 4, NT 3 Beep: Vista, Server 2003, XP SP1, XP, 2000, ME, 98, 95 OSR2, 95
Escrever a função para determinar a versão real do sistema operacional é simples;basta prosseguir do sistema operacional mais recente para o mais antigo e usar GetProcAddress para verificar APIs exportadas.Implementar isso em qualquer linguagem deve ser trivial.
O seguinte código em Delphi foi extraído do arquivo gratuito DSiWin32 biblioteca):
TDSiWindowsVersion = (wvUnknown, wvWin31, wvWin95, wvWin95OSR2, wvWin98,
wvWin98SE, wvWinME, wvWin9x, wvWinNT3, wvWinNT4, wvWin2000, wvWinXP,
wvWinNT, wvWinServer2003, wvWinVista);
function DSiGetWindowsVersion: TDSiWindowsVersion;
var
versionInfo: TOSVersionInfo;
begin
versionInfo.dwOSVersionInfoSize := SizeOf(versionInfo);
GetVersionEx(versionInfo);
Result := wvUnknown;
case versionInfo.dwPlatformID of
VER_PLATFORM_WIN32s: Result := wvWin31;
VER_PLATFORM_WIN32_WINDOWS:
case versionInfo.dwMinorVersion of
0:
if Trim(versionInfo.szCSDVersion[1]) = 'B' then
Result := wvWin95OSR2
else
Result := wvWin95;
10:
if Trim(versionInfo.szCSDVersion[1]) = 'A' then
Result := wvWin98SE
else
Result := wvWin98;
90:
if (versionInfo.dwBuildNumber = 73010104) then
Result := wvWinME;
else
Result := wvWin9x;
end; //case versionInfo.dwMinorVersion
VER_PLATFORM_WIN32_NT:
case versionInfo.dwMajorVersion of
3: Result := wvWinNT3;
4: Result := wvWinNT4;
5:
case versionInfo.dwMinorVersion of
0: Result := wvWin2000;
1: Result := wvWinXP;
2: Result := wvWinServer2003;
else Result := wvWinNT
end; //case versionInfo.dwMinorVersion
6: Result := wvWinVista;
end; //case versionInfo.dwMajorVersion
end; //versionInfo.dwPlatformID
end; { DSiGetWindowsVersion }
function DSiGetTrueWindowsVersion: TDSiWindowsVersion;
function ExportsAPI(module: HMODULE; const apiName: string): boolean;
begin
Result := GetProcAddress(module, PChar(apiName)) <> nil;
end; { ExportsAPI }
var
hKernel32: HMODULE;
begin { DSiGetTrueWindowsVersion }
hKernel32 := GetModuleHandle('kernel32');
Win32Check(hKernel32 <> 0);
if ExportsAPI(hKernel32, 'GetLocaleInfoEx') then
Result := wvWinVista
else if ExportsAPI(hKernel32, 'GetLargePageMinimum') then
Result := wvWinServer2003
else if ExportsAPI(hKernel32, 'GetNativeSystemInfo') then
Result := wvWinXP
else if ExportsAPI(hKernel32, 'ReplaceFile') then
Result := wvWin2000
else if ExportsAPI(hKernel32, 'OpenThread') then
Result := wvWinME
else if ExportsAPI(hKernel32, 'GetThreadPriorityBoost') then
Result := wvWinNT4
else if ExportsAPI(hKernel32, 'IsDebuggerPresent') then //is also in NT4!
Result := wvWin98
else if ExportsAPI(hKernel32, 'GetDiskFreeSpaceEx') then //is also in NT4!
Result := wvWin95OSR2
else if ExportsAPI(hKernel32, 'ConnectNamedPipe') then
Result := wvWinNT3
else if ExportsAPI(hKernel32, 'Beep') then
Result := wvWin95
else // we have no idea
Result := DSiGetWindowsVersion;
end; { DSiGetTrueWindowsVersion }
--- atualizado em 09/10/2009
Acontece que fica muito difícil fazer uma detecção de sistema operacional "não documentado" no Vista SP1 e superior.Uma olhada no Mudanças de API mostra que todas as funções do Windows 2008 também são implementadas no Vista SP1 e que todas as funções do Windows 7 também são implementadas no Windows 2008 R2.Muito ruim :(
--- fim da atualização
FWIW, este é um problema que encontrei na prática.Nós (a empresa para a qual trabalho) temos um programa que não estava realmente pronto para o Vista quando o Vista foi lançado (e algumas semanas depois disso...).Também não estava funcionando na camada de compatibilidade.(Alguns problemas de DirectX.Não pergunte.)
Não queríamos que usuários muito inteligentes para seu próprio bem executassem esse aplicativo no Vista - modo de compatibilidade ou não - então tive que encontrar uma solução (um cara mais inteligente do que eu me indicou a direção certa;o material acima não é ideia minha).Agora estou postando para seu prazer e para ajudar todas as pobres almas que terão que resolver este problema no futuro.Google, indexe este artigo!
Se você tiver uma solução melhor (ou uma atualização e/ou correção para a minha), poste uma resposta aqui ...
Outras dicas
Consulta WMI:
"Select * from Win32_OperatingSystem"
EDITAR:Na verdade melhor seria:
"Select Version from Win32_OperatingSystem"
Você poderia implementar isso no Delphi assim:
function OperatingSystemDisplayName: string;
function GetWMIObject(const objectName: string): IDispatch;
var
chEaten: Integer;
BindCtx: IBindCtx;
Moniker: IMoniker;
begin
OleCheck(CreateBindCtx(0, bindCtx));
OleCheck(MkParseDisplayName(BindCtx, PChar(objectName), chEaten, Moniker));
OleCheck(Moniker.BindToObject(BindCtx, nil, IDispatch, Result));
end;
function VarToString(const Value: OleVariant): string;
begin
if VarIsStr(Value) then begin
Result := Trim(Value);
end else begin
Result := '';
end;
end;
function FullVersionString(const Item: OleVariant): string;
var
Caption, ServicePack, Version, Architecture: string;
begin
Caption := VarToString(Item.Caption);
ServicePack := VarToString(Item.CSDVersion);
Version := VarToString(Item.Version);
Architecture := ArchitectureDisplayName(SystemArchitecture);
Result := Caption;
if ServicePack <> '' then begin
Result := Result + ' ' + ServicePack;
end;
Result := Result + ', version ' + Version + ', ' + Architecture;
end;
var
objWMIService: OleVariant;
colItems: OleVariant;
Item: OleVariant;
oEnum: IEnumvariant;
iValue: LongWord;
begin
Try
objWMIService := GetWMIObject('winmgmts:\\localhost\root\cimv2');
colItems := objWMIService.ExecQuery('SELECT Caption, CSDVersion, Version FROM Win32_OperatingSystem', 'WQL', 0);
oEnum := IUnknown(colItems._NewEnum) as IEnumVariant;
if oEnum.Next(1, Item, iValue)=0 then begin
Result := FullVersionString(Item);
exit;
end;
Except
// yes, I know this is nasty, but come what may I want to use the fallback code below should the WMI code fail
End;
(* Fallback, relies on the deprecated function GetVersionEx, reports erroneous values
when manifest does not contain supportedOS matching the executing system *)
Result := TOSVersion.ToString;
end;
Que tal obter a versão de um arquivo de sistema?
O melhor arquivo seria kernel32.dll, localizado em %WINDIR%\System32\kernel32.dll.
Existem APIs para obter a versão do arquivo.por exemplo:Estou usando o Windows XP -> "5.1.2600.5512 (xpsp.080413-2111)"
Outra solução:
leia a seguinte entrada de registro:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName
ou outras chaves de
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
armazenamento de versão real no bloco PEB de informações do processo.
Amostra para aplicativo Win32 (código Delphi)
unit RealWindowsVerUnit;
interface
uses
Windows;
var
//Real version Windows
Win32MajorVersionReal: Integer;
Win32MinorVersionReal: Integer;
implementation
type
PPEB=^PEB;
PEB = record
InheritedAddressSpace: Boolean;
ReadImageFileExecOptions: Boolean;
BeingDebugged: Boolean;
Spare: Boolean;
Mutant: Cardinal;
ImageBaseAddress: Pointer;
LoaderData: Pointer;
ProcessParameters: Pointer; //PRTL_USER_PROCESS_PARAMETERS;
SubSystemData: Pointer;
ProcessHeap: Pointer;
FastPebLock: Pointer;
FastPebLockRoutine: Pointer;
FastPebUnlockRoutine: Pointer;
EnvironmentUpdateCount: Cardinal;
KernelCallbackTable: PPointer;
EventLogSection: Pointer;
EventLog: Pointer;
FreeList: Pointer; //PPEB_FREE_BLOCK;
TlsExpansionCounter: Cardinal;
TlsBitmap: Pointer;
TlsBitmapBits: array[0..1] of Cardinal;
ReadOnlySharedMemoryBase: Pointer;
ReadOnlySharedMemoryHeap: Pointer;
ReadOnlyStaticServerData: PPointer;
AnsiCodePageData: Pointer;
OemCodePageData: Pointer;
UnicodeCaseTableData: Pointer;
NumberOfProcessors: Cardinal;
NtGlobalFlag: Cardinal;
Spare2: array[0..3] of Byte;
CriticalSectionTimeout: LARGE_INTEGER;
HeapSegmentReserve: Cardinal;
HeapSegmentCommit: Cardinal;
HeapDeCommitTotalFreeThreshold: Cardinal;
HeapDeCommitFreeBlockThreshold: Cardinal;
NumberOfHeaps: Cardinal;
MaximumNumberOfHeaps: Cardinal;
ProcessHeaps: Pointer;
GdiSharedHandleTable: Pointer;
ProcessStarterHelper: Pointer;
GdiDCAttributeList: Pointer;
LoaderLock: Pointer;
OSMajorVersion: Cardinal;
OSMinorVersion: Cardinal;
OSBuildNumber: Cardinal;
OSPlatformId: Cardinal;
ImageSubSystem: Cardinal;
ImageSubSystemMajorVersion: Cardinal;
ImageSubSystemMinorVersion: Cardinal;
GdiHandleBuffer: array [0..33] of Cardinal;
PostProcessInitRoutine: Cardinal;
TlsExpansionBitmap: Cardinal;
TlsExpansionBitmapBits: array [0..127] of Byte;
SessionId: Cardinal;
end;
//Get PEB block current win32 process
function GetPDB: PPEB; stdcall;
asm
MOV EAX, DWORD PTR FS:[30h]
end;
initialization
//Detect true windows wersion
Win32MajorVersionReal := GetPDB^.OSMajorVersion;
Win32MinorVersionReal := GetPDB^.OSMinorVersion;
end.
O seguinte funciona para mim no Windows 10 sem o GUID do Windows 10 listado no manifesto do aplicativo:
uses
System.SysUtils, Winapi.Windows;
type
NET_API_STATUS = DWORD;
_SERVER_INFO_101 = record
sv101_platform_id: DWORD;
sv101_name: LPWSTR;
sv101_version_major: DWORD;
sv101_version_minor: DWORD;
sv101_type: DWORD;
sv101_comment: LPWSTR;
end;
SERVER_INFO_101 = _SERVER_INFO_101;
PSERVER_INFO_101 = ^SERVER_INFO_101;
LPSERVER_INFO_101 = PSERVER_INFO_101;
const
MAJOR_VERSION_MASK = $0F;
function NetServerGetInfo(servername: LPWSTR; level: DWORD; var bufptr): NET_API_STATUS; stdcall; external 'Netapi32.dll';
function NetApiBufferFree(Buffer: LPVOID): NET_API_STATUS; stdcall; external 'Netapi32.dll';
type
pfnRtlGetVersion = function(var RTL_OSVERSIONINFOEXW): LONG; stdcall;
var
Buffer: PSERVER_INFO_101;
ver: RTL_OSVERSIONINFOEXW;
RtlGetVersion: pfnRtlGetVersion;
begin
Buffer := nil;
// Win32MajorVersion and Win32MinorVersion are populated from GetVersionEx()...
ShowMessage(Format('GetVersionEx: %d.%d', [Win32MajorVersion, Win32MinorVersion])); // shows 6.2, as expected per GetVersionEx() documentation
@RtlGetVersion := GetProcAddress(GetModuleHandle('ntdll.dll'), 'RtlGetVersion');
if Assigned(RtlGetVersion) then
begin
ZeroMemory(@ver, SizeOf(ver));
ver.dwOSVersionInfoSize := SizeOf(ver);
if RtlGetVersion(ver) = 0 then
ShowMessage(Format('RtlGetVersion: %d.%d', [ver.dwMajorVersion, ver.dwMinorVersion])); // shows 10.0
end;
if NetServerGetInfo(nil, 101, Buffer) = NO_ERROR then
try
ShowMessage(Format('NetServerGetInfo: %d.%d', [Buffer.sv101_version_major and MAJOR_VERSION_MASK, Buffer.sv101_version_minor])); // shows 10.0
finally
NetApiBufferFree(Buffer);
end;
end.
Atualizar: NetWkstaGetInfo()
provavelmente também funcionaria, semelhante a 'NetServerGetInfo()`, mas ainda não tentei.
Observação: Gabr está perguntando sobre uma abordagem que possa contornar as limitações do GetVersionEx
.O código JCL usa GetVersionEx e, portanto, está sujeito à camada de compatibilidade.Esta informação é apenas para pessoas que não precisam ignorar a camada de compatibilidade.
Usando o Jedi JCL, você pode adicionar a unidade JclSysInfo e chamar a função GetWindowsVersion
.Ele retorna um tipo enumerado TWindowsVersion.
Atualmente, o JCL contém todas as versões fornecidas do Windows e é alterado sempre que a Microsoft envia uma nova versão do Windows em uma caixa:
TWindowsVersion =
(wvUnknown, wvWin95, wvWin95OSR2, wvWin98, wvWin98SE, wvWinME,
wvWinNT31, wvWinNT35, wvWinNT351, wvWinNT4, wvWin2000, wvWinXP,
wvWin2003, wvWinXP64, wvWin2003R2, wvWinVista, wvWinServer2008,
wvWin7, wvWinServer2008R2);
Se você quiser saber se está executando o Windows 7 de 64 bits em vez de 32 bits, ligue JclSysInfo.IsWindows64
.
Observe que JCL também lida com edições, como Pro, Ultimate, etc.Para isso, chame GetWindowsEdition e ele retornará um destes:
TWindowsEdition =
(weUnknown, weWinXPHome, weWinXPPro, weWinXPHomeN, weWinXPProN, weWinXPHomeK,
weWinXPProK, weWinXPHomeKN, weWinXPProKN, weWinXPStarter, weWinXPMediaCenter,
weWinXPTablet, weWinVistaStarter, weWinVistaHomeBasic, weWinVistaHomeBasicN,
weWinVistaHomePremium, weWinVistaBusiness, weWinVistaBusinessN,
weWinVistaEnterprise, weWinVistaUltimate, weWin7Starter, weWin7HomeBasic,
weWin7HomePremium, weWin7Professional, weWin7Enterprise, weWin7Ultimate);
Para interesse histórico, você também pode verificar a edição no nível NT com a função NtProductType, ela retorna:
TNtProductType = (ptUnknown, ptWorkStation, ptServer, ptAdvancedServer,
ptPersonal, ptProfessional, ptDatacenterServer,
ptEnterprise, ptWebEdition);
Observe que "N edições" são detectadas acima.Essa é uma versão do Windows para a UE (Europa), criada devido às regulamentações antitruste da UE.Essa é uma gradação muito boa de detecção dentro do JCL.
Aqui está um exemplo de função que o ajudará a detectar o Vista e a fazer algo especial quando estiver no Vista.
function IsSupported:Boolean;
begin
case GetWindowsVersion of
wvVista: result := false;
else
result := true;
end;
end;
Observe que se você quiser fazer uma verificação "maior que", deverá usar outras técnicas.Observe também que a verificação de versão muitas vezes pode ser uma fonte de falhas futuras.Geralmente opto por avisar os usuários e continuar, para que meu código binário não se torne a verdadeira fonte de quebra no futuro.
Recentemente tentei instalar um aplicativo e o instalador verificou o espaço livre na minha unidade e não instalou, porque eu tinha mais de 2 gigabytes de espaço livre.O valor inteiro assinado de 32 bits no instalador tornou-se negativo, interrompendo o instalador.Tive que instalá-lo em uma VM para fazê-lo funcionar.Adicionar "código inteligente" geralmente torna seu aplicativo mais "estúpido".Seja cauteloso.
Aliás, descobri que na linha de comando você pode executar WMIC.exe e digitar path Win32_OperatingSystem
(O "Select * from Win32_OperatingSystem" não funcionou para mim).No futuro, talvez o JCL possa ser estendido para usar as informações do WMI.
Essencialmente para responder a Q duplicada: Obtendo versões principais, secundárias e de compilação do sistema operacional para Windows 8.1 e superior no Delphi 2007
Começando com W2K você pode usar NetServerGetInfo.NetServerGetInfo retorna as informações corretas em W7 e W8.1, não é possível testar em W10.
function GetWinVersion: string;
var
Buffer: PServerInfo101;
begin
Buffer := nil;
if NetServerGetInfo(nil, 101, Pointer(Buffer)) = NO_ERROR then
try
Result := <Build You Version String here>(
Buffer.sv101_version_major,
Buffer.sv101_version_minor,
VER_PLATFORM_WIN32_NT // Save since minimum support begins in W2K
);
finally
NetApiBufferFree(Buffer);
end;
end;
Uma observação sobre o uso de NetServerGetInfo(), que ainda funciona no Windows 10 (10240.th1_st1)...
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370903%28v=vs.85%29.aspx
sv101_version_major
O número da versão principal e o tipo de servidor.
O número principal da versão de lançamento do sistema operacional é especificado nos 4 bits menos significativos.O tipo de servidor é especificado nos 4 bits mais significativos.O major_version_mask bitmask definido no cabeçalho lmServer.h {0x0f} deve ser usado por um aplicativo para obter o número principal da versão deste membro.
Em outras palavras, (sv101_version_major & MAJOR_VERSION_MASK).