Situation
我有一个第三方GUI应用程序,可以通过CLI接受多个文件,
例如:
MyProgram.exe "file1" "file2"
然后将所有文件立即加载到应用程序的同一实例中。
为了优化我的时间,我想通过右键单击 Windows资源管理器中的某些文件来加载多个文件(例如:选择5个文件>右键单击>选择“在MyProgram中打开”命令)
我知道如何创建所需的注册表项,以在上下文菜单中为特定文件类型添加该命令,这不是问题。
Problem
该第三方程序没有任何可从contextmenu捕获多个文件的驱动程序,shell扩展程序或方法,因此,如果不是我从资源管理器中选择2个文件,则每个文件都在该程序的单独实例中打开,没有开发驱动程序的想法,所以驱动程序不是我想要的。
Focus
我愿意接受建议,也许这不是有效的方法,但似乎是最简单的方法:
我的想法是开发一个迷你CLI应用程序以捕获这些多个文件(可能基于Windows消息或SO非 Activity 状态,我不知道这就是我要问的原因),将这些文件/参数写入文本文件,然后加入所有一行中的参数调用我的第三方程序,并使用这些参数在该程序的单个实例中一次加载所有文件。
换句话说,在此第三方应用程序中选择多个文件以一次打开所有文件时,只需一个简单的加载程序即可从上下文菜单中使用它。
Question
首先,我想知道是否存在一个已知术语来命名应用程序的名称,该名称能够在同一实例中加载多个文件,然后从资源管理器中选择文件,然后从contextmenu中选择文件。我想研究那个学期。
在VB.NET/C#控制台应用程序下,哪种方法最有效地完成此任务? (不是司机)
如何开始开发呢?
已知页面(例如 codeproject ...)中是否存在任何源代码示例?
最佳答案
你确实想要一个ShellExtension
您想要的并不像您想的那么简单。选择多个文件的正常行为是在新的Window/App实例中打开每个文件。实际上,它只是将所选文件发送到注册的应用程序,然后将其留给应用程序来决定如何使用它们。
不过,至少有1种快捷简便的选择:
方法1:使用发送到
打开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
动词的修饰语:对于我的MediaProps小程序,由于它涉及相同的文件类型,因此我通过添加
ViewProps
动词(将其设置为MultiSelectModel.Player
)将动词附加到VLC的文件类型上,只要动词不会混淆VLC,它就可以正常工作。不幸的是,我还没有发现一些错误。 Windows似乎仍然无法像预期的那样将所有文件粘合在一起-即使我自己动词。在注册表配置或应用程序中都缺少一个步骤-但是通过其他两种方法可以完成相同的操作,我从没有进行进一步的研究。
方法3:创建ShellExtension/ContextMenu处理程序
许多提议的解决方案最终都是Whack-a-Mole游戏,您必须在一个干预应用中修复相同的1文件-1实例问题,以便它可以将串联的参数提供给最终参与者。由于最终结果是要使用 Explorer ContextMenu 来做一些有用的事情,因此只需为此其他应用程序构建一个 ShellExtension 。
这很容易,因为已经完成了一个框架,并且该框架在CodeProject:How to Write Windows Shell Extension with .NET Languages上可用。这是MS-PL文章,其中包含完成的ShellExtension项目。
进行一些修改,这将非常适合:
多种文件类型的
此测试床是一个小程序,用于显示媒体文件的MediaInfo属性(诸如Duration,Frame Size,Codec,format等之类的内容)。除了接受拖放的文件外,它还使用ContextMenu DLL帮助器来接受在资源管理器中选择的多个文件,并将其提供给“单实例显示”应用程序。
重要提示
自从首次发布以来,我对进行了修改和更新,原始MS-PL文章的使其更易于使用。该修订版也位于CodeProject Explorer Shell Extensions in .NET (Revised)上,并且仍包含VB和C#版本。
在修订版中,不必在各处进行更改,而是将它们合并为单个变量块。本文还解释了为什么您可能要使用C#版本,并提供了指向文章的链接,这些文章解释了为什么对托管扩展程序使用托管代码不是是一个好主意。
“模型”仍然是仅用于启动相关应用程序的Shell Extension的模型。
对于一般概念和背景,这个答案的平衡仍然值得一读。即使大部分代码更改部分不适用于该修订版,在事实发生后对其进行良好更改似乎也不对。
1.更新程序集/项目值
例如,我将程序集名称更改为“MediaPropsShell”。我还删除了根 namespace ,但这是可选的。
添加您选择的PNG图标。
选择适当的平台。 由于原始版本有2个安装程序,因此您可能必须专门为32位操作系统构建x86版本。 AnyCPU可以在64位操作系统上正常工作,我不确定x86。使用此模型的大多数系统都为shell扩展帮助程序提供了32位和64位DLL,但是过去大多数都不能基于NET(在任何CPU中都可以选择AnyCPU)。
将目标平台保留为NET4。如果您没有阅读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种左右的微小变化,以微软的代码。只需接受IntelliSense建议的更改即可。重要说明
代表EXE“引擎”提供ContextMenu服务的单独DLL的模型是和很常见。这就是所有
xxxShell.DLL
文件的内容,您通常会在文件夹以及程序可执行文件中看到这些文件。此处的区别是您正在构建DLL,而不是所讨论应用程序的作者。FileContextMenuExt
类Tools
菜单上有一个方便的实用程序。 OnVerbDisplayFileName
。如您所见,我没有改变。如果更改它以使其与实际操作相匹配,则还需要在PInvoke重磅代码IContextMenu
中更改对其的一些引用。除了你,没人会看到这个名字。 原始MS项目中的自述文件对此进行了描述,但是在编译后,将文件复制到将要驻留的位置并进行注册:
regasm <asmfilename>.DLL /codebase
取消注册:regasm <asmfilename>.DLL /unregister
使用在RegAsm
文件夹中找到的Microsoft.NET\Framework64\v4.0.xxxx
。这必须在具有管理员权限(或等效脚本)的命令窗口中完成。另外,对于已部署的应用程序,可以使用Public Regster/UnRegister
方法让目标应用程序注册/注销帮助程序DLL。警告:在编译之前,请仔细更改的代码来更改并测试诸如循环和字符串格式之类的内容;您需要尽可能少的编译测试迭代。原因是,一旦激活了新的上下文菜单,资源管理器就将使用该DLL,并且该DLL无法用新的版本替换。您必须终止
explorer.exe
进程(而不仅仅是File Explorer!)才能注册并尝试新的构建。可能还有另一种方法,但是我只关闭了所有Explorer Windows,然后注销并重新打开。
测试中
如果我右键单击一种注册的文件类型,则可以使用正确的菜单文本和位图图像按预期获得菜单:
click for larger image
如果单击,则在一个实例中,applet会按预期方式出现和多个文件:
click for larger image
请注意,如何启用底部的“上一个/下一个”按钮从一个文件移动到另一个文件,而仅加载1个文件则不是这种情况。
在我的机器上工作
资源
How to Write Windows Shell Extension with .NET Languages。这是MS-PL文章,其中包含完成的ShellExtension项目。上面是一组mod,使其可以使用多个扩展名和多个文件,因此需要原始项目作为起点。
Best Practices for Shortcut Menu Handlers and Multiple Verbs
Choosing a Static or Dynamic Shortcut Menu Method
Verbs and File Associations
关于c# - 实现资源管理器ContextMenu并将多个文件传递到一个程序实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27088510/