有一个很另类的需求,要修改另一个程序的文本编辑控件的颜色。“另一个程序”是正常的Win32程序,没有源代码。应该有几种不同的办法来实现,这里用的是非常底层的办法,那就是直接向另一个进程的文本编辑控件对应的HWND SendMessage。这里看一下是如何做的。
找到目标进程id
使用CreateToolhelp32Snapshot函数,直接搜这个函数可以找到例子。官方例子是“Taking a Snapshot and Viewing Processes”
https://docs.microsoft.com/en-us/windows/win32/toolhelp/taking-a-snapshot-and-viewing-processes
略作修改,遍历所有的进程,比较pe32.szExeFile与目标程序的exe名即可找到。下面进程id简称为pid。
找到控件HWND
有了pid,就可以找到该进程下的所有HWND句柄。使用EnumWindows,可以遍历系统的所有HWND句柄,使用GetWindowThreadProcessId可以获取HWND对应的进程的id,比较一下是否和上步获取的pid一致,一致的话就记录下来这个HWND。
要找到这个目标程序的文本编辑控件的类名,可以用inspect.exe找,在Windows Kits里。
这里找到是RichEdit20A
然后对所有的HWND调用GetClassName,与ClassName比较即可找到编辑控件的HWND。
(不知道为啥,我发现又用了EnumChildWindows又遍历了一遍。如果想控制层级估计才需要用EnumChildWindows。)
改背景色
有了文本编辑控件(RichEdit20A)的HWND,改背景色就很简单了。Win32的SendMessage的目标是HWND,整个系统上任何一个HWND都可以,也就是说这是IPC(Inter Process Communication)的一种。
直接SendMessage即可:
void
改字体颜色
改背景色的时候,颜色直接整个放到了PARAM里,很简单。而改字体颜色的话,正常情况需要传指针,本进程很简单,跨进程就复杂了。
需要先在目标进程地址空间分配内存,填满具体参数后,再SendMessage把刚才分配的内存的指针地址传过去。不能在本进程分配内存,因为目标进程和本进程是两个地址空间,否则不但不能用,还会直接crash掉目标进程。这里就用VirtualAllocEx实现“偷偷摸摸”为另一个进程分配内存。
首先,要获取目标进程的pid,然后OpenProcess,得到process handle。
随后就可以在目标进程地址空间分配内存了,使用VirtualAllocEx。
然后,需要把实际的值写入到目标进程的地址空间。使用WriteProcessMemory写入到目标进程。
然后,调用SendMessage,把这个指针的值放在PARAM里,因为目标进程来说,这是个合法的指针,这样才可以正常改变字体颜色。如果把本进程的指针传过去,会把目标进程crash掉,因为对目标进程来说这个地址很可能是非法的。
最后,使用VirtualFreeEx释放掉刚才为目标进程分配的内存,这里的size应该传0,文档里有写。
void
代码
完整代码在https://github.com/xzfn/ywcolor
里面还有份Python的实现,用了ctypes和pywin32。刚开始是用Python写的,相当于Prototype。现在感觉,还不如用C++直接写来的快,因为还要看pywin32的源代码才能明白各个函数的参数是怎么封装的。Python的实现用了ctypes来操作内存,也许有点参考价值。