【问题标题】:Wrapping a C# service in a console app to debug it在控制台应用程序中包装 C# 服务以对其进行调试
【发布时间】:2018-07-04 02:51:29
【问题描述】:

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

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

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

【问题讨论】:

    标签: c# service console-application


    【解决方案1】:

    你可以在主入口点做这样的事情:

    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();
    }
    

    【讨论】:

    • +1 - 我编写的几个服务中的代码几乎完全相同。
    【解决方案2】:

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

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

    【讨论】:

      【解决方案3】:

      为了避免使用全局定义,我通常在运行时通过 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);
              }
         }
      

      【讨论】:

      • 如果您希望控制台显示,请不要忘记在项目属性中将输出类型设置为控制台应用程序。该程序仍然可以作为 Windows 服务运行,无论它是控制台应用程序还是 Windows 应用程序,所以不用担心。
      【解决方案4】:
      【解决方案5】:

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

      使用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();
          }
      }
      

      【讨论】:

        【解决方案6】:

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

        【讨论】:

          【解决方案7】:

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

           #if DEBUG
              Debugger.Break();
           #endif
          

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

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

          【讨论】:

            【解决方案8】:

            这是一个blog post,关于将您的 Windows 服务作为控制台应用程序运行。

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

            【讨论】:

              【解决方案9】:

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

              使用 testdriven.net 或 jetbrains testrunner 会更容易。

              【讨论】:

                【解决方案10】:

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

                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; }
                    }
                }
                

                【讨论】:

                  猜你喜欢
                  • 2011-09-19
                  • 2021-08-25
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-10-29
                  • 1970-01-01
                  • 1970-01-01
                  • 2014-03-28
                  相关资源
                  最近更新 更多