我正在创建另一个Windows服务,我的计时器没有滴答作响,我不明白为什么! 我正在使用system.timers.timer,就像我以前的服务一样,它不起作用。 我试过附加它但它似乎没有做任何事情。
我的代码:
namespace ExpiryNotifier
{
public partial class ExpiryNotifier : ServiceBase
{
public ExpiryNotifier()
{
InitializeComponent();
if (!System.Diagnostics.EventLog.SourceExists("ExpiryNotifier"))
{
System.Diagnostics.EventLog.CreateEventSource("ExpiryNotifier", "ExpiryNotifier");
}
eventLog1.Source = "ExpiryNotifier";
eventLog1.Log = "ExpiryNotifier";
}
private Timer timer1 = new Timer();
protected override void OnStart(string[] args)
{
eventLog1.WriteEntry("Service Started");
timer1.Elapsed += timer1_Elapsed;
timer1.Interval = 10000;
timer1.Enabled = true;
}
protected override void OnStop()
{
eventLog1.WriteEntry("Service Stopped");
timer1.Enabled = false;
}
private void timer1_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
{
eventLog1.WriteEntry("timer tick");
timer1.Stop();
EmailerService.EmailerService service = new EmailerService.EmailerService();
DataSet expiringQualifications = service.GetDetailsOfExpiringQualifications();
if(expiringQualifications.Tables[0].Rows.Count>0)
{
foreach(DataRow rw in expiringQualifications.Tables[0].Rows)
{
if (!string.IsNullOrEmpty(rw["EmailAddress"].ToString()) )
{
if (rw["QualAwardDescription"] != null)
{
service.SendQualExpiryEmail(rw["EmailAddress"].ToString(), rw["firstName"].ToString(),
rw["QualAwardDescription"].ToString());
}
}
}
}
timer1.Start();
}
}
}
有人能看到问题吗?
提前致谢!
贝克斯
答案 0 :(得分:7)
System.Timers.Timer是一个丑陋的计时器。它做的一件令人讨厌的事情是吞噬Elapsed事件处理程序引发的异常。因为你在输入方法时停止它会杀死你的计时器。没有任何通知,它只是停止工作。
您必须至少为此代码添加异常处理,以便您可以记录异常并停止服务。
还要注意OnStart()方法中的错误,每次启动服务时都会不断添加事件处理程序。 Elapsed事件多次运行,本身就是轰炸某事的好方法。
考虑System.Threading.Timer,它没有任何这些问题。
答案 1 :(得分:0)
System.Timers.Timer.Start()与System.Timers.Timer.Enabled = true具有相同的功能; System.Timers.Timer.Stop()与System.Timers.Timer.Enabled = false具有相同的功能;
这两个方法在内部将Enabled属性设置为true或false,从而启动或停止计时器。
检查您是否有权写入事件日志。您还可以在事件日志中检查错误。
System.Timers.Timer不是线程安全的。确保你正确使用它。
答案 2 :(得分:0)
我认为timer1_Elapsed
需要使用事件委托,所以
timer1_Elapsed += new ElapsedEventHandler(timer1_Elapsed);
http://msdn.microsoft.com/es-es/library/system.timers.timer.aspx
虽然我会在服务内部推荐System.Threading.Timer,因为它会在elapsed事件中正确抛出异常。 System.Timers.Timer只是吞下它们。
http://msdn.microsoft.com/en-us/library/system.threading.timer.aspx
答案 3 :(得分:0)
它看起来很有效但你可能是一个更好的调试方法。谁知道你的eventLog1
可能是空的
更新您的onstart以包含此
protected override void OnStart(string[] args)
{
foreach (string arg in args)
{
if (arg == "DEBUG_SERVICE")
DebugMode();
}
#if DEBUG
DebugMode();
#endif
eventLog1.WriteEntry("Service Started");
timer1.Elapsed += timer1_Elapsed;
timer1.Interval = 10000;
timer1.Enabled = true;
}
private static void DebugMode()
{
Debugger.Break();
}
现在当你点击服务开始时,你会得到一个对话框,询问你是否想要附加。它更容易尝试手动附加。
答案 4 :(得分:0)
请参阅下面的文件 Service.cs 和 Program.cs 和事件日志...
我注意到当项目的属性 Output Type 设置为 Windows Application
时,System.Timers.Timer 在 Windows 服务中无法正常工作。但是,当项目的属性 Output Type 设置为 Console Application
时,它确实有效。
最快和最简单的解决方案是将项目的属性输出类型设置为Console Application
。当项目的属性 Output Type 设置为 Windows Application
时,将忽略控制台语句。无需更改代码...只需更改项目设置。
在随后的回复中,我将提交一个替代解决方案,其中 System.Timers.Timer 将在 Windows 服务中按预期运行。它涉及添加“未记录”的代码。
为了了解发生了什么,我添加了三个断点(断点 1、断点 2 和断点 3)。断点 1 和 2 在下面的 Service.cs 中,断点 3 在下面的 Program.cs 中(请参阅代码中的注释:“// Breakpoint...”)。我在项目属性输出类型设置为 Windows Application
的情况下运行了 Windows 服务并记录了所有结果(请参阅下面的事件日志)。
我们看到的是以下内容(请参阅下面 Service.cs 中的 internal void TestStartupAndStop(string[] args) 方法):OnStart
事件触发OnStart 方法,其中定时器初始化成功(即没有抛出异常)。 OnStop
事件触发 OnStop 方法,在该方法中对象被成功地正确处理(即,没有抛出异常)。但是,随后发生了完全意外的事情...... OnTimer
事件触发了 OnTimer 方法,其中任务成功执行了两次(即,没有抛出异常)服务停止后!
?(脑洞大开!)
文件:Service.cs
using System.Timers;
namespace <namespace>
{
public partial class Service : ServiceBase
{
protected void OnTimer(object sender, ElapsedEventArgs e)
{
var methodName = "protected void OnTimer(object sender, ElapsedEventArgs e)";
try
{
EventLogWriteEntry("Running Service...");
EventLogWriteEntry("Do Task...");
// :
// :
// :
EventLogWriteEntry("Task Done...");
EventLogWriteEntry("Service ran successfully.");
}
catch (Exception ex)
{
var errorMessage = "Error running Service...";
HandleGeneralException(errorMessage, ex, methodName);
}
}
protected override void OnStart(string[] args)
{
var methodName = "protected override void OnStart(string[] args)";
try
{
EventLogWriteEntry("Starting Service...");
System.Timers.Timer timer = new System.Timers.Timer();
timer.Interval = double.Parse(ConfigurationManager.AppSettings["Timer.Interval"]); // Set Interval to 5 seconds
timer.Elapsed += new ElapsedEventHandler(OnTimer);
//timer.Enabled = true; // timer.Start() does the same exact thing
timer.Start();
EventLogWriteEntry("Service started successfully.");
}
catch (Exception ex)
{
var errorMessage = "Error starting Service...";
HandleGeneralException(errorMessage, ex, methodName);
}
}
protected override void OnStop()
{
var methodName = "protected override void OnStop()";
try
{
EventLogWriteEntry("Stopping Service...");
EventLogWriteEntry("Do Clean-Up...");
// :
// :
// :
EventLogWriteEntry("Clean-Up Done...");
EventLogWriteEntry("Service stopped successfully.");
}
catch (Exception ex)
{
var errorMessage = "Error stopping Service...";
HandleGeneralException(errorMessage, ex, methodName);
}
}
// Note: Set project's property Output Type to Console Application. Revert to Windows Application when done.
// See comment in static void Main() in Program.cs
internal void TestStartupAndStop(string[] args) // FOR TESTING PURPOSES ONLY!
{
this.OnStart(args);
Console.WriteLine("Press ENTER to stop..."); // Console statements are ignored when Output Type is set to Windows Application.
Console.ReadLine();
this.OnStop(); // Breakpoint 1: Wait 60 seconds...
} // Breakpoint 2: Wait 60 seconds...
}
}
文件:Program.cs
namespace <namespace>
{
static class Program
{
/// <summary>
/// The main entry point for the application.
/// </summary>
static void Main()
{
// Note: The If block is for testing interactively.
// The Else block is the normal execution block of code.
// No need to comment/uncomment code.
// See comment in internal void TestStartupAndStop(string[] args) in Service.cs
if (Environment.UserInteractive) // FOR TESTING PURPOSES ONLY!
{
var service = new Service();
service.TestStartupAndStop(null);
}
else
{
ServiceBase[] ServicesToRun;
ServicesToRun = new ServiceBase[]
{
new Service()
};
ServiceBase.Run(ServicesToRun);
}
} // Breakpoint 3: Wait 60 seconds... Timer events fired AFTER here!
}
}
事件日志
级别 | 日期和时间 | 来源 | 事件 ID | 任务类别 | 留言 |
---|---|---|---|---|---|
断点 1:等待 60 秒... | |||||
信息 | 5/31/2021 7:24 | 服务事件源 | 0 | 无 | 开始服务... |
信息 | 5/31/2021 7:24 | 服务事件源 | 0 | 无 | 初始化定时器... |
信息 | 5/31/2021 7:24 | 服务事件源 | 0 | 无 | 服务启动成功。 |
断点 2:等待 60 秒... | |||||
信息 | 5/31/2021 7:25 | 服务事件源 | 0 | 无 | 停止服务... |
信息 | 5/31/2021 7:25 | 服务事件源 | 0 | 无 | 清理... |
信息 | 5/31/2021 7:25 | 服务事件源 | 0 | 无 | 清理完成... |
信息 | 5/31/2021 7:25 | 服务事件源 | 0 | 无 | 服务已成功停止。 |
断点 3:等待 60 秒...定时器事件在此处触发! | |||||
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 正在运行服务... |
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 做任务... |
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 任务完成... |
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 服务运行成功。 |
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 正在运行服务... |
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 做任务... |
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 任务完成... |
信息 | 5/31/2021 7:27 | 服务事件源 | 0 | 无 | 服务运行成功。 |