سؤال

لدي تطبيق وحدة تحكم يحتوي على عدد كبير جدًا من المواضيع.هناك خيوط تراقب شروطًا معينة وتنهي البرنامج إذا كانت صحيحة.يمكن أن يحدث هذا الإنهاء في أي وقت.

أحتاج إلى حدث يمكن تشغيله عند إغلاق البرنامج حتى أتمكن من تنظيف كافة مؤشرات الترابط الأخرى وإغلاق جميع مقابض الملفات والاتصالات بشكل صحيح.لست متأكدًا مما إذا كان هناك إطار عمل مدمج بالفعل في إطار عمل .NET، لذا فإنني أسأل قبل أن أكتب إطار عمل خاص بي.

كنت أتساءل عما إذا كان هناك حدث على غرار:

MyConsoleProgram.OnExit += CleanupBeforeExit;
هل كانت مفيدة؟

المحلول

لست متأكدًا من المكان الذي وجدت فيه الكود على الويب، لكنني وجدته الآن في أحد مشاريعي القديمة.سيسمح لك هذا بتنفيذ تعليمات برمجية للتنظيف في وحدة التحكم الخاصة بك، على سبيل المثال.عندما يتم إغلاقه فجأة أو بسبب إيقاف التشغيل...

[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);

private delegate bool EventHandler(CtrlType sig);
static EventHandler _handler;

enum CtrlType
{
  CTRL_C_EVENT = 0,
  CTRL_BREAK_EVENT = 1,
  CTRL_CLOSE_EVENT = 2,
  CTRL_LOGOFF_EVENT = 5,
  CTRL_SHUTDOWN_EVENT = 6
}

private static bool Handler(CtrlType sig)
{
  switch (sig)
  {
      case CtrlType.CTRL_C_EVENT:
      case CtrlType.CTRL_LOGOFF_EVENT:
      case CtrlType.CTRL_SHUTDOWN_EVENT:
      case CtrlType.CTRL_CLOSE_EVENT:
      default:
          return false;
  }
}


static void Main(string[] args)
{
  // Some biolerplate to react to close window event
  _handler += new EventHandler(Handler);
  SetConsoleCtrlHandler(_handler, true);
  ...
}

تحديث

بالنسبة لأولئك الذين لم يتحققوا من التعليقات، يبدو أن هذا الحل المحدد يفعل ذلك لا تعمل بشكل جيد (أو على الإطلاق) على ويندوز 7.الأتى خيط يتحدث عن هذا

نصائح أخرى

ومثال عمل كامل، ويعمل مع CTRL-ج، وإغلاق النوافذ مع X وقتل:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading;

namespace TestTrapCtrlC {
    public class Program {
        static bool exitSystem = false;

        #region Trap application termination
        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);

        private delegate bool EventHandler(CtrlType sig);
        static EventHandler _handler;

        enum CtrlType {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT = 1,
            CTRL_CLOSE_EVENT = 2,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT = 6
        }

        private static bool Handler(CtrlType sig) {
            Console.WriteLine("Exiting system due to external CTRL-C, or process kill, or shutdown");

            //do your cleanup here
            Thread.Sleep(5000); //simulate some cleanup delay

            Console.WriteLine("Cleanup complete");

            //allow main to run off
            exitSystem = true;

            //shutdown right away so there are no lingering threads
            Environment.Exit(-1);

            return true;
        }
        #endregion

        static void Main(string[] args) {
            // Some boilerplate to react to close window event, CTRL-C, kill, etc
            _handler += new EventHandler(Handler);
            SetConsoleCtrlHandler(_handler, true);

            //start your multi threaded program here
            Program p = new Program();
            p.Start();

            //hold the console so it doesn’t run off the end
            while (!exitSystem) {
                Thread.Sleep(500);
            }
        }

        public void Start() {
            // start a thread and start doing some processing
            Console.WriteLine("Thread started, processing..");
        }
    }
}

