1 (изменено: wisgest, 2008-09-21 21:37:07)

Тема: 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"».

2

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Крутая вещь, очень удобно! Вроде нормально работает: проверял на ярлыках файлов, папок и сетевого подключения, с Рабочего стола, из Главного меню и с панели Быстрого запуска.

3

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Даа классная вещь, в своё время тоже думал как бы такое создать, но тяму не хватило на тот момент. Скрипт работает.

Йоды магистра речи тайна раскрыта: оказывается, на форте программист старый есть он просто!

4

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Сценарий работает. Изумительно. Я делал себе разрешение ярлыков+переход под Far Manager, но Ваш скрипт гораздо функциональнее. Отдельное спасибо, — я наконец понял:
* как можно получить объект FolderView из Shell.Windows().Item(…);
* что возвращает метод Target объекта ShellLinkObject (IShellLinkDual2) и как потом с этим работать;
* у метода SelectItem есть интересные для меня флаги (теперь буду думать, можно ли и стоит ли сие использовать в «Создание новой папки в Проводнике по горячей клавише»).

После того, как я убедился, что всё работает, тут же захотелось разобраться и понять, как это работает. А чтобы понять досконально, мне требуется VBScript, переводом на который Вашего скрипта я и занялся. В процессе перевода обнаружилось, что метод .Item()

var FolderView=ShellWindows.Item().Document, …

никак не хочет работать под VBScript без указания аргумента, выдавая ошибку времени выполнения (а может быть я что-то делал не так). При этом в js-варианте этот метод, с пустым аргументом, отрабатывал как надо. Поэтому я слегка переделал эту часть. Вот, что у меня в итоге получилось (я намеренно не делал никаких проверок на ошибки, дабы, по возможности, быть как можно ближе к оригиналу):

Option Explicit

Dim strLinkPath
Dim objShell
Dim objFolder3_OfLink
Dim objFolderItem2_OfLink
Dim objIShellLinkDual2_OfLink
Dim objFSO

Dim objFolderItem2_TargetOfLink
Dim objFolderItem2_ParentOfTargetOfLink

Dim objIWebBrowser2
Dim objIShellFolderViewDual2
Dim boolExist


strLinkPath = WScript.Arguments.Item(0)

Set objShell = WScript.CreateObject("Shell.Application")
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objFolder3_OfLink = objShell.NameSpace(objFSO.GetParentFolderName(strLinkPath))
Set objFolderItem2_OfLink = objFolder3_OfLink.ParseName(objFSO.GetFileName(strLinkPath))
Set objIShellLinkDual2_OfLink = objFolderItem2_OfLink.GetLink
Set objFolderItem2_TargetOfLink = objIShellLinkDual2_OfLink.Target
Set objFolderItem2_ParentOfTargetOfLink = objFolderItem2_TargetOfLink.Parent.Self

objFolderItem2_ParentOfTargetOfLink.InvokeVerb "Open"

boolExist = False

Do
    For Each objIWebBrowser2 In objShell.Windows()
        Set objIShellFolderViewDual2 = objIWebBrowser2.Document
        
        If objIShellFolderViewDual2.Folder.Self.Path = objFolderItem2_ParentOfTargetOfLink.Path Then
            boolExist = True
            objIShellFolderViewDual2.SelectItem objFolderItem2_TargetOfLink, 1 + 4 + 8 + 16
        End If
    Next
    
    WScript.Sleep 100
Loop Until boolExist

WScript.Quit

P.S. Поначалу я всё никак не мог понять, почему именно

FolderItem.InvokeVerb "Open"

а не просто

Shell.Open "Какой нужно путь"

пока не попробовал на ярлыке для Корзины, находящейся на рабочем столе. Оказалось, второй вариант открывает окно проводника именно по физическому пути, т.е. «C:\Documents and Settings\***\Рабочий стол», а там, понятное дело, объекта Корзина нет.

5 (изменено: wisgest, 2008-09-10 20:02:58)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

YMP, null, alexii, спасибо за проверку!

alexii пишет:

Код:
var FolderView=ShellWindows.Item().Document, …

