一种在注入进程中使用WTL创建无焦点不在任务栏出现“吸附”窗口的方法和思路
? ? ? ? 最近一直在做沙箱項(xiàng)目,在項(xiàng)目快接近結(jié)尾的時(shí)候,我想給在我們沙箱中運(yùn)行的程序界面打上一個(gè)標(biāo)記——標(biāo)識(shí)其在我們沙箱中運(yùn)行的。我大致想法是:在被注入程序的頂層窗口上方顯示一個(gè)“標(biāo)題性”窗口,頂層窗口外框外顯示一個(gè)“異形”的空心窗口。這些窗口如影子般隨著其被“吸附”窗口移動(dòng)而移動(dòng),大小變化而變化。(轉(zhuǎn)載請(qǐng)指明出處)以記事本為被注入程序?yàn)槔?#xff1a;
? ? ? ? 我用的注入和HooKApi方案是采用微軟的detour庫。關(guān)于如何HookApi的方法,可以參看我之前的《一種注冊(cè)表沙箱的思路、實(shí)現(xiàn)——Hook Nt函數(shù)》。注入的方案,我采用的Detour庫的DetourCreateProcessWithDll函數(shù),該函數(shù)的W版原型是
BOOL WINAPI DetourCreateProcessWithDllW(LPCWSTR lpApplicationName,__in_z LPWSTR lpCommandLine,LPSECURITY_ATTRIBUTES lpProcessAttributes,LPSECURITY_ATTRIBUTES lpThreadAttributes,BOOL bInheritHandles,DWORD dwCreationFlags,LPVOID lpEnvironment,LPCWSTR lpCurrentDirectory,LPSTARTUPINFOW lpStartupInfo,LPPROCESS_INFORMATION lpProcessInformation,LPCSTR lpDllName,PDETOUR_CREATE_PROCESS_ROUTINEW pfCreateProcessW)
? ? ? ? 該函數(shù)從倒數(shù)第二個(gè)參數(shù)之前的全是CreateProcessW的參數(shù),倒數(shù)第二個(gè)參數(shù)是我們要注入的DLL的路徑,最后一個(gè)參數(shù)是真實(shí)的CreateProcessW的函數(shù)入口地址。該函數(shù)的實(shí)現(xiàn)細(xì)節(jié)是:
? ? ? ? 1 以掛起的方式啟動(dòng)被注入程序
? ? ? ? 2 在內(nèi)存中,修改被注入程序的導(dǎo)入表信息,在表中增加一個(gè)我們要注入的DLL中的導(dǎo)出函數(shù)
? ? ? ? 3 恢復(fù)被掛起的進(jìn)程
? ? ? ? 該方案通過修改程序?qū)氡?#xff0c;讓系統(tǒng)誤以為該程序需要調(diào)用到我們要注入的DLL中的導(dǎo)出函數(shù),于是將我們注入的DLL加載到該進(jìn)程內(nèi)存空間,從而實(shí)現(xiàn)注入。這兒有個(gè)細(xì)節(jié)要說明:該方案要求我們注入DLL要至少有一個(gè)導(dǎo)出函數(shù),哪怕這個(gè)函數(shù)什么也不做。
? ? ? ? 源碼中RegSandBoxMainDialog工程是個(gè)MFC工程,它用于啟動(dòng)我們注入的進(jìn)程并實(shí)現(xiàn)注入。我們查看其注入代碼的實(shí)現(xiàn)
UpdateData(TRUE);char chCurrentPath[MAX_PATH] = {0};GetModuleFileNameA( NULL, chCurrentPath, MAX_PATH );std::string wszCurrentPath = chCurrentPath;size_t nindex = wszCurrentPath.rfind('\\');wszCurrentPath = wszCurrentPath.substr( 0, nindex );wszCurrentPath += "\\";std::string szDllPath = wszCurrentPath;szDllPath += "HookWindow.dll"; // 拼接處注入DLL的完整路徑std::wstring wszFilePath = m_Path; // 被注入進(jìn)程的路徑STARTUPINFO StartupInfo;ZeroMemory(&StartupInfo, sizeof(STARTUPINFO));StartupInfo.cb = sizeof(STARTUPINFO);PROCESS_INFORMATION ProcessInfo;ZeroMemory(&ProcessInfo, sizeof(PROCESS_INFORMATION));// FL:使用DetourCreateProcessWithDll需要注入的DLL要有一個(gè)導(dǎo)出函數(shù)BOOL bSuc = DetourCreateProcessWithDll( wszFilePath.c_str(), NULL, NULL, NULL, FALSE, CREATE_DEFAULT_ERROR_MODE ,NULL, NULL, &StartupInfo, &ProcessInfo, szDllPath.c_str(), CreateProcessW );
? ? ? ? HookWindow工程編譯連接處HookWIndow.dll,我將在之后一步一步介紹這個(gè)DLL的編寫。?
? ? ? ? HookWindow是一個(gè)Win32 dll工程,我們?yōu)槠涠x一個(gè)def文件HookWindow.def,其內(nèi)容為:
LIBRARY "HookWindow"
EXPORTS Notify
? ? ? ?Notify函數(shù)是為了達(dá)到DetourCreateProcessWithDll要求:注入DLL必須要至少有一個(gè)導(dǎo)出函數(shù)(原因已在上面說明過)而設(shè)計(jì)的,實(shí)際這個(gè)函數(shù)什么也沒做,他就是個(gè)空殼。
VOID Notify(){
}
? ? ? ? 現(xiàn)在得開始考慮窗口的實(shí)現(xiàn)了。我們知道windows系統(tǒng)是消息驅(qū)動(dòng)的模型,那么我們的“吸附”窗口的消息模型該是什么樣的?當(dāng)時(shí)我思考方案時(shí)得出以下兩種方案:
? ? ? ? 1 Hook進(jìn)程內(nèi)窗口消息,在消息鏈中根據(jù)頂層窗口消息而決定我們窗口的創(chuàng)建、顯示、隱藏和銷毀。這相當(dāng)于我們窗口的消息循環(huán)使用了被注入進(jìn)程的頂層窗口的消息循環(huán)。
? ? ? ? 2 注入進(jìn)程后,啟動(dòng)一個(gè)線程,該線程負(fù)責(zé)創(chuàng)建窗口,同時(shí)在該線程中再啟動(dòng)一個(gè)監(jiān)視被注入進(jìn)程頂層窗口的線程,該線程將根據(jù)其得到的被注入進(jìn)程窗口的位置大小狀態(tài)等信息告訴我們窗口應(yīng)該做何種處理。
? ? ? ?這兩種方法各有其優(yōu)缺點(diǎn),方法1比方法2少1個(gè)線程,但是存在一種場景:當(dāng)點(diǎn)擊被注入程序頂層窗口的非客戶區(qū)時(shí),我們的窗口會(huì)被蓋掉,因?yàn)檫@個(gè)時(shí)候還沒輪到我們窗口處理該消息(SetWIndowsHookEx WH_CALLWNDPROCRET),此時(shí)我們無法讓我們窗口顯示在被注入進(jìn)程頂層窗口前面。方法2就是比方法1多出線程數(shù),如果我想創(chuàng)建兩個(gè)窗口,就多出兩個(gè)窗口線程,以此類推。如我設(shè)想的需求,我將創(chuàng)建一個(gè)管理外框異形空心窗口的線程和一個(gè)“標(biāo)題”窗口,那就多出兩個(gè)線程。
? ? ? ? 我覺得我這兩個(gè)窗口要處理的消息非常簡單,同樣也想做點(diǎn)與眾不同。于是我設(shè)計(jì)了這樣的方案,方案是融合了方案1和方案2的優(yōu)點(diǎn):
? ? ? ? SetWindowsHookEx勾住被注入進(jìn)程的消息,同時(shí)設(shè)置Hook類型為WH_CALLWNDPROCRET。
VOID HookWindowsFn()
{do {g_hhook = SetWindowsHookEx( WH_CALLWNDPROCRET, CallWndRetProc, NULL, GetCurrentThreadId() );if ( NULL == g_hhook ) {_ASSERT(FALSE);}InitializeCriticalSection( &g_cs );} while (0);
}
? ? ? ? 這樣我們將在原程序處理完消息后進(jìn)行消息處理。
LRESULT CALLBACK CallWndRetProc( __in int nCode, __in WPARAM wParam, __in LPARAM lParam )
{if ( NULL != lParam ) {LPCWPRETSTRUCT lptagCWPRETSTRUCT = (LPCWPRETSTRUCT)lParam;DealMsg( lptagCWPRETSTRUCT->hwnd, lptagCWPRETSTRUCT->message, lptagCWPRETSTRUCT->wParam, lptagCWPRETSTRUCT->lParam );}return CallNextHookEx(NULL, nCode, wParam, lParam);
}
? ? ? ? 當(dāng)我們收到消息時(shí),我們要判斷是否是我們關(guān)心的消息,這樣將減少我們處理消息的線程的工作量。
BOOL IsNeedDealMsg( UINT uMsg )
{return ( IsNeedShowMsg( uMsg ) || ( WM_DESTROY == uMsg )|| ( WM_CLOSE == uMsg ) );
}BOOL IsNeedShowMsg( UINT uMsg )
{return ( ( WM_SHOWWINDOW == uMsg )|| ( WM_MOVE == uMsg )|| ( WM_MOVING == uMsg )|| ( WM_SIZE == uMsg )|| ( WM_WINDOWPOSCHANGED == uMsg ) );
}
VOID DealMsg( HWND hAttachedWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{if ( FALSE == IsNeedDealMsg( uMsg ) ) {return;}
? ? ? ? 其次判斷該窗口是否為我們自己創(chuàng)建的“吸附”窗口。如果是我們的“吸附”窗口,我們將不會(huì)做任何處理。
VOID DealMsg( HWND hAttachedWnd, UINT uMsg, WPARAM wParam, LPARAM lParam )
{if ( IsHelperWindow( hAttachedWnd ) ) {return;}
BOOL IsHelperWindow( HWND hwnd )
{WCHAR wszClassNameBuffer[MAX_PATH] = {0};int nClassNameLength = GetClassName( hwnd , wszClassNameBuffer, MAX_PATH - 1 );if ( 0 != nClassNameLength ) {std::wstring wszClassName = wszClassNameBuffer;if ( 0 == wcscmp( wszClassNameBuffer, TITILEWINDOWCLASS ) ||0 == wcscmp( wszClassNameBuffer, OUTSIDEWINDOWCLASS ) ) {return TRUE; // 通過類名判斷}}return FALSE;
}
? ? ? ? 然后我們需要根據(jù)消息類型,對(duì)窗口句柄做個(gè)判斷。因?yàn)槿绻覀儭八拗鳌贝翱谔幚硗闣M_DESTROY后,我們?cè)賹⒉荒軐?duì)其調(diào)用GetWindowLong以獲取其樣式。于是對(duì)WM_DESTORY消息,我們只是判斷其是否為頂層窗口。如果不是該消息,我們將判斷該窗口是否為頂層窗口,且其窗口樣式包含WS_SYSMENU(我試驗(yàn)了下,我所遇到的我認(rèn)為該處理的窗口都有該屬性,這個(gè)屬于經(jīng)驗(yàn)之談,不一定準(zhǔn)確)。
if ( WM_DESTROY != uMsg ) {if ( FALSE == IsValibleWindow( hAttachedWnd ) ) {return;}}else {if ( FALSE == IsBaseWindow(hAttachedWnd) ) {return;}}if ( FALSE == IsNeedDealMsg( uMsg ) ) {return;}
BOOL IsValibleWindow( HWND hWnd )
{if ( FALSE == IsBaseWindow( hWnd ) ) {return FALSE;}DWORD dwStyle = ::GetWindowLong( hWnd, GWL_STYLE );if ( !( WS_SYSMENU & dwStyle ) ) {return FALSE;}return TRUE;
}BOOL IsTopWindow( HWND hwnd )
{BOOL bTop = FALSE;do {HWND hParentHwnd = NULL;HWND hParenthwnd = GetParent( hwnd );if ( NULL == hParenthwnd ){bTop = TRUE;}} while (0);return bTop;
}BOOL IsBaseWindow( HWND hWnd )
{if ( FALSE == ::IsWindow(hWnd) || FALSE == IsTopWindow(hWnd) ) {return FALSE;}return TRUE;
}
? ? ? ? 如果是原程序創(chuàng)建的窗口,則判斷該句柄是否已經(jīng)存在一個(gè)管理“吸附”窗口的線程(該信息保存在一個(gè)Map中)。如果不存在,就創(chuàng)建一個(gè)管理兩個(gè)“吸附”窗口的線程,并將<HWND,HTHREADHANDLE>對(duì)保存到Map中。如果存在,則向這個(gè)線程管理的窗口發(fā)送相應(yīng)的消息。一個(gè)進(jìn)程可能不止是存在一個(gè)頂層窗口,所以我這兒要建立Map信息。
typedef struct _WindowThreadColloction_{LPCWindowThread lpTitleWindowThread;
}WindowThreadColloction, *pWindowThreadColloction;typedef std::map<HWND,WindowThreadColloction> MapHwndThread;
typedef MapHwndThread::iterator MapHwndThreadIter;
LPCWindowThread lpWindowThread = NULL;lpWindowThread = GetTitleWindowThread( hAttachedWnd );if ( NULL == lpWindowThread ) {if ( WM_SHOWWINDOW == uMsg ) {lpWindowThread = new CWindowThread(hAttachedWnd);if ( NULL == lpWindowThread ) {_ASSERT(FALSE);return;}UpdateHwndTitleWindowThread( hAttachedWnd, lpWindowThread );}else {return;}}lpWindowThread->NotifyMsg( uMsg );
}
? ? ? ? 現(xiàn)在我們將看一下我們管理兩個(gè)“吸附”窗口的線程類。
class CWindowThread: public CMessageLoop,public CMessageFilter
{
public:CWindowThread(void);~CWindowThread(void);
public:CWindowThread(HWND hAttachWindow);
public:VOID NotifyMsg(UINT uMsg);VOID ExitThread();BOOL PreTranslateMessage(MSG* pMsg);
private:static DWORD WINAPI ThreadRoutine(LPVOID lpParam);
private:HWND m_hAttachWindow;HANDLE m_hThread;CWTLTitleWindow* m_pCWTLTitleWindow;CWTLOutSideWindow* m_pCWTLOutSideWindow;
};
typedef CWindowThread* LPCWindowThread;
? ? ? ? 消息循環(huán)是在該線程中的,于是繼承于CMessageLoop;因?yàn)槲覀円屛覀兇翱谄帘蜛TL+F4這類的操作,所以我們要PreTranslateMessage,于是要繼承于CMessageFilter。
BOOL CWindowThread::PreTranslateMessage( MSG* pMsg )
{if ( WM_SYSKEYDOWN == pMsg->message ) {return TRUE;}return FALSE;
}
DWORD WINAPI CWindowThread::ThreadRoutine( LPVOID lpParam )
{CWindowThread* pThis = (CWindowThread*) lpParam;if ( NULL == pThis ){return 0xFFFFFFFF;}if ( NULL == pThis->m_pCWTLTitleWindow ) {pThis->m_pCWTLTitleWindow = new CWTLTitleWindow( pThis->m_hAttachWindow );pThis->m_pCWTLTitleWindow->Create( pThis->m_hAttachWindow ); // 注意這兒要設(shè)置為父窗口pThis->m_pCWTLTitleWindow->RunWindowMsgLoop();}if ( NULL == pThis->m_pCWTLOutSideWindow ) {pThis->m_pCWTLOutSideWindow = new CWTLOutSideWindow( pThis->m_hAttachWindow );pThis->m_pCWTLOutSideWindow->Create( pThis->m_hAttachWindow ); // 注意這兒要設(shè)置為父窗口pThis->m_pCWTLOutSideWindow->RunWindowMsgLoop();}pThis->Run(); // 啟動(dòng)消息循環(huán)return 0;
}
VOID CWindowThread::NotifyMsg( UINT uMsg )
{if ( m_pCWTLTitleWindow ){m_pCWTLTitleWindow->DealMsg( uMsg );}if ( m_pCWTLOutSideWindow ) {m_pCWTLOutSideWindow->DealMsg( uMsg );}
}
? ? ? ? 下面再來看看窗口的實(shí)現(xiàn)。因?yàn)槲覀円龅氖恰拔健贝翱?#xff0c;該窗口應(yīng)該不能影響原窗口正常的行為(比如不應(yīng)該搶焦點(diǎn),不在任務(wù)欄出現(xiàn)),同時(shí)考慮到刷新問題,我們要讓該窗口具有雙緩存。以“標(biāo)題”窗口為例
class CWTLTitleWindow:public CDoubleBufferWindowImpl< CWTLTitleWindow, CWindow, CWinTraits<WS_POPUP|WS_CLIPSIBLINGS, WS_EX_LEFT|WS_EX_LTRREADING|WS_EX_NOPARENTNOTIFY|WS_EX_NOACTIVATE>>
{
public:typedef CWTLTitleWindow _thisClass;typedef CDoubleBufferImpl<_thisClass> _baseDblBufImpl;DECLARE_WND_CLASS_EX(TITILEWINDOWCLASS, CS_VREDRAW | CS_HREDRAW | CS_DBLCLKS, COLOR_WINDOW);CWTLTitleWindow(void);~CWTLTitleWindow(void);
public:CWTLTitleWindow(HWND hAttachWindow);BEGIN_MSG_MAP_EX(CWTLTitleWindow)MESSAGE_HANDLER( WM_SHOWWINDOW, OnShow )MESSAGE_HANDLER( WM_DESTROY, OnDestroy)MESSAGE_HANDLER( WM_QUIT, OnQuit )MESSAGE_HANDLER( WM_MOUSEACTIVATE, OnMouseActive )MESSAGE_RANGE_HANDLER( WM_USER, WM_USER + WM_USER, OnDealUserMsg )CHAIN_MSG_MAP(_baseDblBufImpl)END_MSG_MAP()LRESULT OnShow(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);LRESULT OnDestroy(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);LRESULT OnQuit(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);LRESULT OnMouseActive(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);LRESULT OnDealUserMsg(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled);void DoPaint(HDC dc);VOID Start();VOID DealMsg( UINT uMsg );private:ECalcResult CalcTitleWindowXY( int& x, int& y );VOID ShowWindow();
private:HWND m_hAttachHWnd;HINSTANCE m_hInstance;HBITMAP m_hBitmap;RECT m_hAttachWindowRect;
};
? ? ? ? 首先說一下雙緩沖。我繼承于CDoubleBufferWindowImpl。在消息映射中,我們要讓我們不處理的消息交給基類處理
typedef CWTLTitleWindow _thisClass;typedef CDoubleBufferImpl<_thisClass> _baseDblBufImpl;
CHAIN_MSG_MAP(_baseDblBufImpl)
? ? ? ? 同時(shí)實(shí)現(xiàn)DoPaint函數(shù)。
void CWTLTitleWindow::DoPaint( HDC dc )
{CRect rc;GetClientRect(&rc); ? ?CMemoryDC MemDc( dc, rc );HBRUSH hBitmapBrush = CreatePatternBrush(m_hBitmap);if ( NULL == hBitmapBrush ) {return;}MemDc.FillRect( &rc, hBitmapBrush );DeleteObject( hBitmapBrush );
}
? ? ? ? m_bBitmap是我在資源文件中的一個(gè)bmp圖片,我們?cè)赟tart函數(shù)中將其載入。在類釋放時(shí),將其delete。
CWTLTitleWindow::~CWTLTitleWindow(void)
{if ( NULL != m_hBitmap ) {DeleteObject( m_hBitmap );}
}VOID CWTLTitleWindow::Start()
{
#pragma warning(push)
#pragma warning(disable:4312)if ( NULL == m_hInstance ) {m_hInstance = (HINSTANCE)GetWindowLong( GWL_HINSTANCE );}
#pragma warning(pop)if ( NULL == m_hBitmap ) {m_hBitmap = LoadBitmap( m_hInstance, MAKEINTRESOURCE(IDB_BITMAP1));}
}
? ? ? ? 以上基本上算是完成了雙緩沖的操作了,但是為了盡量減少刷新的次數(shù),我會(huì)多加個(gè)判斷:改變的位置和大小是否和現(xiàn)在的位置和大小一致,如果一致則不做任何操作,否則刷新。
ECalcResult CWTLTitleWindow::CalcTitleWindowXY(int& x, int& y )
{ECalcResult eResult = EError;do {RECT rcAttachWindow;if ( FALSE == ::GetWindowRect( m_hAttachHWnd, &rcAttachWindow ) ) {break;}if ( rcAttachWindow.left == m_hAttachWindowRect.left && rcAttachWindow.right == m_hAttachWindowRect.right&& rcAttachWindow.top == m_hAttachWindowRect.top && rcAttachWindow.bottom == m_hAttachWindowRect.bottom ){eResult = ENoChange;break;}else {m_hAttachWindowRect = rcAttachWindow;}x = ( rcAttachWindow.left + rcAttachWindow.right - TIPWINDTH ) / 2;y = rcAttachWindow.top;eResult = ESuc;} while (0);return eResult;
}
? ? ? ? 再說下無焦點(diǎn)窗口的細(xì)節(jié)。
? ? ? ? 首先窗口樣式要有WS_POPUP,網(wǎng)上有人說還要加上WS_VISIBLE,但是我覺得沒必要。其次擴(kuò)展屬性要有WS_EX_NOACTIVATE。再次我們要處理WM_MOUSEACTIVATE消息。
MESSAGE_HANDLER( WM_MOUSEACTIVATE, OnMouseActive )
LRESULT CWTLTitleWindow::OnMouseActive( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{return MA_NOACTIVATE; // MA_NOACTIVATEANDEAT亦可
}
? ? ? ? 最后要特別注意下窗口顯示和移動(dòng)對(duì)焦點(diǎn)的影響。在窗口顯示時(shí),如果我們使用ShowWindow和MoveWindow這類的函數(shù),會(huì)導(dǎo)致我們我們窗口還可以獲得焦點(diǎn)。我們要使用SetWindowPos,最后一個(gè)參數(shù)要帶上SWP_NOACTIVATE。
VOID CWTLTitleWindow::ShowWindow()
{if ( FALSE == IsBaseWindow( m_hAttachHWnd ) ) {return;}int x = 0; int y = 0;ECalcResult eResult = CalcTitleWindowXY( x, y );if ( EError == eResult ) {::SetWindowPos( m_hWnd, NULL, x, y, TIPWINDTH, TIPHEIGHT, SWP_NOACTIVATE | SWP_HIDEWINDOW );return;}else if ( ENoChange == eResult ) {return;}::SetWindowPos( m_hWnd, NULL, x, y, TIPWINDTH, TIPHEIGHT, SWP_NOACTIVATE | SWP_SHOWWINDOW);
}
? ? ? ? 最后說一下業(yè)務(wù)相關(guān)的消息傳遞。在被注入進(jìn)程的頂層窗口接受到一些消息后,我們會(huì)將這些消息傳遞給我們的窗口,讓其做一些處理。為了區(qū)分消息來源于頂層窗口還是自己,我將頂層窗口消息處理為一個(gè)用戶自定義消息。
VOID CWTLTitleWindow::DealMsg( UINT uMsg )
{::PostMessage( m_hWnd, WM_USER + uMsg, NULL, NULL );
}
? ? ? ? 消息映射是這么寫的,用于處理整個(gè)用戶自定義消息(而不會(huì)處理頂層窗口傳來的其用戶自定義消息)
MESSAGE_RANGE_HANDLER( WM_USER, WM_USER + WM_USER, OnDealUserMsg )
LRESULT CWTLTitleWindow::OnDealUserMsg( UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled )
{UINT uAttachedWindowMsg = uMsg - WM_USER;if ( IsNeedShowMsg(uAttachedWindowMsg) ) {ShowWindow();}else {::PostMessage( m_hWnd, uAttachedWindowMsg, wParam, lParam );}return 1;
}
? ? ? ? 外框窗口和標(biāo)題窗口基本類似,但是其背景是使用畫筆畫的,而不是通過貼圖。另一個(gè)很大的區(qū)別就是外框窗口是一個(gè)空心的異形窗口。這些區(qū)別的主要體現(xiàn)是在DoPaint函數(shù)中
void CWTLOutSideWindow::DoPaint( HDC dc )
{CRect rc;GetClientRect(&rc);CMemoryDC MemDc( dc, rc );HRGN RgnInside = CreateRectRgn( rc.left + WIDTHHEIGHTADD, rc.top + WIDTHHEIGHTADD,rc.right - WIDTHHEIGHTADD, rc.bottom - WIDTHHEIGHTADD );HRGN RgnOut = CreateRectRgn( rc.left, rc.top, rc.right, rc.bottom );CombineRgn( RgnOut, RgnOut, RgnInside, RGN_DIFF );MemDc.FillRgn( RgnOut, m_brush );SetWindowRgn( RgnOut, TRUE ); // 設(shè)置異形窗口DeleteObject( RgnInside );DeleteObject( RgnOut );
}
? ? ? ? 這樣整個(gè)工程OK。以下是源碼工程。
? ? ? ?源碼工程
? ? ? 由于在國內(nèi)最近不能訪問OneDriver,故提供百度云盤的下載鏈接:http://pan.baidu.com/s/1i39sfdF 密碼:rtfa
總結(jié)
以上是生活随笔為你收集整理的一种在注入进程中使用WTL创建无焦点不在任务栏出现“吸附”窗口的方法和思路的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: PE文件和COFF文件格式分析——签名、
- 下一篇: 反汇编算法介绍和应用——线性扫描算法分析