Вот если бы можно было запускать программы с рабочего стола… Скажем, через контекстное меню иконки в трее.

 

К чему клоню

 

Итак, для начала запустим Delphi. После этого перетащим компонент, заведующий иконкой в трейбаре и компонент TPopupMenu, а в реакцию иконки в трейбаре на нажатие правой кнопки мыши пишем:

 

procedure TForm1.TrayIcon1MouseDown (Sender: TObject;
Button: TMouseButton;
Shift: TShiftState; X, Y: Integer);
var Result:Boolean;
var Pt:TPoint;
begin
Result:=False;
Result:=((GetSystemMetrics (SM_SWAPBUTTON)=0)
and (Button=mbLeft));
Result:=Result or ((GetSystemMetrics (SM_SWAPBUTTON)>0)
and (Button=mbRight));
if Result then
begin
GetCursorPos (Pt);
PopupMenu1.Popup (Pt.X,Pt.Y);
end;
end;
 


Для чего сделан GetSystemMetrics? Для того чтоб левшам было удобно пользоваться. Дальше же идет простое вышлёпывание Popup`a.

 

О главном

 

Хорошо… Popup мы вышлепнем - но что нам в него запихнуть? Для ответа на этот вопрос вспомним модуль Delphi под названием ShlObj.pas. Этот очень полезный модуль содержит, кроме всего прочего, определения интерфейсов IShellFolder и IEnumIDList, которыми мы и воспользуемся. Кроме того, нам понадобится еще функция:

 

function SHGetDesktopFolder (var ppshf: IShellFolder): HResult; stdcall;

 

- которая возвратит нам ссылку на наш "Рабочий стол". Далее мы должны использовать эту ссылку для того, чтоб вызвать метод:

 

function EnumObjects (hwndOwner: HWND; grfFlags: DWORD;out EnumIDList: IEnumIDList): HResult; stdcall;

 

который, в свою очередь, даст нам интерфейс IEnumIDList для перенумерации содержимого "Рабочего стола". В этом интерфейсе нужно вызывать метод Next:

 

function Next (celt: ULONG; out rgelt: PItemIDList;
var pceltFetched: ULONG): HResult; stdcall;

 

- до тех пор пока он возвращает нам значение S_OK. Кроме того, метод Next возвращает нам еще и идентификаторы элементов (rgelt), находящихся на "Рабочем столе",- их мы будем использовать ниже. После этого нам остается только узнать пути к объектам и оформить их в соответствующем пункте меню.

Путь к объекту можно узнать через метод:

 

function GetDisplayNameOf (pidl: PItemIDList; uFlags: DWORD;
var lpName: TStrRet): HResult; stdcall;

 

Через него также можно узнать, какое значение следует передать свойству TMenuItem.Caption каждого элемента контекстного меню. Для этого нужно только вызвать этот метод с соответствующими параметрами. Для Caption это:

 

OleCheck (Root.GetDisplayNameOf (PID,SHGDN_NORMAL,StrRet));

 

И для пути к файлу:

 

OleCheck (Root.GetDisplayNameOf (PID,SHGDN_FORPARSING,StrRet));

 

Естественно, мы должны где-то сохранить эти данные. И наиболее эффективным будет создание своего класса - потомка TMenuItem с определенным свойством Path, которое и будет хранить путь каждого элемента меню:

 

TMyMenuItem = class (TMenuItem)
private
FPath:String;
public
procedure AfterConstruction;override;
destructor Destroy;override;
published
property Path:String read FPath write FPath;
end;

 

В методах AfterConstruction и Destroy я просто обнуляю поле FPath, хотя это и необязательно (сила привычки, однако).

 

Изюминка

 

А теперь рассмотрим код самой процедуры заполнения контекстного меню:

 

procedure TForm1.FillMenuItems;
var Root:IShellFolder;
Enum:IEnumIDList;
PID:PItemIDList;
Fetched:Cardinal;
StrRet:_STRRET;
Temp:TMyMenuItem;
FI:TSHFileInfo;
FAtt:Cardinal;
begin
FAtt:=0;
While PopupMenu1.Items.Count>0 do PopupMenu1.Items.Delete (0);
OleCheck (SHGetDesktopFolder (Root));
OleCheck (Root.EnumObjects (Self.Handle,SHCONTF_NONFOLDERS,Enum));
While Enum.Next (1,PID,Fetched)=S_OK do
begin
Temp:=TMyMenuItem.Create (Self);
OleCheck (Root.GetDisplayNameOf (PID,SHGDN_NORMAL,StrRet));
Temp.Caption:=StrPas (StrRet.cStr);
OleCheck (Root.GetDisplayNameOf (PID,SHGDN_FORPARSING,StrRet));
Temp.Path:=StrPas (StrRet.cStr);
Temp.OnClick:=DoMenuClick;
PopupMenu1.Items.Insert (0,Temp);
end;
Enum:=nil;
Root:=nil;
end;

 

Небольшое объяснение: сначала мы все обнуляем. После этого запрашиваем ссылку на "Рабочий стол", далее используем ее для определения указателя на интерфейс IEnumIDList, при помощи которого заполняем Имена и Пути элементов контекстного меню (которые и добавляем к нашему PopupMenu1). Ну а под конец обнуляем ссылки на интерфейсы IShellFolder и IEnumIDList, чтоб те не засоряли память.

Только осталось разобраться с реакцией контекстного меню на нажатие:

 

procedure TForm1.DoMenuClick (Sender:TObject);
begin
ShellExecute (Handle,nil,PChar ((Sender as TMyMenuItem).Path),nil,nil,SW_NORMAL);
end;

 

Можно также использовать WinExec, но если вы пишете под Win95/98/NT4/2000/XP, то лучше все же ShellExecute т.к. "Microsoft рекомендует".

 

Обновление меню

 


Итак, контекстное меню создано. Но как нам быть, если содержимое "Рабочего стола" изменится? Придется обновлять наше меню. Как лучше это сделать? Сразу же напрашивается решение: "закинуть на форму таймер и по таймеру обновлять" или "поставить в меню пункт Обновить". Оба решения допустимы, но первое пожирает слишком много ресурсов системы, а второе - это дополнительная работа руками (что также недопустимо).

 

Я же предлагаю использовать для этих целей специальные функции Windows по отслеживанию за изменениями в папках: FindFirstChangeNotification, FindNextChangeNotification и FindCloseChangeNotification.

 

Для начала создадим поток, в котором и будет происходить отслеживание изменений в папке. Определим его следующим образом:

 

TFolderWatchThread = class (TThread)
private
FPath:String;
procedure UpdateItems;
public
constructor Create (Path:String);
procedure Execute;override;
end;
 

Здесь метод Create будет проводить начальную настройку потока: заполнение поля FPath, установка приоритета выполнения потока и т.п.; метод Execute - содержит главный код потока, а UpdateItems будет проводить обновление меню.

Реализация этих методов выглядит следующим образом:

 

constructor TFolderWatchThread.Create (Path:String);
begin
inherited Create (True);
FreeOnTerminate:=True;
FPath:=Path;
Self.Priority:=tpHigher;
Resume;
end;

procedure TFolderWatchThread.UpdateItems;
begin
Form1.FillMenuItems;
Form1.PopupMenu1.Items.Add (Form1.UnLoad);
end;

procedure TFolderWatchThread.Execute;
var Signal:THandle;
begin
Signal:=FindFirstChangeNotification (PChar (FPath),False,
FILE_NOTIFY_CHANGE_FILE_NAME or FILE_NOTIFY_CHANGE_DIR_NAME);
Win32Check (Signal<>INVALID_HANDLE_VALUE);
while not Terminated do
begin
Synchronize (UpdateItems);
FindNextChangeNotification (Signal);
WaitForSingleObject (Signal,INFINITE);
end;
FindCloseChangeNotification (Signal);
end;
 

Что здесь написано: во-первых, в конструкторе идет заполнение необходимых свойств объекта и запуск нити; далее в методе UpdateItems идет стандартное обновление контекстного меню (такое же как и при запуске программы). В методе Execute идет отслеживание содержимого папки FPath и вызов метода обновления при возникновении каких-либо изменений.

 

Описание функций FindFirstChangeNotification, FindNextChangeNotification и FindCloseChangeNotification приводится в таблице.

 

 

P.S.

Ничто не стоит на месте (и исходные коды тоже). Так, я теперь тружусь (время от времени) над тем, чтобы это контекстное меню отображало иконки соответствующих программ. Но пока безрезультатно. Так что, если в листинге найдете что-то лишнее - пожалуйста, не обижайтесь.

 

 

Если же у вас нет компонента для работы с треем - предлагаю использовать мой (http://www.g299792458.boom.ru/).

2004.04.22
19.03.2009
В IV квартале 2008 г. украинский рынок серверов по сравнению с аналогичным периодом прошлого года сократился в денежном выражении на 34% – до $30 млн (в ценах для конечных пользователей), а за весь календарный год – более чем на 5%, до 132 млн долл.


12.03.2009
4 марта в Киеве компания Telco провела конференцию "Инновационные телекоммуникации", посвященную новым эффективным телекоммуникационным технологиям для решения задач современного бизнеса.


05.03.2009
25 февраля в Киеве компания IBM, при информационной поддержке "1С" и Canonical, провела конференцию "Как сохранить деньги в условиях кризиса?"


26.02.2009
18-19 февраля в Киеве прошел юбилейный съезд ИТ-директоров Украины. Участниками данного мероприятия стали ИТ-директора, ИТ-менеджеры, поставщики ИТ-решений из Киева, Николаева, Днепропетровска, Чернигова и других городов Украины...


19.02.2009
10 февраля в Киеве состоялась пресс-конференция, посвященная итогам деятельности компании "DiaWest – Комп’ютерний світ" в 2008 году.


12.02.2009
С 5 февраля 2009 г. в Киеве начали работу учебные курсы по использованию услуг "электронного предприятия/ учреждения" на базе сети информационно-маркетинговых центров (ИМЦ).


04.02.2009
29 января 2009 года в редакции еженедельника "Computer World/Украина" состоялось награждение победителей акции "Оформи подписку – получи приз!".


29.01.2009
22 января в Киеве компания "МУК" и представительство компании Cisco в Украине провели семинар для партнеров "Обзор продуктов и решений Cisco Small Business"

 

 
 
Copyright © 1997-2008 ИД "Комиздат".