I found plenty of partial answers, but nothing really sufficient.
The case:
App is a working command line app, with no user interaction, except for the ability to receive an key press on enter to stop, this is already written in a way that disables even that when not run as Environment.UserInteractive == true.
I'm using Visual Studio 2010.
The problem is I need to convert this app to a windows service. Is it "just" making a new class file as a service, and have it call my start and stop methods on the existing application?
How about the installer (VS' default msi installer), can the existing installer project be "upgraded" to handle the Service installation as well?
I messed with this before, and ended up with an installer that kept refusing to install, as it kept detecting the service as already being installed, stopping the install process by then promptly rolling back everything. The service it detected were the one it had just installed.
To run a console app as either a Windows Service or a Console application, write a single console application and use command line arguments to determine if you should run directly or start up the service. Include an installer/uninstaller to install as a windows service with the right command line arguments.
Here's a base class we use that provides this functionality.
using System;
using System.Collections;
using System.Configuration.Install;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.ServiceProcess;
using System.Windows.Forms;
using Microsoft.Win32;
namespace Console40
{
public abstract class AbstractService : ServiceBase
{
public static AbstractService Current { get; private set; }
protected virtual string HelpTextPattern
{
get
{
#region Help Text
return
#"
USAGE
{0} [command]
WHERE [command] is one of
/console - run as a console application, for debugging
/service - run as a windows service
/install - install as a windows service
/uninstall - uninstall windows service
";
#endregion
}
}
public abstract string DisplayName { get; }
public ServiceExecutionMode ServiceExecutionMode { get; private set; }
protected abstract Guid UninstallGuid { get; }
protected virtual string UninstallRegKeyPath
{
get
{
return #"SOFTWARE\Microsoft\Windows\CurrentVersion\Uninstall";
}
}
protected AbstractService(string serviceName)
{
ServiceName = serviceName;
if (Current != null)
{
throw new InvalidOperationException(String.Format(
"Service {0} is instantiating but service {1} is already instantiated as current. References to AbstractService.Current will only point to the first service.",
GetType().FullName,
Current.GetType().FullName));
}
Current = this;
}
public void Run(string[] args)
{
Environment.CurrentDirectory = AppDomain.CurrentDomain.BaseDirectory;
if (args.Length == 0 && Debugger.IsAttached)
{
args = new[] { "/console" };
}
if (args.Length == 0)
{
Console.WriteLine(HelpTextPattern, Path.GetFileName(GetType().Assembly.CodeBase));
}
else
{
switch (args[0].ToLower())
{
case "/service":
ServiceExecutionMode = ServiceExecutionMode.Service;
Run(new[] { this });
break;
case "/console":
ServiceExecutionMode = ServiceExecutionMode.Console;
Console.WriteLine("Starting Service...");
OnStart(new string[0]);
OnStartCommandLine();
OnStop();
break;
case "/install":
ServiceExecutionMode = ServiceExecutionMode.Install;
InstallService();
break;
case "/uninstall":
ServiceExecutionMode = ServiceExecutionMode.Uninstall;
UninstallService();
break;
case "/uninstallprompt":
ServiceExecutionMode = ServiceExecutionMode.Uninstall;
if (ConfirmUninstall())
{
UninstallService();
InformUninstalled();
}
break;
default:
if (!OnCustomCommandLine(args))
{
Console.WriteLine(HelpTextPattern, Path.GetFileName(GetType().Assembly.CodeBase));
}
break;
}
}
}
protected override void OnStart(string[] args)
{
OnStartImpl(args);
AppDomain.CurrentDomain.UnhandledException += OnCurrentDomainUnhandledException;
}
protected virtual void OnStartCommandLine()
{
Console.WriteLine("Service is running... Hit ENTER to break.");
Console.ReadLine();
}
protected abstract void OnStartImpl(string[] args);
void OnCurrentDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
{
// do something useful here, log it..
}
protected override void OnShutdown()
{
Stop();
}
protected override void OnStop()
{
OnStopImpl();
}
protected abstract void OnStopImpl();
protected virtual bool OnCustomCommandLine(string[] args)
{
// for extension
return false;
}
private void InstallService()
{
GetInstaller(".InstallLog").Install(new Hashtable());
InstallServiceCommandLine();
CreateUninstaller();
}
private void InstallServiceCommandLine()
{
string keyParent = #"SYSTEM\CurrentControlSet\Services\" + ServiceName;
const string VALUE_NAME = "ImagePath";
try
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(keyParent, true))
{
if (key == null)
{
throw new InvalidOperationException("Service not found in registry.");
}
var origPath = key.GetValue(VALUE_NAME) as string;
if (origPath == null)
{
throw new Exception("HKLM\\" + keyParent + "\\" + VALUE_NAME + " does not exist but was expected.");
}
key.SetValue(VALUE_NAME, origPath.Replace("\"\"", "\"") + " /service");
}
}
catch (Exception ex)
{
throw new Exception(
"Error updating service command line after installation. Unable to write to HKLM\\" + keyParent, ex);
}
}
private void CreateUninstaller()
{
using (RegistryKey parent = Registry.LocalMachine.OpenSubKey(UninstallRegKeyPath, true))
{
if (parent == null)
{
throw new Exception(String.Format("Uninstall registry key '{0}' not found.", UninstallRegKeyPath));
}
try
{
RegistryKey key = null;
try
{
string guidText = UninstallGuid.ToString("B");
key = parent.OpenSubKey(guidText, true) ??
parent.CreateSubKey(guidText);
if (key == null)
{
throw new Exception(String.Format("Unable to create uninstaller '{0}\\{1}'", UninstallRegKeyPath, guidText));
}
Assembly asm = GetType().Assembly;
Version v = asm.GetName().Version;
string exe = "\"" + asm.CodeBase.Substring(8).Replace("/", "\\\\") + "\"";
key.SetValue("DisplayName", DisplayName);
key.SetValue("ApplicationVersion", v.ToString());
key.SetValue("Publisher", "B-Line Medical");
key.SetValue("DisplayIcon", exe);
key.SetValue("DisplayVersion", v.ToString(2));
key.SetValue("URLInfoAbout", "http://www.blinemedical.com");
key.SetValue("Contact", "support#blinemedical.com");
key.SetValue("InstallDate", DateTime.Now.ToString("yyyyMMdd"));
key.SetValue("UninstallString", exe + " /uninstallprompt");
}
finally
{
if (key != null)
{
key.Close();
}
}
}
catch (Exception ex)
{
throw new Exception(
"An error occurred writing uninstall information to the registry. The service is fully installed but can only be uninstalled manually through the command line.",
ex);
}
}
}
private bool ConfirmUninstall()
{
string title = "Uninstall " + DisplayName;
string text = "Are you sure you want to remove " + DisplayName + " from your computer?";
return DialogResult.Yes ==
MessageBox.Show(text, title, MessageBoxButtons.YesNo, MessageBoxIcon.Question,
MessageBoxDefaultButton.Button2);
}
private void InformUninstalled()
{
string title = "Uninstall " + DisplayName;
string text = DisplayName + " has been uninstalled.";
MessageBox.Show(text, title, MessageBoxButtons.OK, MessageBoxIcon.Information);
}
private void UninstallService()
{
GetInstaller(".UninstallLog").Uninstall(null);
RemoveUninstaller();
}
private TransactedInstaller GetInstaller(string logExtension)
{
var ti = new TransactedInstaller();
ti.Installers.Add(new ServiceProcessInstaller
{
Account = ServiceAccount.LocalSystem
});
ti.Installers.Add(new ServiceInstaller
{
DisplayName = DisplayName,
ServiceName = ServiceName,
StartType = ServiceStartMode.Automatic
});
string basePath = Assembly.GetEntryAssembly().Location;
String path = String.Format("/assemblypath=\"{0}\"", basePath);
ti.Context = new InstallContext(Path.ChangeExtension(basePath, logExtension), new[] { path });
return ti;
}
private void RemoveUninstaller()
{
using (RegistryKey key = Registry.LocalMachine.OpenSubKey(UninstallRegKeyPath, true))
{
if (key == null)
{
return;
}
try
{
string guidText = UninstallGuid.ToString("B");
RegistryKey child = key.OpenSubKey(guidText);
if (child != null)
{
child.Close();
key.DeleteSubKey(guidText);
}
}
catch (Exception ex)
{
throw new Exception(
"An error occurred removing uninstall information from the registry. The service was uninstalled will still show up in the add/remove program list. To remove it manually delete the entry HKLM\\" +
UninstallRegKeyPath + "\\" + UninstallGuid, ex);
}
}
}
}
public enum ServiceExecutionMode
{
Unknown,
Service,
Console,
Install,
Uninstall,
Custom
}
}
Best thing for you to do is to start a new Project as a windows service.
In this new project you will find Service1.cs and this is the file that will be run from start. the following code will be int the file:
namespace WindowsService1
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
}
It isnt that hard to figure out what to do from here. Simply add your classes to the project and make sure that you copy your main code in the the OnStart() function. Of course you might have to slightly edit the code to make sure it has no readlines in it.
Now you must create and installer. How you can do this can be found here:
http://msdn.microsoft.com/en-us/library/zt39148a%28v=vs.100%29.aspx
I hope this helped :D
Kind Regards
RoXaS
Keep C# application running
public partial class DemoService : ServiceBase
{
static void Main(string[] args)
{
DemoService service = new DemoService();
if (Environment.UserInteractive)
{
service.OnStart(args);
Console.WriteLine("Press any key to stop program");
Console.Read();
service.OnStop();
}
else
{
ServiceBase.Run(service);
}
}
Check the link above. I provide some code as well as a link describing using a console for double-duty as Console and Service. I will use a console project and check for UserInteractive before running as a service. This way you can debug as if it's a console, but install it as a service on a production server.
With regards to installing, I don't have experience with installing from .msi, but we use a batch script to install the service (using sc.exe) and then its just a matter of replacing files if you update the code.
Related
I am trying to get my EasyLogger C# Project to work, but I can't debug this code because it is a Class Library. I use this logger in a Windows Console Application which is installed with squirrel as a Windows Service, so I can't see whats inside the variables....
What do I wrong?
This is in a Windows Console Application:
public void Start()
{
//Define EasyLogger
EasyLogger.Logger.Loggerpath = #"C:\Development\ServerSF";
EasyLogger.Logger.Logname = "Testlog";
Thread.Sleep(1000);
//Start EasyLogger
bool test = EasyLogger.Logger.Log;
EasyLogger.Logger.StartLog();
if (test == true)
{
//Start service
_timer.Start();
ExThred(configManager.ServerName);
}
else
{
//ERROR log isnt started
}
}
This is my EasyLogger Project:
using System;
using System.IO;
using System.Text;
namespace EasyLogger
{
public static class Logger
{
#region GET SET
private static string loggerpath;
private static string logname;
private static string message;
private static bool log;
public static string Loggerpath
{
get => loggerpath;
set => loggerpath = value;
}
public static string Logname
{
get => logname;
set => logname = value;
}
public static string Message
{
get => message;
set => message = value;
}
public static bool Log
{
get => log;
set => log = value;
}
#endregion
#region Logger
private static readonly string loggingpath = loggerpath + #"\logs\"+ logname + "-" + DateTime.Now.ToString("yyyy-dd-M--HH-mm-ss") + ".txt";
public static void StartLog()
{
if(loggerpath == null)
{
Message = "Program path is empty!";
log = false;
}
else
{
try
{
using (FileStream fs = File.Create(loggingpath))
{
byte[] info = new UTF8Encoding(true).GetBytes(DateTime.Now.ToString() + " - Logger v0.1 Started...");
fs.Write(info, 0, info.Length);
}
log = true;
}
catch
{
Message = "Can't create logfile!";
log = false;
}
}
}
public static void WriteLog(string msg)
{
if (Logname != null)
{
if (log == true)
{
string[] lines = new string[] { DateTime.Now.ToString() + " - " + msg };
File.AppendAllLines(loggingpath, lines);
}
else
{
try { Message = "Logger isn't started yet!"; }
catch { Message = "Logger Error!"; }
}
}
else { Message = "Please define first a Logname!"; }
}
public static void StopLog()
{
log = false;
}
#endregion
}
}
Your problem is not the logger. Your problem is how to debug a Windows Service.
With Visual Studio, you can do it this way:
Compile your Windows service with Debug informaton.
Install the Service and run it.
Start Visual Studio.
Select "Debug" --> "Attach Process"
Click "Show processes from all users"
Search for your service and click "Attach".
Select "Common Language Runtime"
i am trying to run this test on local host, but it keeps throwing the error " Unable to locate local.config.user file. Make sure you have run 'build.cmd local'"
So how do I locate local.config.user file? Or how to run build.cmd local ? Please try see Code below
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.IO;
using System.Reflection;
using Microsoft.WindowsAzure.ServiceRuntime;
namespace Microsoft.Azure.Devices.Applications.RemoteMonitoring.Common.Configurations
{
public class ConfigurationProvider : IConfigurationProvider, IDisposable
{
readonly Dictionary<string, string> configuration = new Dictionary<string, string>();
EnvironmentDescription environment = null;
const string ConfigToken = "config:";
bool _disposed = false;
public string GetConfigurationSettingValue(string configurationSettingName)
{
return this.GetConfigurationSettingValueOrDefault(configurationSettingName, string.Empty);
}
public string GetConfigurationSettingValueOrDefault(string configurationSettingName, string defaultValue)
{
try
{
if (!this.configuration.ContainsKey(configurationSettingName))
{
string configValue = string.Empty;
bool isEmulated = true;
bool isAvailable = false;
try
{
isAvailable = RoleEnvironment.IsAvailable;
}
catch (TypeInitializationException) { }
if (isAvailable)
{
configValue = RoleEnvironment.GetConfigurationSettingValue(configurationSettingName);
isEmulated = RoleEnvironment.IsEmulated;
}
else
{
configValue = ConfigurationManager.AppSettings[configurationSettingName];
isEmulated = Environment.CommandLine.Contains("iisexpress.exe") ||
Environment.CommandLine.Contains("WebJob.vshost.exe");
}
if (isEmulated && (configValue != null && configValue.StartsWith(ConfigToken, StringComparison.OrdinalIgnoreCase)))
{
if (environment == null)
{
LoadEnvironmentConfig();
}
configValue =
environment.GetSetting(configValue.Substring(configValue.IndexOf(ConfigToken, StringComparison.Ordinal) + ConfigToken.Length));
}
try
{
this.configuration.Add(configurationSettingName, configValue);
}
catch (ArgumentException)
{
// at this point, this key has already been added on a different
// thread, so we're fine to continue
}
}
}
catch (RoleEnvironmentException)
{
if (string.IsNullOrEmpty(defaultValue))
throw;
this.configuration.Add(configurationSettingName, defaultValue);
}
return this.configuration[configurationSettingName];
}
void LoadEnvironmentConfig()
{
var executingPath = Path.GetDirectoryName(new Uri(Assembly.GetExecutingAssembly().CodeBase).LocalPath);
// Check for build_output
int buildLocation = executingPath.IndexOf("Build_Output", StringComparison.OrdinalIgnoreCase);
if (buildLocation >= 0)
{
string fileName = executingPath.Substring(0, buildLocation) + "local.config.user";
if (File.Exists(fileName))
{
this.environment = new EnvironmentDescription(fileName);
return;
}
}
// Web roles run in there app dir so look relative
int location = executingPath.IndexOf("Web\\bin", StringComparison.OrdinalIgnoreCase);
if (location == -1)
{
location = executingPath.IndexOf("WebJob\\bin", StringComparison.OrdinalIgnoreCase);
}
if (location >=0)
{
string fileName = executingPath.Substring(0, location) + "..\\local.config.user";
if (File.Exists(fileName))
{
this.environment = new EnvironmentDescription(fileName);
return;
}
}
throw new ArgumentException("Unable to locate local.config.user file. Make sure you have run 'build.cmd local'.");
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_disposed)
{
return;
}
if (disposing)
{
if (environment != null)
{
environment.Dispose();
}
}
_disposed = true;
}
~ConfigurationProvider()
{
Dispose(false);
}
}
}
Hi sir, thank you for your help. I have tried "Build.cmd build" and "Build.cmd local" it gave me this error.
Normally in the root folder of your Visual Studio solution you should have a build.cmd file.
You need to run Developer Command Prompt for VS2015 (or with an another version of installed Visual Studio, use Windows Search in the Start menu) as an Administrator and change directory (cd [directory with solution]) to the root folder of mentioned solution and then type and run build.cmd local by clicking Enter.
I've been trying to create some custom check-in policies in TFS 2010 recently, I created a DLL and added a registry key as outlined in the following link:
https://msdn.microsoft.com/en-us/library/bb668980.aspx
(Note: I already know this link is retired, but I'm unable to do it in the updated fashion indicated here: http://blogs.msdn.com/b/jimlamb/archive/2010/03/31/how-to-implement-package-and-deploy-custom-check-in-policy-for-tfs-2010.aspx)
I didn't necessarily expect my own DLL to work correctly from the start in a functional sense, but Step 4 only shows the out of the box policies, mine is not available.
What could be causing mine to not appear? Below is the code I'm using (I've removed what it actually does as I don't think that's important)
[Serializable]
public class CheckInPolicy : PolicyBase
{
private string _status = string.Empty;
public override string Description
{
get { return "My description"; }
}
public override string Type
{
get { return "My type"; }
}
public override string TypeDescription
{
get { return "My type description"; }
}
public override bool Edit(IPolicyEditArgs policyEditArgs)
{
return true;
}
public override PolicyFailure[] Evaluate()
{
// Check if the check in meets the policy rules
// ...
// ...
// ...
}
public override void Activate(PolicyFailure failure)
{
base.Activate(failure);
}
public override void DisplayHelp(PolicyFailure failure)
{
MessageBox.Show(
"My policy help notification", "Policy Help");
}
}
This link mentioned that the policy needs to be placed in a different registry path if the OS is x64, but the server I'm using is a 32-bit Windows Server 2003 machine. The key is placed in:
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\9.0\TeamFoundation\SourceControl\Checkin Policies]
I guess you have a 64-bit machine, in that case the correct registry entry would be the 32-bit path:
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\11.0\TeamFoundation\SourceControl\Checkin Policies
In addition to this, you usually need to build the Policy for each version of Visual Studio, as it also has references to the appropriate TeamExplorer libraries.
EDIT:
You can prohibit developers from overriding policies, if you extend your server side with a plugin, that declines all checkins with overridden policies.
You need to place the DLL under this TFS path (of course depending on your version):
Program Files\Microsoft Team Foundation Server 11.0\Application Tier\Web Services\bin\Plugins\
Here the code of this plugin class (some of the referenced DLLs can only be found on TFS servers). We allow to override if all files are in published folder, but you can of course define your own exclusions:
using System;
using Microsoft.TeamFoundation.Common;
using Microsoft.TeamFoundation.Framework.Server;
using Microsoft.TeamFoundation.VersionControl.Server;
using System.IO;
using System.Text.RegularExpressions;
using Microsoft.TeamFoundation.Framework.Server.Alm;
namespace TFS.CheckinPolicyOverrideEventHandler
{
public sealed class CheckinPolicyOverrideEventHandler : ISubscriber
{
public Type[] SubscribedTypes()
{
return new Type[1] { typeof(CheckinNotification) };
}
public EventNotificationStatus ProcessEvent(TeamFoundationRequestContext requestContext, NotificationType notificationType, object notificationEventArgs,
out int statusCode, out string statusMessage, out ExceptionPropertyCollection properties)
{
statusCode = 0;
properties = null;
statusMessage = String.Empty;
try
{
if (notificationType == NotificationType.DecisionPoint && notificationEventArgs is CheckinNotification)
{
CheckinNotification ev = notificationEventArgs as CheckinNotification;
if (ev != null && ev.PolicyOverrideInfo != null)
{
if (ev.PolicyOverrideInfo.PolicyFailures != null)
{
// One or more of the checkin policies have been overridden
// If all the files being checked in are in the published folder, then allow overridding the policies since those are installation packages
foreach (string file in ev.GetSubmittedItems(null))
{
if (!Regex.IsMatch(file, #"/published", RegexOptions.IgnoreCase) &&
!Regex.IsMatch(Path.GetDirectoryName(file), #"/published", RegexOptions.IgnoreCase))
{
statusCode = -1;
break;
}
}
if (statusCode != 0)
{
// One or more of the checkin policies have been overridden and not all files are installation files (in the published folder)
statusMessage = Resource.CheckinCancelledStatusMessage;
foreach (PolicyFailureInfo policy in ev.PolicyOverrideInfo.PolicyFailures)
{
statusMessage = String.Concat(statusMessage, "\n > ", policy.PolicyName, ": ", policy.Message);
}
return EventNotificationStatus.ActionDenied;
}
}
}
return EventNotificationStatus.ActionPermitted;
}
}
catch (Exception exception)
{
// decide what you want to do, if exception occurs
}
return EventNotificationStatus.ActionPermitted;
}
public string Name
{
get { return "TFS.CheckinPolicyOverrideEventHandler"; }
}
public SubscriberPriority Priority
{
get { return SubscriberPriority.Normal; }
}
}
}
I get this error when I try to connect an # IP (local).
Its a socket (tcp & udp).
I use an emulator
If there is a code to activate WiFi on WP7 emulator??
or maybe I deactivate WiFi coonnection
this all my code to add a connection to can use it
private void AddComputer_Click(object sender, RoutedEventArgs e)
{
IPAddress iPAddress;
if (!IPAddress.TryParse(this.IPAddressTextBox.Text, out iPAddress))
{
MessageBox.Show("Error: Invalid IP Address is empty");
return;
}
this.ipAddressToAdd = iPAddress;
this.progressBar.IsIndeterminate = true;
this.StatusTextBlock.Visibility=(Visibility)0;
this.ContentPanel.Visibility=(Visibility)1;
ThreadPool.QueueUserWorkItem(new WaitCallback(this.AddComputerThread), new NetworkHandler.DiscoveredServer("", this.ipAddressToAdd, false, "", this.checkBoxPassword.IsChecked.Value, this.passwordBox1.Password, NetworkHandler.NetworkCategory.Unknown));
}
private void AddComputerThread(object obj)
{
NetworkHandler.DiscoveredServer info = (NetworkHandler.DiscoveredServer)obj;
NetworkHandlerCommon.ConnectResult connectResult = ConnectionHandler.ConnectToComputer(info, true, MyApp.MajorVersion, MyApp.MinorVersion);
base.Dispatcher.BeginInvoke(delegate
{
this.AddComputerDispatcher(connectResult);
}
);
}
private void AddComputerDispatcher(NetworkHandlerCommon.ConnectResult connectResult)
{
this.progressBar.IsIndeterminate = false;
this.StatusTextBlock.Visibility=(Visibility)1;
this.ContentPanel.Visibility=(Visibility)0;
if (connectResult == NetworkHandlerCommon.ConnectResult.Connected)
{
base.NavigationService.Navigate(MyApp.HomePage());
return;
}
this.ShowConnectError(connectResult);
}
private void ShowConnectError(NetworkHandlerCommon.ConnectResult result)
{
switch (result)
{
case NetworkHandlerCommon.ConnectResult.UpdateServer:
ErrorHandler.ShowErrorMessage(1003);
return;
case NetworkHandlerCommon.ConnectResult.UpdateClient:
ErrorHandler.ShowErrorMessage(1002);
return;
case NetworkHandlerCommon.ConnectResult.Failed:
ErrorHandler.ShowErrorMessage(1005);
return;
case NetworkHandlerCommon.ConnectResult.AuthenticationFailure:
MessageBox.Show("Error: Password is incorrect.");
return;
case NetworkHandlerCommon.ConnectResult.NetworkNotConnected:
MainPage.ShowNetworkDisconnectedMessage();
return;
default:
return;
}
}
and this class is showing the error that it will happen if true
public class ErrorHandler
{
private class ErrorInfo
{
public string ErrorMessage
{
get;
set;
}
public string ErrorURL
{
get;
set;
}
public ErrorInfo(string errorMessage, string errorURL)
{
this.ErrorMessage = errorMessage;
this.ErrorURL = errorURL;
}
}
private static Dictionary<int, ErrorHandler.ErrorInfo> errorMapping;
static ErrorHandler()
{
ErrorHandler.errorMapping = new Dictionary<int, ErrorHandler.ErrorInfo>();
ErrorHandler.errorMapping.Add(1000, new ErrorHandler.ErrorInfo("Error 1000: We have detected that WiFi is NOT turned-on at your phone.", "http://wp.me/p1ZVRQ-w"));
ErrorHandler.errorMapping.Add(1001, new ErrorHandler.ErrorInfo("Error 1001: Password is incorrect.", "http://wp.me/p1ZVRQ-E"));
ErrorHandler.errorMapping.Add(1002, new ErrorHandler.ErrorInfo("Error 1002: Older version of application is running. Update application from Marketplace.", "http://wp.me/p1ZVRQ-H"));
ErrorHandler.errorMapping.Add(1003, new ErrorHandler.ErrorInfo("Error 1003: Older version of PC Remote server is running at PC", "http://wp.me/p1ZVRQ-K"));
ErrorHandler.errorMapping.Add(1004, new ErrorHandler.ErrorInfo("Error 1004: Computer is connected to a public network", "http://wp.me/p1ZVRQ-d"));
ErrorHandler.errorMapping.Add(1005, new ErrorHandler.ErrorInfo("Error 1005: Failed connecting to your PC", "http://wp.me/p1ZVRQ-j"));
ErrorHandler.errorMapping.Add(1006, new ErrorHandler.ErrorInfo("Error 1006: Could not wake up your computer", "http://wp.me/p1ZVRQ-S"));
ErrorHandler.errorMapping.Add(1007, new ErrorHandler.ErrorInfo("Error 1007: Unable to resume the app.", "http://wp.me/p1ZVRQ-W"));
}
public static void ShowErrorMessage(int errorId)
{
if (ErrorHandler.errorMapping.ContainsKey(errorId))
{
ErrorHandler.ErrorInfo errorInfo = ErrorHandler.errorMapping[errorId];
string text = errorInfo.ErrorMessage + Environment.NewLine + Environment.NewLine + "Need help troubleshooting this? ";
if (MessageBox.Show(text, "Error", MessageBoxButton.OKCancel).Equals(1))
{
MyApp.GoToWebURL(errorInfo.ErrorURL);
}
}
}
}
I am writing up a checkout, build and deployment application in C#, and need to know the best way to detect whether my call to msbuild.exe has succeeded or not. I have tried to use the error code from the process, but I am not sure whether this is always accurate.
Is there a way (through the code below) that I can tell whether msbuild.exe completed successfully?
try
{
Process msbProcess = new Process();
msbProcess.StartInfo.FileName = this.MSBuildPath;
msbProcess.StartInfo.Arguments = msbArguments;
msbProcess.Start();
msbProcess.WaitForExit();
if (msbProcess.ExitCode != 0)
{
//
}
else
{
//
}
msbProcess.Close();
}
catch (Exception ex)
{
//
}
As far as I've been able to determine, MSBuild returns an exit code greater then zero when it encounters an error. If it doesn't encounter any errors, it returns exit code 0. I've never seen it exit with code lower than 0.
I use it in a batch file:
msbuild <args>
if errorlevel 1 goto errorDone
In four years of using it this way, I've never had reason to question the correctness of this approach.
Several questions on the MSDN forums ask the same thing.
The standard response is, in effect, "if errorlevel is 0, then there was no error".
Sorry if I'm a little bit too late for the party... but nearly 7 years after the question was posted I wanted to see a complete answer for it. I did some tests using the code below, and here are the conclusions:
Analysis
msbuild.exe returns 1 when at least one build error occurs, and returns 0 when the build is successfully completed. At present, the program does not take warnings into account, which means a successful build with warnings causes msbuild.exe to still return 0.
Other errors like: trying to build a project that does not exist, or providing an incorrect argument (like /myInvalidArgument), will also cause msbuild.exe to return 1.
Source Code
The following C# code is a complete implementation for building your favorite projects by firing msbuild.exe from a command line. Don't forget to setup any necessary environment settings before compiling your projects.
Your BuildControl class:
using System;
namespace Example
{
public sealed class BuildControl
{
// ...
public bool BuildStuff()
{
MsBuilder builder = new MsBuilder(#"C:\...\project.csproj", "Release", "x86")
{
Target = "Rebuild", // for rebuilding instead of just building
};
bool success = builder.Build(out string buildOutput);
Console.WriteLine(buildOutput);
return success;
}
// ...
}
}
MsBuilder class: Builds stuff by calling MsBuild.exe from command line:
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Example
{
public sealed class MsBuilder
{
public string ProjectPath { get; }
public string LogPath { get; set; }
public string Configuration { get; }
public string Platform { get; }
public int MaxCpuCount { get; set; } = 1;
public string Target { get; set; } = "Build";
public string MsBuildPath { get; set; } =
#"C:\Program Files (x86)\Microsoft Visual Studio\2017\Enterprise\MSBuild\15.0\Bin\MsBuild.exe";
public string BuildOutput { get; private set; }
public MsBuilder(string projectPath, string configuration, string platform)
{
ProjectPath = !string.IsNullOrWhiteSpace(projectPath) ? projectPath : throw new ArgumentNullException(nameof(projectPath));
if (!File.Exists(ProjectPath)) throw new FileNotFoundException(projectPath);
Configuration = !string.IsNullOrWhiteSpace(configuration) ? configuration : throw new ArgumentNullException(nameof(configuration));
Platform = !string.IsNullOrWhiteSpace(platform) ? platform : throw new ArgumentNullException(nameof(platform));
LogPath = Path.Combine(Path.GetDirectoryName(ProjectPath), $"{Path.GetFileName(ProjectPath)}.{Configuration}-{Platform}.msbuild.log");
}
public bool Build(out string buildOutput)
{
List<string> arguments = new List<string>()
{
$"/nologo",
$"\"{ProjectPath}\"",
$"/p:Configuration={Configuration}",
$"/p:Platform={Platform}",
$"/t:{Target}",
$"/maxcpucount:{(MaxCpuCount > 0 ? MaxCpuCount : 1)}",
$"/fileLoggerParameters:LogFile=\"{LogPath}\";Append;Verbosity=diagnostic;Encoding=UTF-8",
};
using (CommandLineProcess cmd = new CommandLineProcess(MsBuildPath, string.Join(" ", arguments)))
{
StringBuilder sb = new StringBuilder();
sb.AppendLine($"Build started: Project: '{ProjectPath}', Configuration: {Configuration}, Platform: {Platform}");
// Call MsBuild:
int exitCode = cmd.Run(out string processOutput, out string processError);
// Check result:
sb.AppendLine(processOutput);
if (exitCode == 0)
{
sb.AppendLine("Build completed successfully!");
buildOutput = sb.ToString();
return true;
}
else
{
if (!string.IsNullOrWhiteSpace(processError))
sb.AppendLine($"MSBUILD PROCESS ERROR: {processError}");
sb.AppendLine("Build failed!");
buildOutput = sb.ToString();
return false;
}
}
}
}
}
CommandLineProcess class - Starts a command line process and waits until it finishes. All standard output/error is captured, and no separate window is started for the process:
using System;
using System.Diagnostics;
using System.IO;
namespace Example
{
public sealed class CommandLineProcess : IDisposable
{
public string Path { get; }
public string Arguments { get; }
public bool IsRunning { get; private set; }
public int? ExitCode { get; private set; }
private Process Process;
private readonly object Locker = new object();
public CommandLineProcess(string path, string arguments)
{
Path = path ?? throw new ArgumentNullException(nameof(path));
if (!File.Exists(path)) throw new ArgumentException($"Executable not found: {path}");
Arguments = arguments;
}
public int Run(out string output, out string err)
{
lock (Locker)
{
if (IsRunning) throw new Exception("The process is already running");
Process = new Process()
{
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo()
{
FileName = Path,
Arguments = Arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
},
};
if (!Process.Start()) throw new Exception("Process could not be started");
output = Process.StandardOutput.ReadToEnd();
err = Process.StandardError.ReadToEnd();
Process.WaitForExit();
try { Process.Refresh(); } catch { }
return (ExitCode = Process.ExitCode).Value;
}
}
public void Kill()
{
lock (Locker)
{
try { Process?.Kill(); }
catch { }
IsRunning = false;
Process = null;
}
}
public void Dispose()
{
try { Process?.Dispose(); }
catch { }
}
}
}
PS: I'm using Visual Studio 2017 / .NET 4.7.2