|
|
Регистрация | << Правила форума >> | 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. |
#9
|
|||
|
|||
Цитата:
Что-то не могу ни как разобраться. Я ввел попробовал вот так: Код:
Handles: array[0..0] of HWND; // например, запускаются четыре копии приложения... function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; implementation {$R *.dfm} function EnumThrWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; var w:array[0..200] of char; begin getwindowtext(hndl,w,200); form1.Memo1.Lines.Add(w); Result := not IsWindow(hndl); if not Result then begin Handles[lprm] := hndl; end; end; function EnumThrWndProc2(hndl: HWND; lprm: LPARAM): BOOL; stdcall; var w:array[0..200] of char; begin getwindowtext(hndl,w,200); form1.Memo1.Lines.Add(w); Result := not IsWindow(hndl); if not Result then begin Handles[lprm] := hndl; end; end; procedure TForm1.Button1Click(Sender: TObject); var WinDirPath, NPadPath: PChar; i: Byte; _si: STARTUPINFO; _pi: PROCESS_INFORMATION; begin try for i := 0 to 0 do begin FillChar(_si, SizeOf(_si), 0); _si.cb := SizeOf(_si); _si.dwFlags := STARTF_USESHOWWINDOW; _si.wShowWindow := SW_SHOWNORMAL; CreateProcess('g:\programs\forex\mt4\terminal.exe', nil, nil, nil, False, NORMAL_PRIORITY_CLASS, nil, nil, _si, _pi); Sleep(5000); EnumThreadWindows(_pi.dwThreadId, @EnumThrWndProc, i); EnumChildWindows(Handles[0], @EnumThrWndProc2,i); end; finally end; end; и в мемо оказались только caption формы и пустая строка, хотя через WinSight там видно что есть еще неск-ко Label, кнопок и т.д. Подскажите пожалуйста что не так. И еще такая проблема: если я открываю свою прогу через ярлык(параметров ни каких не прописано) то программа запускается только с одним, главным окном, а если через свою прогу, через createprocess, прога уже открывает 2 окна: главная форма и окошко для авторизации. Почему? Последний раз редактировалось Admin, 21.04.2009 в 12:48. |
#10
|
|||
|
|||
одинадцатый, функцию EnumChildWindows необходимо вызывать из самой callback функции EnumThrWndProc! Я теперь уже забросила первый вариант, который вы привели. Использую тот, который в посте #6. Поэтому буду показывать на примере именно его.
Да простят меня модераторы, но приведу код полностью (в который раз!), чтобы не оставалось никаких неясностей, в конце концов... Код:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type THandles = array of HWND; TThreadIdHandles = record ThreadId: Cardinal; Handles: THandles; end; TForm1 = class(TForm) StartProgramBtn: TButton; GetTextBtn: TButton; Memo1: TMemo; procedure StartProgramBtnClick(Sender: TObject); procedure GetTextBtnClick(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; function EnumChildWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; implementation {$R *.dfm} function EnumChildWndProc(hndl: HWND; lprm: LPARAM): BOOL; stdcall; var WndTxt: PChar; begin Result := True; GetMem(WndTxt, 255); try GetWindowText(hndl, WndTxt, 255); Form1.Memo1.Lines.Add(StrPas(WndTxt)); finally FreeMem(WndTxt, 255); end; end; 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; { take into account only non-tool windows from the thread } if ToolWndStyle = 0 then with ThreadIdHandles[lprm] do begin PrevHandlesLen := Length(Handles); SetLength(Handles, PrevHandlesLen + 1); Handles[PrevHandlesLen] := hndl; end; EnumChildWindows(hndl, @EnumChildWndProc, 0); end; procedure TForm1.GetAllWndHandlesFromThreads; var i: Byte; begin for i := 0 to 3 do EnumThreadWindows(ThreadIdHandles[i].ThreadId, @EnumThrWndProc, i); end; procedure TForm1.StartProgramBtnClick(Sender: TObject); const WinDirPath = 'g:\programs\forex\mt4'; ProgPath = 'g:\programs\forex\mt4\terminal.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(@ProgPath[1], nil, nil, nil, False, NORMAL_PRIORITY_CLASS, nil, @WinDirPath[1], _si, _pi); ThreadIdHandles[i].ThreadId := _pi.dwThreadId; end; // do a delay only once before calling GetAllWndHandlesFromThreads proc. Evnt := CreateEvent(nil, False, False, nil); WaitForSingleObject(Evnt, 1500); CloseHandle(Evnt); Form1.GetAllWndHandlesFromThreads(); end; 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; end. P.S. одинадцатый, уже давайте что-ли пользоваться BB-кодами, а то схватите предупреждение от Admin'а или модератора... Код оформляется так: [code]Сюда вставляется код[/code] P.P.S. По поводу различий при запуске с ярлыка и с CreateProcess. Раскопайте в справке описание функции CreateProcess. Возможно, что дело в том, что вы не используете атрибуты безопасности при запуске приложения. Вместо некоторых nil, которые вы передаете в параметрах функции CreateProcess, в общем случае должны стоять указатели на структуру SECURITY_ATTRIBUTES. Хотя я не утверждаю, что дело именно в этом. И атрибутами безопасности я никогда не пользовалась... Последний раз редактировалось Nyctos Kasignete, 21.04.2009 в 12:21. |
#11
|
|||
|
|||
Спасибо Nyctos Kasignete.
Похоже вы одна здесь отвечаете на вопросы) Ваш метод попробовал - дескрипторы получил. Тока понимаю что тема эта для меня нова и пока непонятна так что cразу от общего к частному перейду. в общем надо для начала извлечь информацию из окна(что то типа stringgrid-а). Дескриптор его я вашим методом получил. А можно ли как нибудь из этого окна выдернуть всю таблицу данных? Как? Выглядет оно вот так: http://www.delphisources.ru/forum/at...1&d=1240403753 Я так понимаю: само окно с вертикальным Caption "Терминал" - это что то типа GroupBox(Который в winsight имеет клас SysListView32), а в него воткнут этот самый компонент похожий на StirngGrid(Который в winsight имеет клас SysHeader32). |
#12
|
||||
|
||||
А зачем влезать в чужой разговор без надобности?
Жизнь такова какова она есть и больше никакова. Помогаю за спасибо. |
#13
|
|||
|
|||
А от куда вам знать с надобностью или без?
|
#14
|
|||
|
|||
Цитата:
одинадцатый, я тоже такими вещами не каждый день занимаюсь. Поэтому обучаюсь на ходу. В Win32 API навалом всяких макросов и всяких сообщений, которые можно отсылать таким окнам, как ListView. Класс SysListView32 — это класс таких окон, как список файлов в Explorer'е (в режиме отображения "Таблица") или в программе WinRAR... Получить содержимое любой "ячейки" можно с помощью отсылки сообщения LVM_GETITEMTEXT. При этом происходит заполнение структуры LV_ITEM, у которой есть поле pszText, в которое и будет записано содержимое "ячейки". Я сейчас горю по времени, пример написать просто не успеваю. Свободное время появится, скорее всего, в субботу, так что лучше всего начинайте пока самостоятельно копать в этом направлении. Если будут затруднения, попробую что-нибудь набросать в качестве примера при первой же возможности. Но, скорее всего, не раньше субботы... |
#15
|
||||
|
||||
Ну как откуда? Я вот топик почитал, вижу что потихонечку все движеться, зачем же мешать? А если нужно много быстрых советов "зубров", задайте это вопрос на sql.ru в разделе Delphi.
Жизнь такова какова она есть и больше никакова. Помогаю за спасибо. Последний раз редактировалось Страдалецъ, 22.04.2009 в 19:27. |
|
Опции темы | Поиск в этой теме |
Опции просмотра | |
|
|