2

我在 Windows 7 机器上运行 Delphi XE4。我想要一个可以识别是使用 CSIDL 还是 KNOWFOLDERID 的代码库。

有没有办法使用 {$IFDEF XXXXXX) 有条件地编译uses部分中的不同文件并调用基于Windows XP或更低版本的不同函数?

4

2 回答 2

4

您很可能不想为此使用条件编译。这样做会迫使您为不同版本的操作系统提供不同的可执行文件。这造成了沉重的安装复杂性。

处理这种情况的常用方法是使用运行时分支而不是条件编译。所以你会写这样的代码:

if IsWindowsVistaOrGreater then
  DoKnownFolderIDVersion
else
  DoCSIDLVersion;

至关重要的一件事是,您必须将这些 if 语句保持在尽可能低的水平。您必须对高级代码隐藏这些细节。从高级代码的角度来看,它必须询问特定文件夹的位置。高级代码不得包含任何基于版本的分支。

复杂之处在于,您不能对使用旧支持平台上可能不存在的 API 函数的分支使用加载时间链接。因此,不要使用加载时链接,而是使用运行时链接。有很多不同的方法可以实现这一点,但是对于系统 API,使用现代版本的 Delphi,延迟加载是一个很好的选择。


就个人而言,即使是现在,我也不反对使用基于 CSIDL 的 API,因为我个人认为 MS 不会很快删除该功能。但是,是否使用 CSIDL 显然是你的决定。我当然可以理解不再使用这些 API 的愿望。很明显,Microsoft 不希望您这样做。


如果您想检查 Windows 版本支持,那么您应该使用新版本的帮助程序 API。我现在知道它们已包含在 Delphi 附带的 Windows 单元中。也许它们是最新的,但您可能有一个没有它们的版本。在这种情况下,您可以使用这些:

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;
于 2014-07-06T16:37:17.283 回答
0
{$IFDEF VISTA_UP}
// use modern APIs
{$ELSE}
// fallback
{$ENDIF}

当您VISTA_UP的目标是 Vista+ 时,在某处定义条件符号,例如:

  • 在“项目选项”中的“目录/条件”下
  • 使用-Dxxx命令行编译器开关
  • $DEFINE VISTA_UP在单独的$INCLUDE-d 文件中与所有其他编译时配置内容一起使用

请记住,您不能保留库单元的 DCU,每次切换目标时都必须重建。

于 2014-07-06T16:20:39.947 回答