никак не хочет работать под VBScript без указания аргумента

Странно, у меня, например, такой vbs-код работает:

Set ShellWindows=CreateObject("Shell.Application").Windows()
If ShellWindows.Count>0 Then
  Set Document=ShellWindows.Item().Document
  WScript.Echo TypeName(Document)
End If

Идея перебирать ShellWindows в поисках нужного окна мне не очень нравится — в принципе могут существовать несколько окон удовлетворяющих нашим требованиям…

alexii пишет:

P.S. Поначалу я всё никак не мог понять, почему именно

Код:
FolderItem.InvokeVerb "Open"
а не просто

Код:
Shell.Open "Какой нужно путь"

Самое забавное, что в первоначальных вариантах скрипта я использовал именно Shell.Open(), но только не со строкой в качестве аргумента, а с объектом папки. Оказывается, Shell.Open() и Shell.NameSpace() могут принимать в качестве аргумента не только строку (путь папки) или число, определяющее специальную папку, но и объекты Folder и FolderItem!
Например,

Set Shell=CreateObject("Shell.Application")
Set Desktop=Shell.NameSpace(0)
Shell.Open(Desktop) ' или:
'Shell.Open(Desktop.Self)

Почему это работает, я не знаю.
Но от такого подхода я отказался, т.к. в особо извращённых случаях он приводит к завершению сценария или HTA-приложения без всякого предупреждения:

Set Shell=CreateObject("Shell.Application")
Set BadFolder=Shell.NameSpace("shell:History").Items().Item(0).GetFolder
Shell.Open(BadFolder)
MsgBox "End"

.
Но, как я убедился прямо сейчас, замена в этом примере

Shell.Open(BadFolder)

на

BadFolder.Self.InvokeVerb "Open"

приводит к такому же эффекту. Так что, возможно, стоит вернутся к первоначальному варианту, тем более, что мне он кажется более красивым, да и «папки» такого рода при работе с ярлыками возникать не должны.

6

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:

Странно, у меня, например, такой vbs-код работает:

Set ShellWindows=CreateObject("Shell.Application").Windows()
If ShellWindows.Count>0 Then
  Set Document=ShellWindows.Item().Document
  WScript.Echo TypeName(Document)
End If

У меня он работает, если открыто хотя бы одно окно IE или Проводника. Иначе ошибка, причём и на JScript так же. А вот если написать If ShellWindows.Count>1, то нормально и там, и там.

7

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

YMP
Сначала решил, что это я поторопился и хотел исправить; но, нет, смотрю, всё правильно. Странно.

8

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Ага, понял. Бывают ситуации, когда ShellWindows содержит значения null. Кажется, при принудительном завершении процесса iexplore.exe. Может быть из-за этого.

9

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:

Кажется, при принудительном завершении процесса iexplore.exe. Может быть из-за этого.

Не знаю, я вроде бы не завершал его принудительно.

wisgest пишет:

Оказывается, Shell.Open() и Shell.NameSpace() могут принимать в качестве аргумента не только строку (путь папки) или число, определяющее специальную папку, но и объекты Folder и FolderItem!
Например,

Set Shell=CreateObject("Shell.Application")
Set Desktop=Shell.NameSpace(0)
Shell.Open(Desktop) ' или:
'Shell.Open(Desktop.Self)

Почему это работает, я не знаю.

Кстати, и это ведь работает:

Set DX = CreateObject("DynamicWrapperX")
Set Shell=CreateObject("Shell.Application")
Set Desktop=Shell.NameSpace(0)
MsgBox Desktop

Как удалось выяснить, MsgBox, Echo и Popup, если получают объект, то вызывают его метод с dispid = 0. Видимо, традиционно этот метод возвращает строку. Open и NameSpace наверно тоже не лыком шиты и знают, что делать с объектом.

Open запрашивает у объекта вот эти два интерфейса:

{1079ACFC-29BD-11D3-8E0D-00C04F6837D5}    IPersistIDList
{1AC3D9F0-175C-11D1-95BE-00609797EA4F}    IPersistFolder2

