Вопрос или проблема
Я хотел бы создать действие в контекстном меню Проводника Windows для нескольких выбранных файлов.
Я создал ключ 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, поэтому нет ограничений на количество или длину пути передаваемых файлов. Также она не требует межпроцессного взаимодействия, сохраняя порядок файлов. Команда для открытия файлов задается в значениях реестра. Смотрите репозиторий для получения деталей.
Извините, если вы уже получили ответ, но я думаю, что единственный способ сделать это – поместить ярлык вашей программы в папку “Отправить” (вы можете открыть ее из окна “Выполнить”, введя shell:sendto) и затем выделить оба файла и выбрать вашу программу из контекстного меню “Отправить”.
РЕЗЮМЕ
-
В реестре укажите целевую программу как 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: ; =============Запустить как администратор=============
If (!A_IsAdmin) ; ЕСЛИ не администратор
{
Run, *RunAs "%A_ScriptFullPath%" ; Запустить скрипт от имени администратора
ExitApp ; Завершить текущий экземпляр, работающий без прав администратора
}
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: ; =============Добавить в контекстное меню=============
path := "" ; путь
program := "Context Menu Create File" ; программа
contextMenu := "Open With Chrome" ; contextMenu
regPath := "HKCR\*\shell" ; regPath
StringReplace, regKey, contextMenu, %A_Space%, , A ; regKey
regKey := 0 regKey ; 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 ; regPath
ControlFocus, Edit1, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; regPath
ControlSend, Edit1, {Enter}, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; regPath
ControlSend, SysListView321, {Control Down}{NumpadAdd}{Control Up}, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; regPath
ControlSend, SysListView321, {F5}, Редактор реестра ahk_class RegEdit_RegEdit ahk_exe regedit.exe ; regPath
MsgBox, 262192, ПРОВЕРКА, Проверьте, добавлен ли %contextMenu% в реестр ; ПРОВЕРКА
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 содержит .,\
, чтобы убедиться, что 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 ; Исправить высокую загрузку процессора
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 .,\ ; ЕСЛИ буфер обмена содержит .,\
{
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
-
Выберите файлы, щелкните правой кнопкой мыши, и элемент контекстного меню Open With Chrome должен появиться.
Я столкнулся с аналогичной проблемой, но в довольно другом контексте… Вот мое решение. На самом деле, я использую тот же самый хак реестра, который приводит к множественным вызовам процессов, но принимающая команда – это скрипт 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
)
# Получение текущего идентификатора процесса 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
}
# Вывод идентификатора процесса и командной строки
Write-Host "Идентификатор процесса: $($process.Id), Командная строка: $commandLine"
}
} catch {
# Обработка любых исключений (например, доступ запрещен) и вывод ошибки
Write-Host "Ошибка обработки идентификатора процесса $($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 " "
}
}
Ответ или решение
Создание контекстного меню для выполнения действия над несколькими выбранными файлами в Windows Explorer может оказаться непростой задачей, особенно когда необходимо обрабатывать их одновременно и передавать в скрипт как список аргументов. Давайте рассмотрим, как это можно сделать, сочетая изменения в реестре и использование PowerShell для достижения этой цели.
Изменение реестра Windows
Для того чтобы добавить новое действие в контекстное меню, необходимо отредактировать реестр Windows. Вот основные шаги, которые помогут вам в этом:
- Откройте редактор реестра (нажмите Win + R, введите
regedit
и нажмите Enter). - Перейдите к следующему ключу:
HKEY_CLASSES_ROOT\*\shell
- Создайте новый ключ с именем вашего действия, например
MyAction
. - Создайте подключ для команды:
HKEY_CLASSES_ROOT\*\shell\MyAction\command
- Установите значение по умолчанию этого ключа на команду, которая будет выполняться. Как пример:
powershell.exe -NoProfile -ExecutionPolicy Bypass -File "C:\path\to\your_script.ps1" "%1"
Написание скрипта PowerShell
Поскольку Windows по умолчанию вызывает команды для каждого выбранного файла отдельно, нам нужен способ сгруппировать их. Вот один из подходов, использование PowerShell для сборки списка выбранных файлов:
# my_script.ps1
param (
[string[]]$files
)
# Ваш код обработки файлов
foreach ($file in $files) {
Write-Host "Обрабатываю файл: $file"
}
# Ваша основная логика обработки
Шаги для реализации
-
Создайте скрипт PowerShell (
my_script.ps1
), который будет принимать множественные аргументы. -
Запустите команду с передачей всех выбранных файлов. Это можно сделать следующим образом. Используйте реестр для привязки к вашему скрипту с корректной обработкой аргументов:
- Убедитесь, что в вашем скрипте вы используете
param()
для сбора всех аргументов. Например:param ( [string[]]$files = @() # Аргумент по умолчанию )
- Убедитесь, что в вашем скрипте вы используете
-
Обработка аргументов: Внутри вашего PowerShell скрипта, вы можете обрабатывать множественные файлы следующим образом:
if ($files.Length -gt 0) { foreach ($file in $files) { # Логика обработки каждого файла } } else { Write-Host "Не выбраны файлы." }
Заключение
Таким образом, с помощью вышеописанных шагов вы сможете создать действительное решение для обработки нескольких файлов через контекстное меню в Windows Explorer. Этот метод позволяет избежать запуска скрипта для каждого файла по отдельности, что значительно упрощает процесс и делает его более эффективным.
Для дальнейших улучшений вы можете интегрировать обработку ошибок и добавление необходимых проверок в ваш PowerShell скрипт, что обеспечит более надежную работу вашей автоматизации.
SEO Оптимизация
Учитывая актуальность темы, убедитесь, что вы используете ключевые слова, такие как "контекстное меню Windows", "обработка множественных файлов", "PowerShell скрипт" и "реестр Windows", чтобы улучшить видимость вашего контента в поисковых системах.