有没有办法在 Delphi 中找到未使用的事件处理程序?
在 Delphi 中查找死代码通常非常简单:只需编译然后扫描缺少蓝点的例程即可。 大多数时候,智能链接器非常擅长追踪它们。
问题是,这对于事件处理程序不起作用,因为它们是发布的方法,(理论上)可以通过 RTTI 以某种方式调用,尽管这在实际实践中几乎从未发生过。
我正在尝试清理一个大型 VCL 模板单元,该单元在其历史上多次被弯曲、折叠、旋转和毁坏。 如果我有某种方法可以找到表单的 DFM 实际未引用的事件处理程序并删除它们,那就太好了。 有什么简单的方法可以做到这一点吗? 例如,插件 IDE Expert?
Finding dead code in Delphi is usually real simple: just compile and then scan for routines missing their blue dots. The smart linker's very good about tracking them down, most of the time.
Problem is, this doesn't work for event handlers because they're published methods, which (theoretically) could be invoked via RTTI somehow, even though this almost never happens in actual practice.
I'm trying to clean up a large VCL form unit that's been bent, folded, spindled and mutilated various times throughout its history. It would sure be nice if I had some way to find event handlers that aren't actually referenced by the form's DFM and delete them. Is there any easy way to do this? A plug-in IDE Expert, for example?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
这有点丑陋(好吧,它非常丑陋),但对于一个单元来说它几乎是万无一失的,并且不需要额外的工具:
This is a bit ugly (OK, it's a lot ugly), but for one unit it's close to foolproof, and requires no additional tools:
使用“重命名方法”重构来重命名每个事件处理程序。 选中“重构前查看引用”复选框。
检查重构窗口。 如果事件处理程序链接到控件,则会有一个“VCL 设计器更新”部分显示哪些控件链接到该方法。
这还将显示该方法是否从任何其他单元调用,或者以编程方式分配。
注:此为D2006版本,后续版本可能略有不同。
Use the "Rename Method" refactoring to rename each event handler. Check the "View references before refactoring" checkbox.
Check the Refactoring window. If the event handler is linked to a control, there will be a "VCL Designer Updates" section show which control(s) are linked to the method.
This will also show if the method is called from any other units, or is assigned programmatically.
Note: this is for D2006, may be slightly different in later versions.
ModelMaker Code Explorer 包含一个所谓的 事件处理程序视图。 它还显示未连接到任何组件的事件处理程序。
ModelMaker Code Explorer contains an so-called Event handler view. It also shows event handlers not connected to any component.
我认为从自动的角度来看这是不可能的。 当对象内部发生特定事件时,事件处理程序被激活。 在给定的运行中未触发该事件并不意味着没有执行路径可以实现该事件。
您还可以在运行时动态分配处理程序,因此在一种情况下使用的处理程序是无法保证的。
例如
button.onclick := DefaultClickHandler;
按钮.onClick := SpecialClickHandler;
假设单击处理程序与 onclick 事件签名匹配,但如果签名不正确,您将无法进行编译。
但是,您可能可以通过查找具有 (Sender: TObject) 方法签名的所有方法并将其方法与 .dfm 中的方法进行比较来找到所有废弃的处理程序(如果您是,请确保将其保存为文本)使用旧版本的delphi),在我的书中,没有自动连接的任何东西都会受到怀疑。
--
如果您不想走 cygwin 路径,您可以将 src 和 dfm 加载到两个 TStirngList 中,并从每个 TStirngList 中删除名称/标识,并生成一个包含几个循环和一些字符串操作的列表。 我的猜测是大约 20 分钟的工作就能得到你可以忍受的东西。
I dont think this is possible from an automatic point of view. event handlers are activated when a particular event occurs inside an object. That the even is not triggered in a given run doesnt mean that there isnt an execution pathway to lead to it.
also you can assign handlers dynamically at runtime so whats used in one situation is not garuanteed.
e.g.
button.onclick := DefaultClickHandler;
button.onClick := SpecialClickHandler;
Assuming that the click handlers match the onclick event signature, but you wouldnt get a compile if the signature was incorrect.
however, you can probably find all the abandoned handlers by looking for all the methods that find have a (Sender: TObject) method signature and comparing that his of methods to those in the .dfm (make sure you save it as text if you are working with an older version of delphi), antyhing not wired up automatically would be suspect in my book.
--
if you dont want to go down the cygwin path, you can load the src and dfm into two TStirngLists and rip out the name/idents from each and generate a list with a couple of loops and some string manipulations. my guess is about 20 minutes of work to get something you can live with .
在最一般的情况下,没有任何解决方案可以保证给出正确的答案(正如您所注意到的,基于通过 RTTI 调用它们的可能性)。
一种解决方案是进行代码覆盖率测试并仔细查看从未到达的处理程序。
There is no solution that is guaranteed to give a correct answer in the most general case (based, as you note, on the possibility of calling them via RTTI).
One solution would be to do code coverage tests and look carefully at handlers that were never reached.
我不知道现有的应用程序或插件可以执行此操作,但编写脚本应该不难。
假设您没有使用 RTTI 或手动分配事件处理程序:(我是 C++Builder 用户而不是 Delphi,因此以下内容可能不太正确。)
*.pas
。 查找以class
声明或published
指令开头并以end
、private
结尾的每个文本块,或公共
。 在每个文本块中,提取每个过程
。我最喜欢使用 Cygwin 或 Linux 工具来编写脚本。 这是一个在 Cygwin 中运行的 bash 脚本,应该可以完成您想要的操作。
要实际使用它,如果您不熟悉 Cygwin:
cleanup.sh
。I'm not aware of a preexisting app or plugin to do this, but it shouldn't be hard to script.
Assuming you're not using RTTI or manually assigning event handlers: (I'm a C++Builder user rather than Delphi, so the following may not be quite correct.)
*.pas
. Find each text block that starts with aclass
declaration or apublished
directive and ends with aend
,private
, orpublic
. Within each of these text blocks, extract eachprocedure
.I'm most comfortable using Cygwin or Linux tools for scripting. Here's a bash script that works in Cygwin and should do what you want.
To actually use this, if you're not familiar with Cygwin:
cleanup.sh
.cd /cygdrive/c/myapp
./cleanup.sh
and press Enter.有一种比克雷格的方法更简单的方法。
转到可疑的事件处理程序。 以一致的方式重命名它——我通过在名称前面放置一个 x 来实现这一点,然后继续执行并执行相同的操作。 看看编译器是怎么想的。
如果不高兴你就把名字改回来。
您可以使用相同的方法来消除不再执行任何操作的数据元素。
There's a much easier approach than Craig's.
Go to a suspect event handler. Rename it in a consistent way--I do this by putting an x in front of the name, go down to the implementation and do the same thing. See what the compiler thinks of it.
If it's not happy you just change the names back.
You can use the same approach to eliminate data elements that no longer do anything.