10

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:
alexii пишет:

Код:
var FolderView=ShellWindows.Item().Document, …

никак не хочет работать под VBScript без указания аргумента

Странно, у меня, например, такой vbs-код работает:

Set ShellWindows=CreateObject("Shell.Application").Windows()
If ShellWindows.Count>0 Then
  Set Document=ShellWindows.Item().Document
  WScript.Echo TypeName(Document)
End If

Вчера и у меня такой вариант заработал . Правда, у меня Explorer.exe в процессе действительно и вылетал, и подвисал; как полностью, так и частично (так, что мне приходилось самому рубить его на корню, иначе скрипт тоже наглухо подвисал). Ну, а, очевидно, после перезагрузки — код заработал.

wisgest пишет:

Идея перебирать ShellWindows в поисках нужного окна мне не очень нравится — в принципе могут существовать несколько окон удовлетворяющих нашим требованиям…

Я об этом не подумал. А с другой стороны, отображать-то они должны, по идее, один и тот же Folder? Так что, какая разница? Или есть вариант, что они могут отображать разные объекты?

wisgest пишет:

Самое забавное, что в первоначальных вариантах скрипта я использовал именно Shell.Open(), но только не со строкой в качестве аргумента, а с объектом папки. Оказывается, Shell.Open() и Shell.NameSpace() могут принимать в качестве аргумента не только строку (путь папки) или число, определяющее специальную папку, но и объекты Folder и FolderItem!

Спасибо. Возьму на заметку.

wisgest пишет:

Но от такого подхода я отказался, т.к. в особо извращённых случаях он приводит к завершению сценария или HTA-приложения без всякого предупреждения:

Set Shell=CreateObject("Shell.Application")
Set BadFolder=Shell.NameSpace("shell:History").Items().Item(0).GetFolder
Shell.Open(BadFolder)
MsgBox "End"

Но, как я убедился прямо сейчас, замена в этом примере

Shell.Open(BadFolder)

на

BadFolder.Self.InvokeVerb "Open"

приводит к такому же эффекту. Так что, возможно, стоит вернутся к первоначальному варианту, тем более, что мне он кажется более красивым, да и «папки» такого рода при работе с ярлыками возникать не должны.

У меня и тот, и другой код открывает папку C:\Documents and Settings\***\Local Settings\History\History.IE5 и пишет «End». Для убедительности я даже добавил

WScript.Sleep 5000

перед MsgBox.

11

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Так, теперь, под другим пользователем тот же самый код (из примеров с BadFolder) уже не работает. Непонятно.

12

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:

Я об этом не подумал. А с другой стороны, отображать-то они должны, по идее, один и тот же Folder? Так что, какая разница? Или есть вариант, что они могут отображать разные объекты?

Есть вариант, что найденное окно не будет активным, и мы его не увидим.

К примеру с BadFolder. Если запустить этот пример от имени другого пользователя, то действительно открывается …\History\History.IE5, т.к. в этом случае спецпапка History обрабатывается как обычная физическая папка. Если же запускать от имени текущего пользователя, то, если бы скрипт не вылетал, должна бы была открываться папка Журнала, например, «На прошлой неделе» или «Сегодня». Можно ещё попробовать заменить в этом примере "shell:History" на число 34 (0x22).

13

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:
alexii пишет:

Я об этом не подумал. А с другой стороны, отображать-то они должны, по идее, один и тот же Folder? Так что, какая разница? Или есть вариант, что они могут отображать разные объекты?

Есть вариант, что найденное окно не будет активным, и мы его не увидим.

Этот вариант я ещё не пробовал. Погляжу.

wisgest пишет:

К примеру с BadFolder. Если запустить этот пример от имени другого пользователя, то действительно открывается …\History\History.IE5, т.к. в этом случае спецпапка History обрабатывается как обычная физическая папка. Если же запускать от имени текущего пользователя, то, если бы скрипт не вылетал, должна бы была открываться папка Журнала, например, «На прошлой неделе» или «Сегодня»…

Да, именно так и есть.

Код

