Есть такая задача — мой софт запускает чужой процесс, этот процесс очень громоздкий поэтому запуск длится долго, вдобавок он при запуске обращается к своему серверу, что тоже занимает время, при запуске этот процесс создает несколько окон, одно из которых отображается, как главное окно. Время от старта процесса и до момента когда окно процесса будет готово к работе, проходит от 30 секунд до минуты-двух.
Мне необходимо программно узнать когда процесс закончил загрузку и начал ожидание пользовательского ввода.
Процесс модифицировать нельзя, контролы основного окна судя по всему написаны на VB (имена класса окон начинается на "ThunderRT6", значение Text у окон почемуто не установлено — видимо особенность VB).
Есть мысли попробовать дождаться когда его основное окно начнет выборку сообщений в message pumping loop, но как это лучше сделать?
Как это можно сделать?? Заранее спасибо за помощь
... << RSDN@Home 1.2.0 alpha rev. 676>>
Re: Как дождаться загрузки приложения?
От:
Аноним
Дата:
21.08.08 19:17
Оценка:
Здравствуйте, _Morpheus_, Вы писали:
_M_>Есть такая задача — мой софт запускает чужой процесс, этот процесс очень громоздкий поэтому запуск длится долго, вдобавок он при запуске обращается к своему серверу, что тоже занимает время, при запуске этот процесс создает несколько окон, одно из которых отображается, как главное окно. Время от старта процесса и до момента когда окно процесса будет готово к работе, проходит от 30 секунд до минуты-двух.
_M_>Мне необходимо программно узнать когда процесс закончил загрузку и начал ожидание пользовательского ввода.
_M_>Процесс модифицировать нельзя, контролы основного окна судя по всему написаны на VB (имена класса окон начинается на "ThunderRT6", значение Text у окон почемуто не установлено — видимо особенность VB). _M_>Есть мысли попробовать дождаться когда его основное окно начнет выборку сообщений в message pumping loop, но как это лучше сделать?
_M_>Как это можно сделать?? Заранее спасибо за помощь
Здравствуйте, <Аноним>, Вы писали:
_M_>>Как это можно сделать?? Заранее спасибо за помощь
А>Повесь момент загрузки на событие
вопрос в том как определить когда этот момент произошел?
Есть идея отправить главному окну процесса какоето мусорное сообщения и дождаться когда оно будет обработано, но не уверен что это сработает, и какое сообщение выбрать, чтобы оно не влияло на работу окна...
спасибо, но к сожалению почемуто этот вызов возвращает управление раньше — еще на этапе загрузки, очевидно дочерний процесс во время загрузки делает вызовы Application.DoEvents()... Из-за которых ошибочно определяется готовность к вводу...
Здравствуйте, _Morpheus_, Вы писали:
_M_>Может есть идеи как это обойти?
Если есть знание WinAPI то:
EnumWindows(ищем главное окно — благо есть ProcessID — по нему и сравниваем)
EnumChildWindows (ищем тот EditBox в который тайпать будут — тут Spy++ тебе поможет...)
SendMessage() — какой — смотри сам WM_GETTEXT пройдет в 90% — но если не поможет — проверь фокус даного контрола — хотя есть любители после установки фокуса еще чего-то поделать — но это уже из разряда домыслов.
ждем ответ от SendMessage()
Здравствуйте, Alyas77, Вы писали:
A>Здравствуйте, _Morpheus_, Вы писали:
_M_>>Может есть идеи как это обойти? A>Если есть знание WinAPI то:
A>EnumWindows(ищем главное окно — благо есть ProcessID — по нему и сравниваем) A>EnumChildWindows (ищем тот EditBox в который тайпать будут — тут Spy++ тебе поможет...)
собственно перед тем как делать именно эти вызовы мне и нужно дождаться пока закончатся "переходные процессы" происходящие внутри запускаемой программы...
кстати с поиском окон есть проблемка — у окон почемуто не установлен текст, т.е. пусто, например имя класса основного окна "ThunderRT6MDIForm", а текста нет....
A>SendMessage() — какой — смотри сам WM_GETTEXT пройдет в 90% — но если не поможет — проверь фокус даного контрола — хотя есть любители после установки фокуса еще чего-то поделать — но это уже из разряда домыслов. A>ждем ответ от SendMessage()
A>как по другому? наверное вряд ли выйдет...
_M_>собственно перед тем как делать именно эти вызовы мне и нужно дождаться пока закончатся "переходные процессы" происходящие внутри запускаемой программы...
А что мешает энумерацию в цикле делать — пока нужное окошко(контрол) не всплывет
_M_>кстати с поиском окон есть проблемка — у окон почемуто не установлен текст, т.е. пусто, например имя класса основного окна "ThunderRT6MDIForm", а текста нет....
Ну на нет и суда нет — а оно тебе надо? задача-то дождаться загрузки.. а не текст получить А что за проблема с поиском я так и не понял. Да и зачем тебе окно — контрол "контрольный" найди на нем — как он появился и ответил — так программа и загрузилась... наверное
_M_>попробую...
Пробуй — дай знать если как что получиться — людям интересно будет...
Здравствуйте, Alyas77, Вы писали:
_M_>>собственно перед тем как делать именно эти вызовы мне и нужно дождаться пока закончатся "переходные процессы" происходящие внутри запускаемой программы... A>А что мешает энумерацию в цикле делать — пока нужное окошко(контрол) не всплывет
вопрос в том что оно может и не всплыть и тогда прога будет ждать вечно или слишком долго и зря
_M_>>кстати с поиском окон есть проблемка — у окон почемуто не установлен текст, т.е. пусто, например имя класса основного окна "ThunderRT6MDIForm", а текста нет....
A>Ну на нет и суда нет — а оно тебе надо? задача-то дождаться загрузки.. а не текст получить А что за проблема с поиском я так и не понял. Да и зачем тебе окно — контрол "контрольный" найди на нем — как он появился и ответил — так программа и загрузилась... наверное
задача в том чтобы сделать автоматическое управление программой, дело в том что система отказывается запускаться параллельно с Rational Robot, TestComplete падает с ошибкой в kernel32 через некоторое время работы с этим приложением, вот и пытаюсь сделать свою приблуду, проблемка в том чтобы определить момент когда закончится активность процесса, вызванная очередным автоматическим кликом или клавиатурным вводом. Чтобы не кликать на кнопку в то время когда процесс занят обработкой предыдущего действия...
_M_>>попробую...
A>Пробуй — дай знать если как что получиться — людям интересно будет...
попробовал — WM_GETTEXT работает, но результат не совсем тот, что я ожидал — момента когда начинается прокачка сообщений дождаться получилось, но вот дальше этот способ не работает. Т.е. при запуске процесса вылазит диалог, моя прога кликает на диалоге, и вот тут загвоздка — через некоторое время (~5-10 сек) вылазит новый диалог, который вылазит не всегда. Задача в том чтобы определить момент когда завершились внутренние процессы и открылось окно или очередной диалог...
WM_GETTEXT тут к сожалению не помогает, очевидно в промежутке между первым и вторым диалогом вызывается чтото вроде Application.DoEvents();
В результате мой метод WaitForInputIdle для окна возвращает управление раньше чем появляется диалог или окно будет готово к очередному вводу:
public class WindowItem
{
private IntPtr _hwnd; // Это хэндл на основное окно процесса
//.........
//.........
//.........public bool WaitForInputIdle(int milliseconds)
{
uint lpResult;
int result = SendMessageTimeout(_hwnd, Messages.GETTEXT, 2, new byte[256], SendMessageTimeoutFlags.SMTO_ABORTIFHUNG | SendMessageTimeoutFlags.SMTO_NORMAL, (uint)milliseconds, out lpResult);
if (result == 0) return false;
return true;
}
private enum Messages : uint
{
/// <summary>
/// This message is sent by an application to simulate the user clicking a button. This message causes the button to receive a WM_LBUTTONDOWN and a WM_LBUTTONUP message, and the button's parent window to receive a BN_CLICKED message.
/// </summary>
BM_CLICK = 0x00F5,
/// <summary>
/// An application sends a WM_GETTEXT message to copy the text that corresponds to a window into a buffer provided by the caller.
/// </summary>
GETTEXT = 0x000D,
/// <summary>
/// An application sends a WM_GETTEXTLENGTH message to determine the length, in characters, of the text associated with a window.
/// </summary>
GETTEXTLENGTH = 0x000E,
}
[System.Runtime.InteropServices.DllImport("user32.dll", CharSet = System.Runtime.InteropServices.CharSet.Auto, SetLastError = true)]
private static extern int SendMessageTimeout(IntPtr hWnd, Messages Msg, int wParam, byte[] lParam, SendMessageTimeoutFlags fuFlags, uint uTimeout, out uint lpdwResult);
[Flags]
private enum SendMessageTimeoutFlags : uint
{
/// <summary>
/// The calling thread is not prevented from processing other requests while waiting for the function to return.
/// </summary>
SMTO_NORMAL = 0x0000,
/// <summary>
/// Prevents the calling thread from processing any other requests until the function returns.
/// </summary>
SMTO_BLOCK = 0x0001,
/// <summary>
/// Returns without waiting for the time-out period to elapse if the receiving thread appears to not respond or "hangs."
/// </summary>
SMTO_ABORTIFHUNG = 0x0002,
/// <summary>
/// Microsoft Windows 2000/Windows XP: Does not return when the time-out period elapses if the receiving thread stops responding.
/// </summary>
SMTO_NOTIMEOUTIFNOTHUNG = 0x0008
}
}
вот как я его использую:
local exitButton;
while(true) do
-- функция checkDialogs проверяет наличие открытых диалогов и если находит известный диалог, обрабатывает его
local dlg = checkDialogs(proc);
wait(1000);
mainWnd.WaitForInputIdle(60000);
exitButton = FindChildWindowByText(mainWnd, "Exit");
if(dlg==nil and exitButton ~= nil) then
mainWnd.WaitForInputIdle(60000);
break;
end
end
exitButton:ClickLeft();
Здравствуйте, Константин Л., Вы писали:
КЛ>как тут уже советовали, ищи окно по имени класса FindWindow/EnumChildWindows
вопрос не в этом. Попробую описать более детально суть задачи.
Есть hwnd окна. Задача состоит в том чтобы выполнить такую последовательность действий:
1. Дождаться когда окно завершит инициализацию и перейдет в режим ожидания ввода от пользователя
2. Программно кликнуть по этому окну
3. Дождаться когда окно обработает клик (этот процесс занимает некоторое неопределенное время в диапазоне от 1 сек до 2 минут)
4. Проверить не открылся ли во время обработки клика диалог, если открылся, то кликнуть по нужной кнопке в диалоге и перейти к п.3
5. программно кликнуть на кнопку "Exit" в окне
6. Дождаться когда окно закроется.
Нет вопроса в том чтобы найти HWND нужного окна и сделать программный клик.
Вопрос состоит в том, как определить, что окно закончило обработку предыдущего события и находится в ожидании очередного действия пользователя?
Здравствуйте, _Morpheus_, Вы писали:
_M_>Здравствуйте, Константин Л., Вы писали:
КЛ>>как тут уже советовали, ищи окно по имени класса FindWindow/EnumChildWindows
_M_>вопрос не в этом. Попробую описать более детально суть задачи.
_M_>Есть hwnd окна. Задача состоит в том чтобы выполнить такую последовательность действий:
_M_>1. Дождаться когда окно завершит инициализацию и перейдет в режим ожидания ввода от пользователя
что за инициализация? после того, как окно создано (те есть HWND), оно инициализировано.
_M_>2. Программно кликнуть по этому окну
нет проблем
_M_>3. Дождаться когда окно обработает клик (этот процесс занимает некоторое неопределенное время в диапазоне от 1 сек до 2 минут)
засабкласить?
_M_>4. Проверить не открылся ли во время обработки клика диалог, если открылся, то кликнуть по нужной кнопке в диалоге и перейти к п.3
хук
_M_>5. программно кликнуть на кнопку "Exit" в окне
нет проблем
_M_>6. Дождаться когда окно закроется.
тоже
_M_>Нет вопроса в том чтобы найти HWND нужного окна и сделать программный клик. _M_>Вопрос состоит в том, как определить, что окно закончило обработку предыдущего события и находится в ожидании очередного действия пользователя?
Здравствуйте, Константин Л., Вы писали:
_M_>>1. Дождаться когда окно завершит инициализацию и перейдет в режим ожидания ввода от пользователя КЛ>что за инициализация? после того, как окно создано (те есть HWND), оно инициализировано.
инициализация это время которое проходит от момента когда создано окно (т.е. появился HWND) и до момента когда поток начнет прокачку сообщений. В это время поднимаются сервисы, создаются громоздкие инстансы объектов и т.п., короче это время уходит на разворачивание приложения и потому занимает некоторое, в данном случае немалое, время...
_M_>>2. Программно кликнуть по этому окну
КЛ>нет проблем
_M_>>3. Дождаться когда окно обработает клик (этот процесс занимает некоторое неопределенное время в диапазоне от 1 сек до 2 минут)
КЛ>засабкласить?
что значит засабкласить? можно поподробнее?
есть hwnd, как дождаться когда поток создавший окно с этим hwnd начнет прокачку оконных сообщений, обработает текущие сообщения и перейдет в режим ожидания нового сообщения?
_M_>>4. Проверить не открылся ли во время обработки клика диалог, если открылся, то кликнуть по нужной кнопке в диалоге и перейти к п.3
КЛ>хук
не знаю зачем тут хук? я проверяю наличие диалогов без хуков — это проще и позволяет реализовать линейное исполнение алгоритма
_M_>>6. Дождаться когда окно закроется. КЛ>тоже
вот об этом тоже хотелось бы спросить, как правильнее дождаться закрытия окна связанного с заданным HWND?
Здравствуйте, _Morpheus_, Вы писали:
_M_>Здравствуйте, Константин Л., Вы писали:
_M_>>>1. Дождаться когда окно завершит инициализацию и перейдет в режим ожидания ввода от пользователя КЛ>>что за инициализация? после того, как окно создано (те есть HWND), оно инициализировано.
_M_>инициализация это время которое проходит от момента когда создано окно (т.е. появился HWND) и до момента когда поток начнет прокачку сообщений. В это время поднимаются сервисы, создаются громоздкие инстансы объектов и т.п., короче это время уходит на разворачивание приложения и потому занимает некоторое, в данном случае немалое, время...
Что там хоть за SomeLongOperation? Ивенты? Мьютексы? Окна? Мож есть к чему привязаться?
_M_>>>2. Программно кликнуть по этому окну
КЛ>>нет проблем
_M_>>>3. Дождаться когда окно обработает клик (этот процесс занимает некоторое неопределенное время в диапазоне от 1 сек до 2 минут)
КЛ>>засабкласить?
_M_>что значит засабкласить? можно поподробнее?
это для того, чтобы точно знать когда клик обработался. Но этот момент можно поскипать
_M_>есть hwnd, как дождаться когда поток создавший окно с этим hwnd начнет прокачку оконных сообщений, обработает текущие сообщения и перейдет в режим ожидания нового сообщения?
надо подумать в сторону хука WH_GETMESSAGE, но пока ничего в голову не приходит
_M_>>>4. Проверить не открылся ли во время обработки клика диалог, если открылся, то кликнуть по нужной кнопке в диалоге и перейти к п.3
КЛ>>хук
_M_>не знаю зачем тут хук? я проверяю наличие диалогов без хуков — это проще и позволяет реализовать линейное исполнение алгоритма
как проверяешь? просто с хуком WH_CBT (вроде так) проще всего отслеживать появление окон
_M_>>>6. Дождаться когда окно закроется. КЛ>>тоже
_M_>вот об этом тоже хотелось бы спросить, как правильнее дождаться закрытия окна связанного с заданным HWND?
Ну дык вариантов масса. От сабклассинга окна до хуков.
Ты как-нить в этот процесс внедриться можешь? Исходники есть?
КЛ>Кхм... КЛ>Что там хоть за SomeLongOperation? Ивенты? Мьютексы? Окна? Мож есть к чему привязаться?
да не к чему особо, там идет подключение к сервисам, запускаются сетевые сервисы, выкачивается из сети необходимая для запуска инфа, подключение к бд и чтение данных и т.п., плюс окна создаются, окон очень много, порядка нескольких тысяч
_M_>>что значит засабкласить? можно поподробнее?
КЛ>это для того, чтобы точно знать когда клик обработался. Но этот момент можно поскипать
и всетаки что значит "засабкласить"? Мне какраз и нужно узнать когда клик обработался...
_M_>>не знаю зачем тут хук? я проверяю наличие диалогов без хуков — это проще и позволяет реализовать линейное исполнение алгоритма
КЛ>как проверяешь? просто с хуком WH_CBT (вроде так) проще всего отслеживать появление окон
в моменты когда могут появиться диалоги, периодически вызываю функцию checkDialogs, которая проверяет висят ли открытыми окна которые ей известны, если висят, то кликает заданную в правилах для этого диалога кнопку.
КЛ>Ты как-нить в этот процесс внедриться можешь? Исходники есть?
исходников нет, ReadProcessMemory/WriteProcessMemory юзать можно
КЛ>>Кхм... КЛ>>Что там хоть за SomeLongOperation? Ивенты? Мьютексы? Окна? Мож есть к чему привязаться?
_M_>да не к чему особо, там идет подключение к сервисам, запускаются сетевые сервисы, выкачивается из сети необходимая для запуска инфа, подключение к бд и чтение данных и т.п., плюс окна создаются, окон очень много, порядка нескольких тысяч
мдя... ну может там какое окно "Loading..." исчезает? Смотри тогда асм
_M_>>>что значит засабкласить? можно поподробнее?
КЛ>>это для того, чтобы точно знать когда клик обработался. Но этот момент можно поскипать
_M_>и всетаки что значит "засабкласить"? Мне какраз и нужно узнать когда клик обработался...
Subclassing общая инфа как это сделать на .net
_M_>>>не знаю зачем тут хук? я проверяю наличие диалогов без хуков — это проще и позволяет реализовать линейное исполнение алгоритма
КЛ>>как проверяешь? просто с хуком WH_CBT (вроде так) проще всего отслеживать появление окон
_M_>в моменты когда могут появиться диалоги, периодически вызываю функцию checkDialogs, которая проверяет висят ли открытыми окна которые ей известны, если висят, то кликает заданную в правилах для этого диалога кнопку.
и где-же тут линейное исполнение?
КЛ>>Ты как-нить в этот процесс внедриться можешь? Исходники есть?
_M_>исходников нет, ReadProcessMemory/WriteProcessMemory юзать можно
_M_>попробовал — WM_GETTEXT работает, но результат не совсем тот, что я ожидал — момента когда начинается _M_>WM_GETTEXT тут к сожалению не помогает, очевидно в промежутке между первым и вторым диалогом вызывается чтото вроде Application.DoEvents();
А ты зачем его в основное окно пинаешь? ты же знаешь и кнопку и возможный диалог с ошибкой — их ищи — и их проверяй на присутиствие и наличие на экране — как обявились — пни для приличия — и считай что все прогрузилось.
и искать по тексту... как-то неприлично — еще класс, паренты(глубина иерархии), размеры есть — они как-то более точно укажут на нужный контрол