如何在Visual Studio中调试Windows服务?


85

是否可以在Visual Studio中调试Windows服务?

我使用了类似的代码

System.Diagnostics.Debugger.Break();

但是它给出了一些代码错误,例如:

我收到两个事件错误:eventID 4096 VsJITDebugger和“服务没有及时响应启动或控制请求。”

Answers:


123

在服务OnStart方法中使用以下代码:

System.Diagnostics.Debugger.Launch();

从弹出消息中选择Visual Studio选项。

注意:要仅在调试模式下使用它,#if DEBUG可以使用编译器指令,如下所示。这将防止在生产服务器上以发布模式进行意外操作或调试。

#if DEBUG
    System.Diagnostics.Debugger.Launch();
#endif

9
记住要以管理员身份运行VS。然后它将在列表中可用。
迈克尔

1
有人可以澄清弹出消息的含义吗?什么时候/如何出现?
Mike

@Mike,安装并启动服务,它将出现。
Harshit

@Mike,它是一个Windows对话框,它在交互式(已登录)桌面上弹出,并询问您是否要选择用于调试过程的应用程序。如果选择VS,它将启动调试器并附加到进程
Chris Johnson

63

您也可以尝试一下。

  1. 创建Windows服务,然后安装并开始…。也就是说,Windows服务必须在您的系统中运行。
  2. 服务运行时,转到“调试”菜单,单击“附加进程”(或旧Visual Studio中的进程)
  3. 找到您正在运行的服务,然后确保选择了“从所有用户显示进程”“在所有会话中显示进程”,如果未选中,则将其选中。

enter image description here

  1. 点击附加按钮
  2. 点击确定
  3. 点击关闭
  4. 将断点设置到所需的位置,然后等待执行。只要您的代码到达该点,它将自动调试。
  5. 请记住,将断点放置可到达的位置(如果它是onStart()),然后停止并再次启动服务

(经过大量的搜索,我在“如何在Visual Studio中调试Windows服务”中找到了这一点。)


2
在VS2013 Update 5中看不到此选项。:(
Sandeep talabathula

1
但是您需要以管理员身份运行Vs-2017
chozha rajan 19'2

1
我试过了 它在onStop中使用断点,但在onStart上不使用断点,因为当服务停止时,调试器将无法连接
KansaiRobot

22

您应该将所有将执行服务工作的代码从一个服务项目中分离出来,然后创建一个可以正常运行和调试的测试应用程序。

服务项目只是实现其服务部分所需的外壳。


哇!Ctrl + C然后Ctrl + V,我的意思是说新项目。是的,只有我在做。不可能将任何进程附加到调试或任何其他选项,而不是附加项目。
PawanS 2011年

1
当然可以,但是如果在开发过程中取出Windows服务部分,则开发Windows服务要容易得多。
Lasse V. Karlsen

嗯...这是个好方法,但简单地它会使工作加倍。我以为还有其他办法。
PawanS 2011年

9
我看不出它将如何使工作“加倍”。当然,在创建额外的项目并将服务中的代码分离到第三个项目时,这会增加一点点开销,但是除此之外,您不会复制代码,而是将其移动,并参考该项目。
Lasse V. Karlsen

3
^ + 1。它使管理服务的工作加倍,就开发时间而言,该服务几乎为零,而您只需执行一次。调试序列是痛苦的-而是使其作为命令行双重启动。检查谷歌的预定义包装类,以允许这样做(他们使用反射来模拟开始/停止没有服务类)。一小时的工作,大量的节省,净损失:负数-您可以节省时间。
TomTom

14

如Lasse V. Karlsen所建议的那样,或者在您的服务中设置一个循环,以等待调试器附加。最简单的是

while (!Debugger.IsAttached)
{
    Thread.Sleep(1000);
}

... continue with code

这样,您可以启动服务,然后在Visual Studio中选择“附加到进程...”并附加到您的服务,然后该服务将恢复正常退出。


我应该将上面的代码放在哪里...在附加过程中,我将服务命名为disable
PawanS 2011年

3
我们也使用了这样的代码 if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Kirill Kovalenko

该代码应尽早运行,然后再运行任何要调试的代码。
PauliØsterø2011年

@Pawan:在 Start/OnStart()我猜
abatishchev 2011年

@Kirill:使用波浪号突出显示注释中的代码,例如foo(bar)
abatishchev 2011年

7

鉴于ServiceBase.OnStart具有protected可见性,我沿着反射路线进行调试。

private static void Main(string[] args)
{
    var serviceBases = new ServiceBase[] {new Service() /* ... */ };

#if DEBUG
    if (Environment.UserInteractive)
    {
        const BindingFlags bindingFlags =
            BindingFlags.Instance | BindingFlags.NonPublic;

        foreach (var serviceBase in serviceBases)
        {
            var serviceType = serviceBase.GetType();
            var methodInfo = serviceType.GetMethod("OnStart", bindingFlags);

            new Thread(service => methodInfo.Invoke(service, new object[] {args})).Start(serviceBase);
        }

        return;
    }
#endif

    ServiceBase.Run(serviceBases);
}

请注意,Thread默认情况下是前台线程。returnMain伪服务线程运行时开始不会终止该过程。


由于OnStart应该可以快速返回,因此您无需在另一个线程中执行此操作。但是,如果该服务没有启动另一个线程,则您的进程将立即退出。
马特·康诺利

@MattConnolly在后者上:如有必要,我更改上面的代码以启动一个永远休眠的前台线程(在正常处理之前)。
ta.speot.is

这应该是一个真实的答案。作品精美!
lentyai

4

微软的一篇文章在这里解释了如何调试Windows服务以及任何人通过附加到进程来调试Windows服务都会错过哪些部分。

下面是我的工作代码。我遵循了Microsoft建议的方法。

将此代码添加到program.cs

static void Main(string[] args)
{
    // 'If' block will execute when launched through Visual Studio
    if (Environment.UserInteractive)
    {
        ServiceMonitor serviceRequest = new ServiceMonitor();
        serviceRequest.TestOnStartAndOnStop(args);
    }
    else // This block will execute when code is compiled as a Windows application
    {
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new ServiceMonitor()
        };
        ServiceBase.Run(ServicesToRun);
    }
}