…
Set BadFolder=Shell.NameSpace("shell:History").Items().Item(0).GetFolder
WScript.Echo BadFolder
…

говорит мне

вторник

14

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:
wisgest пишет:
alexii пишет:

Я об этом не подумал. А с другой стороны, отображать-то они должны, по идее, один и тот же Folder? Так что, какая разница? Или есть вариант, что они могут отображать разные объекты?

Есть вариант, что найденное окно не будет активным, и мы его не увидим.

Этот вариант я ещё не пробовал. Погляжу.

Попробовал с несколькими одинаковыми окнами Проводника — работатают оба варианта скрипта. Одно из этих окон активизируется, всплывает на передний план, происходит выделение объекта.

Или Вы имели ввиду что-то другое?

15 (изменено: wisgest, 2008-09-12 09:06:39)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii
Я имел ввиду, что выделение объекта произойдёт не в том окне которое активизируется. Извините, невнимательно посмотрел Ваш вариант - у Вас выделение будет происходить во всех окнах, а не в первом найденном. Но зачем такие излишества?

16

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:

alexii
Я имел ввиду, что выделение объекта произойдёт не в том окне которое активизируется.

Ага, теперь понял. Пока такой ситуации не возникало. Активизируется именно то окно, в котором происходит попытка выделения объекта.

wisgest пишет:

Извините, невнимательно посмотрел Ваш вариант - у Вас выделение будет происходить во всех окнах, а не в первом найденном. Но зачем такие излишества?

Неа , Вы-то как раз внимательно смотрели, а вот я Exit For «проглотил» при наборе и не заметил даже. Спасибо. У меня, во всяком случае, получается, что все команды отправляются первому же подходящему окну, и, как Вы указали, именно столько раз, сколько будет найдено подходящих окон .

Option Explicit

Dim strLinkPath
Dim objShell
Dim objFolder3_OfLink
Dim objFolderItem2_OfLink
Dim objIShellLinkDual2_OfLink
Dim objFSO

Dim objFolderItem2_TargetOfLink
Dim objFolderItem2_ParentOfTargetOfLink

Dim objIWebBrowser2
Dim objIShellFolderViewDual2
Dim boolExist


strLinkPath = WScript.Arguments.Item(0)

Set objShell = WScript.CreateObject("Shell.Application")
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objFolder3_OfLink = objShell.NameSpace(objFSO.GetParentFolderName(strLinkPath))
Set objFolderItem2_OfLink = objFolder3_OfLink.ParseName(objFSO.GetFileName(strLinkPath))
Set objIShellLinkDual2_OfLink = objFolderItem2_OfLink.GetLink
Set objFolderItem2_TargetOfLink = objIShellLinkDual2_OfLink.Target
Set objFolderItem2_ParentOfTargetOfLink = objFolderItem2_TargetOfLink.Parent.Self

objFolderItem2_ParentOfTargetOfLink.InvokeVerb "Open"

boolExist = False

Do
    For Each objIWebBrowser2 In objShell.Windows()
        Set objIShellFolderViewDual2 = objIWebBrowser2.Document
        
        If objIShellFolderViewDual2.Folder.Self.Path = objFolderItem2_ParentOfTargetOfLink.Path Then
            boolExist = True
            objIShellFolderViewDual2.SelectItem objFolderItem2_TargetOfLink, 1 + 4 + 8 + 16
            
            Exit For
        End If
    Next
    
    WScript.Sleep 100
Loop Until boolExist

WScript.Quit

Я вот думаю, а стоит-таки, по Вашему образцу, добавить код, ограничивающий время ожидания окна, открываемого (если его ещё нет) командой «objFolderItem2_ParentOfTargetOfLink.InvokeVerb "Open"»? На случай подвисания Explorer'а, либо невозможности открытия?!

17 (изменено: wisgest, 2008-09-21 21:34:55)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:

Я вот думаю, а стоит-так добавить код, ограничивающий время ожидания окна?

В том виде в каком сценарии сейчас, конечно, стоит:
1) Вы сами же и ответили