وتحقق أيضا:

AppDomain.CurrentDomain.ProcessExit

وهذا يبدو وكأنه لديك المواضيع إنهاء مباشرة تطبيق؟ ربما سيكون من الأفضل أن يكون موضوع مؤشرا على الترابط الرئيسي القول أنه ينبغي إنهاء التطبيق.

في تلقي هذا إشارة، فإن الترابط الرئيسي يمكن نظيفة اغلاق المواضيع الأخرى، وأخيرا إغلاق نفسه باستمرار.

وهناك لWinForms عناصر التطبيقات؛

Application.ApplicationExit += CleanupBeforeExit;

لتطبيقات وحدة التحكم، حاول

AppDomain.CurrentDomain.DomainUnload += CleanupBeforeExit;

ولكن لست متأكدا ما هي النقطة التي يحصل على استدعاء أو ما اذا كانت ستعمل من داخل المجال الحالي. لا أظن.

ولقد كان لي مشكلة مماثلة، مجرد حدة التحكم الخاصة بي التطبيقات سيتم تشغيلها في حلقة لا نهائية مع بيان وقائية واحدة على الوسط. وإنني هنا الحل:

class Program
{
    static int Main(string[] args)
    {
        // Init Code...
        Console.CancelKeyPress += Console_CancelKeyPress;  // Register the function to cancel event

        // I do my stuffs

        while ( true )
        {
            // Code ....
            SomePreemptiveCall();  // The loop stucks here wating function to return
            // Code ...
        }
        return 0;  // Never comes here, but...
    }

    static void Console_CancelKeyPress(object sender, ConsoleCancelEventArgs e)
    {
        Console.WriteLine("Exiting");
        // Termitate what I have to terminate
        Environment.Exit(-1);
    }
}

والجواب ZeroKelvin وتعمل في ويندوز 10 x64 و. NET 4.6 التطبيق وحدة التحكم. بالنسبة لأولئك الذين لا يحتاجون للتعامل مع التعداد CtrlType، هنا هو طريقة بسيطة حقا ربط إلى إغلاق إطار و:

class Program
{
    private delegate bool ConsoleCtrlHandlerDelegate(int sig);

    [DllImport("Kernel32")]
    private static extern bool SetConsoleCtrlHandler(ConsoleCtrlHandlerDelegate handler, bool add);

    static ConsoleCtrlHandlerDelegate _consoleCtrlHandler;

    static void Main(string[] args)
    {
        _consoleCtrlHandler += s =>
        {
            //DoCustomShutdownStuff();
            return false;   
        };
        SetConsoleCtrlHandler(_consoleCtrlHandler, true);
    }
}

والعودة FALSE من معالج يروي إطار أننا لا "التعامل مع" إشارة تحكم، وظيفة معالج المقبلة في قائمة معالجات لهذه العملية يستخدم. إذا كان أي من معالجات إرجاع TRUE، يتم استدعاء معالج الافتراضي.

لاحظ أنه عندما يقوم المستخدم تسجيل الخروج أو إيقاف، لا يتم استدعاء رد من قبل ويندوز ولكن بدلا من ذلك إنهاء فورا.

فيجوال ستوديو 2015 + ويندوز 10

  • السماح بالتنظيف
  • تطبيق مثيل واحد
  • بعض طلاء الذهب

شفرة:

using System;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading;

namespace YourNamespace
{
    class Program
    {
        // if you want to allow only one instance otherwise remove the next line
        static Mutex mutex = new Mutex(false, "YOURGUID-YOURGUID-YOURGUID-YO");

        static ManualResetEvent run = new ManualResetEvent(true);

