Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Отловить завершение спрятанного окна  (Прочитано 14681 раз)
0 Пользователей и 4 Гостей смотрят эту тему.
SCRIBE
Гость
« : 07-01-2017 16:26 » 

Привет.

Встала такая проблема, надо при Destroy чегой выполнить, когда программу убивают через Task Manager.
Все бы хорошо, Destroy срабатывает когда программа видна, т.е. visible = true.
А вот когда ее спрятать (visible = false), при зевершении оной через Task Manager, это сообщение не отлавливается, ни Close, ни CloseQuery.
Как сделать так, чтобы окно продолжало нормально ловить сообщения?

Спасибо.
Записан
RXL
Технический
Администратор

Offline Offline
Пол: Мужской

WWW
« Ответ #1 : 07-01-2017 17:25 » new

http://stackoverflow.com/questions/18454763/how-to-handle-end-task-from-windows-task-manager-on-a-background-process
Никак.

Рассмотри еще вариант с иконкой в system tray. Может туда придет сообщение.
Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
SCRIBE
Гость
« Ответ #2 : 07-01-2017 18:34 » 

Да не получается, когда прячешь окно, оно сразу попадает в список фоновых процесов (типа не имеет окна), при этом трей отображается.
Никаких сообщений не приходит=(
Записан
SCRIBE
Гость
« Ответ #3 : 07-01-2017 19:01 » 

Решение придумал, не прятать сильно окно Отлично

В коде проекта:
Код: (Delphi)
Application.MainFormOnTaskBar:= false;

Процедуры для отображения/показа что-то типа:
Код: (Delphi)
procedure TfmMain.FormHide;
begin
    FHiding:= true;
    FBaseLeft:= Left;
    FBaseTop:= Top;
    Left:= -99999;
    Top:= -99999;
    ShowWindow(Application.Handle, SW_HIDE);
end;

procedure TfmMain.FormShow;
begin
    if FHiding then
    begin
      Left:= FBaseLeft;
      Top:= FBaseTop;
      FHiding:= false;
      ShowWindow(Application.Handle, SW_SHOW);
    end;
end;
Записан
zubr
Гость
« Ответ #4 : 07-01-2017 21:15 » 

Пустое это. Все эти примочки ничего не дадут, если удалять сам процесс из того же таскменеджера.
Записан
SCRIBE
Гость
« Ответ #5 : 07-01-2017 21:22 » 

Ну я ж тестирую, сообщение обрабатывается, если убить через Task Manager (Win10x64)
В новых версиях менеджер сначала пробует отсылать WM_CLOSE для нормального завершения приложения, но никак не реагирует если приложение не хочет закрыватся, он его все равно убивает. Посмотри например как убивается блокнот с несохраненным текстом, вылетает окошко с запросом, но он закрывается, т.е. сообщение обработалось, а мне больше и не надо.
« Последнее редактирование: 07-01-2017 21:27 от ..::SCRIBE::.. » Записан
zubr
Гость
« Ответ #6 : 08-01-2017 06:31 » 

..::SCRIBE::.., ты удаляешь приложение из таск менеджера, а не процесс.
В таск менеджере есть вкладка приложения, а есть вкладка процессы по 7-ку включительно, в 8-10 процессы и детали соответственно. Так вот там где у тебя вкладка детали - список процессов, а не приложений. Найди там свой процесс и убей его - никакие твои сообщения естественно отлавливаться не будут.
Записан
SCRIBE
Гость
« Ответ #7 : 08-01-2017 14:51 » 

Да, таким способом убивается без них, придется демон писать какой-то...

А нельзя порожденный мною процесс, забрать с собою?) В смысле при снятии главного процесса, снимался и открытый процесс этой программой?

Нашел: http://stackoverflow.com/questions/8822152/how-to-create-a-child-process-depending-on-its-parent
« Последнее редактирование: 08-01-2017 15:27 от ..::SCRIBE::.. » Записан
Aether
Специалист

ru
Offline Offline
Пол: Мужской

« Ответ #8 : 08-01-2017 15:36 » 

winuser.h
Код:
...
#define WM_NULL 0x0000
#define WM_CREATE 0x0001
#define WM_DESTROY 0x0002
...
#define WM_CLOSE 0x0010
...
#define WM_QUIT 0x0012
...
https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms632617(v=vs.85).aspx
Цитата
Remarks