На случай подвисания Explorer'а

2) папка целевого объекта уже не существует;
3) могут существовать экзотические ярлыки, родительской папкой целевого объекта для которых является Internet Explorer (Shell.NameSpace(1)).
Кстати, мой сценарий, оказывается, тоже не обрабатывает такие ярлыки, хотя его более старая версия обрабатывала, так что его надо будет всё-таки изменить, хотя к старой версии в чистом виде возвращаться не хочется, т.к. есть в ней некоторая избыточность.
Пример такого ярлыка (.lnk, не .url!) и старую версию можно посмотреть по временной ссылке: [21.09.2008 - файл удалён].

18 (изменено: wisgest, 2008-09-21 21:31:52)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Доработал предложенный сценарий, вернувшись на новом уровне диалектического развития к своему старому варианту. По-моему, теперь он стал понятнее. Если не будет возражений, через несколько дней помещу его в Коллекцию.

// 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;
}

• Теперь окно папки открывается с помощью Shell.Open() — так нагляднее; также устраняется описанный в #17 мелкий недочёт (его причина в том, Internet Explorer как папка оболочки имеет действие «OpenHomePage», а не «Open»).
• Проверка на совпадение папки открытого окна Проводника с папкой конечного объекта (функция FoldersAreEqual()) выполняется для всех надпапок — как я убедился, такая проверка действительно необходима…
• Функция OpenParentFolderAndSelectItem() (в паре с вызываемой ею FoldersAreEqual()) является достаточно всеобъемлющей и применимой в других сценариях (см. «похожая задача» из #1).

19

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:

• Проверка … выполняется для всех надпапок — как я убедился, такая проверка действительно необходима…

Примерчик бы?

20

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:

Примерчик бы?

Попробую.
Допустим папка «Мои документы» у меня находится по адресу
D:\USER\Мои документы (я её переместил)
и допустим у меня есть файл
D:\USER\Мои документы\Моя музыка\Francis Goya - Moscow Nights\01 Kalinka.wma
Открываю в Проводнике папку «Мои документы» щелчком по её значку на Рабочем столе, добираюсь до «Francis Goya - Moscow Nights» и перетаскиванием создаю на Рабочем столе столе ярлык на файл «01 Kalinka.wma».
Ввожу в адресной строке путь D:\USER\Мои документы и далее, выполняя те же самые действия, создаю ещё один ярлык на тот же самый файл.
Если теперь для этих ярлыков выполнить действие «Найти объект» (неважно, с помощью моего сценария или кнопки в окне свойств ярлыка) то в обоих случаях будет открываться папка
D:\USER\Мои документы\Моя музыка\Francis Goya - Moscow Nights.
При этом заметим (это самое важное), что, если мы это действие выполним для одного ярлыка, а затем для второго, то будут открыты два окна с одной и той же папкой!
Далее, если мы в этих окнах будем двигаться по иерархии вверх, то после
D:\USER\Мои документы\Моя музыка
в первом получим:
Мои документы
Рабочий стол
а во втором:
D:\USER\Мои документы
D:\USER
...

21

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Ага, понятно. Но объект, опять же, что в одной, что в другой папке будет один и тот же, так ведь?! Не знаю, насколько это критично.

Если теперь для этих ярлыков выполнить действие «Найти объект» (неважно, с помощью моего сценария или кнопки в окне свойств ярлыка) то в обоих случаях будет открываться папка
D:\USER\Мои документы\Моя музыка\Francis Goya - Moscow Nights.
При этом заметим (это самое важное), что, если мы это действие выполним для одного ярлыка, а затем для второго, то будут открыты два окна с одной и той же папкой!

А вот это мне не удалось воспроизвести. У меня [я и сам в большой растерянности от подобного поведения — визуально оба ярлыка ссылаются на один объект и путь, и имеют одну и ту же рабочую папку; разве что, чуть разные по размеру ], что с помощью кнопки найти объект, что через сценарии (что оба Ваших, что мой перевод), — из ярлыка, созданного перетаскиванием из «Мои документы» открывают именно папку «Мои документы», из ярлыка, созданного так же, перетаскиванием из «C:\Documents and Settings\***\Мои документы», открывают, соответственно, папку «C:\Documents and Settings\***\Мои документы» и выделяют объект в ней (даже, если уже открыта папка просто «Мои документы»). И наоборот.

Совершенно непонятно, где могут быть различия в ярлыках, которые визуально идентичны до буквы.

22 (изменено: wisgest, 2008-09-21 21:30:38)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:

Но объект, опять же, что в одной, что в другой папке будет один и тот же, так ведь?! Не знаю, насколько это критично.

Всё то же самое — имеется небольшая вероятность, что выделение произойдёт не в той папке, которая становится активной (если перед этим была активной «не та» папка).

alexii пишет:

А вот это мне не удалось воспроизвести.

Не понял. Судя по описанному, скорее удалось. Однако, более неожиданный эффект получается, если взять не саму папку Мои документы, а объект запрятанный в её глубинах.

alexii пишет:

Совершенно непонятно, где могут быть различия в ярлыках, которые визуально идентичны до буквы.

Для этого надо знать их внутренний формат, а где взять такие сведения я не знаю.

23

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:

Однако, более неожиданный эффект получается, если взять не саму папку Мои документы, а объект запрятанный в её глубинах.

Попробую.

24 (изменено: wisgest, 2008-09-21 21:39:15)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Добавил в Коллекцию. Нужно что-нибудь поправить?

25

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Спасибо! Вроде всё понятно.

Предложения в русском языке начинаются с большой буквы и заканчиваются точкой.
В названии ветки всегда должен быть указан язык программирования или среда исполнения скрипта, если это возможно.

26

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

wisgest пишет:

• В статье базы знаний Microsoft говорится, что, если существует ключ реестра
HKEY_CLASSES_ROOT\lnkfile\shell,
то могут возникнуть трудности с запуском ярлыков с панели ярлыков MS Office 2000 (? — Microsoft Office Shortcut Bar). Подробнее в этом вопросе я не разбирался.

Far Manager стал выполнять действие «FindTarget» вместо стандартного «Open» для *.lnk, хотя именно «Open» визуально оставался глаголом по умолчанию. Пользуется он обычным ShellExecuteEx, а вот глагол использует неверный (можно сказать, перестарался):

GetShellAction(…)
…
…
…
// Если RetPtr==NULL - мы не нашли default action.
// Посмотрим - есть ли вообще что-нибудь у этого расширения
…
// ... а теперь все остальное, если "open" нету
…
// Проверим наличие "команды" у этого ключа
…

тут и получается «FindTarget». Лечится пока созданием

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\lnkfile\Shell\Open\Command]
@="\"%1\""

