日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Visual Studio原生开发的20条调试技巧

發布時間:2025/3/12 编程问答 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Visual Studio原生开发的20条调试技巧 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

我的上篇文章《Vistual Studio原生開發的10個調試技巧》引發了很多人的興趣,所以我決定跟大家分享更多的調試技巧。接下來你又能看到一些對于原生應用程序的很有幫助的調試技巧(接著上一篇文章來編號)。這些技巧需要應用在Vistual Studio 2005 或者更新的版本中(當然也有一些適用于舊版本)。如果你能閱讀本文中推薦的一些相關文章,就可以知道每一個技巧的更多信息。

  • 11. 數據斷點
  • 12. 線程重命名
  • 13. 給指定線程設置斷點
  • 14.(粗略)估算執行時間
  • 15.? 數字格式化
  • 16. (內存)數據格式化
  • 17.系統DLL中斷
  • 18.加載符號表
  • 19.? 監測MFC中的內存泄漏
  • 20.? 調試ATL

技巧11: 數據斷點

當數據所在的內存位置發生變化時,可以通知調試器進行中斷,但是每次只能創建4個字節這樣的硬件數據斷點。數據斷點只能在調試期間添加,可以通過菜單(Debug>New Breakpoint>New Data Breakpoint) 或者斷點窗口來添加。

你可以使用內存地址或者地址表達式。盡管棧上和堆上的值你都可以看到,但是我認為當堆上的數值發生變化時,這個功能才會更有用處。它對于識別內存損壞有很大的幫助。

下面的例子中,指針的值發生了變化,不再是它所指向對象的值。為了找出在什么地方發生改變的,我在存儲指針值的位置設置了一個斷點,即&ptr(注意必須在指針初始化之后)。數據發生變化就意味著有人修改了指針的值,調試器發生中斷,我就能找出是哪段代碼引起的改變。

更多閱讀:

1.怎樣查明指針是否損壞內存

2.怎樣查明指針在什么地方發生改變

技巧12: 線程重命名

在調試多線程應用程序時,線程窗口會顯示創建了哪些線程以及當前正在運行的線程。線程越多,想找到你想要的線程就越困難(尤其是當一段程序被多個線程同時執行的時候,你不能確切地知道哪個才是當前正在執行的線程實例)。

調試器允許修改線程的名字,可以在線程窗口使用線程的快捷菜單,給線程重命名。

也可以在程序里給線程命名,盡管有點棘手,而且必須在線程啟動之后給它命名,否則調試器會以默認命名規范將它重新初始化。定義一個線程,并用下面的函數重命名該線程。

