2012-03-08 44 views
5

Chương trình sau ném lỗi trên "Console.ReadKey()".Làm cách nào để đóng/mở bàn điều khiển trong C# bằng các cuộc gọi Win32?

Làm cách nào để bật lại bảng điều khiển sau khi vô hiệu hóa bảng điều khiển?

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

    namespace ConsoleApplication 
    { 
     class Program 
     { 
      static void Main(string[] args) 
      { 
       ThreadPool.QueueUserWorkItem((o) => 
       { 
        Thread.Sleep(1000); 
        IntPtr stdin = GetStdHandle(StdHandle.Stdin); 
        CloseHandle(stdin); 
       }); 
       Console.ReadLine(); 
       Console.Write("ReadLine() successfully aborted by background thread.\n"); 
       Console.Write("[any key to exit]"); 
       Console.ReadKey(); // Throws an exception "Cannot read keys when either application does not have a console or when console input has been redirected from a file. Try Console.Read." 
      } 

      // P/Invoke: 
      private enum StdHandle { Stdin = -10, Stdout = -11, Stderr = -12 }; 
      [DllImport("kernel32.dll")] 
      private static extern IntPtr GetStdHandle(StdHandle std); 
      [DllImport("kernel32.dll")] 
      private static extern bool CloseHandle(IntPtr hdl); 
     } 
    } 

tắm cho chuyên gia

Nếu bạn đang tự hỏi, tôi cần để có thể giết một sợi nền chạy ReadLine(), trong C#. Điều này dường như là cách duy nhất (thread.Abort sẽ không hoạt động vì ReadLine() chạy sâu bên trong ruột của hệ điều hành, trong mã không được quản lý). Có rất nhiều thảo luận về chủ đề này trên StackOverflow, không ai thực sự phát hiện (hoặc được đăng) một phương pháp thỏa đáng để hủy bỏ Console.ReadLine(). Tôi nghĩ rằng mã này đang đi đúng hướng - nếu chúng tôi chỉ có thể bật lại bảng điều khiển sau khi tắt nó.

+0

Nếu bạn đóng Xử lý ... Bạn sẽ phải bằng tay mở lại. Xem xét bạn chỉ cần đóng xử lý không có gurantee bạn có thể sử dụng cùng một xử lý một lần nữa. Bạn có chắc là bạn đóng đúng xử lý? Tôi phải tin rằng có một cách dễ dàng hơn để hủy bỏ một Readline sau đó để đóng xử lý đẫm máu của đầu vào tiêu chuẩn. –

+0

@Ramhound Tôi ước rằng có một cách dễ dàng hơn để hủy bỏ ReadLine. Tôi đã cố gắng tất cả mọi thứ - .Abort, gửi các phím đến ứng dụng (chỉ hoạt động cho tiêu điểm hiện tại), vv Nếu có cách nào tốt hơn, tôi là tất cả các tai. – Contango

+0

@Ramhound Cách duy nhất khác tôi có thể nghĩ đến là viết một bản sao của ReadLine trong 100% được quản lý C#, vì vậy .Abort sẽ hoạt động, chắc chắn phức tạp hơn so với một vài dòng mã Win32. – Contango

Trả lời

0

Không biết nếu nó giúp, nhưng khi tôi đã cần thiết để có thể viết thư cho giao diện điều khiển trong một ứng dụng hình thức chiến thắng, tôi đã sử dụng lớp này:

public class ConsoleHelper 
{ 
    /// <summary> 
    /// Allocates a new console for current process. 
    /// </summary> 
    [DllImport("kernel32.dll")] 
    public static extern Boolean AllocConsole(); 

    /// <summary> 
    /// Frees the console. 
    /// </summary> 
    [DllImport("kernel32.dll")] 
    public static extern Boolean FreeConsole(); 
} 

Gọi AllocConsole tạo một giao diện điều khiển và sau đó bạn có thể ghi vào (và đọc từ) nó. Sau đó gọi FreeConsole khi bạn đã hoàn tất.

+1

Cảm ơn bạn đã biết mẹo. Tôi đã thử điều này. Nếu bạn gọi FreeConsole() thì AllocConsole(), mọi cuộc gọi ReadLine() hiện tại sẽ ném một ngoại lệ "Đã cố đọc hoặc ghi bộ nhớ được bảo vệ. Đây thường là dấu hiệu cho thấy bộ nhớ khác bị hỏng". Ngoại lệ này không thể bị bắt gặp bởi lệnh try/catch trong C#. Nó cũng có một tác dụng phụ xấu: nó mất bất kỳ lịch sử nào trong giao diện điều khiển khi nó bắt đầu một giao diện điều khiển mới. Tôi tự hỏi nếu có một phương pháp khác? – Contango

6

Sử dụng PostMessage để gửi [nhập] vào quá trình hiện tại:

class Program 
    { 
     [DllImport("User32.Dll", EntryPoint = "PostMessageA")] 
     private static extern bool PostMessage(IntPtr hWnd, uint msg, int wParam, int lParam); 

     const int VK_RETURN = 0x0D; 
     const int WM_KEYDOWN = 0x100; 

     static void Main(string[] args) 
     { 
      Console.Write("Switch focus to another window now to verify this works in a background process.\n"); 

      ThreadPool.QueueUserWorkItem((o) => 
      { 
       Thread.Sleep(4000); 

       var hWnd = System.Diagnostics.Process.GetCurrentProcess().MainWindowHandle; 
       PostMessage(hWnd, WM_KEYDOWN, VK_RETURN, 0); 
      }); 

      Console.ReadLine(); 

      Console.Write("ReadLine() successfully aborted by background thread.\n"); 
      Console.Write("[any key to exit]"); 
      Console.ReadKey(); 
     } 
    } 

Câu trả lời này cũng hoạt động trên thực tế là gọi .Abort trên ReadLine() sẽ không làm việc, vì mã đang chạy trong không được quản lý mã sâu bên trong nhân Windows.

Câu trả lời này vượt trội hơn bất kỳ câu trả lời nào chỉ hoạt động nếu quy trình hiện tại có tiêu điểm, chẳng hạn như SendKeysInput Simulator.

Câu trả lời này vượt trội hơn phương pháp đóng bảng điều khiển bàn điều khiển hiện tại, vì hành động đóng kết quả xử lý bảng điều khiển hiện tại trong các cuộc gọi trong tương lai tới ReadLine() ném một lỗi.

+2

Đây là giải pháp tôi ưa thích - cảm ơn! –

+0

Tôi đã sử dụng giải pháp này, nhưng nếu chạy dự án với CTRL + F5 (studio trực quan 2017) nó sẽ không hoạt động, nếu với F5 hoặc chỉ thực thi sẽ ổn – Nikita

Các vấn đề liên quan