Inno Setup detect Windows Firewall state - windows

I need to detect the Windows Firewall state (i.e. whether it is enabled or not) in order to display a message warning that a Firewall rule may need to be configured to allow inbound connections on specific ports when the Firewall is enabled, but not when it isn't. See below code example:
[Code]
//Check if Windows Firewall is enabled
function IsWindowsFirewallEnabled(): Boolean;
begin
//Method required here
Result := True;
end;
function NextButtonClick(CurPageID: Integer): Boolean;
begin
//Display a warning message on a Server install if Windows Firewall is enabled
if CurPageID = wpSelectComponents and IsComponentSelected('Server') and IsWindowsFirewallEnabled then
begin
MsgBox('Windows Firewall is currently enabled.' + #13#10 + #13#10 +
'You may need to enable inbound connections on ports 2638, 445 and 7.'
mbInformation, MB_OK);
Result := True;
end;
end;
What I need is a method for the IsWindowsFirewallEnabled function. One way I have read about, and ironically has now more or less been suggested below whilst I was in the middle of updating the question with this information anyway, would appear to be reading the EnableFirewall value from the Registry:
//Check if Windows Firewall is enabled
function IsWindowsFirewallEnabled(): Boolean;
var
crdFirewallState: Cardinal;
begin
RegQueryDwordValue(HKLM, 'SYSTEM\CurrentControlSet\Services\SharedAccess\Parameters\FirewallPolicy\StandardProfile',
'EnableFirewall', crdFirewallState);
if crdFirewallState = 1 then
Result := True;
end;
However, I am not convinced by this method as the Registry values for all the profiles show enabled on my work PC, but looking in Control Panel the Domain profile shows disabled (I assume this is related to a Group Policy).
Note, that this needs to work for both Windows XP and Server 2003, and for Windows Vista and Server 2008 and above.
Therefore, what's the most reliable or recommended way to do this?

You would need to determine the registry entry and then query it in a manner similar to this using Innosetup's registry query ability.
var
Country: String;
begin
if RegQueryStringValue(HKEY_CURRENT_USER, 'Control Panel\International',
'sCountry', Country) then
begin
// Successfully read the value
MsgBox('Your country: ' + Country, mbInformation, MB_OK);
end;
end;
http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_regquerystringvalue
Allegedly this is the information for the registry key:
Path: HKEY_LOCAL_MACHINE\SOFTWARE\Policies\Microsoft\WindowsFirewall\DomainProfile
Location: Local Machine
Value Name: EnableFirewall
Data Type: DWORD (DWORD Value)
Enabled Value: 0
Disabled Value: 1

Related

Mail not being received after adding a ReplyRecipient

Using Outlook for Microsoft Office 365 32 bit and Delphi XE8
I am evaluation Redemption.dll as Windows 11 has blocked my application from accessing Outlook.
I have Outlook configure with multiple profiles and stores
I can open the profiles find the store/account and can send/receive.
In one combination I do not receive the email.
The issue is with
Mail.ReplyRecipients.Add('xxx#gmail.com');
if I comment this line out I receive the email
In the sent folder I can dblclick and by clicking reply I see the Reply Address but in the inbox (of the recipient) I get nothing. I scanned all Outlook items to no avail.
In the reverse direction I do receive the email.
I intend ti purchase the product on completion of my evaluation. But this feature is important
procedure TForm61._Send(Profile, Sender, _To, _CC, _BCC, Subject, Body: String;
ReadReceiptRequested: Boolean; _Attachments: TArray<String>);
var
FolderFound: Boolean;
c, j, Count, k: Integer;
Session: IRDOSession;
Store: IRDOStore;
Drafts: IRDOFolder;
Recip: IRDORecipient;
MAil: IRDOMail;
IPMRoot: IRDOFolder;
ReplyRecip: IRDORecipient;
_name: String;
begin
Session:=CoRDOSession.Create;
Session.Logon(Profile,'', False, True,EmptyParam,EmptyParam);
Drafts:=Session.GetFolderFromPath('\\'+Sender+'\Inbox');
Mail:= Drafts.Items.Add(olMailItem);
Recip := Mail.Recipients.Add(_To);
Recip.type_:= olTo;
if not _CC.IsEmpty then
begin
Recip := Mail.Recipients.Add(_CC);
Recip.type_:= olCC;
end;
if not _BCC.IsEmpty then
begin
Recip := Mail.Recipients.Add(_BCC);
Recip.type_:= olBCC;
end;
// Recip.Resolve(EmptyParam, EmptyParam);
// ReplyRecip:=Mail.ReplyRecipients.Add('xxx#gmail.com');
Mail.ReplyRecipients.Add('xxx#gmail.com');
// ReplyRecip.Resolve(EmptyParam, EmptyParam);
for j:=0 to Length(_Attachments)- 1 do
Mail.Attachments.Add(_Attachments[j], EmptyParam, EmptyParam, EmptyParam);
Mail.Subject:=Subject;
Mail.ReadReceiptRequested:=ReadReceiptRequested;
Mail.HTMLBody:=Body;
if SignatureIndex>-1 then
begin
Signature:=Session.Signatures.Item(SignatureIndex+1); //noy zero-based
Signature.ApplyTo(Mail, False);
end;
Mail.OriginatorDeliveryReportRequested:=True;
Mail.Save;
Mail.Send;
end;
TIA
Ephraim