An application can prompt the user for confirmation, prior to destroying a window, by processing the WM_CLOSE message and calling the DestroyWindow function only if the user confirms the choice.
То есть, как понимаю - это штатная ситуация - перехватить это сообщение, если нужно что-то выполнить перед завершением. Вот только рассчитано, как понял не для отрубания процесса, а для нажатия на "крестик", действительно ли Task Manager посылает WM_CLOSE перед уничтожением процесса? И какова последовательность?

По идее, но может и не прав:
цепь примерно такая WM_CLOSE -> WM_DESTROY -> WM_QUIT.
Стандартное приложение заканчивает работу через PostQuitMessage.

Может попробовать обработать WM_DESTROY?
Записан
SCRIBE
Гость
« Ответ #9 : 08-01-2017 15:41 » 

Снятие процеса через Диспетчер задач->Подробности->Снять задачу не вызывает никаких сообщений, к сожалению.
Через Диспетчер задач->Процесы, вызывает все стандартные сообщения для завершения, но никак не реагирует если приложение не хочет закрыватся (пример: OnCloseQuery), но это появилось как я где-то прочитал в Windows Vista+.
Записан
Aether
Специалист

ru
Offline Offline
Пол: Мужской

« Ответ #10 : 08-01-2017 16:29 » 

Попробовал, ради интереса, доработать одной строкой пример:
Код: (C)
//+---------------------------------------------------------------------------
//
//  HELLO_WIN.C - Windows GUI 'Hello World!' Example
//
//+---------------------------------------------------------------------------

#include <windows.h>

#define APPNAME "HELLO_WIN"

char szAppName[] = APPNAME; // The name of this application
char szTitle[]   = APPNAME; // The title bar text
const char *pWindowText;

void CenterWindow(HWND hWnd);

//+---------------------------------------------------------------------------
//
//  Function:   WndProc
//
//  Synopsis:   very unusual type of function - gets called by system to
//              process windows messages.
//
//  Arguments:  same as always.
//----------------------------------------------------------------------------

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message) {

        // ----------------------- first and last
        case WM_CREATE:
            CenterWindow(hwnd);
            break;

        case WM_DESTROY:
            MessageBoxA(hwnd, "Destroy!!!", "Destroy!!!", MB_OK);
            PostQuitMessage(0);
            break;

        // ----------------------- get out of it...
        case WM_RBUTTONUP:
            DestroyWindow(hwnd);
            break;

        case WM_KEYDOWN:
            if (VK_ESCAPE == wParam)
                DestroyWindow(hwnd);
            break;

        // ----------------------- display our minimal info
        case WM_PAINT:
        {
            PAINTSTRUCT ps;
            HDC         hdc;
            RECT        rc;
            hdc = BeginPaint(hwnd, &ps);

            GetClientRect(hwnd, &rc);
            SetTextColor(hdc, RGB(240,240,96));
            SetBkMode(hdc, TRANSPARENT);
            DrawText(hdc, pWindowText, -1, &rc, DT_CENTER|DT_SINGLELINE|DT_VCENTER);

            EndPaint(hwnd, &ps);
            break;
        }

        // ----------------------- let windows do all other stuff
        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

//+---------------------------------------------------------------------------
//
//  Function:   WinMain
//
//  Synopsis:   standard entrypoint for GUI Win32 apps
//
//----------------------------------------------------------------------------
int APIENTRY WinMain(
        HINSTANCE hInstance,
        HINSTANCE hPrevInstance,
        LPSTR lpCmdLine,
        int nCmdShow
        )
{
    MSG msg;
    WNDCLASS wc;
    HWND hwnd;

    pWindowText = lpCmdLine[0] ? lpCmdLine : "Hello Windows!";

    // Fill in window class structure with parameters that describe
    // the main window.

    ZeroMemory(&wc, sizeof wc);
    wc.hInstance     = hInstance;
    wc.lpszClassName = szAppName;
    wc.lpfnWndProc   = (WNDPROC)WndProc;
    wc.style         = CS_DBLCLKS|CS_VREDRAW|CS_HREDRAW;
    wc.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor       = LoadCursor(NULL, IDC_ARROW);

    if (FALSE == RegisterClass(&wc))
        return 0;

    // create the browser
    hwnd = CreateWindow(
        szAppName,
        szTitle,
        WS_OVERLAPPEDWINDOW|WS_VISIBLE,
        CW_USEDEFAULT,
        CW_USEDEFAULT,
        360,//CW_USEDEFAULT,
        240,//CW_USEDEFAULT,
        0,
        0,
        hInstance,
        0);

    if (NULL == hwnd)
        return 0;

    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return msg.wParam;
}

//+---------------------------------------------------------------------------

//+---------------------------------------------------------------------------

void CenterWindow(HWND hwnd_self)
{
    HWND hwnd_parent;
    RECT rw_self, rc_parent, rw_parent;
    int xpos, ypos;

    hwnd_parent = GetParent(hwnd_self);
    if (NULL == hwnd_parent)
        hwnd_parent = GetDesktopWindow();

    GetWindowRect(hwnd_parent, &rw_parent);
    GetClientRect(hwnd_parent, &rc_parent);
    GetWindowRect(hwnd_self, &rw_self);

    xpos = rw_parent.left + (rc_parent.right + rw_self.left - rw_self.right) / 2;
    ypos = rw_parent.top + (rc_parent.bottom + rw_self.top - rw_self.bottom) / 2;

    SetWindowPos(
        hwnd_self, NULL,
        xpos, ypos, 0, 0,
        SWP_NOSIZE|SWP_NOZORDER|SWP_NOACTIVATE
        );
}

//+---------------------------------------------------------------------------
Так вот, дописал в обработчик WM_DESTROY MessageBoxA. Запустил в Win10, итог:
1) Закрываю крестиком, убирает окно, спрашивает подтверждение, закрывается совсем.
2) Закрываю из Task Manager, как приложение, окно активно: убирает окно, выдаёт подтверждение, но не ждёт, а сразу же далее всё закрывает.
3) Закрываю из Task Manager, как приложение, окно не активно - свёрнуто: выдаёт подтверждение, но не ждёт, а сразу же далее всё закрывает.

