僅使用 Just My Code 偵錯使用者程式碼

Just My Code 是 Visual Studio 偵錯功能,其會自動逐步執行對系統、架構和其他非使用者程式碼的呼叫。 在 [呼叫堆疊] 視窗中,Just My Code 會將這些呼叫摺疊成 [外部程式碼] 框架。

Just My Code 在 .NET 和 C++ 專案中會以不同的方式運作。

啟用或停用 Just My Code

針對大部分的程式設計語言,預設會啟用 Just My Code。

  • 若要在 Visual Studio 中啟用或停用 Just My Code,請在 [工具]>[選項] (或 [偵錯]>[選項]) >[偵錯]>[一般] 下,選取或取消選取 [啟用 Just My Code]

[選項] 對話框中 [啟用 Just My Code] 的螢幕擷取畫面。

[選項] 對話框中 [啟用 Just My Code] 的螢幕擷取畫面。

注意

[啟用 Just My Code] 是全域設定,適用於所有語言的所有 Visual Studio 專案。

Just My Code 偵錯

在偵錯工作階段期間,[模組] 視窗會顯示偵錯工具將哪些程式碼模組視為 My Code (使用者程式碼),以及其符號載入狀態。 如需詳細資訊,請參閱更熟悉偵錯工具附加至您應用程式的方式

[模組] 視窗中使用者程式碼的螢幕擷取畫面。

[模組] 視窗中使用者程式碼的螢幕擷取畫面。

在 [呼叫堆疊] 或 [工作] 視窗中,Just My Code 會將非使用者程式碼摺疊成標記為 [External Code] 的標註程式碼框架 (呈現灰色)。

[呼叫堆疊] 視窗中 [外部程式碼] 的螢幕擷取畫面。

[呼叫堆疊] 視窗中 [外部程式碼] 的螢幕擷取畫面。

提示

若要開啟 [模組]、[呼叫堆疊]、[工作],或其他大部分偵錯視窗,您必須在偵錯工作階段中。 偵錯時,在 [偵錯]>[視窗] 底下,選取您要開啟的視窗。

若要在摺疊的 [外部程式碼] 框架中檢視程式碼,請以滑鼠右鍵按一下 [呼叫堆疊] 或 [工作] 視窗,然後從捷徑功能表中選取 [顯示外部程式碼]。 展開的外部程式碼行會取代 [外部程式碼] 框架。

[呼叫堆疊] 視窗中 [顯示外部程式碼] 的螢幕擷取畫面。

[呼叫堆疊] 視窗中 [顯示外部程式碼] 的螢幕擷取畫面。

注意

[顯示外部程式碼] 是目前使用者分析工具設定,適用於所有語言中使用者開啟的所專案。

按兩下 [呼叫堆疊] 視窗中展開的外部程式碼行,即會以綠色醒目提示原始程式碼中的呼叫程式碼行。 對於找不到或未載入的 DLL 或其他模組,[找不到符號或來源] 頁面可能會開啟。

從 Visual Studio 2022 17.7 版開始,您可以在 Call Stack 視窗中按兩下外部程式碼,自動反編譯 .NET 程式碼。 如需詳細資訊,請參閱偵錯時從 .NET 元件產生原始程式碼。

.NET Just My Code

在 .NET 專案中,Just My Code 會使用符號 (.pdb) 檔案和程式最佳化,來分類使用者與非使用者程式碼。 .NET 偵錯工具會將最佳化的二進位檔和非載入的 .pdb 檔案視為非使用者程式碼。

三個編譯器屬性也會影響 .NET 偵錯工具視為使用者程式碼的內容:

.NET 偵錯工具會將所有其他程式碼視為使用者程式碼。

在 .NET 偵錯期間:

  • 非使用者程式碼上的 [偵錯]>[逐步執行] (或 F11) 會將程式碼逐步執行到使用者程式碼的下一行。
  • 非使用者程式碼上的 [偵錯]>[跳離函式] (或 Shift+F11) 會執行到使用者程式碼的下一行。

如果沒有更多的使用者程式碼,偵錯會繼續進行直到其結束、叫用另一個中斷點,或擲回錯誤。

如果偵錯工具在非使用者程式碼內中斷 (例如,您使用 [偵錯]>[全部中斷],並在非使用者程式碼中暫停),則 [沒有來源] 視窗會出現。 然後,您可以使用 [偵錯]>[步驟] 命令,移至使用者程式碼的下一行。

如果未處理的例外狀況發生在非使用者程式碼中,偵錯工具就會在產生例外狀況的使用者程式碼行中斷。

如果針對例外狀況啟用第一個可能發生的例外狀況,則會在原始程式碼中以綠色醒目提示呼叫使用者程式碼行。 [呼叫堆疊] 視窗會顯示標記為 [外部程式碼] 的標註框架。

