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

问题描述 投票:73回答:16

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

我用的代码就像

System.Diagnostics.Debugger.Break();

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

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

c# visual-studio visual-studio-2010 windows-services
16个回答
109
投票

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

System.Diagnostics.Debugger.Launch();

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

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

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

2
投票

我在Visual Studio项目中使用/Console参数Debug→Start Options→命令行参数:

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

1
投票

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

System error 1067 has occurred.

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

How to: Launch the Debugger Automatically


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


1
投票

我使用了一个名为ServiceProcess.Helpers的优秀Nuget包。

我引用......

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

所有这一切都有一行代码。

http://windowsservicehelper.codeplex.com/

安装完成后,您需要做的就是将Windows服务项目设置为启动项目,然后单击调试器上的start。


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
投票

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

我不想将我的调试器附加到进程,但我仍然希望能够调用服务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);
}

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。


58
投票

你也可以试试这个。

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

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

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


20
投票

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

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


14
投票

要么就像Lasse V. Karlsen所建议的那样,要么在服务中设置一个等待调试器附加的循环。最简单的是

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

... continue with code

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


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是前台线程。来自returnMaining,而虚假服务线程正在运行时不会终止该过程。


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

现在转到Project Properties,选择选项卡“Application”并在调试时选择Output Type作为“Console Application”,或者在调试完成后选择“Windows Application”,重新编译并安装服务。

Enter image description here


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)。


2
投票

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

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

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


2
投票

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

    public void MyOnStart(string[] args)
    {
        OnStart(args);
    }
© www.soinside.com 2019 - 2024. All rights reserved.