|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
Как добавить мой пункт меню в стандартное контекстное меню Винды?
Здравствуйте, уважаемые!
Тыкаем правой кнопкой на заголовок любого окна любого приложения - появляется контекстное меню: Восстановить, Переместить, Размер, Свернуть, Развернуть, Закрыть (Alt+F4). Вопрос: как в это меню добавить свой пункт (вложенное подменю), а затем обрабатывать события по нажатию на него? Заранее спасибо! Последний раз редактировалось vinni, 14.03.2008 в 10:16. |
#2
|
||||
|
||||
то есть, на форме, или в самой винде, меню тебе нужно? если на форме то скорее всего юзай PopupMenu и обрабатывай нажатие правой кнопки мыши.
|
#3
|
|||
|
|||
В самом простом варианте - вот так.
Код:
unit asmMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs; type TForm1 = class(TForm) procedure FormCreate(Sender: TObject); private { Private declarations } procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} const CM_ABOUT = WM_USER + 1; procedure TForm1.FormCreate(Sender: TObject); const SNewMenuItemCaption = 'About'; var Menu: HMENU; MenuItemInfo: TMenuItemInfo; lpszCapt: PChar; begin // Получаем дескриптор системного меню Menu := GetSystemMenu(Handle, False); // Добавляем пункт "About" GetMem(lpszCapt, Length(SNewMenuItemCaption) + 1); StrPCopy(lpszCapt, SNewMenuItemCaption); FillChar(MenuItemInfo, SizeOf(MenuItemInfo), 0); with MenuItemInfo do begin cbSize := SizeOf(MenuItemInfo); fMask := MIIM_STRING + MIIM_ID; fType := MFT_STRING; fState := 0; wID := CM_ABOUT; hSubMenu := 0; hbmpChecked := 0; hbmpUnchecked := 0; dwItemData := 0; dwTypeData := lpszCapt; cch := Length(SNewMenuItemCaption); end; InsertMenuItem(Menu, 0, True, MenuItemInfo); FreeMem(lpszCapt); // Добавляем разделитель после нашего пункта FillChar(MenuItemInfo, SizeOf(MenuItemInfo), 0); with MenuItemInfo do begin cbSize := SizeOf(MenuItemInfo); fType := MFT_MENUBARBREAK; end; InsertMenuItem(Menu, 1, True, MenuItemInfo); end; procedure TForm1.WMSysCommand(var Msg: TWMSysCommand); begin // Обрабатываем команду нашего пункта меню if Msg.CmdType = CM_ABOUT then ShowMessage('Insert item to system menu demo.'); inherited; end; end. Код:
AppendMenu(Menu, MF_STRING, SC_ABOUT, 'About'); Если вы хотите добавить подменю, то нужно его предварительно создать и указать его дескриптор в поле hSubMenu структуры TMenuItemInfo. Ну и флаги другие, конечно, выставить в fMask (MIIM_SUBMENU). Меню создается функцией CreateMenu Последний раз редактировалось Rosenkrantz, 14.03.2008 в 12:44. |
#4
|
|||
|
|||
Rosenkrantz, спасибо большущее! :-)
Буду пробовать :-) |
#5
|
|||
|
|||
Rosenkrantz, все норм, но нужно чтобы этот пункт меню добавлялся при правом клике не на мое приложение, а на заголовке окна ЛЮБОГО приложения :-)
Т.е., например, я запустил свою прогу (она свернута в трее), тыкаю правой на заголовке окна, например, MS Word или MS Excel, и там должен появиться (кроме стандартных) мой пункт меню (раскрывающееся подменю). |
#6
|
|||
|
|||
Вот в этом вызове
Код:
Menu := GetSystemMenu(Handle, False); Для этого можете использовать EnumWindows. Эта функция находит все окна приложений (но не находит их дочерние окна! для этого есть EnumChildWindows). Вот тут пример использования. Чтобы обрабатывать окна, которые будут открываться после запуска вашего приложения, можно поступить двояко. Первый вариант - повесить глобальный хук. Посмотрите описание функции SetWindowsHookEx. Обратите внимание - глобальный хук обязательно должен находиться в DLL. Либо можно просто периодически повторять перебор всех открытых окон и проверять - есть у них в меню ваш пункт или нет. Если нет, добавлять. В конце концов, мало кто, запустив приложение, сразу лезет в его системное меню. Запуская раз в 2-3 секунды перебор окон, вы свою задачу решите. Нужно только подобрать интервал, чтобы сильно не грузило систему. P.S. И, кстати, не забудьте удалить добавленные пункты меню, когда ваша программа будет завершаться. Последний раз редактировалось Rosenkrantz, 15.03.2008 в 15:46. |
#7
|
|||
|
|||
Уважаемый Rosenkrantz! Есть 2 вопроса:
1. Поясни, плиз, поподробнее как создать подменю, а не пункт меню (не пойму как работать с CreateMenu). 2. Как удалить мои созданные подменю после завершения программы? DeleteMenu()? Если да - то что означают ее параметры (кромер первого, он понятен) . Заранее спасибо! |
#8
|
|||
|
|||
Вот код, который добавляет пункты в меню всех открытых при старте программы окон, и удаляет эти пункты при выходе из нее.
Код:
unit asmMain; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) procedure FormDestroy(Sender: TObject); procedure FormCreate(Sender: TObject); private { Private declarations } procedure WMSysCommand(var Msg: TWMSysCommand); message WM_SYSCOMMAND; public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} const SNewMenuItemCaption = 'About'; var CM_about: Cardinal; function MenuAlreadyProcessed(Menu: HMENU): Boolean; // Проверяет, возможно меню уже было обработано var i, Count : Integer; Data : TMenuItemInfo; begin Result := False; Count := GetMenuItemCount(Menu); for i := 0 to Count - 1 do begin FillChar(Data, SizeOf(Data), 0); Data.cbSize := SizeOf(Data); Data.fMask := MIIM_DATA + MIIM_ID; if GetMenuItemInfo(Menu, i, True, Data) then // Проверяем, если "наш" пункт меню уже присутствует, значит // повторная обработка меню не требуется if (Data.wID = CM_ABOUT) and (Data.dwItemData = Application.Handle) then begin Result := True; Exit; end; end; end; procedure DeleteItem(Menu: HMENU); // Удалить добавленные пункты из меню var i, Count : Integer; Data : TMenuItemInfo; S : String; begin Count := GetMenuItemCount(Menu); for i := Count - 1 downto 0 do begin FillChar(Data, SizeOf(Data), 0); Data.cbSize := SizeOf(Data); Data.fMask := MIIM_TYPE + MIIM_DATA + MIIM_ID; // Опознаем "свой" пункт меню по дескриптору приложения в dwItemData if GetMenuItemInfo(Menu, i, True, Data) and (Data.dwItemData = Application.Handle) then DeleteMenu(Menu, i, MF_BYPOSITION); end; end; procedure AddItem(Menu: HMENU); // Добавить пунк в меню var MenuItemInfo: TMenuItemInfo; CaptStr: PChar; begin // При добавлении своих пунктов меню в dwItemData прописываем дескриптор // приложения. При завершении приложения нам нужно // будет удалить эти пункты меню; мы будем опознавать их по этому признаку. // Добавляем пункт "About" GetMem(CaptStr, Length(SNewMenuItemCaption) + 1); StrPCopy(CaptStr, SNewMenuItemCaption); FillChar(MenuItemInfo, SizeOf(MenuItemInfo), 0); with MenuItemInfo do begin cbSize := SizeOf(MenuItemInfo); fMask := MIIM_TYPE + MIIM_DATA + MIIM_ID; fType := MFT_STRING; wID := CM_ABOUT; dwItemData := Application.Handle; // Признак для опознания "своих" пунктов меню dwTypeData := CaptStr; cch := Length(SNewMenuItemCaption); end; InsertMenuItem(Menu, 0, True, MenuItemInfo); FreeMem(CaptStr); // Добавляем разделитель после нашего пункта FillChar(MenuItemInfo, SizeOf(MenuItemInfo), 0); with MenuItemInfo do begin cbSize := SizeOf(MenuItemInfo); fMask := MIIM_TYPE + MIIM_DATA; fType := MFT_SEPARATOR; dwItemData := Application.Handle; end; InsertMenuItem(Menu, 1, True, MenuItemInfo); end; function EnumWinToAddItem(hWindow: HWND): BOOL; stdcall; // Функция добавляет пункт меню // Вызывается из EnumWindows var Menu: HMENU; WCaption: Array [0..255] Of Char; begin Menu := GetSystemMenu(hWindow, False); if (Menu <> 0) and (not MenuAlreadyProcessed(Menu)) then AddItem(Menu); Result := True; end; function EnumWinToDelItem(hWindow: HWND): BOOL; stdcall; // Функция удаляет пункт меню // Вызывается из EnumWindows var Menu: HMENU; begin // Получаем системное меню окна Menu := GetSystemMenu(hWindow, False); // Меню есть не у всех окон, поэтому нужна проверка на 0 if (Menu <> 0) then DeleteItem(Menu); // Если вернуть False, EnumWindows закончит работу Result := True; end; procedure TForm1.FormDestroy(Sender: TObject); begin // Удаляем наши пункты меню из всех окон EnumWindows(@EnumWinToDelItem, LongInt(Self)); end; procedure TForm1.FormCreate(Sender: TObject); begin // Регистрируем уникальное системное сообщение CM_ABOUT := RegisterWindowMessage('CM_ABOUT'); // Добавляем пункт меню во все окна EnumWindows(@EnumWinToAddItem, LongInt(Self)); end; procedure TForm1.WMSysCommand(var Msg: TWMSysCommand); begin // Обрабатываем команду нашего пункта меню if Msg.CmdType = CM_ABOUT then ShowMessage('Insert item to system menu demo.'); inherited; end; end. Параметры DeleteMenu: 1 - Меню 2 - Смысл его зависит от третьего параметра 3 - MF_BYPOSITION или MF_BYCOMMAND. Если MF_BYPOSITION, то второй параметр означает позицию пункта, который мы удаляем в меню (начиная с нуля). Если MF_BYCOMMAND - второй параметр это команда, которая связана с пунктом меню. В моем примере используется удаление по позиции. Последний раз редактировалось Rosenkrantz, 20.03.2008 в 17:41. |
#9
|
|||
|
|||
Если можно, напиши, плиз, пример с раскрыващимся подменю, в котором, например, 10 пунктов. И обработка нажатия на мой пункт меню (procedure TForm1.WMSysCommand(var Msg: TWMSysCommand)) работает только при клике на меню, появляющееся при нажатии на форму моего приложения, т.е. обработка нажатия на других окнах не работает - ничего не происходит...
|
#10
|
|||
|
|||
Цитата:
Пример создания меню напишу чуть попозже. |
#11
|
|||
|
|||
Цитата:
Во-первых, поправлю сам себя: для создания подменю нужно использовать CreatePopupMenu, а не CreateMenu. Во-вторых, вот функция, которая создаст нам это самое подменю: Код:
function CreateSubMenu: HMENU; // Создаем подменю begin Result := CreatePopupMenu; AppendMenu(Result, MF_STRING, CM_ABOUT, 'Item 0'); AppendMenu(Result, MF_STRING, CM_ABOUT, 'Item 2'); AppendMenu(Result, MF_STRING, CM_ABOUT, 'Item 3'); AppendMenu(Result, MF_STRING, CM_ABOUT, 'Item 4'); AppendMenu(Result, MF_SEPARATOR, WM_NULL, nil); AppendMenu(Result, MF_STRING, CM_ABOUT, 'Item 5'); end; Теперь нужно только подправить немножко процедуру AddItem: Код:
... with MenuItemInfo do begin cbSize := SizeOf(MenuItemInfo); fMask := MIIM_TYPE + MIIM_DATA + MIIM_SUBMENU; fType := MFT_STRING; wID := 0; hSubMenu := CreateSubMenu; // Назначаем подменю dwItemData := Application.Handle; // Признак для опознания "своих" пунктов меню dwTypeData := CaptStr; cch := Length(SNewMenuItemCaption); end; ... |
#12
|
|||
|
|||
Rosenkrantz, я понимаю, достал уже наверное тебя... :-)
Можеш плиз написать пример или объяснить поподробнее как обработать нажатие на определенный пункт меню? |
#13
|
|||
|
|||
Цитата:
Цитата:
Почитайте, поэкспериментируйте, может и пример никакой не понадобится. Для начала напишите локальный хук, который будет ловить нужное вам событие внутри вашей программы, а дальше уже будет проще. |
#14
|
|||
|
|||
Rosenkrantz, спасибо большое за инфу! В теории все ясно, но на практике пока никак... Я так понимаю, чтобы добавить мой пункт меню - нужно это делать при создании окна вновь запускаемой программы. Хук должен быть WH_SHELL? И насчет обработки нажатия - тоже хз. Если можеш, напиши, плиз, пример... Спасибо!
|
#15
|
|||
|
|||
Нет, для добавления в меню своего пункта хук, в общем-то, не нужен. Можно, как я писал уже, сделать по таймеру обход всех открытых окон раз в 30-40 секунд и добавлять. А чтобы вот перехватить выбор пользователем вашего пункта в этом меню, нужен хук.
Насчет написать - м.б. в выходные чего-нибудь напишу. Не обещаю, но постараюсь. |