Как сделать условную компиляцию для CSIDL или KNOWFOLDERID в Delphi XE4?

Я использую Delphi XE4 на компьютере с Windows 7. Я хотел бы иметь одну кодовую базу, которая может распознать, использовать ли CSIDL или KNOWFOLDERID.

Есть ли способ использовать {$IFDEF XXXXXX) для условной компиляции различных файлов в разделе "Использования" и для вызова различных функций на основе Windows XP или ниже?

2 ответа

Решение

Скорее всего, вы не хотите использовать условную компиляцию для этого. Это заставляет вас отправлять разные исполняемые файлы для разных версий операционной системы. Это создает большую сложность установки.

Обычный подход к решению сценария заключается в использовании ветвления во время выполнения, а не в условной компиляции. Таким образом, вы написали бы код так:

if IsWindowsVistaOrGreater then
  DoKnownFolderIDVersion
else
  DoCSIDLVersion;

Одна вещь, которая имеет решающее значение, состоит в том, что вы должны держать эти операторы if на как можно более низком уровне. Вы должны скрыть эти детали от кода высокого уровня. С точки зрения кода высокого уровня, он должен запрашивать местоположение конкретной папки. Код высокого уровня не должен содержать ветвлений в зависимости от версии.

Сложность состоит в том, что вы не можете использовать привязку времени загрузки для ветви, которая использует функции API, которые могут отсутствовать на старых поддерживаемых платформах. Таким образом, вместо привязки по времени загрузки, используйте привязку по времени выполнения. Есть много разных способов добиться этого, но для системных API, с современными версиями Delphi, задержка загрузки является отличным вариантом.


Лично я не против использования API на основе CSIDL даже сейчас, потому что лично я не вижу, чтобы MS удаляла эту функциональность в ближайшее время. Но решение о том, использовать CSIDL или нет, очевидно, принадлежит вам. Я, конечно, могу понять желание не использовать эти API больше. Понятно, что Microsoft не хочет, чтобы вы это делали.


Если вы хотите проверить поддержку версии Windows, то вы должны использовать вспомогательные API новой версии. Теперь я знаю, что они были включены в модули Windows, которые поставляются с Delphi. Возможно, они в самой последней версии, но у вас может быть версия, в которой их нет. В этом случае вы можете использовать это:

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;
{$IFDEF VISTA_UP}
// use modern APIs
{$ELSE}
// fallback
{$ENDIF}

Есть VISTA_UP условный символ определяется где-то, когда ваша цель Vista+, например:

  • в разделе "Каталоги / условия" в разделе "Параметры проекта"
  • с помощью -Dxxx переключатель компилятора командной строки
  • или с $DEFINE VISTA_UP в отдельном $INCLUDE-d файл со всеми другими вашими настройками во время компиляции

Помните, что вы не можете сохранить свои DCU своих библиотечных единиц, вам придется восстанавливать каждый раз, когда вы меняете цель.

Другие вопросы по тегам