{"id":15,"date":"2023-03-21T03:26:32","date_gmt":"2023-03-21T03:26:32","guid":{"rendered":"https:\/\/brunocm.azurewebsites.net\/?page_id=15"},"modified":"2025-10-09T14:00:53","modified_gmt":"2025-10-09T12:00:53","slug":"part-2-first-c-extension","status":"publish","type":"page","link":"https:\/\/brunocm.azurewebsites.net\/?page_id=15","title":{"rendered":"Part 2 &#8211; First c# extension"},"content":{"rendered":"<h1>Build your first extension -&gt; device info<\/h1>\n<p>You might wonder why creating an extension on a C# assembly instead of writing a vbs, powershell ,\u2026. Script ?<\/p>\n<p>I see two advantages to do so:<\/p>\n<ol>\n<li>you can directly rely on the console&#8217;s connection information, no need to manage the connection on your own.<\/li>\n<li>The external executable solution <strong><u>only<\/u><\/strong> works if you select a <strong><u>single<\/u><\/strong> object in the console meaning you can&#8217;t run an action for several objects at once.<\/li>\n<\/ol>\n<p>Here I will explain how to create an extension that will show in a MessageBox all the collections a device is a member of.<\/p>\n<p>Edit: starting with SCCM CB 1906, this feature will be directly implemented in the console as a tab in the detail pane. This extension will then be useless but it&#8217;s still a good example of what can be achieved with custom extensions.<\/p>\n<p>First download Visual studio IDE (Community edition is enough and freely distributed by Microsoft)<\/p>\n<p>Start it and create a new &#8220;Class Library&#8221; project<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-41 size-medium\" src=\"\/wp-content\/uploads\/2023\/03\/image1-300x92.png\" alt=\"\" width=\"300\" height=\"92\" srcset=\"\/wp-content\/uploads\/2023\/03\/image1-300x92.png 300w, \/wp-content\/uploads\/2023\/03\/image1.png 688w\" sizes=\"(max-width: 300px) 85vw, 300px\" \/><\/p>\n<p>And chose a simple name for it<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-42 size-medium\" src=\"\/wp-content\/uploads\/2023\/03\/image2-300x37.png\" alt=\"\" width=\"300\" height=\"37\" srcset=\"\/wp-content\/uploads\/2023\/03\/image2-300x37.png 300w, \/wp-content\/uploads\/2023\/03\/image2.png 673w\" sizes=\"(max-width: 300px) 85vw, 300px\" \/><\/p>\n<p>The class automatically generated is always named Class1. Just change it for a more accurate one (right-click on &#8220;Class1&#8221; then select &#8220;rename&#8221;)<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-43 size-medium\" src=\"\/wp-content\/uploads\/2023\/03\/image3-300x50.png\" alt=\"\" width=\"300\" height=\"50\" srcset=\"\/wp-content\/uploads\/2023\/03\/image3-300x50.png 300w, \/wp-content\/uploads\/2023\/03\/image3.png 470w\" sizes=\"(max-width: 300px) 85vw, 300px\" \/><\/p>\n<p>Don&#8217;t forget to also rename the file or you can quickly get lost ^^<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-44 size-medium\" src=\"\/wp-content\/uploads\/2023\/03\/image4-300x106.png\" alt=\"\" width=\"300\" height=\"106\" srcset=\"\/wp-content\/uploads\/2023\/03\/image4-300x106.png 300w, \/wp-content\/uploads\/2023\/03\/image4.png 543w\" sizes=\"(max-width: 300px) 85vw, 300px\" \/><\/p>\n<p>The SCCM console is only 32bits so we need to limit compilation to X86 architecture.<\/p>\n<p>Open the small menu showing &#8220;Any CPU&#8221; then select configuration manager<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-45 size-full\" src=\"\/wp-content\/uploads\/2023\/03\/image5.png\" alt=\"\" width=\"243\" height=\"91\"><\/p>\n<p>Open &#8220;Active solution platform&#8221; and choose &#8220;&lt;New\u2026&gt;&#8221;<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-46 size-medium\" src=\"\/wp-content\/uploads\/2023\/03\/image6-300x71.png\" alt=\"\" width=\"300\" height=\"71\" srcset=\"\/wp-content\/uploads\/2023\/03\/image6-300x71.png 300w, \/wp-content\/uploads\/2023\/03\/image6.png 693w\" sizes=\"(max-width: 300px) 85vw, 300px\" \/><\/p>\n<p>Select x86 in the first dropdown menu and click &#8220;OK&#8221;<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-47 size-medium\" src=\"\/wp-content\/uploads\/2023\/03\/image7-300x157.png\" alt=\"\" width=\"300\" height=\"157\" srcset=\"\/wp-content\/uploads\/2023\/03\/image7-300x157.png 300w, \/wp-content\/uploads\/2023\/03\/image7.png 335w\" sizes=\"(max-width: 300px) 85vw, 300px\" \/><\/p>\n<p>Close the &#8220;configuration manager&#8221; window.<\/p>\n<p>We require some references to SCCM console assemblies. In the left pane, in the solution explorer, right-click on &#8220;References&#8221; in your project then select &#8220;Add Reference\u2026&#8221;<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-48 size-medium\" src=\"\/wp-content\/uploads\/2023\/03\/image8-300x193.png\" alt=\"\" width=\"300\" height=\"193\" srcset=\"\/wp-content\/uploads\/2023\/03\/image8-300x193.png 300w, \/wp-content\/uploads\/2023\/03\/image8.png 339w\" sizes=\"(max-width: 300px) 85vw, 300px\" \/><\/p>\n<p>Click on the &#8220;Browse&#8221; at the bottom of the &#8220;Reference Manager&#8221; windows, then browse your drive to the bin directory of the console installation folder and select &#8220;Microsoft.ConfigurationManagement.exe&#8221;. Repeat the same operation for &#8220;Microsoft.ConfigurationManagement.ManagementProvider.dll&#8221; and &#8220;AdminUI.WqlQueryEngine.dll&#8221;<\/p>\n<p>Now, create the function that we will call from the console:<\/p>\n<pre><span style=\"color: #0000ff;\">public static void <\/span>deviceCollections(<span style=\"color: #0000ff;\">object <\/span>sender, <span style=\"color: #33cccc;\">ScopeNode<\/span> scopenode, <span style=\"color: #33cccc;\">ActionDescription<\/span> action, <span style=\"color: #33cccc;\">IResultObject<\/span> result, <span style=\"color: #33cccc;\">PropertyDataUpdated<\/span> dataUpdatedDelegate, <span style=\"color: #33cccc;\">Status<\/span> status)\n{\n}<\/pre>\n<p>And add these line on top of the file:<\/p>\n<pre><span style=\"color: #0000ff;\">using <\/span>Microsoft.ConfigurationManagement.AdminConsole;\n<span style=\"color: #0000ff;\">using <\/span>Microsoft.ConfigurationManagement.AdminConsole.Schema;\n<span style=\"color: #0000ff;\">using <\/span>Microsoft.ConfigurationManagement.ManagementProvider;<\/pre>\n<p>That&#8217;s all!! you have an assembly that can be called from within the console. For now, it does nothing so let&#8217;s retrieve target object info and connection info.<\/p>\n<p>The &#8220;result&#8221; variable contains info about selected object(s). These objects can provide information about active connection but it&#8217;s safer to rely on the &#8220;scopenode&#8221; object that will always provide this information even if no object is selected.<\/p>\n<p>Add this at the beginning of your function<\/p>\n<pre><span style=\"color: #33cccc;\">ConsoleParentNode<\/span> consoleParentNode = scopenode <span style=\"color: #0000ff;\">as <\/span>ConsoleParentNode;\n\n<span style=\"color: #33cccc;\">ConnectionManagerBase<\/span> connectionMgr = consoleParentNode.RootConnectionNode.GetConnectionManagerInstance(<span style=\"color: #993300;\">\"WQL\"<\/span>);<\/pre>\n<p>Now we retrieve the device ResourceID and name with :<\/p>\n<pre><span style=\"color: #0000ff;\">string <\/span>deviceName = result.Properties[<span style=\"color: #993300;\">\"Name\"<\/span>].StringValue;\n\n<span style=\"color: #0000ff;\">string <\/span>resourceid = result.Properties[<span style=\"color: #993300;\">\"ResourceID\"<\/span>].StringValue;<\/pre>\n<p>The next task is to query SCCM for all collection that contains this device by submitting a WQL query to the active connection.<\/p>\n<pre><span style=\"color: #33cccc;\">IResultObject<\/span> collectionList = connectionMgr.QueryProcessor.ExecuteQuery(<span style=\"color: #993300;\">\"SELECT CollectionID FROM SMS_FullCollectionMembership where ResourceID = \"<\/span> + resourceid);<\/pre>\n<p>We will only retrieve collectionIDs. Getting the collection&#8217;s names in a single query is alittle bit trickier and will be covered later.<\/p>\n<p>Now prepare a message and show it to end-users!<\/p>\n<pre><span style=\"color: #33cccc;\">String<\/span> msg = deviceName + <span style=\"color: #993300;\">\" is presently member of collections with the following IDs:\\r\\n\"<\/span>;\n\n<span style=\"color: #0000ff;\">foreach <\/span>(<span style=\"color: #33cccc;\">IResultObject<\/span> collID <span style=\"color: #0000ff;\">in <\/span>collectionList)\n{\n    msg += collID.Properties[<span style=\"color: #993300;\">\"CollectionID\"<\/span>].StringValue +<span style=\"color: #993300;\"> \"\\r\\n\"<\/span>;\n}<\/pre>\n<p>Finally, show our message in a messageBox. ( don\u2019t forget to add &#8220;<span style=\"color: #0000ff;\">using <\/span>System.Windows.Forms;&#8221; on top of your file and add the corresponding reference to your project)<\/p>\n<p>The complete code should look like this:<\/p>\n<pre><span style=\"color: #0000ff;\">using <\/span>Microsoft.ConfigurationManagement.AdminConsole;\n<span style=\"color: #0000ff;\">using <\/span>Microsoft.ConfigurationManagement.AdminConsole.Schema;\n<span style=\"color: #0000ff;\">using <\/span>Microsoft.ConfigurationManagement.ManagementProvider;\n<span style=\"color: #0000ff;\">using <\/span>System;\n<span style=\"color: #0000ff;\">using <\/span>System.Collections.Generic;\n<span style=\"color: #0000ff;\">using <\/span>System.Linq;\n<span style=\"color: #0000ff;\">using <\/span>System.Text;\n<span style=\"color: #0000ff;\">using <\/span>System.Threading.Tasks;\n<span style=\"color: #0000ff;\">using<\/span> System.Windows.Forms;\n\n<span style=\"color: #0000ff;\">namespace<\/span> MyFirstExtension\n{\n<span style=\"color: #0000ff;\">   public class<\/span> <span style=\"color: #33cccc;\">DeviceTools<\/span>\n   {\n<span style=\"color: #0000ff;\">      public static void<\/span> deviceCollections(<span style=\"color: #0000ff;\">object<\/span> sender, <span style=\"color: #33cccc;\">ScopeNode<\/span> scopenode, <span style=\"color: #33cccc;\">ActionDescription<\/span> action, <span style=\"color: #33cccc;\">IResultObject<\/span> result, <span style=\"color: #33cccc;\">PropertyDataUpdated<\/span> dataUpdatedDelegate, <span style=\"color: #33cccc;\">Status<\/span> status)\n      {\n         <span style=\"color: #33cccc;\">ConsoleParentNode<\/span> consoleParentNode = scopenode <span style=\"color: #0000ff;\">as<\/span> <span style=\"color: #33cccc;\">ConsoleParentNode<\/span>;\n         <span style=\"color: #33cccc;\">ConnectionManagerBase<\/span> connectionMgr = consoleParentNode.RootConnectionNode.GetConnectionManagerInstance(<span style=\"color: #993300;\">\"WQL\"<\/span>);\n\n<span style=\"color: #0000ff;\">         string<\/span> deviceName = result.Properties[<span style=\"color: #993300;\">\"Name\"<\/span>].StringValue;\n<span style=\"color: #0000ff;\">         string<\/span> resourceid = result.Properties[<span style=\"color: #993300;\">\"ResourceID\"<\/span>].StringValue;\n\n         IResultObject collectionList = connectionMgr.QueryProcessor.ExecuteQuery(<span style=\"color: #993300;\">\"SELECT CollectionID FROM SMS_FullCollectionMembership where ResourceID = \"<\/span> + resourceid);\n\n         <span style=\"color: #33cccc;\">String<\/span> msg = deviceName + <span style=\"color: #993300;\">\" is presently member of collections with the following IDs:\\r\\n\"<\/span>;\n\n<span style=\"color: #0000ff;\">         foreach<\/span> (<span style=\"color: #33cccc;\">IResultObject<\/span> collID <span style=\"color: #0000ff;\">in<\/span> collectionList)\n         {\n            msg += collID.Properties[<span style=\"color: #993300;\">\"CollectionID\"<\/span>].StringValue + <span style=\"color: #993300;\">\"\\r\\n\"<\/span>;\n         }\n         <span style=\"color: #33cccc;\">MessageBox<\/span>.Show(msg, <span style=\"color: #993300;\">\"message\"<\/span>);\n      }\n   }\n}<\/pre>\n<p>Switch to &#8220;Release&#8221;<br \/>\n<img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-49 size-full\" src=\"\/wp-content\/uploads\/2023\/03\/image9.png\" alt=\"\" width=\"277\" height=\"58\"><\/p>\n<p>and build the solution.<\/p>\n<p>Browse your disk to your project folder<\/p>\n<p><img decoding=\"async\" loading=\"lazy\" class=\"alignnone wp-image-50 size-full\" src=\"\/wp-content\/uploads\/2023\/03\/image10-1024x101-1.png\" alt=\"\" width=\"1024\" height=\"101\" srcset=\"\/wp-content\/uploads\/2023\/03\/image10-1024x101-1.png 1024w, \/wp-content\/uploads\/2023\/03\/image10-1024x101-1-300x30.png 300w, \/wp-content\/uploads\/2023\/03\/image10-1024x101-1-768x76.png 768w\" sizes=\"(max-width: 709px) 85vw, (max-width: 909px) 67vw, (max-width: 1362px) 62vw, 840px\" \/><\/p>\n<p>And copy your Dll to a more dedicated folder (i.e: C:\\Program Files (x86)\\myCompany\\myextension)<\/p>\n<p>It&#8217;s time to modify our XML file to link the console to our dll.<\/p>\n<p>The Class of the ActionDescription becomes &#8220;AssemblyType&#8221; and the &#8220;Executable&#8221; section is replaced by a corresponding &#8220;ActionAssembly&#8221; section.<\/p>\n<pre>&lt;ActionDescription Class=\"AssemblyType\" DisplayName=\"show collections\" MnemonicDisplayName=\"show collections\" Description=\"Shows the list of collection IDs this device is member of\"&gt;\n   &lt;ShowOn&gt;\n      &lt;string&gt;ContextMenu&lt;\/string&gt;\n      &lt;string&gt;DefaultHomeTab&lt;\/string&gt;\n   &lt;\/ShowOn&gt;\n   <em><strong>&lt;ActionAssembly&gt;<\/strong><\/em>\n<em><strong>      &lt;Assembly&gt;C:\\Program Files (x86)\\myCompany\\myextension\\MyFirstExtension.dll&lt;\/Assembly&gt;<\/strong><\/em>\n<em><strong>      &lt;Type&gt;MyFirstExtension.DeviceTools&lt;\/Type&gt;<\/strong><\/em>\n<em><strong>      &lt;Method&gt;deviceCollections&lt;\/Method&gt;<\/strong><\/em>\n<em><strong>   &lt;\/ActionAssembly&gt;<\/strong><\/em>\n&lt;\/ActionDescription&gt;<\/pre>\n<p>Note that the &#8220;Type&#8221; item is composed of our assembly namespace followed by the class name.<\/p>\n<p>As usually, save this file and copy it in the dedicated folders.<\/p>\n<p>As I wrote before, this is not a C# programming course so the code is not optimal (no exception or error management &#8230;). There are a lot of good tutorials on the internet for that. With a little bit of reading and the Configuration Manager SDK code samples, I&#8217;m sure you will be able to do what you need.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Build your first extension -&gt; device info You might wonder why creating an extension on a C# assembly instead of writing a vbs, powershell ,\u2026. Script ? I see two advantages to do so: you can directly rely on the console&#8217;s connection information, no need to manage the connection on your own. The external executable &hellip; <a href=\"https:\/\/brunocm.azurewebsites.net\/?page_id=15\" class=\"more-link\">Continue reading<span class=\"screen-reader-text\"> &#8220;Part 2 &#8211; First c# extension&#8221;<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"open","ping_status":"closed","template":"","meta":{"_sitemap_exclude":false,"_sitemap_priority":"","_sitemap_frequency":""},"_links":{"self":[{"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=\/wp\/v2\/pages\/15"}],"collection":[{"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=15"}],"version-history":[{"count":4,"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=\/wp\/v2\/pages\/15\/revisions"}],"predecessor-version":[{"id":79,"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=\/wp\/v2\/pages\/15\/revisions\/79"}],"wp:attachment":[{"href":"https:\/\/brunocm.azurewebsites.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=15"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}