C++ Just My Code

從 Visual Studio 2017 15.8 版開始,也支援 Just My Code for code 逐步執行。 此功能也需要使用 /JMC (Just My Code 偵錯) 編譯器參數。 預設會在 C++ 專案中啟用此參數。 對於 Call Stack 視窗和 Just My Code 中的呼叫堆疊支援,不需要 /JMC 切換。

若要分類為使用者程式碼,偵錯工具必須載入二進位檔 (其中包含使用者程式碼) 的 PDB (使用 [模組] 視窗來檢查此情況)。

針對呼叫堆疊行為,例如在 [呼叫堆疊] 視窗中,C++ 中的 Just My Code 只會將這些函式視為「非使用者程式碼」

  • 在其符號檔中已移除來源資訊之函式。
  • 其中符號檔案指示沒有與堆疊框架對應的來源檔案的函式。
  • *.natjmc 檔案中指定的函式,這些檔案位於 %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 資料夾中。

針對程式碼逐步執行行為,C++ 中的 Just My Code 只會將這些函式視為「非使用者程式碼」

  • 尚未在偵錯工具中載入其對應的 PDB 檔案的函式。
  • *.natjmc 檔案中指定的函式,這些檔案位於 %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 資料夾中。

注意

如需 Just My Code 中的程式碼逐步執行支援,必須使用 Visual Studio 15.8 Preview 3 或更新版本中的 MSVC 編譯器來編譯 C++ 程式碼,而且必須啟用 /JMC 編譯器參數 (預設為啟用)。 如需其他詳細資料,請參閱自訂 C++ 呼叫堆疊和程式碼逐步執行行為和這篇部落格文章。 對於使用舊版編譯器編譯的程式碼,.natstepfilter 檔案是自訂程式碼逐步執行的唯一方法,這與 Just My Code 無關。 請參閱自訂 C++ 逐步執行行為

在 C++ 偵錯期間,預設會跳過非使用者程式碼。 在 C++ 偵錯期間:

  • 如果從非使用者程式碼呼叫 [逐步執行],則非使用者程式碼上的 [偵錯]>[逐步執行] (或 F11) 會逐步執行程式碼或執行到使用者程式碼的下一行。
  • 非使用者程式碼上的 [偵錯]>[跳離函式] (或 Shift+F11) 會執行到使用者程式碼的下一行 (在目前堆疊框架之外)。

如果沒有更多的使用者程式碼,偵錯會繼續進行直到其結束、叫用另一個中斷點,或擲回錯誤。

如果偵錯工具在非使用者程式碼內中斷 (例如,您使用 [偵錯]>[全部中斷],並在非使用者程式碼中暫停),則逐步執行會在非使用者程式碼中繼續。

如果偵錯工具叫用例外狀況,其會在例外狀況上停止,無論是在使用者程式碼中還是在非使用者程式碼中。 系統會忽略 [例外狀況] 對話方塊中的 [使用者未處理] 選項。

自訂 C++ 呼叫堆疊和程式碼逐步執行行為

針對 C++ 專案,您可以指定 [呼叫堆疊] 視窗會將其視為非使用者程式碼的模組、來源檔案和函式,方法是在 *.natjmc 檔案中指定這些項目。 如果您使用最新的編譯器,此自訂也適用於程式碼步進 (請參閱 C++ Just My Code)。

  • 若要為 Visual Studio 電腦的所有使用者指定非使用者程式碼,請將 .natjmc 檔案新增至 %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 資料夾。
  • 若要為個別使用者指定非使用者程式碼,請將 .natjmc 檔案新增至 %USERPROFILE%\My Documents\<Visual Studio version>\Visualizers 資料夾。

.natjmc 檔案是具有下列語法的 XML 檔案:

<?xml version="1.0" encoding="utf-8"?>
<NonUserCode xmlns="http://schemas.microsoft.com/vstudio/debugger/jmc/2015">

  <!-- Modules -->
  <Module Name="ModuleSpec" />
  <Module Name="ModuleSpec" Company="CompanyName" />

  <!-- Files -->
  <File Name="FileSpec"/>

  <!-- Functions -->
  <Function Name="FunctionSpec" />
  <Function Name="FunctionSpec" Module ="ModuleSpec" />
  <Function Name="FunctionSpec" Module ="ModuleSpec" ExceptionImplementation="true" />

</NonUserCode>

模組項目屬性

屬性 描述
Name 必要。 該模組的完整路徑。 您可以使用 Windows 萬用字元 ? (零或一個字元) 和 * (零或多個字元)。 例如,

<Module Name="?:\3rdParty\UtilLibs\*" />