C++ typedef struct tagTHREADNAME_INFO {DWORD dwType; // must be 0x1000LPCSTR szName; // pointer to name (in same addr space)DWORD dwThreadID; // thread ID (-1 caller thread)DWORD dwFlags; // reserved for future use, most be zero } THREADNAME_INFO;void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) {THREADNAME_INFO info;info.dwType = 0x1000;info.szName = szThreadName;info.dwThreadID = dwThreadID;info.dwFlags = 0;__try{RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info);}__except (EXCEPTION_CONTINUE_EXECUTION){} }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 typedef struct tagTHREADNAME_INFO { DWORD dwType;????????// must be 0x1000 LPCSTR szName;?????? // pointer to name (in same addr space) DWORD dwThreadID;????// thread ID (-1 caller thread) DWORD dwFlags;?????? // reserved for future use, most be zero } THREADNAME_INFO; void SetThreadName(DWORD dwThreadID, LPCSTR szThreadName) { THREADNAME_INFO info; info.dwType = 0x1000; info.szName = szThreadName; info.dwThreadID = dwThreadID; info.dwFlags = 0; __try { RaiseException(0x406D1388, 0, sizeof(info)/sizeof(DWORD), (DWORD*)&info); } __except (EXCEPTION_CONTINUE_EXECUTION) { } }

更多閱讀:

設置線程名字(非托管)

技巧13: 給指定線程設置斷點

?對于多線程應用程序來說,另一個有用的技巧就是給指定的線程,進程,甚至是計算機中的斷點設置過濾.可以通過斷點的Filter命令來實現此功能.

?

調試器允許你指定線程名,線程ID,進程名,進程ID和機器名的任意組合(使用AND,OR,NOT)來設置過濾。了解怎樣設置線程名字也使得這項過濾操作變得更加簡單。

更多閱讀:

  • 怎樣指定斷點過濾器
  • 設置斷點過濾
  • 技巧14: (粗略)估算執行時間

    在上一篇文章中,我有寫關于Watch窗口中的偽變量,有一個沒提到的是@clk,它用于顯示計數器的值,可以粗略地計算出兩個斷點之間的代碼的執行時間,單位是微秒(μS)。但是,千萬不要用這個方法來分析程序的執行效率,應該使用Visual Studio 分析工具或者性能計時器來分析。

    可以在Watch 窗口或者即時窗口添加@clk=0來完成對計時器的重置。因此要想估算執行一段代碼需要多長時間,可以按照下面的步驟來操作:

  • 在代碼塊的開始位置設置斷點
  • 在代碼塊的結束位置設置斷點
  • 在Watch窗口添加 @clk
  • 程序進入到第一個斷點時,在即時窗口輸入@clk=0
  • 運行程序直到執行進入代碼塊末尾的斷點,查看Watch窗口 @clk的值。
  • 注意網上有一些技巧說在Watch窗口添加兩個表達式:@clk和@clk=0,需要在每次執行斷點的時候都要重置計時器。這種用法只適用于Visual Studio的老版本,在VS2005及以上版本不再適用。

    更多閱讀:

    ?調試技巧-@CLK

    技巧15:數字格式化

    當你在Watch或者Quick Watch窗口查看變量時, 這些值是以默認的預定義可視化形式顯示的。而對于數字,則是根據數據類型(integer, float, double),用十進制形式顯示的。但是你可以使用調試器把數字用不同的類型或者進制數顯示出來。

    想要改變顯示類型可在變量前加以下前綴:

  • by –unsigned char (又稱為unsigned byte)
  • wo – unsigned shot(又稱為 unsigned word)
  • dw – unsigned long(又稱為 unsigned double word)
  • 要改變顯示的進制數在變量前加下列前綴:

  • d 或者 i– 有符號十進制數
  • u – 無符號十進制數
  • o –? 無符號八進制數
  • x – 小寫十六進制數
  • X – 大寫十六進制數
  • ?

    更多閱讀:

    C++ 調試技巧

    技巧16:(內存數據)格式化

    除了數字,調試器還可以在Watch窗口顯示格式化的內存數據,最多64 bytes。 你可以使用在表達式(變量或內存地址)后添加下列說明符作為后綴來格式化數據:

  • mb 或者 m – 十六進制顯示的16字節數據,后跟16個ASCII 字符
  • mw – 8 words
  • md – 4 double words
  • mq –? 2 quad-words
  • ma – 64個ASCII字符
  • mu – 2字節的UNICODE字符
  • ?

    更多閱讀:

  • C++中的格式說明符
  • Developer Studio的調試技巧
  • 技巧17: 系統DLL的中斷

    ?有時候在DLL中的函數被調用時進行中斷是很有用的,像系統DLL(比如 Kernel32.dll 或者user32.dll).實現此中斷,需要使用本機調試器提供的上下文運算符.你可以設定斷點位置,變量名或者表達式:

    ?1.{[函數],[源碼],[模塊]}位置

    ?2. [函數],[源碼],[模塊]}變量名

    ?3. [函數],[源碼],[模塊]}表達式

    花括號里可以是函數名,源代碼和模塊的任意組合,但是逗號不能省略.

    我們假設想要在CreateThread函數被調用時發生中斷,這個函數是從kernel32.dll中導出的,所以上下文運算符應該為: ?{,,kernel32.dll}CreateThread. 然而,這樣并不可行,因為上下文運算符需要CreatThread的修飾符,可以使用DBH.exe來獲取一個特定函數的修飾符。

    下面就是如何得到CreateThread函數的修飾符的:

    C++ C:\Program Files (x86)\Debugging Tools for Windows (x86)>dbh.exe -s:srv*C:\Symbo ls*http://msdl.microsoft.com/Download/Symbols -d C:\Windows\SysWOW64\kernel32.dl l enum *CreateThread* Symbol Search Path: srv*C:\Symbols*http://msdl.microsoft.com/Download/Symbolsindex address name1 10b4f65 : _BaseCreateThreadPoolThread@122 102e6b7 : _CreateThreadpoolWork@123 103234c : _CreateThreadpoolStub@44 1011ea8 : _CreateThreadStub@245 1019d40 : _NtWow64CsrBasepCreateThread@126 1019464 : ??_C@_0BC@PKLIFPAJ@SHCreateThreadRef?$AA@7 107309c : ??_C@_0BD@CIEDBPNA@TF_CreateThreadMgr?$AA@8 102ce87 : _CreateThreadpoolCleanupGroupStub@09 1038fe3 : _CreateThreadpoolIoStub@16a 102e6f0 : _CreateThreadpoolTimer@12b 102e759 : _CreateThreadpoolWaitStub@12c 102ce8e : _CreateThreadpoolCleanupGroup@0d 102e6e3 : _CreateThreadpoolTimerStub@12e 1038ff0 : _CreateThreadpoolIo@16f 102e766 : _CreateThreadpoolWait@1210 102e6aa : _CreateThreadpoolWorkStub@1211 1032359 : _CreateThreadpool@4
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 C:\Program Files (x86)\Debugging Tools for Windows (x86)>dbh.exe -s:srv*C:\Symbo ls*http://msdl.microsoft.com/Download/Symbols -d C:\Windows\SysWOW64\kernel32.dl l enum *CreateThread* Symbol Search Path: srv*C:\Symbols*http://msdl.microsoft.com/Download/Symbols index????????????address???? name ???? 1????????????10b4f65 :?? _BaseCreateThreadPoolThread@12 ???? 2????????????102e6b7 :?? _CreateThreadpoolWork@12 ???? 3????????????103234c :?? _CreateThreadpoolStub@4 ???? 4????????????1011ea8 :?? _CreateThreadStub@24 ???? 5????????????1019d40 :?? _NtWow64CsrBasepCreateThread@12 ???? 6????????????1019464 :?? ??_C@_0BC@PKLIFPAJ@SHCreateThreadRef?$AA@ ???? 7????????????107309c :?? ??_C@_0BD@CIEDBPNA@TF_CreateThreadMgr?$AA@ ???? 8????????????102ce87 :?? _CreateThreadpoolCleanupGroupStub@0 ???? 9????????????1038fe3 :?? _CreateThreadpoolIoStub@16 ???? a????????????102e6f0 :?? _CreateThreadpoolTimer@12 ???? b????????????102e759 :?? _CreateThreadpoolWaitStub@12 ???? c????????????102ce8e :?? _CreateThreadpoolCleanupGroup@0 ???? d????????????102e6e3 :?? _CreateThreadpoolTimerStub@12 ???? e????????????1038ff0 :?? _CreateThreadpoolIo@16 ???? f????????????102e766 :?? _CreateThreadpoolWait@12 ????10????????????102e6aa :?? _CreateThreadpoolWorkStub@12 ????11????????????1032359 :?? _CreateThreadpool@4

    看上去實際名字應該是_CreateThreadStub@24,這樣我們就可以創建斷點,{,,kernel32.dll}_CreateThreadStub@24

    運行程序,發生中斷時會有消息提示斷點處無相關源代碼,直接忽略它。
    1 運行程序,發生中斷時會有消息提示斷點處無相關源代碼,直接忽略它。

    使用調用棧窗口查看調用該函數的代碼。
    1 使用調用棧窗口查看調用該函數的代碼。

    更多閱讀:
    1 更多閱讀:

    1.? 在Visual Studio 2010中,沒有源代碼如何設置斷點
    1 1.? Visual Studio 2010中,沒有源代碼如何設置斷點

    2.? 上下文運算符(C/C++語言表達式)
    1 2.? 上下文運算符(C/C++語言表達式)

    3.? 怎樣給函數設置斷點
    1 3.? 怎樣給函數設置斷點

    技巧18:加載符號表

    在調試程序的時候,調用棧窗口不會顯示完整的調用棧,跳過了系統DLL(比如kernel32.dll 和 user32.dll)的信息。

    可以通過加載這些DLL的符號表來獲得完整的調用棧信息,直接在調用棧窗口使用快捷菜單就能完成。你可以從預先指定的符號路徑或者微軟的符號服務器(如果是系統DLL)來下載符號。符號下載完成后,直接導入到調試器,調用棧就會得到更新。</span>

    ?

    這些符合也可以從組件Modules窗口導入。

    ?

    一旦下載完成,符號會保存在緩存中,可以在Tools>Options>Debugging>Symbols中配置。

    ?

    技巧19:監測MFC中的內存泄漏

    如果你想要在MFC應用程序中檢測內存泄漏,需要使用宏DEBUG_NEW來重新定new運算符,這是new運算符的修改版本,記錄了每個對象內存分配的文件名和行號.在發行版中DEBUG_NEW會解析成new運算符.

    向導生成的MFC源文件在#includes后包含了下面的預處理指令:

    C++ #ifdef _DEBUG #define new DEBUG_NEW #endif
    1 2 3 #ifdef _DEBUG #define new DEBUG_NEW #endif

    ?這就是怎樣重新定義new運算符的。

    然而,很多STL頭文件和重新定義的new運算符和版本不兼容.如果你重新定義了new運算符后,又包含了<map>,<vector>,<list>,<string>等頭文件的話,就會出現下面的錯誤(以<vector>為例):

    1&gt;c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(43) : error C2665: &#039;operator new&#039; : none of the 5 overloads could convert all the argument types 1&gt; c:\program files\microsoft visual studio 9.0\vc\include\new.h(85): could be &#039;void *operator new(size_t,const std::nothrow_t &amp;) throw()&#039; 1&gt; c:\program files\microsoft visual studio 9.0\vc\include\new.h(93): or &#039;void *operator new(size_t,void *)&#039; 1&gt; while trying to match the argument list &#039;(const char [70], int)&#039; 1&gt; c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(145) : see reference to function template instantiation &#039;_Ty *std::_Allocate&lt;char&gt;(size_t,_Ty *)&#039; being compiled 1&gt; with 1&gt; [ 1&gt; _Ty=char 1&gt; ] 1&gt; c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(144) : while compiling class template member function &#039;char *std::allocator&lt;_Ty&gt;::allocate(std::allocator&lt;_Ty&gt;::size_type)&#039; 1&gt; with 1&gt; [ 1&gt; _Ty=char 1&gt; ] 1&gt; c:\program files (x86)\microsoft visual studio 9.0\vc\include\xstring(2216) : see reference to class template instantiation &#039;std::allocator&lt;_Ty&gt;&#039; being compiled 1&gt; with 1&gt; [ 1&gt; _Ty=char 1&gt; ]
    1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 1&gt;c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(43) : error C2665: &#039;operator new&#039; : none of the 5 overloads could convert all the argument types 1&gt;????????c:\program files\microsoft visual studio 9.0\vc\include\new.h(85): could be &#039;void *operator new(size_t,const std::nothrow_t &amp;) throw()&#039; 1&gt;????????c:\program files\microsoft visual studio 9.0\vc\include\new.h(93): or?????? &#039;void *operator new(size_t,void *)&#039; 1&gt;????????while trying to match the argument list &#039;(const char [70], int)&#039; 1&gt;????????c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(145) : see reference to function template instantiation &#039;_Ty *std::_Allocate&lt;char&gt;(size_t,_Ty *)&#039; being compiled 1&gt;????????with 1&gt;????????[ 1&gt;????????????_Ty=char 1&gt;????????] 1&gt;????????c:\program files (x86)\microsoft visual studio 9.0\vc\include\xmemory(144) : while compiling class template member function &#039;char *std::allocator&lt;_Ty&gt;::allocate(std::allocator&lt;_Ty&gt;::size_type)&#039; 1&gt;????????with 1&gt;????????[ 1&gt;????????????_Ty=char 1&gt;????????] 1&gt;????????c:\program files (x86)\microsoft visual studio 9.0\vc\include\xstring(2216) : see reference to class template instantiation &#039;std::allocator&lt;_Ty&gt;&#039; being compiled 1&gt;????????with 1&gt;????????[ 1&gt;????????????_Ty=char 1&gt;????????]

    解決辦法就是總是把包含這些STL頭文件放在重新定義new運算符之前.

    更多閱讀:

    ?DEBUG_NEW

    技巧20: 調試ATL

    在開發ATL COM組件時,你可以在調試器觀察COM對象的QueryInterface,AddRef和Release的調用情況.默認情況下并不支持這些,但是你只要在預處理定義或者預編譯頭文件時定義兩個宏,宏定義好之后,關于這些函數的調用信息就會顯示在output窗口.

    這兩個宏如下:

  • _ATL_DEBUG_QI: 顯示你定義的對象里每一個被查詢的接口的名字,必須在atlcom.h被包含之前定義.
  • _ATL_DEBUG_INTERFACES: 在每次AddRef 或者Release被調用時,顯示接口的當前引用計數以及對應的類名和接口名,必須在atlbase.h被包含之前定義.
  • 更多閱讀:

  • 調試技巧
  • ATL調試技巧
  • _ATL_DEBUG_INTERFACES是如何工作的?
  • 結束語

    盡管這兩篇文章并不是包含了所有的調試技巧,但是足以幫你解決原生開發中調試時遇到的大多數問題.

    總結

    以上是生活随笔為你收集整理的Visual Studio原生开发的20条调试技巧的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。