本主題描述如何使用調試程序數據模型C++調試程序數據模型C++腳本,以支援使用腳本的調試程式引擎自動化。
在調試程序數據模型中 腳稿管理
除了數據模型管理員作為物件建立和擴充性中央授權單位的角色之外,它也負責管理腳本的抽象概念。 從數據模型管理員的腳本管理員部分的觀點來看,腳本是可以動態載入、卸除和可能由提供者偵錯的腳本,以擴充或提供新的功能給數據模型。
腳本提供者是一個元件,會將語言(例如:NatVis、JavaScript 等...) 橋接至數據模型。 它會註冊一或多個擴展名(例如:“。NatVis“, ”.js“) 由提供者處理,允許調試程式用戶端或使用者介面,允許透過委派給提供者來載入具有該特定擴展名的腳本檔案。
Core Script Manager:IDataModelScriptManager
核心腳本管理員介面的定義如下。
DECLARE_INTERFACE_(IDataModelScriptManager, IUnknown)
{
STDMETHOD(GetDefaultNameBinder)(_COM_Outptr_ IDataModelNameBinder **ppNameBinder) PURE;
STDMETHOD(RegisterScriptProvider)(_In_ IDataModelScriptProvider *provider) PURE;
STDMETHOD(UnregisterScriptProvider)(_In_ IDataModelScriptProvider *provider) PURE;
STDMETHOD(FindProviderForScriptType)(_In_ PCWSTR scriptType, _COM_Outptr_ IDataModelScriptProvider **provider) PURE;
STDMETHOD(FindProviderForScriptExtension)(_In_ PCWSTR scriptExtension, _COM_Outptr_ IDataModelScriptProvider **provider) PURE;
STDMETHOD(EnumerateScriptProviders)(_COM_Outptr_ IDataModelScriptProviderEnumerator **enumerator) PURE;
}
GetDefaultNameBinder 方法會傳回數據模型的預設腳本名稱系結器。 名稱系結器是解析物件內容內名稱的元件。 例如,假設表達式 「foo.bar」,呼叫名稱系結器來解析物件 foo 內容中的名稱列。 此處傳回的系結器會遵循一組數據模型的默認規則。 腳本提供者可以使用這個系結器,在提供者之間提供名稱解析的一致性。
RegisterScriptProvider 方法會通知數據模型,新的腳本提供者存在,能夠將新語言橋接至數據模型。 呼叫此方法時,腳本管理員會立即回呼指定的腳本提供者,並詢問其所管理的腳本屬性。 如果已在指定文本提供者所指出的名稱或擴展名下註冊提供者,這個方法將會失敗。 只有單一腳本提供者可以註冊為特定名稱或擴展名的處理程式。
UnregisterScriptProvider 方法會復原 RegisterScriptProvider 方法的呼叫。 Inpassed 腳本提供者所提供的名稱和擴展名將不再與其相關聯。 請務必注意,即使在取消註冊之後,腳本提供者仍有大量的未完成 COM 參考。 這個方法只會防止載入/建立指定之腳本提供者所管理的型別腳本。 如果由該提供者載入的腳本仍然載入,或已作調試程序的物件模型(或數據模型),這些作可能仍會參考回腳本。 腳本中可能會有直接參考建構的數據模型、方法或物件。 腳本提供者必須準備好處理。
FindProviderForScriptType 方法會在腳本管理員中搜尋具有腳本類型字串的提供者,如此方法所示。 如果找不到,這個方法將會失敗;否則,這類腳本提供者會傳回給呼叫端。
EnumerateScriptProviders 方法會傳回列舉值,列舉已透過先前呼叫 RegisterScriptProvider 方法向腳本管理員註冊的每個腳本提供者。
腳本提供者列舉:IDataModelScriptProviderEnumerator
EnumerateScriptProviders 方法會傳回下列形式的列舉值:
DECLARE_INTERFACE_(IDataModelScriptProviderEnumerator, IUnknown)
{
STDMETHOD(Reset)() PURE;
STDMETHOD(GetNext)(_COM_Outptr_ IDataModelScriptProvider **provider) PURE;
}
Reset 方法會將列舉值移至傳回第一個專案之前所在的位置。
GetNext 方法會將列舉值向前移動一個專案,並傳回位於該元素的腳本提供者。 當列舉值叫用列舉的結尾時,將會傳回E_BOUNDS。 在收到此錯誤之後呼叫 GetNext 方法會繼續無限期地傳回E_BOUNDS。
調試程序數據模型C++用於腳本的主機介面
在腳本 中 主機的角色
偵錯主機會公開一系列非常低層級的介面,以瞭解其偵錯目標類型系統的本質、評估其偵錯目標語言中的表達式等等...一般而言,它與較高層級的建構無關,例如腳本。 這類會留給整體調試程式應用程式,或提供給提供這些功能的擴充功能。 不過,有例外狀況。 任何想要參與數據模型所提供整體腳本體驗的偵錯主機,都必須實作幾個簡單的介面,以提供腳本的內容。 實際上,偵錯主機會控制它想要腳本環境將函式和其他腳本提供的功能放在數據模型命名空間內的位置。 參與此程式可讓主機允許或不允許在 中使用這些函式,例如其運算式評估工具。 從主機的觀點來看,涉及的介面如下:
| 介面 | 說明 |
|---|---|
| IDebugHostScriptHost | 介面,表示偵錯主機參與腳本環境的功能。 這個介面可讓您建立內容,以通知腳本引擎放置物件的位置。 |
| IDataModelScriptHostContext | 腳本提供者用來作為腳本內容的容器的主機介面。 除了腳本對調試程式應用程式物件模型執行的作以外,腳本的內容如何由特定的偵錯主機決定。 這個介面可讓腳本提供者取得放置其內容位置的相關信息。 如需詳細資訊,請參閱本主題稍後的 數據模型C++腳本介面。 |
偵錯主機的腳本主機:IDebugHostScriptHost
IDebugHostScriptHost 介面是腳本提供者用來從偵錯主機取得新建立腳本的內容的介面。 此內容包含物件(由偵錯主機提供),腳本提供者可以在數據模型與腳本環境之間放置任何網橋。 例如,這類網橋可能是叫用腳本函式的數據模型方法。 這樣做可讓數據模型端的呼叫端利用IModelMethod 介面上的 Call 方法來叫用腳本方法。
IDebugHostScriptHost 介面的定義如下。
DECLARE_INTERFACE_(IDebugHostScriptHost, IUnknown)
{
STDMETHOD(CreateContext)(_In_ IDataModelScript* script, _COM_Outptr_ IDataModelScriptHostContext** scriptContext) PURE;
}
CreateContext 方法是由腳本提供者呼叫,以建立要放置腳本內容的新內容。 這類內容是由數據模型C++腳本介面頁面上詳細描述的 IDataModelScriptHostContext 介面來表示。
調試程序數據模型C++腳本介面
腳本和腳本介面
數據模型的整體架構可讓第三方定義某些語言與數據模型物件模型之間的橋樑。 一般而言,橋接的語言是腳本語言,因為數據模型的環境非常動態。 定義及實作數據模型之語言與物件模型之間的這個網橋的元件稱為腳本提供者。 初始化時,腳本提供者會向數據模型管理員的腳本管理員部分,以及管理擴充性的任何介面,後續允許編輯、載入、卸除,以及可能偵錯寫入腳本提供者所管理語言的腳本。
請注意,Windows 的偵錯工具目前定義兩個腳本提供者。
- NatVis 提供者。 此提供者內嵌在 NatVis XML 與數據模型之間的 DbgEng.dll 和網橋內,允許原生/語言數據類型的視覺效果。
- JavaScript 提供者。 此提供者包含在舊版調試程式擴充功能中:JsProvider.dll。 它會在以 JavaScript 語言和數據模型撰寫的腳本之間橋接,以允許任意形式的調試程式控制和擴充性。
新的提供者可以寫入至數據模型,以橋接其他語言(例如:Python 等...)。 這類目前會封裝在舊版調試程式延伸模組中,以供載入之用。 腳本提供者本身應該將舊版引擎介面的相依性降到最低,而且應該只盡可能利用數據模型 API。 這可讓提供者更容易地移植到其他環境。
有兩種與腳本提供者相關的介面類別。 介面的第一類是用於腳本提供者的一般管理,以及其管理的腳本。 介面的第二個類別是支援腳本偵錯。 雖然第一組的支持是強制性的,但對第二個集合的支持是選擇性的,而且對每個提供者來說可能都沒有意義。
一般管理介面包括:
| 介面 | 說明 |
|---|---|
| IDataModelScriptProvider | 腳本提供者必須實作的核心介面。 這是向數據模型管理員的腳本管理員部分註冊的介面,以便公告提供者支援特定類型的腳本,並針對特定擴展名註冊 |
| IDataModelScript | 由提供者所管理之特定腳本的抽象概念。 載入或編輯的每個腳本都有個別的 IDataModelScript 實例 |
| IDataModelScriptClient | 腳本提供者用來將資訊傳達給使用者介面的用戶端介面。 腳本提供者不會實作這個介面。 裝載想要使用腳本提供者之數據模型的應用程式。 文稿提供者會呼叫文本用戶端的方法,以報告狀態、錯誤等等... |
| IDataModelScriptHostContext | 腳本提供者用來作為腳本內容的容器的主機介面。 除了腳本對調試程式應用程式物件模型執行的作以外,腳本的內容如何由特定的偵錯主機決定。 這個介面可讓腳本提供者取得放置其內容位置的相關信息。 |
| IDataModelScriptTemplate | 腳本提供者可以提供一或多個範本,做為使用者撰寫腳本的起點。 提供內建編輯器的調試程式應用程式,可以使用提供者透過這個介面公告的範本內容預先填入新的腳本。 |
| IDataModelScriptTemplateEnumerator | 腳本提供者實作的列舉值介面,以便公告它支援的所有範本。 |
| IDataModelNameBinder | 名稱系結器 -- 物件,可將內容中的名稱與值產生關聯。 對於指定表達式,例如 「foo.bar」,名稱系結器可以在物件 「foo」 的內容中系結名稱 「bar」。,併產生值或參考。 名稱系結器通常不是由腳本提供者實作;相反地,您可以從數據模型取得預設系結器,並由腳本提供者使用 |
偵錯介面包括:
| 介面 | 說明 |
|---|---|
| IDataModelScriptDebug | 腳本提供者必須提供的核心介面,才能讓腳本可偵錯。 如果腳本可偵錯,IDataModelScript 介面的實作類別必須查詢 IDataModelScriptDebug 的 QueryInterface。 |
| IDataModelScriptDebugClient | 想要提供腳本偵錯功能的使用者介面會實作 IDataModelScriptDebugClient 介面。 文稿提供者會利用這個介面來回傳遞偵錯資訊(例如:發生的事件、斷點等...) |
| IDataModelScriptDebugStack | 腳本提供者會實作這個介面,以將呼叫堆棧的概念公開給腳本調試程式。 |
| IDataModelScriptDebugStackFrame | 腳本提供者會實作這個介面,以公開呼叫堆棧內特定堆疊框架的概念。 |
| IDataModelScriptDebugVariableSetEnumerator | 腳本提供者會實作這個介面來公開一組變數。 此集合可能代表函式的參數集、局部變數集,或特定範圍內的變數集。 確切的意義取決於取得介面的方式。 |
| IDataModelScriptDebugBreakpoint | 腳本提供者會實作這個介面,以公開腳本中特定斷點的概念和控制。 |
| IDataModelScriptDebugBreakpointEnumerator | 腳本提供者會實作這個 來列舉腳本中目前存在的所有斷點(不論是否已啟用)。 |
核心腳本提供者:IDataModelScriptProvider
任何想要成為腳本提供者的延伸模組都必須提供 IDataModelScriptProvider 介面的實作,並透過 RegisterScriptProvider 方法向數據模型管理員的腳本管理員部分註冊這類延伸模組。 必須實作的這個核心介面定義如下。
DECLARE_INTERFACE_(IDataModelScriptProvider, IUnknown)
{
STDMETHOD(GetName)(_Out_ BSTR *name) PURE;
STDMETHOD(GetExtension)(_Out_ BSTR *extension) PURE;
STDMETHOD(CreateScript)(_COM_Outptr_ IDataModelScript **script) PURE;
STDMETHOD(GetDefaultTemplateContent)(_COM_Outptr_ IDataModelScriptTemplate **templateContent) PURE;
STDMETHOD(EnumerateTemplates)(_COM_Outptr_ IDataModelScriptTemplateEnumerator **enumerator) PURE;
}
GetName 方法會傳回提供者透過 SysAllocString 方法配置的字串所管理的腳本類型(或語言)名稱。 呼叫端負責透過 SysFreeString 釋放傳回的字串。 可能從此方法傳回的字串範例為 “JavaScript” 或 “NatVis”。 傳回的字串可能會出現在裝載數據模型的調試程式應用程式的使用者介面中。 沒有兩個腳本提供者可以傳回相同的名稱(不區分大小寫)。
GetExtension 方法會傳回此提供者所管理之腳本的擴展名(不含點),做為透過 SysAllocString 方法配置的字串。 裝載數據模型的調試程式應用程式(具有腳本支援)會將開啟具有此延伸模組的腳本檔案委派給腳本提供者。 呼叫端負責透過 SysFreeString 釋放傳回的字串。 可能從此方法傳回的字串範例為 「js」 或 「NatVis」。
呼叫 CreateScript 方法以建立新的腳本。 每當呼叫此方法時,腳本提供者必須傳回由傳回的 IDataModelScript 介面所代表的新和空白腳本。 請注意,不論使用者介面是建立新的空白腳本以供使用者編輯,還是調試程式應用程式正在從磁碟載入腳本,都會呼叫此方法。 提供者不會參與檔案 I/O。 它只會透過傳遞至 IDataModelScript 上方法的數據流來處理來自主控應用程式的要求。
GetDefaultTemplateContent 方法會傳回提供者預設範本內容的介面。 這是腳本提供者想要在新建立腳本的編輯視窗中預先填入的內容。 如果腳本提供者沒有範本(或沒有指定為預設內容的範本內容),腳本提供者可能會從此方法傳回E_NOTIMPL。
EnumerateTemplates 方法會傳回列舉值,其能夠列舉腳本提供者所提供的各種範本。 範本內容是建立新腳本時,腳本提供者想要「預先填入」到編輯視窗中的內容。 如果支援多個不同的範本,則可以命名這些範本(例如:「命令式腳本」、「擴充腳本」,以及裝載數據模型的調試程式應用程式,可以選擇如何將「範本」呈現給使用者。
核心腳本介面:IDataModelScript
管理提供者所實作之個別腳本的主要介面是 IDataModelScript 介面。 當用戶端想要建立新的空白腳本,並在 IDataModelScriptProvider 上呼叫 CreateScript 方法時,會傳回實作這個介面的元件。
提供者所建立的每個腳本都應該位於獨立的尋址接收器中。 除了透過數據模型與外部對象的明確互動之外,一個腳本不應該影響另一個腳本。 例如,兩個腳本都可以擴充某些類型或概念(例如調試程式的流程概念)。 然後,任一個腳本都可以透過外部進程物件存取彼此的欄位。
介面的定義如下。
DECLARE_INTERFACE_(IDataModelScript, IUnknown)
{
STDMETHOD(GetName)(_Out_ BSTR *scriptName) PURE;
STDMETHOD(Rename)(_In_ PCWSTR scriptName) PURE;
STDMETHOD(Populate)(_In_ IStream *contentStream) PURE;
STDMETHOD(Execute)(_In_ IDataModelScriptClient *client) PURE;
STDMETHOD(Unlink)() PURE;
STDMETHOD(IsInvocable)(_Out_ bool *isInvocable) PURE;
STDMETHOD(InvokeMain)(_In_ IDataModelScriptClient *client) PURE;
}
GetName 方法會透過 SysAllocString 函式傳回腳本的名稱做為配置的字串。 如果腳本還沒有名稱,方法應該會傳回 Null BSTR。 在此情況下,它不應該失敗。 如果腳本是透過對 Rename 方法的呼叫明確重新命名,GetName 方法應該會傳回新指派的名稱。
Rename 方法會將新名稱指派給腳本。 腳本實作負責儲存此名稱,並在對 GetName 方法的任何呼叫時傳回它。 當使用者介面選擇將腳本另存成新名稱時,通常會呼叫這個名稱。 請注意,重新命名腳本可能會影響主控應用程式選擇投影腳本內容的位置。
用戶端會呼叫 Populate 方法,以便變更或同步處理腳本的「內容」。 這是對腳本提供者所發出,腳本程式代碼已變更的通知。 請務必注意,這個方法不會造成腳本的執行,也不會對腳本作的任何對象進行變更。 這隻是腳本提供者的通知,腳本的內容已變更,以便它可以同步處理自己的內部狀態。
Execute 方法會執行腳本的內容,如上一次成功的 Populate 呼叫所決定,並根據該內容修改調試程序的物件模型。 如果語言(或腳本提供者)定義「主要函式」,則作者想要在使用者介面中按兩下虛構的「執行腳本」按鈕時呼叫該函式,則不會在「執行」作業期間呼叫這類「主要函式」。 「執行」作業可以視為只執行初始化和物件模型操作(例如:執行根程式代碼並設定擴充點)。
Unlink 方法會復原 Execute 作業。 在執行腳本期間建立的任何物件模型操作或擴充點都是復原的。 取消連結作業之後,腳本可能會透過呼叫 Execute 重新執行,或可能釋放。
IsInvocable 方法會傳回腳本是否可叫用 ,也就是說,它是否有其語言或提供者所定義的「main 函式」。 這類「主要函式」在概念上是腳本作者想要在使用者介面中按下虛構的 [執行腳本] 按鈕時所要呼叫的。
如果腳本具有「main 函式」,其打算從 UI 調用執行,它會透過 IsInvocable 方法的 true 傳回來表示。 然後,使用者介面可以呼叫 InvokeMain 方法,以實際「叫用」腳本。 請注意,這與執行 執行 不同,它會執行所有根程序代碼,並將腳本橋接至基礎主機的命名空間。
**腳本用戶端:IDataModelScriptClient **
裝載數據模型的應用程式,其想要管理腳本,並具有圍繞此概念的使用者介面(無論是圖形化或控制台),實作 IDataModelScriptClient 介面。 這個介面會在執行或叫用或腳本期間傳遞至任何腳本提供者,以便將錯誤和事件資訊傳回給使用者介面。
IDataModelScriptClient 介面的定義如下。
DECLARE_INTERFACE_(IDataModelScriptClient, IUnknown)
{
STDMETHOD(ReportError)(_In_ ErrorClass errClass, _In_ HRESULT hrFail, _In_opt_ PCWSTR message, _In_ ULONG line, _In_ ULONG position) PURE;
}
如果在腳本執行或叫用期間發生錯誤,腳本提供者會呼叫 ReportError 方法來通知使用者介面錯誤。
腳本的主機內容:IDataModelScriptHostContext
偵錯主機對數據模型腳本內容的方式和位置有一些影響。 預期每個腳本都會要求主機提供將網橋放置至腳本的內容(例如:可以呼叫的函式物件等等...)。透過在 IDebugHostScriptHost 上呼叫 CreateContext 方法並取得 IDataModelScriptHostContext 來擷取此內容。
IDataModelScriptHostContext 介面的定義如下。
DECLARE_INTERFACE_(IDataModelScriptHostContext, IUnknown)
{
STDMETHOD(NotifyScriptChange)(_In_ IDataModelScript* script, _In_ ScriptChangeKind changeKind) PURE;
STDMETHOD(GetNamespaceObject)(_COM_Outptr_ IModelObject** namespaceObject) PURE;
}
腳本提供者必須在特定作業發生時,在相關聯內容上呼叫 NotifyScriptChange 方法時通知偵錯主機。 這類作業定義為 ScriptChangeKind 列舉的成員
GetNamespaceObject 方法會傳回物件,腳本提供者可以在數據模型與腳本之間放置任何網橋。 例如,腳本提供者可能會將數據模型方法物件 (IModelMethod 介面 Boxed 到 IModelObject) 放置,其實作會呼叫腳本中對應的具名函式。
新建立腳本的 範本:IDataModelScriptTemplate
想要為新腳本呈現預先填入內容的腳本提供者(例如:協助使用者在調試程式使用者介面中撰寫腳本)可以藉由提供一或多個腳本範本來執行此動作。 這類範本是實作 IDataModelScriptTemplate 介面的元件,會透過腳本提供者上的 GetDefaultTemplate 方法或 EnumerateTemplates 方法傳回。
IDataModelScriptTemplate 介面的定義如下。
DECLARE_INTERFACE_(IDataModelScriptTemplate, IUnknown)
{
STDMETHOD(GetName)(_Out_ BSTR *templateName) PURE;
STDMETHOD(GetDescription)(_Out_ BSTR *templateDescription) PURE;
STDMETHOD(GetContent)(_COM_Outptr_ IStream **contentStream) PURE;
}
GetName 方法會傳回範本的名稱。 如果範本沒有名稱,E_NOTIMPL可能會失敗。 單一預設範本(如果存在的話)不需要有名稱。 所有其他範本都是。 這些名稱可能會顯示在使用者介面中,做為功能表的一部分,以選取要建立的範本。
GetDescription 方法會傳回範本的描述。 這類描述會在更具描述性的介面中向用戶呈現,以協助使用者了解範本的設計用途。 如果範本沒有描述,範本可能會從此方法傳回E_NOTIMPL。
GetContent 方法會傳回範本的內容(或程式代碼)。 如果使用者選取從此範本建立新的腳本,這會預先填入編輯視窗。 範本負責在用戶端可提取的內容上建立標準數據流(並傳回)。
提供者範本內容的列舉:IDataModelScriptTemplateEnumerator
腳本提供者可以提供一或多個範本,這些範本會在某些使用者介面中預先填入新建立的腳本。 如果提供這些範本中的任何一個,腳本提供者必須透過這些範本實作列舉值,此列舉值會在呼叫 EnumerateTemplates 方法時傳回。
這類列舉值是 IDataModelScriptTemplateEnumerator 介面的實作,其定義如下。
DECLARE_INTERFACE_(IDataModelScriptTemplateEnumerator, IUnknown)
{
STDMETHOD(Reset)() PURE;
STDMETHOD(GetNext)(_COM_Outptr_ IDataModelScriptTemplate **templateContent) PURE;
}
Reset 方法會將列舉值重設為第一次建立時所在的位置,在產生第一個範本之前。
GetNext 方法會將列舉值移至下一個範本,並傳回它。 列舉值結束時,列舉值會傳回E_BOUNDS。 一旦叫用E_BOUNDS標記,列舉值就會無限期地產生E_BOUNDS錯誤,直到進行 Reset 呼叫為止。
解析名稱的意義:IDataModelNameBinder
數據模型為腳本提供者提供標準方式,以判斷指定內容中指定名稱的意義(例如:判斷 foo.bar 的橫條意義),以在各種腳本提供者之間運作。 此機制稱為名稱系結器,並以 IDataModelNameBinder 介面表示。 這類系結器會封裝一組規則,說明名稱的解析方式,以及如何處理物件上多次定義名稱的衝突解析。 這些規則的一部分包括投影名稱(由數據模型新增的名稱)如何針對原生名稱解析(一個在所偵錯的語言類型系統中解析)。
為了在腳本提供者之間提供一定程度的一致性,數據模型的腳本管理員會提供預設名稱系結器』。 這個預設名稱系結器可以透過 IDataModelScriptManager 介面上的 GetDefaultNameBinder 方法呼叫來取得。 名稱系結器介面的定義如下。
DECLARE_INTERFACE_(IDataModelNameBinder, IUnknown)
{
STDMETHOD(BindValue)(_In_ IModelObject* contextObject, _In_ PCWSTR name, _COM_Errorptr_ IModelObject** value, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
STDMETHOD(BindReference)(_In_ IModelObject* contextObject, _In_ PCWSTR name, _COM_Errorptr_ IModelObject** reference, _COM_Outptr_opt_result_maybenull_ IKeyStore** metadata) PURE;
STDMETHOD(EnumerateValues)(_In_ IModelObject* contextObject, _COM_Outptr_ IKeyEnumerator** enumerator) PURE;
STDMETHOD(EnumerateReferences)(_In_ IModelObject* contextObject, _COM_Outptr_ IKeyEnumerator** enumerator) PURE;
}
BindValue 方法會根據一組系結規則,在指定的對象上執行相等的 contextObject.name。 這個系結的結果是值。 作為值,基礎腳本提供者無法使用 值來執行指派回名。
BindReference 方法類似於 BindValue,因為它也會根據一組系結規則,在指定的物件上執行相當於 contextObject.name。 不過,這個方法的系結結果是參考,而不是值。 做為參考,腳本提供者可以利用參考來執行指派回名。
EnumerateValues 方法會根據 BindValue 方法的規則,列舉一組名稱和值,這些名稱和值會根據 BindValue 方法的規則系結至物件。 不同於 IModelObject 上的 EnumerateKeys、EnumerateValues 和類似的方法,這些方法可能會傳回多個具有相同值的名稱(針對基類、父模型和類似),此列舉值只會傳回與 BindValue 和 BindReference 系結的特定名稱集。 名稱永遠不會重複。 請注意,透過名稱系結器列舉物件的成本明顯高於呼叫IModelObject方法的成本。
EnumerateReferences 方法會根據 BindReference 方法的規則,列舉一組名稱和參考,這些名稱會根據 BindReference 方法的規則來系結至物件。 不同於 IModelObject 上的 EnumerateKeys、EnumerateValues 和類似的方法,這些方法可能會傳回多個具有相同值的名稱(針對基類、父模型和類似),此列舉值只會傳回與 BindValue 和 BindReference 系結的特定名稱集。 名稱永遠不會重複。 請注意,透過名稱系結器列舉物件的成本明顯高於呼叫IModelObject方法的成本。
調試程序數據模型C++腳本偵錯介面
數據模型中腳本提供者的基礎結構也提供偵錯腳本的概念。 任何想要向偵錯主機公開偵錯功能的腳本,以及裝載數據模型的調試程式應用程式,除了 IDataModelScript 介面之外,也可以讓可偵錯的腳本實作 IDataModelScriptDebug 介面。 腳本上存在這個介面,會向基礎結構指出其可偵錯。
雖然 IDataModelScriptDebug 介面是存取腳本提供者偵錯功能的起點,但它是由一組其他介面聯結,以提供整體偵錯功能。
偵錯介面包括:
| 介面 | 說明 |
|---|---|
| IDataModelScriptDebug | 腳本提供者必須提供的核心介面,才能讓腳本可偵錯。 如果腳本可偵錯,IDataModelScript 介面的實作類別必須查詢 IDataModelScriptDebug 的 QueryInterface。 |
| IDataModelScriptDebugClient | 想要提供腳本偵錯功能的使用者介面會實作 IDataModelScriptDebugClient 介面。 文稿提供者會利用這個介面來回傳遞偵錯資訊(例如:發生的事件、斷點等...) |
| IDataModelScriptDebugStack | 腳本提供者會實作這個介面,以將呼叫堆棧的概念公開給腳本調試程式。 |
| IDataModelScriptDebugStackFrame | 腳本提供者會實作這個介面,以公開呼叫堆棧內特定堆疊框架的概念。 |
| IDataModelScriptDebugVariableSetEnumerator | 腳本提供者會實作這個介面來公開一組變數。 此集合可能代表函式的參數集、局部變數集,或特定範圍內的變數集。 確切的意義取決於取得介面的方式。 |
| IDataModelScriptDebugBreakpoint | 腳本提供者會實作這個介面,以公開腳本中特定斷點的概念和控制。 |
| IDataModelScriptDebugBreakpointEnumerator | 腳本提供者會實作這個 來列舉腳本中目前存在的所有斷點(不論是否已啟用)。 |
一般管理介面包括:
| 介面 | 說明 |
|---|---|
| IDataModelScriptProvider | 腳本提供者必須實作的核心介面。 這是向數據模型管理員的腳本管理員部分註冊的介面,以便公告提供者支援特定類型的腳本,並針對特定擴展名註冊 |
| IDataModelScript | 由提供者所管理之特定腳本的抽象概念。 載入或編輯的每個腳本都有個別的 IDataModelScript 實例 |
| IDataModelScriptClient | 腳本提供者用來將資訊傳達給使用者介面的用戶端介面。 腳本提供者不會實作這個介面。 裝載想要使用腳本提供者之數據模型的應用程式。 文稿提供者會呼叫文本用戶端的方法,以報告狀態、錯誤等等... |
| IDataModelScriptHostContext | 腳本提供者用來作為腳本內容的容器的主機介面。 除了腳本對調試程式應用程式物件模型執行的作以外,腳本的內容如何由特定的偵錯主機決定。 這個介面可讓腳本提供者取得放置其內容位置的相關信息。 |
| IDataModelScriptTemplate | 腳本提供者可以提供一或多個範本,做為使用者撰寫腳本的起點。 提供內建編輯器的調試程式應用程式,可以使用提供者透過這個介面公告的範本內容預先填入新的腳本。 |
| IDataModelScriptTemplateEnumerator | 腳本提供者實作的列舉值介面,以便公告它支援的所有範本。 |
| IDataModelNameBinder | 名稱系結器 -- 物件,可將內容中的名稱與值產生關聯。 對於指定表達式,例如 「foo.bar」,名稱系結器可以在物件 「foo」 的內容中系結名稱 「bar」。,併產生值或參考。 名稱系結器通常不是由腳本提供者實作;相反地,您可以從數據模型取得預設系結器,並由腳本提供者使用。 |
讓腳本進行偵錯:IDataModelScriptDebug
任何可偵錯的腳本會透過實作 IDataModelScriptScript 的相同元件上存在 IDataModelScriptDebug 介面來指出這項功能。 偵錯主機或裝載數據模型的調試程式應用程式,此介面的查詢表示偵錯功能是否存在。
IDataModelScriptDebug 介面的定義如下。
DECLARE_INTERFACE_(IDataModelScriptDebug, IUnknown)
{
STDMETHOD_(ScriptDebugState, GetDebugState)() PURE;
STDMETHOD(GetCurrentPosition)(_Out_ ScriptDebugPosition *currentPosition, _Out_opt_ ScriptDebugPosition *positionSpanEnd, _Out_opt_ BSTR *lineText) PURE;
STDMETHOD(GetStack)(_COM_Outptr_ IDataModelScriptDebugStack **stack) PURE;
STDMETHOD(SetBreakpoint)(_In_ ULONG linePosition, _In_ ULONG columnPosition, _COM_Outptr_ IDataModelScriptDebugBreakpoint **breakpoint) PURE;
STDMETHOD(FindBreakpointById)(_In_ ULONG64 breakpointId, _COM_Outptr_ IDataModelScriptDebugBreakpoint **breakpoint) PURE;
STDMETHOD(EnumerateBreakpoints)(_COM_Outptr_ IDataModelScriptDebugBreakpointEnumerator **breakpointEnum) PURE;
STDMETHOD(GetEventFilter)(_In_ ScriptDebugEventFilter eventFilter, _Out_ bool *isBreakEnabled) PURE;
STDMETHOD(SetEventFilter)(_In_ ScriptDebugEventFilter eventFilter, _In_ bool isBreakEnabled) PURE;
STDMETHOD(StartDebugging)(_In_ IDataModelScriptDebugClient *debugClient) PURE;
STDMETHOD(StopDebugging)(_In_ IDataModelScriptDebugClient *debugClient) PURE;
}
GetDebugState 方法會傳回腳本的目前狀態(例如:是否正在執行)。 狀態是由 ScriptDebugState 列舉中的值所定義。
GetCurrentPosition 的 方法會傳回腳本中的目前位置。 只有當腳本中斷至調試程式時,才能呼叫 GetScriptState 會傳回 ScriptDebugBreak。 任何其他對此方法的呼叫都無效,而且會失敗。
GetStack 方法會取得位於中斷位置的目前呼叫堆棧。 只有在腳本中斷至調試程式時,才能呼叫這個方法。
SetBreakpoint 方法會在腳本中設定斷點。 請注意,實作可以自由地調整內嵌行和數據行位置,以往前移至適當的程序代碼位置。 在傳回的 IDataModelScriptDebugBreakpoint 介面上,可以透過方法呼叫來擷取斷點的實際行號和數據行編號。
實作會透過 SetBreakpoint 方法在腳本內建立的每個斷點,都會被實作指派唯一標識符(64 位無符號整數)。 FindBreakpointById 方法可用來從指定的標識碼取得斷點的介面。
EnumerateBreakpoints 方法會傳回列舉值,其能夠列舉特定腳本內設定的每個斷點。
GetEventFilter 方法會傳回是否為特定事件啟用「中斷事件」。 ScriptDebugEventFilter 列舉的成員會描述可能導致「事件中斷」的事件。
SetEventFilter 方法會變更特定事件的「中斷事件」行為,如 ScriptDebugEventFilter 列舉的成員所定義。 如需可用事件的完整清單(以及此列舉的描述),請參閱 GetEventFilter 方法的檔。
StartDebugging 方法「開啟」特定腳本的調試程式。 啟動偵錯的動作不會主動造成任何執行中斷或逐步執行。 它只會讓腳本可偵錯,並提供一組介面供客戶端與偵錯介面通訊。
StopDebugging 方法是由想要停止偵錯的用戶端所呼叫。 這個方法呼叫可能會在 StartDebugging 成功完成之後的任何時間點進行(例如:在中斷期間,而腳本正在執行等等...)。呼叫會立即停止所有偵錯活動,並在呼叫 StartDebugging 之前將狀態重設為 。
偵錯介面:IDataModelScriptDebugClient
想要在腳本偵錯周圍提供介面的偵錯主機或調試程式應用程式,必須透過腳本偵錯介面上的 StartDebugging 方法,為腳本調試程式提供 IDataModelScriptDebugClient 介面的實作。
IDataModelScriptDebugClient 是用來傳遞偵錯事件的通道,而且控制權會從腳本執行引擎移至調試程式介面。 其定義如下。
DECLARE_INTERFACE_(IDataModelScriptDebugClient, IUnknown)
{
STDMETHOD(NotifyDebugEvent)(_In_ ScriptDebugEventInformation *pEventInfo, _In_ IDataModelScript *pScript, _In_opt_ IModelObject *pEventDataObject, _Inout_ ScriptExecutionKind *resumeEventKind) PURE;
}
每當發生任何中斷至腳本調試程式的事件時,偵錯程序代碼本身就會透過 NotifyDebugEvent 方法呼叫介面。 這個方法是同步的。 在介面從 事件傳回之前,腳本不會繼續執行。 腳本調試程式的定義很簡單:絕對沒有需要處理的巢狀事件。 偵錯事件是由稱為 ScriptDebugEventInformation 的變體記錄所定義。 事件資訊中的哪些欄位有效,主要是由 DebugEvent 成員定義。 它會定義由 ScriptDebugEvent 列舉成員所描述的事件種類。
呼叫堆疊:IDataModelScriptDebugStack
當發生中斷至腳本調試程式的事件時,偵錯介面會想要擷取中斷位置的呼叫堆棧。 這是透過 GetStack 方法完成的。 這類堆棧會透過 IDataModelScriptDebugStack 來表示,其定義如下。
請注意,整體堆疊可能會跨越多個腳本和/或多個腳本提供者。 從特定腳本偵錯介面上 GetStack 方法單一呼叫傳回的呼叫堆疊,應該只會傳回該腳本界限內的呼叫堆棧區段。 如果相同提供者的兩個腳本互動,腳本偵錯引擎可能會擷取呼叫堆疊,因為跨越多個腳本內容。 GetStack 方法不應該傳回另一個腳本中堆疊的部分。 相反地,如果可以偵測到這種情況,腳本中的界限框架應該透過該堆疊框架的IsTransitionPoint和 GetTransition 方法實作,將本身標示為轉換框架。 預期調試程式介面會從存在的多個堆疊區段,將整體堆疊結合在一起。
必須以這種方式實作轉換,或偵錯介面可能會將局部變數、參數、斷點和其他腳本特定建構的查詢導向至錯誤的腳本內容! 這會導致調試程式介面中未定義的行為。
DECLARE_INTERFACE_(IDataModelScriptDebugStack, IUnknown)
{
STDMETHOD_(ULONG64, GetFrameCount)() PURE;
STDMETHOD(GetStackFrame)(_In_ ULONG64 frameNumber, _COM_Outptr_ IDataModelScriptDebugStackFrame **stackFrame) PURE;
}
GetFrameCount 方法會傳回呼叫堆疊此區段中的堆疊框架數目。 如果提供者可以偵測不同腳本內容或不同提供者中的框架,它應該藉由在此堆棧區段的專案框架上實作 IsTransitionPoint 和 GetTransition 方法,向呼叫端指出這一點。
GetStackFrame 會從堆疊區段取得特定的堆疊框架。 呼叫堆疊具有以零起始的索引系統:發生中斷事件的目前堆疊框架是框架0。 目前方法的呼叫端是框架 1 (依此類推)。
中斷時 檢查狀態:IDataModelScriptDebugStackFrame
當中斷至腳本調試程式時,呼叫堆棧的特定框架可以透過IDataModelScriptDebugStack 介面上的 GetStackFrame 方法呼叫來擷取,代表發生中斷的堆棧區段。 傳回來表示此框架的 IDataModelScriptDebugStackFrame 介面定義如下。
DECLARE_INTERFACE_(IDataModelScriptDebugStackFrame, IUnknown)
{
STDMETHOD(GetName)(_Out_ BSTR *name) PURE;
STDMETHOD(GetPosition)(_Out_ ScriptDebugPosition *position, _Out_opt_ ScriptDebugPosition *positionSpanEnd, _Out_opt_ BSTR *lineText) PURE;
STDMETHOD(IsTransitionPoint)(_Out_ bool *isTransitionPoint) PURE;
STDMETHOD(GetTransition)(_COM_Outptr_ IDataModelScript **transitionScript, _Out_ bool *isTransitionContiguous) PURE;
STDMETHOD(Evaluate)(_In_ PCWSTR pwszExpression, _COM_Outptr_ IModelObject **ppResult) PURE;
STDMETHOD(EnumerateLocals)(_COM_Outptr_ IDataModelScriptDebugVariableSetEnumerator **variablesEnum) PURE;
STDMETHOD(EnumerateArguments)(_COM_Outptr_ IDataModelScriptDebugVariableSetEnumerator **variablesEnum) PURE;
}
GetName 方法會傳回此框架的顯示名稱(例如:函式名稱)。 這類名稱會顯示在調試程式介面中向用戶顯示的堆疊回溯內。
GetPosition 方法會傳回堆疊框架所表示之腳本內的位置。 只有在腳本位於包含此框架的堆疊所代表的中斷內時,才能呼叫這個方法。 一律會傳回此圖文框內的線條和數據行位置。 如果調試程式能夠傳回腳本中「執行位置」的範圍,則可以在positionSpanEnd自變數中傳回結束位置。 如果調試程式無法達到此目的,則範圍結尾中的行和數據行值應該設定為零。
IDataModelScriptDebugStack 介面代表呼叫堆疊的區段,該部分的呼叫堆疊包含在一個腳本的內容中。 如果調試程式能夠偵測從一個腳本轉換到另一個腳本(或一個腳本提供者到另一個腳本提供者),它可以藉由實作IsTransitionPoint方法,並適當地傳回 true 或 false 來表示此情況。 進入腳本的呼叫堆疊框架,其中應該將區段套用的腳本視為轉換點。 所有其他框架都不是。
如果指定的堆疊框架是由IsTransition方法所決定的轉換點(請參閱該處的轉換點定義檔),GetTransition 方法會傳回轉換的相關信息。 特別是,這個方法會傳回先前的腳本,也就是呼叫包含此 IDataModelScriptDebugStackFrame 的堆棧區段所代表的腳本。
Evaluate 方法會在呼叫此方法的 IDataModelScriptDebugStackFrame 介面所代表的堆疊框架內容中評估表示式(腳本提供者的語言)。 表達式評估的結果必須封送處理出腳本提供者作為IModelObject。 在產生的IModelObject 上,屬性和其他建構都必須能夠在調試程式處於中斷狀態時取得。
EnumerateLocals 方法會傳回變數集(由 IDataModelScriptDebugVariableSetEnumerator 介面表示),這些局部變數位於呼叫此方法之 IDataModelScriptDebugStackFrame 介面所代表之堆棧框架的內容範圍內。
EnumerateArguments 方法會傳回變數集(由 IDataModelScriptDebugVariableSetEnumerator 介面表示),這是呼叫此方法之堆棧框架中所呼叫之函式的所有函式自變數。
查看變數:IDataModelScriptDebugVariableSetEnumerator
正在偵錯的腳本中的一組變數(特定範圍中的變數、函式的局部變數、函式的自變數等等...)是由透過 IDataModelScriptDebugVariableSetEnumerator 介面定義的變數集來表示:
DECLARE_INTERFACE_(IDataModelScriptDebugVariableSetEnumerator, IUnknown)
{
STDMETHOD(Reset)() PURE;
STDMETHOD(GetNext)(_Out_ BSTR *variableName, _COM_Outptr_opt_ IModelObject **variableValue, _COM_Outptr_opt_result_maybenull_ IKeyStore **variableMetadata) PURE;
}
Reset 方法會將列舉值的位置重設為建立后立即的位置,也就是在集合的第一個專案之前。
GetNext 方法會將列舉值移至集合中的下一個變數,並傳回變數的名稱、值,以及與其相關聯的任何元數據。 如果列舉值已到達集合的結尾,則會傳回錯誤E_BOUNDS。 一旦從 GetNext 方法傳回E_BOUNDS標記之後,除非進行介入的 Reset 呼叫,否則它會在再次呼叫時繼續產生E_BOUNDS。
斷點:IDataModelScriptDebugBreakpoint
腳本斷點是透過指定腳本偵錯介面上的 SetBreakpoint 方法設定。 這類斷點會以唯一標識碼和 IDataModelScriptDebugBreakpoint 介面的實作來表示,其定義如下。
DECLARE_INTERFACE_(IDataModelScriptDebugBreakpoint, IUnknown)
{
STDMETHOD_(ULONG64, GetId)() PURE;
STDMETHOD_(bool, IsEnabled)() PURE;
STDMETHOD_(void, Enable)() PURE;
STDMETHOD_(void, Disable)() PURE;
STDMETHOD_(void, Remove)() PURE;
STDMETHOD(GetPosition)(_Out_ ScriptDebugPosition *position, _Out_opt_ ScriptDebugPosition *positionSpanEnd, _Out_opt_ BSTR *lineText) PURE;
}
GetId 方法會將腳本提供者偵錯引擎指派的唯一標識碼傳回斷點。 此標識碼在包含文稿的內容中必須是唯一的。 斷點標識符對提供者而言可能是唯一的;不過,這並非必要。
IsEnabled 方法會傳回是否啟用斷點。 停用的斷點仍然存在,而且仍在腳本的斷點清單中,只是暫時「關閉」。 所有斷點都應該以啟用的狀態建立。
Enable 方法會啟用斷點。 如果斷點已停用,呼叫這個方法之後「叫用斷點」會導致調試程式中斷。
Disable 方法會停用斷點。 在此呼叫之後,呼叫這個方法之後的「點擊斷點」將不會中斷至調試程式。 斷點,雖然仍然存在,但被視為「已關閉」。
Remove 方法會從包含的清單中移除斷點。 這個方法傳回之後,斷點不再以語意方式存在。 代表斷點的 IDataModelScriptDebugBreakpoint 介面在呼叫之後會被視為孤立。 除了釋放它之外,別無他法地可以完成它之後的呼叫。
GetPosition 方法會傳回腳本中斷點的位置。 腳本調試程式必須傳回斷點所在原始程式碼內的行和數據行。 如果能夠這樣做,它也可以填入positionSpanEnd自變數所定義的結束位置,以傳回斷點所代表的來源範圍。 如果調試程式無法產生此範圍,且呼叫端要求此範圍,則範圍結束位置的 Line 和 Column 字段應該填入為零,表示無法提供值。
斷點列舉:IDataModelScriptDebugBreakpointEnumerator
如果腳本提供者支援偵錯,它也必須追蹤與每個和每個腳本相關聯的所有斷點,並且能夠將這些斷點列舉至偵錯介面。 斷點的列舉值是透過指定腳本之偵錯介面上的 EnumerateBreakpoints 方法取得,並定義如下。
DECLARE_INTERFACE_(IDataModelScriptDebugBreakpointEnumerator, IUnknown)
{
STDMETHOD(Reset)() PURE;
STDMETHOD(GetNext)(_COM_Outptr_ IDataModelScriptDebugBreakpoint **breakpoint) PURE;
}
Reset 方法會將列舉值的位置重設為在建立列舉值之後的位置,也就是第一個列舉斷點之前的位置。
GetNext 方法會將列舉值向前移至下一個要列舉的斷點,並傳回該斷點的 IDataModelScriptDebugBreakpoint 介面。 如果列舉值已到達列舉的結尾,則會傳回E_BOUNDS。 產生E_BOUNDS錯誤之後,除非已對 Reset 方法進行介入呼叫,否則 GetNext 方法的後續呼叫將會繼續產生E_BOUNDS。
另請參閱
本主題是一系列的一部分,描述可從C++存取的介面、如何使用它們來建置以C++為基礎的調試程式延伸模組,以及如何從C++數據模型延伸模組使用其他數據模型建構(例如:JavaScript 或 NatVis)。