會告知偵錯工具中的所有模組都視為 \3rdParty\UtilLibs 為外部程式碼的任何磁碟機上。
Company 選擇性。 發行內嵌於可執行檔之模組的公司名稱。 您可以使用這個屬性使模組意義清楚。

檔案項目屬性

屬性 描述
Name 必要。 要視為外部程式碼的原始程式檔之完整路徑。 在指定路徑時,您可以使用 Windows 萬用字元 ?*

Function 項目屬性

屬性 描述
Name 必要。 要視為外部程式碼的函式之完整名稱。 在指定路徑時,您可以使用 Windows 萬用字元 ?*
Module 選擇性。 包含此函式的模組名稱或完整路徑。 您可以使用這個屬性使具有相同名稱的函式意義清楚。
ExceptionImplementation 當設定為 true 時,此呼叫堆疊會顯示擲回例外狀況的函式,而不是這個函式。

自訂與 Just My Code 設定無關的 C++ 逐步執行行為

在 C++ 專案中,您可以指定要逐步執行的函式,方法是將這些函式列為 *.natstepfilter 檔案中的 NoStepInto 函式。 在 *.natstepfilter 檔案中列出的函式不依賴於 Just My Code 設定。 NoStepInto 函式會告知偵錯工具逐步執行函式,即使其呼叫一些 StepInto 函式或其他使用者程式碼也一樣。 不同於 .natjmc 中列出的函式,偵錯工具會逐步執行 NoStepInto 函式內使用者程式碼的第一行。

  • 若要為所有本機 Visual Studio 使用者指定非使用者程式碼,請將 .natstepfilter 檔案新增至 %VsInstallDirectory%\Common7\Packages\Debugger\Visualizers 資料夾。
  • 若要為個別使用者指定非使用者程式碼,請將 .natstepfilter 檔案新增至 %USERPROFILE%\My Documents\<Visual Studio version>\Visualizers 資料夾。

注意

某些協力廠商延伸模組可能會停用 .natstepfilter 功能。

.natstepfilter 檔案是具有下列語法的 XML 檔案:

<?xml version="1.0" encoding="utf-8"?>
<StepFilter xmlns="http://schemas.microsoft.com/vstudio/debugger/natstepfilter/2010">
    <Function>
        <Name>FunctionSpec</Name>
        <Action>StepAction</Action>
    </Function>
    <Function>
        <Name>FunctionSpec</Name>
        <Module>ModuleSpec</Module>
        <Action>StepAction</Action>
    </Function>
</StepFilter>

元素 描述
Function 必要。 指定一個或多個函式做為非使用者函式。
Name 必要。 指定要比對的完整函式名稱之 ECMA-262 格式化規則運算式。 例如:

<Name>MyNS::MyClass::.*</Name>

告知偵錯工具在 MyNS::MyClass 中的所有方法要視為非使用者程式碼。 該比對會區分大小寫。
Module 選擇性。 指定包含此函式的模組之完整路徑的 ECMA-262 格式化規則運算式。 該比對不區分大小寫。
Action 必要。 區分大小寫值的其中之一:

NoStepInto - 告知偵錯工具逐步執行函式。
StepInto - 告知偵錯工具要逐步執行函式,覆寫相符函式的任何其他 NoStepInto

有關 .natstepfilter.natjmc 檔案的其他資訊

  • 從 Visual Studio 2022 17.6 版開始,您可以將 .natjmc.natstepfilter 檔案直接新增至解決方案或專案。

  • 偵錯工具的 Output 視窗中不會報告 .natstepfilter 和 .natjmc 檔案中的語法錯誤。

  • .natvis 檔案不同,.natstepfilter .natjmc 檔案不會重新熱載入。 相反地,這些檔案會在偵錯工作階段即將開始時重新載入。

  • 針對範本函式,在名稱中使用 &lt;.*&gt;&lt;.* 可能會有所幫助。

JavaScript Just My Code

JavaScript Just My Code 將程式碼分類為下列類別其中一類,來控制逐步執行和呼叫堆疊顯示:

分類 描述
MyCode 您所擁有並控制的使用者程式碼。
LibraryCode 來自程式庫,您經常使用且應用程式為正常運作所依賴的非使用者程式碼 (例如 jQuery)。
UnrelatedCode 您應用程式中您未擁有且您應用程式不依賴其正常運作的非使用者程式碼。 例如,顯示廣告的廣告 SDK 可能是 UnrelatedCode。

JavaScript 偵錯工具會依下列順序將程式碼分類為使用者或非使用者:

  1. 預設分類。

    • 透過將字串傳遞至主機所提供 eval 函式來執行的指令碼是 MyCode
    • 透過將字串傳遞至 Function 建構函式來執行的指令碼是 LibraryCode
    • 架構參考 (例如 WinJS 或 Azure SDK) 中的指令碼是 LibraryCode
    • 透過將字串傳遞至 setTimeoutsetImmediatesetInterval 函式來執行的指令碼是 UnrelatedCode
  2. 目前專案的 mycode.json 檔案中的分類。

