当前位置:  开发笔记 > 编程语言 > 正文

我是作为一种服务运行的

如何解决《我是作为一种服务运行的》经验,为你挑选了6个好方法。

我目前正在为可以在控制台中运行的服务编写一些引导代码.它本质上归结为调用OnStart()方法而不是使用ServiceBase来启动和停止服务(因为如果它没有作为服务安装并且使调试成为一场噩梦,它就不会运行应用程序).

现在我使用Debugger.IsAttached来确定我是否应该使用ServiceBase.Run或[service] .OnStart,但我知道这不是最好的主意,因为有些时候最终用户想要在控制台中运行服务(看看输出等实时).

关于如何确定Windows服务控制器是否启动"我",或者用户是否在控制台中启动"我"的任何想法?Apparantly Environment.IsUserInteractive不是答案.我想过使用命令行args,但这看起来很"脏".

我总是可以看到围绕ServiceBase.Run的try-catch语句,但这看起来很脏.编辑:尝试捕获不起作用.

我有一个解决方案:把它放在这里给所有其他感兴趣的堆叠器:

    public void Run()
    {
        if (Debugger.IsAttached || Environment.GetCommandLineArgs().Contains("-console"))
        {
            RunAllServices();
        }
        else
        {
            try
            {
                string temp = Console.Title;
                ServiceBase.Run((ServiceBase[])ComponentsToRun);
            }
            catch
            {
                RunAllServices();
            }
        }
    } // void Run

    private void RunAllServices()
    {
        foreach (ConsoleService component in ComponentsToRun)
        {
            component.Start();
        }
        WaitForCTRLC();
        foreach (ConsoleService component in ComponentsToRun)
        {
            component.Stop();
        }
    }

编辑:在StackOverflow上有另一个问题,那个人有环境问题.CurrentDirectory是"C:\ Windows\System32"看起来可能是答案.我今天要考试.



1> 小智..:

另一种解决方法..因此可以作为WinForm或Windows服务运行

var backend = new Backend();

if (Environment.UserInteractive)
{
     backend.OnStart();
     Application.EnableVisualStyles();
     Application.SetCompatibleTextRenderingDefault(false);
     Application.Run(new Fronend(backend));
     backend.OnStop();
}
else
{
     var ServicesToRun = new ServiceBase[] {backend};
     ServiceBase.Run(ServicesToRun);
}


我喜欢这个解决方案,它似乎是为什么`Environment.UserInteractive`设计的.
我尝试从Windows Docker容器运行进程,并且在那里UserInteractive也是false ...但是,我绝对不是作为服务运行的。
我测试过:当您选中"允许服务与桌面交互"时,UserInteractive为true.

2> Sean..:

我通常将Windows服务标记为控制台应用程序,它将命令行参数"-console"作为控制台运行,否则它作为服务运行.要调试你只需将项目选项中的命令行参数设置为"-console",然后就可以了!

这使得调试变得简单易行,这意味着默认情况下应用程序可以作为服务运行,这正是您所需要的.


这也是我如何做到的.效果很好; 调试的唯一问题是安全性(哪个帐户)和工作文件夹 - 它们更容易编码.

3> Kramii Reins..:

像Ash一样,我将所有实际的处理代码写在一个单独的类库程序集中,然后由windows服务可执行文件和控制台应用程序引用.