P.S. http://forum.farmanager.com/viewtopic.p … 049#p26049

27 (изменено: wisgest, 2008-09-28 22:05:57)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii, спасибо за замечание! Если бы это я обнаружил сам, то не понял бы в чём дело.
Признаюсь, Ваш способ исправления мне не очень понравился — меню ярлыков строится на основании меню их конечных объектов, а у тех действием по умолчанию может быть и не «Open». Да и не хочется засорять реестр. Хотя, может быть, Ваше решение будет полезно в других случаях.

Я пока установил в самом Far'е ассоциацию для *.lnk:

rundll32.exe shell32,ShellExec_RunDLL "!.!"

/* Исправлено 28.09.2008: Взял !.! в кавычки, т.к. без них не работало с ярлыками, содержащими в имени пробелы. */

Возможно, я что-то не учёл или наоборот можно сделать проще, но вроде работает.

28

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Я искал хоть что-то, что бы работало , ориентируясь на текст функции. Ну, и, решение было конкретно под *.lnk.

А насчёт Вашего решения — я думаю, это более правильный подход.

29 (изменено: emergency, 2008-10-27 01:42:12)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

прошу прощения, попробовал воспользоваться вашим скриптом, вылезает
http://smages.com/t/a6/a4/a6a43ea99b9b4dee5767b1a43f39215d.jpg
подскажите в чем может быть проблема... свойство папки я думаю тут не причем

