Problems reading registry from Delphi 7 on Windows 7 64 bit - windows

I think this question was already asked, but I couldn't find a solution which works for me. I use Delphi 7 under Windows 7 Ultimate, 64 bit. Actually I started writing application under 32 bit OS, but then changes PC, so now its 64. In my program I use registration process with Licence ID generated from PROGID value of Windows. Unfortunately it doesn't read the value, seems like it is looking in a different folder, probably redirected by Windows 64 to 32 bit registry. Can you help? This is the code I use:
Registry := TRegistry.Create(KEY_READ OR $0100);
try
Registry.Lazywrite := false;
Registry.RootKey := HKEY_LOCAL_MACHINE;
if CheckForWinNT = true then
Begin
if not Registry.OpenKeyReadOnly('\Software\Microsoft\Windows NT\CurrentVersion') then showmessagE('cant open');
end
else
Registry.OpenKeyReadOnly('\Software\Microsoft\Windows\CurrentVersion');
result := Registry.ReadString('ProductID');
Registry.CloseKey;
finally
Registry.Free;
end; // try..finally
Also, do you know how to find whether program is running under 64 bit or 32 bit computer in Delphi 7?

You already asked this question see Registry ReadString method is not working in Windows 7 in Delphi 7.
So you know that you have to add $0100 in the TRegistry.Create. The problem with your code is that you use OpenKeyReadOnly which resets the Access property of the registry to KEY_READ, so KEY_READ or $0100 is lost.
Just use OpenKey instead of OpenKeyReadOnly, this won't reset your Access property.

Here is some Delphi 7 code to detect whether you are running in a 64-bit OS:
function Is64BitOS: Boolean;
type
TIsWow64Process = function(Handle:THandle; var IsWow64 : BOOL) : BOOL; stdcall;
var
hKernel32 : Integer;
IsWow64Process : TIsWow64Process;
IsWow64 : BOOL;
begin
// we can check if the operating system is 64-bit by checking whether
// we are running under Wow64 (we are 32-bit code). We must check if this
// function is implemented before we call it, because some older versions
// of kernel32.dll (eg. Windows 2000) don't know about it.
// see http://msdn.microsoft.com/en-us/library/ms684139%28VS.85%29.aspx
Result := False;
hKernel32 := LoadLibrary('kernel32.dll');
if (hKernel32 = 0) then RaiseLastOSError;
#IsWow64Process := GetProcAddress(hkernel32, 'IsWow64Process');
if Assigned(IsWow64Process) then begin
IsWow64 := False;
if (IsWow64Process(GetCurrentProcess, IsWow64)) then begin
Result := IsWow64;
end
else RaiseLastOSError;
end;
FreeLibrary(hKernel32);
end;
(Shamelessly plagiarized from myself, here)
It looks like you are passing KEY_WOW64_64KEY ($0100), so you should be looking at the 64-bit registry branch. If you want to look at the 32-bit registry branch, you should pass KEY_WOW64_32KEY ($0200).

As to your side question, whether it's a 64-bit computer (which is not the same thing as running on a 64-bit OS), have a look at the answers to this question.