Скорее всего, возможно, есть какое-то специальное сообщение, чтобы уведомить систему о том, что нужно подождать... Хотя по мне это не правильно, если человек отрубает приложение через менеджер и у него есть права на это, то приложение должно быть уничтожено мгновенно и безоговорочно.
Записан
SCRIBE
Гость
« Ответ #11 : 08-01-2017 16:45 » 

Я имел ввиду не свернуто, а Visible = false, тогда оно не срабатывает, т.е. Менеджер думает что это фоновый процесс, и не отсылает ничего.
Записан
Aether
Специалист

ru
Offline Offline
Пол: Мужской

« Ответ #12 : 08-01-2017 17:18 » 

Я имел ввиду не свернуто, а Visible = false, тогда оно не срабатывает, т.е. Менеджер думает что это фоновый процесс, и не отсылает ничего.
Не знаю, что под этим подразумевается.

Но:
https://msdn.microsoft.com/ru-ru/library/windows/desktop/ms644936(v=vs.85).aspx
Цитата
If hWnd is NULL, GetMessage retrieves messages for any window that belongs to the current thread, and any messages on the current thread's message queue whose hwnd value is NULL (see the MSG structure). Therefore if hWnd is NULL, both window messages and thread messages are processed.
Код: (C)
    // Main message loop:
    while (GetMessage(&msg, NULL, 0, 0) > 0) {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

Вполне возможно, что по умолчанию обрабатываются сообщения окна, и когда его нет, то и сообщений нет... А сообщения потока функции окна не ловят. Может быть такой нюанс?
Записан
SCRIBE
Гость
« Ответ #13 : 08-01-2017 17:44 » 

Не знаю откуда этот пример, но мы в разделе Делфи Улыбаюсь
А вообще да, на "окна нет", и "сообщений нет"=)
В любом случае задача уже решена, мне надо было просто убивать порожденный процесс при завершении(снятии через диспетчер) главного процесса.

Ох, Си, сразу не увидел=)

Всем спасибо за помощь. Скромно так...
« Последнее редактирование: 08-01-2017 17:46 от ..::SCRIBE::.. » Записан
zubr
Гость
« Ответ #14 : 08-01-2017 18:29 » 

Как вариант, чтобы отследить, что твой процесс убивают, можно в своем процессе сделать цикл, который будет через небольшой интервал посылать глобальные сообщения, или устанавливать(сбрасывать) евенты, или писать что то в глобальную память, или в пайп и т. д. - вариантов много. Поставить глобальный хук, в теле которого отслеживать цикл процесса, как только цикл прекратился - значит наш процесс убили. Ну это в более сложном случае, чем закрывать дочерний процесс, при убийстве родительского.
Записан
SCRIBE
Гость
« Ответ #15 : 08-01-2017 18:53 » 

Антивирусы не любят хуки))
Записан
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines