Тема: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков
Пользоваться кнопкой «Найти объект...» в окне свойств ярлыков (lnk-файлов) мне не очень нравится, хотя бы потому, что это окошко после нажатия на кнопку остаётся висеть спрятанным под другими окнами. Поэтому я решил добавить соответствующий пункт непосредственно в контекстное меню. (Как я позднее узнал, пункт «Открыть место хранения файла» добавлен в меню ярлыков в Windows Vista.)
Похожая задача: требуется, чтобы при щелчке по имени файла на созданной с помощью сценария или HTA веб-странице (скажем, неком отчёте) открывалась содержащая этот файл папка и фокус устанавливался на этот файл. Решать эту задачу я здесь не буду, её решение аналогично; её я привёл, чтобы подчеркнуть важность первоначальной.
Все решения, использующие WSH, которые мне удалось найти, основываются на вызове explorer.exe с ключом /select. Однако, по крайней мере, у меня, это работает не вполне должным образом: если окно папки целевого объекта уже открыто, то окно активируется, но передача фокуса целевому объекту не происходит. Кроме того, если в общем случае требуется найти и выделить объект, не принадлежащий файловой системе, этим способом, по-видимому, воспользоваться можно не всегда. Поэтому я применил другой подход:
1) открываем папку целевого объекта;
2) получаем доступ к только что открытому окну папки как к ShellWindows.Item();
3) получаем объект FolderView окна папки и вызываем его метод SelectItem().
Остановлюсь на п.2. В документации говорится, что вызов ShellWindows.Item() без параметров возвращает то же, что и ShellWindows.Item(0). Однако, как я убедился, это не соответствует действительности: стабильно возвращается последнее активное окно в коллекции ShellWindows. Хотя, может быть, это верно не на всех системах…
Встретилось следующее затруднение: открытие окна происходит в асинхронном режиме и ShellWindows.Item() может возвратить ссылку на предыдущее активное окно (или null, если открытых окон не было) или окно еще не готово предоставить свой объект FolderView. Поэтому пришлось добавить дополнительную проверку в цикле, то ли окно мы получили. При этом возникла задача сравнения двух объектов Folder или FolderItem (если Item — наш целевой объект, то нужно сравнить Item.Parent с FolderView.Folder или Item.Parent.Self с FolderView.Folder.Self). Простое сравнение на равенство возвращает false, а сравнения по свойству .Path (Item.Parent.Self.Path с FolderView.Folder.Self.Path) может оказаться недостаточно: например, папка «Рабочий стол» как вершина иерархии пространства имён оболочки и физическая папка «Рабочий стол» — несколько разные папки, то же (хотя и в меньшей степени) относится к виртуальной и физической папкам «Мои документы»… Поэтому для большей верности добавлена проверка на совпадение путей родительских папок. Кажется, для целевых объектов, на которые могут указывать ярлыки, такой проверки вполне достаточно; но для отчётов на веб-страницах она может быть как избыточной (если целевые объекты заведомо принадлежат файловой системе), так и недостаточной (если в качестве целевого объекта может выступать что-нибудь вроде записи журнала истории IE; а как решить такую задачу в общем случае, я не очень ясно представляю…).
В итоге я написал следующий сценарий для обработки пункта «Найти объект...» контекстного меню ярлыков (доработанный сценарий см. ниже, в сообщении #18):
// FindTarget.js
// аргумент - путь к lnk-ярлыку
try {
var
Item=GetItemByPath(WScript.Arguments.Item(0)).GetLink.Target,
ShellWindows=Item.Application.Windows(),
ParentItem=Item.Parent.Self,
ParentFolderPath=ParentItem.Path,
GrandParentFolderPath=ParentItem.Parent.Self.Path;
ParentItem.InvokeVerb("Open");
for (var t=50; --t>=0; WScript.Sleep(50))
try {
var
FolderView=ShellWindows.Item().Document,
FolderViewItem=FolderView.Folder.Self;
if (
FolderViewItem.Path==ParentFolderPath &&
FolderViewItem.Parent.Self.Path==GrandParentFolderPath
) {
FolderView.SelectItem(Item, 1|4|8|16);
break;
}
} catch (Err) {}
} catch (Err) {}
function GetItemByPath (Path) {
var
Shell=new ActiveXObject("Shell.Application"),
FSO=new ActiveXObject("Scripting.FileSystemObject");
return Shell.
NameSpace(FSO.GetParentFolderName(Path)).ParseName(FSO.GetFileName(Path));
}
Пожалуйста, проверьте, работает ли у вас предложенный сценарий.
Для тестирования можно просто поперетаскивать в Проводнике ярлыки (только .lnk; с ярлыками .url и .pif он работать не будет) на значок этого сценария.
Для закрепления его в контекстном меню, создайте ключ реестра
HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget
со значением по умолчанию
«&Найти объект...»
и подкюч
HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget\command
со значением по умолчанию вида:
«"путь_к_WScript.exe" "путь_к_файлу_сценария" "%1"».