[译]InterSpy-集成的Windows消息跟踪和筛选器实用程序
By robot-v1.0
本文链接 https://www.kyfws.com/applications/interspy-an-integrated-windows-message-trace-and-f-zh/
版权声明 本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
- 12 分钟阅读 - 5760 个词 阅读量 0[译]InterSpy-集成的Windows消息跟踪和筛选器实用程序
原文地址:https://www.codeproject.com/Articles/3923/InterSpy-An-integrated-Windows-message-trace-and-f
原文作者:.dan.g.
译文由本站 robot-v1.0 翻译
前言
A utility providing enhanced Windows message debugging.
提供增强的Windows消息调试功能的实用程序.
- 使用演示项目下载源文件-45 Kb(Download source files with demo project - 45 Kb)
- 下载演示可执行文件-185 Kb(Download demo executables - 185 Kb)
版本1.1(Version 1.1)
介绍(Introduction)
去年的大部分时间里,我都在开发和使用外观系统,之前我已将其中两个元素发布到CodeProject(请参阅(I’ve spent most of the last year developing and playing with a skinning system, two elements of which I have previously posted to CodeProject (see*) ‘A Revolutionary New Approach to Custom Drawn Menus’*)“革命性的自定义绘制菜单新方法”( 和(and*) ‘A template class for easy implementation of Windows hooks’*)“用于轻松实现Windows挂钩的模板类”( ).().)
要在窗口蒙皮中取得良好的结果,就需要确切地了解将哪些Windows消息发送给谁以及何时发送.(Achieving good results in window skinning requires an exacting knowledge of exactly what Windows messages are sent to whom and when.)
在过去,像你们中的许多人一样,我会接触到Spy ++和/或生成大量TRACE语句,然后凝视着似乎要花几个小时才能理解输出的内容.(In the past, like many of you, I would reach for Spy++ and/or generate reams of TRACE statements and then stare for what seemed like hours trying to make sense of the output.)
不过,直到最近,许多具体问题才真正开始困扰我:(Just recently though, a number of specific issues had really begun to bug me:)
- 间谍++不允许您在Windows上进行监视(Spy++ doesn’t allow you to spy on windows until)*后(after)*它们已经创建,这意味着您通常会错过窗口生命周期中一个非常有趣的时期.(they have been created which means you generally miss a very interesting period in the window’s life cycle.)
- 出于同样的原因,很难监视菜单,工具提示,组合框下拉列表等瞬态窗口(For the same reason, its very hard to spy on transient windows like menus, tooltips, combobox droplists, etc)
- 间谍++并不容易(或根本没有?)让您同时监视一堆窗户.(Spy++ doesn’t easily (or at all?) allow you to spy on a whole bunch of windows all at the same time.)
- 使用TRACE语句通常会导致输出,该输出的消息类型表示为数字而不是通常的形式(using TRACE statements generally leads to output which has the message type expressed as a number rather than its usual)
WM_xxx
标识符.(identifier.) - Windows消息很难使用TRACE语句进行过滤,通常导致太多或太少.(Windows messages are hard to filter using TRACE statements, resulting usually in either too many or too few.) 由此诞生了InterSpy,这是一个集成的Windows消息跟踪实用程序,它可以解决我之前概述的所有问题,并为我提供了数小时的乐趣.(Out of this was born InterSpy, an integrated Windows message tracing utility, which solves all the problems I previously outlined and provided me with hours of enjoyment.)
重要的提示(Important Note)
此实用程序需要您在应用程序中构建一些源代码,因此不能应用于第三方应用程序(除非您也有源代码).(This utility requires you to build some source code into your application and cannot therefore be applied to a 3rd party application (unless you also have the source code).)
挑战(The challenge)
因为此解决方案需要包含源代码,所以最大的挑战是创建简单易用的东西.可以用最少的麻烦,但给出最大的结果.(Because this solution requires the inclusion of source code, the greatest challenge was to create something simple and accessible; that could be used with the minimum of fuss but giving the maximum result.)
我很高兴同意我可能没有达到这个目标,但是我发现它始终是一个有用的目标.(I happy to agree that I probably haven’t reached that goal, but I find its always a useful place to aim for.)
寻求以下积极属性:(The following positive attributes were sought:)
- 紧凑的形式(Compact form)
- 清晰划定的信息(Clearly delineated information)
- 易于导航(Easily navigable)
- 可过滤(Filterable)
解决方案(The solution)
要解决的第一个"问题"是如何处理流经应用程序的所有Windows消息.(The first ‘problem’ to solve was how to get a handle on all the Windows messages flowing through an application.)
我发现的最简单的解决方案是使用应用程序范围的Windows挂钩.(The simplest solution to this I’ve found is to use application-wide Windows hooks.)
**注意:(Note:)**为了那些不关心Windows钩子的人的利益,我确实花了一些时间调查可能使用的Windows钩子.(In the interest of those who do not care for Windows hooks I did spend sometime investigating the possible use of) PreTranslateMessage
查看应用程序的消息,但这是一个惨痛的失败; MFC的实现似乎遗漏了比显示更多的消息,并且肯定在窗口生命的开始和结束时都忽略了有趣的消息. (如果有人有(to see an application’s messages but it was a dismal failure; MFC’s implementation seems to leave out more messages than it shows and it certainly leaves out the interesting ones at the start and end of a window’s life. (if anyone has anymore)*具体(specific)*关于什么的知识(knowledge about exactly what) PreTranslateMessage()
会和不会表明我有兴趣了解更多信息.)(does and does not show I would be interested to learn more.))
为了实现这一点,我用了(To implement this I used) 厨师(CHookMgr) 这消除了难看的钩子细节,仅需要覆盖一些虚拟函数来处理消息钩子.(which removes the ugly hooking details and simply requires the overriding of a few virtual functions to handle the message hooks.)
据我所知,捕获所有Windows消息所需的Windows挂钩是(As far as I can tell, the required Windows hooks to trap all Windows messages are) WH_CALLWNDPROC
和(and) WH_GETMESSAGE
(转化为通过((which translate to passing) HM_CALLWNDPROC | HM_GETMESSAGE
至(to) CHookMgr
).().)
下一步是决定如何处理消息.(The next step was to decide what to do with the messages.)
来自工程背景,我总是喜欢在做太多细节之前先弄清楚项目的较大元素.这样,如果我偶然遇到了一个在木棚里令人讨厌的事情(这个玩笑需要对英语戏剧化的戏剧"冷舒适的农场"有所了解),我将不会浪费太多的辛苦工作.(Coming from an engineering background, I always favor figuring out the larger elements of a project before doing too much detail. That way, if I stumble up against something nasty in the woodshed (joke requiring some knowledge of an English dramatized play called ‘cold comfort farm’) I will not have wasted too much hard work.)
已经很明显,消息需要发送到单独的窗口,但是"位置,内容和方式"尚未确定.(It was already obvious that the messages would need to be sent to a separate window but the ‘where, what and how’ had not been decided.)
我的第一个原型以与被跟踪的应用程序相同的过程创建了一个窗口,但是最大的问题是,在应用程序终止时该窗口被破坏了,这是不可接受的"功能".(My first prototype created a window in the same process as the application that was being traced, but the biggest problem was that the window was destroyed when the application terminated, which was an unacceptable ‘feature’.)
我还对输出到Visual Studio工具窗口的可能性感兴趣,但是我不了解为Visual Studio编写插件的第一件事,而是将阻尼器放在这个想法上.(I was also interested in the possibility of outputting to a Visual Studio tool window, but not knowing the first thing about writing plug-ins for Visual Studio, rather put the dampeners on that idea.)
因此,我剩下的工作是使用单独注册的类名创建一个单独的应用程序,以便内置在客户端应用程序中的代码可以轻松找到它.(So I was left with creating a separate application with a specially registered class name so that the code built into the client application could find it easily.)
要解决的唯一其他架构问题是两者之间的通信方式,尽管严格来说,通信只是单向的.我用过(The only other architectural issue to resolve was the means of communicating between two, although strictly the communication is only one-way. I’d used) WM_COPYDATA
之前有很多次,所以坚持下去,因为我对此很熟悉.(a number of times before and so stuck with that because I was familiar with it.)
实施细节(Implementation details)
客户端(应用程序中内置的代码)(Client-side (code built into the application))
具有某种客户端-服务器体系结构的一个复杂之处在于,双方没有共享相同的地址空间.(One complication of having a sort-of client-server architecture is that the two sides do not share the same address space.)
这简单地意味着,消息处理将必须在客户端而不是在日志窗口中进行,就像我最初想要的那样(请记住:我的第一个原型在与应用程序相同的过程中创建了日志窗口以利用该优势.共享地址空间).(What this means simply is that the message processing would have to be done on the client side and not in the logging window, as I had initially wanted (remember: my first prototype created the logging window in the same process as the application to take advantage of the shared address space).)
这很麻烦,因为我一直想将要编译到客户端应用程序中的源代码保持为绝对必要的最低限度.哦,老鼠和男人的计划以及其他东西…(This was a nuisance because I had wanted to keep the source code to be compiled into the client application to be the absolute minimum necessary. Oh well, plans of mice and men and stuff …)
所有Windows消息实质上都采用相同的形式:MSG结构的形式.但是,此后,每个人在(All Windows messages essentially take the same form: that of the MSG structure. However, after that, every one is different in how the) wParam
和(and) lParam
使用值.(values are used.)
一些消息没有参数,一些消息仅使用一个参数,而某些消息使用两个参数.此外,尽管最常用的参数是提供简单的(Some messages have no parameters, some use only one parameter and some use both parameters. Further, whilst most often the parameters are used to supply simple) UINT
值,有些使用(values, some use the) lParam
(通常)指向一个存储结构,它提供的详细信息要比用其他简单的参数简化的方式多.((typically) to point to a memory structure supplying more detail than could otherwise be shoe-horned into the simple parameters.)
因此,我首先决定实现一个简单的基类消息处理程序,(I therefore first decided to implement a simple base-class message handler,) CISMsgHandler
,这只会将每条消息破解成其组成参数.(, which would do no more than crack each message into its constituent parameters.)
然后,从MFC中获取一个指针,我编写了各种消息映射(使用宏实现),以便将该类的实例分配给我可能找到的所有Windows消息(然后包括某些消息).其中包括:所有标准消息,MFC中的特定消息(Then, taking a pointer from MFC, I wrote a message map of sorts (implemented using macros) to assign an instance of this class to all the possible Windows messages I could find (and then some). These include: all the standard messages, the MFC specific messages in)*afxpriv.h(afxpriv.h)*在搜索新闻组和进行调试时遇到了很多问题.(and a bunch I have come across during my searches of the news groups and my own debugging.)
注意:我确信某些消息已过时,但是由于在这种情况下根本不会触发这些消息,因此将它们留在里面几乎没有什么害处.(Note: I am sure that some of the messages are obsolete but, since these will simply not be triggered if that is the case, I see little harm in leaving them in.)
此默认消息处理程序的明显和预期的好处是,它使我可以使消息映射正常工作,以便随后可以构建"服务器端"日志记录窗口来接收消息.(The obvious and intended benefit of this default message handler is that it allowed me to get the message map working so that I could then build the ‘server-side’ logging window to receive the messages.)
此后,为所有不同的消息类型实现特定的处理程序是普通的老工作.幸运的是,许多消息例如(Thereafter, it was plain old slog work to implement specific handlers for all the different message types. Fortunately, many messages such as) WM_LBUTTONDOWN
与其他鼠标按钮消息共享通用参数,以便单个处理程序可用于6或7条消息.在其他地方,它是一对一的,在这种情况下,您会看到我还没有完全完成任务.(share common parameters with other mouse button messages so that a single handler could be used for 6 or 7 messages. Elsewhere, its one for one, and in this case you’ll see that I have not quite finished the task.)
在破解消息时,您可能还注意到,我并没有一直走得很远.例如其中的参数描述了窗口句柄((In cracking the messages, you may also note that I have not always gone as far as I might have. e.g. where a parameter describes a window handle () HWND
)我还可以检索窗口的类和文本以提供更多信息.我目前没有这样做的原因仅仅是效率和性能问题.() I could have also retrieved the window’s class and text to provide more information. The reason I have not done this at present is simply an issue of efficiency and performance.)
服务器端(消息记录窗口)(Server-side (Message logging window))
没有什么可以说的,不能从上面的屏幕截图中收集到.(There’s not much more to say that cannot be gleaned from the screenshot above.)
主要输出视图是(The main output view is a) CTreeCtrl
之所以选择,是因为数据本质上是分层的,并且树控件允许如此轻松地隐藏和显示数据.(, chosen simply because the data is hierarchical in nature and the tree control allows data to be hidden and displayed with such ease.)
我也喜欢在树上自定义绘制的特定实现,以及您可以仅通过设置标志就可以增强项目的事实.(I also like the specific implementation of custom-draw on trees and the fact that you can embolden items just by setting a flag.)
日志记录窗口当前提供以下"功能"以简化消息的输出和导航:(The logging window currently provides the following ‘features’ to simplify the output and navigation of messages:)
- 可以在所有窗口中禁用特定消息的功能(禁用的消息显示为浅灰色).注意:禁用后,该消息将不再显示,直到重新启动日志记录窗口或您重新启用该消息.(Facility to disable a specific message across all windows (disabled messages appear light-grey). Note: once disabled, the message will no longer appear until the logging window is restarted or you re-enable the message.)
- 清除树中所有禁用消息的功能.(Facility to clear all disabled messages from the tree.)
- 禁用特定窗口的所有消息的功能(禁用的窗口及其消息显示为深灰色).(Facility to disable all messages for a specific window (disabled windows and their messages appear dark-grey).)
- 在接收到消息的严格顺序中,单步向前和向后浏览消息列表(因此,[(Single stepping forwards and backwards through the message list in the strict order in which the messages were received (indicated thus [)
nn
]在输出窗口中).(] in the output window).) - 将消息列表保存为纯文本或XML的功能.(Facility to save message lists as plain text or XML.)
使用代码(Using the code)
-
将以下源文件添加到您的项目:(Add the following source files to your project:)注意:在我的演示项目中,这些文件位于单独的" skinwindows"文件夹中,因为它们构成了更大的蒙皮系统的子集,但您无需这样做.(Note: in my demo project, these files are in a separate ‘skinwindows’ folder because they form a subset of a much larger skinning system, but there is no need for you to do the same.)
CInterSpy
((()interspy.h/.cpp(interspy.h/.cpp))-派生的挂钩管理器,它拦截所有消息,破解它们,并将详细信息转发到日志记录窗口.() - the derived hook manager which intercepts all messages, cracks them, and forwards the details to the logging window.)CHookMgr
((()hookmgr.h/.cpp(hookmgr.h/.cpp))-挂钩管理器基类.() - the hook manager base class.)ISMsgManager/ISMsgHandler
((()ismsgmanager.h/.cpp,ismsghandlers.h(ismsgmanager.h/.cpp, ismsghandlers.h))-消息处理程序类和管理器,可将每个MSG转换为参数字符串数组.注意:这些文件可以放入需要消息跟踪输出的任何项目中.() - message handler classes and manager which converts each MSG into an array of parameter strings. Note: these files can be dropped into any project where you need message trace output.)
-
初始化您的Interspy(Initialize Interspy in your)
CWinApp
派生应用(derived application)InitInstance()
方法如下:(method as follows:)可以传递给的标志(The flags which may be passed to)CInterspy::Initialize()
如下面所述:(are as follows:)IS_NOKICKIDLE
-不包括(- excludes)WM_KICKIDLE
.(.)IS_NODUPLICATES
-剔除相同类型的顺序消息.(- culls out sequential messages of the same type.)IS_RESETONSTART
-在应用程序(重新)启动时清除消息日志窗口.(- clears the message log window when the app (re)starts.)IS_AUTOSTARTOUTPUT
-如果日志窗口未运行,则自动启动.(- automatically starts the log window if it is not running.)
进一步的工作(Further work)
- 提供内置的和用户可定义的过滤器来处理某些窗口类型.例如菜单过滤器将仅显示菜单窗口以及发送到其他窗口的菜单相关消息.(Provide built-in and user-definable filters to handle certain window types. e.g. a filter for menus would display only menu windows together with menu related messages sent to other windows.)
- 提供更多有关(Provide more detail on)
HWND
参数,可能具有跳转到代表所引用窗口的树项的能力.(parameters, perhaps with the ability to jump to the tree item representing the window being referenced.)
版权(Copyright)
此处提供了该代码,供您不受限制地使用和滥用,除了您不能修改它并以自己的身份将其传递出去.但是,概念和设计仍然是我的知识产权.(The code is supplied here for you to use and abuse without restriction, except that you may not modify it and pass it off as your own. The concept and design, however, remains my intellectual property.)
历史(History)
- 1.0-初始版本(1.0 - Initial Release)
- 1.1-添加了40个左右的其他消息处理程序(仅过时的和深奥的消息未完成)(1.1 - 40 or so further message handlers added (only obsolete and esoteric messages outstanding))
许可
本文以及所有相关的源代码和文件均已获得The Code Project Open License (CPOL)的许可。
C++ VC6 WinXP Windows Win2K MFC Visual-Studio Dev 新闻 翻译