procedure TForm1.Button1Click(Sender: TObject); var wnd:hwnd; TmpArray : array[0..100 - 1] of Char; mymenu:Hmenu; begin Wnd := FindWindow('Notepad',nil); GetWindowText(wnd, @TmpArray, 100); label1.caption:=tmparray; mymenu:=getmenu(wnd); label2.Caption:=inttostr(GetMenuItemCount(mymenu)); end;
Тут я нашел окно блокнота, узнал его заголовок и количество пунктов меню. Как узнать название пункта ? и если есть его подпунтков?
volvo
14.08.2009 18:57
Скажем, вот так:
procedure ListSubmenus(s: string; memo: TMemo; menu: HMENU); var TmpArray : array[0..100 - 1] of Char; i, count: integer; submenu: HMENU; begin count := GetMenuItemCount(menu); Memo.Lines.Add('Menu items count: ' + IntToStr(count));
for i := 0 to Pred(count) do begin GetMenuString(menu, i, TmpArray, sizeof(TmpArray), MF_BYPOSITION); Memo.Lines.Add(Format('%sItem #%d : %s', [s, i, TmpArray])); submenu := GetSubMenu(menu, i); if submenu <> 0 then begin ListSubmenus(s + ' ', memo, submenu); end; end;
Спасибо! есть 2 вопроса GetMenuString(menu, i, TmpArray, sizeof(TmpArray), MF_BYPOSITION); что за параметр MF_BYPOSITION? Memo.Lines.Add(Format('%sItem #%d : %s', [s, i, TmpArray])); как тут задается формат?
volvo
14.08.2009 19:20
Цитата
что за параметр MF_BYPOSITION?
Это параметр, который определяет, что передается в функцию вторым параметром. Если указываешь MF_BYPOSITION, то этот параметр расценивается как порядковый номер элемента меню, начиная с 0. Если указать MF_BYCOMMAND - значит, будет трактоваться как команда (т.е., сообщение, которое отсылается при выборе пункта меню).
Цитата
как тут задается формат?
Как в Сях, так и тут. В "DRKB -> Язык программирования Delphi -> Работа со строками -> Форматирование строк -> Функции форматирования строк" посмотри, там написано какие могут быть спецификаторы.
Client
15.08.2009 17:54
Цитата
Если указываешь MF_BYPOSITION, то этот параметр расценивается как порядковый номер элемента меню, начиная с 0
Т.е. берется название его?
Цитата
Если указать MF_BYCOMMAND - значит, будет трактоваться как команда
А это тоже самое что если бы нажать по этому пункту меню?
volvo
15.08.2009 18:10
Нет, ты не понял... Смотри, допустим имеется вот такое описание меню в ресурсах:
POPUP "Название" BEGIN MENUITEM "Пункт 1", 3 MENUITEM SEPARATOR MENUITEM "Пункт 2", 4 MENUITEM SEPARATOR MENUITEM "Выход", 100 END
. Тогда в TmpArray возвращается метка элемента меню с индексом 4, то есть, нулевой, первый, второй и третий пропускаются, и берется четвертый. TmpArray будет хранить что? Правильно, "Выход". Если же MF_BYCOMMAND - то берется метка элемента меню, у которого идентификатор = 4, то есть, что? Правильно, "Пункт 2". Чувствуешь разницу?
Client
15.08.2009 18:19
Понял, спасибо. Щас еще покапаю что-нибудь)
Client
16.08.2009 22:20
теперь хочу скопировать меню из блокнота в меню в моей программе. Проблема в том, что не могу добавить элемент в Popupmenu. Вроде так PopupMenu1.Items.Add(), только какой параметр надо я не знаю Upd все уже понял)
volvo
16.08.2009 22:35
А при чем тут PopupMenu? Ты ж хочешь меню скопировать оттуда себе? Вот и копируй меню... MSDN -> CreateMenu + там дальше по ссылкам: InsertMenuItem, AppendMenu, и InsertMenu - в помощь...
Иначе у тебя уже не WinAPI, а VCL получается...
Client
16.08.2009 23:06
Да я хочу скопировать МЕНЮ целиком из блокнота в свою программу.
Цитата
Иначе у тебя уже не WinAPI, а VCL получается...
Хм, у меня форма, на ней мемо и кнопка. Что не так? или лучше создать консольное приложение?
volvo
16.08.2009 23:54
В таком случае название темы не соответствует действительности. Реши для себя, ты хочешь чистый WinAPI, или хочешь пользоваться VCL-ными "плюшками".
Client
16.08.2009 23:59
я пока создавал только VCL приложения (форма, кнопки). А для WinAPI надо консольное создавать? буду смотреть для примера как делать хоть что-то в консоли
volvo
17.08.2009 0:01
Это не совсем консоль. Это оконное приложение, но безо всяких библиотек-оберток. Да, создается консольное приложение, и в нем пишется весь код. Ручками, потому что никаких компонентов нет.
function createmenuitem( hmenu, submenu: hmenu; cap: pchar; _uid, _wid: uint; sep: boolean ): boolean; var mi: menuiteminfo; begin with mi do begin cbsize := sizeof( mi ); fmask := miim_state or miim_type or miim_submenu or miim_id; if not sep then ftype := mft_string else ftype := mft_separator; fstate := mfs_enabled; wid := _wid; hsubmenu := submenu; dwitemdata := 0; dwtypedata := cap; cch := sizeof( cap ); end; result := insertmenuitem( hmenu, _uid, false, mi ); end;
function windowproc( wnd: hwnd; msg: uint; wparam: wparam; lparam: lparam ): lresult; stdcall; begin case msg of wm_destroy: begin postquitmessage( 0 ); result := 0; exit; end; wm_command: begin case loword( wparam ) of sexit: postmessage( wnd, wm_quit, 0, 0 ); scopy: messagebox( wnd, 'Пункт: copy', 'Меню: edit', 0 ); scut: messagebox( wnd, 'Пункт: cut', 'Меню: edit', 0 ); spaste: messagebox( wnd, 'Пункт: paste', 'Меню: edit', 0 ); sselect: begin if check then checkmenuitem( submenucheck, sselect, mf_unchecked ) else checkmenuitem( submenucheck, sselect, mf_checked ); check := not check; end; ssecondlevel: messagebeep( mb_iconhand ); end; end; else result := defwindowproc( wnd, msg, wparam, lparam ); end; end;
// Цикл обработки сообщений while getmessage( msg, 0, 0, 0 ) do begin translatemessage( msg ); dispatchmessage( msg ); end; halt( msg.wparam ); end.
Вот нашел щас буду учить)
Цитата
setmenu function setmenu( hwnd: hwnd; hmenu: hmenu ): bool; Функция setmenu связываем новое меню с окном. hwnd Идентификатор окна, которому должно быть назначено новое меню. hmenu Идентификатор меню, которое должно быть назначено окну. Если этот параметр нулевой, текущее меню окна удаляется.
Это не понятно, или у меня не что не связалось или не увидел
Только перед тем, как закрыть свою программу надо было бы вернуть Notepad-ное меню назад... Подумай, как это можно сделать.
Client
17.08.2009 18:21
омг я не туда смотрел) Думал из блокнота в мой проект меню скопируется А почему блокнот не реагирует на нажатие по меню?
Добавлено через 4 мин.
Цитата
Подумай, как это можно сделать
Сохранив меню в переменную типа HMenu. А событие на закрытие формы
function windowproc( wnd: hwnd; msg: uint; wparam: wparam; lparam: lparam ): lresult; stdcall; begin case msg of wm_destroy: begin postquitmessage( 0 ); result := 0; //А здесь setmenu с начальным меню блокнота exit; end; ...
?
volvo
17.08.2009 18:30
Цитата
Думал из блокнота в мой проект меню скопируется
Это тебе так надо:
wnd1 := FindWindow('Notepad',nil); nbMenu := getmenu(wnd1); setmenu(wnd, nbmenu); // Копируем другое меню себе
Цитата
А почему блокнот не реагирует на нажатие по меню?
А потому что меню твое обрабатывается в ТВОЕЙ функции окна, а не в блокнотовской. Чтобы Блокнот реагировал на твое меню, мало заменить само меню, надо еще заменить (ну, или расширить) оконную функцию. Вот в приведенном здесь примере твое приложение тоже не будет реагировать на Блокнотовские команды, оно просто про них не знает.
Client
17.08.2009 18:40
case msg of wm_destroy: begin SetMenu(wnd1,hfirst); postquitmessage( 0 ); result := 0; exit; ... wnd1:=FindWindow('Notepad',nil); hfirst:=GetMenu(wnd1); //доп. переменная end;
Возвращает меню назад)
Добавлено через 11 мин. А почему нет обращения к функции
function windowproc
хоть она и выполняется? И зачем нужна регистрация класса?
volvo
17.08.2009 18:59
Цитата
А почему нет обращения к функции
Цитата
with wc do begin cbsize := sizeof( wc ); style := cs_hredraw or cs_vredraw; lpfnwndproc := @windowproc; // Это что?
Ты своему классу поставил в соответствие эту самую функцию. Теперь система знает, что в случае, когда твое окно получает сообщение, обрабатываться оно будет в функции WindowProc. Собственно, для этого и нужна инициализация класса.
Client
18.08.2009 21:22
Цитата
Чтобы Блокнот реагировал на твое меню, мало заменить само меню, надо еще заменить (ну, или расширить) оконную функцию
Надо ловить нажатие кнопки меню и выполнять тоже действие только не для своей формы, а для блокнота?
volvo
18.08.2009 21:59
Надо блокноту "подсунуть" обработчик событий, который будет реагировать на меню так, как ты хочешь, а не так, как заложено в Блокноте. Ибо из твоего приложения недоступно то, что нажимается в Блокноте, это разные процессы. Сообщения из его меню поступают в его очередь сообщений, а не в твою.
Гугл по темам "сабклассинг" и "суперклассинг" даст тебе информацию о том, как это делается (Сабклассинг - это "контроль сообщений окон путем модификации их оконной процедуры", суперклассинг - "создание и регистрация нового класса окна в системе")
Client
20.08.2009 22:15
Гуглил, гуглил и нагуглил Тут описан сабклассинг своего же окна. Зачем? И что значит
result := defwindowproc( wnd, msg, wparam, lparam );
Теорию вроде понял, буду разбираться с практикой )
Добавлено через 10 мин. А вот сабклассинг другого приложения пока не нашел ((
пасиб за ссылки) получается что окнами могут быть и кнопка и эдит? и если получив хендл кнопки (пока не знаю как) то процедура SetWindowText заменит текст на кнопке?
volvo
20.08.2009 22:58
Цитата
получается что окнами могут быть и кнопка и эдит?
Естественно. Все наследники TWinControl (я про VCL сейчас) - это окна. И Эдиты, и ТекстБоксы, и Мемо, и Чекбоксы, чего там только нет... Любой из этих объектов находится через FindWindowEx или EnumWindows/EnumChildWindows. На досуге можешь попробовать поиграться: сделай форму с десятком компонентов, а потом из другой программы найди все компоненты
Client
20.08.2009 23:17
Цитата
На досуге можешь попробовать поиграться
Обязательно А что такое HWND и THANDLE? какая разница? И какая разница между PCHAR и STRING?
volvo
21.08.2009 0:10
Цитата
А что такое HWND и THANDLE? какая разница?
Технически - никакой. И то и другое - переименование типа Cardinal. Но... Смысл: THandle - это дескриптор какого-то объекта, HWnd - это дескриптор окна. И желательно не путать, потому что кто знает, всегда ли они будут являться одним и тем же типом... По крайней мере, когда я пишу под WinAPI на Аде, компилятор мне не позволяет подставлять один тип вместо другого, там где нужен HWnd, принимается только HWnd, и ничего другого, никаких THandle-ов.