|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
|
#1
|
|||
|
|||
подскажите как воздействовать на окна другой программы
Вот с чем надо помочь
1. Надо чтобы при нажатии button на моей проге открывались несколько других(одного типа, но разные копии) программы и при этом я знал хэндлы этих копий. я их открываю через createprocess, но так я получаю хэндл процесса а не формы. Как получить из этого хэндла хэндл формы? И как зная хэндл формы узнать какие окна в ней есть и какие у них хэндлы? 2. Чтобы при изменении данных в некоторых окнах одной из этих копий, посредством моей проги передавались измененные данные в те же окна других копий поочередно. проблема в том что я не знаю класс этой чужой программы , ну узнать - узнаю, только заголвки могут быть разные, потому что моя прога может стоять у других узеров. По-этому функция FindWindow в данном случае не помошник. Или посоветуйте книжечку где про это написано. Всем заранее спасибо. Последний раз редактировалось одинадцатый, 16.04.2009 в 14:05. |
#2
|
|||
|
|||
Пока что только по поводу первого вопроса.
Вот вам мой вариант реализации... Искомые дескрипторы запущенных окон записываются в массив Handles. Код:
var Form1: TForm1; Handles: array[0..3] of HWND; // например, запускаются четыре копии приложения... function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; ........................ { Эта callback-функция вызывается при переборе окон, принадлежащих нити, запускаемой через CreateProcess } function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; begin Result := not IsWindow(hndl); if not Result then Handles[lprm] := hndl; end; // нажатие на кнопку вызывает запуск четырех копий Блокнота =) procedure TForm1.Button1Click(Sender: TObject); var WinDirPath, NPadPath: PChar; i: Byte; _si: STARTUPINFO; _pi: PROCESS_INFORMATION; begin GetMem(WinDirPath, MAX_PATH); GetMem(NPadPath, MAX_PATH); try GetWindowsDirectory(WinDirPath, MAX_PATH); StrPCopy(NPadPath, StrPas(WinDirPath) + '\Notepad.exe'); for i := 0 to 3 do begin FillChar(_si, SizeOf(_si), 0); _si.cb := SizeOf(_si); _si.dwFlags := STARTF_USESHOWWINDOW; _si.wShowWindow := SW_SHOWNORMAL; CreateProcess(NPadPath, nil, nil, nil, False, NORMAL_PRIORITY_CLASS, nil, WinDirPath, _si, _pi); Sleep(1000); { какая-то задержка принципиально необходима перед тем как начинать перебор! Нужно дать приложению время запуститься. Возможно, что секунда — это даже много, но смотрите сами. } EnumThreadWindows(_pi.dwThreadId, @EnumThrWndProc, i); end; finally FreeMem(WinDirPath, MAX_PATH); FreeMem(NPadPath, MAX_PATH); end; end; { это просто проверка того, что получены дескрипторы всех запущенных окон. Например, изменим их заголовки =) } procedure TForm1.Button2Click(Sender: TObject); const WndTexts: array[0..3] of string = ('Wnd#1', 'Wnd#2', 'Wnd#3', 'Wnd#4'); var i: Byte; begin for i := 0 to 3 do SendMessage(Handles[i], WM_SETTEXT, 0, Integer(@WndTexts[i][1])); end; Насчет второго вопроса пока лень думать =) Может, у кого-нибудь другого будет желание... Последний раз редактировалось Nyctos Kasignete, 16.04.2009 в 23:44. |
#3
|
|||
|
|||
Что значит callback-функция?
что такое @EnumThrWndProc в строке EnumThreadWindows(_pi.dwThreadId, @EnumThrWndProc, i);? И что означает @? По поводу задержки которая должна быть: нельзя ли как нибудь сделать чоб моя прога при этом не висла? Попробовал ваш метод - работает, но возникло 2 проблемы: 1. При запуске той программы неск-ко копий которой я хочу открыть(кстати из разных папок и по большому счету они не связаны) появляется главное окно программы и еще окошечко(не модальное) так вот ваш метод мне выдает хэндл этого окошечка, а надо главной формы. 2. Как получить хэндл если эта программа уже запущена, не зная заголовка? |
#4
|
|||
|
|||
одинадцатый, объяснять подробно не буду: все ответы, как я уже говорила, можно найти в Windows SDK и в книжечках.
Поясняю коротко (по сути перевожу то, что написано в справке): функция EnumThreadWindows перебирает все окна, ассоциированные с потоком, дескриптор которого ей передается в первом параметре (у нас _pi.dwThreadId). Второй параметр — указатель на ту самую callback-функцию, которую мы объявили как EnumThrWndProc (символ @ как раз и означает, что это указатель). Функция EnumThreadWindows вызывает эту callback-функцию всякий раз, когда находит очередное окно, принадлежащее данному потоку. В свою очередь callback-функция получает дескриптор найденного окна... Последний параметр можно использовать по своему усмотрению, чем я и воспользовалась (передаю текущий индекс массива Handles). Цитата:
Если процедура Sleep, которую я использовала, слишком загружает вашу программу, есть альтернативный вариант создания задержки: Код:
Hndl := CreateEvent(nil, False, False, nil); WaitForSingleObject(Hndl, 1000); CloseHandle(Hndl); Цитата:
Последний раз редактировалось Nyctos Kasignete, 17.04.2009 в 20:35. |
#5
|
|||
|
|||
Цитата:
Делаем'c разные игры. Искать на glscene.ru |
#6
|
|||
|
|||
Есть на самом деле еще одно обстоятельство, о котором я не сказала. Тот код, который выше, я писала в предположении, что у запускаемого приложения будет единственное окно. А именно, функция EnumThrWndProc возвратит False сразу как только найдет первое окно, принадлежащее нити.
Цитата:
__________________________________ Сегодня между делом ломала голову над вышеобозначенным вопросом. Уж не знаю, продолжает ли работать над проблемой сам автор темы... В общем, пришла к выводу, что решение задачи в общем виде (т.е. для вообще произвольного многооконного приложения, копии которого мы запускаем) найти у меня не получается. =( Вся проблема состоит в том, чтобы по заданному набору дескрипторов, соответствующих окнам многооконного приложения, выбрать тот единственный, который принадлежит главному окну. Не понятно, по какому критерию можно опознать, что данное окно — главное. Хотела найти лазейку через стили (и расширенные стили) окна, считывая их функцией GetWindowLong. Понятное дело, можно отсеять те окна, которые, например, обладают расшренным стилем WS_EX_TOOLWINDOW. Но на самом деле не исключены такие окна в многооконном приложении, которые имеют в точности тот же стиль, что и главное... Так что, наверное, не выход. Была идея воспользоваться тем обстоятельством, что обычно приложение создает лишь одну кнопку на панели задач, соответствующую главному окну, а дополнительные окна не светятся на панели задач. Здесь написано, как определить, какие окна видятся на панели задач. Но проблема в том, что и для главного окна многооконного приложения не выполняется условие GetWindowLong(Wnd, GWL_HWNDPARENT) = 0, т.е. главное, как и остальные окна, имеет родительское окно программы, общее для всех окон программы. Это не то окно, которое нужно нам. Короче, уже надоело писать. X( Скажу только, что на данный момент вижу один способ дальнейших действий. Все-таки надо забрать дескрипторы всех окон нити, а потом на основании каких-то отличительных признаков главного окна той самой, конкретной программы выбрать из имеющихся окон нужное. Ну ведь чем-то отличается все-таки главное окно от остальных?? Код немного переделала. Теперь не нужно каждый раз перед запуском очередной копии делать задержку. Делается одна-единственная общая задержка после того как все нужные копии программы отправлены на запуск с помощью CreateProcess. После этого проходимся по всем запущенным нитям и собираем дескрипторы окон. Код:
type THandles = array of HWND; { новый тип — структура, в которой хранится идентификатор процесса и набор дескрипторов ассоциированных с ним окон} TThreadIdHandles = record ThreadId: Cardinal; Handles: THandles; end; TForm1 = class(TForm) StartProgramBtn: TButton; procedure StartProgramBtnClick(Sender: TObject); private { Private declarations } procedure GetAllWndHandlesFromThreads; // новая общая процедура перебора public { Public declarations } end; var Form1: TForm1; ThreadIdHandles: array[0..3] of TThreadIdHandles; // допустим, будем запускать четыре копии программы function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; implementation {$R *.dfm} function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; var ToolWndStyle: Integer; PrevHandlesLen: Integer; begin Result := True; ToolWndStyle := GetWindowLong(hndl, GWL_EXSTYLE) and WS_EX_TOOLWINDOW; { убираем ненужные окна, имеющие стиль WS_EX_TOOLWINDOW } if ToolWndStyle = 0 then with ThreadIdHandles[lprm] do begin PrevHandlesLen := Length(Handles); SetLength(Handles, PrevHandlesLen + 1); Handles[PrevHandlesLen] := hndl; end; end; procedure TForm1.StartProgramBtnClick(Sender: TObject); const WinDirPath = 'D:\Path_to_program_directory'; NPadPath = 'D:\Path_to_program_directory\Program.exe'; var i: Byte; _si: STARTUPINFO; _pi: PROCESS_INFORMATION; Evnt: THandle; begin for i := 0 to 3 do begin ThreadIdHandles[i].Handles := nil; FillChar(_si, SizeOf(_si), 0); _si.cb := SizeOf(_si); _si.dwFlags := STARTF_USESHOWWINDOW; _si.wShowWindow := SW_SHOWNORMAL; CreateProcess(@NPadPath[1], nil, nil, nil, False, NORMAL_PRIORITY_CLASS, nil, @WinDirPath[1], _si, _pi); ThreadIdHandles[i].ThreadId := _pi.dwThreadId; end; // делаем лишь однократную задержку перед вызовом общей процедуры перебора Evnt := CreateEvent(nil, False, False, nil); WaitForSingleObject(Evnt, 1500); CloseHandle(Evnt); Form1.GetAllWndHandlesFromThreads(); end; procedure TForm1.GetAllWndHandlesFromThreads; var i: Byte; begin for i := 0 to 3 do EnumThreadWindows(ThreadIdHandles[i].ThreadId, @EnumThrWndProc, i); end; Последний раз редактировалось Nyctos Kasignete, 18.04.2009 в 23:09. |
#7
|
|||
|
|||
По поводу
Цитата:
А вообще про SDK я не знал, но все равно теперь вряд ли быстро найду решение, потому что на англ. да и толком не знаю что искать... Есть некоторые изменения: допустим что при запуске пары копий нужной мне проги открывается лишь главная форма. значит получить дескрипторы всех окон на ней я могу так же через функцию EnumThrWndProc правильно? Я попробовал так: function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; var ToolWndStyle: Integer; PrevHandlesLen: Integer; begin getwindowtext(hndl,w,200); form1.Memo1.Lines.Add(w); Result := True; ... Но в мемо у меня оказалось только 7 пустых строк, caption этой самой формы и еще 19 пустых строк. В чем дело? И еще я не совсем понял что такое callback функция и не ясно почему в строке EnumThreadWindows(_pi.dwThreadId, @EnumThrWndProc, i); мы не передаем параметры для ф-ии EnumThrWndProc, их же там нет поумолчанию. Последний раз редактировалось одинадцатый, 20.04.2009 в 12:19. |
#8
|
|||
|
|||
одинадцатый, я в процессе отладки заголовки окон считывала немного по-другому. Поскольку имеется постоянно доступный массив, где хранятся дескрипторы всех найденных окон, то необязательно загромождать функцию EnumThrWndProc. Просто делала Memo и еще одну кнопку с вот таким обработчиком события OnClick:
Код:
procedure TForm1.GetTextBtnClick(Sender: TObject); var i: Byte; j: Integer; wndtxt: PChar; begin GetMem(wndtxt, 200); try Memo1.Lines.Clear; for i := 0 to 3 do for j := 0 to High(ThreadIdHandles[i].Handles) do begin GetWindowText(ThreadIdHandles[i].Handles[j], wndtxt, 200); Memo1.Lines.Add(StrPas(wndtxt)); end; finally FreeMem(wndtxt, 200); end; end;
Цитата:
Цитата:
Последний раз редактировалось Nyctos Kasignete, 20.04.2009 в 14:56. |