1

Тема: WSH: пункт «Найти объект» в контекстном меню ярлыков в Проводнике

По-моему, было бы удобнее, если бы вместо или кроме кнопки «Найти объект...» в окне свойств ярлыков (файлов .lnk) был бы такой же пункт в их контекстном меню. (Как я слышал, в Windows Vista в меню ярлыков есть пункт «Открыть место хранения файла».) Поэтому я написал сценарий для обработки такого пункта:

// FindTarget.js
// аргумент - путь к lnk-ярлыку
try {
  OpenParentFolderAndSelectItem(
    GetFolderItemByFilePath(WScript.Arguments.Item(0)).GetLink.Target,
    1|4|8|16
  );
} catch (Err) {};

function GetFolderItemByFilePath(Path) {
  var FSO=new ActiveXObject("Scripting.FileSystemObject");
  return (
    new ActiveXObject("Shell.Application")).
      NameSpace(FSO.GetParentFolderName(Path)).ParseName(FSO.GetFileName(Path));
}

function OpenParentFolderAndSelectItem(FolderItem, Flags) {
  var
    Shell=FolderItem.Application,
    ShellWindows=Shell.Windows(),
    ParentFolder=FolderItem.Parent,
    Err;
  Shell.Open(ParentFolder);
  for (var t=50; --t>=0; WScript.Sleep(50))
    try {
      var FolderView=ShellWindows.Item().Document;
      if (FoldersAreEqual(ParentFolder, FolderView.Folder)) {
        FolderView.SelectItem(FolderItem, Flags);
        return;
      }
    } catch (Err) {}
}

function FoldersAreEqual(Folder1, Folder2) {
  while (Folder1!=Folder2) {
    if (Folder1==null || Folder2==null || Folder1.Self.Path!=Folder2.Self.Path)
      return false;
    Folder1=Folder1.ParentFolder; Folder2=Folder2.ParentFolder;
  }
  return true;
}

Для появления его в меню, создайте ключ реестра
HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget
со значением по умолчанию
«&Найти объект»
и подкюч
HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget\command
со значением по умолчанию вида:
«"путь_к_WScript.exe" "путь_к_файлу_сценария" "%1"».

Например, если сценарий у вас находится по адресу
D:\My Works\My Scripts\FindTarget.js,
то можно воспользоваться таким reg-файлом:

REGEDIT4

[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget]
@="&Найти объект"
[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget\command]
@="C:\\WINDOWS\\System32\\WScript.exe \"D:\\My Works\\My Scripts\\FindTarget.js\" \"%1\""

Описание работы сценария:
1) сценарий в качестве аргумента получает путь к файлу-ярлыку;
2) получаем объект FolderItem конечного объекта ярлыка;
3) открываем (или активируем, если оно уже открыто) окно Проводника с родительской папкой конечного объекта ярлыка с помощью команды

  Shell.Open(ParentFolder);

— замечено, что метод Shell.Open() (а также Shell.NameSpace()) может принимать в качестве аргумента не только путь папки или целочисленный идентификатор специальной папки, но и объекты Folder и FolderItem. Если это у вас не работает, то можете взамен воспользоваться командой

  ParentFolder.Self.InvokeVerb("Open");

4)  получаем доступ к только что открытому окну папки, вызвав метод  ShellWindows.Item() без параметров, — замечено, что вызов этого метода без параметров возвращает последнее активное окно в коллекции ShellWindows, а не то же самое, что ShellWindows.Item(0), как утверждается в документации;
5)  получаем объект FolderView окна папки и вызываем его метод SelectItem().

Рассмотрим переход от п.2 к п.3 и п.4:
Т.к. открытие окна происходит асинхронно выполнению сценария, то без соответствующей проверки ShellWindows.Item() может возвратить ссылку на предыдущее активное окно (или null, если открытых окон не было) или окно может быть ещё не готово предоставить свой объект FolderView.
Поэтому в сценарии добавлено ожидание совпадения родительской папки конечного объекта ярлыка (ParentFolder) и папки в активном окне (FolderView.Folder).
При сравнении папок (объектов Folder) нужно учитывать, что некоторые папки (Рабочий стол, Мои документы, Общие документы…) могут рассматриваться и как обычные папки файловой системы, и как специальные системные папки; причём информация об этом может сохранятся в ярлыках. При сравнении путей таких папок (получаемых при помощи .Self.Path) получаются одинаковые значения; но при открытии окна такой папки, если открыто окно её «двойника», всё равно открывается новое окно. Более того, это происходит не только для самих папок-двойников, но и для их подпапок любого уровня. Поэтому при сравнении папок нужно сравнивать и их родительские папки, родительские папки родительских папок и т.д. Такое сравнение осуществляет пользовательская функция FoldersAreEqual(Folder1, Folder2), где Folder1, Folder2 — объекты Folder.

Дополнительные замечания:
• У меня сложилось впечатление, что мне удалось полностью воспроизвести поведение кнопки «Найти объект...» за исключением исправления ярлыков на более несуществующие объекты.
• Функция OpenParentFolderAndSelectItem(FolderItem, Flags) (в паре с используемой ею функцией FoldersAreEqual()) может без изменений использоваться в других сценариях решающих похожие задачи.
• В статье базы знаний Microsoft говорится, что, если существует ключ реестра
HKEY_CLASSES_ROOT\lnkfile\shell,
то могут возникнуть трудности с запуском ярлыков с панели ярлыков MS Office 2000 (? — Microsoft Office Shortcut Bar). Подробнее в этом вопросе я не разбирался.

Тема форума