是否可以在Visual Studio中调试Windows服务?
我使用了类似的代码
System.Diagnostics.Debugger.Break();
但是它给出了一些代码错误,例如:
我收到两个事件错误:eventID 4096 VsJITDebugger和“服务没有及时响应启动或控制请求。”
Answers:
在服务OnStart
方法中使用以下代码:
System.Diagnostics.Debugger.Launch();
从弹出消息中选择Visual Studio选项。
注意:要仅在调试模式下使用它,#if DEBUG
可以使用编译器指令,如下所示。这将防止在生产服务器上以发布模式进行意外操作或调试。
#if DEBUG
System.Diagnostics.Debugger.Launch();
#endif
您也可以尝试一下。
(经过大量的搜索,我在“如何在Visual Studio中调试Windows服务”中找到了这一点。)
您应该将所有将执行服务工作的代码从一个服务项目中分离出来,然后创建一个可以正常运行和调试的测试应用程序。
服务项目只是实现其服务部分所需的外壳。
如Lasse V. Karlsen所建议的那样,或者在您的服务中设置一个循环,以等待调试器附加。最简单的是
while (!Debugger.IsAttached)
{
Thread.Sleep(1000);
}
... continue with code
这样,您可以启动服务,然后在Visual Studio中选择“附加到进程...”并附加到您的服务,然后该服务将恢复正常退出。
if (Environment.UserInteractive) { InteractiveRun(args); } else { Service instance = new Service(); ServiceBase[] servicesToRun = new ServiceBase[] { instance }; ServiceBase.Run(servicesToRun); }
Start
/OnStart()
我猜
foo(bar)
鉴于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
伪服务线程运行时开始不会终止该过程。
微软的一篇文章在这里解释了如何调试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应用程序”的时候与调试,进行重新编译和安装服务。
您可以制作一个控制台应用程序。我使用此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服务应用程序中的类完全相同。
ServiceBase
)。”。我发现在控制台应用程序中调试更容易,但是我知道这是否不能说服所有人。
我使用了一个很棒的Nuget包,称为ServiceProcess.Helpers。
我引用...
它通过在连接调试器的情况下创建播放/停止/暂停UI来帮助Windows服务进行调试,而且还允许Windows服务器环境安装和运行该服务。
所有这些只需一行代码。
http://windowsservicehelper.codeplex.com/
安装并连线后,只需将Windows服务项目设置为启动项目,然后在调试器上单击“开始”。
您也可以尝试使用System.Diagnostics.Debugger.Launch()方法。它有助于将调试器指针带到指定位置,然后可以调试代码。
在执行此步骤之前,请使用Visual Studio命令提示符-installutil projectservice.exe命令行安装service.exe。
然后从控制面板->管理工具->计算机管理->服务和应用程序->服务->您的服务名称启动服务
我/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() {....}
}
我找到了这个问题,但我认为缺少一个清晰而简单的答案。
我不想将调试器附加到进程,但是我仍然希望能够调用服务OnStart
和OnStop
方法。我还希望它作为控制台应用程序运行,以便我可以将信息从NLog记录到控制台。
我发现这些出色的指南可以做到这一点:
首先将项目更改Output type
为Console 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);
}
不幸的是,如果您尝试在Windows Service操作的开始进行调试,则无法“附加”到正在运行的进程。我尝试在OnStart过程中使用Debugger.Break(),但对于64位Visual Studio 2010编译的应用程序,break命令只会引发如下错误:
System error 1067 has occurred.
那时,您需要在注册表中为可执行文件设置“图像文件执行”选项。设置需要五分钟,并且效果很好。这是Microsoft文章,其详细信息如下:
尝试使用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则足以对其进行调试。
在该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
首先,您必须在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。