但是,有时候知道类库是否在服务可执行文件或控制台应用程序的上下文中运行是有用的.我这样做的方式是反映托管应用程序的基类.(抱歉VB,但我想以下可能很容易被c#化):

Public Class ExecutionContext
    ''' 
    ''' Gets a value indicating whether the application is a windows service.
    ''' 
    ''' 
    ''' true if this instance is service; otherwise, false.
    ''' 
    Public Shared ReadOnly Property IsService() As Boolean
        Get
            ' Determining whether or not the host application is a service is
            ' an expensive operation (it uses reflection), so we cache the
            ' result of the first call to this method so that we don't have to
            ' recalculate it every call.

            ' If we have not already determined whether or not the application
            ' is running as a service...
            If IsNothing(_isService) Then

                ' Get details of the host assembly.
                Dim entryAssembly As Reflection.Assembly = Reflection.Assembly.GetEntryAssembly

                ' Get the method that was called to enter the host assembly.
                Dim entryPoint As System.Reflection.MethodInfo = entryAssembly.EntryPoint

                ' If the base type of the host assembly inherits from the
                ' "ServiceBase" class, it must be a windows service. We store
                ' the result ready for the next caller of this method.
                _isService = (entryPoint.ReflectedType.BaseType.FullName = "System.ServiceProcess.ServiceBase")

            End If

            ' Return the cached result.
            Return CBool(_isService)
        End Get
    End Property

    Private Shared _isService As Nullable(Of Boolean) = Nothing
#End Region
End Class


如果同一个程序集可以作为控制台应用程序或Windows服务运行,我看不出这是如何工作的...... Assembly.GetEntryAssembly()和Assembly.EntryPoint在这两种情况下都返回相同的值.我猜它只有在两种情况下运行不同的程序集才有效.

4> gyrolf..:

什么对我有用:

执行实际服务工作的类在单独的线程中运行.

该线程从OnStart()方法中启动,并从OnStop()停止.

服务和控制台模式之间的决定取决于 Environment.UserInteractive

示例代码:

class MyService : ServiceBase
{
    private static void Main()
    {
        if (Environment.UserInteractive)
        {
            startWorkerThread();
            Console.WriteLine ("======  Press ENTER to stop threads  ======");
            Console.ReadLine();
            stopWorkerThread() ;
            Console.WriteLine ("======  Press ENTER to quit  ======");
            Console.ReadLine();
        }
        else
        {
            Run (this) ;
        }
    }

    protected override void OnStart(string[] args)
    {
        startWorkerThread();
    }

    protected override void OnStop()
    {
        stopWorkerThread() ;
    }
}


据我了解文档及其中的示例代码,Windows Forms应用程序没有任何限制.我在普通控制台应用程序中成功使用它.
我可以确认这是正确的.运行控制台时,"Environment.UserInteractive"为True,如果作为服务运行,则为False.
在某些情况下,此代码将失败-如果您从Windows Scheduler中运行该应用程序作为任务,则即使不是服务,Environment.UserInteractive也会设置为false。如果您打算从调度程序运行应用程序,请考虑使用更可靠的解决方案。

5> Ash..:

Jonathan,不完全是你的问题的答案,但我刚刚完成了一个Windows服务,并注意到调试和测试的困难.

通过简单地将所有实际处理代码写入单独的类库程序集中解决了这个问题,然后由Windows服务可执行文件引用,以及控制台应用程序和测试工具.

除了基本的计时器逻辑之外,所有更复杂的处理都发生在通用程序集中,并且可以非常容易地按需测试/运行.



6> Rolf Kristen..:

我已经修改了ProjectInstaller来附加命令行参数参数/ service,当它作为服务安装时:

static class Program
{
    static void Main(string[] args)
    {
        if (Array.Exists(args, delegate(string arg) { return arg == "/install"; }))
        {
            System.Configuration.Install.TransactedInstaller ti = null;
            ti = new System.Configuration.Install.TransactedInstaller();
            ti.Installers.Add(new ProjectInstaller());
            ti.Context = new System.Configuration.Install.InstallContext("", null);
            string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
            ti.Context.Parameters["assemblypath"] = path;
            ti.Install(new System.Collections.Hashtable());
            return;
        }

        if (Array.Exists(args, delegate(string arg) { return arg == "/uninstall"; }))
        {
            System.Configuration.Install.TransactedInstaller ti = null;
            ti = new System.Configuration.Install.TransactedInstaller();
            ti.Installers.Add(new ProjectInstaller());
            ti.Context = new System.Configuration.Install.InstallContext("", null);
            string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
            ti.Context.Parameters["assemblypath"] = path;
            ti.Uninstall(null);
            return;
        }

        if (Array.Exists(args, delegate(string arg) { return arg == "/service"; }))
        {
            ServiceBase[] ServicesToRun;

            ServicesToRun = new ServiceBase[] { new MyService() };
            ServiceBase.Run(ServicesToRun);
        }
        else
        {
            Console.ReadKey();
        }
    }
}

然后修改ProjectInstaller.cs以覆盖OnBeforeInstall()和OnBeforeUninstall()

[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
    public ProjectInstaller()
    {
        InitializeComponent();
    }

    protected virtual string AppendPathParameter(string path, string parameter)
    {
        if (path.Length > 0 && path[0] != '"')
        {
            path = "\"" + path + "\"";
        }
        path += " " + parameter;
        return path;
    }

    protected override void OnBeforeInstall(System.Collections.IDictionary savedState)
    {
        Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
        base.OnBeforeInstall(savedState);
    }

    protected override void OnBeforeUninstall(System.Collections.IDictionary savedState)
    {
        Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
        base.OnBeforeUninstall(savedState);
    }
}

推荐阅读
和谐啄木鸟
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有