Установка формы Windows как нижней - PullRequest
12 голосов
/ 08 января 2010

Некоторый фон

Один из моих текущих клиентов управляет сетью точек доступа в Интернет, где клиенты получают доступ к сети через ПК: они настроены как «киоски» (пользовательское приложение «блокирует» компьютер до тех пор, пока пользователь не войдет в систему, текущая учетная запись сильно ограничена групповой политикой Windows). В настоящее время каждый компьютер работает под управлением Windows XP и использует Active Desktop для отображения рекламы в фоновом режиме. Тем не менее, поскольку у моего клиента ежедневно возникают проблемы со сбоями Active Desktop (помимо общего замедления работы компьютера), меня попросили разработать приложение, которое его заменит.

Проблема

Я пытаюсь выяснить, возможно ли создать приложение для форм Windows (с использованием C #), которое всегда остается в фоновом режиме . Приложение должно располагаться над рабочим столом (чтобы оно покрывало любые значки, файлы и т. Д.), Но всегда позади всех других работающих приложений. Наверное, я действительно ищу свойство BottomMost класса Form (которого, конечно, не существует).

Любые советы или указатели о том, как этого добиться, будут высоко оценены.

Ответы [ 7 ]

16 голосов
/ 08 января 2010

Это напрямую не поддерживается классом .NET Form, поэтому у вас есть два варианта:

1) Используйте функцию Win32 API SetWindowPos.

pinvoke.net показывает, как объявить это для использования в C #:

[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X, int Y, int cx, int cy, uint uFlags);

static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;

Так в вашем коде звоните:

SetWindowPos(Handle, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);

Как вы прокомментировали, это перемещает форму в конец z-порядка, но не сохраняет ее там. Единственный обходной путь, который я вижу для этого, - это вызвать SetWindowPos из событий Form_Load и Form_Activate. Если ваше приложение развернуто, а пользователь не может переместить или свернуть форму, то вам может не понравиться этот подход, но он все равно остается взломанным. Также пользователь может увидеть небольшое «мерцание», если форма будет перенесена в начало z-порядка до того, как будет сделан вызов SetWindowPos.


2) подкласс формы , переопределить функцию WndProc и перехватить сообщение WM_WINDOWPOSCHANGING Windows, установив флаг SWP_NOZORDER (взятый из этой страницы ) .

3 голосов
/ 01 июля 2010

Установите ваше окно как дочернее окно рабочего стола (процесс "Диспетчер программ" или "progman"). Мне удалось использовать этот метод в Windows XP (x86) и Windows Vista (x64).

Я наткнулся на этот метод, когда искал способ заставить экранную заставку отображать как обои. Оказывается, это что-то вроде встроенного в системный обработчик .scr. Вы используете screensaver.scr /p PID, где PID - это идентификатор процесса другой программы для подключения. Поэтому напишите программу, чтобы найти дескриптор программы, а затем вызовите .scr с этим аргументом / p, и у вас появятся обои с заставкой!

Проект, с которым я сейчас играю, - это отображение состояния рабочего стола (показывает время, некоторые задачи, подключенные диски и т. Д.), И он построен на Strawberry Perl и обычном Win32 APIS (в основном Win32 :: GUI и Win32 :: Модули API), поэтому код легко переносить или понимать любой динамический язык с похожими привязками Win32 API или с доступом к Windows Scripting Host (например, ActivePerl, Python, JScript, VBScript). Вот соответствующая часть класса, которая создает окно:

do { Win32::API->Import(@$_) or die "Win32::API can't import @$_ ($^E)" } for
    [user32 => 'HWND FindWindow(LPCTSTR lpClassName, LPCTSTR lpWindowName)'],
    [user32 => 'HWND SetParent(HWND hWndChild, HWND hWndNewParent)'],