30

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Мне удалось воспроизвести эту ошибку, удалив ключ реестра
[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget\command]

Возможно, Вы создали только
[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget]

Давайте порпробуем так: Вы сообщите точный путь, по которому Вы сохранили скрипт, а я напишу Вам reg-файл для его регистрации в системе.

31

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Выложите сюда содержимое ветки реестра HKEY_CLASSES_ROOT\lnkfile.

32 (изменено: emergency, 2008-10-27 03:52:34)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:

Выложите сюда содержимое ветки реестра HKEY_CLASSES_ROOT\lnkfile.

Windows Registry Editor Version 5.00

[HKEY_CLASSES_ROOT\lnkfile]
@="Ярлык"
"EditFlags"=dword:00000001
"IsShortcut"=""
"NeverShowExt"=""

[HKEY_CLASSES_ROOT\lnkfile\CLSID]
@="{00021401-0000-0000-C000-000000000046}"

[HKEY_CLASSES_ROOT\lnkfile\shell]

[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget]
@="&Найти объект"

[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget\command]

[HKEY_CLASSES_ROOT\lnkfile\shellex]

[HKEY_CLASSES_ROOT\lnkfile\shellex\ContextMenuHandlers]

[HKEY_CLASSES_ROOT\lnkfile\shellex\ContextMenuHandlers\Offline Files]
@="{750fdf0e-2a26-11d1-a3ea-080036587f03}"

[HKEY_CLASSES_ROOT\lnkfile\shellex\ContextMenuHandlers\{00021401-0000-0000-C000-000000000046}]

[HKEY_CLASSES_ROOT\lnkfile\shellex\DropHandler]
@="{00021401-0000-0000-C000-000000000046}"

[HKEY_CLASSES_ROOT\lnkfile\shellex\IconHandler]
@="{00021401-0000-0000-C000-000000000046}"

[HKEY_CLASSES_ROOT\lnkfile\shellex\PropertySheetHandlers]

[HKEY_CLASSES_ROOT\lnkfile\shellex\PropertySheetHandlers\ShimLayer Property Page]
@="{513D916F-2A8E-4F51-AEAB-0CBC76FB1AF8}"
wisgest пишет:

Мне удалось воспроизвести эту ошибку, удалив ключ реестра
[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget\command]

Возможно, Вы создали только
[HKEY_CLASSES_ROOT\lnkfile\shell\FindTarget]

Давайте порпробуем так: Вы сообщите точный путь, по которому Вы сохранили скрипт, а я напишу Вам reg-файл для его регистрации в системе.

G:\findobject\findobject.js


текст рег файла (изменил только путь)

REGEDIT4

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

исходный текст рег файла взял вот отсюда http://forum.script-coding.com/viewtopic.php?id=2253 из примера копипастом

33

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Надо заэкранировать слэш «\» как «\\», то есть что-то вида

…\"G:\\findobject\\findobject.js\"…

34 (изменено: wisgest, 2008-10-27 04:05:58)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Ошибка в строке

@="C:\\WINDOWS\\System32\\WScript.exe \"G:\findobject\findobject.js\" \"%1\""

В итоге эта строка игнорировалась и, как можно заметить, в выложенной Вами ветке реестра этой строки нет.

В reg-файлах надо «\» заменять на «\\».
Правильно будет

@="C:\\WINDOWS\\System32\\WScript.exe \"G:\\findobject\\findobject.js\" \"%1\""

(Собственно, alexii уже об этом сказал несколькими минутами ранее:))

35

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

спасибо, работает!

36

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

2wisgest: можем укладываться на покой ?

37 (изменено: major1101, 2010-03-29 22:29:36)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Спасибо!:D

38 (изменено: alexii, 2010-03-29 22:33:59)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Берёте код «FindTarget.js» из Серый форум / WSH: Пункт «Найти объект» в контекстном меню ярлыков в Проводнике, проделываете манипуляции с реестром, описанные там же от слов:

Для появления его в меню, создайте ключ реестра…

Уже разобрались?