将此代码添加到ServiceMonitor类。

internal void TestOnStartAndOnStop(string[] args)
{
    this.OnStart(args);
    Console.ReadLine();
    this.OnStop();
}

现在去项目属性,选择选项卡“应用程序”,然后选择输出类型调试时为“控制台应用程序”或“Windows应用程序”的时候与调试,进行重新编译和安装服务。

在此处输入图片说明


有没有一种方法可以在Debug和Window Application中将输出设置为Console Application?
kofifus

3

您可以制作一个控制台应用程序。我使用此main功能:

    static void Main(string[] args)
    {
        ImportFileService ws = new ImportFileService();
        ws.OnStart(args);
        while (true)
        {
            ConsoleKeyInfo key = System.Console.ReadKey();
            if (key.Key == ConsoleKey.Escape)
                break;
        }
        ws.OnStop();
    }

ImportFileService除了继承者(ServiceBase),我的类与Windows服务应用程序中的类完全相同。


是在同一项目上还是该控制台应用程序的另一个项目上
PawanS 2011年

这是两个具有相似类的不同项目。就我而言,这是一个简单的服务,只有ImportFileService类是重复的。当我想进行开发/测试时,我使用consoleapp,然后复制/粘贴。就像Lasse V. Karlsen所说的,这是一个调试程序,所有逻辑(业务)都在第三个项目上。
kerrubin 2011年

OnStart不受保护吗?
Joe Phillips

是的,是。这就是为什么我说“除了继承者(ServiceBase)。”。我发现在控制台应用程序中调试更容易,但是我知道这是否不能说服所有人。
kerrubin

3

我使用了一个很棒的Nuget包,称为ServiceProcess.Helpers。

我引用...

它通过在连接调试器的情况下创建播放/停止/暂停UI来帮助Windows服务进行调试,而且还允许Windows服务器环境安装和运行该服务。

所有这些只需一行代码。

http://windowsservicehelper.codeplex.com/

安装并连线后,只需将Windows服务项目设置为启动项目,然后在调试器上单击“开始”。


感谢分享!到目前为止,这是最简单的解决方案!
惠灵顿扎纳利

2

您也可以尝试使用System.Diagnostics.Debugger.Launch()方法。它有助于将调试器指针带到指定位置,然后可以调试代码。

在执行此步骤之前,使用Visual Studio命令提示符-installutil projectservice.exe命令行安装service.exe。

然后从控制面板->管理工具->计算机管理->服务和应用程序->服务->您的服务名称启动服务


2

我只是将此代码添加到我的服务类中,因此可以间接调用OnStart,类似于OnStop。

    public void MyOnStart(string[] args)
    {
        OnStart(args);
    }

2

