很多時(shí)候程序的 Debug 版本運(yùn)行沒有任何問題,但是一旦發(fā)布 Release 版本后,運(yùn)行就出錯(cuò),著實(shí)讓人郁悶。大家知道,VC++ 中 Release 版本是對(duì)無法對(duì)源代碼進(jìn)行調(diào)試的。一般的做法是在懷疑有錯(cuò)誤的代碼前后插入MessageBox 函數(shù),在函數(shù)中顯示可能導(dǎo)致錯(cuò)誤的變量的值。或者插入寫文件的語(yǔ)句,輸出可能導(dǎo)致錯(cuò)誤的變量的值到一個(gè)記錄文件。其實(shí),除了上面講的這個(gè)辦法之外,還有其它的途徑來調(diào)試 Release 版本的。下面就結(jié)合自己的經(jīng)驗(yàn)和網(wǎng)上查找的一些資料給出調(diào)試 Release 版本的兩個(gè)方法:方法一、利用 *.PDB 符號(hào)文件調(diào)試 Release 版本在 VCKBASE 的在線雜志中有一篇參考文章:符號(hào)文件——Windows 應(yīng)用程序調(diào)試必備(http://www.vckbase.com/document/viewdoc/?id=1710),文章談到了如何產(chǎn)生 Release 版本二進(jìn)制文件對(duì)應(yīng)的 PDB 文件的問題。有了 PDB 文件后,就可以調(diào)試 Release 了,方法是:1、在Project Settings里選Settings For為All Configurations。 2、在C/C++標(biāo)簽中,Debug info 選 Program Database。 3、在Link 標(biāo)簽中,Category選 Debug,選中Debug info 復(fù)選框和Microsoft format。 進(jìn)行了上述設(shè)置后,我們就可以像在調(diào)試版本中那樣設(shè)置斷點(diǎn)進(jìn)行測(cè)試了,由于代碼優(yōu)化,有些變量觀察不到,行的運(yùn)行順序可能也會(huì)不同。 有一點(diǎn)需要注意:ASSERT宏在 Release 版本中不起作用,在 Release 版本中應(yīng)該使用 VERIFY 來代替 ASSERT 進(jìn)行調(diào)試。如果發(fā)行版本運(yùn)行有問題,可以先禁止所有代碼優(yōu)化再進(jìn)行調(diào)試。方法二、在需要加斷點(diǎn)的地方添加如下匯編語(yǔ)句: __asm int 3 不過調(diào)試的時(shí)候無法顯示C程序,只有asm代碼。 此處 int 3 是專門用來設(shè)置斷點(diǎn)的,是 CPU 定義的,Windows 和 DOS 下的大多數(shù)調(diào)試器都采用這種方法。本貼不斷更新中,希望大家跟貼貢獻(xiàn)出更好的調(diào)試方法。以便受益者少走彎路......
本文轉(zhuǎn)自:http://m.shnenglu.com/mzty/archive/2006/11/19/15439.html
FTP Wanderer - FTP Client using WININET

Description
This article presents a fully functional implementation of a FTP client. FTP Wanderer is a multithreaded FTP client with the look-and-feel of Windows Explorer, and makes moving files between the Internet and your computer as simple as local file manipulation. This application uses WININET API's to handle the FTP protocol, and while it's not the most elegant way to connect to a FTP server it does the job pretty well.
Features list
- All file transfer requests are handled in the background, so you don't have to wait while files copy to continue browsing the current server. You can even connect to a different server, while file transfers on another server are still in progress.
- The Transfer Manager shows all file transfers currently in progress plus the ones that are in the queue. You can configure how many transfers are processed simultaneously, so you don't run out of resources.
- FTP Wanderer uses the Windows 9x/NT/XP style list boxes for displaying the contents of remote servers. With all familiar system icons and context menus.
- You can specify the number of times it should try to connect, and the time delay between connection attempts.
- Full Drag and Drop support. Simply drag one or more files or even complete folders onto FTP Wanderer's file view area to transfer them to the location of the server that is currently connected.
- Easily configure FTP site settings, like username, password, port, initial remote folder, number of retries and default download folder.
Here's a brief description of some of the important classes of the application:
CMainFrame
CMainFrame contains most of the code for managing the FTP connection. It takes care of connecting to the server; initiate up/downloads and handling other FTP related actions.
CFTPListView
This is just a CListView derived class that takes care of displaying the files and enabling you to sort them. When the application is busy downloading or sorting a directory listing it can display an animation, just like explorer does.
CConnectDlg
This class (= Site Manager) enables the user to manage FTP sites. You can add/change and delete sites and configure properties such as hostname, port, username and password. The settings of each site are save to disk using serializing.
CTransferManagerDlg
CTransferManagerDlg (= Transfer manager) takes care of queuing the file transfers. It shows all file transfers currently in progress plus the ones that are in the queue. You can configure how many transfers are processed simultaneously, so you don't run out of resources. Each up/download in handled in a separate thread by initiating aCDownloadThread or CUploadThread thread.
CDownloadThread
Each download is handled in the background using a separate thread. CDownloadThread creates a new connection with the FTP server and displays a progress dialog that tells you the status of the file transfer. When the download is completed (or aborted) it notifies the transfer manager so the UI can be updated.
CUploadThread
Each upload is handled in the background using a separate thread. CUploadThread creates a new connection with the FTP server and displays a progress dialog that tells you the status of the file transfer. When the upload is completed (or aborted) it notifies the transfer manager so the UI can be updated.
To do list:
While this application has most of the features you might expect in a FTP client, there are a few things left to do:
- Drag and drop support out of the file view area to Explorer. I think this one is not that easy, because this would probably 'block' explorer until the file transfer is completed (any suggestions?).
- Progress indication in the transfer manager. This is not really that hard to do, so this will probably be in the next release.
- Better asynchrony handling of getting directory listing. The UI now does not respond when downloading a huge directory listing.
Contacting the Author
Please send any comments or bug reports to me via
email. For any updates to this article, check my site
here.
Revision history
- 31st July 2002 - Initial revision.
License
在Window平臺(tái)上開發(fā)任何稍微底層一點(diǎn)的東西,基本上都是Hook滿天飛, 普通應(yīng)用程序如此,安全軟件更是如此, 這里簡(jiǎn)單記錄一些常用的Hook技術(shù)。
基本上做Windows開發(fā)都知道這個(gè)API, 它給我們提供了一個(gè)攔截系統(tǒng)事件和消息的機(jī)會(huì), 并且它可以將我們的DLL注入到其他進(jìn)程。
但是隨著64位時(shí)代的到來和Vista之后的UAC機(jī)制開啟,這個(gè)API很多時(shí)候不能正常工作了:
首先,32位DLL沒法直接注入到64位的應(yīng)用程序里面, 因?yàn)樗麄兊牡刂房臻g完全不一樣的。當(dāng)然盡管沒法直接注入,但是在權(quán)限范圍內(nèi),系統(tǒng)會(huì)盡量以消息的方式讓你能收到64位程序的消息事件。
其次,UAC打開的情況下低權(quán)限程序沒法Hook高權(quán)限程序, 實(shí)際上低權(quán)限程序以高權(quán)限程序窗口為Owner創(chuàng)建窗口也會(huì)失敗, 低權(quán)限程序在高權(quán)限程序窗口上模擬鼠標(biāo)鍵盤也會(huì)失敗。
有人說我們可以關(guān)閉UAC, Win7下你確實(shí)可以,但是Win8下微軟已經(jīng)不支持真正關(guān)閉UAC, 從這里我們也可以看到微軟技術(shù)過渡的方式, 中間會(huì)提供一個(gè)選項(xiàng)來讓你慢慢適應(yīng),最后再把這個(gè)選項(xiàng)關(guān)掉, UAC和Aero模式都是如此。
那么我們?nèi)绾谓鉀Q這些問題?
對(duì)于64位問題 , 解決方法是提供2個(gè)DLL,分別可以Hook32和64位程序。
對(duì)于權(quán)限問題, 解決方法是提升權(quán)限, 通過注冊(cè)系統(tǒng)服務(wù), 由服務(wù)程序創(chuàng)建我們的工作進(jìn)程。這里為什么要?jiǎng)?chuàng)建一個(gè)其他進(jìn)程而不直接在服務(wù)進(jìn)程里干活? 因?yàn)閂ista后我們有了Session隔離機(jī)制,服務(wù)程序運(yùn)行在Session 0,我們的其他程序運(yùn)行在Session 1, Session 2等, 如果我們直接在服務(wù)程序里干活,我們就只能在Session 0里工作。通過創(chuàng)建進(jìn)程,我們可以在
DuplicateTokenEx后將Token的SessionID設(shè)置成目標(biāo)Session,并且在
CreateProcessAsUser時(shí)指定目標(biāo)WinStation和Desktop, 這樣我們就既獲得了System權(quán)限,并且也可以和當(dāng)前桌面進(jìn)程交互了。
很多人可能都不知道這個(gè)API, 但是這個(gè)API其實(shí)挺重要的, 看名字就知道它是Hook事件(Event)的, 具體哪些事件可以看
這里.
為什么說這個(gè)API重要, 因?yàn)檫@個(gè)API大部分時(shí)候沒有SetWindowsHookEx的權(quán)限問題, 也就是說這個(gè)API可以讓你Hook到高權(quán)限程序的事件, 它同時(shí)支持進(jìn)程內(nèi)(WINEVENT_INCONTEXT)和進(jìn)程外(WINEVENT_OUTOFCONTEXT)2種Hook方式, 你可以以進(jìn)程外的方式Hook到64位程序的事件。
為什么這個(gè)API沒有權(quán)限問題, 因?yàn)樗墙oAccessibility用的, 也就是它是給自動(dòng)測(cè)試和殘障工具用的, 所以它要保證有效。
我曾經(jīng)看到這樣一個(gè)程序,當(dāng)任何程序(無論權(quán)限高低)有窗口拖動(dòng)(拖標(biāo)題欄改變位置或是拖邊框改變大小), 程序都能捕獲到, 當(dāng)時(shí)很好奇它是怎么做到的?
Spy了下窗口消息, 知道有這樣2個(gè)消息:WM_ENTERSIZEMOVE和WM_EXITSIZEMOVE表示進(jìn)入和退出這個(gè)事件, 但是那也只能獲得自己的消息,其他程序的消息它是如何捕獲到的?當(dāng)時(shí)懷疑用的是Hook, 卻發(fā)現(xiàn)沒有DLL注入。查遍了Windows API 也沒有發(fā)現(xiàn)有API可以查詢一個(gè)窗口是否在這個(gè)拖動(dòng)狀態(tài)。最后發(fā)現(xiàn)用的是SetWinEventHook的EVENT_SYSTEM_MOVESIZESTART和EVENT_SYSTEM_MOVESIZEEND。
API Hook
常見的API Hook包括2種, 一種是基于PE文件的導(dǎo)入表(IAT), 還有一種是修改前5個(gè)字節(jié)直接JMP的inline Hook.
對(duì)于基于IAT的方式, 原理是PE文件里有個(gè)導(dǎo)入表, 代表該模塊調(diào)用了哪些外部API,模塊被加載到內(nèi)存后, PE加載器會(huì)修改該表,地址改成外部API重定位后的真實(shí)地址, 我們只要直接把里面的地址改成我們新函數(shù)的地址, 就可以完成對(duì)相應(yīng)API的Hook。《Windows核心編程》里第22章有個(gè)封裝挺好的CAPIHook類,我們可以直接拿來用。
對(duì)于基于Jmp方式的inline hook, 原理是修改目標(biāo)函數(shù)的前5個(gè)字節(jié), 直接Jmp到我們的新函數(shù)。雖然原理挺簡(jiǎn)單, 但是因?yàn)橛玫搅似脚_(tái)相關(guān)的匯編代碼, 一般人很難寫穩(wěn)定。真正在項(xiàng)目中用還是要求穩(wěn)定, 所以我們一般用微軟封裝好的Detours, 對(duì)于Detours的原理,這里有篇不錯(cuò)的文章 微軟研究院Detour開發(fā)包之API攔截技術(shù)。
比較一下2種方式:
IAT的方式比較安全簡(jiǎn)單, 但是只適用于Hook導(dǎo)入函數(shù)方式的API。
Inline Hook相對(duì)來說復(fù)雜點(diǎn), 但是它能Hook到任何函數(shù)(API和內(nèi)部函數(shù)),但是它要求目標(biāo)函數(shù)大于5字節(jié), 同時(shí)把握好修改時(shí)機(jī)或是Freeze其他線程, 因?yàn)槎嗑€程中改寫可能會(huì)引起沖突。
還有一種是Hook被調(diào)用模塊的導(dǎo)出表(EAT), 但是感覺一般用得用的不多。原理是調(diào)用模塊里IAT中的函數(shù)地址是通過被調(diào)用模塊的EAT獲取的, 所以你只要修改了被調(diào)用模塊的EAT中的函數(shù)地址,對(duì)方的調(diào)用就自然被你Hook了。但是這里有個(gè)時(shí)機(jī)問題, 就是你替換EAT表要足夠早,要在IAT使用它之前才行。但是感覺這個(gè)行為是由PE加載器決定的, 一般人很難干涉, 不知道大家有什么好方法?
我們一般可以將IAT Hook和EAT Hook結(jié)合起來使用, 先枚舉所有模塊Hook IAT,這樣當(dāng)前已有模塊的API都被你Hook了,然后再Hook EAT, 這樣后續(xù)的模塊也被你Hook了(因?yàn)橐ㄟ^EAT獲取函數(shù)地址)。
如果你只用Hook IAT而不Hook EAT, 當(dāng)有新模塊加載時(shí),你要Hook它的IAT, 所以你就要Hook LoadLibrary(Ex)和GetProcAddress來攔截新模塊的加載。所以理論上感覺 Hook EAT不是很有必要, 因?yàn)閱斡肏ook IAT已經(jīng)可以解決我們所有的問題了, HOOK IAT還有一種優(yōu)勢(shì)是我們可以過濾某個(gè)模塊不Hook,而一旦hook EAT, 它就會(huì)影響我們所有調(diào)用該函數(shù)的模塊。
COM Hook
Window上因?yàn)橛泻芏嚅_發(fā)包是以COM方式提供的(比如DirectX), 所以我們就有了攔截COM調(diào)用的COM Hook。
因?yàn)镃OM里面很關(guān)鍵的是它的接口是C++里虛表的形式提供的, 所以COM的Hook很多是時(shí)候其實(shí)就是虛表(vtable)的Hook。
對(duì)于COMHook,考慮下面2種case:
一種是我們Hook程序先運(yùn)行,然后啟動(dòng)某個(gè)游戲程序(DirectX 9), 我們想Hook游戲的繪畫內(nèi)容。
這種方式下, 我們可以先Hook API Direct3DCreate9, 然后我們繼承于IDirect3D9, 自己實(shí)現(xiàn)一個(gè)COM對(duì)象返回回去, 這樣我們就可以攔截到所有對(duì)該對(duì)象的操作,為所欲為了, 當(dāng)然我們自己現(xiàn)實(shí)的COM對(duì)象內(nèi)部會(huì)調(diào)用真正的Direct3DCreate9,封裝真正的IDirect3D9。
當(dāng)然有時(shí)我們可能不用替代整個(gè)COM組件,我們只需要修改其中一個(gè)或幾個(gè)COM函數(shù), 這種情況下我們可以創(chuàng)建真正的IDirect3D9對(duì)象后直接修改它的虛表, 把其中某些函數(shù)改成我們自己的函數(shù)地址就可以了。
其實(shí)ATL就是用接口替代的方式來調(diào)試和記錄COM接口引用計(jì)數(shù)的次數(shù), 具體可以看我這篇 理解ATL中的一些匯編代碼
還有一種case是游戲程序已經(jīng)在運(yùn)行了, 然后才啟動(dòng)我們的Hook進(jìn)程, 我們?cè)趺礃硬拍蹾ook到里面的內(nèi)容?
這種情況下我們首先要對(duì)程序內(nèi)存有比較詳細(xì)的認(rèn)識(shí), 才能思考創(chuàng)建出來的D3D對(duì)象的虛表位置, 從而進(jìn)行Hook, 關(guān)于程序內(nèi)存布局,可見我這篇 理解程序內(nèi)存。
理論上說COM對(duì)象如果是以C++接口的方式實(shí)現(xiàn), 虛表會(huì)位于PE文件的只讀數(shù)據(jù)節(jié)(.rdata), 并且所有該類型的對(duì)象都共享該虛表, 所以我們只要?jiǎng)?chuàng)建一個(gè)該類型對(duì)象,我們就可以獲得其他人創(chuàng)建的該類型對(duì)象的虛表位置,我們就可以改寫該虛表實(shí)現(xiàn)Hook(實(shí)際操作時(shí)需要通過VirtualProtect修改頁(yè)面的只讀屬性才能寫入)。
但是實(shí)際上COM的虛表只是一塊內(nèi)存, 它并不一定是以C++實(shí)現(xiàn), 所以它可以存在于任何內(nèi)存的任何地方。另外對(duì)象的虛表也不一定是所有同類型的對(duì)象共享同一虛表, 我們完全可以每個(gè)對(duì)象都有自己的一份虛表。比如我發(fā)現(xiàn)IDirect3D9是大家共享同一虛表的(存在D3D9.dll), 但是IDirect3DDevice9就是每個(gè)對(duì)象都有自己的虛表了(存在于堆heap)。所以如果你要Hook IDirect3DDevice9接口,通過修改虛表實(shí)際上沒法實(shí)現(xiàn)。
但是盡管有時(shí)每個(gè)對(duì)象的虛表不一樣,同類型對(duì)象虛表里的函數(shù)地址卻都是一樣的, 所以這種情況下我們可以通過inline Hook直接修改函數(shù)代碼。當(dāng)然有些情況下如果是靜態(tài)鏈接庫(kù),即使函數(shù)代碼也是每個(gè)模塊都有自己的一份, 這種情況下就只能反匯編獲取虛表和函數(shù)的地址了。
最后,總結(jié)一下, 上面主要探討了Windows上的各種Hook技術(shù),通過將這些Hook技術(shù)組起來, 可以實(shí)現(xiàn)很多意想不到的功能, 比如我們完全可以通過Hook D3D實(shí)現(xiàn)Win7任務(wù)欄那種Thumbnail預(yù)覽的效果(當(dāng)然該效果可以直接由DWM API實(shí)現(xiàn), 但是如果我們可以通過HOOK以動(dòng)畫的方式實(shí)現(xiàn)是不是更有趣 )
。
本文轉(zhuǎn)自:http://m.shnenglu.com/weiym/archive/2013/10/30/203991.html
談到"C#代碼反編譯",大家可能都會(huì)想到 Reflector代碼反編譯工具,對(duì)其應(yīng)該也不會(huì)太陌生;做C#開發(fā),它算得上是一個(gè)不可或缺的實(shí)用工具。通過它我們可以很方便的查看一個(gè)程序集的源代碼(這是其最基本的使用,也是大家常用的),還可以方便破解軟件...,而我這篇文章要跟大家分享的是:用Reflector將C#的開發(fā)的軟件[項(xiàng)目](此文中所說的是winForm項(xiàng)目,對(duì)于.net項(xiàng)目可以借鑒或參考)的項(xiàng)目代碼還原——反編譯得到可運(yùn)行項(xiàng)目源碼。 我所做的反編譯,并不是'破解',其使用的前提或情況是:公司原來請(qǐng)人開發(fā)的'配餐軟件(幼兒園版)',現(xiàn)有客戶咨詢想買,并且軟件中存在一些問題需改,——不是本人開發(fā)的,該軟件也無源碼,想修改只能想辦法得到軟件的源碼。具體的'破解'方法記錄在下,方便自己以后查看!(ps: '破解'的方法,自己之前就摸索過。但困于反編譯之后的error比較多,沒有耐心,有的問題感覺比較離奇,試了幾次不行就放棄了 ——這就間接的說明:真正的黑客,應(yīng)該都是比較有耐心的,呵呵..., 但做軟件開發(fā),又何嘗不需要這樣?!)
a.使用到的工具:Reflector,具體的說是:Reflector插件File disassembler(具體是什么和how to use,直接上網(wǎng)查)。
b. 打開Reflector,選擇要'破解'的軟件(.exe)主程序,再選擇并點(diǎn)擊 如圖:
, 在右側(cè)出現(xiàn)的 程序集(輸出)類型選擇界面 選擇類型'windows Appliction' (因?yàn)槭莣inForm應(yīng)用程序,如果是選擇的其它 如'class ...'類庫(kù),則需要在之后的步驟中,改變項(xiàng)目屬性中的 輸出類型,不建議這樣操作),如圖:
點(diǎn)擊 生成 即可 得到此主程序集的源碼,其它的相關(guān)程序集再如此操作即可! ——不要以為大功告成,這只是第一步,麻煩的在后面!
c.將生成的源碼 在Vs中打開(項(xiàng)目), 先試著運(yùn)行下,(一般)會(huì)報(bào)錯(cuò),排除'xxx程序集不存在'這類的錯(cuò)誤,我所遇到的如下:
using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
[CompilerGenerated]
internalclass<PrivateImplementationDetails>{1FF4F699-35E0-4117-BDBC-9E44A1B0F9F5}
{
internalstatic Dictionary<string, int> $$method0x600012e-1;
internalstatic Dictionary<string, int> $$method0x6000137-1;
internalstatic Dictionary<string, int> $$method0x6000137-2;
internalstatic Dictionary<string, int> $$method0x600014a-1;
internalstatic Dictionary<string, int> $$method0x6000169-1;
internalstatic Dictionary<string, int> $$method0x60001b6-1;
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-1; // data size: 20 bytes
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-2; // data size: 20 bytes
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-3; // data size: 20 bytes
internalstatic __StaticArrayInitTypeSize=20 $$method0x6000213-4; // data size: 20 bytes
[StructLayout(LayoutKind.Explicit, Size=20, Pack=1)]
privatestruct __StaticArrayInitTypeSize=20
{
}
}
源碼文件中會(huì)出現(xiàn)一個(gè) _PrivateImplementationDetails_{1FF4F699-35E0-4117-BDBC-9E44A1B0F9F5}.cs 文件名很長(zhǎng) 內(nèi)容如上的 亂碼類,對(duì)此解決辦法時(shí):注釋此類 或直接刪除。【程序相關(guān)的文件,如圖片、數(shù)據(jù)庫(kù)等要記得放到項(xiàng)目關(guān)聯(lián)位置,一般在bin/debug/文件夾下】
d. 添加相關(guān)程序集的引用,設(shè)置啟動(dòng)對(duì)象。到這兒,差不多程序就可以 跑起來了,但是還沒有完 ——因?yàn)?反編譯后的代碼,文件夾的位置和界面與資源引用之間的關(guān)聯(lián),基本上都亂了.所以現(xiàn)在要解決的關(guān)鍵問題是:恢復(fù)文件間的關(guān)聯(lián)和引用(其它的問題,通過調(diào)試就差不多可以解決)。如圖:
窗體的.cs和.resx(資源)文件不在同一個(gè)文件夾中,對(duì)應(yīng)窗體的.resx文件都加上了項(xiàng)目或解決方案名前綴(zhiyiSystem.) ——This is point! 這就是我們要解決問題的關(guān)鍵:恢復(fù)窗體的.cs和.resx文件間的關(guān)聯(lián),操作大致有以下兩步:1.將窗體的.cs和.resx文件放在同一個(gè)文件中——即同一目錄。2.去掉窗體的.resx文件的前綴 ——即修改文件名,如果是一個(gè)個(gè)文件去修改,窗體比較多的話,是一件非常重復(fù)而無聊的事,于是 就上網(wǎng)找 "批量修改文件名"的工具,下載了一兩個(gè)感覺都不好用,找不到,只能自己搞了,再說這東西簡(jiǎn)單,說白了就是 遍歷文件夾中文件并'重命名'(代碼就不貼出了,文章后 附有 自己寫的 "批量修改文件名"工具)。
到此,程序就可以真正跑起來了。別看我寫出來,似乎'破解'就是一會(huì)兒的事,但我做的時(shí)候,卻幾經(jīng)折騰 好幾次都感覺"算了,又卡住了...",有些或大或小問題,在這里因?yàn)闀r(shí)間的原因 及有些步驟一時(shí)半會(huì)也想不起來了,但主要的方法應(yīng)該都沒有落下,如果有不明白的朋友,可以留言交流,再做解答;也希望在'破解'方法有經(jīng)驗(yàn)的,能多提些意見,分享下你的‘破解’經(jīng)驗(yàn)!
后附:
批量修改文件名工具.rar
本文轉(zhuǎn)自:http://www.cnblogs.com/know/archive/2011/03/15/1985026.html
相關(guān)連接:http://www.cnblogs.com/verygis/archive/2008/12/02/1346072.html
看了《深入理解linux內(nèi)核》的中斷與異常,簡(jiǎn)單總結(jié)了下,如果有錯(cuò)誤,望指正!
一 什么是中斷和異常
異常又叫同步中斷,是當(dāng)指令執(zhí)行時(shí)由cpu控制單元產(chǎn)生的,之所以稱之為異常,是因?yàn)橹挥性谝粭l指令結(jié)束之后才發(fā)出中斷(程序執(zhí)行異常或者系統(tǒng)調(diào)用)。
中斷又叫異步中斷,是由其他硬件設(shè)備依照cpu時(shí)鐘信號(hào)隨機(jī)產(chǎn)生的。
二 高級(jí)可編程中斷控制器