I know this topic is about delphi 7, but I thought I was having problems reading the registry and came here to learn.. I ended up using Key_Read instead of all the extras suggested here.
I'm using Delphi 2010 and I used Key_Read just fine.
Here is my part of my source that works:
//Search registry
reg:=TRegistry.Create(KEY_READ);
with reg do begin
try
RootKey := HKEY_LOCAL_MACHINE;
if OpenKey('\SOFTWARE\Wow6432Node\Blizzard Entertainment\World of Warcraft',false) then
begin
memo.Lines.Add('HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Blizzard Entertainment\World of Warcraft - exists');
wowdir1 := readstring('InstallPath');
memo.Lines.Add('InstallPath - ' + wowdir1);
newline;
closekey;
end;
if OpenKey('\SOFTWARE\Wow6432Node\Blizzard Entertainment\World of Warcraft',false) then
begin
memo.Lines.Add('HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Blizzard Entertainment\World of Warcraft - exists');
wowdir2 := readstring('GamePath');
memo.Lines.Add('GamePath - ' + wowdir2);
newline;
wowdir1 := readstring('');
closekey;
end;
if OpenKey('\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\World of Warcraft',false) then
begin
memo.Lines.Add'HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Uninstall\World of Warcraft - exists');
wowdir3 := readstring('InstallLocation');
memo.Lines.Add('InstallLocation - ' + wowdir3);
newline;
wowdir1 := readstring('');
closekey;
end;
finally
reg.Free;
end;
I tried the other Keys that are displayed here and found I don't need KEY_WOW64_64KEY OR KEY_WOW64_32KEY. This must have been a bug that has been corrected in Delphi 2010.

Related

How to permanently terminate Windows Explorer (the "explorer.exe" process)?

I'm using the following code to terminate a process:
function KillTask(ExeFileName: string): Integer;
const
PROCESS_TERMINATE = $0001;
var
ContinueLoop: BOOL;
FSnapshotHandle: THandle;
FProcessEntry32: TProcessEntry32;
begin
Result := 0;
FSnapshotHandle := CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
FProcessEntry32.dwSize := SizeOf(FProcessEntry32);
ContinueLoop := Process32First(FSnapshotHandle, FProcessEntry32);
while Integer(ContinueLoop) <> 0 do
begin
if ((UpperCase(ExtractFileName(FProcessEntry32.szExeFile)) =
UpperCase(ExeFileName)) or (UpperCase(FProcessEntry32.szExeFile) =
UpperCase(ExeFileName))) then
Result := Integer(TerminateProcess(
OpenProcess(PROCESS_TERMINATE,
BOOL(0),
FProcessEntry32.th32ProcessID),
0));
ContinueLoop := Process32Next(FSnapshotHandle, FProcessEntry32);
end;
CloseHandle(FSnapshotHandle);
end;
The problem is, when I call the above function in order to permanently terminate the explorer.exe, the Windows Explorer terminates though, but it's re-started afterwards:
KillTask('explorer.exe');
I'm using Delphi XE3, Delphi 7 and Windows 8.
Based on this Exit Explorer feature and code debugged by Luke in this post you may try to use the following code:
Warning:
This way is absolutely undocumented! So all constants and variables appearing in this post are fictitious. Any resemblance to real, documented code is purely coincidental :-)
function ExitExplorer: Boolean;
var
TrayHandle: HWND;
const
WM_EXITEXPLORER = $5B4;
begin
Result := False;
TrayHandle := FindWindow('Shell_TrayWnd', nil);
if TrayHandle <> 0 then
Result := PostMessage(TrayHandle, WM_EXITEXPLORER, 0, 0);
end;
I've tested it in Windows 7, where it works and doesn't even need the administrator elevation. Don't know how about the other systems (I'd say this won't work at least on Windows XP, but it's just a guess).

Wow64DisableWow64FsRedirection fileExits