/Console在Visual Studio项目的Debug(调试)Start Options(开始选项)Command Line arguments(命令行参数)中使用该参数:

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
         var runMode = args.Contains(@"/Console")
             ? WindowsService.RunMode.Console
             : WindowsService.RunMode.WindowsService;
         new WinodwsService().Run(runMode);
    }
}


public class WindowsService : ServiceBase
{
    public enum RunMode
    {
        Console,
        WindowsService
    }

    public void Run(RunMode runMode)
    {
        if (runMode.Equals(RunMode.Console))
        {
            this.StartService();
            Console.WriteLine("Press <ENTER> to stop service...");
            Console.ReadLine();

            this.StopService();
            Console.WriteLine("Press <ENTER> to exit.");
            Console.ReadLine();
        }
        else if (runMode.Equals(RunMode.WindowsService))
        {
            ServiceBase.Run(new[] { this });
        }
    }

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

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

    /// <summary>
    /// Logic to Start Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StartService(params string[] args){ ... }

    /// <summary>
    /// Logic to Stop Service
    /// Public accessibility for running as a console application in Visual Studio debugging experience
    /// </summary>
    public virtual void StopService() {....}
}

2

我找到了这个问题,但我认为缺少一个清晰而简单的答案。

我不想将调试器附加到进程,但是我仍然希望能够调用服务OnStartOnStop方法。我还希望它作为控制台应用程序运行,以便我可以将信息从NLog记录到控制台。

我发现这些出色的指南可以做到这一点:

首先将项目更改Output typeConsole Application

在此处输入图片说明

更改Program.cs为如下所示:

static class Program
{
    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    static void Main()
    {
        // Startup as service.
        ServiceBase[] ServicesToRun;
        ServicesToRun = new ServiceBase[]
        {
            new Service1()
        };

        if (Environment.UserInteractive)
        {
            RunInteractive(ServicesToRun);
        }
        else
        {
            ServiceBase.Run(ServicesToRun);
        }
    }
}

然后添加以下方法以允许服务以交互方式运行。

static void RunInteractive(ServiceBase[] servicesToRun)
{
    Console.WriteLine("Services running in interactive mode.");
    Console.WriteLine();

    MethodInfo onStartMethod = typeof(ServiceBase).GetMethod("OnStart",
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Starting {0}...", service.ServiceName);
        onStartMethod.Invoke(service, new object[] { new string[] { } });
        Console.Write("Started");
    }

    Console.WriteLine();
    Console.WriteLine();
    Console.WriteLine(
        "Press any key to stop the services and end the process...");
    Console.ReadKey();
    Console.WriteLine();

    MethodInfo onStopMethod = typeof(ServiceBase).GetMethod("OnStop",
        BindingFlags.Instance | BindingFlags.NonPublic);
    foreach (ServiceBase service in servicesToRun)
    {
        Console.Write("Stopping {0}...", service.ServiceName);
        onStopMethod.Invoke(service, null);
        Console.WriteLine("Stopped");
    }

    Console.WriteLine("All services stopped.");
    // Keep the console alive for a second to allow the user to see the message.
    Thread.Sleep(1000);
}

好代码!简单,有效。+1。但是就像我将其制作为Forms应用程序一样容易。我真的很讨厌控制台应用程序。另外,您可以轻松地为每个服务事件实现一个“表单按钮”。
罗兰

1

不幸的是,如果您尝试在Windows Service操作的开始进行调试,则无法“附加”到正在运行的进程。我尝试在OnStart过程中使用Debugger.Break(),但对于64位Visual Studio 2010编译的应用程序,break命令只会引发如下错误:

System error 1067 has occurred.

那时,您需要在注册表中为可执行文件设置“图像文件执行”选项。设置需要五分钟,并且效果很好。这是Microsoft文章,其详细信息如下:

如何:自动启动调试器


1

尝试使用Visual Studio自己的构建后事件命令行

尝试在构建后添加此内容:

@echo off
sc query "ServiceName" > nul
if errorlevel 1060 goto install
goto stop

:delete
echo delete
sc delete "ServiceName" > nul
echo %errorlevel%
goto install

:install
echo install
sc create "ServiceName" displayname= "Service Display Name" binpath= "$(TargetPath)" start= auto > nul
echo %errorlevel%
goto start

:start
echo start
sc start "ServiceName" > nul
echo %errorlevel%
goto end

:stop
echo stop
sc stop "ServiceName" > nul
echo %errorlevel%
goto delete

:end

如果生成错误并带有类似Error 1 The command "@echo off sc query "ServiceName" > nul这样的消息,则Ctrl+C然后Ctrl+V将错误消息导入记事本,然后查看消息的最后一句。

