写一个Windows上的守护进程(8)获取进程路径

要想守护某个进程,就先得知道这个进程在不在。我们假设要守护的进程只会存在一个实例(这也是绝大部分情形)。

我是遍历系统上的所有进程,然后判断他们的路径和要守护的进程是否一致,以此来确定进程是否存在。

遍历进程大家都知道用CreateToolhelp32Snapshot系列API,但是他们最后取得的是进程exe名称,不是全路径,如果仅依靠名称就可以达到目的也就罢了,但是有的时候还是得取到全路径,这样会更靠谱一些。

那么问题来了,如何取到进程全路径?

首先登场的是GetModuleFileNameEx。大多数时候,我们都可以用这个函数来取得进程的全路径,而且这个函数是全平台都有,但是你有没有注意到这样一段话:

(来自VS2008的MSDN文档)

这个函数有缺陷。除了这个问题之外,还有一个问题:如果你是32进程要获取某个64位进程的全路径,有可能也会出错,具体请看我很早前写过的一片帖子http://blog.csdn.net/mkdym/article/details/8688597

当然微软也说了,你还可以用GetProcessImageFileName和QueryFullProcessImageName两个函数呀。

那么接下来说GetProcessImageFileName。这函数确实不存在GetModuleFileNameEx的两个问题,但是它不支持Windows2000,当然如果你不需要支持Windows2000这种老古董,你完全不用理会这个缺憾。然而,它还有别的问题:

1. 这家伙返回的是个内核名称,形如\Device\Harddisk0\Partition1\WINNT\System32\Ctype.nls。内核名称咱不怕,咱用QueryDosDevice反向获取:先用QueryDosDevice获取到所有磁盘的驱动器名称和内核名称的对应关系,然后把刚获取到的内核名称的前面部分换掉就可以了。你到网上一搜,也全是这种办法。

以为这就结束了吗?请看https://msdn.microsoft.com/en-us/library/windows/desktop/ms683217(v=vs.85).aspx 下面关于动态磁盘的问题,这个问题是我提的:在动态磁盘的情况下,驱动器的链接会有两层,这个你可以从Winobj中看到,而QueryDosDevice只能获取到第一层,GetProcessImageFileName返回的是第二层的!我勒个去!网上一番搜索之后,许多人给出的答案都是在结果上反复调用QueryDosDevice,我只能对这些人呵呵,也许他们的电脑比较特殊,QueryDosDevice可以那样使用。

关于动态磁盘的问题,你还可以看我在上面给出的那个csdn的帖子,那是我发现这个问题的过程。

怎么办?后来忘了在那个论坛里边看到的,用内核函数NtQuerySymbolicLinkObject可以做到QueryDosDevice一样的事情,经过试验,这个函数是可以在结果上重复调用的,能够获取到最底层的内核名称。嗯,这个问题就这样解决了。

当然,一般的机子不会有动态磁盘。

2. 当你看MSDN页面上我说的“动态磁盘”的问题的时候,应该会注意到,下面有个哥们说,这个函数会返回短路径名,所以咱也得注意了。把每个返回的路径都转换成长路径名,不管它是不是短路径名(我也不知道它是不是短路径名啊)。虽然我测试的时候它返回的都是长路径名。

这里还有一点,咱给进程们做了快照了,快照的那一刻驱动器们是确定的,所以完全不需要获取每个进程路径的时候都去获取所有驱动器的名称对应关系,只需要在遍历开始的时候,准备一下这些名称,然后遍历的时候直接拿来用。

嗯,用这个API就是这么麻烦。

最后咱们说QueryFullProcessImageName。这个函数啥都好,就是平台要求太高,得是Vista及以上啊,所以只能是能用则用。

最终我获取进程路径的方案是这样的:如果有QueryFullProcessImageName函数,就用这个函数,没有的话先上GetModuleFileNameEx,如果GetModuleFileNameEx出错了再上GetProcessImageFileName。把最麻烦的放到最后。

类名CProcessPathQuery。

大家看代码的时候可能会注意到,我在处理的时候用了个for循环,这是因为我也不知道进程路径有多长,如果返回错误码说缓冲区不够,就加长重试,但是也不能无限重试,有个限度。

进程路径获取出来之后,因为有可能是GetProcessImageFileName获取出来的,那就是一个内核名称,需要转换,转换类是CDosPathConverter。

进程遍历类是CProcessScanner,可以看到我在里面放了一个CDosPathConverter的实例,随着遍历类的初始化一起初始化。

遍历进程的时候需要注意,有几类特殊的进程我们是获取不到路径的,或者获取他们的路径是没有意义的,可以看CProcessPathQuery的注释。

有一点需要注意,一定要提权,否则就会有好多进程因为我们自己权限不够而query失败。

源码:https://git.oschina.net/mkdym/DaemonSvc.git (主)&& https://github.com/mkdym/DaemonSvc.git (提升逼格用的)。

2015年11月19日星期四