APIC
每個(gè)CPU都有一個(gè)本地的APIC,通過IIC bus鏈接到一個(gè)I/O APIC,這個(gè)I/O APIC負(fù)責(zé)處理外部IRQS,分發(fā)IRQS給本地APIC。
三 中斷與異常處理程序嵌套執(zhí)行
中斷處理程序允許被另一個(gè)中斷處理程序”中斷“,從而引起內(nèi)核控制路徑嵌套執(zhí)行。但是中斷處理程序是不允許發(fā)生阻塞,即任務(wù)切換的。
中斷可以搶占異常處理程序,但異常處理程序不會(huì)搶占中斷。因?yàn)橹袛嗵幚沓绦虮囟ㄌ幱趦?nèi)核態(tài),如果發(fā)生異常,那只能是BUG了,也就是說內(nèi)核控制路徑中異常處理程序不會(huì)超過一個(gè)。
四 Linux中斷描述符
Intel把中斷描述符分三類:任務(wù)門、中斷門、陷阱門,而Linux則分成五類:
- 中斷門:Intel的中斷門,DPL = 0,描述中斷處理程序,通過set_intr_gate宏設(shè)置
- 系統(tǒng)門:Intel的陷阱門,DPL = 3,用于系統(tǒng)調(diào)用,通過set_system_gate宏設(shè)置
- 系統(tǒng)中斷門:Intel的中斷門,DPL = 3,用于向量3的異常處理,通過set_system_intr_gate宏設(shè)置
- 陷阱門:Intel陷阱門,DPL = 0,大部分的異常處理,通過set_trap_gate宏設(shè)置
- 任務(wù)門:Intel任務(wù)門,DPL = 0,對(duì)”Double fault“異常處理,通過set_task_gate宏設(shè)置
五 異常處理
當(dāng)cpu產(chǎn)生異常時(shí),會(huì)自動(dòng)根據(jù)產(chǎn)生的異常編號(hào)在IDT中找對(duì)應(yīng)的異常處理程序,異常處理程序保存大多數(shù)寄存器的值,調(diào)用異常處理的高級(jí)C函數(shù)處理該異常,然后通過調(diào)用ret_from_exception從異常處理程序退出。
六 中斷處理
I/O中斷處理程序執(zhí)行的四個(gè)基本過程:
- 在內(nèi)核態(tài)堆棧中保存IRQ的值和寄存器的內(nèi)容
- 給正在為IRQ線服務(wù)的PIC發(fā)送一個(gè)應(yīng)答,這將允許該P(yáng)IC進(jìn)一步發(fā)中斷
- 執(zhí)行共享該IRQ的所有設(shè)備的中斷服務(wù)例程(ISR)
七 IRQ數(shù)據(jù)結(jié)構(gòu)

