Приложение WinForm, работающее на консоли — проблема с отключением кнопок сворачивания / разворачивания / закрытия на консоли

#c# #winforms #winforms-interop

#c# #winforms #winforms-взаимодействие

Вопрос:

Я пишу приложение Windows forms, которое может запускать консоль для отладки. Я хочу отключить кнопку закрытия консоли, чтобы приложение Windows forms нельзя было закрыть с помощью кнопки закрытия консоли. Я создал скелет тестового кода, и он работает. Код приведен ниже:

 using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Diagnostics;
using System.Runtime.InteropServices;

namespace bsa_working
{
    public partial class Form1 : Form
    {
        static bool console_on = false;

        public Form1()
        {
            InitializeComponent();
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            if (ViewConsole.Checked)
            {
                Win32.AllocConsole();
                ConsoleProperties.ConsoleMain();

                // Set console flag to true
                console_on = true;  // will be used later
            }
            else
                Win32.FreeConsole();
        }
    }

    public class Win32
    {
        [DllImport("kernel32.dll")]
        public static extern Boolean AllocConsole();
        [DllImport("kernel32.dll")]
        public static extern Boolean FreeConsole();
    }

    public class ConsoleProperties
    {
        [DllImport("user32.dll")]
        static extern bool EnableMenuItem(IntPtr hMenu, uint uIDEnableItem, uint uEnable);

        [DllImport("user32.dll")]
        static extern IntPtr GetSystemMenu(IntPtr hWnd, bool bRevert);

        [DllImport("user32.dll")]
        static extern IntPtr RemoveMenu(IntPtr hMenu, uint nPosition, uint wFlags);

        internal const uint SC_CLOSE = 0xF060;
        internal const uint MF_GRAYED = 0x00000001;
        internal const uint MF_BYCOMMAND = 0x00000000;

        public static void ConsoleMain()
        {
            IntPtr hMenu = Process.GetCurrentProcess().MainWindowHandle;
            IntPtr hSystemMenu = GetSystemMenu(hMenu, false);

            EnableMenuItem(hSystemMenu, SC_CLOSE, MF_GRAYED);
            RemoveMenu(hSystemMenu, SC_CLOSE, MF_BYCOMMAND);

            // Set console title
            Console.Title = "Test Console";

            // Set console surface foreground and background color
            Console.BackgroundColor = ConsoleColor.DarkBlue;
            Console.ForegroundColor = ConsoleColor.White;
            Console.Clear();
        }
    }
}
  

Код работает нормально, ЗА ИСКЛЮЧЕНИЕМ:

  1. Когда код скомпилирован и запущен в первый раз, X на консоли не отображается серым цветом, но он отображается серым цветом в приложении Windows Forms. Однако, когда код закрывается и запускается снова, код работает так, как должен; то есть X на консоли становится серым, а приложение Windows Forms таким, каким оно должно быть. Есть идеи, почему и как это можно исправить?

  2. Иногда консоль отображается за формой win. Есть ли способ заставить консоль всегда быть на вершине?

Кроме того, есть ли какой-либо способ привязать консоль к определенному месту в WinForm? приложение? Я могу установить его размер, поэтому, если бы я мог закрепить его в определенном месте, я мог бы создать для него место в форме.

Комментарии:

1. Почему у вас просто нет Windows forms в вашем приложении, например, другой формы или элемента управления внутри текстового поля с включенной многострочностью, тогда вы перенаправляете стандартный вывод консоли на этот элемент управления без всей этой магии, которую вы пытаетесь сделать?

2. Потому что консоль в первую очередь предназначена для отладки, и я не обязательно хочу видеть ее все время.

3. @Zeos6: Я делаю то же самое в некоторых своих приложениях. Мне действительно нравится, как это работает. Вместо вызова Process.GetCurrentProcess().MainWindowHandle (что может привести к конфликту между формой и окном консоли), после вызова AllocConsole() получите дескриптор окна консоли, вызвав GetConsoleWindow()

4. Я думаю, что GetConsoleWindow () работает только для XP, и я хочу, чтобы это работало даже в среде операционной системы, отличной от XP.

5. Я не могу воспроизвести свою систему (64-разрядная версия Win7). Это поддерживает совет Boo о состоянии гонки. Хотя я вообще не понимаю, почему консоль становится главным окном процесса.

Ответ №1:

Чтобы заставить это работать, вам нужно изменить, IntPtr hMenu = Process.GetCurrentProcess().MainWindowHandle; чтобы вместо этого использовать дескриптор окна Консоли (который вы можете получить, вызвав GetConsoleWindow() ).

Чтобы отобразить его сверху, вы могли бы использовать, например, SetForegroundWindow с дескриптором окна консоли.

Что касается закрепления, я действительно не уверен, возможно ли это вообще.

Комментарии:

1. Спасибо за совет. Я согласен с условием гонки, и я также думаю, что закрепление, вероятно, не годится. Я просто подумал спросить, если я что-то упустил. Мое единственное возражение против GetConsoleWindow () заключается в том, что оно работает только для Win 2000 и XP. Если вы попытаетесь обойти (который существует), то возникнут проблемы, если у вас несколько comsoles, поскольку обходной путь находит только nearestone в порядке z. Я надеюсь избежать использования GetConsoleWindow ().

2. @Zeos6 с вашим приложением связана максимум ОДНА консоль (см. msdn.microsoft.com/en-us/library/windows/desktop / … ) так что это не будет проблемой… И GetConsoleWindow работает и для более поздних версий Windows! Пожалуйста, не забудьте проголосовать за / отметить как принятый любой ответ, который помог (см. meta.stackexchange.com/questions/5234 / … )

3. Спасибо Yahia. Я отмечу ответ. Я согласен с тем, что у вас есть только одна консоль с выделением. Я предполагаю, что моя путаница в том, что если у меня есть несколько приложений, и каждое приложение создает консоль, то какую консоль я получу? Насколько я понимаю, вы получите ближайшую консоль в порядке z, не обязательно ту, которая связана с нужным вам приложением. Кроме того, спасибо за SetForegroundWindow. В любом случае, мне может понадобиться использовать GetConsoleWindow(), хотя я бы предпочел этого не делать.

4. @Zeos6 Вы абсолютно точно не получите «ближайшую», но, согласно MS, именно ту консоль, которая связана с вашим приложением (см. msdn.microsoft.com/en-us/library/windows/desktop / … )… этот вызов API учитывает только контекст процесса, А НЕ какие-либо координаты экрана.