每個分類步驟都會覆寫先前的步驟。

所有其他程式碼會分類為 MyCode

您可以修改預設分類,並將特定檔案和 URL 分類為使用者或非使用者程式碼,方法是將名為 mycode.json.json 檔案新增至 JavaScript 專案的根資料夾。 請參閱自訂 JavaScript Just My Code

在 JavaScript 偵錯期間:

  • 如果函式是非使用者程式碼,[偵錯]>[逐步執行] (或 F11) 的行為與 [偵錯]>[逐步執行] (或 F10) 相同。
  • 如果步驟在非使用者 (LibraryCodeUnrelatedCode) 程式碼開始,則逐步執行的行為暫時會如同 Just My Code 未啟用的情況一樣。 當您倒退回使用者程式碼時,Just My Code 逐步執行隨即重新啟用。
  • 當使用者程式碼步驟導致離開目前的執行內容時,偵錯工具會在下一個執行的使用者程式碼行停止。 例如,如果回呼於 LibraryCode 程式碼中執行,則偵錯工具會繼續執行,直到下一行使用者程式碼執行為止。
  • [跳離函式] (或 Shift+F11) 會在使用者程式碼的下一行停止。

如果沒有更多的使用者程式碼,偵錯會繼續進行直到其結束、叫用另一個中斷點,或擲回錯誤。

程式碼中設定的中斷點一律叫用,但程式碼會進行分類。

  • 如果 debugger 關鍵字出現在 LibraryCode 中,則偵錯工具一律中斷。
  • 如果 debugger 關鍵字出現在 UnrelatedCode 中,偵錯工具不會停止。

如果 MyCodeLibraryCode 程式碼中發生未處理的例外狀況,偵錯工具一律會中斷。

如果 UnrelatedCode 中發生未處理的例外狀況,而且 MyCodeLibraryCode 位於呼叫堆疊上,偵錯工具就會中斷。

如果針對例外狀況啟用第一次可能發生的例外狀況,且例外狀況發生在 LibraryCodeUnrelatedCode 中:

  • 如果已經處理此例外狀況,則偵錯工具不會中斷。
  • 如果未處理例外狀況,偵錯工具會中斷。

自訂 JavaScript Just My Code

若要將單一 JavaScript 專案的使用者和非使用者程式碼分類,您可以將名為 mycode.json.json 檔案新增至專案的根資料夾。

mycode.json 檔案不需要列出所有索引鍵值對。 MyCodeLibrariesUnrelated 值可以是空陣列。

Mycode.json 檔案會使用下列語法:

{
    "Eval" : "Classification",
    "Function" : "Classification",
    "ScriptBlock" : "Classification",
    "MyCode" : [
        "UrlOrFileSpec",
        . . .
        "UrlOrFileSpec"
    ],
    "Libraries" : [
        "UrlOrFileSpec",
        . .
        "UrlOrFileSpec"
    ],
    "Unrelated" : [
        "UrlOrFileSpec",
        . . .
        "UrlOrFileSpec"
    ]
}

Eval、Function 和 ScriptBlock

EvalFunctionScriptBlock 索引鍵值組會判斷要如何分類動態產生的程式碼:

名稱 描述
Eval 藉由傳遞字串給主機所提供的 eval 函式來執行的指令碼。 根據預設,Eval 指令碼會分類為 MyCode
Function 藉由傳遞字串給 Function 建構函式來執行的指令碼。 根據預設,Function 指令碼會分類為 LibraryCode
ScriptBlock 由傳遞字串給 setTimeoutsetImmediatesetInterval 函式所執行的指令碼。 根據預設,ScriptBlock 指令碼會分類為 UnrelatedCode

您可以變更此值為這些關鍵字之一:

  • MyCode 將此指令碼分類為 MyCode
  • Library 將此指令碼分類為 LibraryCode
  • Unrelated 將此指令碼分類為 UnrelatedCode

MyCode、Libraries 和 Unrelated

MyCodeLibrariesUnrelated 索引鍵值組會指定您在分類中要包含的 URL 或檔案:

名稱 描述
MyCode 分類為 MyCode 的 URL 陣列或檔案陣列。
Libraries 分類為 LibraryCode 的 URL 陣列或檔案陣列。
Unrelated 分類為 UnrelatedCode 的 URL 陣列或檔案陣列。

URL 或檔案字串可有一或多個 * 字元,與零或多個字元相符。 * 與規則運算式 .* 相同。