给Source Insight做个外挂系列之六--“TabSiPlus”的其它问题
关于如何做一个Source Insight外挂插件的全过程都已经写完了,这么一点东西拖了一年的时间才写完,足以说明我是一个很懒的人,如果不是很多朋友的关心和督促,恐怕是难以完成了。许多朋友希望顺着本文的思路也作一个类似于TabSiPlus功能的Source Insight外挂插件,抱歉让他们等了这么长时间,看了本文或许能让大家消消气(大头在后面)。其实即使不是为了给Source Insight做外挂插件,本文的很多方法都可以用于给其它软件做外挂。
尽管本文介绍了做TabSiPlus外挂插件的完整过程,但是要做一个有使用价值的外挂插件还有很多细节要注意,首先是稳定,插入到Source Insight进程中的代码一定要考虑周到,仔细地测试所有分支,确保不能频繁地挂死Source Insight;其次,附加的功能一定不能干扰Source Insight的正常工作,比如窗口消息的处理;最后就是在界面上要能和Source Insight融为一体,插件创建的窗口一定不能覆盖Source Insight的窗口。
以上都是空话,现在用具体的例子来说几个细节,比如TabsiPlus提供了直接根据标签关闭文档窗口的功能,由于文档窗口创建的时候已经获取到窗口的句柄,所以TabsiPlus的第一个版本就使用DestroyWindow() API直接关闭了文档窗口,从外表看确实大到了效果,但是却隐藏了一个BUG,那就是虽然窗口被关闭了,但是Source Insight并不知道文档窗口被关闭了,相应的文件依然处于打开状态,如果文档修改过,这样关闭窗口甚至不会提醒用户保存文档。在自己的程序中关闭窗口当然直接DestroyWindow()就行了,但是既然你的代码是“寄人篱下”,就要按照“别人”的规矩来。通过Spy++观察Source Insight窗口的消息,发现Source Insight窗口只对WM_CLOSE消息会有正常的反映,也就是说Source Insight可能在OnClose()中处理了关闭文件和提示保存修改的操作(很奇怪DestroyWindow()后为什么没有触发Source Insight的OnClose()处理被调用,看来远程注入的代码确实有很多需要注意的地方),后来的版本使用SendMessage将一个WM_CLOSE消息发给文档窗口,这样就很好地解决了这个问题。
上面的问题还没完,让窗口消失就算是关闭了吗,有没有考虑窗口的Focus? 如果关闭某个拥有“焦点(Focus)”的子窗口,Windows会激活此焦点窗口的一个兄弟窗口,通常是上一个拥有焦点的窗口,这个相信使用Windows的人都知道,我也是这么认为的,但是,这一点在外挂中失灵了,在TabSiPlus的线程中,关闭当前拥有焦点的文档窗口后,其它的文档窗口标题栏竟然都是灰的,也就是Source Insight的MDIClient窗口没有选择上一个焦点窗口激活,怎么办?看看TabSiPlus中关闭文档窗口的代码:
void CTabBarsWnd::CloseSIWindow(CSiWindow*& pWindow)
{
ASSERT(pWindow);
HWND hPrevActive = NULL;
if(pGlobalActiveSIWindow != NULL && pWindow != pGlobalActiveSIWindow)
{
DebugTracing(gnDbgLevelNormalDebug,_T("CTabBarsWnd::CloseSIWindow() pGlobalActiveSIWindow = %x"),pGlobalActiveSIWindow);
hPrevActive = pGlobalActiveSIWindow->GetSafeHwnd();
m_iLockUpdates++;
}
pWindow->SendMessage(WM_CLOSE, 0, 0);//now close it!
if(hPrevActive != NULL)
{
::PostMessage(::GetParent(hPrevActive), WM_MDIACTIVATE,(WPARAM)hPrevActive, 0);
}
}
核心只有一句:
pWindow->SendMessage(WM_CLOSE, 0, 0);//now close it!
却要围绕它做很多事情。
再来看一个问题,有没有考虑过Tab标签栏上的标签与实际打开的文档窗口个数不一致的情况?虽然我们Hook可MDI_CREATE消息,但是依然有一些窗口创建是TabsiPlus插件无法感知的,比如Source Insight支持内置宏语言,通过宏进行窗口操作TabsiPlus插件无法感知,还有一种情况是Source Insight对于一些不激活的文档通常不是立即创建窗口,而是在激活的时候才创建窗口显示文档,当用户通过Windows菜单看到的已经打开的文件与你的Tab标签栏不一致会怎么想?没有好的办法,TabSiPlus使用一个定时器处理这种不一致,具体代码在CTabBarsWnd::OnTimer()中。
还有一个问题,如何安全地关闭外挂插件?有一种方法先关闭Source Insight,然后关闭加载器TabSiHost.exe,然后再打开Source Insight。让自己接受这种方案都很难,更何况别人,如果能够在插件中提供一个界面,通过用户选择可以直接退出插件就好了,实现这一点关键是Source Insight内部关闭Tab标签窗口后如何中止加载器TabSiHost.exe,如果不中止TabSiHost.exe,TabSiHost.exe会再次加载TabsiPlus.dll插件。TabsiPlus通过内核对象完成与TabSiHost.exe的同步:
void CTabBarsWnd::ShutDownTabSiPlus()
{
HANDLE hAnotherTabSiHostEvent = NULL;
LPCTSTR szGlobalKernelName = _T("Local//TabSiHostIsAlreadyRunning");
hAnotherTabSiHostEvent = CreateEvent( NULL, TRUE, FALSE, szGlobalKernelName );
DWORD dwer = GetLastError();
if(dwer == ERROR_ALREADY_EXISTS)
{
ResetEvent( hAnotherTabSiHostEvent );
}
::CloseHandle(hAnotherTabSiHostEvent);
g_pSiMDIClientWnd->SetManaging(false);
DestroyWindow();
}
还有,TabSiPlus内部窗口之前传递数据都是通过自定义消息进行的,原因就是Tab标签窗口与Source Insight的窗口是工作在不同的线程中的,在线程之间只有句柄是安全的,向窗口句柄发送消息要比直接操纵数据要安全。还有,当使用了Source Insight的查找字符串功能时,Source Insight会打开一个窗口显示搜索的结果,这个窗口的窗口类名和代码窗口一样,都是si_Sw,但是其窗口标题却和代码窗口的标题不一样,这个要区分。其它的细节还有很多,就不一样列举了,具体看代码吧。
罗嗦了半天,代码在哪里?本来想随本文一起上传的,但是这个Blog上传附件太麻烦,只好放到我的CSDN资源里了,大家可以到我的空间下载源代码。本文附带的代码是一份精简的TabSiPlus插件代码,为了大家理解代码,我去掉了全部装饰性的代码和附加功能代码,包括很多预防性代码,这样做地目的就是为了大家在学习源代码时能够将注意力集中在框架上而不是枝节琐事。尽管如此,这是一个完整的可工作的Tab标签栏,演示了本文写的全部内容。除了我的代码之外,源代码中还使用了一些自由代码,使用时请注意相关作者的权利要求。
代码的编译很简单,用VC 6.0打开直接Build就行了,为什么不升级到VC7 or VC8?其实我很懒。调试的时候注意相关的资源文件要在同一个目录中,TaiSiHost.exe的调试比较简单,直接加载就行了,调试TabSiPlus.dll比较麻烦,首先关闭已经打开的Source Insight程序,然后在Project Setting/Debug 窗口中设置“Executable for debug session:”为Source Insight的主程序,通常是Insight3.exe,再然后运行TabSiHost.exe,最后就可以按F5开始调试了。整个过程就是:按下F5后,VC根据调试设置启动insight3.exe,已经运行的TabSiHost.exe发现启动了Source Insight,就会远程代码注入到insight3.exe,于是insight3.exe就会加载TabSiPlus.dll,这样就可以调试了。
Source Insignt文件标签外挂:TabSiPlus的下载地址:
http://www.winmsg.com/download/tabsiplus.zip
给Source Insight做个外挂系列之六--“TabSiPlus”的其它问题的更多相关文章
- 给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架
上一篇文章介绍了“TabSiPlus”是如何进行代码注入的,本篇将介绍如何构建一个外挂软件最重要的部分,也就是为其扩展功能的定制代码.本文前面提到过,由于windows进程管理的限制,扩展代码必须以动 ...
- 给Source Insight做个外挂系列之一--发现Source Insight
一提到外挂程序,大家肯定都不陌生,QQ就有很多个版本的去广告外挂,很多游戏也有用于扩展功能或者作弊的工具,其中很多也是以外挂的形式提供的.外挂和插件的区别在于插件通常依赖于程序的支持,如果程序不支持插 ...
- 给Source Insight做个外挂系列之五--Insight “TabSiPlus”
“TabSiPlus 外挂插件”主要有两部分组成,分别是“外挂插件加载器”和“插件动态库”.“插件动态库”完成Source Insight窗口的Hook,显示Tab标签栏,截获Source Insig ...
- 给Source Insight做个外挂系列之四--分析“Source Insight”
外挂的目的就是将代码注入到其它进程中,所以必须要有目标进程才能完成注入,而所谓的目标进程通常是某软件的一部分或者是全部,所以要对目标程序有深入地了解.一般外挂都是针对某个应用程序开发的,其装载.运行都 ...
- 给Source Insight做个外挂系列之二--将本地代码注入到Source Insight进程
上一篇文章介绍了如何发现正在运行的“Source Insight”窗口,本篇将介绍“TabSiPlus”是如何进行代码注入的.Windows 9x以后的Windows操作系统都对进程空间进行了严格的保 ...
- Source Insight 3.X 标签插件v1.0发布
Source Insight可以说是一款程序员必备的开发/阅读源码工具,美中不足的是SI没有标签栏,多个源码之间切换很不方便,于是我就乘闲暇之余写了该作品sihook:标签插件;不过严格意义上来说si ...
- Source Insight 插件
一提到外挂程序,大家肯定都不陌生,QQ就有很多个版本的去广告外挂,很多游戏也有用于扩展功能或者作弊的工具,其中很多也是以外挂的形式提供的.外挂和插件的区别在于插件通常依赖于程序的支持,如果程序不支持插 ...
- source insight 编程风格(持续更新)
1.字体Source Code Pro 出身于豪门Adobe,从名字上来看就知道是转为编码而生的.基本上也是拥有前面所提的编程字体的所有要素的.这个字体基本上具有编程字体所需的所有要素:等宽.支持Cl ...
- Linux下Source Insight的安装和汉化
原创文章,转载请注明出处. 工欲善其事,必先利其器.Source Insight绝对是阅读C和C++代码的利器,另外,Source Insight的体量很小,安装便捷,显示直观,比vim+cscope ...
随机推荐
- LeetCode之383. Ransom Note
-------------------------------------------- 思路就是进行频率统计. 统计一下第二个字符串字符出现次数++统计一下第一个字符串中字符出现次数--如果出现负数 ...
- ElasticSearch(站内搜索)
简介 Elasticsearch是一个实时的分布式搜索和分析引擎.它可以帮助你用前所未有的速度去处理大规模数据.它可以用于全文搜索,结构化搜索以及分析,当然你也可以将这三者进行组合.Elasticse ...
- oracle 12c中的隐含列
Invisible Columns 使用select * from ,desc 等看不到该列, DROP TABLE tab1 PURGE; CREATE TABLE tab1 ( id NUMB ...
- 中国175个 AAAAA级风景区,去过20个 以上,你就是旅游达人
省份 数量 景区名称 我 北京 7 故宫博物院 1 天坛公园 颐和园 1 八达岭-慕田峪长城旅游区 1 明十三陵景区(神路-定陵-长陵-昭陵) 恭王府景区 北京奥林匹克公园(鸟巢-水立方-中国科技馆- ...
- 在SOUI中非半透明窗口如何实现圆角窗口?
如果SOUI的宿主窗口没有包含子窗口,直接使用窗口的半透明属性:translucent=1就可以解决了,整个窗口形状完全由背景图决定,可以实现完美的圆角. 然后窗口半透明时,窗口中的子窗口(非SWin ...
- UML学习(一)-----用例图
1.什么是用例图 用例图源于Jacobson的OOSE方法,用例图是需求分析的产物,描述了系统的参与者与系统进行交互的功能,是参与者所能观察和使用到的系统功能的模型图.它的主要目的就是帮助开发团队以一 ...
- 11 JSP/EL表达式/EL函数
JSP * 概述: JSP(Java Server Pages)与Java Servlet一样,是在服务器端执行的不同的是先由服务器编译部署成Servlet执行 * JSP的运行原 ...
- 《你不知道的JavaScript -- 上卷》笔记 --- 基于ES6新标准
1.let A:let关键字:将变量绑定到所在的任意作用域 function process(){ //do something } //在这个块中定义的内容完事就可以销毁 { let someRea ...
- XE2 泛型练习1
要引用单元 System.Generics.Collections implementation {$R *.dfm}var i: Integer; str: string; procedure TF ...
- Linux下定时任务配置-crontab
实际中经常有一些任务需要定期执行,人工操作比较麻烦,如果定时执行将会省去很多人力,还可以在一些资源占用不多的时间段执行,linux下crontab命令就实现了这一便捷的功能,实现脚本的自动化运行. 常 ...