IRQ數(shù)據(jù)結(jié)構(gòu)
hw_irq_controller是對(duì)PIC進(jìn)程控制的一些函數(shù),包括應(yīng)答PIC什么的。action指向的是一個(gè)irqaction鏈,每個(gè)irqaction描述一個(gè)設(shè)備的服務(wù)例程。irq_desc_t中的state字段保證了同一時(shí)刻只有一個(gè)設(shè)備會(huì)擁有該IRQ,正在處理該IRQ的CPU會(huì)禁用這條IRQ(本地),其它c(diǎn)pu還是可以接受該IRQ的請(qǐng)求,不過由于此時(shí)state的狀態(tài)為IRQ_INPROGRESS,所以新的IRQ請(qǐng)求會(huì)在其它的CPU上應(yīng)答,但不會(huì)處理,也就是該新的IRQ處理會(huì)被延遲到處理同一個(gè)IRQ的前面一個(gè)CPU上執(zhí)行。能這樣做是因?yàn)镮RQ的數(shù)據(jù)結(jié)構(gòu)是所有CPU所共享的。
八 多種類型的內(nèi)核棧
如果編譯內(nèi)核設(shè)置內(nèi)核棧為8k,那么進(jìn)程的內(nèi)核棧被用于所有類型的內(nèi)核控制路徑。如果內(nèi)核棧為4k,則內(nèi)核使用3種類型的內(nèi)核棧:異常棧,用于處理異常,每個(gè)進(jìn)程一個(gè);硬中斷棧,用于處理中斷,每個(gè)cpu一個(gè);軟中斷棧,用于出來延遲函數(shù),每個(gè)cpu一個(gè)。
九 軟中斷
1 為什么要引進(jìn)軟中斷機(jī)制,用前面的中斷機(jī)制不就可以了嗎(老版本的linux就沒有軟中斷機(jī)制)?
從前面的中斷處理中可以看出,一個(gè)中斷處理程序的幾個(gè)中斷服務(wù)例程(每個(gè)設(shè)備一個(gè))是串行執(zhí)行的,如果某個(gè)處理例程執(zhí)行的時(shí)間比較長(zhǎng),而后面的例程又很緊急,那么會(huì)導(dǎo)致這個(gè)緊急的例程匯編延遲比較久的時(shí)間。所以如果能把一些服務(wù)例程中不是很緊急但又花費(fèi)比較長(zhǎng)的操作延遲到執(zhí)行完該IRQ上所有中斷服務(wù)例程之后執(zhí)行,那么一些緊急的,花費(fèi)時(shí)間短(一般緊急的操作所需的時(shí)間都是比較短的)的例程就可以得到快速的響應(yīng)。還有就是對(duì)于某個(gè)設(shè)備的中斷服務(wù)例程,如果它的服務(wù)例程服務(wù)時(shí)間過長(zhǎng),cpu在執(zhí)行該服務(wù)例程時(shí)是會(huì)中斷本地cpu對(duì)該設(shè)備的中斷或者整個(gè)本地中斷,這樣會(huì)導(dǎo)致很多中斷會(huì)得不到快速的響應(yīng)。
2 軟中斷使用的關(guān)鍵數(shù)據(jù)結(jié)構(gòu)
softirq_vec數(shù)組,每個(gè)數(shù)組元素類型為softirq_action,該數(shù)組總共有32個(gè)元素,目前只用了前面六個(gè)。softirq_action數(shù)據(jù)結(jié)構(gòu)包含兩個(gè)字段:指向軟中斷處理函數(shù)的action指針和指向軟中斷函數(shù)需要的通用數(shù)據(jù)結(jié)構(gòu)的data指針,這是cpu共享的。
每個(gè)進(jìn)程描述的thread_info字段中的preempt_count字段,該字段被編碼來表示三個(gè)不同的計(jì)數(shù)器和一個(gè)標(biāo)志。0-7位表示是否允許搶占內(nèi)核,8-15表示是否正在處理軟中斷,16-27表示硬件中斷控制路徑嵌套數(shù),28為是PREEMPT_ACTIVE標(biāo)志。每個(gè)進(jìn)程有一個(gè)。
另一個(gè)是每個(gè)cpu都有的32位掩碼,存放在irq_cpustat_t數(shù)據(jù)結(jié)構(gòu)中的__softirq_pending字段,32位,每一位表示softirq_vec數(shù)組中的對(duì)應(yīng)的軟中斷函數(shù)是否已激活。irq_cpustat_t存放在irq_stat數(shù)組中,每個(gè)cpu對(duì)于數(shù)組中的一個(gè)irq_cpustat_t。每個(gè)cpu一個(gè)。
3 軟中斷可延遲函數(shù)的四個(gè)操作
- 初始化,定義一個(gè)新的可延遲函數(shù),并加入到softirq_vec數(shù)組中,所有cpu共享該softirq_vec。
- 激活,標(biāo)記一個(gè)可延遲函數(shù)為”掛起“,通過前面描述的__softirq_pending字段。
- 屏蔽,有選擇地屏蔽一個(gè)可延遲函數(shù),即使它被激活。它是通過前面的preempt_count字段或者關(guān)閉本地中斷(延遲函數(shù)一般是通過中斷處理程序激活的,如果沒有中斷處理程序執(zhí)行,自然也就不會(huì)有延遲函數(shù)的激活)來實(shí)現(xiàn)的。
- 執(zhí)行,執(zhí)行一個(gè)掛起的可延遲函數(shù)和同類型的其它掛起的可延遲函數(shù)。通過do_softirq實(shí)現(xiàn)。
激活和執(zhí)行可延遲函數(shù)必須要在同一個(gè)cpu上,從前面激活和執(zhí)行可以看出這一點(diǎn)。因?yàn)開_softirq_pending是每個(gè)cpu一個(gè),所有在特定cpu激活的延遲函數(shù),只有在該cpu上的__softirq_pending標(biāo)記激活,而其它c(diǎn)pu是不知道該函數(shù)被激活的,也就不會(huì)去執(zhí)行該函數(shù)了。
4 linux現(xiàn)有的六種軟中斷
處理高級(jí)優(yōu)先級(jí)的tasklet軟中斷HI_SOFTIRQ,在softirq_vec數(shù)組的下標(biāo)為0;和時(shí)鐘中斷關(guān)聯(lián)的tasklet軟中斷TIMER_SOFTIRQ,在softirq_vec數(shù)組的下標(biāo)為1;把數(shù)據(jù)包傳送到網(wǎng)卡軟中斷NET_TX_SOFTIRQ,在softirq_vec數(shù)組的下標(biāo)為2;從網(wǎng)卡接收數(shù)據(jù)包的軟中斷NET_RX_SOFTIRQ,在softirq_vec數(shù)組的下標(biāo)為3;SCSI命令的后天中斷處理的軟中斷SCSI_SOFTIRQ,在softirq_vec數(shù)組的下標(biāo)為4;處理常規(guī)tasklet軟中斷TASKLET_SOFTIRQ,在softirq_vec數(shù)組的下標(biāo)為5;它們的優(yōu)先級(jí)是從高到低的。
5 檢查是否有軟中斷掛起(也叫激活)的時(shí)機(jī)
- 內(nèi)核調(diào)用local_bh_enable激活本地軟中斷
- smp_apic_timer_interrupt處理完本地定時(shí)中斷
- cpu處理CALL_FUNCTION_VECTOR中斷處理
- 當(dāng)一個(gè)特殊的ksoftirq/n內(nèi)核線程被喚醒。
ksoftirq/n內(nèi)核線程是專門用來處理激活的軟中斷的,每個(gè)cpu有一個(gè)。
十 tasklet
tasklet是在軟中斷的基礎(chǔ)上實(shí)現(xiàn)的。正如前面說的linux現(xiàn)有的六種軟中斷,其中HI_SOFTIRQ和TASKLET_SOFTIRQ軟中斷就是tasklet。
對(duì)于每種tasklet(HI_SOFTIRQ和TASKLET_SOFTIRQ),每個(gè)cpu都有一個(gè)tasklet_head類型來描述這種tasklet。tasklet_head類型指向了一個(gè)由tasklet類型組成的鏈表,而每個(gè)tasklet類型描述了該tasklet要執(zhí)行的函數(shù)和函數(shù)需要的數(shù)據(jù)以及tasklet的狀態(tài)(狀態(tài)表示同類型的tasklet是否在運(yùn)行等)。
當(dāng)do_softirq處理軟中斷時(shí),如果相應(yīng)的HI_SOFTIRQ和TASKLET_SOFTIRQ軟中斷被激活,就會(huì)調(diào)用對(duì)應(yīng)的軟中斷函數(shù)tasklet_hi_action和tasklet_action。而這兩個(gè)函數(shù)處理的數(shù)據(jù)正是HI_SOFTIRQ和TASKLET_SOFTIRQ類型tasklet所對(duì)應(yīng)的tasklet_head類型指向的tasklett鏈表。
為了能夠?qū)懽约旱膖asklet函數(shù)并加入到對(duì)應(yīng)tasklet類型的tasklet_head指向的鏈表中,需要調(diào)用tasklet_init來初始化新的tasklet和調(diào)用tasklet_schedule或tasklet_hi_schedule來把我們自定義的tasklet加入到對(duì)應(yīng)的tasklet_head指向的鏈表中并激活該tasklet類型所對(duì)應(yīng)的軟中斷。
所以說tasklet是在軟中斷的基礎(chǔ)上實(shí)現(xiàn)的,但不同的是軟中斷時(shí)靜態(tài)分配的(linux分配了6個(gè)軟中斷),而tasklet是可以動(dòng)態(tài)加入和刪除tasklet的。
十一 工作隊(duì)列
每個(gè)工作隊(duì)列,在每個(gè)cpu上都有一個(gè)cpu_workqueue_struct結(jié)構(gòu)來描述,即對(duì)于同一個(gè)工作隊(duì)列,每個(gè)cpu都有該隊(duì)列的一個(gè)拷貝。cpu_workqueue_struct中的worklist成員指向了由work_struct結(jié)構(gòu)組成的鏈表,這個(gè)鏈表描述的是該工作隊(duì)列被掛起的函數(shù),等待工作者線程去執(zhí)行。
通過調(diào)用create_workqueue創(chuàng)建工作隊(duì)列。如果是smp,則每個(gè)cpu都會(huì)創(chuàng)建一個(gè)工作隊(duì)列和為該工作隊(duì)列工作的工作者線程。該工作者線程一直阻塞,直到有函數(shù)被加入到工作隊(duì)列中去,工作者線程執(zhí)行了函數(shù)之后就把函數(shù)從工作隊(duì)列中刪除。
可以通過queue_work函數(shù)把一個(gè)函數(shù)加入到工作隊(duì)列中去。
由于僅僅為了一個(gè)函數(shù)就創(chuàng)建一個(gè)工作隊(duì)列開銷太大,所以內(nèi)核預(yù)定義了一個(gè)叫events的工作隊(duì)列,為該隊(duì)列工作的工作者線程叫events/n,每個(gè)cpu一個(gè)。還有就是專門為塊設(shè)備使用的kblocked工作隊(duì)列。
十二 軟中斷與工作隊(duì)列的區(qū)別
從上面可以看出,軟中斷和工作隊(duì)列十分的相似,都是對(duì)服務(wù)例程中某些耗時(shí)的操延后處理。主要區(qū)別在于,軟中斷的可延遲函數(shù)是在中斷上下文中執(zhí)行的,而工作者隊(duì)列的函數(shù)則是在進(jìn)程上下文中執(zhí)行的,也就是說軟中斷的延遲函數(shù)執(zhí)行期間不允許內(nèi)核被搶占,而工作隊(duì)列則是可以的。
本文轉(zhuǎn)自:http://www.cnblogs.com/chengxuyuancc/p/3380922.html
示例代碼:
long begin,end;
double time;
begin = clock();
{
//查看代碼
}
end = clock();
time = (double)(end - begin) / CLOCKS_PER_SEC;
Logger->Info( "%f seconds\n", time );
其他參考:
#include<iostream>
include<windows.h>
using namespace std;
int main()
{
DWORD start_time=GetTickCount();
{
//此處為被測(cè)試代碼
}
DWORD end_time=GetTickCount();
cout<<"The run time is:"<<(end_time-start_time)<<"ms!"<<endl;//輸出運(yùn)行時(shí)間
return 0;
}
#include<iostream>
#include<time.h>
using namespace std;
int main()
{
clock_t start_time=clock();
{
//被測(cè)試代碼
}
clock_t end_time=clock();
cout<< "Running time is: "<<static_cast<double>(end_time- start_time)/CLOCKS_PER_SEC*1000<<"ms"<<endl;//輸出運(yùn)行時(shí)間
return 0;
}
STL中的容器按存儲(chǔ)方式分為兩類:一類是按以數(shù)組形式存儲(chǔ)的容器(如:vector,deque);另一類是以不連續(xù)的節(jié)點(diǎn)形式存儲(chǔ)的容器(如:list,map,set)。 在使用erase方法刪除元素時(shí),迭代器有時(shí)候會(huì)失效。在Effective STL,條款9,找到了erase容器中元素的原則。
1. 對(duì)于關(guān)聯(lián)容器(如map, set, multimap,multiset),刪除當(dāng)前的iterator,僅僅會(huì)使當(dāng)前的iterator失效,只要在erase時(shí),遞增當(dāng)前iterator即可。這是因?yàn)閙ap之類的容器,使用了紅黑樹來實(shí)現(xiàn),插入、刪除一個(gè)結(jié)點(diǎn)不會(huì)對(duì)其他結(jié)點(diǎn)造成影響。
錯(cuò)誤的使用方法:
std::map<string, string> mapTest;
std::map<string, string>::iterator iter;
for ( iter = mapTest.begin();iter != mapTest.end(); iter ++ )
{
if ( iter->second == "test" )
{
mapTest.erase( iter );
}
}
正確的使用方法1:
std::map<string, string> mapTest;
std::map<string, string>::iterator iter;
for ( iter = mapTest.begin();iter != mapTest.end();)
{
if ( iter->second == "test" )
{
mapTest.erase( iter++ );
}
else
{
iter++; // Use Pre Increment for efficiency.
}
}
因?yàn)閕ter傳給erase方法的是一個(gè)副本,iter++會(huì)指向下一個(gè)元素。 正確的使用方法2:
std::map<string, string> mapTest;
std::map<string, string>::iterator iter;
for ( iter = mapTest.begin();iter != mapTest.end();)
{
if ( iter->second == "test" )
{
iter = mapTest.erase( iter );
}
else
{
++iter; // Use Pre Increment for efficiency.
}
}
2. 對(duì)于序列式容器(如vector,deque),刪除當(dāng)前的iterator會(huì)使后面所有元素的iterator都失效。這是因?yàn)関etor,deque使用了連續(xù)分配的內(nèi)存,刪除一個(gè)元素導(dǎo)致后面所有的元素會(huì)向前移動(dòng)一個(gè)位置。還好erase方法可以返回下一個(gè)有效的iterator。 3. 對(duì)于list來說,它使用了不連續(xù)分配的內(nèi)存,并且它的erase方法也會(huì)返回下一個(gè)有效的iterator,因此上面兩種正確的方法都可以使用。 其他鏈接:
http://m.shnenglu.com/Herbert/archive/2009/01/08/70479.html
其他鏈接:
http://blog.csdn.net/kay226/article/details/6126515
其他鏈接:
http://m.shnenglu.com/humanchao/archive/2013/04/22/199630.html
在MFC開發(fā)過程中使用ComboBox,有時(shí)會(huì)根據(jù)需求要求只能輸入數(shù)字,并且要求數(shù)字在一定的范圍內(nèi),例如一個(gè)用于選擇小時(shí)的ComboBox,時(shí)間范圍為00到23,那么該如何實(shí)現(xiàn)?
代碼如下:
在Dialog.h中:
……
CComboBox m_cbHour;// 聲明CComboBox類型作為CDialog類的成員變量
……
afx_msg void OnCbnEditchangeCbHour();
afx_msg void OnCbnEditupdateCbHour();
在Dialog.cpp中:
……
DDX_Control(pDX, IDC_CB_HOUR, m_cbHour);// 數(shù)據(jù)交換
……
ON_CBN_EDITCHANGE(IDC_CB_HOUR, &CDialog::OnCbnEditchangeCbHour)//添加消息映射,用于CComboBox內(nèi)容改變中
ON_CBN_EDITUPDATE(IDC_CB_HOUR, &CCDialog::OnCbnEditupdateCbHour)//添加消息映射,用于CComboBox內(nèi)容更新
……
下面來具體實(shí)現(xiàn)函數(shù):
循環(huán)遍歷CComboBox中已經(jīng)輸入的字符如果發(fā)現(xiàn)含有非數(shù)字則,置為“00”,使用循環(huán)的目的是為了防止用戶跳著輸(大概只有測(cè)試人員才會(huì)跳著輸)。
將for循環(huán)改為for (int i = 0; i < (strTmp.GetLength()); i++)遇到非數(shù)字字符就設(shè)置為00;
當(dāng)CComboBox更新數(shù)據(jù)時(shí)判斷數(shù)字范圍,如果超過邊界,則設(shè)置值為最近邊界值,至此一個(gè)只接受可控范圍數(shù)字的CComboBox就完成了,但是在WinCE開發(fā)中卻有一個(gè)問題,就是所有的漢字都輸不進(jìn)去,但是“頭”這個(gè)漢字卻能輸入,不知道這是不是一個(gè)BUG,如果在代碼中對(duì)“頭”做特別判斷,那這段程序也太奇怪了!
本文轉(zhuǎn)自:http://blog.csdn.net/wyunteng/article/details/6370882
在用SetWindowText設(shè)置控件內(nèi)容時(shí),有時(shí)候會(huì)出字體重疊問題,調(diào)用次數(shù)越多,重疊越嚴(yán)重。以下為解決方法。
方法一:RedrawWindow()1 GetDlgItem(IDC_STATIC)->SetWindowText("your string");
2 GetDlgItem(IDC_STATIC)->GetParent()->RedrawWindow();
缺點(diǎn):窗口刷新太頻繁,一閃一閃,效果不太好。
方法二:局部刷新1 void YourDlg::RefreshControl(UINT uCtlID)
2 {
3 CRect rc;
4 GetDlgItem(uCtlID)->GetWindowRect(&rc);
5 ScreenToClient(&rc);
6 InvalidateRect(rc);
7 }
方法三:隱藏和顯示1 GetDlgItem(IDC_STATIC)->ShowWindow(SW_HIDE);
2 GetDlgItem(IDC_STATIC)->ShowWindow(SW_SHOW);
方法一:
CFile類的成員變量:
m_hFile:表示一個(gè)打開文件的操作系統(tǒng)文件句柄。通過對(duì)m_hFile 與 CFile::hFileNull的比較來判斷該文件是否已經(jīng)打開。
示例代碼:
1 CString strFilename = _T("D:\\大學(xué)語(yǔ)文.docx");
2 CFile file;
3 file.Open(strFilename,CFile::modeReadWrite);//
4
6 if (file.m_hFile != CFile::hFileNull)
7 {
8 file.Close();
9 }
10 else
11 {
12 printf("File Already Close \n");
13 }
方法二:
利用file.GetFileName().IsEmpty()來判斷
示例代碼:
1 CString strFilename = _T("D:\\大學(xué)語(yǔ)文.docx");
2 CFile file;
3 file.Open(strFilename,CFile::modeReadWrite);//
4
5 if (!file.GetFileName().IsEmpty())
6 {
7 file.Close();
8 }
9 else
10 {
11 printf("File Already Close \n");
12 }
方法三:
通過設(shè)置成員變量來記錄文件是否被打開。如BOOL bIsFileOpen;默認(rèn)是FALSE,
打開成功,把它置為TRUE;否則置為FALSE;
然后在程序里面判斷就可以了。關(guān)閉后置bIsFileOpen為FALSE,