65.9K
CodeProject 正在变化。 阅读更多。
Home

ProSysLib:剖析进程

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.84/5 (67投票s)

2008年8月12日

CPOL

12分钟阅读

viewsIcon

136099

downloadIcon

2618

以最简单的方式访问有关当前进程的详细信息。

ProSysLib.gif

目录

前言

本文继续介绍 ProSysLib 项目在其进一步发展中的系列文章。这一次,我们将通过 ProSysLib 组件的 System->Process 命名空间,以简单的方式介绍该库允许我们访问当前进程各种信息的功能。

对于首次接触 ProSysLib 项目的读者,强烈建议从ProSysLib 简介文章中了解该项目及其目标。本文不再赘述 ProSysLib 项目所追求的目标。

ProSysLib 项目的文章在新功能实现时会立即在此处发布,因为这是一个相对较新的项目。本文引用的是库的 0.5 版本,其中大部分更改是为了提供当前进程的信息。

引言

任何客户端应用程序都需要处理有关其自身进程的信息,以调整和控制其自身状态,并了解当前进程的许多方面和状态。在 ProSysLib 中,所有与当前进程相关的信息都可以在 System->Process 命名空间下找到。在本文中,我们将通过库的最新版本介绍大多数与进程相关的属性,以展示如何从 ProSysLib 中轻松访问所有此类信息。

作者抱歉,本文中唯一展示的 COM 客户端应用程序是用 C# 2005 编写的,这确实对 VC++、VB6、Delphi、Java、BC++、Office、FoxPro 等其他环境的开发人员不公平,他们可以使用相同的简单方式使用该库,但他们的数量实在太多了,我面临着选择一种 COM 客户端类型的选择,仅受限于时间,就像我们所有人一样。我正在努力在未来版本的ProSysLib SDK 中包含更多类型的环境示例。当然,我可以使用 VB6 来展示库可以做什么,但这样它就变得不那么流行了,并且在转换为新的 VS 时会遇到问题,而且无法使用 64 位示例,而我每次都非常喜欢指出该项目是 100% 64 位就绪的。所以是的,这些都是艰难的选择,这就是为什么我现在仍然坚持使用 C# 2005,考虑到我拥有的时间。

这里考虑的名为 ProcessInfo 的示例允许查看当前进程的大多数属性,但它不允许更改属性,而 ProSysLib 是允许的,但在示例中实现它会不必要地使其复杂化,而我只想总体上提供一个可用功能的概述。但是,我在下面的对象属性快速列表中提到了读/写功能,但我建议使用ProSysLib SDK 提供的文档。

这个示例本身本质上是一个非常简单的项目,当你在 Visual Studio 中打开它时,你会发现项目中的代码不仅简单,而且非常简单。它所做的就是用 ProSysLib 提供的信息填充列表控件,仅此而已。然而,这就是重点,是为了展示其简单性,以便我之前提到的其他平台的开发人员可以看到如何为他们自己的项目以简单明了的方式使用它。

与往常一样,此处二进制示例的源代码以及其他示例和 ProSysLib 组件本身的源代码都已随ProSysLib SDK 一起安装。

有关项目开发的所有努力都组织在项目网站上。

示例:进程信息

这里我们考虑一个显示当前进程各种信息的非常简单的应用程序。尽管它是用 C# 实现的,但它也可以是任何其他类型的 COM 客户端,如 VB.NET、VB6、C++、Delphi、FoxPro、Office 等,并且它们的实现几乎相同,只是从 ProSysLib 库中读取值并在 UI 中显示它们。请参阅ProSysLib SDK 中的示例源代码。

该应用程序分为八个选项卡,从而将信息逻辑地分组。下面的章节为每个选项卡提供了屏幕截图,并简要说明了显示信息的性质。

一般信息

General

一般的进程信息只是我们无法以其他方式分组的许多进程属性的列表。这里我们有静态属性,即在进程运行时永远不会改变的属性,以及动态属性,这些属性要么自己改变,要么可以被应用程序逻辑改变。

静态属性

  • Application Name
  • 应用程序路径
  • 应用程序目录
  • 进程创建时间
  • 进程 ID
  • 进程是否为 64 位
  • 文件版本
  • 产品版本

所有这些属性都相当自述。我将只评论文件版本和产品版本属性。System->Process 命名空间包含类型为 PSLModuleVersionVersion 属性,该属性可以从当前进程的 Version 资源中提取版本信息。这实际上只是一个可以从 Version 资源中提取信息并以良好形式呈现的接口的第一个实现,因此它被简化了。对于 ProSysLib 的当前版本,我只实现了提取实际版本(文件版本 + 产品版本)之类的信息,因为 Version 资源中的其余信息是可本地化的,而且我暂时不想在那里使事情复杂化。但是,PSLModuleVersion 是一个通用接口,可用于提取任何模块的版本信息。System->Software 命名空间有一个 GetModuleVersion 方法,该方法创建一个与系统中任何模块关联的版本对象。

