none
EventLog.GetEventLogsで取得した Entries の Message(説明)変換で失敗する RRS feed

すべての返信

  •  

    >必要なレジストリ情報またはメッセージを表示する

    > メッセージ DLL ファイルがローカル コンピュータに

    > 存在しない可能性があります

     

    と書いてあるとおりです。

     

    もう少し詳しく説明すると、イベントログは、表示するコンピュータの言語に合わせて内容が自動的に翻訳(ローカライズ)されて

    表示できるように 、メッセージ部分は DLL の中にリソースとして含まれています。

     

    イベントログを出力するプログラムは、アプリケーションをインストール時にそのメッセージDLLを一緒にインストールし、レジストリも設定します。

     

    上記では、他のコンピュータにあるイベントログを表示しようとしているため、メッセージ内容が含まれたDLLが見つからないために正しく文字が表示されないようになっているのだと思います。

     

    それぞれ変換に失敗するエントリのローカライズDLLをレジストリ

     

    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\EventLog\Application\{アプリケーション名}

     

    から調べてみてはいかがでしょうか?

     

    未検証ですが、

    http://msdn.microsoft.com/en-us/library/bb427356(VS.85).aspx

    を参考に

    DLL を LoadLibray して FormatMessage すればメッセージが取得できるようなので、

    PInvoke を使って 文字列の生成は可能かもしれません。

     

    注:

     最初の投稿では、イベントログの キー名を間違えていたので修正しました。

     

     

    2008年10月7日 15:41
  • ちょっと検証してみました。

     

    まず C++ での検証ですが、

    参考URL に記載されているコードは DLL 名を レジストリにあるとおりに変更したら動作しました。

    CodeProject に 参考になる ソースをみつけました。

    http://www.codeproject.com/KB/system/sysevent.aspx

     

    しかし、Vista の イベントログ のレジストリには DLL 名ではなく ProviderGuid の定義がある物があり、

    ETWトレース 形式のログで・・ これに関しては FormatMessage だけでは 対応できません。

     

    次に C# で InteropService で呼び出したのですが、今一歩のところで うまくいきませんでした。

    FormatMessage に渡す Argments に イベントログの ReplacementStrings を渡す必要があるのですが、

     va_list *  形式に変換する方法がわかりませんでした。

    これされできれば DLL からイベントログ出力は可能です。

     

    Code Snippet

    using System.Runtime.InteropServices;


     

    [DllImport("kernel32.dll")]
    static extern IntPtr LoadLibrary(string dllName);

    [DllImport("kernel32.dll", CharSet = CharSet.Unicode)]
    static extern uint FormatMessage(uint dwFlags, IntPtr lpSource,
       uint dwMessageId, uint dwLanguageId, [Out] StringBuilder lpBuffer,
       uint nSize, IntPtr Arguments);

    uint MAKELANGID(uint p, uint s)
    {
        return (s << 10) | p;
    }

    const uint LANG_JAPANESE = 0x11;
    const uint SUBLANG_DEFAULT = 0x01;    // user default

    const int LANG_ENGLISH = 0x09;
    const int SUBLANG_ENGLISH_US = 0x01;

    const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800;
    const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100;
    const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200;
    const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000;
    const uint FORMAT_MESSAGE_ARGUMENT_ARRAY = 0x00002000;

    private void button3_Click(object sender, EventArgs e)
    {
        string msiDLL = @"C:\Windows\system32\msimsg.dll";
        IntPtr hMsiDLL = LoadLibrary(msiDLL);

        StringBuilder message = new StringBuilder(512);
        IntPtr lpMsgBuf = IntPtr.Zero;

        FormatMessage(
          FORMAT_MESSAGE_FROM_HMODULE,
          hMsiDLL,
          (uint)1033,
            //0,
            MAKELANGID(LANG_JAPANESE,SUBLANG_DEFAULT),
            //MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
          message,
          (uint)message.Capacity,
          IntPtr.Zero);

        Console.WriteLine(message.ToString());
    }

     

     

    今回の質問の直接の解決策ではありませんが、

     .NET Framework 3.5 では ETW にも対応した

    System.Diagnostics.Eventing.Reader 名前空間の EventLogRecord クラスがあるので

    .NET Framework 3.5 の環境を利用できるのであれば、そちらを使った方が簡単にイベントログメッセージが取得できます。

     

     

    2008年10月8日 9:23
  • イベントログを取得する対象ホストが指定されているようです。

    対象ホストと、アプリケーションの実行ホストが違うのでしょうか?

    その場合、実行ホストにそのイベントを出力するアプリケーションをインストールすればよいと思われます。

    2008年10月9日 2:56
  • 再現しました。

    「リモート ホストから取得」すると、ダメのようです。「ローカルから取得」するためには、GetEventLogs の引数なしバージョンを使用します。

    コンピュータの管理で、リモート ホストから取得した場合についてどうなるかは、試せていません。コンピュータの管理で、ローカルにインストール済みのアプリケーションからのメッセージは解決できるなら、同じように解決してほしいところです。できないのなら、「仕様」で仕方がないと思います。

    2008年10月9日 7:36
  • こんにちは~

    少し調べてみたので記述しておきますね。 再現自体は下記のようにシンプルなコードにした状態で、私も W2K3 で確認できました(VS 2005 で確認)。 Jitta さんが指摘されているように、2 行目のマシン名の指定有無によって、同じローカル環境を対象にしても現象が異なり、マシン名を指定すると NG が出力されるイベントが出てきます。

     

    Code Snippet

            Dim log As New EventLog("System")

            log.MachineName = "PCName"

     

     

            Dim entries As EventLogEntryCollection = log.Entries

            Dim entry As EventLogEntry

     

            For Each entry In entries

                If InStr(entry.Message, "の説明が見つかりません。必要な") > 0 Then

                    Debug.Print("NG, " & entry.Source & "," & entry.InstanceId)

                Else

                    'Debug.Print("OK, " & entry.Source & "," & entry.InstanceId)

                End If

            Next

            MsgBox("Finished")

     

     

    k_kazu さんご指摘のように、例えば WinHttpAutoProxySvc のレジストリ項目と他の項目を比べてみると、この項目はEventMessageFile が winhttp.dll のみで、他の項目のように %SystemRoot% など場所を示すパスが入っていない事がわかりましたので、ためしに下記のようにフルパスを指定してみました。

    C:\WINDOWS\WinSxS\x86_Microsoft.Windows.WinHTTP_6595b64144ccf1df_5.1.3790.3959_x-ww_D1A2C081\winhttp.dll

    これで実行したところ DLL からメッセージが取得できていましたので、この現象はマシン名を指定した場合には、単に DLL の読み込みに失敗してメッセージが取得できない項目があるということのようです。

     

    ちなみに、Windows Update Agent のように、2 種類以上のコンポーネントが EventMessageFile に登録されている場合も、メッセージが読み取れないですね。 これも下記のように1つだけにすれば読み取れました。

    %SystemRoot%\system32\wuaucpl.cpl.mui

     

    また、イベントビューワーは他のサーバーのログを見れますので試したところ、リモートでアクセスしても正常にメッセージ内容が表示されますので、Windows API レベルの問題ではなく .NET Framework の内部実装かなという気がしています。

     

    質問者の方が質問内容を削除したという事は、ここには戻ってこないかもしれませんが、ここから先は、.NET Framework に関する詳細な調査が必要になるので、有償サポートを使ってもらうのがよいと思います。

     

    P.S

    VS2008 では試していませんが、.NET Framework のソースコードは公開されているので、

    http://www.atmarkit.co.jp/fdotnet/insiderseye/20080222sourcecode/sourcecode.html

    を元に追いかけていけばもう少しはっきりすると思います。 が、時間がかかるので興味がある人向けですね。

    2008年10月14日 4:36
  •  

    使用条件により、してはならないとされている、「他のユーザーによるコミュニケーション サービスの使用または享受を制限したり、または妨げること。」に当てはまりそうですね。


    質問要旨:
     .NET Framework の EventLog クラス、GetEventLogs メソッドでイベントを拾ってきた場合、一部のイベントについて、DLL がないためメッセージを表示できない、というメッセージになった。どうすれば完全なメッセージを取得できるか。

    対話要旨:(敬称略)
    k_kazu; メッセージ DLL が読み込めなかったからではないか。
    blanca57move; 「イベント ログの表示」では表示されるので、メッセージ DLL がない、ということはない。
    k_kazu; .NET Framework 3.5 の Eventing.Reader.EventLogRecord クラスを使ってはどうか。
    blanca57move; 簡単に表示できることが目的ではない。EventLog クラスで表示することが目的。
    Jitta; サンプルコードでは、リモート ホストにアクセスしているようだ。ローカルで実行するとどうか。
    blanca57move; ローカルで実行しても同じ。
    Jitta; GetEventLogs メソッドに引数を渡すと、ローカルで実行してもリモート ホストへのアクセスとなる。リモート ホストのイベントでは、メッセージ DLL を解決できないようだ。

    2008年10月15日 11:35