我找到了很多部分答案,但没有什么足够的。
案例: 应用程序是一个工作命令行应用程序,没有用户交互,除了能够在输入停止时接收按键,这已经以一种方式编写,即使没有运行时也禁用环境.UserInteractive == true。 / p>
我正在使用Visual Studio 2010。
问题是我需要将此应用程序转换为Windows服务。是“只是”将新的类文件作为服务,并让它在现有应用程序上调用我的启动和停止方法?
安装程序(VS'默认的msi安装程序)如何,现有的安装程序项目是否可以“升级”以处理服务安装?
之前我搞砸了这个问题,并最终得到一个拒绝安装的安装程序,因为它一直在检测已安装的服务,然后停止安装过程,然后立即回滚所有内容。它检测到的服务就是刚刚安装的服务。
答案 0 :(得分:11)
要将控制台应用程序作为Windows服务或控制台应用程序运行,请编写单个控制台应用程序并使用命令行参数来确定是应该直接运行还是启动该服务。包含安装程序/卸载程序,以使用正确的命令行参数安装为Windows服务。
这是我们使用的提供此功能的基类。
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
}
}
答案 1 :(得分:1)
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);
}
}
检查上面的链接。我提供了一些代码以及描述使用控制台作为控制台和服务的双重任务的链接。我将使用控制台项目并在作为服务运行之前检查UserInteractive。通过这种方式,您可以像调试控制台一样进行调试,但可以将其作为服务安装在生产服务器上。
关于安装,我没有从.msi安装的经验,但是我们使用批处理脚本来安装服务(使用sc.exe),然后只需更新文件即可更新代码
答案 2 :(得分:0)
最好的办法是将新项目作为Windows服务启动。 在这个新项目中,您将找到Service1.cs,这是将从start开始运行的文件。以下代码将在文件中:
namespace WindowsService1
{
public partial class Service1 : ServiceBase
{
public Service1()
{
InitializeComponent();
}
protected override void OnStart(string[] args)
{
}
protected override void OnStop()
{
}
}
}
从这里弄清楚要做什么并不难。只需将您的类添加到项目中,并确保在OnStart()函数中复制主代码。当然,您可能需要稍微编辑代码以确保其中没有读取行。
现在您必须创建并安装。您可以在此处找到如何执行此操作: http://msdn.microsoft.com/en-us/library/zt39148a%28v=vs.100%29.aspx
我希望这有帮助:D
亲切的问候
罗哈斯