{"id":8077,"date":"2023-03-30T16:08:53","date_gmt":"2023-03-30T23:08:53","guid":{"rendered":"https:\/\/visualgdb.com\/w\/?p=8077"},"modified":"2023-04-04T10:56:50","modified_gmt":"2023-04-04T17:56:50","slug":"creating-custom-live-watch-nodes-via-live-watch-plugins","status":"publish","type":"post","link":"https:\/\/visualgdb.com\/tutorials\/extensibility\/livewatch\/nodes\/","title":{"rendered":"Creating Custom Live Watch Nodes via Live Watch Plugins"},"content":{"rendered":"<p>This tutorial shows how to use the Live Watch Plugin API to create custom Live Watch nodes showing various information about the debugged program. We will show how to create a &#8220;Counter&#8221; node displaying the name and value of a counter inside the program. We will also explain how to make the custom nodes plottable and how to optimize the memory reading performance when using Live Watch.<\/p>\n<p>Before you begin, follow <a href=\"https:\/\/visualgdb.com\/tutorials\/extensibility\/livewatch\/intro\">this tutorial<\/a> or clone <a href=\"https:\/\/github.com\/sysprogs\/VisualGDBExtensibilityExamples\/releases\/tag\/tutorials%2FLiveWatchPlugin%2Fbasic\">this Git tag<\/a> to get a basic Live Watch plugin that creates 2 simple, but not very functional nodes. Once you got the basic setup working, make sure you open the plugin project in Visual Studio and follow the steps below to add functionality to it:<\/p>\n<ol>\n<li>In the <a href=\"https:\/\/visualgdb.com\/tutorials\/extensibility\/livewatch\/intro\">previous tutorial<\/a> we have implemented a basic <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_ILiveWatchNode.htm\">ILiveWatchNode<\/a> showing an empty counter list. In this tutorial we will update it to have actual counter nodes corresponding to instances of the &#8220;<strong>Counter<\/strong>&#8221; struct in the code. Replace the original empty <strong>GetChildren()<\/strong> method with the following code that finds a single counter instance called <strong>g_Counter<\/strong> and creates a node for it if it was found (see the next tutorial for examples of locating multiple variables automatically):\n<pre class=\"\">ILiveWatchEngine _Engine;\r\n\r\npublic CounterListNode(ILiveWatchEngine engine)\r\n{\r\n\t_Engine = engine;\r\n}\r\n\r\npublic ILiveWatchNode[] GetChildren(LiveWatchChildrenRequestReason reason)\r\n{\r\n\tvar result = new List&lt;ILiveWatchNode&gt;();\r\n\tvar counter = _Engine.Symbols.LookupVariable(\"g_Counter\");\r\n\tif (counter != null)\r\n\t\tresult.Add(new CounterNode(_Engine, counter));\r\n\treturn result.ToArray();\r\n}<\/pre>\n<p>Then, go to the <strong>CounterNode<\/strong> word, press Ctrl+. and select &#8220;<strong>Generate class &#8216;CounterNode&#8217;<\/strong>&#8220;:<br \/>\n<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/01-add.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8078\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/01-add.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Go back to the <strong>CounterNode<\/strong> instantiation and generate a constructor with fields:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/02-constr.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8079\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/02-constr.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Finally, click on the <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_ILiveWatchNode.htm\">ILiveWatchNode<\/a> interface name near the generated CounterNode declaration and have Visual Studio implement it for you. You can then <a href=\"https:\/\/github.com\/sysprogs\/VisualGDBExtensibilityExamples\/blob\/tutorials\/LiveWatchPlugin\/counter\/LiveWatchPlugins\/LiveWatchPluginTutorial\/SampleLiveWatchExtension\/CounterNode.cs\">tweak<\/a> the generated implementation to look as shown below:\n<pre class=\"\">internal class CounterNode : ILiveWatchNode\r\n{\r\n\tprivate ILiveWatchEngine _Engine;\r\n\tprivate IPinnedVariable _Counter;\r\n\r\n\tpublic CounterNode(ILiveWatchEngine engine, IPinnedVariable counter)\r\n\t{\r\n\t\t_Engine = engine;\r\n\t\t_Counter = counter;\r\n\t}\r\n\r\n\tpublic string UniqueID =&gt; \"counter:\" + _Counter.UserFriendlyName;\r\n\tpublic string RawType =&gt; \"Sample Counter\";\r\n\tpublic string Name =&gt; _Counter.UserFriendlyName + \" from \" + Path.GetFileName(_Counter.SourceLocation.File ?? \"\");\r\n\tpublic LiveWatchCapabilities Capabilities =&gt; LiveWatchCapabilities.CanHaveChildren;\r\n\r\n\tpublic LiveWatchPhysicalLocation Location\r\n\t{\r\n\t\tget\r\n\t\t{\r\n\t\t\treturn new LiveWatchPhysicalLocation(_Counter.Address,\r\n\t\t\t\t_Counter.SourceLocation.File,\r\n\t\t\t\t_Counter.SourceLocation.Line);\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void Dispose()\r\n\t{\r\n\t}\r\n\r\n\tpublic ILiveWatchNode[] GetChildren(LiveWatchChildrenRequestReason reason)\r\n\t{\r\n\t\treturn null;\r\n\t}\r\n\r\n\tpublic void SetSuspendState(LiveWatchNodeSuspendState state)\r\n\t{\r\n\t}\r\n\r\n\tpublic void SetValueAsString(string newValue)\r\n\t{\r\n\t\tthrow new NotSupportedException();\r\n\t}\r\n\r\n\tpublic LiveWatchNodeState UpdateState(LiveWatchUpdateContext context)\r\n\t{\r\n\t\treturn default;\r\n\t}\r\n}<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/03-impl.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8080\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/03-impl.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Note that we designed the <strong>CounterNode<\/strong> class to keep a reference to the <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_ILiveWatchEngine.htm\">ILiveWatchEngine<\/a> instance (.Net will automatically take care of reference counting and deallocation), and hence needed to pass it to <strong>CounterListNode<\/strong>. Update the <strong>SampleLiveWatchNodeSource()<\/strong> constructor to take a reference to it and use it when instantiating <strong>CounterListNode()<\/strong>. You can let Visual Studio do it for you, or simply add it manually:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/04-engine.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8081\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/04-engine.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Finally, update the <strong>CreateNodeSource()<\/strong> method to pass the engine reference to <strong>SimpleLiveWatchNodeSource<\/strong>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/05-engine2.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8082\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/05-engine2.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Now you can build and run the plugin. This will open another Visual Studio instance where you can start debugging your embedded project and will see the counter node in the <strong>Live Watch<\/strong> view:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/06-node.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8083\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/06-node.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>If you set a breakpoint inside the <strong>CounterListNode.GetChildren()<\/strong> method <span style=\"text-decoration: underline;\"><strong>before<\/strong><\/span> the child node is created for the first time, you can inspect the value of <strong>counter<\/strong> in the debugger. See how VisualGDB automatically determined the address and size of the variable based on the DWARF debugging symbols:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/07-var.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8084\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/07-var.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>If you set the breakpoint after the variable was already shown, simply restart the embedded debug session (not the plugin debugging session) and it will be queried again.<\/li>\n<li>Now we will modify the counter node to display the actual counter value. Set a breakpoint in its constructor, restart the embedded debug session, and once the breakpoint hits, add the following code to the constructor:\n<pre class=\"\">            var countField = counter.LookupChildRecursively(\"Count\");\r\n            if (countField != null)\r\n            {\r\n                _LiveVar = _Engine.CreateLiveVariable(countField);\r\n            }<\/pre>\n<p>You can let Visual Studio generate the definition of <strong>_LiveVar<\/strong> field for you, or simply define it as &#8220;<strong>private readonly ILiveVariable _LiveVar;<\/strong>&#8220;.<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/08-field.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8085\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/08-field.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>If you step over the CreateLiveVarible() call, you will see how the live variable object knows the address and size of the variable, but doesn&#8217;t know anything about the type or layout. This is intentional &#8211; VisualGDB handles the target symbols via the <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_ILiveSymbolParser.htm\">ILiveSymbolParser<\/a> interface (<strong>engine.Symbols<\/strong>) and the actual target memory via <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_ILiveMemoryManager.htm\">ILiveMemoryManager<\/a> (<strong>engine.Memory<\/strong>):<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/09-livevar.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8086\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/09-livevar.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Set a breakpoint in the <strong>UpdateState()<\/strong> method and once it triggers, add the following code there:\n<pre class=\"\">        public LiveWatchNodeState UpdateState(LiveWatchUpdateContext context)\r\n        {\r\n            var state = new LiveWatchNodeState();\r\n            var value = _LiveVar.GetValue();\r\n            if (value.IsValid)\r\n                state.Value = value.ToUlong().ToString();\r\n\r\n            return state;\r\n        }<\/pre>\n<p>Step through it to see how the value of the variable is just a raw array of bytes with a timestamp:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/10-value.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8087\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/10-value.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Remove the breakpoint and resume the execution. The counter value will now appear in the &#8220;<strong>Value<\/strong>&#8221; column for the node:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/11-viewvalue.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8088\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/11-viewvalue.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Before we go any further, try collapsing the <strong>Counters<\/strong> list and take a note of the active variable count below:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/12-hide.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8089\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/12-hide.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>Despite having hidden the <strong>CounterNode<\/strong>, the underlying live variable is still being queried. This is not a problem for 1 node, but if your program had 1000 counters and you were only observing one of them, it would lower the performance quite a bit!<\/li>\n<li>In order to avoid it, we will need to add some logic to the <strong>SetSuspendState()<\/strong> method. Set a breakpoint there and try collapsing the <strong>Counters<\/strong> list again:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/13-suspend.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8090\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/13-suspend.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>Note how <strong>SetSuspendState()<\/strong> got called, indicating that the node is hidden. VisualGDB distinguishes visible nodes, hidden nodes (e.g. if they are scrolled out), hidden nodes with visible children, and hidden but otherwise monitorred (e.g. plotted) nodes. In most cases, you can rely on the <strong>SuspendRegularUpdates<\/strong> and <strong>SuspendDirectValueUpdates<\/strong> properties. If the node is not directly visible and not plotted, the <strong>SuspendDirectValueUpdates<\/strong> will be set to <strong>true.<\/strong> If none of the node&#8217;s children are visible either, <strong>SuspendRegularUpdates<\/strong> will turn to true as well. You can use these fields to set the <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/P_VisualGDBExtensibility_LiveWatch_ILiveVariable_SuspendUpdating.htm\"><strong>SuspendUpdating<\/strong><\/a> field of the live variable:\n<pre class=\"\">        public void SetSuspendState(LiveWatchNodeSuspendState state)\r\n        {\r\n            if (_LiveVar != null)\r\n                _LiveVar.SuspendUpdating = state.SuspendRegularUpdates;\r\n        }<\/pre>\n<p>If you resume the program now, collapsing the counter list will automatically reduce the number of active variables to 0:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/14-suspended.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8091\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/14-suspended.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>It is also recommended to dispose of the <strong>_LiveVar<\/strong> variable in the CounterNode&#8217;s <strong>Dispose()<\/strong> method (&#8220;<strong>?.<\/strong>&#8221; stands for &#8220;do it if <strong>_LiveVar<\/strong> is not <strong>null<\/strong>&#8220;):<\/p>\n<pre class=\"\">        public void Dispose()\r\n        {\r\n            _LiveVar?.Dispose();\r\n        }<\/pre>\n<p>VisualGDB will call the node&#8217;s <strong>Dispose()<\/strong> method when the node was permanently deleted, and calling <strong>_LiveVar.Dispose() <\/strong>will remove the variable from the updated variable list. In this tutorial, however, it is not strictly necessary: the list of counters does not change throughout the debug session, and disposing of the live variable at the end of the debug session will not change anything anymore, as VisualGDB stops polling the target memory at that time.<\/li>\n<li>Now we will change the <strong>CounterNode<\/strong> logic to show the original live variable (of type &#8220;<strong>struct SampleCounter<\/strong>&#8220;) with all of its fields. This can be done by calling <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/M_VisualGDBExtensibility_LiveWatch_ILiveWatchEngine_CreateNodeForPinnedVariable.htm\">CreateNodeForPinnedVariable()<\/a> and returning it from the list of children as shown below:\n<pre class=\"\">        public ILiveWatchNode[] GetChildren(LiveWatchChildrenRequestReason reason)\r\n        {\r\n            var result = new List&lt;ILiveWatchNode&gt;();\r\n            var node = _Engine.CreateNodeForPinnedVariable(_Counter,\r\n                new LiveWatchNodeOverrides\r\n                {\r\n                    Name = \"[raw object]\"\r\n                });\r\n\r\n            if (node != null)\r\n                result.Add(node);\r\n\r\n            return result.ToArray();\r\n        }<\/pre>\n<p>Nodes created via this interface will automatically handle suspension, plotting, cleanup, etc, so you won&#8217;t need to worry about it:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/15-raw.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8092\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/15-raw.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Resume the debug session. Note how the <strong>Live Watch<\/strong> window now contains 4 nodes: the counter list node, the counter node, and the raw <strong>g_Counter<\/strong> node (with the name overridden to <strong>[raw object]<\/strong>) that is automatically managed by VisualGDB and creates its children when expanded:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/16-rawview.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8093\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/16-rawview.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>If you are frequently using Live Watch, consider setting it to activate at the beginning of each session via the <strong>Tool Window Behavior Configurator<\/strong>:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/17-settings.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8094\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/17-settings.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Showing the value of the only structure field in a custom Live Watch node won&#8217;t help the debugging a lot, however combining the values of multiple fields in a meaningful summary line could. We will demonstrate it by parsing and showing custom names for the counter objects. First, add a <strong>Name<\/strong> field to the <strong>SampleCounter<\/strong> struct and set it to &#8220;<strong>Test counter<\/strong>&#8221; inside main():<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/18-name.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8095\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/18-name.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Then, go to the constructor of <strong>CounterNode()<\/strong>, locate the <strong>Name<\/strong> field and create a live variable if it is found:\n<pre class=\"\">            if (counter.LookupChildRecursively(\"Name\") is IPinnedVariable nameField)\r\n                _NameVar = _Engine.CreateLiveVariable(nameField);<\/pre>\n<p>If you are new to C#, the &#8220;<strong>if (&lt;&#8230;&gt; is IPinnedVariable nameField)<\/strong>&#8221; is just a shorthand for declaring the nameField variable and checking that it&#8217;s not <strong>null<\/strong> in one statement.<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/19-name.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8096\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/19-name.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Add the <strong>_LastKnownNamePtr<\/strong> and <strong>_LastKnownName<\/strong> fields as shown below and update the <strong>UpdateState()<\/strong> method to read the name from the &#8216;Name&#8217; field:\n<pre class=\"\">            var namePtr = _NameVar.GetValue().ToUlong();\r\n            if (namePtr != _LastKnownNamePtr)\r\n            {\r\n                _LastKnownNamePtr = namePtr;\r\n                _LastKnownName = _Engine.Memory.ReadMemory(namePtr, 128).ToNullTerminatedString();\r\n            }\r\n            state.NewName = _LastKnownName;<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/20-readname.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8097\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/20-readname.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>Note that the actual memory accesses on the target will look as follows:<\/p>\n<ul>\n<li>VisualGDB will automatically read both <strong>Name<\/strong> (just pointer) and <strong>Count<\/strong> fields before updating <span style=\"text-decoration: underline;\"><strong>any<\/strong><\/span> Live Watch nodes because we have created (and not suspended) the corresponding <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_ILiveVariable.htm\">Live Variable<\/a> objects. Because the fields are adjacent, they both will be read within 1 sequential read operation. Non-sequential would also be grouped by VisualGDB to minimize latency.<\/li>\n<li>When <strong>UpdateState()<\/strong> is invoked by VisualGDB, it calls the <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/M_VisualGDBExtensibility_LiveWatch_ILiveVariable_GetValue.htm\">GetValue()<\/a> method that immediately returns the value read in the previous step.<\/li>\n<li>If the value of the pointer has changed, the plugin will call <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/M_VisualGDBExtensibility_LiveWatch_ILiveMemoryManager_ReadMemory.htm\">ReadMemory()<\/a> that will read the actual target memory, adding some latency. However, because we only re-read the actual string contents when the pointer changes, it should not happen too often.<\/li>\n<\/ul>\n<\/li>\n<li>If you resume the debug session now, you will see that the name of the <strong>CounterNode <\/strong>changed to &#8220;Test Counter&#8221;<strong>:<\/strong> <a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/21-gotname.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8098\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/21-gotname.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>Also note how the <strong>[raw object]<\/strong> node now shows both fields of the <strong>SampleCounter<\/strong> struct because VisualGDB maintains this node automatically.<\/li>\n<li>If you carefully compare the node for the <strong>Count<\/strong> field against the &#8220;<strong>Test Counter<\/strong>&#8221; node created by the plugin, you will notice that the <strong>Count<\/strong> node supports breakpoints and plotting, but the plugin&#8217;s node does not:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/22-bkpt-1.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8104\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/22-bkpt-1.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a> This happens because plotting and breakpoints require the node to report some additional information (raw value and the means of converting it to a number). We will fix it in the next step.<\/li>\n<li>Change the <strong>CounterNode<\/strong> class to implement the <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_IScalarLiveWatchNode.htm\">IScalarLiveWatchNode<\/a> interface. Visual Studio can automatically generate the missing method\/property implementations for you:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/23-scalar.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8100\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/23-scalar.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>Also, update the node capabilities accordingly:\n<pre class=\"\">        public LiveWatchCapabilities Capabilities =&gt; LiveWatchCapabilities.CanHaveChildren | LiveWatchCapabilities.CanPlotValue | LiveWatchCapabilities.CanSetBreakpoint;<\/pre>\n<\/li>\n<li>Update the property implementations as shown below:\n<pre class=\"\">        public ILiveWatchFormatter[] SupportedFormatters { get; }\r\n        public ILiveWatchFormatter SelectedFormatter { get; set; }\r\n        public LiveVariableValue RawValue { get; private set; }\r\n        public LiveWatchEnumValue[] EnumValues =&gt; null;<\/pre>\n<p>If you are new to C#, here&#8217;s a quick explanation of the property syntax:<\/p>\n<ul>\n<li><strong>SupportedFormatters<\/strong> is a read-only property that can only be set in a constructor, and will always return the value set in the constructor.<\/li>\n<li><strong>SelectedFormatter<\/strong> is a read-write property that can be set by this class, or by VisualGDB when you select a formatter in the Live Watch window.<\/li>\n<li><strong>RawValue<\/strong> is a read-write property, that can be read by everyone, but set anywhere within this class.<\/li>\n<li><strong>EnumValues<\/strong> is a read-only property (basically, a function looking like a field) that always returns <strong>null<\/strong> when called.<\/li>\n<\/ul>\n<\/li>\n<li>Add the following 2 lines to the end of the CounterNode&#8217;s constructor:\n<pre class=\"\">            SupportedFormatters = _Engine.GetFormattersForSize(4, ScalarVariableType.UInt32);\r\n            SelectedFormatter = SupportedFormatters?.FirstOrDefault();<\/pre>\n<p>The <strong>SupportedFormatters<\/strong> correspond to the format list (e.g. <strong>32-bit unsigned (dec)<\/strong>) shown in the Type column of the Live Watch window. Although you could implement them yourself via <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/T_VisualGDBExtensibility_LiveWatch_ILiveWatchFormatter.htm\">this interface<\/a>, it is much easier to simply have VisualGDB return a list of all formatters for 32-bit types. You can also filter them by name or ID if you wish.<\/li>\n<li>Update the <strong>UpdateState()<\/strong> method to automatically set the <strong>RawValue<\/strong> property to the last known value of the counter, and use <strong>SelectedFormatter<\/strong> to convert it to a string:\n<pre class=\"\">var value = RawValue = _LiveVar.GetValue();\r\nif (value.IsValid)\r\n    state.Value = SelectedFormatter?.FormatValue(value.Value) ?? value.ToUlong().ToString();<\/pre>\n<p>the &#8220;?.&#8221; and &#8220;??&#8221; syntax above is a convenient way to write this:<\/p>\n<pre class=\"\">if (SelectedFormatter != null)\r\n    state.Value = SelectedFormatter.FormatValue(value.Value);\r\nelse\r\n    state.Value = null;\r\n\r\nif (state.Value == null)\r\n    state.Value = value.ToUlong().ToString();<\/pre>\n<p><a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/24-format.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8101\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/24-format.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a><\/li>\n<li>Run the debugging session again. You will now be able to plot the value of the <strong>Test counter<\/strong> node and set live watch breakpoints on it:<a href=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/25-bkpt.png\"><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter size-full wp-image-8102\" src=\"https:\/\/visualgdb.com\/w\/wp-content\/uploads\/2023\/03\/25-bkpt.png\" alt=\"\" width=\"1226\" height=\"843\" \/><\/a>You can also now switch between the decimal and hexadecimal representation of <strong>Test Counter<\/strong> via the <strong>Type<\/strong> column. It will affect the <strong>SelectedFormatter<\/strong> field that is used in the <strong>UpdateState()<\/strong> method to convert the raw value to a string.<\/li>\n<\/ol>\n<p>You can also find a detailed reference of Live Watch classes and interfaces <a href=\"https:\/\/visualgdb.com\/reference\/extensibility\/html\/N_VisualGDBExtensibility_LiveWatch.htm\">here<\/a>.<\/p>\n<p>Now that you know how to create custom Live Watch nodes for your objects, see our <a href=\"https:\/\/visualgdb.com\/tutorials\/extensibility\/livewatch\/lists\/\">next tutorial<\/a> to learn how to discover these objects both statically and dynamically, and automatically create the nodes for them.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This tutorial shows how to use the Live Watch Plugin API to create custom Live Watch nodes showing various information<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[26],"tags":[219,110,61],"_links":{"self":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8077"}],"collection":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/comments?post=8077"}],"version-history":[{"count":5,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8077\/revisions"}],"predecessor-version":[{"id":8130,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/posts\/8077\/revisions\/8130"}],"wp:attachment":[{"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/media?parent=8077"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/categories?post=8077"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/visualgdb.com\/w\/wp-json\/wp\/v2\/tags?post=8077"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}