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

时间:2011-01-13 10:09:14

标签: c# visual-studio visual-studio-2010 windows-services

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

我使用的代码如

System.Diagnostics.Debugger.Break();

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

  

我遇到两个事件错误:eventID 4096   VsJITDebugger和“服务确实   不响应开始或控制   请及时提出要求。“

17 个答案:

答案 0 :(得分:109)

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

System.Diagnostics.Debugger.Launch();

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

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

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

答案 1 :(得分:58)

您也可以试试这个。

  1. 创建Windows服务并安装并启动....也就是说,Windows服务必须在您的系统中运行。
  2. 当您的服务正在运行时,请转到调试菜单,单击附加进程(或在旧的Visual Studio中处理)
  3. 找到正在运行的服务,然后确保选中显示所有用户的流程在所有会话中显示流程,如果没有,则选择它。
  4. enter image description here

    1. 点击附加按钮
    2. 点击确定
    3. 点击关闭
    4. 将断点设置为您想要的位置并等待执行。只要代码到达那一点,它就会自动调试。
    5. 请记住,将断点置于可到达的地方,如果 onStart (),则停止并再次启动服务
    6. (经过大量的谷歌搜索后,我在“如何在Visual Studio中调试Windows服务”中找到了这一点。)

答案 2 :(得分:20)

您应该将内容的所有代码从服务项目中分离到一个单独的项目中,然后创建一个可以正常运行和调试的测试应用程序。

服务项目只是实现服务部分所需的shell。

答案 3 :(得分:14)

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

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

... continue with code

通过这种方式,您可以启动服务,在Visual Studio中选择“附加到流程...”并附加到您的服务,然后恢复正常的服务。

答案 4 :(得分:6)

鉴于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默认为前台线程。在虚假服务线程运行时return Main将不会终止该过程。

答案 5 :(得分:4)

一篇Microsoft文章解释了如何调试Windows服务here以及如果他们通过附加到进程来调试它,那么任何人都可以错过哪些部分。

以下是我的工作代码。我遵循了微软建议的方法。

将此代码添加到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应用程序"完成调试后,重新编译并安装您的服务。

Enter image description here

答案 6 :(得分: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类与我的Windows服务应用程序完全相同,但继承者(ServiceBase)除外。

答案 7 :(得分:2)

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

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

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

答案 8 :(得分:2)

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

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

答案 9 :(得分:2)

我在Visual Studio项目中使用/Console参数调试启动选项命令行参数

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() {....}
}

答案 10 :(得分:1)

不幸的是,如果您在Windows服务操作的最初阶段尝试调试某些内容,则“附加”到正在运行的进程将无法正常工作。我尝试在OnStart程序中使用Debugger.Break(),但是使用64位Visual Studio 2010编译的应用程序,break命令只会抛出这样的错误:

System error 1067 has occurred.

此时,您需要在注册表中为可执行文件设置“图像文件执行”选项。设置需要五分钟,而且效果非常好。这是一篇微软文章,详细信息如下:

How to: Launch the Debugger Automatically

答案 11 :(得分:1)

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

尝试在post-build中添加:

@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.

有关错误代码here的更多信息。

如果构建错误的消息是这样的,

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 应足以调试它。

答案 12 :(得分: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

答案 13 :(得分:0)

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

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

我找到了这些出色的指南:

首先将项目Output type更改为Console Application

Enter image description here

将您的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);
}

答案 14 :(得分:0)

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

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

在新项目中,创建一个类&#34; ConsoleHost&#34;用那段代码:

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 - &gt;开始新实例。

答案 15 :(得分:0)

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

我引用...

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

所有这些只需一行代码。

http://windowsservicehelper.codeplex.com/

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

答案 16 :(得分:0)

只需将构造器添加到您的服务类中(如果您还没有的话)。在下面,您可以查看Visual Basic .net的示例。

Public Sub New()
   OnStart(Nothing) 
End Sub

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