Inno Setup - How to validate serial number online

Using Inno Setup, setup.exe was given to a client, according to contract he is allowed only to use 2016 and 2017. But on 01-01-2018 he should not be able to continue with same serial 2017.
How to make the setup.exe by innosetup limited to from and to date?
[Setup]
#define SerialNumber "2017"
UserInfoPage=yes
[Code]
function CheckSerial(Serial: String): Boolean;
begin
Result := Serial = '{#SerialNumber}';
end;
setup.exe is executed
license key is inserted
after submit, i want to check URL https://www.example.com/query/license?id=2017
if the result is ok or nok based on that the installation continue
Starting with a code from: Inno Setup - HTTP request - Get www/web content, you will get something like:
[Setup]
UserInfoPage=yes
[Code]
// Presence of the CheckSerial event function displays the serial number box.
// But here we accept any non-empty serial.
// We will validate it only in the NextButtonClick,
// as the online validation can take long.
function CheckSerial(Serial: String): Boolean;
begin
Result := (Serial <> '');
end;
function NextButtonClick(CurPageID: Integer): Boolean;
var
WinHttpReq: Variant;
Url: string;
begin
Result := True;
if CurPageID = wpUserInfo then
begin
WinHttpReq := CreateOleObject('WinHttp.WinHttpRequest.5.1');
Url := 'https://www.example.com/serial.php?serial=' +
WizardForm.UserInfoSerialEdit.Text;
WinHttpReq.Open('GET', Url, False);
WinHttpReq.Send('');
// Depending on implementation of the server,
// use either HTTP status code (.Status)
// or contents of returned "page" (.ResponseText)
// Here we use the HTTP status code:
// 200 = serial is valid, anything else = serial is invalid,
// and when invalid, we display .ResponseText
Result := (WinHttpReq.Status = 200);
if not Result then
MsgBox(WinHttpReq.ResponseText, mbError, MB_OK);
end;
end;
A simple server-side validation PHP script (serial.php) would be like:
<?
if (empty($_REQUEST["serial"]) || ($_REQUEST["serial"] != "2017"))
{
header("HTTP/1.0 401 The serial number is not valid");
// error message to be displayed in installer
echo "The serial number is not valid";
}
For consideration:
This validation is not difficult to bypass, i.e. using a proxy server.
It also does not prevent the user from extracting the files from the installer and installing them manually.
You may consider instead an on-line download of the actual files only after validating the serial number.
Or downloading some license file that the application will require for functioning. You need that anyway, if you want to enforce the application to stop working once the license expires.
Or you can also encrypt the installer and make the online service return the decryption password:
Read Inno Setup encryption key from Internet instead of password box
For a similar question, see also
How to store serial numbers in a Sharepoint List, for to call from Inno Setup and verify if is autorized user?

Problems reading registry from Delphi 7 on Windows 7 64 bit

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.

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).

How do you create your own moniker (URL Protocol) on Windows systems?

How do you create your own custom moniker (or URL Protocol) on Windows systems?
Examples:
http:
mailto:
service:
Take a look at Creating and Using URL Monikers , About Asynchronous Pluggable Protocols and Registering an Application to a URL Protocol from MSDN
Here's some old Delphi code we used as a way to get shortcuts in a web application to start a windows program locally for the user.
procedure InstallIntoRegistry;
var
Reg: TRegistry;
begin
Reg := TRegistry.Create;
try
Reg.RootKey := HKEY_CLASSES_ROOT;
if Reg.OpenKey('moniker', True) then
begin
Reg.WriteString('', 'URL:Name of moniker');
Reg.WriteString('URL Protocol', '');
Reg.WriteString('Source Filter', '{E436EBB6-524F-11CE-9F53-0020AF0BA770}');
Reg.WriteInteger('EditFlags', 2);
if Reg.OpenKey('shell\open\command', True) then
begin
Reg.WriteString('', '"' + ParamStr(0) + '" "%1"');
end;
end else begin
MessageBox(0, 'You do not have the necessary access rights to complete this installation!' + Chr(13) +
'Please make sure you are logged in with a user account with administrative rights!', 'Access denied', 0);
Exit;
end;
finally
FreeAndNil(Reg);
end;
MessageBox(0, 'Application WebStart has been installed successfully!', 'Installed', 0);
end;
Inside OLE from Craig Brockschmidt probably has the best coverage on monikers. If you want to dig a little deeper into this topic, I'd recommend getting this book. It is also contained on the MSDN disk that came along with VS 6.0, in case you still have that.

Resources