39

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

а как перед надписью "найти объект" создать иконку (как у WinRAR)?

40

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Писать свой Context Menu Handler .

В принципе, под Windows 7 сие можно проделать уже просто через реестр: How to Add Icons to Custom Right-Click Menu (Context Menu) in Windows 7 - The Winhelponline Blog.

41

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:

Писать свой Context Menu Handler

а можно подробнее в WinXP (sp3) !

42

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Creating Context Menu Handlers (Windows)

43

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

на русском языке есть?

44 (изменено: wisgest, 2018-01-06 23:35:31)

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

alexii пишет:

Far Manager стал выполнять действие «FindTarget» вместо стандартного «Open» для *.lnk…

Столкнулся с таким же поведением в Take Command Console (точнее в её бесплатном выпуске — TCC/LE). Затруднение преодолевается так же просто как и с Far: TCC позволяет сопоставлять расширениям файлов свои обработчики — через переменные окружения:

set .lnk=rundll32.exe shell32,ShellExec_RunDLL

и назначать команды, выполняющиеся при запуске.

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

[HKEY_CLASSES_ROOT\lnkfile\shell]
@="none"

— по крайней мере поведение Far и TCC исправилось.

Значение «none» я подсмотрел в «живом» реестре. Внятного описания найти не смог, кроме обрывочных упоминаний, например:

user456803 пишет:

Set 'none' as default action allows to add contextmenu items without changing windows default behaviour.

45

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

Здравствуйте! Заюзал ваш вариант из поста No18, все, конечно, круто, вот только… в Win98 это хозяйство не работает :( (в WinMe уже работает). Я так понимаю из-за того, что в Win98 у объекта Folder не поддерживается свойство Self. Самому вникать сил уже нет, да и плохо я в этом понимаю. Пожалуйста, не могли бы вы допилить ваш скрипт так, чтобы он работал и в Win98?

46

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

EgorS, спасибо за отзыв о работоспособности для различных версий Windows и за то, что храните верность и не забываете Win98 и WinMe.
Проверить, к сожалению, не могу и не уверен, что причина неработоспособности в этом, но, насколько знаю, эквивалентом Folder.Self является (по крайней мере в большинстве случаев) Folder.Items().Item() (без аргументов).

47

Re: WSH(+ Реестр): «Найти объект...» в контекстном меню ярлыков

to: wisgest

насколько знаю, эквивалентом Folder.Self является (по крайней мере в большинстве случаев) Folder.Items().Item()

Я проверил, да, похоже, это так. Я этого не знал. До этого знал другой способ (более громоздкий):

Folder.ParentFolder.Items().Item(Folder.Title)

Но как бы там ни было, я заменил в коде из поста 18 в функции FoldersAreEqual это:

if (Folder1==null || Folder2==null || Folder1.Self.Path!=Folder2.Self.Path)

на это:

if (Folder1==null || Folder2==null || Folder1.Items().Item().Path!=Folder2.Items().Item().Path)

И похоже, что в момент перехода от "Мой компьютер" к "Рабочий Стол" это перестает работать: вылезает ошибка "Требуется объект" (если, конечно, убрать из вашего кода "ловушки" try/catch).

На WinMe, Win2000+ (с вариантом Self) такого нет, там проверка благополучно доходит до корня.

Кстати, я всё-таки не понимаю, зачем вы проверяете эквивалентность папок, восходя по дереву до самого корня?! Разве не достаточно просто проверить пути целевой и текущей папки? Зачем их сравнивать по всем предкам?


* * *

P.S.

не уверен, что причина неработоспособности в этом

В MSDN сказано, что Self есть, начиная с WinMe/2000, поэтому в Win98 это никак работать не будет

Я не фанат Win98 (хотя интерфейс у нее, в смысле, внешность, по моему самый лучший из всех Windows, WinXP еще так-сяк, а про блевотное убожество Win7 и выше я вообще молчу), просто иногда играю под нее в старые игрушки, ну и вот решил добавить ваш скрипт, которым уже много лет пользуюсь на WinXP, но облом-с :( …