Многие объяснения уже присутствуют, чтобы объяснить, как это происходит и как это исправить, но вы также должны следовать рекомендациям, чтобы избежать NullPointerException
вообще.
См. также: A хороший список лучших практик
Я бы добавил, очень важно, хорошо использовать модификатор final
. Использование "окончательной" модификатор, когда это применимо в Java
Сводка:
final
для обеспечения хорошей инициализации. @NotNull
и @Nullable
if("knownObject".equals(unknownObject)
valueOf()
поверх toString (). StringUtils
StringUtils.isEmpty(null)
. Как Пепел, я пишу весь фактический код обработки в отдельном блоке библиотеки классов, на который тогда сослался сервисный исполняемый файл окон, а также консольное приложение.
Однако существуют случаи, когда полезно знать, работает ли библиотека классов в контексте сервисного исполняемого файла или консольного приложения. Путем я делаю это должно размышлять над базовым классом приложения хостинга. (Извините за VB, но я предполагаю, что следующее могло быть c#-ified довольно легко):
Public Class ExecutionContext
''' <summary>
''' Gets a value indicating whether the application is a windows service.
''' </summary>
''' <value>
''' <c>true</c> if this instance is service; otherwise, <c>false</c>.
''' </value>
Public Shared ReadOnly Property IsService() As Boolean
Get
' Determining whether or not the host application is a service is
' an expensive operation (it uses reflection), so we cache the
' result of the first call to this method so that we don't have to
' recalculate it every call.
' If we have not already determined whether or not the application
' is running as a service...
If IsNothing(_isService) Then
' Get details of the host assembly.
Dim entryAssembly As Reflection.Assembly = Reflection.Assembly.GetEntryAssembly
' Get the method that was called to enter the host assembly.
Dim entryPoint As System.Reflection.MethodInfo = entryAssembly.EntryPoint
' If the base type of the host assembly inherits from the
' "ServiceBase" class, it must be a windows service. We store
' the result ready for the next caller of this method.
_isService = (entryPoint.ReflectedType.BaseType.FullName = "System.ServiceProcess.ServiceBase")
End If
' Return the cached result.
Return CBool(_isService)
End Get
End Property
Private Shared _isService As Nullable(Of Boolean) = Nothing
#End Region
End Class
Я обычно отмечаю службой Windows как консольное приложение, которое берет параметр командной строки "-консоль" для выполнения как консоль, иначе это работает как услуга. Для отладки Вы просто устанавливаете параметры командной строки в опциях проекта к "-консоль", и Вы прочь!
Это делает отладку хорошей и легкой и означает, что приложение функционирует как услуга по умолчанию, который является тем, что Вы захотите.
Какие работы для меня:
Environment.UserInteractive
Пример кода:
class MyService : ServiceBase
{
private static void Main()
{
if (Environment.UserInteractive)
{
startWorkerThread();
Console.WriteLine ("====== Press ENTER to stop threads ======");
Console.ReadLine();
stopWorkerThread() ;
Console.WriteLine ("====== Press ENTER to quit ======");
Console.ReadLine();
}
else
{
Run (this) ;
}
}
protected override void OnStart(string[] args)
{
startWorkerThread();
}
protected override void OnStop()
{
stopWorkerThread() ;
}
}
Jonathan, не точно ответ на Ваш вопрос, но я только что закончил писать сервис окон и также отметил трудность с отладкой и тестированием.
Решенный это путем простой записи всего фактического кода обработки в отдельном блоке библиотеки классов, на который тогда сослались сервисный исполняемый файл окон, а также консольное приложение и тестовая обвязка.
Кроме базовой логики таймера, вся более сложная обработка произошла в общем блоке и могла тестироваться/выполняться по требованию невероятно легко.
Возможно, проверяя, является ли родитель процесса C:\Windows\system32\services.exe.
Единственным путем я нашел для достижения этого, должен проверить, присоединена ли консоль к процессу во-первых путем доступа к какому-либо Консольному свойству объекта (например, Заголовок) в блоке попытки/выгоды.
, Если сервис запускается SCM, нет никакой консоли, и доступ к свойству бросит Систему. IO.IOError.
Однако, так как это чувствует немного совсем как доверие определенной для реализации детали (что, если SCM на некоторых платформах или когда-нибудь решает предоставить консоль процессам, это запускает?), я всегда использую переключатель командной строки (-консоль) в производственных приложениях...
Это - что-то вроде саморазъема, но у меня есть немного приложения, которое загрузит Ваши сервисные типы в Вашем приложении через отражение и выполнит их тот путь. Я включаю исходный код, таким образом, Вы могли изменить его немного для отображения стандартного вывода.
Никакие изменения кода не должны были использовать это решение. У меня есть Отладчик. Тип IsAttached решения также, которое достаточно универсально, чтобы использоваться с любым сервисом. Ссылка находится в этой статье: Бегун службы Windows.NET
Я модифицировал ProjectInstaller, чтобы добавить параметр / сервис аргумента командной строки, когда он устанавливается в качестве сервиса:
static class Program
{
static void Main(string[] args)
{
if (Array.Exists(args, delegate(string arg) { return arg == "/install"; }))
{
System.Configuration.Install.TransactedInstaller ti = null;
ti = new System.Configuration.Install.TransactedInstaller();
ti.Installers.Add(new ProjectInstaller());
ti.Context = new System.Configuration.Install.InstallContext("", null);
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
ti.Context.Parameters["assemblypath"] = path;
ti.Install(new System.Collections.Hashtable());
return;
}
if (Array.Exists(args, delegate(string arg) { return arg == "/uninstall"; }))
{
System.Configuration.Install.TransactedInstaller ti = null;
ti = new System.Configuration.Install.TransactedInstaller();
ti.Installers.Add(new ProjectInstaller());
ti.Context = new System.Configuration.Install.InstallContext("", null);
string path = System.Reflection.Assembly.GetExecutingAssembly().Location;
ti.Context.Parameters["assemblypath"] = path;
ti.Uninstall(null);
return;
}
if (Array.Exists(args, delegate(string arg) { return arg == "/service"; }))
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[] { new MyService() };
ServiceBase.Run(ServicesToRun);
}
else
{
Console.ReadKey();
}
}
}
ProjectInstaller.cs затем изменен для переопределения Onbeforeinstall () и onbeforeuninstall ()
[RunInstaller(true)]
public partial class ProjectInstaller : Installer
{
public ProjectInstaller()
{
InitializeComponent();
}
protected virtual string AppendPathParameter(string path, string parameter)
{
if (path.Length > 0 && path[0] != '"')
{
path = "\"" + path + "\"";
}
path += " " + parameter;
return path;
}
protected override void OnBeforeInstall(System.Collections.IDictionary savedState)
{
Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
base.OnBeforeInstall(savedState);
}
protected override void OnBeforeUninstall(System.Collections.IDictionary savedState)
{
Context.Parameters["assemblypath"] = AppendPathParameter(Context.Parameters["assemblypath"], "/service");
base.OnBeforeUninstall(savedState);
}
}
Другой способ обхода проблемы .. поэтому его можно запускать как WinForm или как службу Windows
var backend = new Backend();
if (Environment.UserInteractive)
{
backend.OnStart();
Application.EnableVisualStyles();
Application.SetCompatibleTextRenderingDefault(false);
Application.Run(new Fronend(backend));
backend.OnStop();
}
else
{
var ServicesToRun = new ServiceBase[] {backend};
ServiceBase.Run(ServicesToRun);
}