Using EventLog in ClickOnce application

2020-03-21 05:16发布

问题:

I've got a library that I use across multiple ClickOnce applications. In the event of an error in this library I would like to write the error to the windows EventLog.

I found a KB article on how but it seems that this requires administrator permissions to search the for the source. Specifically it chokes when trying to search the Security event log.

Is there anyway to work around this and write to the event log in a ClickOnce application? I saw one person trying to write to a known source, but they didn't seem to be able to find a source that was consistently available.

EDIT:

Based on answers here I create an program that's included with my application that I can run on the first run to set up the event source that can get admin privileges. However once the source is created it seems I still cannot write to it.

    /// <summary>
    /// The main entry point for the application.
    /// </summary>
    [STAThread]
    static void Main()
    {
        if (!EventLog.SourceExists("ATE"))
        {
            EventLog.CreateEventSource("ATE", "Application");
        }
    }

Is properly creates a source (which is equivalent to the registry edit provided by Yannick Blondeau). When I write to the source in my non-elevated application I receive an new error, but it still doesn't work. The new error is:

Cannot open log for source 'ATE'. You may not have write access.

EDIT 2:

I've now been trying to get it to work through registry edits on the CustomSD key. I tried adding (A;;0x7;;;AU) to give authenticated users full access but it didn't seem to have any effect.

回答1:

Unfortunately, the event source requires administrative priveledges to be created. However, you don't need admin rights to write to the event log, or read from it.

There are two ways around this.

You add the new event source when you install the application as an administrator.

You create a simple app that you run as an admin to configure your application. This could even be included in the installer.

If you don't have or want an installer, the load the app onto the computer as an admin and run the program once. Your app startup should configure the event source if it isn't already there anyway, right? (Okay, that's three ways.)

This code snippet is from microsoft: http://msdn.microsoft.com/en-us/library/system.diagnostics.eventlog.aspx and is designed to be run as an admin user.

using System;
using System.Diagnostics;
using System.Threading;

class MySample
{
    public static void Main()
    {
        // Create the source, if it does not already exist. 
        if (!EventLog.SourceExists("MySource"))
        {
             //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. 
             //Execute this sample a second time to use the new source.
             EventLog.CreateEventSource("MySource", "MyNewLog");
             Console.WriteLine("CreatedEventSource");
             Console.WriteLine("Exiting, execute the application a second time to use the source.");
            // The source is created.  Exit the application to allow it to be registered. 
            return;
        }
        // Create an EventLog instance and assign its source.
        EventLog myLog = new EventLog();
        myLog.Source = "MySource";

        // Write an informational entry to the event log.    
        myLog.WriteEntry("Writing to event log.");
    }
}

I know it may not be exactly what you were after, but I reckon it's the only way to do this.

EDIT 1: Added some more code

public class EventLogger
{
    private const string logName = "Application";
    private static string appName = "";
    private static bool sourceExists = false;

    public static string AppName
    {
        get { return appName; }
        set { appName = value; }
    }

    public static void Init(string appName)
    {
        AppName = appName;
        sourceExists = EventLog.SourceExists(AppName);

        if (!sourceExists)
        {
            EventLog.CreateEventSource(AppName, logName);
            sourceExists = true;
        }
    }

    private static void Write(string entry, EventLogEntryType logType, int eventID)
    {
        if (sourceExists)
        {
            EventLog.WriteEntry(AppName, entry, logType, eventID);
        }
    }

    public static void Warning(string entry) { Write(entry, EventLogEntryType.Warning, 200); }
    public static void Warning(string entry, int eventID) { Write(entry, EventLogEntryType.Warning, eventID); }
    public static void Error(string entry) { Write(entry, EventLogEntryType.Error, 300); }
    public static void Error(string entry, int eventID) { Write(entry, EventLogEntryType.Error, eventID); }
    public static void Info(string entry) { Write(entry, EventLogEntryType.Information, 100); }
    public static void Info(string entry, int eventID) { Write(entry, EventLogEntryType.Information, eventID); }
}

This is the way that I have implemented my EventLogger class which is in use in a production application.

If you could post your code we can do a comparison.

One thing that occurs to me is that when I create my source, I use the application name, but stick with the Application logfile. Are you also attempting to create a new logfile. If so check that it is created in the event viewer.

EDIT 2: Impersonate User with a user token value of zero

This is a bit of a stumper.

Try this code, wrapped around the event writing code.

System.Security.Principal.WindowsImpersonationContext wic = System.Security.Principal.WindowsIdentity.Impersonate(IntPtr.Zero);
// your code to write to event log or any to do something which needs elevated permission--
wic.Undo();

I haven't tried this, simply because my code is working, it comes from here: http://sharenotes.wordpress.com/2008/03/18/cannot-open-log-for-source-you-may-not-have-write-access/



回答2:

An alternative approach to this is to download the ClickOnce setup.exe file, right click and run as administrator. Not very satisfactory but seems to work.



回答3:

In the ClickOnce documentation, it is said that if the user running a ClickOnce application is not an administrator, the application will fail writing to the event log.

Full quote:

After a ClickOnce deployment, the application will create an event source if it does not already exist when the application attempts to write an event to the Event log. If the user is not an administrator, the attempt fails and the application will not log any events. In this case, you can create the event source manually.

To create the event source manually, you will have to add an entry in the registry like this one during your deployment process:

[HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Eventlog\Application\YourApp]
"EventMessageFile"="%SystemRoot%\\Microsoft.NET\\Framework\\v2.0.50727\\EventLogMessages.dll"

This approach will limit the elevation need at the deployment phase.