25

在安装 .Net Windows 服务期间,我难以可靠地创建/删除事件源。

这是我的 ProjectInstaller 类的代码:

// Create Process Installer
ServiceProcessInstaller spi = new ServiceProcessInstaller();
spi.Account = ServiceAccount.LocalSystem;

// Create Service
ServiceInstaller si = new ServiceInstaller();
si.ServiceName = Facade.GetServiceName();
si.Description = "Processes ...";
si.DisplayName = "Auto Checkout";
si.StartType = ServiceStartMode.Automatic;

// Remove Event Source if already there
if (EventLog.SourceExists("AutoCheckout"))
    EventLog.DeleteEventSource("AutoCheckout");

// Create Event Source and Event Log     
EventLogInstaller log = new EventLogInstaller();
log.Source = "AutoCheckout";
log.Log = "AutoCheckoutLog";

Installers.AddRange(new Installer[] { spi, si, log });

引用的外观方法只返回日志、服务等名称的字符串。

此代码大部分时间都有效,但最近在安装后,我开始让我的日志条目显示在应用程序日志中,而不是自定义日志中。日志中还有以下错误:

找不到源 (AutoCheckout) 中事件 ID (0) 的描述。本地计算机可能没有必要的注册表信息或消息 DLL 文件来显示来自远程计算机的消息。您可以使用 /AUXSOURCE= 标志来检索此描述;有关详细信息,请参阅帮助和支持。

由于某种原因,它要么在卸载期间没有正确删除源,要么在安装期间没有创建它。

感谢您对此处最佳实践的任何帮助。

谢谢!

此外,这是我如何将异常写入日志的示例:

// Write to Log
EventLog.WriteEntry(Facade.GetEventLogSource(), errorDetails, EventLogEntryType.Error, 99);

关于 stephbu 的回答:推荐的路径是安装程序脚本和 installutil,或 Windows 安装程序。

我正在使用一个安装项目,它执行服务的安装并设置日志。无论我使用 installutil.exe 还是 windows 安装项目,我相信它们都调用了我上面显示的相同的 ProjectInstaller 类。

如果在重新启动之前没有真正删除日志,我会看到我的测试机器的状态如何导致错误。我将进行更多实验,看看是否能解决问题。

编辑: 我对在安装服务期间注册源和日志名称的可靠方式感兴趣。因此,如果该服务之前已安装,它将删除源,或在后续安装期间重用源。

我还没有机会学习 WiX 来尝试这条路线。

4

12 回答 12

27

该类ServiceInstaller会自动创建一个EventLogInstaller并将其放入自己的 Installers 集合中。

试试这个代码:

ServiceProcessInstaller serviceProcessInstaller = new ServiceProcessInstaller();
serviceProcessInstaller.Password = null;
serviceProcessInstaller.Username = null;
serviceProcessInstaller.Account = ServiceAccount.LocalSystem;

// serviceInstaller
ServiceInstaller serviceInstaller = new ServiceInstaller();
serviceInstaller.ServiceName = "MyService";
serviceInstaller.DisplayName = "My Service";
serviceInstaller.StartType = ServiceStartMode.Automatic;
serviceInstaller.Description = "My Service Description";
// kill the default event log installer
serviceInstaller.Installers.Clear(); 

// Create Event Source and Event Log     
EventLogInstaller logInstaller = new EventLogInstaller();
logInstaller.Source = "MyService"; // use same as ServiceName
logInstaller.Log = "MyLog";

// Add all installers
this.Installers.AddRange(new Installer[] {
   serviceProcessInstaller, serviceInstaller, logInstaller
});
于 2009-08-17T10:59:49.320 回答
8

这里有几件事

动态创建事件日志和源是非常不受欢迎的。主要是因为执行该操作所需的权限 - 您真的不想用这种权力来祝福您的应用程序。

此外,如果您删除事件日志或源,则只有在服务器重新启动时才会真正删除该条目,因此如果您删除并重新创建条目而不弹跳框,您可能会进入奇怪的状态。由于元数据在注册表中的存储方式,还有一堆关于命名冲突的不成文规则。

推荐的路径是安装程序脚本和 installutil,或 Windows 安装例程。

于 2008-09-22T17:22:57.163 回答
5

最好的建议是不要在 Visual Studio 中使用安装项目。它有非常严重的局限性。我用WiX取得了非常好的结果

于 2008-10-01T10:44:46.207 回答
2

我必须同意 stephbu 关于事件日志进入的“奇怪状态”,我之前遇到过。如果我猜的话,你的一些困难就在那里。

