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を独自のインストーラーコレクション内に配置します。

このコードを試してください:

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

また、ヘルブの提案に従いましたが、基本的に標準のデザイナー生成クラス (デフォルト オブジェクト "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)

これは、インストーラーがイベント ソースをアプリケーション ログに作成せず、新しいカスタム ログ ファイルに作成したという点で、期待どおりに機能しました。

しかし、私は 1 台のサーバーでちょっとした混乱が生じるほど十分にうまくいっていませんでした。カスタム ログの問題は、イベント ソース名が間違ったログ ファイル (たとえば、新しいカスタム ログではなく「アプリケーション」ログ) に関連付けられている場合、まずソース名を削除する必要があることです。その後、マシンが再起動しました。その後、正しいログに関連付けてソースを作成できます。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 に答える
2

イベントログが入る「奇妙な状態」についてstephbuに同意する必要があります。以前に遭遇しました。推測すると、あなたの困難のいくつかはそこにあります。

ただし、私が知っているアプリケーションでイベント ログを記録する最善の方法は、実際には TraceListener を使用することです。これらは、サービスの app.config を介して構成できます。

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

そのページの中央付近に、EventLog プロパティを使用して書き込み先の EventLog を指定する方法を説明するセクションがあります。

それが役立つことを願っています。

于 2008-10-01T14:56:17.117 に答える
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 に答える
1

私は同じ問題を抱えています。私の場合、Windows インストーラーが私のサービスと同じ名前のイベント ソースを自動的に追加しているようで、これが問題を引き起こしているようです。Windows サービスとログ ソースに同じ名前を使用していますか? イベント ログ ソースがサービスの名前とは異なる名前になるように変更してみてください。

于 2009-05-06T09:39:10.490 に答える
0

ヘルブの提案に従うと、私にとっては問題が解決しました。彼の例に示されている時点でデフォルトのイベントログインストーラーを強制終了すると、インストーラーはアプリケーションイベントログにWindowsサービスを自動的に登録できなくなりました。

この苛立たしい癖を解決しようとして、あまりにも多くの時間が失われました。どうもありがとう!

FWIW、デザイナーが生成したProjectInstallerクラス内のコードを変更するには、VSにmodについての鯉を振るわせないでください。デザイナーが生成したコードを破棄し、手動でクラスに参加しました。

于 2009-09-10T20:55:04.843 に答える
0

この問題は、デフォルトで「Application」EventLog にサービス名でイベント ソースを登録する installutil に起因します。私はまだこのがらくたをやめさせる方法を探しています。installutil の動作に影響を与えることができれば、本当に素晴らしいことです :(

于 2009-08-11T05:45:02.563 に答える
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 に答える
0

開始したサービスと同じ名前のイベント ソースを登録しようとしたため、同様の奇妙な動作が発生しました。

DisplayName もイベント ソースと同じ名前に設定されていることに気付きました。

サービスを開始すると、Windows がソースを DisplayName としてアプリケーション ログに「サービスが正常に開始されました」というエントリを記録したことがわかりました。これにより、 Application Nameをアプリケーション ログに登録する効果があったようです。

私のイベント ロガー クラスでは、後でApplication Nameを別のイベント ログのソースとして登録しようとしましたが、新しいイベント ログ エントリを追加すると、常にアプリケーション ログに追加されてしまいました。

また、「ソース内のイベント ID ( 0 ) の説明」というメッセージが何度か表示されました。

回避策として、メッセージ ソースを DisplayName とは少し異なる名前で登録しただけで、それ以来機能しています。まだ試していない場合は、これを試す価値があります。

于 2008-10-08T16:49:14.467 に答える