a file is located in C:\program files (x86)\my app\myexe.exe
FileExists('C:\program files (x86)\my app\myexe.exe') returns true;
FileExists('C:\program files\my app\myexe.exe') returns false;
in both cases, if I use Wow64DisableWow64FsRedirection or not.
Why ? Thanks
File system redirection is only there for the %windir%\system32 directory. The description of the File System Redirector seems to make this obvious.
Note the comment in the page
Applications should use the SHGetSpecialFolderPath function to determine the %ProgramFiles% directory name.
Edit Turns out that the FOLDERID_ProgramFilesx64 does not work on 32bit applications running on 64bit windows. In this case, you can use the environment variable %ProgramW6432% instead. Note that this variable is only available on Windows 7 and later for 32bit applications.
The following delphi snippet allows accessing the variable:
function GetEnvironmentString(aString : string) : string;
var
dest : string;
retSize : integer;
begin
SetLength(dest, MAX_PATH);
retSize := ExpandEnvironmentStrings(pchar(aString), pchar(dest), MAX_PATH);
if retSize > 0 then
SetLength(dest, retSize - 1);
result := dest;
end;
Called as:
GetEnvironmentString('%ProgramW6432%');
IF you're on a 64bit version of windows, then a 32bit application cannot use FOLDERID_ProgramFilesX64 to explicitly get the 64bit location of Program Files, but can use the environment variable expansion instead. On a 32bit version of windows, this location is invalid, and will not get you a value. You need to check the bitness of the system before attempting to access this variable.
You can use the function IsWow64Process to determine this. The following snippet should allow you to check this:
function IsWow64: Boolean;
type
TIsWow64Process = function(Handle: Windows.THandle; var Res: Windows.BOOL): Windows.BOOL; stdcall;
var
IsWow64Result: Windows.BOOL;
IsWow64Process: TIsWow64Process;
begin
// Try to load required function from kernel32
IsWow64Process := Windows.GetProcAddress(Windows.GetModuleHandle('kernel32.dll'), 'IsWow64Process');
if Assigned(IsWow64Process) then
begin
// Function is implemented: call it
if not IsWow64Process(Windows.GetCurrentProcess, IsWow64Result) then
raise SysUtils.Exception.Create('IsWow64: bad process handle');
// Return result of function
Result := IsWow64Result;
end
else
// Function not implemented: can't be running on Wow64
Result := False;
end;
In summary: FOLDERID_ProgramFiles gives you the 32/64 bit variant when accessed from a 32/64 bit program, FOLDERID_ProgramFilesX64 gives you the 64bit version explicitly on a 64-bit application, and FOLDERID_ProgramFilesX86 gives you the 32bit variant explicitly. You can use the environment variable expansion to get the 64bit value on a 32bit application

How can I freeze the execution of a program?

