次の方法で共有


I've only ever written one application

Actually, I mean, I've only ever written one application startup - in the same way I've only ever written two make files, one for applications and one for libraries, I just keep changing the words... OK, that's obviously a facetious statement, but it is true that almost all my .NET desktop applications have a very standard startup sequence just to make things a bit tidier: I include the following class in most applications;

 internal class Program
{
    [STAThread()]
    public static void Main()
    {
        string name = new AssemblyName(Assembly.GetExecutingAssembly().FullName).Name;
        bool mutexCreated;
        using (var mutex = new Mutex(false, name, out mutexCreated))
        {
            if (mutexCreated)
            {
                WerExclusion(true);

                App.Main();

                WerExclusion(false);
            }
            else
                MessageBox.Show("This app already running");
        }
    }
}

I create a mutex to be able to make sure only one copy of the application is running at a time - quite useful when debugging, in case I don't notice that a copy is already running and grabbing some shared resource. The assembly name is a convenient vaguely unique name for me to use, but I do sometimes make use of an explicit name, for example, when I have multiple co-operating processes, and I want one to be able to check if another is running. (I use this in Project Colletta for application add-ins to check if the main executable is running, to avoid messaging timeouts and errors.)

Unless you're a programming demigod, the first few versions of your application probably crash every so often. And this typically results in that dialog box asking you if you want to report errors to Microsoft, which is fairly pointless for a self-developed application, certainly in its early days. Since Windows Vista, a couple of functions have been available to mark (and unmark) applications as being excluded from this automatic error reporting - see the WerAddExcludedApplication documentation for details. I tend to mark my applications in this way, if nothing else, to remove the delay before I can dismiss the crash dialog and close the faulting application. For tidiness, though I don't think it's necessary, I get rid of the entry in the exclusion list when my app exits (ideally this should be later than shown here, but this suffices). The following is a simple wrapper on this native mechanism:

 private static void WerExclusion(bool add)
{
    var exe = Assembly.GetExecutingAssembly().Location;
    try
    {
        int hr = add ? Win32.WerAddExcludedApplication(exe, false) :
                       Win32.WerRemoveExcludedApplication(exe, false);
        Debug.Assert(hr == 0);
    }
    catch
    {
        Debug.WriteLine("WER functions not available");
    }
}

[DllImport("wer.dll", CharSet=CharSet.Unicode)]
internal static extern int WerAddExcludedApplication(string exeName, [MarshalAs(UnmanagedType.Bool)] bool allUsers);

[DllImport("wer.dll", CharSet=CharSet.Unicode)]
internal static extern int WerRemoveExcludedApplication(string exeName, [MarshalAs(UnmanagedType.Bool)] bool allUsers);

(Yes, I know, I'm being far too sloppy in terms of error handling here - my feeble excuse is that this is a mere nice-to-have and not essential.)

Of course, an alternative to the Windows error report exclusion list manipulations would be to implement an unhandled exception handler (AppDomain.UnhandledException) but I tend not to do that for a couple of reasons: first, it needs to be done for each application domain and I often forget; second, it interferes (though only slightly) with debugging, in that by default Visual Studio traps unhandled exceptions itself, unless there's an handler for them (an easy default to change via the exception settings page). I'm in two minds about whether "proper" production applications should have unhandled exception handlers - yes, it's rather unprofessional to show your end users a crash dialog box, but then so is crashing in the first place... Besides, there's not really a huge amount such a handler can do apart from perhaps log some small amount of data and then exit the application.

Finally, deep within my Program.Main is a call to App.Main. In Windows Forms applications, the Visual Studio Wizard provides a Program class with a Main method, and I add my extras to that, around the call to Application.Run. In WPF applications, the normal entry point is App.Main, which is autogenerated by the build process. An executable can have as many Main methods as you want, but you do need to tell Windows which one is the real entry point - and that's pretty easy to do via the Application page of the project options - the Startup object dropdown lists all available entry points and I make sure I pick mine. App.Main can, of course, just be called like any other function too.

And that's it, my one and only application startup. Well, for desktop .NET applications anyway.