Reputation: 5184
I'm running Delphi XE4 on a Windows 7 machine. I'd like to have one code base that can recognize whether to use CSIDL or KNOWFOLDERID.
Is there a way to use {$IFDEF XXXXXX) to conditionally compile different files in the uses section and to call different functions based on Windows XP or lower?
Upvotes: 2
Views: 929
Reputation: 613013
You very likely don't want to use conditional compilation for this. Doing so forces you to ship different executables for different versions of the operating system. That creates heavy installation complexity.
The usual approach to coping with the scenario is to using runtime branching rather than conditional compilation. So you would write code like this:
if IsWindowsVistaOrGreater then
DoKnownFolderIDVersion
else
DoCSIDLVersion;
One thing that is crucial is that you must keep these if statements at as low a level as possible. You must hide these details from high level code. From the perspective of the high level code, it must ask for the location of a particular folder. The high level code must not contain any branching based on version.
The complication is that you cannot use load time linking for the branch that uses API functions that may not be present on the older supported platforms. So, instead of load time linking, use run time linking. There are lots of different ways to achieve that, but for system APIs, with modern versions of Delphi, delay loading is an excellent option.
Personally, I'm not averse to using CSIDL based APIs even now because I personally don't see MS removing the functionality any time soon. But the decision on whether or not to use CSIDL is obviously yours. I can certainly understand a desire not to use these APIs any more. It's clear that Microsoft don't want you to do so.
If you want to check for Windows version support then you are expected to use the new version helper APIs. I'm now aware that they have been included in the Windows units that ship with Delphi. Perhaps they are in the very latest but you may well have a version that does not have them. In which case you can use these:
function IsWindowsVersionOrGreater(wMajorVersion, wMinorVersion, wServicePackMajor: Word): Boolean;
function IsWindowsXPOrGreater: Boolean;
function IsWindowsXPSP1OrGreater: Boolean;
function IsWindowsXPSP2OrGreater: Boolean;
function IsWindowsXPSP3OrGreater: Boolean;
function IsWindowsVistaOrGreater: Boolean;
function IsWindowsVistaSP1OrGreater: Boolean;
function IsWindowsVistaSP2OrGreater: Boolean;
function IsWindows7OrGreater: Boolean;
function IsWindows7SP1OrGreater: Boolean;
function IsWindows8OrGreater: Boolean;
function IsWindows8Point1OrGreater: Boolean;
....
const
VER_EQUAL = 1;
VER_GREATER = 2;
VER_GREATER_EQUAL = 3;
VER_LESS = 4;
VER_LESS_EQUAL = 5;
VER_AND = 6;
VER_OR = 7;
_WIN32_WINNT_WINXP = $0501;
_WIN32_WINNT_VISTA = $0600;
_WIN32_WINNT_WIN7 = $0601;
_WIN32_WINNT_WIN8 = $0602;
_WIN32_WINNT_WINBLUE = $0603;
function VerSetConditionMask(dwlConditionMask: ULONGLONG; dwTypeBitMask: DWORD; dwConditionMask: Byte): ULONGLONG; stdcall; external kernel32;
function VerifyVersionInfo(var lpVersionInfo: TOSVersionInfoEx; dwTypeMask: DWORD; dwlConditionMask: DWORDLONG): BOOL; stdcall; external kernel32 name 'VerifyVersionInfoW';
function IsWindowsVersionOrGreater(wMajorVersion, wMinorVersion, wServicePackMajor: Word): Boolean;
var
osvi: TOSVersionInfoEx;
dwlConditionMask: DWORDLONG;
begin
osvi := Default(TOSVersionInfoEx);
osvi.dwOSVersionInfoSize := SizeOf(osvi);
osvi.dwMajorVersion := wMajorVersion;
osvi.dwMinorVersion := wMinorVersion;
osvi.wServicePackMajor := wServicePackMajor;
dwlConditionMask := VerSetConditionMask(0, VER_MAJORVERSION, VER_GREATER_EQUAL);
dwlConditionMask := VerSetConditionMask(dwlConditionMask, VER_MINORVERSION, VER_GREATER_EQUAL);
dwlConditionMask := VerSetConditionMask(dwlConditionMask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL);
Result := VerifyVersionInfo(osvi, VER_MAJORVERSION or VER_MINORVERSION or VER_SERVICEPACKMAJOR, dwlConditionMask);
end;
function IsWindowsXPOrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WINXP), LoByte(_WIN32_WINNT_WINXP), 0);
end;
function IsWindowsXPSP1OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WINXP), LoByte(_WIN32_WINNT_WINXP), 1);
end;
function IsWindowsXPSP2OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WINXP), LoByte(_WIN32_WINNT_WINXP), 2);
end;
function IsWindowsXPSP3OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WINXP), LoByte(_WIN32_WINNT_WINXP), 3);
end;
function IsWindowsVistaOrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_VISTA), LoByte(_WIN32_WINNT_VISTA), 0);
end;
function IsWindowsVistaSP1OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_VISTA), LoByte(_WIN32_WINNT_VISTA), 1);
end;
function IsWindowsVistaSP2OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_VISTA), LoByte(_WIN32_WINNT_VISTA), 2);
end;
function IsWindows7OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WIN7), LoByte(_WIN32_WINNT_WIN7), 0);
end;
function IsWindows7SP1OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WIN7), LoByte(_WIN32_WINNT_WIN7), 1);
end;
function IsWindows8OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WIN8), LoByte(_WIN32_WINNT_WIN8), 0);
end;
function IsWindows8Point1OrGreater: Boolean;
begin
Result := IsWindowsVersionOrGreater(HiByte(_WIN32_WINNT_WINBLUE), LoByte(_WIN32_WINNT_WINBLUE), 0);
end;
function IsWindowsServer: Boolean;
var
osvi: TOSVersionInfoEx;
dwlConditionMask: DWORDLONG;
begin
osvi := Default(TOSVersionInfoEx);
osvi.dwOSVersionInfoSize := SizeOf(osvi);
osvi.wProductType := VER_NT_WORKSTATION;
dwlConditionMask := VerSetConditionMask(0, VER_PRODUCT_TYPE, VER_EQUAL);
Result := not VerifyVersionInfo(osvi, VER_PRODUCT_TYPE, dwlConditionMask);
end;
Upvotes: 4
Reputation: 4402
{$IFDEF VISTA_UP}
// use modern APIs
{$ELSE}
// fallback
{$ENDIF}
Have VISTA_UP
conditional symbol defined somewhere when your target is Vista+, like:
-Dxxx
command-line compiler switch$DEFINE VISTA_UP
in the separate $INCLUDE
-d file with all your other compile-time configuration stuffRemember, you cannot keep your DCUs of your library unit(s), you will have to rebuild each time you switch target.
Upvotes: 0