Say I have got a program that hogs the processor and/or hard disk to the point that it makes it nearly impossible to do anything else on that computer. Now I don't want to kill that program because what it does is useful (it's a batch job that really is that CPU or disk heavy, e.g. it could ZIP a few gigabytes of data files) but for a short time I need to do something else on that computer. Is there any way an external program could do to freeze that performance killer for a while?
It's like the old DOS option to switch between programs without actually having multitasking.
Assume that the hypothetical program in question is a 3rd party product for which I don't have the source code and there is no way to tell it to pause.
I know I can change the program's priority class e.g. in TaskManager but that's not enough, I want to freeze it.
I am talking about Windows XP as the OS and would like to program a solution with Delphi. I have got all rights on the machine, so I could start something as administrator, replace files and I could also install a service if that is necessary.
You can freeze it with Process Explorer: Right-click on your program and select Suspend.
Here is some sample code for programmatic freezing from http://www.c-plusplus.de/forum/viewtopic-var-p-is-1460293.html, edited and omitted error checking for brevity:
#include <windows.h>
_NtSuspendProcess NtSuspendProcess =
(_NtSuspendProcess) GetProcAddress( GetModuleHandle( "ntdll" ),
"NtSuspendProcess" );
HANDLE ProcessHandle = OpenProcess( PROCESS_ALL_ACCESS, FALSE, pid);
NtSuspendProcess( ProcessHandle );
If you want to do it programatically you can use the approach described here.
What is does, is enumerating all the threads in a process and then suspending them. There is no SuspendProcess API, so this is a simulation of such a call.
Beware that this can potentionally have some bad side effects. It depend on the process and how it is written.
I don't know of any other way to do it in the Win32/64 API world. If you go lower to the kernel land and use the NT* APIs you have "NtSuspendProcess" API available. But this is undocumented so it can change with any version of windows or even with any service pack (not very likely though).
The declaration of "NtSuspendProcess" can be found in the JEDI ports of the windows APIs.
You can use my ProcessInfo component to suspend all threads belonging to the process. The approach is similar to what Runner explained to you. The code would be something like this:
var
Process : TProcessItem;
AThread: TThreadItem;
begin
Process := ProcessInfo1.RunningProcesses.FindByName('notepad.exe');
if Assigned(Process) then
begin
for AThread in Process.Threads do
AThread.SuspendThread;
end;
end;
You can download source code of ProcessInfo form here
function OpenThread(dwDesiredAccess: DWORD; InheritHandle: Boolean; dwThreadID: DWORD): THandle; stdcall; external 'kernel32.dll';
function ResumeProcess(PID: DWORD):Boolean;
var
tid, snap: THandle;
TE32: TThreadEntry32;
begin
Result := False;
snap := CreateToolHelp32SnapShot(TH32CS_SNAPTHREAD, 0);
TE32.dwSize := SizeOf(TThreadEntry32);
Thread32First(snap, TE32);
repeat
if TE32.th32OwnerProcessID = PID then begin
tid := OpenThread($0002, FALSE, TE32.th32ThreadID);
ResumeThread(tid);
Result := TRUE;
CloseHandle(tid);
end;
until Thread32Next(snap, TE32) = false;
CloseHandle(snap);
end;
function SuspendProcess(PID: DWORD): Boolean;
var
tid, snap: THandle;
TE32: TThreadEntry32;
begin
Result := False;
snap := CreateToolHelp32SnapShot(TH32CS_SNAPTHREAD, 0);
TE32.dwSize := SizeOf(TThreadEntry32);
Thread32First(snap, TE32);
repeat
if TE32.th32OwnerProcessID = PID then begin
tid := OpenThread($0002, FALSE, TE32.th32ThreadID);
SuspendThread(tid);
Result := TRUE;
CloseHandle(tid);
end;
until Thread32Next(snap, TE32) = false;
CloseHandle(snap);
end;
Hope this helps

How to retrieve a file from Internet via HTTP?

