关于如何做一个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”的其它问题相关推荐

  1. 给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架

    上一篇文章介绍了"TabSiPlus"是如何进行代码注入的,本篇将介绍如何构建一个外挂软件最重要的部分,也就是为其扩展功能的定制代码.本文前面提到过,由于windows进程管理的限 ...

  2. 给Source Insight做个外挂系列之一--发现Source Insight

    作者:星轨(oRbIt)   E_Mail :inte2000@163.com 一提到外挂程序,大家肯定都不陌生,QQ就有很多个版本的去广告外挂,很多游戏也有用于扩展功能或者作弊的工具,其中很多也是以 ...

  3. 给Source Insight做个外挂系列之五--Insight “TabSiPlus”

    "TabSiPlus 外挂插件"主要有两部分组成,分别是"外挂插件加载器"和"插件动态库"."插件动态库"完成Sourc ...

  4. 给Source Insight做个外挂系列之四--分析“Source Insight”

    外挂的目的就是将代码注入到其它进程中,所以必须要有目标进程才能完成注入,而所谓的目标进程通常是某软件的一部分或者是全部,所以要对目标程序有深入地了解.一般外挂都是针对某个应用程序开发的,其装载.运行都 ...

  5. 给Source Insight做个外挂系列之二--将本地代码注入到Source Insight进程

    作者:星轨(oRbIt)  E_Mail :inte2000@163.com 上一篇文章介绍了如何发现正在运行的"Source Insight"窗口,本篇将介绍"TabS ...

  6. Source Insight 3.X 标签插件v1.0发布

    Source Insight可以说是一款程序员必备的开发/阅读源码工具,美中不足的是SI没有标签栏,多个源码之间切换很不方便,于是我就乘闲暇之余写了该作品sihook:标签插件;不过严格意义上来说si ...

  7. 「视频直播技术详解」系列之六:现代播放器原理

    ​关于直播的技术文章不少,成体系的不多.我们将用七篇文章,更系统化地介绍当下大热的视频直播各环节的关键技术,帮助视频直播创业者们更全面.深入地了解视频直播技术,更好地技术选型. 本系列文章大纲如下: ...

  8. Source Insight完美转换UTF-8 到 GB2312

    Source Insight完美转换UTF-8 到 GB2312 文/蒹葭 前言 很多人用source insight 打开某些源码文件时,汉字显示为一堆乱码.这个问题是因为编码方式不同.记事本和一些 ...

  9. Source Insight 经典教程

    Source Insight实质上是一个支持多种开发语言(java,c ,c 等等)的编辑器,只不过由于其查找.定位.彩色显示等功能的强大,常被我们当成源代码阅读工具使用. 作为一个开放源代码的操作系 ...

最新文章

  1. 发表的原创文章还希望大家多提宝贵意见!
  2. sql group by having用法_神奇的 SQL,Group By 真扎心,原来是这样!
  3. (转)网上常用免费WebServices集合
  4. mysql求表中年龄同张三,mysql子查询与连接查询
  5. 一款不错的编程字体Source Code Pro
  6. AWS SQS和Spring JMS集成
  7. MS SQL入门基础:系统数据库
  8. 【Java】数据结构——栈(图文)
  9. 请珍爱自己的身体搞开发的兄弟们
  10. SAP License:ERP面试记
  11. ubuntu下pkgconfig的搜索路径
  12. java char 比较,为什么我在Java中使用char和int进行比较?
  13. Apple在线商店iphone5, ipad mini到货提醒 - 邮件
  14. centos下的nginx安装工作记录20130701
  15. 类申明 java,申明Java类必须使用的关键字是( )。
  16. 190510每日一句
  17. 《土豆荣耀》重构笔记(七)控制角色移动并添加音效
  18. iphone 控制 android手机,苹果手机如何远程控制安卓手机
  19. 转载:程序员必备的8个学习工具
  20. 【数据结构与算法】迪杰斯特拉算法的介绍和最短路径问题程序实现

热门文章

  1. 为什么要用RabbitMQ
  2. Innodb存储引擎的特性(2)
  3. SpringCloud:Hystrix 熔断机制(基本配置、服务降级、HystrixDashboard服务监控、Turbine聚合监控)
  4. SpringCloud:Ribbon负载均衡(基本使用、 负载均衡、自定义配置、禁用 Eureka 实现 Ribbon 调用)
  5. redis(2)--数据结构与对象
  6. 210221阶段三线程、信号量、互斥锁
  7. 获取二进制或者16/32位的某一位的值
  8. ERROR: Could not read unit serial number!
  9. RMAN删除归档日志不释放问题
  10. 关于Unity中坐标系的种类