写一个Windows上的守护进程(8)获取进程路径的更多相关文章

  1. 写一个Windows上的守护进程(7)捕获异常并生成dump

    写一个Windows上的守护进程(7)捕获异常并生成dump 谁都不能保证自己的代码不出bug.一旦出了bug,最好是崩溃掉,这样很快就能被发现,若是不崩溃,只是业务处理错了,就麻烦了,可能很长时间之 ...

  2. 写一个Windows上的守护进程(6)Windows服务

    写一个Windows上的守护进程(6)Windows服务 守护进程因为要开机启动,还要高权限,所以我就把它做成Windows服务了. 关于Windows服务的官方文档,大家可以看https://msd ...

  3. 写一个Windows上的守护进程(5)文件系统重定向

    写一个Windows上的守护进程(5)文件系统重定向 在Windows上经常操作文件或注册表的同学可能知道,有"文件系统/注册表重定向"这么一回事.大致来说就是32位程序在64位的 ...

  4. 写一个Windows上的守护进程(4)日志其余

    写一个Windows上的守护进程(4)日志其余 这次把和日志相关的其他东西一并说了. 一.vaformat C++日志接口通常有两种形式:流输入形式,printf形式. 我采用printf形式,因为流 ...

  5. 写一个Windows上的守护进程(3)句柄的管理

    写一个Windows上的守护进程(3)句柄的管理 在Windows中编程,跟HANDLE打交道是家常便饭.为了防止忘记CloseHandle,我都是使用do-while-false手法: void f ...

  6. 写一个Windows上的守护进程(2)单例

    写一个Windows上的守护进程(2)单例 上一篇的日志类的实现里有个这: class Singleton<CLoggerImpl> 看名字便知其意--单例.这是一个单例模板类. 一个进程 ...

  7. 写一个Windows上的守护进程(1)开篇

    写一个Windows上的守护进程(1)开篇 最近由于工作需要,要写一个守护进程,主要就是要在被守护进程挂了的时候再把它启起来.说起来这个功能是比较简单的,但是我前一阵子写了好多现在回头看起来比较糟糕的 ...

  8. C#依据进程名称获取进程的句柄?

    C#依据进程名称获取进程的句柄或C#怎样获取其它进程的句柄? 有时候标题名是动态变化的,所以不使用FindWindow方法! [StructLayout(LayoutKind.Sequential)] ...

  9. C#根据进程名称获取进程的句柄?

    C#根据进程名称获取进程的句柄或C#如何获取其他进程的句柄? 有时候标题名是动态变化的,所以不使用FindWindow方法! [StructLayout(LayoutKind.Sequential)] ...

随机推荐

  1. Js 时间间隔计算(间隔天数)

    function GetDateDiff(startDate,endDate)  {      var startTime = new Date(Date.parse(startDate.replac ...

  2. hdu 1262寻找素数对

    Problem Description 哥德巴赫猜想大家都知道一点吧.我们现在不是想证明这个结论,而是想在程序语言内部能够表示的数集中,任意取出一个偶数,来寻找两个素数,使得其和等于该偶数. 做好了这 ...

  3. 小安,今天学会了MySQL中查询时间的方法哦

  4. 使apache解析域名到目录的方法

    apache如何将一个域名自动定位到目录 有两种解决办法 首先,你的拥有一个有泛域名解析的顶级域名,例如: domain.com其次,在 httpd.conf 中打开 mod_rewrite之后,在 ...

  5. 【好程序员笔记分享】——iOS开发之纯代码键盘退出

    -iOS培训,iOS学习-------型技术博客.期待与您交流!------------ iOS开发之纯代码键盘退出(非常简单)     iOS开发之纯代码键盘退出 前面说到了好几次关于键盘退出的,但 ...

  6. nodeclub 学习记录

    源码地址:https://github.com/cnodejs/nodeclub 按照 它的步骤 在系统中跑没有出错,但是注册后没有发送邮件验证码,我将 controller层下面的sign.js 的 ...

  7. Effective Java提升Code Coverage代码涵盖率 - 就是爱Java

    虽然我们已经有了测试程序,但是如何得知是否已完整测试了主程序?,透过Code Coverage代码涵盖率,我们可以快速地得知,目前系统中,有多少程序中被测试过,不考虑成本跟投资效益比,涵盖率越高,代表 ...

  8. 一道有关球赛队员分配的C++程序题目

    题目描述: 两个球队进行比赛,各出三人.甲队为a,b,c三人,乙队为x,y,z三人.已经抽签决定比赛名单. 有人向队员打听比赛安排的名单.a说他不和x比,c说他不和x,z比,请编程找出三队赛手的名单. ...

  9. flexbox自动完成

    1.引入文件 <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js" ty ...

  10. PHP 字符串替换 substr_replace 与 str_replace 函数

    PHP 字符串替换 用于从字符串中替换指定字符串. 相关函数如下: substr_replace():把字符串的一部分替换为另一个字符串 str_replace():使用一个字符串替换字符串中的另一些 ...