        [DllImport("Kernel32")]
        private static extern bool SetConsoleCtrlHandler(EventHandler handler, bool add);                
        private delegate bool EventHandler(CtrlType sig);
        static EventHandler exitHandler;
        enum CtrlType
        {
            CTRL_C_EVENT = 0,
            CTRL_BREAK_EVENT = 1,
            CTRL_CLOSE_EVENT = 2,
            CTRL_LOGOFF_EVENT = 5,
            CTRL_SHUTDOWN_EVENT = 6
        }
        private static bool ExitHandler(CtrlType sig)
        {
            Console.WriteLine("Shutting down: " + sig.ToString());            
            run.Reset();
            Thread.Sleep(2000);
            return false; // If the function handles the control signal, it should return TRUE. If it returns FALSE, the next handler function in the list of handlers for this process is used (from MSDN).
        }


        static void Main(string[] args)
        {
            // if you want to allow only one instance otherwise remove the next 4 lines
            if (!mutex.WaitOne(TimeSpan.FromSeconds(2), false))
            {
                return; // singleton application already started
            }

            exitHandler += new EventHandler(ExitHandler);
            SetConsoleCtrlHandler(exitHandler, true);

            try
            {
                Console.BackgroundColor = ConsoleColor.Gray;
                Console.ForegroundColor = ConsoleColor.Black;
                Console.Clear();
                Console.SetBufferSize(Console.BufferWidth, 1024);

                Console.Title = "Your Console Title - XYZ";

                // start your threads here
                Thread thread1 = new Thread(new ThreadStart(ThreadFunc1));
                thread1.Start();

                Thread thread2 = new Thread(new ThreadStart(ThreadFunc2));
                thread2.IsBackground = true; // a background thread
                thread2.Start();

                while (run.WaitOne(0))
                {
                    Thread.Sleep(100);
                }

                // do thread syncs here signal them the end so they can clean up or use the manual reset event in them or abort them
                thread1.Abort();
            }
            catch (Exception ex)
            {
                Console.ForegroundColor = ConsoleColor.Red;
                Console.Write("fail: ");
                Console.ForegroundColor = ConsoleColor.Black;
                Console.WriteLine(ex.Message);
                if (ex.InnerException != null)
                {
                    Console.WriteLine("Inner: " + ex.InnerException.Message);
                }
            }
            finally
            {                
                // do app cleanup here

                // if you want to allow only one instance otherwise remove the next line
                mutex.ReleaseMutex();

                // remove this after testing
                Console.Beep(5000, 100);
            }
        }

        public static void ThreadFunc1()
        {
            Console.Write("> ");
            while ((line = Console.ReadLine()) != null)
            {
                if (line == "command 1")
                {

                }
                else if (line == "command 1")
                {

                }
                else if (line == "?")
                {

                }

                Console.Write("> ");
            }
        }


        public static void ThreadFunc2()
        {
            while (run.WaitOne(0))
            {
                Thread.Sleep(100);
            }

           // do thread cleanup here
            Console.Beep();         
        }

    }
}

للراغبين في VB.net. (ولقد بحثت في الإنترنت ولا يتمكن من العثور على ما يعادل لذلك) هنا ترجمته إلى vb.net.

    <DllImport("kernel32")> _
    Private Function SetConsoleCtrlHandler(ByVal HandlerRoutine As HandlerDelegate, ByVal Add As Boolean) As Boolean
    End Function
    Private _handler As HandlerDelegate
    Private Delegate Function HandlerDelegate(ByVal dwControlType As ControlEventType) As Boolean
    Private Function ControlHandler(ByVal controlEvent As ControlEventType) As Boolean
        Select Case controlEvent
            Case ControlEventType.CtrlCEvent, ControlEventType.CtrlCloseEvent
                Console.WriteLine("Closing...")
                Return True
            Case ControlEventType.CtrlLogoffEvent, ControlEventType.CtrlBreakEvent, ControlEventType.CtrlShutdownEvent
                Console.WriteLine("Shutdown Detected")
                Return False
        End Select
    End Function
    Sub Main()
        Try
            _handler = New HandlerDelegate(AddressOf ControlHandler)
            SetConsoleCtrlHandler(_handler, True)
     .....
End Sub
scroll top