动态属性

  • 当前目录(读/写),API GetCurrentDirectory/SetCurrentDirectory
  • 亲和力掩码(读/写),API GetProcessAffinityMask/SetProcessAffinityMask
  • 优先级(读/写),API GetPriorityClass/SetPriorityClass
  • 句柄计数(只读),API GetProcessHandleCount/NtQueryInformationProcess
  • GDI 对象计数(只读),API GetGuiResources
  • USER 对象计数(只读),API GetGuiResources
  • 是否被调试(只读),API CheckRemoteDebuggerPresent/NtQueryInformationProcess/IsDebuggerPresent
  • 关机级别(读/写),API GetProcessShutdownParameters/SetProcessShutdownParameters
  • 关机标志(读/写),API GetProcessShutdownParameters/SetProcessShutdownParameters

这里为每个属性提供了基本 API 参考。有关更多详细信息,您可以查看 ProSysLib 组件的源代码,在 CPSLCurrentProcess 类中。

资源:内存 + IO 计数器

Resources

有关当前进程内存使用情况的信息可通过类型为 PSLProcessMemorySystem->Process->Memory 属性获得。大部分信息通过 GetProcessMemoryInfo API 从系统提取,并且属性名称与 MSDN 中的名称相同,以保持一致性。PSLProcessMemory 的所有属性都是只读的。

有关 IO 计数器的信息可通过类型为 PSLProcessIOSystem->Process->IO 属性获得。所有当前属性都通过 GetProcessIoCounters API 从系统提取,并且名称与 MSDN 中描述的名称相同,以保持一致性。PSLProcessIO 的所有属性都是只读的。

环境变量

Environment

环境变量的正确使用对许多应用程序来说非常重要。例如,我们可以使用 BAT 文件配置应用程序,通过 SET 命令指定所有参数,该命令会为进程注册环境变量。而且,如果我们创建一个命令行实用程序,这可能比仅使用命令行参数要好得多,命令行参数可能非常麻烦,有时根本无法使用。有时,父进程需要将重要信息传递给子进程,这就是环境变量发挥作用的地方。根据我在 VC++ 和 VB6 等环境中的经验,没有适当访问环境变量的接口(我在 .NET 中不知道)。

通过类型为 PSLEnvironmentVars 的属性集合 System->Process->EnvVars 可以访问当前进程的环境变量。这个集合允许以最简单的方式进行任何类型的环境变量操作:添加、设置、删除或查找变量。

命令行参数

Commands

命令行参数是许多程序的重要信息,并且访问和处理它们是否容易非常重要。在 C++ 或 .NET 等环境中,我们得到的是一个命令列表,这些命令只经过最基本的解析,将一个命令与另一个命令分开。然而,在其他环境中,它可能比这更复杂。

在提供跨任何环境的统一访问命令行参数的同时,ProSysLib 更进一步,解析每个命令以将其拆分为参数名称 + 参数值。您可以在上面的屏幕截图中看到命令如何被解析。所有命令行参数都可以通过类型为 PSLCmdParamsSystem->Process->Commands 属性获得,它是一个预解析命令的集合。

为了能够将命令解析为 {Name, Value} 对,命令行语法必须满足关于命令行格式的现有不成文规则,这些规则如下……

命令只能以 '-' 或 '/' 符号开头,后跟不带空格的命令名称。如果命令具有参数/值,则使用 ':' 或 '=' 分隔参数名称。命令参数/值要么是单个非空格字符,要么用双引号括起来。此外,语法允许解析没有命令的值,即以任何非 '-' 和 '/' 符号开头的命令被视为简单值。从屏幕截图中可以看到传递给应用程序的一些参数是如何被解析的:我们可以看到原始命令(Command 属性),以及 ProSysLib 能从中提取出的 Name + Value 属性对。

窗口

Windows

类型为 PSLWindowsSystem->Process->Windows 属性提供了对当前进程中创建的所有窗口的访问。集合中的每个元素都属于 PSLWindow 类型,并且是 ProSysLib 组件中窗口的表示。使用 PSLWindow,我们可以直接对窗口执行任何操作,并且当通过所有进程窗口的集合访问时,我们可以自由地对进程中创建的所有窗口执行任何操作。

