<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Notes from the Field</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/donmoore/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/donmoore/feed.xml</link>
    <description>Sharing solutions...</description>
    <lastBuildDate>Tue, 16 Apr 2019 05:02:38 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>PowerShell Using Threads in C# to Launch a Process and Read Output Streams</title>
      <link>https://docs.microsoft.com/archive/blogs/donmoore/powershell-using-threads-in-c-to-launch-a-process-and-read-output-streams</link>
      <pubDate>Thu, 17 Mar 2011 16:56:00 GMT</pubDate>
      <dc:creator><![CDATA[DonMooz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/donmoore/2011/03/17/powershell-using-threads-in-c-to-launch-a-process-and-read-output-streams/</guid>
      <description><![CDATA[The other day I wanted to write a PowerShell script that required repeatedly launching an...]]></description>
      <content:encoded><![CDATA[<p><a name="Source Code"></a>The other day I wanted to write a PowerShell script that required repeatedly launching an application to process a list of items. The application needed to capture both stdout and stderr from the application so that I could echo that information to my log file for future reference. Seems like something that would be pretty trivial in PowerShell...</p>
<p>But I found this task to be surprisingly challenging.&nbsp; (Maybe it's just because I don't work with PowerShell as much as I should?) The hard part was to capture both output streams from the launched application. It's pretty easy to capture stdout using any one of several ways to launch stuff in PowerShell.&nbsp; With some additional effort, it's also not that hard to capture stderr. But the hard part was to capture both. To do this you need to 'resort' to using the .net diagnostics library, and even then the approach seemed to be prone to hangs.</p>
<p>The problem is that you need to read from both streams at the same time. Even though there are lots of code samples out there showing calls to ReadToEnd on the standard streams, my experience is that one or the other call will hang forever waiting for input that never comes.</p>
<p>In the past, I've solved this problem in .Net using helper threads to read the streams while the main thread waits on the helper threads and the launched process to finish.&nbsp; Unfortunately, it appears that creating multi-threaded apps in PowerShell is not supported.</p>
<p>Or is it? After some more digging I found a simple way to combine my old .Net threaded approach to capturing those streams with the new and modern PowerShell world. </p>
<p>The solution uses the very nifty PowerShell cmdlet call Add-Type. This cmdlet lets me create a Frankenstein like solution that incorporates a fragment of C# code that handles the running of the command and that captures of the output streams using some simple background threads. Once I have that output captured, I can log it to my heart's content.</p>
<p>The PowerShell script has the following components:</p>
<ul>
<li>Source code for a C# class in a string literal assigned to a variable. </li>
<li>The C# class has one method. Call this method to launch a process. That method wraps the use of background threads to capture the output streams from the process. </li>
<li>Use the Add-Type cmdlet to 'hydrate' that code into a class that can be instantiated in the script.</li>
<li>Use New-Object to instantiate an object of this new class.</li>
<li>Call a method on the new object to launch the process. When the method returns, you can harvest the captured output streams from properties of the object, and do what you want with them.</li>
</ul>
<p>You can download the script from here:&nbsp; <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/03/40/PowerShell%20with%20C_2300_/3666.RunCommand%20PowerShell.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-PowerShell+with+C_2300_/3666.RunCommand-PowerShell.zip">https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-PowerShell+with+C_2300_/3666.RunCommand-PowerShell.zip</a></p>
<p>Here is the source code. It shows how to launch two programs.&nbsp; Hopefully you can imagine how this can be applied in your scripts to run commands and to get at all those exciting output streams.</p>
<pre class="scroll"><code class="cplusplus">#A powershell script that shows how to:<br />
# - use Add-Type to define and use a class define in c#<br />
# - launch a program from Powershell and capture both stdout and stderr<br />
<br />
<br />
# Place C# definition of a class into a local string variable.<br />
# This class provide one method that can be used to launch a process and waits for it to complete.<br />
# The output streams stdout and stderr from the process are captured and made available via properties of the class.<br />
#<br />
$source = @"    <br />
using System;<br />
using System.IO;<br />
using System.Threading;<br />
using System.Diagnostics;<br />
<br />
    public class RunCommand<br />
    {<br />
        private class AllAboutIO<br />
        {<br />
            public AllAboutIO(StreamReader reader)<br />
            {<br />
                this.reader = reader;<br />
            }<br />
<br />
            public string result;<br />
            public StreamReader reader;<br />
        }<br />
<br />
        public string stdout;<br />
        public string stderr;<br />
<br />
        public void AsyncStreamReader(object data)<br />
        {<br />
            AllAboutIO myData = (AllAboutIO)data;<br />
            myData.result = myData.reader.ReadToEnd();<br />
        }<br />
<br />
        public int Run(string Program, string Arguments)<br />
        {       <br />
            Process myProc = new Process();<br />
<br />
            myProc.StartInfo.UseShellExecute = false;<br />
            myProc.StartInfo.FileName = Program;<br />
            myProc.StartInfo.Arguments = Arguments;<br />
            myProc.StartInfo.CreateNoWindow = true;<br />
            myProc.StartInfo.RedirectStandardError = true;<br />
            myProc.StartInfo.RedirectStandardOutput = true;<br />
<br />
            try<br />
            {<br />
                if (myProc.Start())<br />
                {<br />
                    AllAboutIO stdoutReader = new AllAboutIO(myProc.StandardOutput);<br />
                    AllAboutIO stderrReader = new AllAboutIO(myProc.StandardError);<br />
<br />
                    Thread readStdOutThread = new Thread(this.AsyncStreamReader);<br />
                    Thread readStdErrThread = new Thread(this.AsyncStreamReader);<br />
<br />
                    readStdOutThread.Start(stdoutReader);<br />
                    readStdErrThread.Start(stderrReader);<br />
<br />
                    myProc.WaitForExit();<br />
<br />
                    readStdErrThread.Join();<br />
                    readStdOutThread.Join();<br />
<br />
                    stdout = stdoutReader.result;<br />
                    stderr = stderrReader.result;<br />
<br />
                    return myProc.ExitCode;<br />
                }<br />
                else<br />
                {<br />
                    stderr = "Process did not start. Verify that the program exists at the specified location.";<br />
                    return 1;<br />
                }<br />
            }<br />
            catch (System.Exception excp)<br />
            {<br />
                stderr = "Failed to launch the process: " + excp.Message;<br />
                return 1;<br />
            }<br />
        }<br />
    }<br />
"@<br />
<br />
# Create a type from the C#.<br />
Add-Type -TypeDefinition $source -ReferencedAssemblies ("System.Core")<br />
<br />
# Helper function for launching a a program.<br />
function Start-Proc  {   <br />
    param (   <br />
    $exe,   <br />
    $arguments,<br />
    [ref]$stdout,<br />
    [ref]$stderr<br />
     ) <br />
<br />
    $runCommand = New-Object RunCommand<br />
    <br />
    $runCommandResult = $runCommand.Run($exe, $arguments)<br />
    <br />
    $stdout.value = $runCommand.stdOut<br />
    $stderr.value = $runCommand.stdErr<br />
     <br />
    return $runCommandResult       <br />
 }<br />
 <br />
<br />
$stderr = ""<br />
$stdout = ""<br />
<br />
 <br />
#A test of running a program that shows capture of stderr<br />
$exitcode = Start-Proc ($env:windir + "\system32\net.exe") "/notacommand" ([ref]$stdout) ([ref]$stderr)<br />
write-host "Exit code was: " $exitcode<br />
write-host -ForegroundColor Green $stdout<br />
write-host -ForegroundColor Red $stderr<br />
<br />
#A test of running a command that shows capture of stdout<br />
$exitcode = Start-Proc ($env:windir + "\system32\net.exe") "help" ([ref]$stdout) ([ref]$stderr)<br />
write-host "Exit code was: " $exitcode<br />
write-host -ForegroundColor Green $stdout<br />
write-host -ForegroundColor Red $stderr<br />
</code></pre>
<p>After seeing this you might think: Gosh that's a lot of work for just capturing some output from a command. It is. But it does illustrates some interesting capabilities of PowerShell. Thanks for reading.</p>]]></content:encoded>
    </item>
    <item>
      <title>System Center Operations Manager (SCOM) Overrideable Parameters Listing Tool</title>
      <link>https://docs.microsoft.com/archive/blogs/donmoore/system-center-operations-manager-scom-overrideable-parameters-listing-tool</link>
      <pubDate>Fri, 10 Dec 2010 18:50:00 GMT</pubDate>
      <dc:creator><![CDATA[DonMooz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/donmoore/2010/12/10/system-center-operations-manager-scom-overrideable-parameters-listing-tool/</guid>
      <description><![CDATA[This article is about a System Center Operations Manager (SCOM) tool that I created for listing the...]]></description>
      <content:encoded><![CDATA[<p>This article is about a System Center Operations Manager (SCOM) tool that I created for listing the overrideable parameters available in a particular Management Pack. The tool outputs an HTML document containing a table of all discoveries, rules and unit monitors in the specified Management Pack with their associated overrideable parameters.</p>
<p>Here are a few scenarios where this tool might be useful:</p>
<ul>
<li>You would like to understand in detail the configuration options for a vendor supplied management. Since the overrideable parameters are the primary means for configuring a management pack, this information can provide useful insights into how to tune a management pack for a specific environment.</li>
<li>You want to provide detailed documentation for a custom developed management pack to your customer. The output of this tool can be easily incorporated into the documentation you provide with your management pack.</li>
<li>You are looking for sample code that shows how to access management pack objects.</li>
</ul>
<p>This tool requires a connection to the Management Group where the management pack is imported. </p>
<p>The tool is a simple command line program. It takes three arguments. The first specifies the management server (use "localhost" if running the command on the server). The second is the management pack name, and the third is the output file name.</p>
<p>For example:</p>
<p>GetOverrideableParameters.exe localhost Microsoft.SQLServer.2000.Monitoring c:\SqlMonitoringParamerts.html</p>
<p>Download the tool and the Visual Studio project from the links provided at the end of this article.</p>
<p><b>Implementation Details</b></p>
<p>The program is written in C# and was developed using Visual Studio 2010. It has been successfully used in a System Center Operations Manager R2 deployment.</p>
<p>The program references SCOM .Net assemblies Microsoft.EnterpriseManagement.OperationsManager and Microsoft.EnterpriseManagement.OperationsManager.Common. It should run on computers where the Operations Console has been installed.</p>
<p>The generated HTML is very basic and should be easy to enhance either through extension of the program or via direct editing in an HTML editor. The generated HTML consists of a table of parameters. &nbsp;It is also straight forward to copy / paste this HTML from the browser into a Word document for more extensive formatting.</p>
<p>The program flow is as follows:</p>
<ul>
<li>- Connect to the management group</li>
<li>- Get the Management Pack object</li>
<li>- Emit the HTML header info</li>
<li>- Loop through all Discoveries in the management pack and emit the overrideable parameters for each.</li>
<li>- Loop through all Rules in the management pack and emit the overrideable parameters for each.</li>
<li>- Loop through all UnitMonitors in the management pack and emit the overrideable parameters for each.</li>
<li>- Emit the closing tags for the HTML</li>
<li>- Close the output file</li>
</ul>
<p>Note that the reason this program must connect to the Management Group is because the resolution of datasource information requires access to management packs referenced by the specified management pack.</p>
<p>Also note that the "Value" column displayws in the output file shows the parameter value as configured in the Management Pack. It does not reflect any overrides that may be in effect. </p>
<p>You can download a zip file containing the executable for the program from here: <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/03/40/SCOM%20Overrideable%20Parameters/8345.GetOverrideableParametersExecutable.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Overrideable+Parameters/8345.GetOverrideableParametersExecutable.zip">https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Overrideable+Parameters/8345.GetOverrideableParametersExecutable.zip</a> </p>
<p>You can download a zip file containing the Visual Studio project from here: <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/03/40/SCOM%20Overrideable%20Parameters/1004.GetOverrideableParametersProject.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Overrideable+Parameters/1004.GetOverrideableParametersProject.zip">https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Overrideable+Parameters/1004.GetOverrideableParametersProject.zip</a> </p>
<p>You can download a sample of the program output here: <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/03/40/SCOM%20Overrideable%20Parameters/6675.sample.html" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Overrideable+Parameters/6675.sample.html">https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Overrideable+Parameters/6675.sample.html</a> </p>
<p>Here is a listing of the main program:</p>
<p>using System;</p>
<p>using System.Text;</p>
<p>using System.Collections.Generic;</p>
<p>using Microsoft.EnterpriseManagement;</p>
<p>using Microsoft.EnterpriseManagement.Common;</p>
<p>using Microsoft.EnterpriseManagement.Administration;</p>
<p>using Microsoft.EnterpriseManagement.Configuration;</p>
<p>using System.Collections.ObjectModel;</p>
<p>using System.IO;</p>
<p>using System.Xml;</p>
<p>&nbsp;</p>
<p>namespace GetOverrideableParameters</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; class Program</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main(string[] args)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (args.GetLength(0) != 3)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Error: Unexpected commandline argument count. Usage: GetOverrideableParameters &lt;Management Group server name&gt; &lt;MP name&gt; &lt;outputfile&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Environment.Exit(1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;}</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPack mp = null;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementGroup mg = null;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mg = new ManagementGroup(args[0]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception excp)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Error: Could not connect to management group: {0}", excp.Message);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Environment.Exit(1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mp = mg.GetManagementPacks(args[1])[0];</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;catch (System.Exception excp)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Error: Could not get management pack: {0}", excp.Message);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Environment.Exit(1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackElementCollection&lt;ManagementPackRule&gt; rules = mp.GetRules();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackElementCollection&lt;ManagementPackMonitor&gt; monitors = mp.GetMonitors();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackElementCollection&lt;ManagementPackDiscovery&gt; discoveries = mp.GetDiscoveries();</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StreamWriter writer = null;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer = new StreamWriter(args[2]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception excp)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Error: Could not create output file: {0}", excp.Message);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Environment.Exit(1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Output preamble for HTML document</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;html xmlns=\"https://www.w3.org/1999/xhtml\" &gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;head&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;title&gt;Untitled Page&lt;/title&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/head&gt;");</p>
<p>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;writer.WriteLine("&lt;body&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;h1&gt;{0} Overrideable Parameters&lt;/h1&gt;", mp.DisplayName == null ? mp.Name : mp.DisplayName);</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Start the table and emit a header line</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;table style=\"width: 100%;\" border=\"2\" &gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;tr&gt;&lt;td&gt;Monitoring Object&lt;/td&gt;&lt;td&gt;Parameter&lt;/td&gt;&lt;td&gt;Description&lt;/td&gt;&lt;td&gt;Value&lt;/td&gt;&lt;/tr&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ManagementPackDiscovery discovery in discoveries)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackDataSourceModuleType datasourceType = (ManagementPackDataSourceModuleType)mg.GetMonitoringModuleType(discovery.DataSource.TypeID.Id);</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EmitOverrideableParameters(discovery.DisplayName != null ? discovery.DisplayName : discovery.Name, writer, discovery.DataSource.Configuration, datasourceType.OverrideableParameterCollection);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ManagementPackRule rule in rules)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; string ruleName = rule.DisplayName != null ? rule.DisplayName : rule.Name;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ManagementPackDataSourceModule datasource in rule.DataSourceCollection)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackDataSourceModuleType datasourceType = (ManagementPackDataSourceModuleType)mg.GetMonitoringModuleType(datasource.TypeID.Id);</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EmitOverrideableParameters(ruleName, writer, datasource.Configuration, datasourceType.OverrideableParameterCollection);</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ruleName = ""; // We only want the name to display on the first row emitted for this rule.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ManagementPackMonitor monitor in monitors)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (monitor is ManagementPackUnitMonitor)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;ManagementPackUnitMonitor unitMonitor = (ManagementPackUnitMonitor)monitor;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; UnitMonitorType unitMonitorType = mg.GetUnitMonitorType(unitMonitor.TypeID.Id);</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; EmitOverrideableParameters(unitMonitor.DisplayName != null ? unitMonitor.DisplayName : unitMonitor.Name, writer, unitMonitor.Configuration, unitMonitorType.OverrideableParameterCollection);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/table&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Close();</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("All done!");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Emit the HTML for a collection of overrideable parameters.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="Named"&gt;&lt;/param&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="Name"&gt;&lt;/param&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="writer"&gt;&lt;/param&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="Configuration"&gt;&lt;/param&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="OverrideableParameterCollection"&gt;&lt;/param&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static void EmitOverrideableParameters(string Name, StreamWriter writer, string Configuration, ManagementPackSubElementCollection&lt;ManagementPackOverrideableParameter&gt; OverrideableParameterCollection)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Load the config section into an XML document for use in looking up actual values.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlDocument xmlConfig = new XmlDocument();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlConfig.LoadXml("&lt;root&gt;" + Configuration + "&lt;/root&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; bool firstRow = true;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ManagementPackOverrideableParameter op in OverrideableParameterCollection)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;tr&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // rule name</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write("&lt;td&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (firstRow)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Display object name only on the first row for this object.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write(Name);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; firstRow = false;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/td&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Parameter name</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write("&lt;td&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (op.DisplayName == null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write(string.Format("{0}", op.Name));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write(string.Format("{0}", op.DisplayName));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/td&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Parameter description</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;writer.Write("&lt;td&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (op.Description == null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write(string.Format(""));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write(string.Format("{0}", op.Description));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/td&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get the parameter Value. This gets the value from the configuration in the MP. It does not take into account overrides.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write("&lt;td&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlNodeList nodes = xmlConfig.GetElementsByTagName(op.Name);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (nodes.Count &gt; 0)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Write(XMLEncode(nodes[0].InnerXml));</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/td&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/tr&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;summary&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// Helper function for encoding strings to be embedded in XML</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;/summary&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;param name="Value"&gt;&lt;/param&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; /// &lt;returns&gt;&lt;/returns&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; private static string XMLEncode(string Value)</p>
<p>&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; return Value.Replace("&amp;", "&amp;amp;").Replace("'", "&amp;apos;").Replace("\"", "&amp;quot;").Replace("&lt;", "&amp;lt;").Replace("&gt;", "&amp;gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>}</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>System Center Operations Manager (SCOM) Knowledge Article Extraction Tool (with MAML to HTML transform)</title>
      <link>https://docs.microsoft.com/archive/blogs/donmoore/system-center-operations-manager-scom-knowledge-article-extraction-tool-with-maml-to-html-transform</link>
      <pubDate>Wed, 08 Dec 2010 11:30:00 GMT</pubDate>
      <dc:creator><![CDATA[DonMooz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/donmoore/2010/12/08/system-center-operations-manager-scom-knowledge-article-extraction-tool-with-maml-to-html-transform/</guid>
      <description><![CDATA[This article describes a simple .Net command line program that can be used to extract System Center...]]></description>
      <content:encoded><![CDATA[<p>This article describes a simple .Net command line program that can be used to extract System Center Operations Manager (SCOM)&nbsp;R2 Knowledge Articles from a Management Pack into an HTML file. The program utilizes an XLST transform that converts the Knowledge Article MAML into HTML.</p>
<p>Have you ever been working on a Management Pack and wanted to have an application SME review all the knowledge articles in your new MP? Or maybe you would like to learn more about the internals of a vendor supplied MP by reading the Knowledge Articles they have included in the MP? Having a tool that would dump all the Knowledge Articles into a basic HTML document would make these scenarios simpler. </p>
<p>There's probably a tool like this out there somewhere already, but I have been unable to find it.&nbsp; So I endeavored to build one. Links to the Visual Studio project and the compiled program are included at the end of the article.</p>
<p>The tool is a simple command line program.&nbsp; It takes two arguments. The first specifies the input management pack file (either .xml or .mp format). The second is the output file name. The output will be a text file containing the HTML for the extracted Knowledge Articles.</p>
<p>For example:</p>
<p>GetKnowledge.exe Microsoft.SQLServer.2000.Monitoring.mp c:\SqlMpKnowledge.html</p>
<p><b>Implementation Details</b></p>
<p>The program is written in C# and was developed using Visual Studio 2010.</p>
<p>As you probably know, using the SCOM SDK, it's easy to extract the MAML for MP Knowledge Articles.&nbsp; You might even have seen the sample code in the reference page for ManagementPackKnowledgeArticle.MamlContent property. It shows how to access the property, but right where the XSLT transform should be there's a mysterious comment: "Get the MAML content. You must write an XSLT to transform the MAML to HTML." Then you hunt for an XSLT transform, and find none.&nbsp; My mission here is to fill that void by providing a simple tool based on this sample code that includes an actual working XSLT transform for MAML to HTLM.</p>
<p>The XSLT transform I created is quite basic and is not guaranteed to process all known MAML. In fact, it only processes the MAML fragments that I have found in actual management pack Knowledge Articles.&nbsp; Over time, you should expect to have to evolve this transform, but for now it seems to work on all of the Microsoft provided Management Packs that I have tried it on.</p>
<p>The program extracts all Knowledge articles for rules and monitors in the specified Management Pack file, and outputs them as an HTML document.&nbsp; It should be pretty easy to extend the program to output Knowledge Articles for other MP elements, but at the time I wrote the program, I was only interested in Rules and Monitors.</p>
<p>The generated HTML is very basic and should be easy to enhance either through extension of the program or via direct editing in an HTML editor. It is also straight forward to copy / paste from the browser into a Word document for more extensive formatting.</p>
<p>The XSLT transform code is embedded as a resource in the .Net program (and I've included it in the text of this article).</p>
<p>The program references SCOM .Net assemblies Microsoft.EnterpriseManagement.OperationsManager and Microsoft.EnterpriseManagement.OperationsManager.Common. It should run on computers where the Operations Console or the Authoring Console are been installed.</p>
<p>Here is the XSLT Transform:</p>
<p>&lt;?xml version="1.0" encoding="utf-8"?&gt;</p>
<p>&nbsp;&lt;xsl:stylesheet version="1.0" xmlns:xsl="https://www.w3.org/1999/XSL/Transform"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:msxsl="urn:schemas-microsoft-com:xslt"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xmlns:maml="https://schemas.microsoft.com/maml/2004/10"</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; exclude-result-prefixes="msxsl maml"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:output method="html" indent="no"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="/root"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:section"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:navigationLink"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;a&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:attribute name="href"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:value-of select="maml:uri/@href" /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:attribute&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:value-of select="maml:linkText"/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/a&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;&nbsp;&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:listItem"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;li&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/li&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:list"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;ul&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/ul&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:title"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;h2&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:value-of select="."/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/h2&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="text()"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:value-of select="."/&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:para"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;p&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/p&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:ui"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:choose&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:when test="normalize-space(.)"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;span style="text-decoration:underline"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/span&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:when&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:otherwise&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:otherwise&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:choose&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:entry"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;td&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/td&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:row"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;tr&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/tr&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:template match="maml:table"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;table&nbsp; border="1"&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;xsl:apply-templates /&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/table&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &lt;/xsl:template&gt;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </p>
<p>&lt;/xsl:stylesheet&gt;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>Here is the c# code:</p>
<p>&nbsp;</p>
<p>using System;</p>
<p>using System.Text;</p>
<p>using System.Collections.Generic;</p>
<p>using Microsoft.EnterpriseManagement;</p>
<p>using Microsoft.EnterpriseManagement.Common;</p>
<p>using Microsoft.EnterpriseManagement.Administration;</p>
<p>using Microsoft.EnterpriseManagement.Configuration;</p>
<p>using System.Collections.ObjectModel;</p>
<p>using System.IO;</p>
<p>using System.Xml;</p>
<p>using System.Xml.Xsl;</p>
<p>using System.Reflection;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>namespace GetKnowledge</p>
<p>{</p>
<p>&nbsp;&nbsp;&nbsp; class Program</p>
<p>&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void TransformMamlFragmentToHtml(string mamlContent, StreamWriter writer)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // The maml from OpsMgr has no root node, so one must be added or else the xml is not valid.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mamlContent = "&lt;root&gt;" + mamlContent + "&lt;/root&gt;";</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Wrap the maml string in a reader that is usable by the transform</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (XmlReader doc = XmlReader.Create(new System.IO.StringReader(mamlContent)))</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Get the transfrom from a string resource in this assembly</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (System.IO.Stream s = Assembly.GetExecutingAssembly().GetManifestResourceStream("GetKnowledge.KATransform.xslt"))</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; using (System.Xml.XmlTextReader xsltReader = new System.Xml.XmlTextReader(s))</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Load the transform</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XslCompiledTransform xslt = new XslCompiledTransform();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xslt.Load(xsltReader);</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Setup for writing the results</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; XmlWriterSettings settings = new XmlWriterSettings();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; settings.ConformanceLevel = ConformanceLevel.Fragment;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Do the transform</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; xslt.Transform(doc, XmlWriter.Create(writer, settings));</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; static void Main(string[] args)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (args.GetLength(0) != 2)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Error: unexpected commandline argument count. Usage: GetKnowledge &lt;mp file&gt; &lt;outputfile&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Environment.Exit(1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPack mp = null;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Load the MP from the file specified in the argument list.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mp = new ManagementPack(args[0]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception excp)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Error: Could not load the management pack: {0}", excp.Message);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; System.Environment.Exit(1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;// A hint of code that could be used to load the MP from management group instead of from the file</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ManagementGroup mg = new ManagementGroup("localhost");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //ManagementPack mp = mg.GetManagementPacks(args[0])[0];</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;ManagementPackElementCollection&lt;ManagementPackRule&gt; rules;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackElementCollection&lt;ManagementPackMonitor&gt; monitors;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; rules = mp.GetRules();</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; monitors = mp.GetMonitors();</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; StreamWriter writer = null;</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer = new StreamWriter(args[1]);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (System.Exception excp)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("Error: Could not create output file: {0}", excp.Message);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;System.Environment.Exit(1);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Write out the HTML page header section </p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Transitional//EN\" \"https://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd\"&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;html xmlns=\"https://www.w3.org/1999/xhtml\" &gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;head&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;title&gt;Untitled Page&lt;/title&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/head&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;body&gt;");</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Output Knowledge article for all rules</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ManagementPackRule rule in rules)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackKnowledgeArticle article;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; article = rule.GetKnowledgeArticle(System.Globalization.CultureInfo.CurrentCulture);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Microsoft.EnterpriseManagement.Common.ObjectNotFoundException error)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //There is no knowledge article for this rule.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (article != null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (article.HtmlContent != null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Get the HTML content.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;}</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (article.MamlContent != null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;h1&gt;Rule: {0}&lt;/h1&gt;", rule.DisplayName);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; TransformMamlFragmentToHtml(article.MamlContent, writer);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; // Output knowledge article for all monitors</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; foreach (ManagementPackMonitor monitor in monitors)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ManagementPackKnowledgeArticle article;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;try</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; article = monitor.GetKnowledgeArticle(System.Globalization.CultureInfo.CurrentCulture);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; catch (Microsoft.EnterpriseManagement.Common.ObjectNotFoundException error)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //There is no knowledge article for this monitor.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; continue;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (article != null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; if (article.HtmlContent != null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;{</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; //Get the HTML content.</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; else if (article.MamlContent != null)</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;h1&gt;Monitor: {0}&lt;/h1&gt;", monitor.DisplayName);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;TransformMamlFragmentToHtml(article.MamlContent, writer);</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/body&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.WriteLine("&lt;/html&gt;");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; writer.Close();</p>
<p>&nbsp;</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Console.WriteLine("All done!");</p>
<p>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }</p>
<p>&nbsp;&nbsp;&nbsp; }</p>
<p>}</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>A zip file containing the Visual Studio 2010 project can be downloaded from here: <a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/03/40/SCOM%20Knowledge%20Extraction/7624.GetKnowledgeProject.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Knowledge+Extraction/7624.GetKnowledgeProject.zip">https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Knowledge+Extraction/7624.GetKnowledgeProject.zip</a></p>
<p>A zip file containing the program executable can be downloaded from here: <br /><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/01/03/40/SCOM%20Knowledge%20Extraction/4162.GetKnowledgeExecutable.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Knowledge+Extraction/4162.GetKnowledgeExecutable.zip">https://blogs.msdn.com/cfs-file.ashx/__key/CommunityServer-Blogs-Components-WeblogFiles/00-00-01-03-40-SCOM+Knowledge+Extraction/4162.GetKnowledgeExecutable.zip</a></p>
<p>&nbsp;</p>]]></content:encoded>
    </item>
    <item>
      <title>Maintaining State in SCOM Scripts</title>
      <link>https://docs.microsoft.com/archive/blogs/donmoore/maintaining-state-in-scom-scripts</link>
      <pubDate>Tue, 24 Jun 2008 16:37:00 GMT</pubDate>
      <dc:creator><![CDATA[DonMooz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/donmoore/2008/06/24/maintaining-state-in-scom-scripts/</guid>
      <description><![CDATA[This post describes a method for maintaining state across script executions in a SCOM Management...]]></description>
      <content:encoded><![CDATA[<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>This post describes a method for maintaining state across script executions in a SCOM Management Pack. This method is appropriate for holding a small amount of numeric or string information (such as timestamps or high water marks). The approach mimics what was used in MOM 2005, so it may also be useful for scripts that are being migrated forward from MOM 2005.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>This approach uses the registry to store state information. This isn’t really all that complex to do in a script, but the added value here is a wrapper class that approximates the ease of use that was provided by the state object in MOM 2005.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT size=3><FONT face=Calibri>You’ll have to use your own judgment regarding the applicability of this to your application. Clearly, the registry is not appropriate for storing large amounts of rapidly changing date. It is safe to use this for a few numbers or strings.<SPAN style="mso-spacerun: yes">&nbsp; </SPAN></FONT></FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>See the attachment for a script fragment that contains the VBScript class and associated declarations that provides a convenient registry based State object. Paste this into your script to take advantage of the class.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>The state class defines two methods:</FONT></P>
<P class=MsoListParagraphCxSpFirst style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"><SPAN style="FONT-FAMILY: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol"><SPAN style="mso-list: Ignore"><FONT size=3>·</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>GetState(stateVariableName) – Get the value of a ‘state variable’ from the registry.</FONT></P>
<P class=MsoListParagraphCxSpLast style="MARGIN: 0in 0in 10pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"><SPAN style="FONT-FAMILY: Symbol; mso-bidi-font-family: Symbol; mso-fareast-font-family: Symbol"><SPAN style="mso-list: Ignore"><FONT size=3>·</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>PutState(stateVariableName) – Set the value of a ‘state variable’ in the registry.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>The stateVariableName is just a unique name for the item. <SPAN style="mso-spacerun: yes">&nbsp;</SPAN>Pass a string that is descriptive and unique for this value. It is likely that you will need to utilize a naming convention to ensure uniqueness across MPs and scripts.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>I couldn’t resist also utilizing the error class that is frequently used scripts found in the Microsoft Management Packs. See the attachment for more details on that class.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>Here is an example of using the State object to save information to the registry:</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt 0.5in"><FONT face=Calibri size=3>' Put some data in the state object</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt 0.5in"><FONT face=Calibri size=3>Dim myDataIn : myDataIn = "save this data"</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt 0.5in"><FONT face=Calibri size=3>Call State.PutState("MyVarName", myDataIn)</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>Here is an example of using the State object to get the data back out of the registry.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt 0.5in"><FONT face=Calibri size=3>' Get the data back out</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt 0.5in"><FONT face=Calibri size=3>Dim myDataOut</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt 0.5in"><FONT face=Calibri size=3>myDataOut = State.GetState("MyVarName")</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" /><o:p><FONT face=Calibri size=3>&nbsp;</FONT></o:p></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>It couldn’t be simpler. To be absolutely safe, it’s a good idea to embellish this code sample with error checking. Add logic that checks for non-zero value in g_oError.err property after the calls to GetState and PutState.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>A few things to note about the State class in the attachment:</FONT></P>
<P class=MsoListParagraphCxSpFirst style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"><SPAN style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><SPAN style="mso-list: Ignore"><FONT face=Calibri size=3>1.</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>It uses the RegRead and RegWrite methods of WshShell to the do the actual registry I/O.</FONT></P>
<P class=MsoListParagraphCxSpMiddle style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"><SPAN style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><SPAN style="mso-list: Ignore"><FONT face=Calibri size=3>2.</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>The secret sauce in this code is in the use of the GetScriptStateKeyPath method provided by the MOM.ScriptAPI script object. This method determines a unique registry path accessible by the current identity. <SPAN style="mso-spacerun: yes">&nbsp;</SPAN>You don’t really need to know the registry path to use the code, but on my machine it is in HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Microsoft Operations Manager\3.0\Modules\MyState\S-1-5-21-581608182-3256059318-2886809674-500\Script.</FONT></P>
<P class=MsoListParagraphCxSpLast style="MARGIN: 0in 0in 10pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l1 level1 lfo2"><SPAN style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><SPAN style="mso-list: Ignore"><FONT face=Calibri size=3>3.</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>Note the argument in the call to GetScriptStateKeyPath("MyState") in the attachment. The “MyState” becomes part of the registry path. Consider using this to uniquely identify your state by management pack or other means.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><o:p><FONT face=Calibri size=3>&nbsp;</FONT></o:p></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><o:p><FONT face=Calibri size=3></FONT></o:p>&nbsp;</P>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/08/64/89/85/StateClass.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-08-64-89-85/StateClass.zip">StateClass.zip</a></p>]]></content:encoded>
    </item>
    <item>
      <title>Notes on Using System.Performance.DeltaValueCondition</title>
      <link>https://docs.microsoft.com/archive/blogs/donmoore/notes-on-using-system-performance-deltavaluecondition</link>
      <pubDate>Sun, 18 May 2008 12:42:00 GMT</pubDate>
      <dc:creator><![CDATA[DonMooz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/donmoore/2008/05/18/notes-on-using-system-performance-deltavaluecondition/</guid>
      <description><![CDATA[This post is about some interesting things I discovered when trying to use...]]></description>
      <content:encoded><![CDATA[<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>This post is about some interesting things I discovered when trying to use System.Performance.DeltaValueCondition as part of a workflow in a SCOM Management Pack.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>The purpose of the Management Pack is to monitor the health of an ETL process for a data warehouse. The customer wanted to be alerted when certain record counts processed by the ETL jobs varied more than a specified threshold from one day to the next.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>The basic design for this type of monitor is unsurprising. I created a data source type that knew how to get the record counts. Then I built a unit monitor type that feed the record counts from the data source into a condition detection module (a.k.a. filter) based on System.Performance.DeltaValueCondition.<SPAN style="mso-spacerun: yes">&nbsp;&nbsp; </SPAN>After the filter is the expression logic to test the delta against the thresholds. </FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>The System.Performance.DeltaValueCondition filter is designed to do exactly what I needed: it outputs the delta between samples. It can be configured to calculate the delta in terms of percentage or absolute difference. I reflected these config settings into the configuration section of the Monitor type so that everything would be super flexible.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>Everything seemed to work fine in my unit testing. </FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>The issues didn’t show up until I ran the monitor under more realistic conditions. The primary difference between my unit testing and the production application is that the rules in production have an interval of 24 hours, whereas in testing I ran things much faster. </FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>Here is what I discovered about System.Performance.DeltaValueCondition:</FONT></P>
<P class=MsoListParagraphCxSpFirst style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"><SPAN style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><SPAN style="mso-list: Ignore"><FONT face=Calibri size=3>1)</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>It considerable effort to understand the meaning of the numsamples property. Basically I found that any setting other than 1 is of dubious value for my application. Numsamples controls the number of samples held in memory by the filter. The filter will start outputting the delta after it has seen 2 values, regardless of what the NumSamples is set to. However, the output delta is always between the incoming sample and the oldest sample in history, which eventually gets updated as the queue fills up and the oldest entry is removed to make room for the newer entry. </FONT></P>
<P class=MsoListParagraphCxSpMiddle style="MARGIN: 0in 0in 0pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"><SPAN style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><SPAN style="mso-list: Ignore"><FONT face=Calibri size=3>2)</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>The queue of values is in memory. Any time the system is restarted or the service restarts, the queue is lost and will need to be reprimed before alerts are output. For me,&nbsp;this means&nbsp;I will miss alerts for a 24 hour period after a service restart.</FONT></P>
<P class=MsoListParagraphCxSpLast style="MARGIN: 0in 0in 10pt 0.5in; TEXT-INDENT: -0.25in; mso-list: l0 level1 lfo1"><SPAN style="mso-bidi-font-family: Calibri; mso-bidi-theme-font: minor-latin"><SPAN style="mso-list: Ignore"><FONT face=Calibri size=3>3)</FONT><SPAN style="FONT: 7pt 'Times New Roman'">&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; </SPAN></SPAN></SPAN><FONT face=Calibri size=3>The delta is a signed number. For some reason I assumed that the filter would output the ‘absolute value’ delta. Since, I couldn’t find an absolute value function in the expression syntax, my expressions doubled in complexity.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>So the moral of the story is that while there are lots of great things available for reuse in the System MPs (and System.Performance.DeltaValueCondition is one of them), the devil is in the details and in the requirements of your application. Particularly in scenarios with very long cycles.</FONT></P>
<P class=MsoNormal style="MARGIN: 0in 0in 10pt"><FONT face=Calibri size=3>Because of these issues, I decided to forgo the flexibility promised by my initial design based on System.Performance.DeltaValueCondition. I rewrote the data source module (which is script based) to incorporate the delta logic. This removed the need for <SPAN style="mso-spacerun: yes">&nbsp;</SPAN>System.Performance.DeltaValueCondition filter from my monitor type. The new data source outputs the ‘absolute value’ delta, and it has no statefulness<SPAN style="mso-spacerun: yes">&nbsp; </SPAN>(it queries for current and previous values each polling cycle) so there is no delay in alerting and no issues with service restarts. The big tradeoff now is some loss of flexibility.</FONT></P>]]></content:encoded>
    </item>
    <item>
      <title>SCOM Product Connector Notes</title>
      <link>https://docs.microsoft.com/archive/blogs/donmoore/scom-product-connector-notes</link>
      <pubDate>Thu, 15 May 2008 15:39:00 GMT</pubDate>
      <dc:creator><![CDATA[DonMooz]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/donmoore/2008/05/15/scom-product-connector-notes/</guid>
      <description><![CDATA[Updated: The sample code (see attachment) has been updated for SCOM 2012 R2.
I had the opportunity...]]></description>
      <content:encoded><![CDATA[<p class="MsoNormal" style="margin: 0in 0in 10pt;">Updated: The sample code (see attachment) has been updated for SCOM 2012 R2.</p>
<p class="MsoNormal" style="margin: 0in 0in 10pt;">I had the opportunity to build a simple product connector for System Center Operations Manager 2007 last week. A product connector is used to forward alert information from OpsMgr to an external system.&nbsp; This post describes how I built the product connector and contains some sample code. This sample extends the code found on MSDN by wrapping it in a windows service.</p>
<p class="MsoNormal" style="margin: 0in 0in 10pt;">Product connectors use the Operations Manager SDK to poll for alerts. The connector API has a built-in mechanism for marking processed alerts to prevent the connector from outputting the same alert every polling cycle. It also has a mechanism for controlling the number of alerts that are read during each polling cycle. Because of the polling approach, a connector must be hosted by something that can periodically wake up and perform the polling operation.&nbsp; The connector described here uses a service to host the polling logic.</p>
<p>Once you have installed a connector, the administrator must create a subscription that defines the alerts that are available to the connector. For more information on building and configuring product connectors, see <a href="https://msdn.microsoft.com/en-us/library/bb437511.aspx">https://msdn.microsoft.com/en-us/library/bb437511.aspx</a> and <a href="https://blogs.msdn.com/jakuboleksy/archive/2008/01/09/scom-connector-quickstart-guide.aspx">https://blogs.msdn.com/jakuboleksy/archive/2008/01/09/scom-connector-quickstart-guide.aspx</a>.</p>
<p>I simplified the connector that I built to create a small sample showing how to build an outbound connector. The sample was built using Visual Studio 2008 and written in c#. See the attached files for the complete sample.</p>
<p>This sample extends what you find on MSDN in the following ways:</p>
<ul>
<li>&nbsp;It is a Windows Service instead of a command line program. MSDN suggests that you create a windows service if you intend to run the connector continuously.</li>
<li>The sample includes code for use by the service installer.</li>
<li>The sample utilizes a thread timer to control the alert polling.&nbsp; The interval and startup delay are configurable via application settings.</li>
<li>The sample writes alert information to an XML formatted text file.</li>
</ul>
<p><strong>The Installer Class</strong></p>
<p>The project includes an installer class. The installer class will create the service and register the product connector using the Operations Manager SDK API. The uninstaller will do the reverse, as well as removing the subscriptions created for this connector.</p>
<p>You can install the service using the InstallUtil tool that is part of the .net framework. Look for this tool in the %windir%\Microsoft.NET\&lt;.net version&gt; folder.&nbsp; Use the /u option of InstallUtil To uninstall the service.</p>
<p>There are lots of names and descriptions to create when building a product connector. They are used for the service and for the product connector itself. Most are defined in the installer class. The service name is defined in the service class. You should update these names to be descriptive of your solution if you use this sample code.</p>
<p>The service is installed to run as local system by default. Consider changing this to use a less privileged account after installing the service.&nbsp; You can use the service control manager to change the credentials of the service after it is installed.</p>
<p>The service is installed with a run mode of &lsquo;Automatic&rsquo;. You must manually start the service after the installation, but it will start automatically after a system restart.</p>
<p>Every product connector must have a unique guid. There is a property on the service class that defines the guid for the sample. You should change this guid if you create a product connector based on this sample.</p>
<p><strong>The Configuration File</strong></p>
<p>I used the appsettings section in the app.config file for three settings:</p>
<ul>
<li>PollingIntervalMS &ndash; This determines how frequently the service will poll for new alerts (in milliseconds).&nbsp; You will need to find the correct tradeoff between polling overhead and latency for your application.</li>
<li>StartupDelay &ndash; This allows you to specify a delay before starting the polling when the service first starts. I have found that if you attempt to connect and poll for alerts before the SDK is ready after a system restart, the connection will fail. A delay of a few minutes seems to be sufficient to avoid this problem.</li>
<li>OutputFilePath &ndash; The full path for the output file where alerts are to be written.</li>
</ul>
<p><strong>The Management Group Connection</strong></p>
<p>The service is written such that it expects to run on the RMS. It connects to the management group using the &ldquo;localhost&rdquo; identifier.</p>
<p><strong>You Must Create a Subscription</strong></p>
<p>The service won&rsquo;t start outputting alerts to the file until you create a subscription. Use the Operations Console to do this. From the administrator view, select &lsquo;Product Connectors&rsquo; from the tree view on the left. If you have installed the sample product connector, you should see it in the list of product connectors. Select it, and create a subscription for it just as you would for a notification. Note that the MSDN documentation states that a connector with no subscription will process all alerts. My experience was that the connector did not process any alerts until at least one subscription was created for it. Perhaps I was not waiting long enough?</p>
<p><strong>The Timer</strong></p>
<p>The service is designed around a thread based timer. When the OnStart event runs, the timer is created using the interval and startup values from the configuration file.</p>
<p>A few things to note about the timer function. I designed it to use a &lsquo;lazy&rsquo; approach to connecting to the management group. When the timer function first runs, it detects that it needs to connect. Subsequent runs will use the existing connection. If an error of some sort occurs while connecting or polling for alerts, the connection is set back to null so that a new one will be created when the timer next fires. I wasn&rsquo;t sure if there was a performance benefit from holding onto the connection, but I assumed there was. This approach makes the service fairly robust, but may create lots of events in your application log if the connection or polling is persistently broken.</p>
<p><strong>The WriteAlert Method</strong></p>
<p>The sample defines a WriteAlert method that is called for each alert. The purpose of this method is to do the work of output the alert to your target system. In the sample, this method just writes the alert the output file as an XML fragment containing many of the alert property.&nbsp; There are many additional alert properties you might find useful in your connector.</p>
<p>The current WriteAlert method really only makes sense for demonstration purposes because it will result in an output file that grows continuously as alerts are processed. You will need to create a mechanism for rolling that file over or harvesting it in some way.</p>
<p>You will probably need to rewrite this function in your connector to format the alert as needed and to output to whatever device your external system requires.</p>
<p>You may also need to modify the OutputAlerts method to establish a connection your external device. This is the method that actually calls the WriteAlert method while walking over the alert collection.</p>
<p><strong>Event Logging</strong></p>
<p>The service will place events in the event log for any errors it encounters. You might consider setting up an Operations Manager rule to alert on these events. See the source code for specifics on event numbers. The event source will be the name of the service. Consider building a small MP to go along with your connector for the purpose of monitoring the connector.</p>
<p><strong>Project Notes</strong></p>
<p>The attachment to this post contains a zip file with the project used to build the service. The project contains references to both of the assemblies in the Program Files\System Center Operations Manager 2007\SDK Binaries folder.</p>
<p><strong>Interesting Things in the Database</strong></p>
<p>There are a few interesting columns in the AlertView view of the Operations Manager database that I have found useful in debugging connectors. They are ConnectorId and ConnectorStatus. The ConnectorId column will be populated with the guid for the connector that will process the alert. (I&rsquo;m curious about what would happen if two connectors have subscriptions that cover the same alert). The ConnectorStatus column is used to track whether or not the alert has been processed yet by the connector. A value of 1 in this column indicates that the alert is pending processing; a value of 2 indicates that processing is complete. Since the sample doesn&rsquo;t use the batch size version of the GetMonitoringAlerts method, you would expect all pending alerts that this connector subscribes to would be returned by that method.</p>
<p><strong>Disclaimer</strong></p>
<p>Use of included sample code is subject to the terms specified at <a href="https://www.microsoft.com/info/cpyright.htm">https://www.microsoft.com/info/cpyright.htm</a>.</p>
<p><strong>Next Steps For Sample Code Users</strong></p>
<ul>
<li>Consider adding more error checking everywhere.</li>
<li>Does your connector need to use the batching overloads of the GetMonitoringAlerts method?</li>
<li>Update the assorted names (connector name, description and service name).</li>
<li>Create a new GUID for your connector</li>
<li>Update the WriteAlert function to output the alert in the format you desire.</li>
<li>Consider building a small management pack to monitor your connector service.</li>
</ul>
<p class="MsoNormal" style="margin: 0in 0in 10pt;">&nbsp;</p>
<p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Components.PostAttachments/00/08/50/90/03/ProductConnectorSample2012.zip" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-components-postattachments/00-08-50-90-03/ProductConnectorSample2012.zip">ProductConnectorSample2012.zip</a></p>]]></content:encoded>
    </item>
  </channel>
</rss>