给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架
生活随笔
收集整理的這篇文章主要介紹了
给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
??????? 上一篇文章介紹了“TabSiPlus”是如何進行代碼注入的,本篇將介紹如何構建一個外掛軟件最重要的部分,也就是為其擴展功能的定制代碼。本文前面提到過,由于windows進程管理的限制,擴展代碼必須以動態鏈接庫的形式掛載到被掛程序的進程空間中,使用上一篇介紹的方法已經可以通過創建遠程線程的方式啟動一個線程,讓這個線程加載我們的定制動態鏈接庫,現在就看看這個動態鏈接庫是如何實現的。
??? 首先這是一個動態鏈接庫,因為考慮到擴展功能中有大量的界面操作,所以選擇支持MFC,同時,還要提供一個名為“InitFunc”的導出函數,供在被掛程序中啟動的遠程線程調用,以初始化外掛動態鏈接庫,這個導出函數的原型是這樣的:
typedef DWORD (WINAPI *PFN)();
沒有參數,但是有一個返回值用于表示初始化是否成功?,F在就用Visual C++的向導生成一個支持MFC的動態鏈接庫的框架,并手工添加一個名為“InitFunc”的導出函數,如果你還不清楚怎么做,那么可以停止看本文了,因為本文可能對你毫無用處。
??
??? 在生成的代碼中,MFC對DllMain進行了封裝,所以有了一個CxxxApp的類,xxx與你的動態鏈接庫的名字一致,TabSiPlus使用的是CTabSiPlusApp,現在有三個地方需要特別注意,一個是CTabSiPlusApp::InitInstance(),一個是CTabSiPlusApp::ExitInstance(),另一個就是我們的導出函數“InitFunc”。當遠程線程中LoadLibrary()調用我們的定制動態鏈接庫時,CTabSiPlusApp::InitInstance()被調用,當FreeLibrary()調用發生時,CTabSiPlusApp::ExitInstance()被調用,當然,伴隨而出現的還有兩個函數調用,那就是CTabSiPlusApp類的構造函數和析構函數,部分初始化代碼也可以放在構造函數中完成,不過并不推薦這樣做,因為如果因為構造函數觸發異常導致LoadLibrary()失敗,那么隨后的析構函數也不會被調用,因為構造函數沒有完成對象的構造,同時,由于LoadLibrary()失敗,使得FreeLibrary()調用分支沒有執行,那么導致CTabSiPlusApp::ExitInstance()也沒有被調用,這會引起資源釋放的異常。
??? 很顯然,CTabSiPlusApp::InitInstance()的調用發生在InitFunc函數的調用之前,所以要控制好初始化代碼之前的先后關系。CTabSiPlusApp::InitInstance()中布置對資源初始化的代碼,而諸如創建文件標簽欄窗口,Hook “Source Insight”窗口消息,管理這些消息的代碼則可以布置到InitFunc函數中實現。這里需要注意的是由于我們的外掛代碼是以動態鏈接庫的形式掛載到“Source Insight”進程中的,所以它沒有消息循環,所有的窗口UI系統無法正常工作,解決的辦法有兩個,一個是在InitFunc函數創建窗口之后人為地添加一個消息循環,關于這一點如何實現可以參考Windows SDK編程的方法;另一個方法就是不要把主要的工作放在InitFunc,而是在InitFunc函數中再創建一個本地線程,把窗口UI這些麻煩的東西放在這個線程中處理,這樣就可以利用這個線程的消息循環使窗口UI系統工作起來,這樣做還有一個好處,就是InitFunc函數可以立即返回,加載外掛的宿主程序也可以及時得到外掛的加載情況,以便根據情況安排下一次加載動作(就是調用CreateRemoteThread()),同時還可以及時釋放在被掛程序中分配的內存。TabSiPlus就是采用的第二種方法,下面就是TabSiPlus的InitFunc函數實現,當然省去了一些代碼,主要核心就是一行:
DWORD WINAPI InitFunc()
{
??? //其它初始化操作
??? g_pTabWndUIThread = (CTabWndUIThread *)AfxBeginThread(RUNTIME_CLASS(CTabWndUIThread),THREAD_PRIORITY_NORMAL,0,0,NULL);
??? //其它操作
????
??? return (g_pTabWndUIThread != NULL);
}
在CTabWndUIThread類的InitInstance()函數中創建標簽欄窗口,hook “Source Insight”中相關窗口的消息:
BOOL CTabWndUIThread::InitInstance()
{
? AFX_MANAGE_STATE(AfxGetStaticModuleState());
? g_pSiFrameWnd = new CSIFrameWnd(); //CWnd::FromHandle(hDevStudioWnd);
? g_pSiFrameWnd->Attach(hWndSIFrame); //hook SI主窗口
? HWND hMDIWnd = g_pSiFrameWnd->GetMDIClientWnd();
????
??? //UINT uThressID = GetCurrentThreadId();
? // create the tabs window
? m_pTabbarWnd = new CTabBarsWnd();
? m_pTabbarWnd->Create(CWnd::FromHandle(g_pSiFrameWnd->GetSafeHwnd()),?
??? ??? RBS_BANDBORDERS | RBS_AUTOSIZE | RBS_FIXEDORDER | RBS_DBLCLKTOGGLE,?
??? ??? ? WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CBRS_TOP | CBRS_SIZE_FIXED, AFX_IDW_REBAR );
??? m_pMainWnd = m_pTabbarWnd;//這很重要,否則這個線程就無法退出
??? g_pSiFrameWnd->SetTabbarWnd(m_pTabbarWnd->GetSafeHwnd());
??? g_MdiChildMng.SetTabbarWnd(m_pTabbarWnd->GetSafeHwnd());
??? m_pTabbarWnd->SetWindowPos(CWnd::FromHandle(hMDIWnd)->GetWindow(GW_HWNDPREV), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
??? g_pSiMDIClientWnd = new CSiMDIWnd();
??? g_pSiMDIClientWnd->SetTabbarWnd(m_pTabbarWnd->GetSafeHwnd());
??? DebugTracing(gnDbgLevelNormalDebug,_T("MDI Client Attach..."));
??? g_pSiMDIClientWnd->Attach(hMDIWnd);
??? DebugTracing(gnDbgLevelNormalDebug,_T("MDI Client Enum..."));
??? g_pSiMDIClientWnd->EnumMdiChildWnd(g_MdiChildMng,TRUE);
??? DebugTracing(gnDbgLevelNormalDebug,_T("MDI Client Enum end (%d)"),g_MdiChildMng.GetChildCount());
??? pGlobalActiveSIWindow = g_MdiChildMng.LookupMdiChild(g_pSiMDIClientWnd->MDIGetActive(NULL));
??? g_pSiMDIClientWnd->SetManaging(true);
??? return TRUE;
}
是不是很象標準單文檔結構的MFC程序中的CxxxApp::InitInstance()函數?特別是對m_pMainWnd的賦值?對m_pMainWnd賦值其實很重要,否則線程就會直接退出,對m_pMainWnd賦值還有一個好處,就是關閉m_pTabbarWnd窗口就會中止CTabWndUIThread線程,這和標準單文檔結構的MFC程序中的結果一樣。
??? CTabWndUIThread::InitInstance()函數中有很多是對“Source Insight”內部窗口進行hook的代碼,那么TabSiPlus是如何得到這些窗口的句柄呢,又是如何關聯它們之間的消息呢,請看下篇:給Source Insight做個外掛系列之四--分析“Source Insight”
Source Insignt文件標簽外掛:TabSiPlus的下載地址:
?http://blog.csdn.net/orbit/
??? 首先這是一個動態鏈接庫,因為考慮到擴展功能中有大量的界面操作,所以選擇支持MFC,同時,還要提供一個名為“InitFunc”的導出函數,供在被掛程序中啟動的遠程線程調用,以初始化外掛動態鏈接庫,這個導出函數的原型是這樣的:
typedef DWORD (WINAPI *PFN)();
沒有參數,但是有一個返回值用于表示初始化是否成功?,F在就用Visual C++的向導生成一個支持MFC的動態鏈接庫的框架,并手工添加一個名為“InitFunc”的導出函數,如果你還不清楚怎么做,那么可以停止看本文了,因為本文可能對你毫無用處。
??
??? 在生成的代碼中,MFC對DllMain進行了封裝,所以有了一個CxxxApp的類,xxx與你的動態鏈接庫的名字一致,TabSiPlus使用的是CTabSiPlusApp,現在有三個地方需要特別注意,一個是CTabSiPlusApp::InitInstance(),一個是CTabSiPlusApp::ExitInstance(),另一個就是我們的導出函數“InitFunc”。當遠程線程中LoadLibrary()調用我們的定制動態鏈接庫時,CTabSiPlusApp::InitInstance()被調用,當FreeLibrary()調用發生時,CTabSiPlusApp::ExitInstance()被調用,當然,伴隨而出現的還有兩個函數調用,那就是CTabSiPlusApp類的構造函數和析構函數,部分初始化代碼也可以放在構造函數中完成,不過并不推薦這樣做,因為如果因為構造函數觸發異常導致LoadLibrary()失敗,那么隨后的析構函數也不會被調用,因為構造函數沒有完成對象的構造,同時,由于LoadLibrary()失敗,使得FreeLibrary()調用分支沒有執行,那么導致CTabSiPlusApp::ExitInstance()也沒有被調用,這會引起資源釋放的異常。
??? 很顯然,CTabSiPlusApp::InitInstance()的調用發生在InitFunc函數的調用之前,所以要控制好初始化代碼之前的先后關系。CTabSiPlusApp::InitInstance()中布置對資源初始化的代碼,而諸如創建文件標簽欄窗口,Hook “Source Insight”窗口消息,管理這些消息的代碼則可以布置到InitFunc函數中實現。這里需要注意的是由于我們的外掛代碼是以動態鏈接庫的形式掛載到“Source Insight”進程中的,所以它沒有消息循環,所有的窗口UI系統無法正常工作,解決的辦法有兩個,一個是在InitFunc函數創建窗口之后人為地添加一個消息循環,關于這一點如何實現可以參考Windows SDK編程的方法;另一個方法就是不要把主要的工作放在InitFunc,而是在InitFunc函數中再創建一個本地線程,把窗口UI這些麻煩的東西放在這個線程中處理,這樣就可以利用這個線程的消息循環使窗口UI系統工作起來,這樣做還有一個好處,就是InitFunc函數可以立即返回,加載外掛的宿主程序也可以及時得到外掛的加載情況,以便根據情況安排下一次加載動作(就是調用CreateRemoteThread()),同時還可以及時釋放在被掛程序中分配的內存。TabSiPlus就是采用的第二種方法,下面就是TabSiPlus的InitFunc函數實現,當然省去了一些代碼,主要核心就是一行:
DWORD WINAPI InitFunc()
{
??? //其它初始化操作
??? g_pTabWndUIThread = (CTabWndUIThread *)AfxBeginThread(RUNTIME_CLASS(CTabWndUIThread),THREAD_PRIORITY_NORMAL,0,0,NULL);
??? //其它操作
????
??? return (g_pTabWndUIThread != NULL);
}
在CTabWndUIThread類的InitInstance()函數中創建標簽欄窗口,hook “Source Insight”中相關窗口的消息:
BOOL CTabWndUIThread::InitInstance()
{
? AFX_MANAGE_STATE(AfxGetStaticModuleState());
? g_pSiFrameWnd = new CSIFrameWnd(); //CWnd::FromHandle(hDevStudioWnd);
? g_pSiFrameWnd->Attach(hWndSIFrame); //hook SI主窗口
? HWND hMDIWnd = g_pSiFrameWnd->GetMDIClientWnd();
????
??? //UINT uThressID = GetCurrentThreadId();
? // create the tabs window
? m_pTabbarWnd = new CTabBarsWnd();
? m_pTabbarWnd->Create(CWnd::FromHandle(g_pSiFrameWnd->GetSafeHwnd()),?
??? ??? RBS_BANDBORDERS | RBS_AUTOSIZE | RBS_FIXEDORDER | RBS_DBLCLKTOGGLE,?
??? ??? ? WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS | CBRS_TOP | CBRS_SIZE_FIXED, AFX_IDW_REBAR );
??? m_pMainWnd = m_pTabbarWnd;//這很重要,否則這個線程就無法退出
??? g_pSiFrameWnd->SetTabbarWnd(m_pTabbarWnd->GetSafeHwnd());
??? g_MdiChildMng.SetTabbarWnd(m_pTabbarWnd->GetSafeHwnd());
??? m_pTabbarWnd->SetWindowPos(CWnd::FromHandle(hMDIWnd)->GetWindow(GW_HWNDPREV), 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE);
??? g_pSiMDIClientWnd = new CSiMDIWnd();
??? g_pSiMDIClientWnd->SetTabbarWnd(m_pTabbarWnd->GetSafeHwnd());
??? DebugTracing(gnDbgLevelNormalDebug,_T("MDI Client Attach..."));
??? g_pSiMDIClientWnd->Attach(hMDIWnd);
??? DebugTracing(gnDbgLevelNormalDebug,_T("MDI Client Enum..."));
??? g_pSiMDIClientWnd->EnumMdiChildWnd(g_MdiChildMng,TRUE);
??? DebugTracing(gnDbgLevelNormalDebug,_T("MDI Client Enum end (%d)"),g_MdiChildMng.GetChildCount());
??? pGlobalActiveSIWindow = g_MdiChildMng.LookupMdiChild(g_pSiMDIClientWnd->MDIGetActive(NULL));
??? g_pSiMDIClientWnd->SetManaging(true);
??? return TRUE;
}
是不是很象標準單文檔結構的MFC程序中的CxxxApp::InitInstance()函數?特別是對m_pMainWnd的賦值?對m_pMainWnd賦值其實很重要,否則線程就會直接退出,對m_pMainWnd賦值還有一個好處,就是關閉m_pTabbarWnd窗口就會中止CTabWndUIThread線程,這和標準單文檔結構的MFC程序中的結果一樣。
??? CTabWndUIThread::InitInstance()函數中有很多是對“Source Insight”內部窗口進行hook的代碼,那么TabSiPlus是如何得到這些窗口的句柄呢,又是如何關聯它們之間的消息呢,請看下篇:給Source Insight做個外掛系列之四--分析“Source Insight”
Source Insignt文件標簽外掛:TabSiPlus的下載地址:
?http://blog.csdn.net/orbit/
版權聲明:本文為博主原創文章,未經博主允許不得轉載。
總結
以上是生活随笔為你收集整理的给Source Insight做个外挂系列之三--构建外挂软件的定制代码框架的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 给Source Insight做个外挂系
- 下一篇: 给Source Insight做个外挂系