sub __screen_x {
    Win32::GUI::GetSystemMetrics(SM_CXSCREEN)
}
sub __screen_y {
    Win32::GUI::GetSystemMetrics(SM_CYSCREEN)
}
sub _create_window { # create window that covers desktop
    my $self = shift;
    my $wnd = $$self{_wnd} = Win32::GUI::Window->new(
        -width   => __screen_x(), -left => 0,
        -height  => __screen_y(), -top  => 0,
    ) or die "can't create window ($^E)";

    $wnd->SetWindowLong(GWL_STYLE,
        WS_VISIBLE
        | WS_POPUP # popup: no caption or border
    );
    $wnd->SetWindowLong(GWL_EXSTYLE, 
        WS_EX_NOACTIVATE # noactivate: doesn't activate when clicked
        | WS_EX_NOPARENTNOTIFY # noparentnotify: doesn't notify parent window when created or destroyed
        | WS_EX_TOOLWINDOW # toolwindow: hide from taskbar
    );
    SetParent($$wnd{-handle}, # pin window to desktop (bottommost)
        (FindWindow('Progman', 'Program Manager') or die "can't find desktop window ($^E)")
    ) or die "can't pin to desktop ($^E)";
    Win32::GUI::DoEvents; # allow sizing and styling to take effect (otherwise DC bitmaps are the wrong size)
}

Эта программа буферизирует вывод, чтобы предотвратить мерцание, что вы, вероятно, захотите сделать также. Я создаю DC (контекст устройства) и PaintDesktop к нему (вы можете использовать любое растровое изображение только с парой строк - CreateCompatibleBitmap, прочитать файл и выбрать дескриптор растрового изображения в качестве кисти), затем создать буфер хранения, чтобы сохранить чистая копия этого фона и рабочий буфер для сборки фрагментов - в каждом цикле копируйте в фоновом режиме, затем рисуйте линии и чистите растровые изображения и используйте TextOut - который затем копируется в исходный DC, после чего он появляется на экран.

2 голосов
/ 05 декабря 2014

Я думаю, что лучший способ сделать это - использовать активированный обработчик событий и метод SendToBack, например:

private void Form1_Activated(object sender, EventArgs e)
{
    this.SendToBack();
}
0 голосов
/ 21 декабря 2016

Создайте панель, которая покрывает вашу форму, но все, что вы хотите на этой панели, затем в Click-Event панели напишите this.sendback.

0 голосов
/ 19 марта 2013

Мне удалось избавиться от мерцания при использовании setwindowpos ...

const UInt32 SWP_NOSIZE = 0x0001;
const UInt32 SWP_NOMOVE = 0x0002;
const UInt32 SWP_NOACTIVATE = 0x0010;
const UInt32 SWP_NOZORDER = 0x0004;
const int WM_ACTIVATEAPP = 0x001C;
const int WM_ACTIVATE = 0x0006;
const int WM_SETFOCUS = 0x0007;
static readonly IntPtr HWND_BOTTOM = new IntPtr(1);
const int WM_WINDOWPOSCHANGING = 0x0046;

[DllImport("user32.dll")]
static extern bool SetWindowPos(IntPtr hWnd, IntPtr hWndInsertAfter, int X,
   int Y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr DeferWindowPos(IntPtr hWinPosInfo, IntPtr hWnd,
   IntPtr hWndInsertAfter, int x, int y, int cx, int cy, uint uFlags);
[DllImport("user32.dll")]
static extern IntPtr BeginDeferWindowPos(int nNumWindows);
[DllImport("user32.dll")]
static extern bool EndDeferWindowPos(IntPtr hWinPosInfo);

private void Window_Loaded(object sender, RoutedEventArgs e)
{
    IntPtr hWnd = new WindowInteropHelper(this).Handle;
    SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);

    IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
    HwndSource src = HwndSource.FromHwnd(windowHandle);
    src.AddHook(new HwndSourceHook(WndProc));
}

private IntPtr WndProc(IntPtr hWnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
{
    if (msg == WM_SETFOCUS)
    {
        IntPtr hWnd = new WindowInteropHelper(this).Handle;
        SetWindowPos(hWnd, HWND_BOTTOM, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
        handled = true;
    }
    return IntPtr.Zero;
}

private void Window_Closing(object sender, System.ComponentModel.CancelEventArgs e)
{
    IntPtr windowHandle = (new WindowInteropHelper(this)).Handle;
    HwndSource src = HwndSource.FromHwnd(windowHandle);
    src.RemoveHook(new HwndSourceHook(this.WndProc));
}
0 голосов
/ 08 января 2010

создает подкласс формы, переопределяет функцию WndProc и перехватывает сообщения Windows, которые отвечают за ее перемещение по z-порядку при активации.

0 голосов
/ 08 января 2010

Да, функция SetWindowPo s с флагом HWND_BOTTOM должна вам помочь. Но из моего опыта: даже после вызова SetWindowPos в результате некоторых пользовательских операций ваше окно может вывести на передний план.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...