為 C++實作 NatVis 自定義內部函數
在本文中,您將瞭解在 NatVis 視覺效果中實作自定義內建函式的指導方針。 如需 NatVis 的詳細資訊,請參閱 建立 C++ 物件的自定義檢視。
語法
NatVis 檔案可以使用下列語法來定義內建函式:
<Intrinsic Name="Func" Expression="arg + 1">
<Parameter Name="arg" Type="int" />
</Intrinsic>
定義存在之後,任何調試程式表達式都可能會呼叫 函式,就像任何其他函式一樣。 例如,使用上述 NatVis 定義,表達式 Func(3)
評估為 4。
<Intrinsic>
元素可以出現在檔案層級上,或是在 <Type>
元素內,並在其他任何元素之前。 定義 <Type>
內建函式定義該型別的成員函式,而定義於 <Type>
外部的內建函式則定義全域函式。 例如:
<?xml version="1.0" encoding="utf-8"?>
<AutoVisualizer xmlns="http://schemas.microsoft.com/vstudio/debugger/natvis/2010">
<Type Name="std::vector<*>">
<Intrinsic Name="size" Expression="_Mypair._Myval2._Mylast - _Mypair._Myval2._Myfirst" />
<Intrinsic Name="capacity" Expression="_Mypair._Myval2._Myend - _Mypair._Myval2._Myfirst" />
</Type>
<Intrinsic Name="LOWORD" Expression="arg & 0xFFFF">
<Parameter Name="arg" Type="unsigned int" />
</Intrinsic>
</AutoVisualizer>
在上述範例中,size()
和 capacity()
定義為 std::vector
類別的成員函式,因此每當表達式評估 vector.size()
時,它實際上就會評估 vector._Mypair._Myval2._Mylast - vector._Mypair._Myval2._Myfirst
,而不是執行 func-eval。
同樣地,LOWORD(0x12345678)
傳回 0x5678
,同時不使用 func-eval。
如需另一個範例,請參閱 內建擴充。
使用內部函數的指導方針
使用內部函數時,請注意下列指導方針:
內部函數可以重載,不論是與 PDB 定義的函數重載,或是彼此間重載。
當內部函數與具有相同名稱和自變數清單的 PDB 定義函式衝突時,內部函數將會獲勝。 如果相等的內建函式存在,就無法對 PDB 函式進行運算評估。
您無法取得內部函數的位址;您只能呼叫它。
內建成員函式必須屬於對應類型的預設視圖,才能在表達式中被運算。 例如,具有
IncludeView
條件約束的類型專案可能不會指定內部函數。內部函數可以從任何表達式呼叫,包括 NatVis 表達式。 內部函數也可以彼此呼叫。 不過,目前不支援使用遞歸的內部函數。 例如:
<!-- OK --> <Intrinsic Name="Func2" Expression="this->Func3(arg + 1)"> <Parameter Name="arg" Type="int" /> </Intrinsic> <Intrinsic Name="Func3" Expression="arg + 1"> <Parameter Name="arg" Type="int"/> </Intrinsic> <!-- Unsupported --> <Intrinsic Name="Func2" Expression="this->Func3(arg + 1)"> <Parameter Name="arg" Type="int" /> </Intrinsic> <Intrinsic Name="Func3" Expression="Func2(arg + 1)"> <Parameter Name="arg" Type="int"/> </Intrinsic> <!-- Also unsupported--> <Intrinsic Name="Fib" Expression="(n <= 1) ? 1 : Fib(n - 1) + Fib(n - 2)"> <Parameter Name="n" Type="int"/> </Intrinsic>
根據預設,內部函數會假設為無副作用。 也就是說,可以在不允許副作用的內容中呼叫它們,而且不允許實作表達式包含副作用。
定義可以藉由在宣告中指定
SideEffect
屬性來覆寫此行為。 如果函式標示為有副作用,則允許實作表達式中的副作用。 不過,不再允許在禁止副作用的內容中叫用 函式。當兩個內部函數的定義彼此衝突時(同名、同一個簽章),最後一個函式就會獲勝(在同一個檔案內)。
在不同的檔案中,具有較高優先順序的檔案實例會獲勝(專案高於使用者目錄,高於安裝目錄)。 如果較高優先順序的定義包含未剖析的表達式,則會忽略該定義,並改用下一個最高的優先順序定義。
實作內建函式的指導方針
內部函數支援兩種可能的實作形式:
以表達式為基礎的
NatVis 檔案會定義一個表達式,該表達式解析為函式的傳回值。 表達式可以使用任何傳入的引數,這些引數被宣告為
<Parameter>
元素。 類別內定義的函式也會假設為 「實例」函式,而且也可以存取 「this」 指標。延伸型
NatVis 檔案提供叫用調試程式延伸模組以實際評估函式的指示。 調試程式延伸模組具有 Concord API 的完整存取權,而且能夠執行 NatVis 運算式內無法執行的工作。
若要提供以延伸為基礎的實作,<Intrinsic>
元素應該省略 Expression
屬性,而是提供 SourceId
、LanguageId
、Id
和 ReturnType
屬性,如下列範例所示:
<Intrinsic Name="MyFunc" SourceId="a665fa54-6e7d-480e-a80b-1fc1202e9646" LanguageId="3a12d0b7-c26c-11d0-b442-00a0244a1dd2" Id="1000" ReturnType="double">
<Parameter Type="int" />
<Parameter Type="int" />
<Parameter Type="int" />
</Intrinsic>
若要實作 函式,調試程式擴充功能必須使用符合 NatVis 檔案中 <Intrinsic>
元素對應值的 LanguageId
和 SourceId
篩選來實作 IDkmIntrinsicFunctionEvaluator140
介面。 呼叫 函式時,呼叫會轉譯為元件的 Execute()
方法:
STDMETHOD(Execute)(
_In_ Evaluation::IL::DkmILExecuteIntrinsic* pExecuteIntrinsic,
_In_ Evaluation::DkmILContext* pILContext,
_In_ Evaluation::IL::DkmCompiledILInspectionQuery* pInspectionQuery,
_In_ const DkmArray<Evaluation::IL::DkmILEvaluationResult*>& Arguments,
_In_opt_ DkmReadOnlyCollection<Evaluation::DkmCompiledInspectionQuery*>* pSubroutines,
_Out_ DkmArray<Evaluation::IL::DkmILEvaluationResult*>* pResults,
_Out_ Evaluation::IL::DkmILFailureReason* pFailureReason
);
元件會透過 Arguments
參數接收每個參數的位元組。 如果所討論的函式是成員函式,則 this
指標會先出現,然後是顯式的參數。 元件應該透過在 pResults
中配置單一元素的陣列,來傳回結果,並儲存傳回值的位元組。
使用下列指南來撰寫函式:
“混搭”這兩種實作形式是非法的。 也就是說,您無法同時包含運算式和來源標識碼。
允許指定表達式型實作的傳回型別,但不需要。 如果指定傳回型別,表達式的傳回型別必須完全符合它(不允許隱含轉換)。 如果未指定傳回型別,則會從表達式推斷傳回型別。 任何擴充型實作都必須在 NatVis 檔案中陳述傳回類型。
允許對其他內部函數的非遞歸呼叫。 不允許遞歸。
如果函式有副作用,您必須在宣告中指定
SideEffect="true"
。 表達式型實作在表達式中具有副作用並不合法,而不需要宣告函式具有副作用。 叫用基於擴充的實作來產生副作用而不宣告函式具有副作用,是未定義行為,應予以避免。允許 Varargs 內部函數。 若要宣告 varargs 函式,請在宣告中指定
Varargs="true"
。 雖然表達式型實作宣告vararg
函式是合法的,但目前只有擴充型實作有方法可存取變數自變數。 使用擴充型實作時,Execute()
函式會接收實際傳入的所有自變數,而不只是宣告的自變數。不支援使用類別/結構/等位型別做為自變數類型的內建函式。 傳回類別/結構/聯合體類型是可以的。 (類別/結構/共用體類型的指標或參考是可以作為參數類型的)。
自定義呼叫內建函式的圖示。
根據預設,當您呼叫內建函式時,表達式會在與函數調用相關聯的 Watch 視窗中提供粉紅色菱形圖示。 您可以使用下列其中一個值來指定 Category
屬性,以覆寫此行為:
- 方法。 使用粉紅色菱形圖示,通常與方法呼叫搭配使用(預設值)。
- 財產。 使用通常與屬性搭配的黑色扳手圖示。
- 數據。 使用藍色菱形圖示,通常與數據搭配使用。
藉由結合內建函式與 <Item>
元素,即可撰寫 NatVis 檔案,其中項目表達式具有扳手屬性圖示:
<Type Name="MyClass">
<Intrinsic Name="GetValue" ReturnType="int" Expression="m_value" Category="Property" />
<Expand>
<Item Name="Value">this->GetValue()</Item>
</Expand>
</Type>
注意
將圖示選擇放在函式層級,而不是 <Item>
層級,可避免在評估完整名稱時遺失圖示自定義的問題。 因為完整名稱包含對函式的呼叫,所以其圖示自定義與 <Item>
本身相同。