给Source Insight做个外挂系列之五--Insight “TabSiPlus”
生活随笔
收集整理的這篇文章主要介紹了
给Source Insight做个外挂系列之五--Insight “TabSiPlus”
小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.
“TabSiPlus 外掛插件”主要有兩部分組成,分別是“外掛插件加載器”和“插件動(dòng)態(tài)庫”。“插件動(dòng)態(tài)庫”完成Source Insight窗口的Hook,顯示Tab標(biāo)簽欄,截獲Source Insight的窗口消息并根據(jù)消息調(diào)整Tab標(biāo)簽欄等功能,這是一個(gè)動(dòng)態(tài)鏈接庫,不能主動(dòng)執(zhí)行,所以還需要一個(gè)“外掛插件加載器”,“外掛插件加載器”是一個(gè)獨(dú)立的可執(zhí)行文件,它的主要功能就是發(fā)現(xiàn)Source Insight的進(jìn)程,并把插件動(dòng)態(tài)庫“注入”到Source Insight的進(jìn)程中。本文將簡單介紹這兩個(gè)組件是如何協(xié)同工作,最終實(shí)現(xiàn)插件的完整功能。
????
??? 首先介紹“外掛插件加載器”,它就是TabSiHost.exe組件,它的功能就是監(jiān)視Source Insight進(jìn)程,發(fā)現(xiàn)Source Insight的主窗口,將插件主體動(dòng)態(tài)庫插入到Source Insight。關(guān)于如何發(fā)現(xiàn)Source Insight和代碼注入的方法請(qǐng)查看本系列的第一篇《發(fā)現(xiàn)Source Insight》和第二篇《將本地代碼注入到Source Insight》,此處就不再贅述。此處主要介紹TabSiHost.exe組件的工作流程以及一些實(shí)現(xiàn)細(xì)節(jié)問題。TabSiHost.exe組件的主函數(shù)是MainFunction(),這個(gè)函數(shù)首先獲取參數(shù),根據(jù)參數(shù)判斷是加載還是卸載插件,然后是啟動(dòng)一個(gè)定時(shí)器,這個(gè)定時(shí)器負(fù)責(zé)監(jiān)視Source Insight進(jìn)程,最后進(jìn)入一個(gè)消息循環(huán),等待進(jìn)程結(jié)束。定時(shí)器的主要任務(wù)就是檢查是否有Source Insight進(jìn)程創(chuàng)建,其實(shí)就是枚舉當(dāng)前全部程序的主窗口,查看是否有Source Insight的主窗口,如果有則要根據(jù)設(shè)置確定是否對(duì)其實(shí)施代碼注入。這里需要注意的是不能重復(fù)注入,因?yàn)槊看巫⑷攵家獎(jiǎng)?chuàng)建一個(gè)Tab標(biāo)簽欄,重復(fù)注入會(huì)導(dǎo)致很難看的事情發(fā)生。如何確定Source Insight已經(jīng)被Hook過了呢?其實(shí)可以有很多方法,比如創(chuàng)建內(nèi)核對(duì)象進(jìn)行標(biāo)識(shí),這只內(nèi)存標(biāo)志等等,TabSiPlus使用了一種很巧妙的方式,就是在Source Insight主窗口的窗口標(biāo)題字符串中插入一個(gè)標(biāo)識(shí)字符串“with TabSiPlus”,這樣定時(shí)器在枚舉程序的主窗口的時(shí)候就可以根據(jù)窗口標(biāo)題判斷這個(gè)進(jìn)程實(shí)例是否已經(jīng)被Hook過了,同時(shí),這個(gè)字符串標(biāo)識(shí)還宣示了“TabSiPlus 插件”的存在,一舉兩得。簡單了解了“外掛插件加載器”的工作過程之后,下面將重點(diǎn)介紹“插件動(dòng)態(tài)庫”的工作過程。
??? 本系列的第三篇已經(jīng)介紹過TabSiPlus.dll組件中的代碼執(zhí)行順序,當(dāng)LoadLibrary()調(diào)用發(fā)生時(shí),CTabSiPlusApp類的構(gòu)造函數(shù)和InitInstance()首先被調(diào)用,緊接著導(dǎo)出函數(shù)Initialize()被調(diào)用,Initialize()函數(shù)創(chuàng)建了CTabWndUIThread線程,CTabWndUIThread線程在初始化函數(shù)InitInstance()中創(chuàng)建并顯示一個(gè)Tab標(biāo)簽欄,于是插件開始工作了。說起來簡單,其實(shí)還是有很多細(xì)節(jié),最重要的一點(diǎn)就是Hook(子類化) Source Insight的內(nèi)部窗口。毫無疑問,要使外掛插件能夠在沒有對(duì)外接口的情況下無縫地介入到Source Insight當(dāng)中,必需要Hook它的窗口消息,這樣才能覺察它的變化,所以,在看是內(nèi)部探索之前,先看看TabSiPlus.dll組件的內(nèi)部Hook類關(guān)系圖:
圖 5.1 TabSiPlus.dll組件內(nèi)部Hook類關(guān)系圖
這個(gè)關(guān)系圖基本上和Source Insight的實(shí)際窗口關(guān)系是一致的,只是多了一個(gè)CMdiChildManagment類,這個(gè)類的主要目的是維護(hù)CSiMDIWnd類的所有子窗口。
??? CSIFrameWnd類主要是Hook Source Insight的主窗口消息,CSIFrameWnd類主要關(guān)心的消息有兩個(gè),一個(gè)是WM_SETTEXT消息,另一個(gè)是WM_DESTROY消息。處理WM_SETTEXT的目的是為了在窗口標(biāo)題發(fā)生變化的時(shí)候,添加本插件的標(biāo)識(shí)字符串:with TabSiPlus。當(dāng)Windows需要改變標(biāo)題文字時(shí),WM_SETTEXT消息就會(huì)觸發(fā),其中第二個(gè)參數(shù)就是存放窗口標(biāo)題的緩沖區(qū),這是一個(gè)地址,將其替換成我們定制的字符串地址就能起到定制窗口標(biāo)題的作用:
??? ??? case WM_SETTEXT:
??? ??? {
??? ??? ??? static TCHAR szBuffer[512];
??? ??? ??? if(lParam)
??? ??? ??? {
??? ??? ??? ??? lstrcpy(szBuffer,(LPCTSTR)lParam);
??? ??? ??? ??? lstrcat(szBuffer,lpszTextMark);
??? ??? ??? ??? lParam = (LPARAM)szBuffer;
??? ??? ??? }
??? ??? ??? DebugTracing(gnDbgLevelNormalDebug,_T("SIFrameWnd reach WM_SETTEXT, %s"),g_szCurDircetory);
??? ??? ??? break;
??? ??? }
處理WM_DESTROY消息的目的是要知道Source Insight什么時(shí)候被關(guān)閉了,主窗口是最后被銷毀的窗口,攔截主窗口的WM_DESTROY消息使Tab標(biāo)簽欄能夠有機(jī)會(huì)在主窗口銷毀前銷毀自己,然后等待CTabWndUIThread線程結(jié)束,既能夠避免資源泄漏,也能防止Source Insight出現(xiàn)異常:
??? if(uMsg == WM_DESTROY)
??? {
??? ??? DebugTracing(gnDbgLevelNormalDebug,_T("SIFrameWnd reach WM_DESTROY, Destroy Tabbar"));
??? ??? bExitPadding = TRUE;
??? ??? g_pSiFrameWnd->DestroyTabbarWnd();
??? ??? if(g_pTabWndUIThread != NULL)
??? ??? {
??? ??? ??? ::WaitForSingleObject(g_pTabWndUIThread->m_hThread,INFINITE);
??? ??? ??? g_pTabWndUIThread = NULL;
??? ??? }
??? ??? DebugTracing(gnDbgLevelNormalDebug,_T("SIFrameWnd reach WM_DESTROY, Waiting Tabbar thread end"));
??? }
??? 接下來是CSiMDIWnd類。CSIFrameWnd類的實(shí)例在Hook主窗口后,就會(huì)調(diào)用CSIFrameWnd::GetMDIClientWnd()函數(shù)獲取MDIClient窗口,MDIClient窗口是所有MDI Child窗口的直接父窗口,它處理MDI Child窗口的消息,比如創(chuàng)建、最大化、最小化以及銷毀等等,所以非常重要。和工具欄,狀態(tài)欄一樣,MDIClient窗口是主窗口的一個(gè)子窗口,前文也講過,它有固定的窗口類名:MDIClient,所以獲取MDIClient窗口的方法就是遍歷主窗口的所有子窗口,找到窗口類名是MDIClient的子窗口,幸運(yùn)的是,Source Insight的主窗口有且只有一個(gè)MDIClient窗口,看代碼:
/*這個(gè)函數(shù)總是返回一個(gè)有效的窗口句柄,即使不是MDIClient*/
HWND CSIFrameWnd::GetMDIClientWnd()
{
??? TCHAR cClassName[256];
? HWND hMDIWnd = g_pSiFrameWnd->GetTopWindow();
? ::GetClassName(hMDIWnd, cClassName, sizeof(cClassName));
? while(lstrcmp(cClassName, lpszMdiClientWndClass) != 0)
? {
??? hMDIWnd = ::GetNextWindow(hMDIWnd, GW_HWNDNEXT);
??? ASSERT(hMDIWnd);
??? GetClassName(hMDIWnd, (LPTSTR)cClassName, sizeof(cClassName));
? }
??? return hMDIWnd;
}
找到MDIClient的窗口句柄后,就用CSiMDIWnd類Hook它,Hook之后的第一件事就是遍歷MDIClient窗口的所有子窗口(就是Source Insight的視圖窗口,窗口類名是si_Sw),分別用CSiWindow類Hook全部子窗口,并將它們納入到CMdiChildManagment類的管理中。CSiMDIWnd類是整個(gè)插件中最重要的部分,它主要處理幾個(gè)重要的Windows消息,分別是WM_MDICREATE、WM_MDIDESTROY、WM_MDIACTIVATE和WM_WINDOWPOSCHANGING。在前文《分析“Source Insight”》中已經(jīng)分析過,當(dāng)Source Insight打開一個(gè)新文件時(shí),主窗口會(huì)創(chuàng)建一個(gè)窗口類名是si_Sw的窗口,由于Source Insight使用的是Windows標(biāo)準(zhǔn)MDI界面,所以截獲MDIClient窗口的WM_MDICREATE消息就可以獲取新打開的窗口,從窗口的標(biāo)題中分析出文件名,然后在Tab標(biāo)簽欄創(chuàng)建相應(yīng)的標(biāo)簽。同樣當(dāng)WM_MDIDESTROY發(fā)生時(shí),表示關(guān)閉了一個(gè)文檔,此時(shí)要從Tab標(biāo)簽欄中刪除相應(yīng)的標(biāo)簽,當(dāng)WM_MDIACTIVATE發(fā)生時(shí),要對(duì)Tab標(biāo)簽欄中對(duì)應(yīng)的標(biāo)簽突出顯示。要在Source Insight中顯示一個(gè)標(biāo)簽窗口其實(shí)很簡單,以主窗口為父窗口創(chuàng)建一個(gè)子窗口就可以了,但這樣硬插進(jìn)去的一個(gè)窗口會(huì)破壞整個(gè)Source Insight的界面,不是覆蓋其它子窗口就是被其它子窗口覆蓋,所以要處理好窗口之間的位置關(guān)系很重要。要正確處理窗口位置關(guān)系,還要Hook MDIClient窗口的WM_WINDOWPOSCHANGING消息,當(dāng)MDIClient窗口的大小或位置或窗口Z-Order將要發(fā)生變化時(shí),Windows就會(huì)發(fā)送WM_WINDOWPOSCHANGING消息給MDIClient窗口,獲取窗口的位置,大小等等信息,這個(gè)消息的LParam參數(shù)是一個(gè)WINDOWPOS結(jié)構(gòu)(指針),其中cx和cy兩個(gè)參數(shù)就是窗口的寬度和高度,修改這兩個(gè)值就可以欺騙Windows按照我們的要求調(diào)整MDIClient窗口的位置,通常我們要減少cy的值,給我們的Tab標(biāo)簽欄留出空間。
??? 接下來是CSiWindow類,這個(gè)subclass主要是HookMDIClient窗口的子窗口,子窗口的窗口句柄和CSiWindow對(duì)象之間有一個(gè)映射關(guān)系,這個(gè)關(guān)系由CMdiChildManagment類維護(hù)。CSiWindow主要是Hook了WM_GETTEXT消息,目的是獲取子窗口標(biāo)題的變化,然后在標(biāo)簽欄中相應(yīng)的有所表現(xiàn),比如,對(duì)于具有只讀屬性的文件,Source Insight會(huì)在其文件名前面顯示一個(gè)“!”,當(dāng)只讀屬性被取消后,“!”也會(huì)消失,CSiWindow要捕獲這個(gè)變化,然后通知標(biāo)簽欄相應(yīng)地修改窗口對(duì)應(yīng)的標(biāo)簽欄。再比如,當(dāng)一個(gè)文件被修改過后,文件名后面會(huì)有一個(gè)“*”,當(dāng)執(zhí)行文件保存后,這個(gè)“*”號(hào)要消失,這些也要在標(biāo)簽欄同步變化。
??? 最后是標(biāo)簽欄窗口CTabBarsWnd,這個(gè)類代碼最多,其實(shí)功能很簡單,就是維護(hù)一個(gè)TabCtrl控件,處理界面操作,根據(jù)主窗口的位置和大小調(diào)整自己的大小和位置。本來CTabBarsWnd就是一個(gè)普通的窗口類,但是在本插件中我讓它繼承自CReBar類,為的是偷一些懶,那個(gè)功能按鈕其實(shí)就是只有一個(gè)圖標(biāo)的工具條,將它和TabCtrl控件作為兩個(gè)Band添加到Rebar上,省了很多麻煩。
??? TabSiPlus的內(nèi)部結(jié)構(gòu)就是這樣,很簡單啊,不是嗎?
Source Insight文件標(biāo)簽外掛:TabSiPlus的下載地址:
http://www.winmsg.com/download/tabsiplus.zip?
????
??? 首先介紹“外掛插件加載器”,它就是TabSiHost.exe組件,它的功能就是監(jiān)視Source Insight進(jìn)程,發(fā)現(xiàn)Source Insight的主窗口,將插件主體動(dòng)態(tài)庫插入到Source Insight。關(guān)于如何發(fā)現(xiàn)Source Insight和代碼注入的方法請(qǐng)查看本系列的第一篇《發(fā)現(xiàn)Source Insight》和第二篇《將本地代碼注入到Source Insight》,此處就不再贅述。此處主要介紹TabSiHost.exe組件的工作流程以及一些實(shí)現(xiàn)細(xì)節(jié)問題。TabSiHost.exe組件的主函數(shù)是MainFunction(),這個(gè)函數(shù)首先獲取參數(shù),根據(jù)參數(shù)判斷是加載還是卸載插件,然后是啟動(dòng)一個(gè)定時(shí)器,這個(gè)定時(shí)器負(fù)責(zé)監(jiān)視Source Insight進(jìn)程,最后進(jìn)入一個(gè)消息循環(huán),等待進(jìn)程結(jié)束。定時(shí)器的主要任務(wù)就是檢查是否有Source Insight進(jìn)程創(chuàng)建,其實(shí)就是枚舉當(dāng)前全部程序的主窗口,查看是否有Source Insight的主窗口,如果有則要根據(jù)設(shè)置確定是否對(duì)其實(shí)施代碼注入。這里需要注意的是不能重復(fù)注入,因?yàn)槊看巫⑷攵家獎(jiǎng)?chuàng)建一個(gè)Tab標(biāo)簽欄,重復(fù)注入會(huì)導(dǎo)致很難看的事情發(fā)生。如何確定Source Insight已經(jīng)被Hook過了呢?其實(shí)可以有很多方法,比如創(chuàng)建內(nèi)核對(duì)象進(jìn)行標(biāo)識(shí),這只內(nèi)存標(biāo)志等等,TabSiPlus使用了一種很巧妙的方式,就是在Source Insight主窗口的窗口標(biāo)題字符串中插入一個(gè)標(biāo)識(shí)字符串“with TabSiPlus”,這樣定時(shí)器在枚舉程序的主窗口的時(shí)候就可以根據(jù)窗口標(biāo)題判斷這個(gè)進(jìn)程實(shí)例是否已經(jīng)被Hook過了,同時(shí),這個(gè)字符串標(biāo)識(shí)還宣示了“TabSiPlus 插件”的存在,一舉兩得。簡單了解了“外掛插件加載器”的工作過程之后,下面將重點(diǎn)介紹“插件動(dòng)態(tài)庫”的工作過程。
??? 本系列的第三篇已經(jīng)介紹過TabSiPlus.dll組件中的代碼執(zhí)行順序,當(dāng)LoadLibrary()調(diào)用發(fā)生時(shí),CTabSiPlusApp類的構(gòu)造函數(shù)和InitInstance()首先被調(diào)用,緊接著導(dǎo)出函數(shù)Initialize()被調(diào)用,Initialize()函數(shù)創(chuàng)建了CTabWndUIThread線程,CTabWndUIThread線程在初始化函數(shù)InitInstance()中創(chuàng)建并顯示一個(gè)Tab標(biāo)簽欄,于是插件開始工作了。說起來簡單,其實(shí)還是有很多細(xì)節(jié),最重要的一點(diǎn)就是Hook(子類化) Source Insight的內(nèi)部窗口。毫無疑問,要使外掛插件能夠在沒有對(duì)外接口的情況下無縫地介入到Source Insight當(dāng)中,必需要Hook它的窗口消息,這樣才能覺察它的變化,所以,在看是內(nèi)部探索之前,先看看TabSiPlus.dll組件的內(nèi)部Hook類關(guān)系圖:
圖 5.1 TabSiPlus.dll組件內(nèi)部Hook類關(guān)系圖
這個(gè)關(guān)系圖基本上和Source Insight的實(shí)際窗口關(guān)系是一致的,只是多了一個(gè)CMdiChildManagment類,這個(gè)類的主要目的是維護(hù)CSiMDIWnd類的所有子窗口。
??? CSIFrameWnd類主要是Hook Source Insight的主窗口消息,CSIFrameWnd類主要關(guān)心的消息有兩個(gè),一個(gè)是WM_SETTEXT消息,另一個(gè)是WM_DESTROY消息。處理WM_SETTEXT的目的是為了在窗口標(biāo)題發(fā)生變化的時(shí)候,添加本插件的標(biāo)識(shí)字符串:with TabSiPlus。當(dāng)Windows需要改變標(biāo)題文字時(shí),WM_SETTEXT消息就會(huì)觸發(fā),其中第二個(gè)參數(shù)就是存放窗口標(biāo)題的緩沖區(qū),這是一個(gè)地址,將其替換成我們定制的字符串地址就能起到定制窗口標(biāo)題的作用:
??? ??? case WM_SETTEXT:
??? ??? {
??? ??? ??? static TCHAR szBuffer[512];
??? ??? ??? if(lParam)
??? ??? ??? {
??? ??? ??? ??? lstrcpy(szBuffer,(LPCTSTR)lParam);
??? ??? ??? ??? lstrcat(szBuffer,lpszTextMark);
??? ??? ??? ??? lParam = (LPARAM)szBuffer;
??? ??? ??? }
??? ??? ??? DebugTracing(gnDbgLevelNormalDebug,_T("SIFrameWnd reach WM_SETTEXT, %s"),g_szCurDircetory);
??? ??? ??? break;
??? ??? }
處理WM_DESTROY消息的目的是要知道Source Insight什么時(shí)候被關(guān)閉了,主窗口是最后被銷毀的窗口,攔截主窗口的WM_DESTROY消息使Tab標(biāo)簽欄能夠有機(jī)會(huì)在主窗口銷毀前銷毀自己,然后等待CTabWndUIThread線程結(jié)束,既能夠避免資源泄漏,也能防止Source Insight出現(xiàn)異常:
??? if(uMsg == WM_DESTROY)
??? {
??? ??? DebugTracing(gnDbgLevelNormalDebug,_T("SIFrameWnd reach WM_DESTROY, Destroy Tabbar"));
??? ??? bExitPadding = TRUE;
??? ??? g_pSiFrameWnd->DestroyTabbarWnd();
??? ??? if(g_pTabWndUIThread != NULL)
??? ??? {
??? ??? ??? ::WaitForSingleObject(g_pTabWndUIThread->m_hThread,INFINITE);
??? ??? ??? g_pTabWndUIThread = NULL;
??? ??? }
??? ??? DebugTracing(gnDbgLevelNormalDebug,_T("SIFrameWnd reach WM_DESTROY, Waiting Tabbar thread end"));
??? }
??? 接下來是CSiMDIWnd類。CSIFrameWnd類的實(shí)例在Hook主窗口后,就會(huì)調(diào)用CSIFrameWnd::GetMDIClientWnd()函數(shù)獲取MDIClient窗口,MDIClient窗口是所有MDI Child窗口的直接父窗口,它處理MDI Child窗口的消息,比如創(chuàng)建、最大化、最小化以及銷毀等等,所以非常重要。和工具欄,狀態(tài)欄一樣,MDIClient窗口是主窗口的一個(gè)子窗口,前文也講過,它有固定的窗口類名:MDIClient,所以獲取MDIClient窗口的方法就是遍歷主窗口的所有子窗口,找到窗口類名是MDIClient的子窗口,幸運(yùn)的是,Source Insight的主窗口有且只有一個(gè)MDIClient窗口,看代碼:
/*這個(gè)函數(shù)總是返回一個(gè)有效的窗口句柄,即使不是MDIClient*/
HWND CSIFrameWnd::GetMDIClientWnd()
{
??? TCHAR cClassName[256];
? HWND hMDIWnd = g_pSiFrameWnd->GetTopWindow();
? ::GetClassName(hMDIWnd, cClassName, sizeof(cClassName));
? while(lstrcmp(cClassName, lpszMdiClientWndClass) != 0)
? {
??? hMDIWnd = ::GetNextWindow(hMDIWnd, GW_HWNDNEXT);
??? ASSERT(hMDIWnd);
??? GetClassName(hMDIWnd, (LPTSTR)cClassName, sizeof(cClassName));
? }
??? return hMDIWnd;
}
找到MDIClient的窗口句柄后,就用CSiMDIWnd類Hook它,Hook之后的第一件事就是遍歷MDIClient窗口的所有子窗口(就是Source Insight的視圖窗口,窗口類名是si_Sw),分別用CSiWindow類Hook全部子窗口,并將它們納入到CMdiChildManagment類的管理中。CSiMDIWnd類是整個(gè)插件中最重要的部分,它主要處理幾個(gè)重要的Windows消息,分別是WM_MDICREATE、WM_MDIDESTROY、WM_MDIACTIVATE和WM_WINDOWPOSCHANGING。在前文《分析“Source Insight”》中已經(jīng)分析過,當(dāng)Source Insight打開一個(gè)新文件時(shí),主窗口會(huì)創(chuàng)建一個(gè)窗口類名是si_Sw的窗口,由于Source Insight使用的是Windows標(biāo)準(zhǔn)MDI界面,所以截獲MDIClient窗口的WM_MDICREATE消息就可以獲取新打開的窗口,從窗口的標(biāo)題中分析出文件名,然后在Tab標(biāo)簽欄創(chuàng)建相應(yīng)的標(biāo)簽。同樣當(dāng)WM_MDIDESTROY發(fā)生時(shí),表示關(guān)閉了一個(gè)文檔,此時(shí)要從Tab標(biāo)簽欄中刪除相應(yīng)的標(biāo)簽,當(dāng)WM_MDIACTIVATE發(fā)生時(shí),要對(duì)Tab標(biāo)簽欄中對(duì)應(yīng)的標(biāo)簽突出顯示。要在Source Insight中顯示一個(gè)標(biāo)簽窗口其實(shí)很簡單,以主窗口為父窗口創(chuàng)建一個(gè)子窗口就可以了,但這樣硬插進(jìn)去的一個(gè)窗口會(huì)破壞整個(gè)Source Insight的界面,不是覆蓋其它子窗口就是被其它子窗口覆蓋,所以要處理好窗口之間的位置關(guān)系很重要。要正確處理窗口位置關(guān)系,還要Hook MDIClient窗口的WM_WINDOWPOSCHANGING消息,當(dāng)MDIClient窗口的大小或位置或窗口Z-Order將要發(fā)生變化時(shí),Windows就會(huì)發(fā)送WM_WINDOWPOSCHANGING消息給MDIClient窗口,獲取窗口的位置,大小等等信息,這個(gè)消息的LParam參數(shù)是一個(gè)WINDOWPOS結(jié)構(gòu)(指針),其中cx和cy兩個(gè)參數(shù)就是窗口的寬度和高度,修改這兩個(gè)值就可以欺騙Windows按照我們的要求調(diào)整MDIClient窗口的位置,通常我們要減少cy的值,給我們的Tab標(biāo)簽欄留出空間。
??? 接下來是CSiWindow類,這個(gè)subclass主要是HookMDIClient窗口的子窗口,子窗口的窗口句柄和CSiWindow對(duì)象之間有一個(gè)映射關(guān)系,這個(gè)關(guān)系由CMdiChildManagment類維護(hù)。CSiWindow主要是Hook了WM_GETTEXT消息,目的是獲取子窗口標(biāo)題的變化,然后在標(biāo)簽欄中相應(yīng)的有所表現(xiàn),比如,對(duì)于具有只讀屬性的文件,Source Insight會(huì)在其文件名前面顯示一個(gè)“!”,當(dāng)只讀屬性被取消后,“!”也會(huì)消失,CSiWindow要捕獲這個(gè)變化,然后通知標(biāo)簽欄相應(yīng)地修改窗口對(duì)應(yīng)的標(biāo)簽欄。再比如,當(dāng)一個(gè)文件被修改過后,文件名后面會(huì)有一個(gè)“*”,當(dāng)執(zhí)行文件保存后,這個(gè)“*”號(hào)要消失,這些也要在標(biāo)簽欄同步變化。
??? 最后是標(biāo)簽欄窗口CTabBarsWnd,這個(gè)類代碼最多,其實(shí)功能很簡單,就是維護(hù)一個(gè)TabCtrl控件,處理界面操作,根據(jù)主窗口的位置和大小調(diào)整自己的大小和位置。本來CTabBarsWnd就是一個(gè)普通的窗口類,但是在本插件中我讓它繼承自CReBar類,為的是偷一些懶,那個(gè)功能按鈕其實(shí)就是只有一個(gè)圖標(biāo)的工具條,將它和TabCtrl控件作為兩個(gè)Band添加到Rebar上,省了很多麻煩。
??? TabSiPlus的內(nèi)部結(jié)構(gòu)就是這樣,很簡單啊,不是嗎?
Source Insight文件標(biāo)簽外掛:TabSiPlus的下載地址:
http://www.winmsg.com/download/tabsiplus.zip?
版權(quán)聲明:本文為博主原創(chuàng)文章,未經(jīng)博主允許不得轉(zhuǎn)載。
總結(jié)
以上是生活随笔為你收集整理的给Source Insight做个外挂系列之五--Insight “TabSiPlus”的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 给Source Insight做个外挂系
- 下一篇: 给Source Insight做个外挂系