Power up Hyper_V client in C# using WMI - c#

I am new to coding the Hyper-V within WMI, and always welcome to a learning opportunity in this area.
There is a need for me to create a winform application that lists all VMs available within a computer. When a user clicks on one VM, it will launch the Hyper-V client window.
My codes below could pretty much start or stop any specific VM. However, it doesn't launch the hyper-v client window.
Here are my prototype codes (in command lines for now):
using System;
using System.Management;
namespace HyperVSamples
{
public class RequestStateChangeClass
{
public static void RequestStateChange(string vmName, string action)
{
ManagementScope scope = new ManagementScope(#"\\.\root\virtualization\v2", null);
ManagementObject vm = Utility.GetTargetComputer(vmName, scope);
if (null == vm)
{
throw new ArgumentException(
string.Format(
"The virtual machine '{0}' could not be found.",
vmName));
}
ManagementBaseObject inParams = vm.GetMethodParameters("RequestStateChange");
const int Enabled = 2;
const int Disabled = 3;
if (action.ToLower() == "start")
{
inParams["RequestedState"] = Enabled;
}
else if (action.ToLower() == "stop")
{
inParams["RequestedState"] = Disabled;
}
else
{
throw new Exception("Wrong action is specified");
}
ManagementBaseObject outParams = vm.InvokeMethod(
"RequestStateChange",
inParams,
null);
if ((UInt32)outParams["ReturnValue"] == ReturnCode.Started)
{
if (Utility.JobCompleted(outParams, scope))
{
Console.WriteLine(
"{0} state was changed successfully.",
vmName);
}
else
{
Console.WriteLine("Failed to change virtual system state");
}
}
else if ((UInt32)outParams["ReturnValue"] == ReturnCode.Completed)
{
Console.WriteLine(
"{0} state was changed successfully.",
vmName);
}
else
{
Console.WriteLine(
"Change virtual system state failed with error {0}",
outParams["ReturnValue"]);
}
}
public static void Main(string[] args)
{
if (args != null && args.Length != 2)
{
Console.WriteLine("Usage: <application> vmName action");
Console.WriteLine("action: start|stop");
return;
}
RequestStateChange(args[0], args[1]);
}
}
}
Given:
The computer has Hyper-V manager installed with several pre-populated VMs.
Question:
How would I fire up the hyper-v client window from a winform?
Thanks

After taking some research, it appears firing up the hyper-v client is quite simple.... Below is the full function just in case anyone looks for it in the future...
public static string ConnectVM(string VMName)
{
var error = string.Empty;
var runspace = RunspaceFactory.CreateRunspace();
runspace.Open();
//create a pipeline
var path = ConfigurationManager.AppSettings["VMConnectPath"];
var pipeline = runspace.CreatePipeline();
pipeline.Commands.AddScript($"& \"{path}\" localhost '{VMName}'");
try
{
pipeline.Invoke();
}
catch (Exception e)
{
error = e.Message;
}
runspace.Close();
return error;
}

Related

Missing Output when Redirecting Standard Error in C# / .NET Process

I'm working with a 3rd party, command-line tool called "sam-ba" v3.5 (available here for free). It's a C++ / QML command line tool that interfaces with a hardware module to read/write data. Output from commands, in most cases, is sent to Standard Error.
I have a C# / .NET application that creates a Process object to execute the sam-ba tool and run commands. Executing the commands works as expected. What doesn't always work is the redirect of the Standard Error output. In some commands, part or all of the output is not received by the C# application. For example, here is the execution of a command using the sam-ba tool directly at the Windows 10 command line:
C:\Temp\Stuff\sam-ba_3.5>sam-ba -p serial:COM5 -d sama5d3 -m version
Error: Cannot open invalid port 'COM5'
Cannot open invalid port 'COM5'
Here is some simple code from a C# application to create a Process object to execute the sam-ba tool with the same command:
Process p = new Process
{
StartInfo = new ProcessStartInfo("sam-ba.exe", "-p serial:COM5 -d sama5d3 -m version")
{
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
p.Start();
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
Console.WriteLine("Standard Out: " + output);
Console.WriteLine("Standard Error: " + error);
The Output of the C# application:
Standard Out:
Standard Error: Cannot open invalid port 'COM5'
In this simple example, only 1 of the output lines is redirected to Standard Error while the other is not. I've tried many different commands and results are mixed. Sometimes I get everything, sometimes partial output, sometimes no output.
Now ... here's the real issue. The following is a python script (v3.8) that does exactly what the C# application is doing:
import subprocess
import sys
result = subprocess.run("sam-ba.exe -p serial:COM5 -d sama5d3 -m version", capture_output=True, text=True)
print("stdout:", result.stdout)
print("stderr:", result.stderr)
This script always returns the correct output to standard error. BUT ... when I run this script from the C# app to create a chain of C# -> python -> sam-ba, I get the same issue of output missing from the stream.
This has led me to 2 conclusions:
Something in that sam-ba tool is different about the way it is outputting its text. Format, content, ... something. There's an inconsistency going on somewhere in that code
Something is different about the environment created by the C# Process object when executing external applications that doesn't happen when the external application is run directly. Otherwise, why would the python script get all the output when run directly, but not when run through the C# Process object?
It is #2 that has brought me here. I'm looking for any insight on how to diagnose this. Something I'm doing wrong, settings I can try within the Process object, thoughts on how data can go into a stream and not come out on redirect, or if anyone has ever seen something like this before and how they resolved it.
UPDATE
Got a hold of the sam-ba tool's source code. The output the C# app is not capturing is coming from the QML files. They are using this 'print()' method that I can't really find any details on. The output the C# app can capture is being delivered back to the C++ side via signals and then sent to standard error. This feeds back into my conclusion #1 where they have inconsistencies in their code.
Still, this potentially means there is a conflict between C# and QT/QML, which would explain why the Python script gets the QML output, but the C# app does not.
The following uses ShellExecute instead of CreateProcess when running
process. When using ShellExecute one can't re-direct StandardOutput and/or StandardError for Process. To work around this, both StandardOutput and StandardError are re-directed to a temp file, and then the data is read from the temp file--which seems to result in the same output that one sees when running from a cmd window.
Note: In the following code it's necessary to use %windir%\system32\cmd.exe (ex: C:\Windows\system32\cmd.exe) with the /c option. See the usage section below.
Add using statement: using System.Diagnostics;
Then try the following:
public string RunProcess(string fqExePath, string arguments, bool runAsAdministrator = false)
{
string result = string.Empty;
string tempFilename = System.IO.Path.Combine(System.IO.Path.GetTempPath(), "tempSam-ba.txt");
string tempArguments = arguments;
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
return "Error: fqExePath not specified";
}
//redirect both StandardOutput and StandardError to a temp file
if (!arguments.Contains("2>&1"))
{
tempArguments += String.Format(" {0} {1} {2}", #"1>", tempFilename, #"2>&1");
}
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(fqExePath, tempArguments);
if (runAsAdministrator)
{
startInfo.Verb = "runas"; //elevates permissions
}//if
//set environment variables
//pStartInfo.EnvironmentVariables["SomeVar"] = "someValue";
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardInput = false;
startInfo.UseShellExecute = true; //use ShellExecute instead of CreateProcess
startInfo.CreateNoWindow = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(fqExePath);
using (Process p = Process.Start(startInfo))
{
//start
p.Start();
//waits until the process is finished before continuing
p.WaitForExit();
}
//read output from temp file
//file may still be in use, so try to read it.
//if it is still in use, sleep and try again
if (System.IO.File.Exists(tempFilename))
{
string errMsg = string.Empty;
int count = 0;
do
{
//re-initialize
errMsg = string.Empty;
try
{
result = System.IO.File.ReadAllText(tempFilename);
Debug.WriteLine(result);
}
catch(System.IO.IOException ex)
{
errMsg = ex.Message;
}
catch (Exception ex)
{
errMsg = ex.Message;
}
System.Threading.Thread.Sleep(125);
count += 1; //increment
} while (!String.IsNullOrEmpty(errMsg) && count < 10);
//delete temp file
System.IO.File.Delete(tempFilename);
}
return result;
}
Usage:
RunProcess(#"C:\Windows\system32\cmd.exe", #"/c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version");
Note: /c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version is the value of the process "Argument" property.
Update:
Option 2:
Here's a solution that uses named pipes. Process is used to redirect the output to a named pipe instead of a file. One creates a named pipe "server" which listens for a connection from a client.Then System.Diagnostics.Process is used to run the desired command and redirect the output to the named pipe server. The "server" reads the output, and then raises event "DataReceived" which will return the data to any subscribers.
The named pipe server code is from here, however I've modified it. I've added numerous events--which can be subscribed to. I've also added the ability for the server to shut itself down after it's finished reading the data by setting "ShutdownWhenOperationComplete" to "true".
Create a class named: HelperNamedPipeServer.cs
HelperNamedPipeServer.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.IO.Pipes;
using System.IO;
using System.Diagnostics;
using System.Threading;
using System.Security.Principal;
namespace ProcessTest
{
public class HelperNamedPipeServer : IDisposable
{
//delegates
public delegate void EventHandlerClientConnected(object sender, bool e);
public delegate void EventHandlerDataReceived(object sender, string data);
public delegate void EventHandlerOperationCompleted(object sender, bool e);
public delegate void EventHandlerMessageComplete(object sender, bool e);
public delegate void EventHandlerReadComplete(object sender, bool e);
public delegate void EventHandlerServerShutdown(object sender, bool e);
public delegate void EventHandlerServerStarted(object sender, bool e);
//event that subscribers can subscribe to
public event EventHandlerClientConnected ClientConnected;
public event EventHandlerDataReceived DataReceived;
public event EventHandlerMessageComplete MessageReadComplete;
public event EventHandlerOperationCompleted OperationCompleted;
public event EventHandlerReadComplete ReadComplete;
public event EventHandlerServerShutdown ServerShutdown;
public event EventHandlerServerStarted ServerStarted;
public bool IsClientConnected
{
get
{
if (_pipeServer == null)
{
return false;
}
else
{
return _pipeServer.IsConnected;
}
}
}
public string PipeName { get; set; } = string.Empty;
public bool ShutdownWhenOperationComplete { get; set; } = false;
//private int _bufferSize = 4096;
private int _bufferSize = 65535;
//private volatile NamedPipeServerStream _pipeServer = null;
private NamedPipeServerStream _pipeServer = null;
public HelperNamedPipeServer()
{
PipeName = "sam-ba-pipe";
}
public HelperNamedPipeServer(string pipeName)
{
PipeName = pipeName;
}
private NamedPipeServerStream CreateNamedPipeServerStream(string pipeName)
{
//named pipe with security
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinAdministratorsSid, null); //member of Administrators group
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.WorldSid, null); //everyone
//SecurityIdentifier sid = new SecurityIdentifier(WellKnownSidType.BuiltinUsersSid, null); //member of Users group
//PipeAccessRule rule = new PipeAccessRule(sid, PipeAccessRights.ReadWrite, System.Security.AccessControl.AccessControlType.Allow);
//PipeSecurity pSec = new PipeSecurity();
//pSec.AddAccessRule(rule);
//named pipe - with specified security
//return new NamedPipeServerStream(PipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous, _bufferSize, _bufferSize, pSec);
//named pipe - access for everyone
//return new System.IO.Pipes.NamedPipeServerStream(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Message, PipeOptions.Asynchronous);
return new System.IO.Pipes.NamedPipeServerStream(pipeName, PipeDirection.InOut, NamedPipeServerStream.MaxAllowedServerInstances, PipeTransmissionMode.Byte, PipeOptions.Asynchronous);
}
public void Dispose()
{
Shutdown();
}
private void OnClientConnected()
{
LogMsg("OnClientConnected");
//raise event
if (ClientConnected != null)
ClientConnected(this, true);
}
private void OnDataReceived(string data)
{
LogMsg("OnClientConnected");
//raise event
if (DataReceived != null && !String.IsNullOrEmpty(data))
{
if (DataReceived != null)
DataReceived(this, data);
}
}
private void OnMessageReadComplete()
{
LogMsg("OnMessageReadComplete");
//raise event
if (MessageReadComplete != null)
MessageReadComplete(this, true);
}
private void OnOperationCompleted()
{
LogMsg("OnOperationCompleted");
//raise event
if (OperationCompleted != null)
OperationCompleted(this, true);
}
private void OnReadComplete()
{
LogMsg("OnReadComplete");
//raise event
if (ReadComplete != null)
ReadComplete(this, true);
}
private void OnServerShutdown()
{
LogMsg("OnServerShutdown");
//raise event
if (ServerShutdown != null)
ServerShutdown(this, true);
}
private void OnServerStarted()
{
LogMsg("OnServerStarted");
//raise event
if (ServerStarted != null)
ServerStarted(this, true);
}
private async void DoConnectionLoop(IAsyncResult result)
{ //wait for connection, then process the data
if (!result.IsCompleted) return;
if (_pipeServer == null) return;
//IOException = pipe is broken
//ObjectDisposedException = cannot access closed pipe
//OperationCanceledException - read was canceled
//accept client connection
try
{
//client connected - stop waiting for connection
_pipeServer.EndWaitForConnection(result);
OnClientConnected(); //raise event
}
catch (IOException) { RebuildNamedPipe(); return; }
catch (ObjectDisposedException) { RebuildNamedPipe(); return; }
catch (OperationCanceledException) { RebuildNamedPipe(); return; }
while (IsClientConnected)
{
if (_pipeServer == null) break;
try
{
// read from client
string clientMessage = await ReadClientMessageAsync(_pipeServer);
OnDataReceived(clientMessage); //raise event
}
catch (IOException) { RebuildNamedPipe(); return; }
catch (ObjectDisposedException) { RebuildNamedPipe(); return; }
catch (OperationCanceledException) { RebuildNamedPipe(); return; }
}
//raise event
OnOperationCompleted();
if (!ShutdownWhenOperationComplete)
{
//client disconnected. start listening for clients again
if (_pipeServer != null)
RebuildNamedPipe();
}
else
{
Shutdown();
}
}
private void LogMsg(string msg)
{
//ToDo: log message
string output = String.Format("{0} - {1}", DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss"), msg);
//ToDo: uncomment this line, if desired
//Debug.WriteLine(output);
}
private void RebuildNamedPipe()
{
Shutdown();
_pipeServer = CreateNamedPipeServerStream(PipeName);
_pipeServer.BeginWaitForConnection(DoConnectionLoop, null);
}
private async Task<string> ReadClientMessageAsync(NamedPipeServerStream stream)
{
byte[] buffer = null;
string clientMsg = string.Empty;
StringBuilder sb = new StringBuilder();
int msgIndex = 0;
int read = 0;
LogMsg("Reading message...");
if (stream.ReadMode == PipeTransmissionMode.Byte)
{
LogMsg("PipeTransmissionMode.Byte");
//byte mode ignores message boundaries
do
{
//create instance
buffer = new byte[_bufferSize];
read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read > 0)
{
clientMsg = Encoding.UTF8.GetString(buffer, 0, read);
//string clientMsg = Encoding.Default.GetString(buffer, 0, read);
//remove newline
//clientMsg = System.Text.RegularExpressions.Regex.Replace(clientString, #"\r\n|\t|\n|\r|", "");
//LogMsg("clientMsg [" + msgIndex + "]: " + clientMsg);
sb.Append(clientMsg);
msgIndex += 1; //increment
}
} while (read > 0);
//raise event
OnReadComplete();
OnMessageReadComplete();
}
else if (stream.ReadMode == PipeTransmissionMode.Message)
{
LogMsg("PipeTransmissionMode.Message");
do
{
do
{
//create instance
buffer = new byte[_bufferSize];
read = await stream.ReadAsync(buffer, 0, buffer.Length);
if (read > 0)
{
clientMsg = Encoding.UTF8.GetString(buffer, 0, read);
//string clientMsg = Encoding.Default.GetString(buffer, 0, read);
//remove newline
//clientMsg = System.Text.RegularExpressions.Regex.Replace(clientString, #"\r\n|\t|\n|\r|", "");
//LogMsg("clientMsg [" + msgIndex + "]: " + clientMsg);
sb.Append(clientMsg);
msgIndex += 1; //increment
}
} while (!stream.IsMessageComplete);
//raise event
OnMessageReadComplete();
} while (read > 0);
//raise event
OnReadComplete();
LogMsg("message completed");
}
return sb.ToString();
}
private void Shutdown()
{
LogMsg("Shutting down named pipe server");
if (_pipeServer != null)
{
try { _pipeServer.Close(); } catch { }
try { _pipeServer.Dispose(); } catch { }
_pipeServer = null;
}
}
public void StartServer(object obj = null)
{
LogMsg("Info: Starting named pipe server...");
_pipeServer = CreateNamedPipeServerStream(PipeName);
_pipeServer.BeginWaitForConnection(DoConnectionLoop, null);
}
public void StopServer()
{
Shutdown();
OnServerShutdown(); //raise event
LogMsg("Info: Server shutdown.");
}
}
}
Next, I've created a "Helper" class that contains the code to start the named pipe server, run the command using Process, and return the data. There are three ways to get the data. It's returned by the method, one can subscribe to the "DataReceived" event, or once the method completes, the data will be in property "Data".
Helper.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.IO.Pipes;
using System.IO;
using System.Threading;
namespace ProcessTest
{
public class Helper : IDisposable
{
public delegate void EventHandlerDataReceived(object sender, string data);
//event that subscribers can subscribe to
public event EventHandlerDataReceived DataReceived;
private StringBuilder _sbData = new StringBuilder();
private HelperNamedPipeServer _helperNamedPipeServer = null;
private bool _namedPipeServerOperationComplete = false;
public string Data { get; private set; } = string.Empty;
public Helper()
{
}
private void OnDataReceived(string data)
{
if (!String.IsNullOrEmpty(data) && DataReceived != null)
{
DataReceived(this, data);
//Debug.Write("Data: " + data);
}
}
public void Dispose()
{
ShutdownNamedPipeServer();
}
public async Task<string> RunSambaNamedPipesAsync(string fqExePath, string arguments, string pipeName = "sam-ba-pipe", string serverName = ".", bool runAsAdministrator = false)
{
string result = string.Empty;
string tempArguments = arguments;
//re-initialize
_namedPipeServerOperationComplete = false;
_sbData = new StringBuilder();
Data = string.Empty;
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
return "fqExePath not specified";
}
//create new instance
_helperNamedPipeServer = new HelperNamedPipeServer(pipeName);
_helperNamedPipeServer.ShutdownWhenOperationComplete = true;
//subscribe to events
_helperNamedPipeServer.DataReceived += HelperNamedPipeServer_DataReceived;
_helperNamedPipeServer.OperationCompleted += HelperNamedPipeServer_OperationCompleted;
//start named pipe server on it's own thread
Thread t = new Thread(_helperNamedPipeServer.StartServer);
t.Start();
//get pipe name to use with Process
//this is where output from the process
//will be redirected to
string fqNamedPipe = string.Empty;
if (String.IsNullOrEmpty(serverName))
{
fqNamedPipe = String.Format(#"\\{0}\pipe\{1}", serverName, pipeName);
}
else
{
fqNamedPipe = String.Format(#"\\{0}\pipe\{1}", ".", pipeName);
}
//redirect both StandardOutput and StandardError to named pipe
if (!arguments.Contains("2>&1"))
{
tempArguments += String.Format(" {0} {1} {2}", #"1>", fqNamedPipe, #"2>&1");
}
//run Process
RunProcess(fqExePath, tempArguments, runAsAdministrator);
while (!_namedPipeServerOperationComplete)
{
await Task.Delay(125);
}
//set value
Data = _sbData.ToString();
return Data;
}
public void RunProcess(string fqExePath, string arguments, bool runAsAdministrator = false)
{
if (String.IsNullOrEmpty(fqExePath))
{
Debug.WriteLine("fqExePath not specified");
throw new Exception( "Error: fqExePath not specified");
}
//create new instance
ProcessStartInfo startInfo = new ProcessStartInfo(fqExePath, arguments);
if (runAsAdministrator)
{
startInfo.Verb = "runas"; //elevates permissions
}//if
//set environment variables
//pStartInfo.EnvironmentVariables["SomeVar"] = "someValue";
startInfo.RedirectStandardError = false;
startInfo.RedirectStandardOutput = false;
startInfo.RedirectStandardInput = false;
startInfo.UseShellExecute = true; //use ShellExecute instead of CreateProcess
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.ErrorDialog = false;
startInfo.WorkingDirectory = System.IO.Path.GetDirectoryName(fqExePath);
using (Process p = Process.Start(startInfo))
{
//start
p.Start();
//waits until the process is finished before continuing
p.WaitForExit();
}
}
private void HelperNamedPipeServer_OperationCompleted(object sender, bool e)
{
//Debug.WriteLine("Info: Named pipe server - Operation completed.");
//set value
Data = _sbData.ToString();
//set value
_namedPipeServerOperationComplete = true;
}
private void HelperNamedPipeServer_DataReceived(object sender, string data)
{
Debug.WriteLine("Info: Data received from named pipe server.");
if (!String.IsNullOrEmpty(data))
{
//append
_sbData.Append(data.TrimEnd('\0'));
//send data to subscribers
OnDataReceived(data);
}
}
private void ShutdownNamedPipeServer()
{
Debug.WriteLine("Info: ShutdownNamedPipeServer");
try
{
if (_helperNamedPipeServer != null)
{
//unsubscribe from events
_helperNamedPipeServer.DataReceived -= HelperNamedPipeServer_DataReceived;
_helperNamedPipeServer.OperationCompleted -= HelperNamedPipeServer_OperationCompleted;
_helperNamedPipeServer.Dispose();
_helperNamedPipeServer = null;
}
}
catch (Exception ex)
{
}
}
}
}
Usage:
private async void btnRunUsingNamedPipes_Click(object sender, EventArgs e)
{
//Button name: btnRunUsingNamedPipes
using (Helper helper = new Helper())
{
//subscribe to event
helper.DataReceived += Helper_DataReceived;
var result = await helper.RunSambaNamedPipesAsync(#"C:\Windows\system32\cmd.exe", #"/c C:\Temp\sam-ba_3.5\sam-ba.exe -p serial:COM5 -d sama5d3 -m version");
Debug.WriteLine("Result: " + result);
//unsubscribe from event
helper.DataReceived -= Helper_DataReceived;
}
}
private void Helper_DataReceived(object sender, string data)
{
//System.Diagnostics.Debug.WriteLine(data);
//RichTextBox name: richTextBoxOutput
if (richTextBoxOutput.InvokeRequired)
{
richTextBoxOutput.Invoke((MethodInvoker)delegate
{
richTextBoxOutput.Text = data;
richTextBoxOutput.Refresh();
});
}
}
Resources:
When do we need to set ProcessStartInfo.UseShellExecute to True?
Redirecting error messages from Command Prompt: STDERR/STDOUT
PipeTransmissionMode.Message: How do .NET named pipes distinguish between messages?
WellKnownSidType Enum

How to use HttpListener in Identifying the hostheader usage

I a trying to find a hostheader is already under usage. So I start a HTTP listener with an url. I expected an exception, as the url having host header is already registered. The code is below. The URL abc:81 is already up and running, however the listener starts and stops without exception. can anyone help
bool notUnderUse;
HttpListener listener = new HttpListener();
listener.Prefixes.Add("http://abc:81/") ;
try
{
listener.Start();
listener.Stop();
notUnderUse = true;
}
catch (Exception ex)
{
notUnderUse = false;
}
I coded the below function with various references and it worked fine.
public static bool IsHostHeaderUnderUse(string ipAddress,string port, string hostname)
{
bool alreadyUnderUse = false;
var header = string.Format("{0}:{1}:{2}", ipAddress, port, hostname);
int? iisVersion = IISUtility.GetIISVersion();
if (iisVersion.HasValue && iisVersion < 7)
{
DirectoryEntry iis = new DirectoryEntry("IIS://localhost/W3SVC");
foreach (DirectoryEntry directoryEntry in iis.Children)
{
var bindings = directoryEntry.Properties["ServerBindings"];
if (bindings.Contains(header))
{
alreadyUnderUse = true;
break;
}
}
}
else
{
var serverManager = new ServerManager();
foreach (Site site in serverManager.Sites)
{
foreach (var binding in site.Bindings)
{
if (binding.BindingInformation.Contains(header))
{
alreadyUnderUse = true;
break;
}
}
if (alreadyUnderUse) { break; }
}
}
return alreadyUnderUse;
}

Trying To Install Windows Services - What's Wrong With This Code?

I create a Windows Service project in VS2010 that contains multiple services. I tried to cull together a way to install it without a complex installer. But, it seems to rollback and not work.
Here's Program.cs:
static class Program
{
static void Main(string[] args)
{
bool install = false, uninstall = false, console = false;
WindowsServiceInstaller inst = new WindowsServiceInstaller();
if (args.Length > 0)
{
foreach (string arg in args)
{
switch (arg)
{
case "-i":
case "-install":
install = true;
break;
case "-u":
case "-uninstall":
uninstall = true;
break;
case "-c":
case "-console":
console = true;
break;
default:
Console.Error.WriteLine("Argument not expected: " + arg);
break;
}
}
}
if (uninstall)
{
inst.InstallServices(false, args);
}
if (install)
{
inst.InstallServices(true, args);
}
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
// scans Email table for outbound email jobs; uses multiple threads to lock and work on data in Email table
new EmailLogScanner()
// generates email digest of document status on secheduled basis; single thread
, new EmailStatusDigester()
// keeps Fax table and third-party fax service accounts synchronized; uses a fixed nb of threads, one thread syncs one account at a time
, new FaxSynchronizer()
};
if (console)
{
foreach (IDebuggableService srv in ServicesToRun)
{
string[] strs = new string[] { String.Empty };
srv.DebugStart(strs);
}
Console.WriteLine("Press any key to terminate...");
Console.ReadKey();
foreach (IDebuggableService srv in ServicesToRun)
{
srv.DebugStop();
}
Console.WriteLine("Service has exited.");
}
else
{
ServiceBase.Run(ServicesToRun);
}
}
}
Here's WindowsServiceInstaller.cs:
[RunInstaller(true)]
public class WindowsServiceInstaller : Installer
{
public WindowsServiceInstaller()
{
ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
serviceProcessInstaller.Account = ServiceAccount.NetworkService;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Password = null;
Installers.Add(serviceProcessInstaller);
ServiceInstaller emailLogScannerInstaller = new ServiceInstaller();
emailLogScannerInstaller.DisplayName = "Email Scanner";
emailLogScannerInstaller.StartType = ServiceStartMode.Automatic;
emailLogScannerInstaller.ServiceName = "EmailLogScanner"; // must match the ServiceBase ServiceName property
emailLogScannerInstaller.Description = "Scan for and sends out pending emails in stack.";
Installers.Add(emailLogScannerInstaller);
ServiceInstaller emailStatusDigesterInstaller = new ServiceInstaller();
emailStatusDigesterInstaller.DisplayName = "Status Digester";
emailStatusDigesterInstaller.StartType = ServiceStartMode.Automatic;
emailStatusDigesterInstaller.ServiceName = "EmailDigester";
emailStatusDigesterInstaller.Description = "Prepares document status email digests.";
Installers.Add(emailStatusDigesterInstaller);
ServiceInstaller faxSynchronizerInstaller = new ServiceInstaller();
faxSynchronizerInstaller.DisplayName = "Fax Synchronizer";
faxSynchronizerInstaller.StartType = ServiceStartMode.Automatic;
faxSynchronizerInstaller.ServiceName = "FaxSynchronizer";
faxSynchronizerInstaller.Description = "Synchronizes database with external fax service(s).";
Installers.Add(faxSynchronizerInstaller);
}
public void InstallServices(bool doInstall, string[] args)
{
try
{
using (AssemblyInstaller aInstaller = new AssemblyInstaller(typeof(Program).Assembly, args))
{
IDictionary state = new Hashtable();
aInstaller.UseNewContext = true;
try
{
if (doInstall)
{
aInstaller.Install(state);
aInstaller.Commit(state);
}
else
{
aInstaller.Uninstall(state);
}
}
catch
{
try
{
aInstaller.Rollback(state);
}
catch { }
throw;
}
}
}
catch (Exception ex)
{
Console.Error.WriteLine(ex.Message);
}
}
}
The logged output (when I run daemon.exe -i in a command window, as admin) shows the text below. Also, I get the "cannot start service from the command line" dialog:
Installing assembly 'C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.exe'.
Affected parameters are:
i =
assemblypath = C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.exe
logfile = C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.InstallLog
Installing service EmailLogScanner...
Service EmailLogScanner has been successfully installed.
Creating EventLog source EmailLogScanner in log Application...
See the contents of the log file for the C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.exe assembly's progress.
The file is located at C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.InstallLog.
Rolling back assembly 'C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.exe'.
Affected parameters are:
logtoconsole =
i =
assemblypath = C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.exe
logfile = C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.InstallLog
Restoring event log to previous state for source EmailLogScanner.
Service EmailLogScanner is being removed from the system...
Service EmailLogScanner was successfully removed from the system.
UPDATE: When I comment out the try...catch block around the 'aInstaller.Install(state)' line, I get a slightly different output:
Installing assembly 'C:\Users\xxx\Documents\~Business\Projects\Daemon\bin\Release\Daemon.exe'.
Affected parameters are:
i =
assemblypath = C:\Users\xxx\Documents\~Business\Projects\Da
emon\bin\Release\Daemon.exe
logfile = C:\Users\xxx\Documents\~Business\Projects\Daemon\
bin\Release\Daemon.InstallLog
Installing service EmailLogScanner...
Creating EventLog source EmailLogScanner in log Application...
Source EmailLogScanner already exists on the local computer.
Is it because I already have Event Log sources setup? If so, how do I skip that step in the AssemblyInstaller? If not, wha? :)
You should put this
if (EventLog.SourceExists("YourEventSourceName"))
EventLog.DeleteEventSource("YourEventSourceName");
when service installation begins.

Starting a service: "Error 1053: The service did not respond to the start or control request in a timely fashion"

I installed my c# application as windows service, by using the command installutil.
It get successfully installed. While starting the service I am getting the following error.
"Error 1053: The service did not respond to the start or control request in a timely fashion"
Why it happens?
Below is the source code
static void Main(string[] args)
{
// Get the version of the current application.
Assembly assem = Assembly.GetExecutingAssembly();
AssemblyName assemName = assem.GetName();
Version ver = assemName.Version;
// Console.WriteLine("{0}, Version {1}", assemName.Name, ver.ToString());
Console.WriteLine("{0} version {1}", assemName.Name, ver.ToString());
TouchService touchService = new TouchService();
if (Environment.UserInteractive)
{
bool show_help = false;
bool install_service = false;
bool uninstall_service = false;
string servicename = "";
OptionSet p = new OptionSet()
.Add("h|?|help", delegate(string v) { show_help = v != null; })
.Add("s|servicename=", "name of installed service", delegate(string v) { servicename = v; })
.Add("i|install", "install program as a Windows Service. A valid servicename is needed.", delegate(string v) { install_service = v != null; })
.Add("u|uninstall", "uninstall program from Windows Services. A valid servicename is needed.", delegate(string v) { uninstall_service = v != null; });
List<string> extra;
try
{
extra = p.Parse(args);
}
catch (OptionException e)
{
Console.Write("TouchServer: ");
Console.WriteLine(e.Message);
Console.WriteLine("Try `TouchServer --help' for more information.");
return;
}
if (show_help)
{
ShowHelp(p);
return;
}
else if (install_service)
{
IntegratedServiceInstaller Inst = new IntegratedServiceInstaller();
Inst.Install(servicename, null, "Provides XML data over HTTP for Touch clients",
System.ServiceProcess.ServiceAccount.NetworkService,
System.ServiceProcess.ServiceStartMode.Manual);
return;
}
else if (uninstall_service)
{
IntegratedServiceInstaller Inst = new IntegratedServiceInstaller();
Inst.Uninstall(servicename);
return;
}
// start and run the server,
// and receive commands from the console
else
{
touchService.OnStart(args);
while (true)
{
Console.Write("TouchServer>");
string commandLine = Console.ReadLine().ToLower();
if (commandLine == "exit" || commandLine == "x")
{
break;
}
if (commandLine == "quit" || commandLine == "q")
{
break;
}
else if (commandLine == "version" || commandLine == "v")
{
Console.WriteLine("{0} version {1}", assem.GetName().Name, assem.GetName().Version.ToString());
}
else if (commandLine == "list" || commandLine == "l")
{
TouchServer.showURLs = (TouchServer.showURLs == false) ? true : false;
Console.WriteLine("List URLs: {0}", (TouchServer.showURLs ? "active" : "inactive"));
}
else if (commandLine == "status" || commandLine == "s")
{
Console.WriteLine("{0,-20} {1,8}", "Name", "Sessions");
Console.WriteLine("----------------------------");
foreach (Site site in TouchServer.siteCollection.All)
{
Console.WriteLine("{0,-20} {1,8}", site.Name, site.AllSessions.Length);
}
Console.WriteLine();
}
}
touchService.OnStop();
}
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new TouchService()
};
ServiceBase.Run(ServicesToRun);
}
}
static void ShowHelp(OptionSet p)
{
Console.WriteLine("Usage: TouchServer [OPTIONS]+ ");
Console.WriteLine();
Console.WriteLine("Options:");
p.WriteOptionDescriptions(Console.Out);
Console.WriteLine();
Console.WriteLine("Providing no options results in the server running in console mode (for debugging purposes).");
}
public TouchService()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
taskList.Clear();
taskList.Add(new TouchServerTask("TouchServer"));
taskList.Add(new HouseKeeperTask());
//TouchServer.Execute();
setupSynchronizerTasks();
taskList.StartAllTasks();
}
Your question has been answered here.
What's assumed is that there's a problem within your code that resides in
void OnStart(string[] args)
in your code. In that case, attach a service debugger to troubleshoot your problem.
Good luck.
Your service does to much in OnStart.
Start the service choose Tools->attach to process in Visual Studio, choose your service. Break the process and try to figure out what the process is doing so long time in OnStart.

Capturing nslookup shell output with C#

I have a command-line process I would like to automate and capture in C#.
At the command line, I type:
nslookup
This launches a shell which gives me a > prompt. At the prompt, I then type:
ls -a mydomain.local
This returns a list of local CNAMEs from my primary DNS server and the physical machines they are attached to.
What I would like to do is automate this process from C#. If this were a simple command, I would just use Process.StartInfo.RedirectStandardOutput = true, but the requirement of a second step is tripping me up.
ProcessStartInfo si = new ProcessStartInfo("nslookup");
si.RedirectStandardInput = true;
si.RedirectStandardOutput = true;
Process nslookup = new Process(si);
nslookup.Start();
nslookup.StandardInput.WriteLine("ls -a mydomain.local");
nslookup.StandardInput.Flush();
// use nslookup.StandardOutput stream to read the result.
Not what you asked, but I once wrote an app that did what you're doing. I eventually moved to using a .NET library to do the DNS lookups, which turned out to be a lot faster.
I'm pretty sure I used this library from the CodeProject site.
I know this is an old one, but still like to contribute.
I used the shell output from "NsLookup Hostname Server" to get the IPv4 addresses from a computername in our domain and strip out any other information like DNS server / ipv6 addresses..
This is kinda quickly done but it works, there is also a failover added if the shell fails that you would use the built in nslookup method from C#.
it is rather long but it gave me the possibility to read the ipv4 from the shell without using an external library or without using the built in nslookup function as it does allow to choose the dns server.
If you are wondering about the if loops in the middle, there might be more elegant solutions but for my personal use, this worked out quite well, most hosts in our domain returned 2 ipv6 and 2 ipv4, therefore, it test up to 4 times.
Hope this can help..
private void button1_Click(object sender, EventArgs e)
{
IPAddress[] ips = NsLookup(computername, dnsserver);
txtResult.Text = string.Empty;
if (ips != null)
{
txtResult.Text = ips[0].ToString();
txtResult.Text += Environment.NewLine;
if (ips[1] != null)
{
txtResult.Text += ips[1].ToString();
}
else
{
}
}
else
{
txtResult.Text = "No IP found";
}
}
public IPAddress[] NsLookup(string computername, string domaincontroller)
{
IPAddress[] ips = new IPAddress[2];
try
{
// Creating streamreaders to read the output and the errors
StreamReader outputReader = null;
StreamReader errorReader = null;
string nslookup = #"C:\Windows\System32\Nslookup.exe";
try
{
// Setting process startupinfo
ProcessStartInfo processStartInfo = new ProcessStartInfo(nslookup, computername + " " + domaincontroller);
processStartInfo.ErrorDialog = false;
processStartInfo.UseShellExecute = false;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Minimized;
// Starting Process
Process process = new Process();
process.StartInfo = processStartInfo;
bool processStarted = process.Start();
if (processStarted)
{
// Catching the output streams
outputReader = process.StandardOutput;
errorReader = process.StandardError;
string errorresult = errorReader.ReadLine();
errorReader.Close();
if (errorresult != null)
{
// Failure got thrown in NsLookup Streamreading, try build-in Method
try
{
ips = Dns.GetHostAddresses(computername);
return ips;
}
catch
{
return null;
}
}
else
{
// Clearing out all the values before the addresses.
outputReader.ReadLine();
outputReader.ReadLine();
outputReader.ReadLine();
outputReader.ReadLine();
// Reading and Verifying the first outputline (the address is found after "Addresses: ") - 2 part of the array is taken (after second space)
string outputline = outputReader.ReadLine();
string[] outputlineaftersplit = outputline.Split(' ');
string ipfortesting = outputlineaftersplit[2].Trim();
if (verifyIP(ipfortesting) != null) // First entry is ipv4
{
ips[0] = verifyIP(ipfortesting);
outputline = outputReader.ReadLine();
ipfortesting = outputline.Trim();
if (verifyIP(ipfortesting) != null) // First and second entry are ipv4
{
ips[1] = verifyIP(ipfortesting);
return ips;
}
else
{
return ips;
}
}
else
{
outputline = outputReader.ReadLine();
ipfortesting = outputline.Trim();
if (verifyIP(ipfortesting) != null)
{
ips[0] = verifyIP(ipfortesting);
outputline = outputReader.ReadLine();
ipfortesting = outputline.Trim();
if (verifyIP(ipfortesting) != null)
{
ips[0] = verifyIP(ipfortesting);
outputline = outputReader.ReadLine();
ipfortesting = outputline.Trim();
if (verifyIP(ipfortesting) != null)
{
ips[1] = verifyIP(ipfortesting);
return ips;
}
else
{
return ips;
}
}
else
{
return ips;
}
}
else
{
outputline = outputReader.ReadLine();
ipfortesting = outputline.Trim();
if (verifyIP(ipfortesting) != null)
{
ips[0] = verifyIP(ipfortesting);
outputline = outputReader.ReadToEnd();
ipfortesting = outputline.Trim();
if (verifyIP(ipfortesting) != null)
{
ips[1] = verifyIP(ipfortesting);
return ips;
}
else
{
return ips;
}
}
else
{
ips = null;
return ips;
}
}
}
}
}
else
{
// Failure got thrown in NsLookup Streamreading, try build-in Method
try
{
ips = Dns.GetHostAddresses(computername);
return ips;
}
catch
{
return null;
}
}
}
catch
{
System.Windows.Forms.MessageBox.Show("ERROR 1");
// Failure got thrown in NsLookup Streamreading, try build-in Method
try
{
ips = Dns.GetHostAddresses(computername);
return ips;
}
catch
{
return null;
}
}
finally
{
if (outputReader != null)
{
outputReader.Close();
}
}
}
catch
{
System.Windows.Forms.MessageBox.Show("ERROR 2");
// Failure got thrown in NsLookup Streamreading, try build-in Method
try
{
ips = Dns.GetHostAddresses(computername);
return ips;
}
catch
{
return null;
}
}
}
public IPAddress verifyIP(string ipfromreader)
{
IPAddress ipresult = null;
bool isIP = IPAddress.TryParse(ipfromreader, out ipresult);
if (isIP && (ipresult.AddressFamily != AddressFamily.InterNetworkV6))
{
return ipresult;
}
else
{
return null;
}
}
}
}

Categories