类型为 PSLWindowsFilterSystem->Process->Windows->Filter 属性允许我们设置一个灵活的过滤器,该过滤器将在枚举所有窗口时使用。默认情况下,过滤器未设置,并允许枚举进程中的所有窗口,但可以像演示应用程序中那样轻松更改它,以满足枚举集合中每个窗口的特定要求。请注意,演示应用程序并未显示所有可用的过滤器(有关详细信息,请参阅 SDK 帮助中的 PSLWindowsFilter 接口)。

ProSysLib 还允许将系统中的任何窗口作为自己的窗口进行处理。System->Software->WindowFromHandle 方法允许创建与任何窗口句柄关联的新窗口对象。如果我们需要在不允许直接与窗口交互的环境中覆盖窗口行为,这会非常有用。在当前实现中,PSLWindow 已经实现了所有必要的基本方法来控制窗口,例如 SendMsgPostMsg,以及窗口 ID、父窗口、类名、线程 ID、进程 ID、样式控制等的隐藏属性。

线程

Threads

类型为 PSLThreadsSystem->Process->Threads 属性提供了对当前进程中创建的所有线程的访问。集合中的每个元素都是 PSLThread 类型,它提供了关于线程的所有必要信息,还可以通过简单地设置相应属性值来更改线程优先级、亲和力或提升值。

通过直接访问线程可以实现更好的应用程序负载平衡和同步。例如,一个规划良好的应用程序将利用进程亲和力掩码(System->Process->AffinityMask 属性),并将所有线程定位在特定的核心子集上运行,以最好地满足进程的需求,从而优化资源消耗。

集合类型 PSLThread 实现了所有基本方法,用于监视和控制进程中的任何线程,例如 PostMsgWaitSuspendResumeTerminate,这些方法分别对线程对象执行 PostThreadMessageWaitForSingleObjectSuspendThreadResumeThreadTerminateThread 的相应 API 函数。有关接口方法和属性的更多详细信息,请参阅ProSysLib SDK 帮助中的描述。

模块

Modules

类型为 PSLModulesSystem->Process->Modules 属性是当前进程加载的所有模块的集合。这允许监视应用程序正在加载的内容,以便在规划软件分发时(如有必要)确切知道。此集合的所有属性均为只读。

权限

Privileges

尽管位于 System->Security 命名空间下,Privileges 属性提供了与当前进程设置相关的信息,因此我也将其包含在演示中,因为权限本身首先总是进程的权限。该属性是一个权限集合,允许您轻松获取或设置当前进程中关于权限的信息。您可以在ProSysLib 简介文章中找到更多信息以及使用权限的代码示例。

背景

ProSysLib 项目本身的所有后台工作都在 SDK 提供的 C++ 源代码中。至于本文,在实现组件的 0.5 版本并实现当前进程的所有属性时,首先需要处理各种枚举 API,其中包括以下关键函数:

  • EnumWindowsEnumChildWindowsEnumThreadWindows 用于实现窗口集合(类 CPSLWindows);
  • CreateToolhelp32SnapshotThread32FirstThread32Next 用于枚举进程中的所有线程(类 CPSLThreads);
  • EnumProcessModules 用于枚举当前进程使用的模块(类 CPSLModules);
  • GetCommandLine,以及大量的我自己的解析,用于正确组织命令行参数的集合(类 CPSLCmdParams);
  • GetEnvironmentStringsSetEnvironmentVariable,以及我自己的解析,用于组织环境变量的集合(类 CPSLEnvironmentVars);

您可以在 ProSysLib 源代码中亲自查看所有内容,有关详细信息超出了本文的主题。

总结

本文中的演示应用程序并未显示 ProSysLib 库提供的所有当前进程信息。有关更多详细信息,我建议使用随ProSysLib SDK 安装的帮助系统,并在www.prosyslib.org 上在线获取。不能声称本文和 ProSysLib 的当前版本中的考虑是完整的。在当前进程中总能找到额外的信息。而且,虽然 ProSysLib 的未来版本可能包含其中一些高级进程属性,但该库专注于对开发人员普遍最感兴趣的内容,而不是提供对很少使用的信息的访问。

历史

  • 2008年8月12日;文章初稿
  • 2008年8月13日;第二次修订,在全文中添加了更多细节,并增加了背景章节
  • 2008年8月30日;更新了指向原始 ProSysLib 文章的链接,该文章自上次以来已发布并移至不同类别
  • 2008年9月6日;只是通知我的读者,该库发展非常迅速,今天我发布了 ProSysLib v0.6,可以从www.prosyslib.org 下载
  • 2010年10月12日:更新了源代码
  • 2010年11月20日:更新了下载文件
© . All rights reserved.