但是,据我所知,在应用程序中进行事件记录的最佳方法实际上是使用 TraceListener。您可以通过服务的 app.config 配置它们:

http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlogtracelistener.aspx

该页面中间附近有一个部分描述了如何使用 EventLog 属性来指定要写入的 EventLog。

希望有帮助。

于 2008-10-01T14:56:17.117 回答
2

我也遵循了 helb 的建议,除了我基本上使用了标准设计器生成的类(默认对象“ServiceProcessInstaller1”和“ServiceInstaller1”)。我决定发布这个,因为它是一个稍微简单的版本;也因为我在 VB 中工作,人们有时喜欢看 VB 方式。

正如tartheode所说,您不应该修改ProjectInstaller.Designer.vb文件中设计器生成的ProjectInstaller类但您可以修改 ProjectInstaller.vb文件中的代码。在创建一个普通的 ProjectInstaller(使用标准的“添加安装程序”机制)之后,我所做的唯一更改是在 ProjectInstaller 类的 New() 中。在正常的“InitializeComponent()”调用之后,我插入了这段代码:

  ' remove the default event log installer 
  Me.ServiceInstaller1.Installers.Clear()

  ' Create an EventLogInstaller, and set the Event Source and Event Log      
  Dim logInstaller As New EventLogInstaller
  logInstaller.Source = "MyServiceName"
  logInstaller.Log = "MyCustomEventLogName"

  ' Add the event log installer
  Me.ServiceInstaller1.Installers.Add(logInstaller)

这按预期工作,因为安装程序没有应用程序日志中创建事件源,而是在新的自定义日志文件中创建。

但是,我已经搞砸了,以至于我在一台服务器上有点乱。自定义日志的问题在于,如果存在与错误日志文件关联的事件源名称(例如“应用程序”日志而不是您的新自定义日志),则必须首先删除源名称;然后机器重新启动;然后可以创建与正确日志关联的源。Microsoft 帮助明确指出(在EventLogInstaller 类描述中):

如果 Source 属性与为计算机上的不同事件日志注册的源名称匹配,Install 方法将引发异常。

因此,我的服务中也有这个功能,在服务启动时调用:

   Private Function EventLogSourceNameExists() As Boolean
      'ensures that the EventSource name exists, and that it is associated to the correct Log 

      Dim EventLog_SourceName As String = Utility.RetrieveAppSetting("EventLog_SourceName")
      Dim EventLog_LogName As String = Utility.RetrieveAppSetting("EventLog_LogName")

      Dim SourceExists As Boolean = EventLog.SourceExists(EventLog_SourceName)
      If Not SourceExists Then
         ' Create the source, if it does not already exist.
         ' An event log source should not be created and immediately used.
         ' There is a latency time to enable the source, it should be created
         ' prior to executing the application that uses the source.
         'So pass back a False to cause the service to terminate.  User will have 
         'to re-start the application to make it work.  This ought to happen only once on the 
         'machine on which the service is newly installed

         EventLog.CreateEventSource(EventLog_SourceName, EventLog_LogName)  'create as a source for the SMRT event log
      Else
         'make sure the source is associated with the log file that we want
         Dim el As New EventLog
         el.Source = EventLog_SourceName
         If el.Log <> EventLog_LogName Then
            el.WriteEntry(String.Format("About to delete this source '{0}' from this log '{1}'.  You may have to kill the service using Task Manageer.  Then please reboot the computer; then restart the service two times more to ensure that this event source is created in the log {2}.", _
            EventLog_SourceName, el.Log, EventLog_LogName))

            EventLog.DeleteEventSource(EventLog_SourceName)
            SourceExists = False  'force a close of service
         End If
      End If
      Return SourceExists
   End Function

如果函数返回 False,则服务启动代码只是停止服务。此功能几乎可以确保您最终获得与正确事件日志文件关联的正确事件源名称。您可能需要重新启动机器一次;您可能需要多次尝试启动该服务。

于 2010-09-09T08:03:43.427 回答
1

我有同样的问题。在我的情况下,Windows 安装程序似乎正在自动添加与我的服务同名的事件源,这似乎会导致问题。您是否对 Windows 服务和日志源使用相同的名称?尝试更改它,以便您的事件日志源的调用方式与服务名称不同。

于 2009-05-06T09:39:10.490 回答
1

我刚刚在 MSDN 论坛上发布了一个解决方案,我设法使用标准设置 MSI 项目解决了这个问题。我所做的是将代码添加到 PreInstall 和 Committed 事件中,这意味着我可以将其他所有内容保持原样:

