Ответ 1
Вы хотите ShellExtension
То, что вы хотите, не так просто, как вы думаете. Обычное поведение для множественного выбора файлов заключается в открытии каждого из них в новом экземпляре Window/App. Фактически, он просто отправляет выбранные файлы в зарегистрированное приложение и оставляет его в приложении, чтобы решить, как с ними работать.
Существует, по крайней мере, 1 быстрая и простая альтернатива:
Способ 1: Использовать Send-To
Откройте папку Send To
("C:\Users\YOURNAME\AppData\Roaming\Microsoft\Windows\SendTo"
) и добавьте запись для приложения. Целью было бы приложение, которое вы хотите передать/отправить выбор файла:
"C:\Program Files\That Other App\OtherApp.exe "
Вам не нужны заполнители "% 1" или что-то еще. Вам не нужно писать посредника, чтобы что-либо сделать, просто отправьте файлы непосредственно в приложение. Он будет работать нормально, если приложение будет принимать более одного файла в командной строке.
Единственное, что связано с тем, что он находится в "общем" или общем подменю, а не в контекстном меню верхнего уровня. Он также не "умный", поскольку он доступен для любого расширения файла, в отличие от соответствующего обработчика ContextMenu, но это быстрое и простое решение без кода, которое существует уже давно.
Способ 2. Измените глагольный квалификатор
Вы также можете изменить квалификатор/режим глагола, который звучит как самый простой способ. Возьмем, к примеру, проигрыватель VideoLan VLC:
Если вы нажмете несколько файлов .MP4, а не открываете несколько экземпляров, он открывается одним из них, а остальные будут поставлены в очередь для воспроизведения. Это делается путем изменения глагола в реестре:
+ VLC.MP4
+ shell
+ Open
- MultiSelectModel = Player
+ Command
- (Default) "C:\Program Files.... %1"
MultiSelectModel
является модификатором для глагола Open
:
- Одиночный для глаголов, поддерживающих только один элемент
- Player для глаголов, поддерживающих любое количество элементов
- Документ для глаголов, которые создают окно верхнего уровня для каждого элемента
Для моего апплета MediaProps, поскольку он касается одинаковых типов файлов, я включил свой глагол в типы файлов VLC, добавив глагол ViewProps
, который был установлен как MultiSelectModel.Player
и, как правило, работал в той мере, глаголы не путают VLC.
К сожалению, есть еще что-то нехорошее, что я еще не определил. Кажется, что Windows по-прежнему не склеивает все файлы вместе, как и ожидалось, даже если я создаю свои собственные глаголы. В конфигурации реестра или с приложением отсутствует шаг, но с двумя другими способами сделать то же самое, я никогда не исследовал дальше.
Способ 3: Создать обработчик ShellExtension/ContextMenu
Многие предлагаемые решения в конечном итоге являются игрой Whack-a-Mole, где вы должны исправить то же самое 1 проблема экземпляра файла-1 в промежуточном приложении, чтобы он мог передавать конкатенированные аргументы конечному игроку. Поскольку конечный результат состоит в том, чтобы Explorer ContextMenu сделать что-то полезное, просто создайте ShellExtension для этого другого приложения.
Это легко, потому что фреймворк уже сделан и доступен в CodeProject: Как написать расширение Windows Shell с .NET-языками. Это статья MS-PL с готовым проектом ShellExtension.
С помощью нескольких модификаций это будет отлично работать:
- установочные ассоциации для нескольких типов файлов
- собрать несколько файлов, нажатых
- отформатировать их в командной строке arg set
- передать командную строку фактическому рабочему приложению
- предоставить пользовательский ContentMenu
- отображает значки меню
Тест-кровать для этого является апплетом для отображения свойств MediaInfo медиафайлов (таких как Duration, Frame Size, Codec, format и т.д.). В дополнение к принятию Dropped файлов он использует помощник DLL ContextMenu для приема нескольких файлов, выбранных в Проводнике, и передает их в приложение отображения Single Instance.
Очень важное примечание
Поскольку это было впервые опубликовано, у меня есть переработанная и обновленная оригинальная статья MS-PL, что делает ее намного проще в использовании. Пересмотр также находится в CodeProject Расширения оболочки проводника в .NET(пересмотренный) и содержит версию VB и С#.
В пересмотренной версии вместо того, чтобы делать изменения здесь и там, они объединяются в один блок переменных. В статье также объясняется, почему вы можете использовать версию С# и ссылаетесь на статьи, объясняющие, почему не рекомендуется использовать управляемый код для расширений оболочки.
"Модель" остается для расширения оболочки, чтобы просто запустить соответствующее приложение.
Баланс этого ответа по-прежнему стоит прочитать для общих концепций и фона. Не кажется правильным изменить его после факта, даже если большая часть раздела Изменения кода не применяется к ревизии.
1. Обновить значения сборки/проекта
Например, я изменил имя сборки на "MediaPropsShell". Я также удалил корневое пространство имен, но это необязательно.
Добавьте значок PNG по вашему выбору.
Выберите подходящую платформу.. Поскольку в оригинале есть 2 установщика, вам, возможно, придется специально построить версию x86 для 32-разрядной ОС. AnyCPU отлично работает для 64-битной ОС, я не уверен в x86. Большинство систем, которые используют эту модель, предоставляют 32 и 64-разрядную DLL для вспомогательного помощника оболочки, но большинство из них в прошлом не могло быть основано на NET, либо там, где AnyCPU является опцией.
Держите целевую платформу как NET 4. Если вы не читали статью CodeProject или не исследовали ее ранее, это важно.
2. Изменения кода
Как опубликовано в CodeProject, обработчик также передает только один файл и связывает себя только с одним типом файла. В приведенном ниже коде используется обработчик для нескольких типов файлов. Вы также захотите исправить имена меню и так далее. Все изменения отмечены в коде ниже предисловий с помощью {PL}
:
' {PL} - change the GUID to one you create!
<ClassInterface(ClassInterfaceType.None),
Guid("1E25BCD5-F299-496A-911D-51FB901F7F40"), ComVisible(True)>
Public Class MediaPropsContextMenuExt ' {PL} - change the name
Implements IShellExtInit, IContextMenu
' {PL} The nameS of the selected file
Private selectedFiles As List(Of String)
' {PL} The names and text used in the menu
Private menuText As String = "&View MediaProps"
Private menuBmp As IntPtr = IntPtr.Zero
Private verb As String = "viewprops"
Private verbCanonicalName As String = "ViewMediaProps"
Private verbHelpText As String = "View Media Properties"
Private IDM_DISPLAY As UInteger = 0
Public Sub New()
' {PL} - no NREs, please
selectedFiles = New List(Of String)
' Load the bitmap for the menu item.
Dim bmp As Bitmap = My.Resources.View ' {PL} update menu image
' {PL} - not needed if you use a PNG with transparency (recommended):
'bmp.MakeTransparent(bmp.GetPixel(0, 0))
Me.menuBmp = bmp.GetHbitmap()
End Sub
Protected Overrides Sub Finalize()
If (menuBmp <> IntPtr.Zero) Then
NativeMethods.DeleteObject(menuBmp)
menuBmp = IntPtr.Zero
End If
End Sub
' {PL} dont change the name (see note)
Private Sub OnVerbDisplayFileName(ByVal hWnd As IntPtr)
'' {PL} the command line, args and a literal for formatting
'Dim cmd As String = "C:\Projects .NET\Media Props\MediaProps.exe"
'Dim args As String = ""
'Dim quote As String = """"
'' {PL} concat args
For n As Integer = 0 To selectedFiles.Count - 1
args &= String.Format(" {0}{1}{0} ", quote, selectedFiles(n))
Next
' Debug command line visualizer
MessageBox.Show("Cmd to execute: " & Environment.NewLine & "[" & cmd & "]", "ShellExtContextMenuHandler")
'' {PL} start the app with the cmd line we made
'If selectedFiles.Count > 0 Then
' Process.Start(cmd, args)
'End If
End Sub
#Region "Shell Extension Registration"
' {PL} list of media files to show this menu on (short version)
Private Shared exts As String() = {".avi", ".wmv", ".mp4", ".mpg", ".mp3"}
<ComRegisterFunction()>
Public Shared Sub Register(ByVal t As Type)
' {PL} use a loop to create the associations
For Each s As String In exts
Try
ShellExtReg.RegisterShellExtContextMenuHandler(t.GUID, s,
"MediaPropsShell.MediaPropsContextMenuExt Class")
Catch ex As Exception
Console.WriteLine(ex.Message)
Throw ' Re-throw the exception
End Try
Next
End Sub
<ComUnregisterFunction()>
Public Shared Sub Unregister(ByVal t As Type)
' {PL} use a loop to UNassociate
For Each s As String In exts
Try
ShellExtReg.UnregisterShellExtContextMenuHandler(t.GUID, s)
Catch ex As Exception
Console.WriteLine(ex.Message) ' Log the error
Throw ' Re-throw the exception
End Try
Next
End Sub
#End Region
В разделе IShellExtInit Members
REGION нужно изменить немного ниже:
Public Sub Initialize(pidlFolder As IntPtr, pDataObj As IntPtr,
hKeyProgID As IntPtr) Implements IShellExtInit.Initialize
If (pDataObj = IntPtr.Zero) Then
Throw New ArgumentException
End If
Dim fe As New FORMATETC
With fe
.cfFormat = CLIPFORMAT.CF_HDROP
.ptd = IntPtr.Zero
.dwAspect = DVASPECT.DVASPECT_CONTENT
.lindex = -1
.tymed = TYMED.TYMED_HGLOBAL
End With
Dim stm As New STGMEDIUM
' The pDataObj pointer contains the objects being acted upon. In this
' example, we get an HDROP handle for enumerating the selected files
' and folders.
Dim dataObject As System.Runtime.InteropServices.ComTypes.IDataObject = Marshal.GetObjectForIUnknown(pDataObj)
dataObject.GetData(fe, stm)
Try
' Get an HDROP handle.
Dim hDrop As IntPtr = stm.unionmember
If (hDrop = IntPtr.Zero) Then
Throw New ArgumentException
End If
' Determine how many files are involved in this operation.
Dim nFiles As UInteger = NativeMethods.DragQueryFile(hDrop,
UInt32.MaxValue, Nothing, 0)
' ********************
' {PL} - change how files are collected
Dim fileName As New StringBuilder(260)
If (nFiles > 0) Then
For n As Long = 0 To nFiles - 1
If (0 = NativeMethods.DragQueryFile(hDrop, CUInt(n), fileName,
fileName.Capacity)) Then
Marshal.ThrowExceptionForHR(WinError.E_FAIL)
End If
selectedFiles.Add(fileName.ToString)
Next
Else
Marshal.ThrowExceptionForHR(WinError.E_FAIL)
End If
' {/PL}
' *** no more changes beyond this point ***
' [-or-]
' Enumerates the selected files and folders.
'...
Finally
NativeMethods.ReleaseStgMedium((stm))
End Try
End Sub
В исходном коде на самом деле есть код для многофайлового метода, который закомментирован. Я действительно не видел его, прежде чем добавлять его. Измененная часть находится между звездообразными строками.
Кроме того, это печально, но с Option Strict
вам придется внести 10 или около того небольших изменений в код Microsoft. Просто примите изменения, предлагаемые IntelliSense.
Важные примечания
Модель отдельной DLL для предоставления услуг ContextMenu от имени EXE-движка " очень является общей. Это все файлы xxxShell.DLL
, которые вы часто видите в папках вместе с исполняемыми программами. Разница здесь в том, что вы создаете DLL, а не автор приложения.
- Все изменения, кроме одного, находятся в классе
FileContextMenuExt
- Обязательно измените GUID, иначе ваш обработчик может столкнуться с другими на основе того же шаблона MS! В вашем меню
Tools
есть удобная утилита. - BMP/PNG необязательно
- Оригинальная версия MS просто отображает имя выбранного файла. Соответственно, соответствующая процедура называется
OnVerbDisplayFileName
. Как вы видите, я не изменил этого. Если вы измените его в соответствии с фактической операцией, вам также нужно будет изменить некоторые ссылки на него в тяжелом коде PInvoke дляIContextMenu
. Никто, кроме вас, никогда не увидит это имя. - Отладка MessageBox - это все, что есть для действия invoke. Вы можете видеть, как используется мой код.
ReadMe в исходном проекте MS описывает это, но после компиляции скопируйте файл туда, где он будет находиться, и зарегистрируйте его:
regasm <asmfilename>.DLL /codebase
Отменить регистрацию:
regasm <asmfilename>.DLL /unregister
Используйте RegAsm
, найденный в вашей папке Microsoft.NET\Framework64\v4.0.xxxx
. Это нужно сделать из окна команд с правами администратора (или эквивалентом script). В качестве альтернативы для развернутого приложения вы можете настроить целевое приложение для регистрации/отмены регистрации вспомогательной DLL с помощью методов Public Regster/UnRegister
.
Предупреждение: тщательно измените свой код и проверите вещи, такие как циклы и строковые форматы , прежде чем компилировать; вы хотите как можно меньше итераций для компиляции. Причина в том, что после активации нового контекстного меню DLL используется Explorer и не может быть заменена новой сборкой. Вам нужно завершить процесс explorer.exe
(а не только Проводник!) Для регистрации и попробовать новую сборку.
Может быть другой способ, но я просто закрываю любую проводку Windows, а затем выхожу и снова включаю.
Тестирование
Если я нажимаю правой кнопкой мыши на один из зарегистрированных типов файлов, я получаю меню, как ожидалось, с правильным текстом меню и растровым изображением:
нажмите для увеличения изображения
Если я нажму, апплет появится как ожидалось с несколькими файлами в одном экземпляре:
нажмите для увеличения изображения
Обратите внимание, что кнопки Prev/Next внизу позволяют перемещаться из файла в файл, что не соответствует загрузке только одного файла.
Работает на моей машине TM
Ресурсы
Как написать расширение Windows Shell с языками .NET. Это статья MS-PL с готовым проектом ShellExtension. Вышеупомянутый набор модов, чтобы он работал с несколькими расширениями и несколькими файлами, поэтому исходный проект требуется в качестве отправной точки.
Рекомендации для ярлыков меню и несколько глаголов
Выбор метода статического или динамического контекстного меню