I want to download a file from Internet and InternetReadFile seem a good and easy solution at the first glance. Actually, too good to be true. Indeed, digging a bit I have started to see that actually there are a lot of issues with it. People are complaining about all kinds of problems when using this code.
Problems could appear because:
the application freezes temporarily until the HTTP server responds
the application freezes temporarily because the Internet connections breaks
the application locks up because the HTTP server never responds
the InternetOpen (I just discovered this recently) MUST be called only once during application life time
I could not find a complete example about how to use it properly and robustly. Does anybody have an idea about how to implement it in a separate thread and with a time out? There is another SIMPLE way to robustly download a file from Internet. Though I don't want to complicate my life with very large libraries like Jedi or even Indy.
function GetFileHTTP (const fileURL, FileName: String): boolean;
CONST
BufferSize = 1024;
VAR
hSession, hURL: HInternet;
Buffer: array[1..BufferSize] of Byte;
BufferLen: DWORD;
f: File;
sAppName: string;
begin
// result := false;
sAppName := ExtractFileName(Application.ExeName) ;
hSession := InternetOpen(PChar(sAppName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0) ; { be aware that InternetOpen need only be called once in your application!!!!!!!!!!!!!! }
TRY
hURL := InternetOpenURL(hSession, PChar(fileURL), nil, 0, 0, 0) ;
TRY
AssignFile(f, FileName) ;
Rewrite(f, 1) ;
REPEAT
InternetReadFile(hURL, #Buffer, SizeOf(Buffer), BufferLen);
BlockWrite(f, Buffer, BufferLen)
UNTIL BufferLen = 0;
CloseFile(f) ;
Result:= True;
FINALLY
InternetCloseHandle(hURL)
end
FINALLY
InternetCloseHandle(hSession)
END;
END;
Edit:
This functions checks if Internet connection is available. It seems to work on Win98 also.
{ Are we connected to the Internet? }
function IsConnectedToInternet: Boolean; { Call SHELL32.DLL for Win < Win98 otherwise call URL.dll }
var InetIsOffline: function(dwFlags: DWORD): BOOL; stdcall;
begin
Result:= FALSE;
if IsApiFunctionAvailable('URL.DLL', 'InetIsOffline', #InetIsOffline)
then Result:= NOT InetIsOffLine(0)
else
if IsApiFunctionAvailable('SHELL32.DLL', 'InetIsOffline', #InetIsOffline)
then Result:= NOT InetIsOffLine(0)
end;
I am using Delphi 7. Many thanks.
Edit:
Losing customers because the application hangs at the first start up is the perfect recipe for losing money.
Writing your code to be Microsoft platform dependent is bad. You never know if the customer has the IE version x.x installed.
Installing stuff into a user's computer is like playing with guns. It will backfire.
(see more about this here: http://thesunstroke.blogspot.com/2010/06/programmig-like-there-is-no-ms-windows.html)
I basically do the same as you do. For me it works fairly flawlessly.
The only differences between my code and your code is I have an INTERNET_FLAG_RELOAD parameter to force a download from the file and not the cache. You can try that and see if it works better:
hURL := InternetOpenURL(hSession, PChar(fileURL), nil, 0, INTERNET_FLAG_RELOAD, 0) ;
Also check for an internet connection before downloading. Do this:
dwConnectionTypes := INTERNET_CONNECTION_MODEM
+ INTERNET_CONNECTION_LAN
+ INTERNET_CONNECTION_PROXY;
InternetConnected := InternetGetConnectedState(#dwConnectionTypes, 0);
if InternetConnected then ...
Here's some sample code that uses Indy. This code is for Delphi 2010 (with Indy 10?), but the code for Delphi 7 would be similar. I've used Indy for years with D7 and have been very happy with it. I think in D7 we use Indy 9. Check if you need to download a new version...
You can use OnWork and OnWorkBegin to add a progress meter if you need to.
This code I excerpted from a bigger piece, editing it a bit. I did not try compiling it, but it will give you a good starting place.
function Download( const aSourceURL: String;
const aDestFileName: String;
out aDownloadResult: TDownloadResult;
out aErrm: String): boolean;
var
Stream: TMemoryStream;
IDAntiFreeze: TIDAntiFreeze;
begin
aDownloadResult := DROther;
Result := FALSE;
fIDHTTP := TIDHTTP.Create;
fIDHTTP.HandleRedirects := TRUE;
fIDHTTP.AllowCookies := FALSE;
fIDHTTP.Request.UserAgent := 'Mozilla/4.0';
fIDHTTP.Request.Connection := 'Keep-Alive';
fIDHTTP.Request.ProxyConnection := 'Keep-Alive';
fIDHTTP.Request.CacheControl := 'no-cache';
IDAntiFreeze := TIDAntiFreeze.Create;
Stream := TMemoryStream.Create;
try
try
fIDHTTP.Get(aSourceURL, Stream);
if FileExists(aDestFileName) then
DeleteFile(PWideChar(aDestFileName));
Stream.SaveToFile(aDestFileName);
Result := TRUE;
aDownloadResult :=drSuccess;
except
On E: Exception do
begin
Result := FALSE;
aErrm := E.Message + ' (' + IntToStr(fIDHTTP.ResponseCode) + ')';
end;
end;
finally
Stream.Free;
IDAntiFreeze.Free;
fIDHTTP.Free;
end;
end; { Download }
My personal favorite is using the WebHttpRequest component from importing the "Microsoft WinHTTP Services" type library: http://yoy.be/item.asp?i142
var
w:IWebHttpRequest;
f:TFileStream;
os:TOleStream;
begin
w:=CoWebHttpRequest.Create;
w.Open('GET',SourceURL,false);
w.Send(EmptyParam);
os:=TOleStream.Create(IUnknown(w.ResponseStream) as IStream);
f:=TFileStream.Create(DestinationFilePath,fmCreate);
os.Position:=0;
f.CopyFrom(os,os.Size);
f.Free;
os.Free;
w:=nil;
end;
I recommend Synapse. It's small, stable and easy-to-use (no need of any external libraries).
Example from httpsend.pas
function HttpGetText(const URL: string; const Response: TStrings): Boolean;
var
HTTP: THTTPSend;
begin
HTTP := THTTPSend.Create;
try
Result := HTTP.HTTPMethod('GET', URL);
if Result then
Response.LoadFromStream(HTTP.Document);
finally
HTTP.Free;
end;
end;
Instead of fiddling with the WinAPI, the ExtActns unit provides just what you need for downloading to a file.
procedure TMainForm.DownloadFile(URL: string; Dest: string);
var
dl: TDownloadURL;
begin
dl := TDownloadURL.Create(self);
try
dl.URL := URL;
dl.FileName := Dest;
dl.ExecuteTarget(nil); //this downloads the file
dl.Free;
except
dl.Free;
end;
end;
Under the hood, it uses URLDownloadToFile from the URLMon library - which is part of IE, and therefore part of Windows.
TDownloadURL doesn't handle any timeout for you - it doesn't look like such a thing is supported in URLMon at all, although there could be some default timeout that causes the call to fail - but you could use the OnProgress event on TDownloadURL to get notified when something happens, and then do something in another thread if it's been too long since the last callback.
Solved using improved version of the above code.
(it still does not solve all issues - MS does not actually implemented full support for server time out)
The connection does not timeout while downloading file from internet

How to detect true Windows version?

I know I can call the GetVersionEx Win32 API function to retrieve Windows version. In most cases returned value reflects the version of my Windows, but sometimes that is not so.
If a user runs my application under the compatibility layer, then GetVersionEx won't be reporting the real version but the version enforced by the compatibility layer. For example, if I'm running Vista and execute my program in "Windows NT 4" compatibility mode, GetVersionEx won't return version 6.0 but 4.0.
Is there a way to bypass this behaviour and get true Windows version?
The best approach I know is to check if specific API is exported from some DLL. Each new Windows version adds new functions and by checking the existance of those functions one can tell which OS the application is running on. For example, Vista exports GetLocaleInfoEx from kernel32.dll while previous Windowses didn't.
To cut the long story short, here is one such list containing only exports from 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
Writing the function to determine the real OS version is simple; just proceed from newest OS to oldest and use GetProcAddress to check exported APIs. Implementing this in any language should be trivial.
The following code in Delphi was extracted from the free DSiWin32 library):
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 }
--- updated 2009-10-09
It turns out that it gets very hard to do an "undocumented" OS detection on Vista SP1 and higher. A look at the API changes shows that all Windows 2008 functions are also implemented in Vista SP1 and that all Windows 7 functions are also implemented in Windows 2008 R2. Too bad :(
--- end of update
FWIW, this is a problem I encountered in practice. We (the company I work for) have a program that was not really Vista-ready when Vista was released (and some weeks after that ...). It was not working under the compatibility layer either. (Some DirectX problems. Don't ask.)
We didn't want too-smart-for-their-own-good users to run this app on Vista at all - compatibility mode or not - so I had to find a solution (a guy smarter than me pointed me into right direction; the stuff above is not my brainchild). Now I'm posting it for your pleasure and to help all poor souls that will have to solve this problem in the future. Google, please index this article!
If you have a better solution (or an upgrade and/or fix for mine), please post an answer here ...
WMI QUery:
"Select * from Win32_OperatingSystem"
EDIT: Actually better would be:
"Select Version from Win32_OperatingSystem"
You could implement this in Delphi like so:
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;
How about obtaining the version of a system file?
The best file would be kernel32.dll, located in %WINDIR%\System32\kernel32.dll.
There are APIs to obtain the file version. eg: I'm using Windows XP -> "5.1.2600.5512 (xpsp.080413-2111)"
Another solution:
read the following registry entry:
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProductName
or other keys from
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion
real version store on PEB block of process information.
Sample for Win32 app (Delphi Code)
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.
The following works for me in Windows 10 without the Windows 10 GUID listed in the application manifest:
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.
Update: NetWkstaGetInfo() would probably also work, similar to 'NetServerGetInfo()`, but I have not try it yet.
Note: Gabr is asking about an approach that can bypass the limitations of GetVersionEx. JCL code uses GetVersionEx, and is thus subject to compatibility layer. This information is for people who don't need to bypass the compatibility layer, only.
Using the Jedi JCL, you can add unit JclSysInfo, and call function GetWindowsVersion. It returns an enumerated type TWindowsVersion.
Currently JCL contains all shipped windows versions, and gets changed each time Microsoft ships a new version of Windows in a box:
TWindowsVersion =
(wvUnknown, wvWin95, wvWin95OSR2, wvWin98, wvWin98SE, wvWinME,
wvWinNT31, wvWinNT35, wvWinNT351, wvWinNT4, wvWin2000, wvWinXP,
wvWin2003, wvWinXP64, wvWin2003R2, wvWinVista, wvWinServer2008,
wvWin7, wvWinServer2008R2);
If you want to know if you're running 64-bit windows 7 instead of 32-bit, then call JclSysInfo.IsWindows64.
Note that JCL allso handles Editions, like Pro, Ultimate, etc. For that call GetWindowsEdition, and it returns one of these:
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);
For historical interest, you can check the NT-level edition too with the NtProductType function, it returns:
TNtProductType =       (ptUnknown, ptWorkStation, ptServer, ptAdvancedServer,       
ptPersonal, ptProfessional, ptDatacenterServer,
ptEnterprise, ptWebEdition);
Note that "N editions" are detected above. That's an EU (Europe) version of Windows, created due to EU anti-trust regulations. That's a pretty fine gradation of detection inside the JCL.
Here's a sample function that will help you detect Vista, and do something special when on Vista.
function IsSupported:Boolean;
begin
case GetWindowsVersion of
wvVista: result := false;
else
result := true;
end;
end;
Note that if you want to do "greater than" checking, then you should just use other techniques. Also note that version checking can often be a source of future breakage. I have usually chosen to warn users and continue, so that my binary code doesn't become the actual source of breakage in the future.
Recently I tried to install an app, and the installer checked my drive free space, and would not install, because I had more than 2 gigabytes of free space. The 32 bit integer signed value in the installer became negative, breaking the installer. I had to install it into a VM to get it to work. Adding "smart code" often makes your app "stupider". Be wary.
Incidentally, I found that from the command line, you can run WMIC.exe, and type path Win32_OperatingSystem (The "Select * from Win32_OperatingSystem" didn't work for me). In future perhaps JCL could be extended to use the WMI information.
Essentially to answer duplicate Q: Getting OS major, minor, and build versions for Windows 8.1 and up in Delphi 2007
Starting with W2K you can use NetServerGetInfo. NetServerGetInfo returns the correct info on W7 and W8.1, unable to test on 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;
One note about using NetServerGetInfo(), which does work still on Windows 10 (10240.th1_st1)...
https://msdn.microsoft.com/en-us/library/windows/desktop/aa370903%28v=vs.85%29.aspx
sv101_version_major
The major version number and the server type.
The major release version number of the operating system is specified
in the least significant 4 bits. The server type is specified in the
most significant 4 bits. The MAJOR_VERSION_MASK bitmask defined in the
Lmserver.h header {0x0F} should be used by an application to obtain
the major version number from this member.
In other words, (sv101_version_major & MAJOR_VERSION_MASK).

Resources