可以说exited with code x。在此处查找一些常见错误中的代码,并查看如何解决它。

1072 -- Marked for deletion → Close all applications that maybe using the service including services.msc and Windows event log.
1058 -- Can't be started because disabled or has no enabled associated devices → just delete it.
1060 -- Doesn't exist → just delete it.
1062 -- Has not been started → just delete it.
1053 -- Didn't respond to start or control → see event log (if logged to event log). It may be the service itself throwing an exception.
1056 -- Service is already running → stop the service, and then delete.

更多关于错误代码的信息

如果出现这样的错误消息,

Error    11    Could not copy "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". Exceeded retry count of 10. Failed.    ServiceName
Error    12    Unable to copy file "obj\x86\Debug\ServiceName.exe" to "bin\Debug\ServiceName.exe". The process cannot access the file 'bin\Debug\ServiceName.exe' because it is being used by another process.    ServiceName

打开cmd,然后尝试先杀死它 taskkill /fi "services eq ServiceName" /f

如果一切正常,F5则足以对其进行调试。


0

在该OnStart方法中,执行以下操作。

protected override void OnStart(string[] args)
{
    try
    {
        RequestAdditionalTime(600000);
        System.Diagnostics.Debugger.Launch(); // Put breakpoint here.

        .... Your code
    }
    catch (Exception ex)
    {
        .... Your exception code
    }
}

然后以管理员身份运行命令提示符,并输入以下内容:

c:\> sc create test-xyzService binPath= <ProjectPath>\bin\debug\service.exe type= own start= demand

上一行将在服务列表中创建test-xyzService。

要启动该服务,这将提示您附加是否在Visual Studio中亮相。

c:\> sc start text-xyzService

要停止服务:

c:\> sc stop test-xyzService

要删除或卸载:

c:\> sc delete text-xyzService

0

通过http调试Windows服务(已通过VS 2015 Update 3和.Net FW 4.6测试)

首先,您必须在VS解决方案中创建一个控制台项目(添加->新项目->控制台应用程序)。

在新项目中,使用该代码创建一个“ ConsoleHost”类:

class ConsoleHost : IDisposable
{
    public static Uri BaseAddress = new Uri(http://localhost:8161/MyService/mex);
    private ServiceHost host;

    public void Start(Uri baseAddress)
    {
        if (host != null) return;

        host = new ServiceHost(typeof(MyService), baseAddress ?? BaseAddress);

        //binding
        var binding = new BasicHttpBinding()
        {
            Name = "MyService",
            MessageEncoding = WSMessageEncoding.Text,
            TextEncoding = Encoding.UTF8,
            MaxBufferPoolSize = 2147483647,
            MaxBufferSize = 2147483647,
            MaxReceivedMessageSize = 2147483647
        };

        host.Description.Endpoints.Clear();
        host.AddServiceEndpoint(typeof(IMyService), binding, baseAddress ?? BaseAddress);

        // Enable metadata publishing.
        var smb = new ServiceMetadataBehavior
        {
            HttpGetEnabled = true,
            MetadataExporter = { PolicyVersion = PolicyVersion.Policy15 },
        };

        host.Description.Behaviors.Add(smb);

        var defaultBehaviour = host.Description.Behaviors.OfType<ServiceDebugBehavior>().FirstOrDefault();
        if (defaultBehaviour != null)
        {
            defaultBehaviour.IncludeExceptionDetailInFaults = true;
        }

        host.Open();
    }

    public void Stop()
    {
        if (host == null)
            return;

        host.Close();
        host = null;
    }

    public void Dispose()
    {
        this.Stop();
    }
}

这是Program.cs类的代码:

public static class Program
{
    [STAThread]
    public static void Main(string[] args)
    {
        var baseAddress = new Uri(http://localhost:8161/MyService);
        var host = new ConsoleHost();
        host.Start(null);
        Console.WriteLine("The service is ready at {0}", baseAddress);
        Console.WriteLine("Press <Enter> to stop the service.");
        Console.ReadLine();
        host.Stop();
    }
}

诸如连接字符串之类的配置应复制到Console项目的App.config文件中。

要构建控制台,请右键单击Console项目,然后单击Debug-> Start new instance。


0

只需在服务类中添加构造函数(如果尚未添加)。在下面,您可以查看Visual Basic .net的示例。

Public Sub New()
   OnStart(Nothing) 
End Sub

之后,右键单击项目,然后选择“调试->启动新实例”。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.