How does Thread.Abort() work?

前端 未结 5 646
鱼传尺愫
鱼传尺愫 2020-12-08 16:41

We usually throw exception when invalid input is passed to a method or when a object is about to enter invalid state. Let\'s consider the following example

         


        
5条回答
  •  春和景丽
    2020-12-08 17:08

    To get the QueueUserAPC to work you have to do two things.

    1. Acquire the target thread handle. Note that this is not the same thing as the native thread id.
    2. Allow the target thread to go into an alertable state.

    Here is a complete program that demonstrates this.

    class Program
    {
        [DllImport("kernel32.dll", EntryPoint = "DuplicateHandle", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern bool DuplicateHandle([In] System.IntPtr hSourceProcessHandle, [In] System.IntPtr hSourceHandle, [In] System.IntPtr hTargetProcessHandle, out System.IntPtr lpTargetHandle, uint dwDesiredAccess, [MarshalAsAttribute(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);
    
        [DllImport("kernel32.dll", EntryPoint = "GetCurrentProcess", CallingConvention = CallingConvention.StdCall, SetLastError = true)]
        public static extern IntPtr GetCurrentProcess();
    
        [DllImport("kernel32.dll")]
        private static extern IntPtr GetCurrentThread();
    
        [DllImport("kernel32.dll")]
        private static extern uint QueueUserAPC(ApcMethod pfnAPC, IntPtr hThread, UIntPtr dwData);
    
        private delegate void ApcMethod(UIntPtr dwParam);
    
        static void Main(string[] args)
        {
            Console.WriteLine("Main: " + Thread.CurrentThread.ManagedThreadId);
            IntPtr threadHandle = IntPtr.Zero;
            var threadHandleSet = new ManualResetEvent(false);
            var apcSet = new ManualResetEvent(false);
            var thread = new Thread(
                () =>
                {
                    Console.WriteLine("thread started");
                    threadHandle = GetCurrentThread();
                    DuplicateHandle(GetCurrentProcess(), GetCurrentThread(), GetCurrentProcess(), out threadHandle, 0, false, 2);
                    threadHandleSet.Set();
                    apcSet.WaitOne();
                    for (int i = 0; i < 10; i++)
                    {
                        Console.WriteLine("thread waiting");
                        Thread.Sleep(1000);
                        Console.WriteLine("thread running");
                    }
                    Console.WriteLine("thread finished");
                });
            thread.Start();
            threadHandleSet.WaitOne();
            uint result = QueueUserAPC(DoApcCallback, threadHandle, UIntPtr.Zero);
            apcSet.Set();
            Console.ReadLine();
        }
    
        private static void DoApcCallback(UIntPtr dwParam)
        {
            Console.WriteLine("DoApcCallback: " + Thread.CurrentThread.ManagedThreadId);
        }
    
    }
    

    This essentially allows a developer to inject the execution of a method into any arbitrary thread. The target thread does not have to have a message pump like would be necessary for the traditional approach. One problem with this approach though is that the target thread has to be in an alertable state. So basically the thread must call one of the canned .NET blocking calls like Thread.Sleep, WaitHandle.WaitOne, etc. for the APC queue to execute.

提交回复
热议问题