繁体   English   中英

在控制台应用程序中包装C#服务以进行调试

[英]Wrapping a C# service in a console app to debug it

我想调试一个用C#编写的服务,老式的方式太长了。 我必须停止服务,启动我在调试模式下使用该服务的应用程序(Visual Studio 2008),启动服务,附加到服务进程,然后在我的Asp.Net应用程序中导航以触发服务。

我基本上让服务在后台运行,等待任务。 Web应用程序将触发服务选择的任务。

我想要做的是有一个控制台应用程序来激活服务,以便我调试。 有没有人知道的简单演示?

您可以在主入口点执行以下操作:

static void Main()
{
#if DEBUG
    Service1 s = new Service1();
    s.Init(); // Init() is pretty much any code you would have in OnStart().
#else
    ServiceBase[] ServicesToRun;
    ServicesToRun=new ServiceBase[] 
    { 
        new Service1() 
    };
    ServiceBase.Run(ServicesToRun);
#endif
}

并在您的OnStart事件处理程序中:

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

我总是采用的方法是将所有应用程序的逻辑隔离到类库中。 这使得您的服务项目实际上只是一个托管您的类库作为服务的shell。

通过这样做,您可以轻松地对代码进行单元测试和调试,而无需通过附加到进程来处理调试服务的麻烦。 我当然建议进行单元测试,但是如果你没有这样做,那么添加一个控制台应用程序,它可以调用与你的服务相同的入口点。

为了避免使用全局定义,我通常在运行时测试我是通过Environment.UserInteractive属性的服务还是常规应用程序。

    [MTAThread()]
    private static void Main()
    {
        if (!Environment.UserInteractive)
        {
            ServiceBase[] aServicesToRun;

            // More than one NT Service may run within the same process. To add
            // another service to this process, change the following line to
            // create a second service object. For example,
            //
            //   ServicesToRun = New System.ServiceProcess.ServiceBase () {New ServiceMain, New MySecondUserService}
            //
            aServicesToRun = new ServiceBase[] {new ServiceMain()};

            Run(aServicesToRun);
        }
        else
        {
            var oService = new ServiceMain();
            oService.OnStart(null);
        }
   }

您可以通过反射调用服务方法,如下所示。

使用Environment.UserInteractive使我们能够知道我们是作为控制台应用程序还是作为服务运行。

ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
    new MyService()
};

if (!Environment.UserInteractive)
{
    // This is what normally happens when the service is run.
    ServiceBase.Run(ServicesToRun);
}
else
{
    // Here we call the services OnStart via reflection.
    Type type = typeof(ServiceBase);
    BindingFlags flags = BindingFlags.Instance | BindingFlags.NonPublic;
    MethodInfo method = type.GetMethod("OnStart", flags);

    foreach (ServiceBase service in ServicesToRun)
    {
        Console.WriteLine("Running " + service.ServiceName + ".OnStart()");
        // Your Main method might not have (string[] args) but you could add that to be able to send arguments in.
        method.Invoke(service, new object[] { args });
    }

    Console.WriteLine("Finished running all OnStart Methods.");

    foreach (ServiceBase service in ServicesToRun)
    {
        Console.WriteLine("Running " + service.ServiceName + ".OnStop()");
        service.Stop();
    }
}

TopShelf是另一个非常适合这种方法的项目。 它允许您将进程作为服务运行,或作为具有最少配置的常规控制台应用程序运行。

我倾向于使用配置设置或使用指令进行调试构建:

 #if DEBUG
    Debugger.Break();
 #endif

要么

if(Settings.DebugBreak)
            Debugger.Break();

我把它放在服务组件的OnStart方法中。 然后会自动提示您并附加到该过程。

这是一篇关于将Windows服务作为控制台应用程序运行的博客文章

您也可以创建一个新的控制台应用程序,它引用与您的服务相同的逻辑来测试方法,或者在服务逻辑上设置单元测试

我过去曾使用单元测试来调试困难的设置,只需编写一个单元测试,用任何参数调用任何服务方法,并在单元测试中设置调试断点。

使用testdriven.net或jetbrains testrunner可以更轻松。

我用它来检查我的进程是否作为服务运行。

public class ServiceDiagnostics
{
    readonly bool _isUserService;
    readonly bool _isLocalSystem;
    readonly bool _isInteractive;

    public ServiceDiagnostics()
    {
        var wi = WindowsIdentity.GetCurrent();
        var wp = new WindowsPrincipal(wi);

        var serviceSid = new SecurityIdentifier(WellKnownSidType.ServiceSid, null);
        var localSystemSid = new SecurityIdentifier(WellKnownSidType.LocalSystemSid, null);
        var interactiveSid = new SecurityIdentifier(WellKnownSidType.InteractiveSid, null);

        this._isUserService = wp.IsInRole(serviceSid);

        // Neither Interactive or Service was present in the current user's token, This implies 
        // that the process is running as a service, most likely running as LocalSystem.
        this._isLocalSystem = wp.IsInRole(localSystemSid);

        // This process has the Interactive SID in its token.  This means that the process is 
        // running as a console process.
        this._isInteractive = wp.IsInRole(interactiveSid);
    }

    public bool IsService
    {
        get { return this.IsUserService || this.IsLocalSystem || !this.IsInteractive; }    
    }

    public bool IsConsole
    {
        get { return !this.IsService; }
    }

    /// <summary>
    /// This process has the Service SID in its token. This means that the process is running 
    /// as a service running in a user account (not local system).
    /// </summary>
    public bool IsUserService
    {
        get { return this._isUserService; }
    }

    /// <summary>
    /// Neither Interactive or Service was present in the current user's token, This implies 
    /// that the process is running as a service, most likely running as LocalSystem.
    /// </summary>
    public bool IsLocalSystem
    {
        get { return this._isLocalSystem; }
    }

    /// <summary>
    /// This process has the Interactive SID in its token.  This means that the process is 
    /// running as a console process.
    /// </summary>
    public bool IsInteractive
    {
        get { return this._isInteractive; }
    }
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM