Версия для печати темы

Нажмите сюда для просмотра этой темы в обычном формате

Форум «Всё о Паскале» _ Делфи _ Меню

Автор: Client 14.08.2009 17:59

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;

end;
procedure TForm1.Button3Click(Sender: TObject);
var
wnd: HWND;
TmpArray: array[0 .. 100 - 1] of Char;
mymenu: HMENU;
begin
Wnd := FindWindow('Notepad', nil);
GetWindowText(wnd, @TmpArray, 100);
Memo1.Lines.Add('Title: ' + TmpArray);
mymenu:= GetMenu(wnd);
ListSubMenus('', Memo1, mymenu);
end;
Вот чего получилось:


Эскизы прикрепленных изображений
Прикрепленное изображение

Автор: Client 14.08.2009 19:11

Спасибо!
есть 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
. Мы находим его Handle и делаем:
GetMenuString(menu, 4, TmpArray, sizeof(TmpArray), MF_BYPOSITION);
. Тогда в TmpArray возвращается метка элемента меню с индексом 4, то есть, нулевой, первый, второй и третий пропускаются, и берется четвертый. TmpArray будет хранить что? Правильно, "Выход". Если же MF_BYCOMMAND - то берется метка элемента меню, у которого идентификатор = 4, то есть, что? Правильно, "Пункт 2". Чувствуешь разницу?

Автор: Client 15.08.2009 18:19

Понял, спасибо.
Щас еще покапаю что-нибудь)

Автор: Client 16.08.2009 22:20

smile.gif
теперь хочу скопировать меню из блокнота в меню в моей программе. Проблема в том, что не могу добавить элемент в Popupmenu. Вроде так PopupMenu1.Items.Add(), только какой параметр надо я не знаю
Upd
все уже понял)

Автор: volvo 16.08.2009 22:35

А при чем тут PopupMenu? Ты ж хочешь меню скопировать оттуда себе? Вот и копируй меню... http://msdn.microsoft.com/en-us/library/ms647624%28VS.85%29.aspx + там дальше по ссылкам: 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

Это не совсем консоль. Это оконное приложение, но безо всяких библиотек-оберток. Да, создается консольное приложение, и в нем пишется весь код. Ручками, потому что никаких компонентов нет.

Автор: Client 17.08.2009 17:28

program Project1;
uses
windows,messages;
const
wndclass = 'twinapiwnd';
wndcaption = 'Главное меню формы на win api';
mfile = 100;
medit = 200;
mcheck = 300;
sexit = 101;
scopy = 201;
scut = 202;
spaste = 203;
sselect = 301;
snextmenu = 302;
ssecondlevel = 311;
separator = 1;

var
wc: twndclassex;
wnd: hwnd;
msg: tmsg;
mainmenu: hmenu;
submenufile: hmenu;
submenuedit: hmenu;
submenucheck: hmenu;
submenusecondlevel: hmenu;
check: boolean = false;

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;


begin
// Создаем меню
mainmenu := createmenu;
// Заполняем структуру twndclassex
with wc do
begin
cbsize := sizeof( wc );
style := cs_hredraw or cs_vredraw;
lpfnwndproc := @windowproc;
cbclsextra := 0;
cbwndextra := 0;
hinstance := hinstance;
hicon := loadicon( 0, idi_application );
hcursor := loadcursor( 0, idc_arrow );
hbrbackground := color_window;
lpszmenuname := @mainmenu;
lpszclassname := wndclass;
end;
// Регистрируем класс в системе
registerclassex( wc );
// Создаем подменю
submenufile := createpopupmenu;
submenuedit := createpopupmenu;
submenucheck := createpopupmenu;
submenusecondlevel := createpopupmenu;
// Создаем окно
wnd := createwindowex( 0, wndclass, wndcaption, ws_overlappedwindow,
10, 10, 300, 100, 0, mainmenu, hinstance, nil );
// Создаем пункты главного меню
createmenuitem( mainmenu, submenufile, 'file', 0, mfile, false );
createmenuitem( mainmenu, submenuedit, 'edit', 0, mfile, false );
createmenuitem( mainmenu, submenucheck, 'check', 0, mfile, false );
// Подменю для пункта file
createmenuitem( submenufile, 0, 'exit', 0, sexit, false );
// Подменю для пункта edit
createmenuitem( submenuedit, 0, 'copy', 0, scopy, false );
createmenuitem( submenuedit, 0, 'cut', 0, scut, false );
createmenuitem( submenuedit, 0, ' ', 0, separator, true );
createmenuitem( submenuedit, 0, 'paste', 0, spaste, false );
// Подменю для пункта check->nextmenu
createmenuitem( submenusecondlevel, 0, 'beep', 0, ssecondlevel, false );
// Подменю для пункта check
createmenuitem( submenucheck, 0, 'select', 0, sselect, false );
createmenuitem( submenucheck, submenusecondlevel, 'nextmenu', 0, snextmenu, false );
// Перерисовываем меню
drawmenubar( wnd );
// Показываем окно
showwindow( wnd, sw_shownormal );

