【问题标题】:What may cause EnumProcesses() to fail?什么可能导致 EnumProcesses() 失败?
【发布时间】:2018-03-16 01:50:38
【问题描述】:

documentation 声明:

如果函数失败,返回值为零。获得扩展错误 信息,调用GetLastError。

但它没有给出任何函数如何可能失败的示例。

对于单元测试,我需要可靠地创建使EnumProcesses() 失败的情况。

【问题讨论】:

  • 对于单元测试,您可能应该有一种方法来删除获取进程的方法。
  • 对于托管代码,曾经有 Moles/Pex(现在的 Microsoft Fakes),用于提供系统服务的替代品,以展示可重现的行为。不过,我不知道有任何此类原生代码框架,但我会花时间研究这些框架的可用性。
  • 每个 winapi 函数都可能失败。没有例外规范。它几乎从来都不是什么好东西,对于 EnumProcesses 肯定不会,并且只需要测试应用程序是否成功终止并出现异常。
  • @HansPassant 当然可以,但问题是如何创建使EnumProcesses 失败的条件?
  • 你将不得不对操作系统做一些难以形容的事情。一定要关注如何它报告失败,你所需要的只是一个false返回值。 #ifdef 可以做的事情。

标签: c++ unit-testing winapi


【解决方案1】:

像大多数函数一样,如果您传递无效参数,它可能会失败。在这种情况下,这意味着 PID 数组小于您告诉它的大小或接收计数的 NULL 指针。故意这样做有点冒险,因为您不知道该函数是否使用 SEH 来防止这种情况发生,或者它是否会崩溃。

在调用 NTDLL 以获取进程信息之前,函数必须在内部分配一些内存,如果没有足够的可用内存,这可能会导致函数失败。

您应该在辅助函数中调用 EnumProcesses 以抽象出内存/重试详细信息,这将是在需要时模拟故障的好地方。

如果您绝对需要函数本身失败,您可以使用 Microsoft Detours 或 IAT hooking 之类的东西来挂钩它......

【讨论】:

  • 我希望这可以通过简单地删除进程权限或类似的东西来完成,这会改变EnumProcesses 的环境条件以使其失败。 NULL 接收计数会导致函数失败,但正如你所写,它也可能崩溃(如果不是现在,那么可能在未来的 Windows 版本中)。与发布代码相比,“模拟”失败需要不同的测试代码路径(@HansPassant 建议的一些#ifdef),所以我想避免这种情况。
  • @zett42 - 通过指针变量调用的所有导入函数。如果K32EnumProcesses__imp_K32EnumProcesses,则不是x86,__imp__K32EnumProcesses@12 是x86。所以我们需要的 - 将此变量声明为extern "C" { extern PVOID __imp_K32EnumProcesses; } 并修改它。对于 x86,我们不能直接(在 C++ 代码中)声明名称 PVOID __imp__K32EnumProcesses@12 但我们可以使用 #pragma comment(linker, "/alternatename:___imp_K32EnumProcesses=__imp__K32EnumProcesses@12") 。完整代码example
  • @zett42 - 因此,当您使用 DECLSPEC_IMPORT 链接器声明一些 api 时,为此 api 创建命名变量,您可以按名称访问它。说如果你声明extern "C" extern BOOL (*__imp_K32EnumProcesses)(DWORD*,DWORD,DWORD*); 调用__imp_K32EnumProcesses(*);K32EnumProcesses(*); 产生绝对相同的二进制代码。一对一。事实上,这与我们声明函数变量并通过 GetProcAddress 初始化它并使用此变量调用 api 时相同。仅在您调用 GetProcAddress 的情况下导入函数链接器
  • @zett42 - 这不依赖于编译器。您可以注意到__imp_K32EnumProcesses__imp__K32EnumProcesses@12kernel32.lib 中的名称完全匹配。当我们调用一些标记为 dllimportfunction 时 - 编译器会生成名称为 __imp_function 的变量(或 x86 的 __imp__function@N)。并且链接器在 obj/lib 文件中准确搜索此名称。如果未找到 - 将是众所周知的错误 - 未解析的外部符号 __imp_function。所以名称不依赖于编译器,但必须与 sdk/wdk 中的 lib 中的名称完全匹配(符号到符号)
  • 我们也可以使用__pragma而不是#pragma这让__pragma甚至#define SOMENAME __pragma SOMENAME(comment(linker, "/alternatename:***"))中使用宏
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-05-11
  • 1970-01-01
  • 1970-01-01
  • 2010-12-01
  • 1970-01-01
  • 2012-05-12
相关资源
最近更新 更多