Вопрос или проблема
Я хотел бы создать действие контекстного меню Windows Explorer для нескольких выбранных файлов.
Я создал ключ HKEY_CLASSES_ROOT\*\shell\MyAction\command
с строковым значением
C:\python37\python.exe "c:\test\test.py" "%1"
Этот скрипт на данный момент только отображает аргументы командной строки (для отладки): import sys; print(sys.argv); input()
Когда я выбираю два файла a.txt
, b.txt
в проводнике и щелкаю правой кнопкой мыши на “MyAction”, тогда:
-
Я хотел бы, чтобы этот скрипт вызывался один раз, а аргументы командной строки, переданные Python, должны быть
["c:\test\test.py", "c:\a.txt", "c:\b.txt"]
-
вместо этого я получаю вызов этого скрипта дважды (один раз независимо для каждого файла), и переданные аргументы командной строки:
["c:\test\test.py", "c:\a.txt"] ["c:\test\test.py", "c:\b.txt"]
Как заставить действие контекстного меню вызывать команду только один раз, когда выбраны 2 файла, с 2 файлами в качестве аргументов командной строки?
Кратко Я хотел бы, чтобы это запускалось:
C:\python37\python.exe "c:\test\test.py" "a.txt" "b.raw" "file_with_noext"
а не:
C:\python37\python.exe "c:\test\test.py" "a.txt"
C:\python37\python.exe "c:\test\test.py" "b.raw"
C:\python37\python.exe "c:\test\test.py" "file_with_noext"
при использовании действия контекстного меню на нескольких файлах.
Примечание: Открыть с помощью для нескольких файлов? и его ответы не решают эту проблему; я пробовал с HKEY_CLASSES_ROOT\SystemFileAssociations\*\shell\MyAction\command
, но этот пункт меню не появляется, когда я выбираю несколько файлов (скажем, a.txt
, b.raw
, file_with_noext
и testdir\
).
Я создал небольшую программу, которая полностью решает эту проблему.
https://github.com/ge9/ExecuteCommand-Pipe
Она основана на этом образце кода от Microsoft: https://github.com/microsoft/Windows-classic-samples/tree/main/Samples/Win7Samples/winui/shell/appshellintegration/ExecuteCommandVerb
Она использует технологию COM (Component Object Model) Windows, поэтому нет ограничений на количество или длину пути передаваемых файлов. Также это не предполагает никакой межпроцессной связи, сохраняя порядок файлов.
Команда для открытия файлов указывается в значениях реестра.
Смотрите репозиторий для подробностей.
Извините, если вы уже получили ответ, но я думаю, единственный способ сделать это – это поместить ярлык на ваше приложение в папку SendTo (Вы можете открыть ее из окна Выполнить, введя shell:sendto) и затем выделить оба файла и выбрать вашу программу из контекстного меню “Отправить на”.
Я столкнулся с аналогичной проблемой, но в довольно другом контексте… Итак, вот мое решение. На самом деле, я использую тот же хак реестра, который приводит к множественным вызовам процессов, но принимающая команда – это скрипт PowerShell, который знает о возможности быть запущенным несколько раз, поэтому он проверяет список текущих процессов, запущенных с той же командой (и, предположительно, разными аргументами), затем проверяет, является ли его PID максимальным, и продолжает выполнение, иначе завершает себя. Последний выживший скрипт имеет список аргументов всех родственных процессов, с которыми они были запущены, так что вы можете синхронно делать с этим списком все, что хотите, например, объединить их и передать в качестве аргумента Python.
Вот пример .reg файла:
Windows Registry Editor Version 5.00
[HKEY_CLASSES_ROOT\SystemFileAssociations\*\shell\exmaple]
@="&Объединить выбранные файлы..."
[HKEY_CLASSES_ROOT\SystemFileAssociations\*\shell\example\command]
@="powershell.exe -NoProfile -ExecutionPolicy Bypass -File \"C:\\path\\to\\example.ps1\" \"%1\""
Я объединил основную логику в функцию, которая принимает имя скрипта (не мог получить к нему доступ напрямую из функции) и возвращает список имён файлов, разделённых пробелами. Пример использования:
# Пример вызова функции и использования её результата
$scriptName = $MyInvocation.MyCommand.Name
$arguments = Get-ScriptArguments -scriptName $scriptName
Write-Host "Возвращенные аргументы: $arguments"
python script.py $arguments
# Ожидание ввода пользователя перед закрытием
Read-Host "Нажмите Enter для выхода"
Функция:
function Get-ScriptArguments {
param (
[string]$scriptName
)
# Получить текущий ID процесса PowerShell
$currentProcessId = $PID
# Получить все процессы PowerShell
$processes = Get-Process -Name "powershell"
# Инициализировать счетчик экземпляров и массив для аргументов
$instanceCount = 0
$argumentsList = @()
$scriptProcesses = @()
foreach ($process in $processes) {
try {
# Получить аргументы командной строки для процесса
$commandLine = (Get-CimInstance Win32_Process -Filter "ProcessId = $($process.Id)").CommandLine
# Проверить, включает ли командная строка текущее имя скрипта
if ($commandLine -like "*$scriptName*") {
$instanceCount++
$scriptProcesses += $process
# Извлечь цитированный путь из командной строки (ищем справа)
if ($commandLine -match '.*(".*")$') {
$arguments = $matches[1]
$argumentsList += $arguments
}
# Вывести ID процесса и командную строку
Write-Host "ID процесса: $($process.Id), Командная строка: $commandLine"
}
} catch {
# Обработать любые исключения (например, отказ в доступе) и вывести ошибку
Write-Host "Ошибка обработки ID процесса $($process.Id): $($_.Exception.Message)"
}
}
# Проверить, является ли PID текущего скрипта самым высоким среди экземпляров этого скрипта
$maxPid = ($scriptProcesses | Select-Object -ExpandProperty Id | Measure-Object -Maximum).Maximum
if ($currentProcessId -ne $maxPid) {
Write-Host "Текущий PID скрипта ($currentProcessId) не является самым высоким. Завершение скрипта."
exit
} else {
Write-Host "Текущий PID скрипта ($currentProcessId) является самым высоким. Продолжение выполнения."
return $argumentsList -join " "
}
}
ОБЗОР
-
В реестре укажите целевую программу как Context Menu Create File.exe, которая создает файл reg.txt.
-
В вашем основном цикле программы проверяйте каждые 1 секунду, существует ли reg.txt. Если он существует, убейте Context Menu Create File.exe и удалите файл reg.txt. Затем скопируйте пути выбранных файлов и манипулируйте ими.
-
Если ваша программа в цикле проверяет reg.txt, то вам нужно запустить программу перед выполнением контекстного меню либо при запуске, либо вручную.
Я сделал это с помощью AutoHotkey.
Эти 2 скрипта AutoHotkey ниже позволяют вам добавить элемент контекстного меню
Открыть с Chrome
в Проводнике Windows для открытия нескольких выбранных файлов.Вы можете оставить все значения переменных как есть, но если хотите изменить значение
contextMenu
и имяprogram
, то смотрите 3.1.
ИНСТРУКЦИИ:
Создайте 2 файла в одной директории:
-
Создайте 1-й скрипт Add To Context Menu And Create Startup Shortcut.ahk
-
RunAsAdmin
Пометка гарантирует, что скрипт выполняется от имени администратора (исправляет добавление значений реестра).- Проверка
If (!A_IsAdmin)
проверяет, является ли текущий пользователь НЕ администратором, A_IsAdmin – встроенная переменная AutoHotkey, которая возвращает 1, если пользователь – администратор, 0 – в противном случае. Run, *RunAs "%A_ScriptFullPath%"
Параметр *RunAs запускает скрипт от имени администратора, “%A_ScriptFullPath%” получает полный путь текущего выполняемого скрипта.ExitApp
завершает текущий экземпляр скрипта, выполняемого без прав администратора.- Поскольку команда
Run
запускает скрипт снова с правами администратора, она пропустит условиеIF
и продолжит выполнять код ниже.
- Проверка
-
ContextMenuCreateFile:
метка создает Context Menu Create File.exe, который создает файл reg.txt и завершает Context Menu Create File.exe после записи файла reg.txt. Убедитесь, что вы указали путь к вашемуAhk2Exe.exe
в командеRunWait
. -
Add To Context Menu:
метка добавляет запись в реестр, которая запускает Context Menu Create File.exe.- Установите переменную
contextMenu
в то, что должно отображаться в контекстном меню. (Имяprogram
устанавливается вcontextMenu
) - Установите
regPath
на желаемый путь реестра. - Когда он выполняет
MsgBox
, проверьте, добавлена ли команда в реестр в адресной строке.
- Установите переменную
-
CreateStartupShortcut:
метка создает ярлык главной программы Open With Chrome.exe в папке автозагрузки.
-
Add To Context Menu And Create Startup Shortcut.ahk
; =============Рекомендуемые настройки=============
#NoEnv
SetWorkingDir %A_ScriptDir%
#Warn
CoordMode, Mouse, Window
SendMode Input
#SingleInstance Force
SetTitleMatchMode 2
SetTitleMatchMode Fast
DetectHiddenWindows Off
DetectHiddenText On
#WinActivateForce
#NoTrayIcon
SetControlDelay 1
SetWinDelay 0
SetKeyDelay -1
SetMouseDelay -1
SetBatchLines -1
#Persistent
#MaxThreadsPerHotkey 2
; =============Рекомендуемые настройки=============
AddToContextMenuAndCreateStartupShortcut:
RunAsAdmin: ; =============RunAsAdmin=============
If (!A_IsAdmin) ; ЕСЛИ НЕ администратор
{
Run, *RunAs "%A_ScriptFullPath%" ; Запустите скрипт от имени администратора
ExitApp ; Завершите текущий экземпляр без прав администратора
}
ContextMenuCreateFile: ; =============ContextMenuCreateFile=============
contextMenuCreateFileAhk :=
(LTrim
"#NoEnv
#NoTrayIcon
#SingleInstance Force
SetWorkingDir %A_ScriptDir%
SetBatchLines -1
ContextMenuCreateFile:
FileDelete, reg.txt
FileAppend, , reg.txt
ExitApp
Return"
) ; contextMenuCreateFileAhk
FileDelete, Context Menu Create File.exe ; Удалите Context Menu Create File.exe
FileDelete, Context Menu Create File.ahk ; Удалите Context Menu Create File.ahk
FileAppend, %contextMenuCreateFileAhk%, Context Menu Create File.ahk ; Создайте Context Menu Create File.ahk
RunWait, C:\Program Files\AutoHotkey\Compiler\Ahk2Exe.exe /in "Context Menu Create File.ahk" /out "Context Menu Create File.exe" ; Преобразуйте AHK в EXE
FileDelete, Context Menu Create File.ahk ; Удалите Context Menu Create File.ahk
AddToContextMenu: ; =============AddToContextMenu=============
path := "" ; путь
program := "Context Menu Create File" ; программа
contextMenu := "Open With Chrome" ; контекстное меню
regPath := "HKCR\*\shell" ; путь реестра
StringReplace, regKey, contextMenu, %A_Space%, , A ; ключ реестра
regKey := 0 regKey ; ключ реестра
Loop, Files, %program%.exe, F ; Найдите Program.exe в текущей директории
{
path := A_LoopFileLongPath ; Установите путь к программе
}
cmd :=
(LTrim
"reg add """ regPath "\" regKey """ /ve /t REG_SZ /d """ contextMenu """ /f
reg add """ regPath "\" regKey "\command"" /ve /t REG_SZ /d ""\""" path "\""`"" /f"
) ; Реестр
FileDelete, Add To Context Menu.bat ; Создайте Add To Context Menu.bat
FileAppend, %cmd%, Add To Context Menu.bat ; Создайте Add To Context Menu.bat
RunWait, Add To Context Menu.bat, , Hide ; Запустите Add To Context Menu.bat (*RunAs ADMIN)
FileDelete, Add To Context Menu.bat ; Удалите Add To Context Menu.bat
Run, regedit ; regedit
WinWait, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; Реестр
Sleep, 333
ControlSetText, Edit1, %regPath%\%regKey%\command, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; путь реестра
ControlFocus, Edit1, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; путь реестра
ControlSend, Edit1, {Enter}, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; путь реестра
ControlSend, SysListView321, {Control Down}{NumpadAdd}{Control Up}, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; путь реестра
ControlSend, SysListView321, {F5}, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; путь реестра
MsgBox, 262192, ПРОВЕРКА, Проверьте, добавлен ли %contextMenu% в реестр ; ПРОВЕРКА
CreateStartupShortcut: ; =============CreateStartupShortcut=============
path := "" ; путь
program := contextMenu ; программа
Loop, Files, %program%.exe, F ; Найдите Program.exe в текущей директории
{
path := A_LoopFileLongPath ; Установите путь к программе
}
FileCreateShortcut, %path%, %A_Startup%\%program%.lnk ; Создайте ярлык в автозагрузке
Run, %A_Startup%, , Max ; Проверьте, создан ли ярлык
Run, "%program%.exe" ; Запустите программу
MsgBox, 262144, ПРОВЕРКА, Проверьте, создан ли ярлык ; ПРОВЕРКА
ExitApp ; Завершить
Return
- Создайте 2-й скрипт Open With Chrome.ahk, который является главной программой.
- Здесь создаётся
Loop
и проверяет каждые 1 секунду, существует ли reg.txt. IfExist, reg.txt
, он убивает Context Menu Create File.exe и удаляет reg.txt.- Затем активирует окно explorer.exe и копирует все выбранные пути файлов в CLIPBOARD.
If CLIPBOARD contains .,\
, чтобы убедиться, что CLIPBOARD содержит путь “\” с расширением “.”.- Список выбранных файлов сохраняется в переменной
selectedFiles
. - Цикл ниже
chromeParams := ""
проходит по выбранным файлам, получаетfilePath
и окружает их двойными кавычками, аStringReplace
заменяет путь Windows на путь url файла, например:C:\path\file.jpg
наfile:///path/file.jpg
. - Затем
filePath
добавляется кchromeParams
. StringTrimRight
удаляет последний пробел из строкиchromeParams
.- Затем выполняется
Run, chrome.exe %chromeParams%
с%chromeParams%
(список выбранных файлов). (Если команда не открывает Chrome, то укажите полный путь к Chrome, например:Run, C:\Program Files (x86)\Google\Chrome\Application\chrome.exe
с теми же параметрами)
- Здесь создаётся
Open With Chrome.ahk
; =============Рекомендуемые настройки=============
#NoEnv
SetWorkingDir %A_ScriptDir%
#Warn
CoordMode, Mouse, Window
SendMode Input
#SingleInstance Force
SetTitleMatchMode 2
SetTitleMatchMode Fast
DetectHiddenWindows Off
DetectHiddenText On
#WinActivateForce
#NoTrayIcon
SetControlDelay 1
SetWinDelay 0
SetKeyDelay -1
SetMouseDelay -1
SetBatchLines -1
#Persistent
#MaxThreadsPerHotkey 2
; =============Рекомендуемые настройки=============
OpenWithChrome:
Loop ; Начало цикла
{
Sleep, 1000 ; Устранение высокого использования CPU
IfExist, reg.txt ; ЕСЛИ существует reg.txt
{
RunWait, cmd /c taskkill /im "Context Menu Create File.exe" /f, , Hide ; Исправление открытия 2 окон Compose
FileDelete, reg.txt ; Удалите reg.txt
WinActivate, ahk_class CabinetWClass ahk_exe explorer.exe ; Проводник
CLIPBOARD := "" ; Очистить буфер обмена
Send, {Control Down}{c}{Control Up} ; Скопировать пути файлов
ClipWait, 0 ; Подождите буфер обмена
If CLIPBOARD contains .,\ ; ЕСЛИ В CLIPBOARD содержится .,\
{
selectedFiles := CLIPBOARD ; выбранные файлы
chromeParams := "" ; параметры chrome
Loop, Parse, selectedFiles, `n, `r ; Начало цикла выбранных файлов
{
filePath := """file:///" A_LoopField """" ; путь к файлу
StringReplace, filePath, filePath, \, /, A ; Замените \ на /
chromeParams .= filePath . " " ; chromeParams .= %filePath%,
}
StringTrimRight, chromeParams, chromeParams, 1 ; Удалить последний пробел
Run, chrome.exe %chromeParams% ; Открыть файлы в Chrome
}
}
}
Return
-
Преобразуйте оба Add To Context Menu And Create Startup Shortcut.ahk и Open With Chrome.ahk в EXE файлы в одной директории с помощью Ahk2Exe.exe –> (найдите в меню Пуск, просто выберите файл и нажмите Преобразовать)
-
Запустите
Add To Context Menu And Create Startup Shortcut.exe
-
Выберите файлы, щелкните правой кнопкой мыши и элемент контекстного меню Открыть с Chrome должен появиться.
Ответ или решение
Для выполнения действия контекстного меню для нескольких выбранных файлов в Windows Explorer, необходимо учесть несколько ключевых моментов. Существующий подход создания контекстного меню в реестре не позволяет передать имена нескольких файлов в одной команде, поскольку по умолчанию Windows вызывает команду отдельно для каждого выбранного файла. Давайте рассмотрим, как это можно исправить.
Решение проблемы: использование вспомогательной программы
Одним из самых эффективных способов решения данной задачи является создание вспомогательной программы, которая будет обрабатывать запросы контекстного меню, собирать имена файлов и вызывать основной скрипт или приложение только один раз с полным набором аргументов.
Шаги для реализации:
-
Создание базового приложения:
Создайте приложение (например, на Python или другом языке), которое будет принимать список файлов в качестве аргументов и обрабатывать их. Пример простого скрипта на Python:import sys files = sys.argv[1:] # Получаем все переданные файлы print(f"Обработка файлов: {files}")
-
Использование вспомогательных скриптов:
Вам необходимо создать вспомогательный скрипт, который будет определять, запущен ли он из контекстного меню. В этом случае он должен собирать имена всех файлов и вызывать ваш основной скрипт или приложение с собранными аргументами.Пример на PowerShell:
# PowerShell скрипт param($filePath) $files = @() $instances = Get-Process -Name "YourHelperApp" -ErrorAction SilentlyContinue if ($instances) { $files += $filePath } else { Start-Process -FilePath "Path\To\YourPythonScript.py" -ArgumentList $files }
-
Добавление в реестр:
Добавьте в реестр команду, которая будет вызывать ваш вспомогательный скрипт. Пример записи в реестре:Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\*\shell\MyMergedAction] @="Объединить выбранные файлы" [HKEY_CLASSES_ROOT\*\shell\MyMergedAction\command] @="powershell.exe -ExecutionPolicy Bypass -File \"C:\\path\\to\\your_helper.ps1\" \"%1\""
-
Регистрация контекстного меню:
Убедитесь, что ваш элемент контекстного меню виден при выделении нескольких файлов. ИспользуйтеHKEY_CLASSES_ROOT\SystemFileAssociations\*\shell\MyMergedAction
.
Результат
Теперь, когда вы щелкаете правой кнопкой мыши на нескольких выделенных файлах и выбираете опцию из контекстного меню, будет вызван вспомогательный скрипт, который соберет все имена файлов и передаст их вашему основному приложению в виде одного списка аргументов.
Заключение
Создание контекстного меню для работы с несколькими файлами в Windows Explorer требует дополнительного программирования, однако оно предоставляет значительные преимущества, позволяя вызвать команду один раз и обрабатывать все файлы совместно. Использование PowerShell в качестве промежуточного скрипта позволяет эффективно управлять процессами и аргументами, обеспечивая максимальную гибкость и контроль.