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

写一个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日星期四

时间: 2024-11-03 22:49:07

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

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

写一个Windows上的守护进程(5)文件系统重定向 在Windows上经常操作文件或注册表的同学可能知道,有“文件系统/注册表重定向”这么一回事.大致来说就是32位程序在64位的Windows上运行时,操作系统会把对System32文件夹的访问重定向到SysWow64下,把对HKEY_LOCAL_MACHINE\SOFTWARE的访问重定向到HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node下.当然不止这些路径和注册表.详情请查看MSDN:https://msdn.

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

写一个Windows上的守护进程(1)开篇 最近由于工作需要,要写一个守护进程,主要就是要在被守护进程挂了的时候再把它启起来.说起来这个功能是比较简单的,但是我前一阵子写了好多现在回头看起来比较糟糕的代码,所以就想这次写的顺眼一点.写完后发现,诶,还可以哟.于是就总结总结. 一.大致需求 1. 功能——当被守护进程挂掉后再次启动它 2. 可配置需要守护的进程 二.通盘考虑 1. 为了避免重复造轮子,况且有的轮子可能自己也造不出来,上boost库 2. 为了能够获得较高的权限和能够开机自动启动,将

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

写一个Windows上的守护进程(4)日志其余 这次把和日志相关的其他东西一并说了. 一.vaformat C++日志接口通常有两种形式:流输入形式,printf形式. 我采用printf形式,因为流输入不好控制格式. printf形式要求日志接口支持不定长参数,我没有直接在日志实现类里边支持不定长参数,而是只接受一个字符串参数,可以参见第一篇. 为什么呢? 如果要成为不定长参数,就是这样 bool log_string(const LOG_LEVEL level, const char* fi

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

写一个Windows上的守护进程(6)Windows服务 守护进程因为要开机启动,还要高权限,所以我就把它做成Windows服务了. 关于Windows服务的官方文档,大家可以看https://msdn.microsoft.com/en-us/library/windows/desktop/ms686953(v=vs.85).aspx. 总的来说,服务的行为区别于普通应用程序的地方有以下几点: 1. 一般来说,服务是运行于System用户下的,当然也可以自己指定.也就是说服务可以在无用户登录的情

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

写一个Windows上的守护进程(3)句柄的管理 在Windows中编程,跟HANDLE打交道是家常便饭.为了防止忘记CloseHandle,我都是使用do-while-false手法: void f() { HANDLE h = NULL; do { } while (false); if (h) { CloseHandle(h); h = NULL; } } HANDLE一多,就得写好几段长得一样的清理代码,比较麻烦.仔细一想,这个其实很容易写一个关闭器——在出作用域时自动关闭: class

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

写一个Windows上的守护进程(7)捕获异常并生成dump 谁都不能保证自己的代码不出bug.一旦出了bug,最好是崩溃掉,这样很快就能被发现,若是不崩溃,只是业务处理错了,就麻烦了,可能很长时间之后才能被发现. 那么如果崩溃掉,怎么查错呢? 写过Windows驱动的同学应该知道,一旦崩溃,系统会生成dump文件,然后就可以根据dump文件.pdb文件.源码用windbg分析了.应用层的程序同样可以在崩溃的时候生成dump文件,只是没人帮你完成这个步骤,得自己动手. 1. API 这里涉及到的

【Linux学习】 写一个简单的Makefile编译源码获取当前系统时间

打算学习一下Linux,这两天先看了一下gcc的简单用法以及makefile的写法,今天是周末,天气闷热超市,早晨突然发现住处的冰箱可以用了,于是先出去吃了点东西,然后去超市买了一坨冰棍,老冰棍居多,5毛钱一根,还有几根1.5的. 嗯 接着说gcc的事 先把源代码贴上来 //gettime.h #ifndef _GET_TIME_H_ #define _GET_TIME_H_ void PrintCurrentTime(); #endif //gettime.c #include <stdio.

根据进程ID获取进程路径

根据进程ID获取进程路径有两种方法:方法1:OpenProcess --> GetModuleFileNameEx方法2:OpenProcess --> EnumProcessModules --> GetModuleFileNameEx 注意事项:1.使用GetModuleFileNameEx()而不是GetModuleFileName()2.GetModuleFileNameEx()指定的hProcess需要PROCESS_QUERY_INFORMATION | PROCESS_VM

根据进程名称获取进程id

# -*- conding:utf-8-*- import subprocess def getpid_windows(process_name):    """利用cmd_str = tasklist|find /i "xdict.exe" 来查找windows平台的进程id"""    cmd_line = 'tasklist|find /i "%s"' %process_name    pp = su