SortedList<string, string> eventSources = new SortedList<string, string>();
private void serviceProcessInstaller_BeforeInstall(object sender, InstallEventArgs e)
{
  RemoveServiceEventLogs();
}

private void RemoveServiceEventLogs()
{
  foreach (Installer installer in this.Installers)
    if (installer is ServiceInstaller)
    {
      ServiceInstaller serviceInstaller = installer as ServiceInstaller;
      if (EventLog.SourceExists(serviceInstaller.ServiceName))
      {
        eventSources.Add(serviceInstaller.ServiceName, EventLog.LogNameFromSourceName(serviceInstaller.ServiceName, Environment.MachineName));
        EventLog.DeleteEventSource(serviceInstaller.ServiceName);
      }
    }
}

private void serviceProcessInstaller_Committed(object sender, InstallEventArgs e)
{
  RemoveServiceEventLogs();
  foreach (KeyValuePair<string, string> eventSource in eventSources)
  {
    if (EventLog.SourceExists(eventSource.Key))
      EventLog.DeleteEventSource(eventSource.Key);

    EventLog.CreateEventSource(eventSource.Key, eventSource.Value);
  }
}

可以进一步修改代码以仅删除尚不存在的事件源或创建它们(尽管需要将日志名存储在安装程序的某个位置),但是由于我的应用程序代码实际上在运行时创建了事件源那么对我来说就没有意义了。如果已经有事件,那么应该已经有一个事件源。为确保它们已创建,您只需自动启动服务即可。

于 2011-04-09T23:02:13.970 回答
0

我遇到了一些类似的奇怪行为,因为我试图注册一个与我正在启动的服务同名的事件源。

我注意到您还将 DisplayName 设置为与事件源相同的名称。

在启动服务时,我们发现 Windows 在应用程序日志中记录了“服务已成功启动”条目,源为 DisplayName。这似乎具有在应用程序日志中注册应用程序名称的效果。

在我的事件记录器类中,我后来尝试将应用程序名称注册为具有不同事件日志的源,但是在添加新的事件日志条目时,它们总是被添加到应用程序日志中。

我还多次收到“源中事件 ID (0) 的描述”消息。

作为一种解决方法,我只是将消息源注册为与 DisplayName 稍有不同的名称,并且从那以后它就一直有效。如果您还没有,那么值得尝试一下。

于 2008-10-08T16:49:14.467 回答
0

问题来自 installutil,它默认在“应用程序”事件日志中使用您的服务名称注册事件源。我仍在寻找一种方法来阻止它做这种废话。如果能影响 installutil 的行为,那就太好了 :(

于 2009-08-11T05:45:02.563 回答
0

遵循helb 的建议为我解决了这个问题。在他的示例中指出的点处,终止默认事件日志安装程序会阻止安装程序在应用程序事件日志下自动注册我的 Windows 服务。

试图解决这个令人沮丧的怪癖浪费了太多时间。太感谢了!

FWIW,我无法修改我的设计器生成的 ProjectInstaller 类中的代码,而不会导致 VS 对 mods 抱怨。我废弃了设计器生成的代码并手动输入了课程。

于 2009-09-10T20:55:04.843 回答
0

向 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\services\eventlog\Application\MY_CUSTOM_SOURCE_NAME_HERE 添加一个空的注册表项似乎可以正常工作。

于 2013-02-26T21:59:08.733 回答
0

更改默认行为(即项目安装程序在应用程序日志中使用您的服务名称创建事件日志源)的一种简单方法是轻松修改项目安装程序的构造函数,如下所示:

[RunInstaller( true )]
public partial class ProjectInstaller : System.Configuration.Install.Installer
{
    public ProjectInstaller()
    {
        InitializeComponent();

        //Skip through all ServiceInstallers.
        foreach( ServiceInstaller ThisInstaller in Installers.OfType<ServiceInstaller>() )
        {
            //Find the first default EventLogInstaller.
            EventLogInstaller ThisLogInstaller = ThisInstaller.Installers.OfType<EventLogInstaller>().FirstOrDefault();
            if( ThisLogInstaller == null )
                continue;

            //Modify the used log from "Application" to the same name as the source name. This creates a source in the "Applications and Services log" which separates your service logs from the default application log.
            ThisLogInstaller.Log = ThisLogInstaller.Source;
        }
    }
}
于 2015-01-23T13:49:33.327 回答