How to do Conditional Compilation for CSIDL or KNOWFOLDERID in Delphi XE4?

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

Answers (2)

David Heffernan
David Heffernan

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

Free Consulting
Free Consulting

Reputation: 4402

{$IFDEF VISTA_UP}
// use modern APIs
{$ELSE}
// fallback
{$ENDIF}

Have VISTA_UP conditional symbol defined somewhere when your target is Vista+, like:

  • under "Directories/conditionals" in the "Project Options"
  • using -Dxxx command-line compiler switch
  • or with $DEFINE VISTA_UP in the separate $INCLUDE-d file with all your other compile-time configuration stuff

Remember, you cannot keep your DCUs of your library unit(s), you will have to rebuild each time you switch target.

Upvotes: 0

Related Questions