<?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>Come n see what I'm upto...</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/padmanr/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/padmanr/feed.xml</link>
    <description />
    <lastBuildDate>Wed, 17 Apr 2019 03:41:29 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Execute a process on remote machine, wait for it to exit and retrieve its exit code using WMI</title>
      <link>https://docs.microsoft.com/archive/blogs/padmanr/execute-a-process-on-remote-machine-wait-for-it-to-exit-and-retrieve-its-exit-code-using-wmi</link>
      <pubDate>Sat, 08 May 2010 11:23:38 GMT</pubDate>
      <dc:creator><![CDATA[anandr_27]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/padmanr/2010/05/08/execute-a-process-on-remote-machine-wait-for-it-to-exit-and-retrieve-its-exit-code-using-wmi/</guid>
      <description><![CDATA[This week I spent quite sometime reading through articles and trying to understand how to run a...]]></description>
      <content:encoded><![CDATA[<p>This week I spent quite sometime reading through articles and trying to understand how to run a process on a remote machine and also get the exit code of the process once it terminates. I ran into this issue after having used PSExec to do the same. While PSExec does a great job of executing a process, getting its StdOut and the exit code, while using it with .Net, I came across a bunch of issues. Ultimately I found a neat way to do it using nothing but WMI calls.</p> <p>The solution has multiple parts as follows,</p> <p>1. Start the remote process</p> <p>2. Find if the remote process is running and if it does, start an event monitor to wait for it to exit</p> <p>3. Once the process exits, retrieve its exit code</p> <h5>Start the process using WMI<pre class="code"><span style="color: #2b91af">ConnectionOptions </span>connOptions = <span style="color: blue">new </span><span style="color: #2b91af">ConnectionOptions</span>();
connOptions.Impersonation = <span style="color: #2b91af">ImpersonationLevel</span>.Impersonate;
connOptions.EnablePrivileges = <span style="color: blue">true</span>;
<span style="color: #2b91af">ManagementScope </span>manScope = <span style="color: blue">new </span><span style="color: #2b91af">ManagementScope</span>(<span style="color: #2b91af">String</span>.Format(<span style="color: #a31515">@"\\{0}\ROOT\CIMV2"</span>, remoteComputerName), connOptions);

<span style="color: blue">try
</span>{
    manScope.Connect();
}
<span style="color: blue">catch </span>(<span style="color: #2b91af">Exception </span>e)
{
    <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"Management Connect to remote machine " </span>+ remoteComputerName + <span style="color: #a31515">" as user " </span>+ strUserName + <span style="color: #a31515">" failed with the following error " </span>+ e.Message);
}
<span style="color: #2b91af">ObjectGetOptions </span>objectGetOptions = <span style="color: blue">new </span><span style="color: #2b91af">ObjectGetOptions</span>();
<span style="color: #2b91af">ManagementPath </span>managementPath = <span style="color: blue">new </span><span style="color: #2b91af">ManagementPath</span>(<span style="color: #a31515">"Win32_Process"</span>);
<span style="color: blue">using </span>(<span style="color: #2b91af">ManagementClass </span>processClass = <span style="color: blue">new </span><span style="color: #2b91af">ManagementClass</span>(manScope, managementPath, objectGetOptions))
{
    <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementBaseObject </span>inParams = processClass.GetMethodParameters(<span style="color: #a31515">"Create"</span>))
    {
        inParams[<span style="color: #a31515">"CommandLine"</span>] = arguments;
        <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementBaseObject </span>outParams = processClass.InvokeMethod(<span style="color: #a31515">"Create"</span>, inParams, <span style="color: blue">null</span>))
        {

            <span style="color: blue">if </span>((<span style="color: blue">uint</span>)outParams[<span style="color: #a31515">"returnValue"</span>] != 0)
            {
                <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"Error while starting process " </span>+ arguments + <span style="color: #a31515">" creation returned an exit code of " </span>+ outParams[<span style="color: #a31515">"returnValue"</span>] + <span style="color: #a31515">". It was launched as " </span>+ strUserName + <span style="color: #a31515">" on " </span>+ remoteComputerName);
            }
            <span style="color: blue">this</span>.ProcessId = (<span style="color: blue">uint</span>)outParams[<span style="color: #a31515">"processId"</span>];

        }
    }
}
</pre>
<p>&nbsp;</p></h5>
<p>The above code snippet launches the process on a remote machine using the “Create” method in “Win32_Process” class. The create method returns a value that indicates if the process was launched successfully or not. If the process was launched successfully, it also returns the process id.</p>
<h5>Start an event handler to wait for the process to exit<pre class="code"><span style="color: #2b91af">ManualResetEvent </span>mre = <span style="color: blue">new </span><span style="color: #2b91af">ManualResetEvent</span>(<span style="color: blue">false</span>);
<span style="color: #2b91af">WqlEventQuery </span>q = <span style="color: blue">new </span><span style="color: #2b91af">WqlEventQuery</span>(<span style="color: #a31515">"Win32_ProcessStopTrace"</span>);
<span style="color: blue">using </span>(<span style="color: #2b91af">ManagementEventWatcher </span>w = <span style="color: blue">new </span><span style="color: #2b91af">ManagementEventWatcher</span>(manScope, q))
{
    w.EventArrived += <span style="color: blue">new </span><span style="color: #2b91af">EventArrivedEventHandler</span>(<span style="color: blue">this</span>.ProcessStoptEventArrived);
    w.Start();
    <span style="color: blue">if </span>(!mre.WaitOne(WaitTimePerCommand,<span style="color: blue">false</span>))
    {
        w.Stop();
        <span style="color: blue">this</span>.EventArrived = <span style="color: blue">false</span>;
    }
    <span style="color: blue">else
        </span>w.Stop();
}
</pre></h5>
<p>The above snippet connects to the “Win32_ProcessStopTrace” event and sets a callback to be called when any process exits on the machine. Once the callback is set, we can wait in a loop until we receive the notification for our process or use the ManualResetEvent provided in the System.Threading namespace to indicate that our process has exited. I also have a timeout set in the Event so that if the process takes longer than expected, we can kill it.</p>
<p>The callback method is a simple function in which you can check if the exited process is the process that we are waiting for using the process id as below,<pre class="code"><span style="color: blue">public void </span>ProcessStoptEventArrived(<span style="color: blue">object </span>sender, <span style="color: #2b91af">EventArrivedEventArgs </span>e)
{
    <span style="color: blue">if </span>((<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ProcessId"</span>].Value == ProcessId)
    {
        <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">"Process: {0}, Stopped with Code: {1}"</span>, (<span style="color: blue">int</span>)(<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ProcessId"</span>].Value, (<span style="color: blue">int</span>)(<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ExitStatus"</span>].Value);
        ExitCode = (<span style="color: blue">int</span>)(<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ExitStatus"</span>].Value;
        mre.Set();
    }
}
</pre>
<p>&nbsp;</p>
<p>The event also gives us the exit code of the exiting process!</p>
<p>I have provided a complete class that can be used to launch any process on a remote box below.<pre class="code"><span style="color: blue">public class </span><span style="color: #2b91af">ProcessWMI
</span>{
    <span style="color: blue">public uint </span>ProcessId;
    <span style="color: blue">public int </span>ExitCode;
    <span style="color: blue">public bool </span>EventArrived;
    <span style="color: blue">public </span><span style="color: #2b91af">ManualResetEvent </span>mre = <span style="color: blue">new </span><span style="color: #2b91af">ManualResetEvent</span>(<span style="color: blue">false</span>);
    <span style="color: blue">public void </span>ProcessStoptEventArrived(<span style="color: blue">object </span>sender, <span style="color: #2b91af">EventArrivedEventArgs </span>e)
    {
        <span style="color: blue">if </span>((<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ProcessId"</span>].Value == ProcessId)
        {
            <span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">"Process: {0}, Stopped with Code: {1}"</span>, (<span style="color: blue">int</span>)(<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ProcessId"</span>].Value, (<span style="color: blue">int</span>)(<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ExitStatus"</span>].Value);
            ExitCode = (<span style="color: blue">int</span>)(<span style="color: blue">uint</span>)e.NewEvent.Properties[<span style="color: #a31515">"ExitStatus"</span>].Value;
            EventArrived = <span style="color: blue">true</span>;
            mre.Set();
        }
    }
    <span style="color: blue">public </span>ProcessWMI()
    {
        <span style="color: blue">this</span>.ProcessId = 0;
        ExitCode = -1;
        EventArrived = <span style="color: blue">false</span>;
    }
    <span style="color: blue">public void </span>ExecuteRemoteProcessWMI(<span style="color: blue">string </span>remoteComputerName, <span style="color: blue">string </span>arguments, <span style="color: blue">int </span>WaitTimePerCommand)
    {
        <span style="color: blue">string </span>strUserName = <span style="color: blue">string</span>.Empty;
        <span style="color: blue">try
        </span>{
            <span style="color: #2b91af">ConnectionOptions </span>connOptions = <span style="color: blue">new </span><span style="color: #2b91af">ConnectionOptions</span>();
            connOptions.Impersonation = <span style="color: #2b91af">ImpersonationLevel</span>.Impersonate;
            connOptions.EnablePrivileges = <span style="color: blue">true</span>;
            <span style="color: #2b91af">ManagementScope </span>manScope = <span style="color: blue">new </span><span style="color: #2b91af">ManagementScope</span>(<span style="color: #2b91af">String</span>.Format(<span style="color: #a31515">@"\\{0}\ROOT\CIMV2"</span>, remoteComputerName), connOptions);

            <span style="color: blue">try
            </span>{
                manScope.Connect();
            }
            <span style="color: blue">catch </span>(<span style="color: #2b91af">Exception </span>e)
            {
                <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"Management Connect to remote machine " </span>+ remoteComputerName + <span style="color: #a31515">" as user " </span>+ strUserName + <span style="color: #a31515">" failed with the following error " </span>+ e.Message);
            }
            <span style="color: #2b91af">ObjectGetOptions </span>objectGetOptions = <span style="color: blue">new </span><span style="color: #2b91af">ObjectGetOptions</span>();
            <span style="color: #2b91af">ManagementPath </span>managementPath = <span style="color: blue">new </span><span style="color: #2b91af">ManagementPath</span>(<span style="color: #a31515">"Win32_Process"</span>);
            <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementClass </span>processClass = <span style="color: blue">new </span><span style="color: #2b91af">ManagementClass</span>(manScope, managementPath, objectGetOptions))
            {
                <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementBaseObject </span>inParams = processClass.GetMethodParameters(<span style="color: #a31515">"Create"</span>))
                {
                    inParams[<span style="color: #a31515">"CommandLine"</span>] = arguments;
                    <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementBaseObject </span>outParams = processClass.InvokeMethod(<span style="color: #a31515">"Create"</span>, inParams, <span style="color: blue">null</span>))
                    {

                        <span style="color: blue">if </span>((<span style="color: blue">uint</span>)outParams[<span style="color: #a31515">"returnValue"</span>] != 0)
                        {
                            <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"Error while starting process " </span>+ arguments + <span style="color: #a31515">" creation returned an exit code of " </span>+ outParams[<span style="color: #a31515">"returnValue"</span>] + <span style="color: #a31515">". It was launched as " </span>+ strUserName + <span style="color: #a31515">" on " </span>+ remoteComputerName);
                        }
                        <span style="color: blue">this</span>.ProcessId = (<span style="color: blue">uint</span>)outParams[<span style="color: #a31515">"processId"</span>];
                    }
                }
            }

            <span style="color: #2b91af">SelectQuery </span>CheckProcess = <span style="color: blue">new </span><span style="color: #2b91af">SelectQuery</span>(<span style="color: #a31515">"Select * from Win32_Process Where ProcessId = " </span>+ ProcessId);
            <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementObjectSearcher </span>ProcessSearcher = <span style="color: blue">new </span><span style="color: #2b91af">ManagementObjectSearcher</span>(manScope, CheckProcess))
            {
                <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementObjectCollection </span>MoC = ProcessSearcher.Get())
                {
                    <span style="color: blue">if </span>(MoC.Count == 0)
                    {
                        <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"ERROR AS WARNING: Process " </span>+ arguments + <span style="color: #a31515">" terminated before it could be tracked on " </span>+ remoteComputerName);
                    }
                }
            }

            <span style="color: #2b91af">WqlEventQuery </span>q = <span style="color: blue">new </span><span style="color: #2b91af">WqlEventQuery</span>(<span style="color: #a31515">"Win32_ProcessStopTrace"</span>);
            <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementEventWatcher </span>w = <span style="color: blue">new </span><span style="color: #2b91af">ManagementEventWatcher</span>(manScope, q))
            {
                w.EventArrived += <span style="color: blue">new </span><span style="color: #2b91af">EventArrivedEventHandler</span>(<span style="color: blue">this</span>.ProcessStoptEventArrived);
                w.Start();
                <span style="color: blue">if </span>(!mre.WaitOne(WaitTimePerCommand,<span style="color: blue">false</span>))
                {
                    w.Stop();
                    <span style="color: blue">this</span>.EventArrived = <span style="color: blue">false</span>;
                }
                <span style="color: blue">else
</span>                    w.Stop();
            }
            <span style="color: blue">if </span>(!<span style="color: blue">this</span>.EventArrived)
            {
                <span style="color: #2b91af">SelectQuery </span>sq = <span style="color: blue">new </span><span style="color: #2b91af">SelectQuery</span>(<span style="color: #a31515">"Select * from Win32_Process Where ProcessId = " </span>+ ProcessId);
                <span style="color: blue">using </span>(<span style="color: #2b91af">ManagementObjectSearcher </span>searcher = <span style="color: blue">new </span><span style="color: #2b91af">ManagementObjectSearcher</span>(manScope, sq))
                {
                    <span style="color: blue">foreach </span>(<span style="color: #2b91af">ManagementObject </span>queryObj <span style="color: blue">in </span>searcher.Get())
                    {
                        queryObj.InvokeMethod(<span style="color: #a31515">"Terminate"</span>, <span style="color: blue">null</span>);
                        queryObj.Dispose();
                        <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"Process " </span>+ arguments + <span style="color: #a31515">" timed out and was killed on " </span>+ remoteComputerName);
                    }
                }
            }
            <span style="color: blue">else
            </span>{
                <span style="color: blue">if </span>(<span style="color: blue">this</span>.ExitCode != 0)
                    <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: #a31515">"Process " </span>+ arguments + <span style="color: #a31515">"exited with exit code " </span>+ <span style="color: blue">this</span>.ExitCode + <span style="color: #a31515">" on " </span>+ remoteComputerName + <span style="color: #a31515">" run as " </span>+ strUserName);
                <span style="color: blue">else
                    </span><span style="color: #2b91af">Console</span>.WriteLine(<span style="color: #a31515">"process exited with Exit code 0"</span>);
            }
            
        }
        <span style="color: blue">catch </span>(<span style="color: #2b91af">Exception </span>e)
        {
            <span style="color: blue">throw new </span><span style="color: #2b91af">Exception</span>(<span style="color: blue">string</span>.Format(<span style="color: #a31515">"Execute process failed Machinename {0}, ProcessName {1}, RunAs {2}, Error is {3}, Stack trace {4}"</span>, remoteComputerName, arguments, strUserName, e.Message, e.StackTrace), e);
        }
    }
}
</pre>
<p>&nbsp;</p>
<p>This can be used in a program with two lines of code.<pre class="code"><span style="color: #2b91af">ProcessWMI </span>p = <span style="color: blue">new </span><span style="color: #2b91af">ProcessWMI</span>();
p.ExecuteRemoteProcessWMI(remoteMachine, sBatFile, timeout);</pre>
<p>One thing that I am yet to do is to try and capture the StdOut and StdErr of the remote process.. </p>]]></content:encoded>
    </item>
  </channel>
</rss>