// Цикл обработки сообщений
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 Идентификатор меню, которое должно быть назначено окну. Если этот параметр нулевой, текущее меню окна удаляется.
Это не понятно, или у меня не что не связалось или не увидел

Автор: volvo 17.08.2009 17:39

Цитата
Это не понятно
Что именно не понятно?

Автор: Client 17.08.2009 17:47

wnd1:=FindWindow('Notepad',nil);
SetMenu(wnd1,mainmenu)
Попробовал добавить в код выше. Ничего. И как можно вывести название окна тут? (label'а то нету)

Добавлено через 4 мин.
wnd1:=FindWindow('Notepad',nil);
GetWindowText(wnd1, @TmpArray, 100);
messagebox( wnd, TmpArray, 'Меню: edit', 0 );
Вывести смог smile.gif

Автор: volvo 17.08.2009 18:17

Цитата
Попробовал добавить в код выше. Ничего.
Ну, это уж я не знаю, как ты пробовал:
Прикрепленное изображение

Только перед тем, как закрыть свою программу надо было бы вернуть 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

Надо блокноту "подсунуть" обработчик событий, который будет реагировать на меню так, как ты хочешь, а не так, как заложено в Блокноте. Ибо из твоего приложения недоступно то, что нажимается в Блокноте, это разные процессы. Сообщения из его меню поступают в его очередь сообщений, а не в твою.

Гугл по темам "сабклассинг" и "суперклассинг" даст тебе информацию о том, как это делается smile.gif (Сабклассинг - это "контроль сообщений окон путем модификации их оконной процедуры", суперклассинг - "создание и регистрация нового класса окна в системе")

Автор: Client 20.08.2009 22:15

Гуглил, гуглил и нагуглил smile.gif http://damagelab.org/lofiversion/index.php?t=15120 описан сабклассинг своего же окна. Зачем?
И что значит

result := defwindowproc( wnd, msg, wparam, lparam );
Теорию вроде понял, буду разбираться с практикой )

Добавлено через 10 мин.
А вот сабклассинг другого приложения пока не нашел ((

Автор: volvo 20.08.2009 22:31

Странно... Первая ссылка Гугла выводит на Виноградный форум: http://forum.vingrad.ru/forum/topic-69246.html smile.gif и по ссылке - дальше: http://forum.vingrad.ru/index.php?showtopic=226725&view=findpost&p=1630388

Автор: Client 20.08.2009 22:40

пасиб за ссылки)
получается что окнами могут быть и кнопка и эдит? и если получив хендл кнопки (пока не знаю как) то процедура SetWindowText заменит текст на кнопке?

Автор: volvo 20.08.2009 22:58

Цитата
получается что окнами могут быть и кнопка и эдит?
Естественно. Все наследники TWinControl (я про VCL сейчас) - это окна. И Эдиты, и ТекстБоксы, и Мемо, и Чекбоксы, чего там только нет... Любой из этих объектов находится через http://msdn.microsoft.com/en-us/library/ms633500%28VS.85%29.aspx или EnumWindows/EnumChildWindows. На досуге можешь попробовать поиграться: сделай форму с десятком компонентов, а потом из другой программы найди все компоненты smile.gif

Автор: Client 20.08.2009 23:17

Цитата
На досуге можешь попробовать поиграться
Обязательно smile.gif
А что такое HWND и THANDLE? какая разница?
И какая разница между PCHAR и STRING?

Автор: volvo 21.08.2009 0:10

Цитата
А что такое HWND и THANDLE? какая разница?
Технически - никакой. И то и другое - переименование типа Cardinal. Но... Смысл: THandle - это дескриптор какого-то объекта, HWnd - это дескриптор окна. И желательно не путать, потому что кто знает, всегда ли они будут являться одним и тем же типом... По крайней мере, когда я пишу под WinAPI на Аде, компилятор мне не позволяет подставлять один тип вместо другого, там где нужен HWnd, принимается только HWnd, и ничего другого, никаких THandle-ов.

О разнице String <-> PChar очень хорошо написано в Королевстве: http://www.delphikingdom.com/asp/viewitem.asp?catalogid=1206

Хм... По-моему, надо разделить тему. От "меню" уже перешли к общей информации о WinAPI smile.gif

Автор: Client 21.08.2009 1:26

Цитата
Хм... По-моему, надо разделить тему. От "меню" уже перешли к общей информации о WinAPI
ОК smile.gif будет еще 1 тема
СПАСИБО VOLVO ЗА ПОМОЩЬ!