Windowsshell扩展与C#
我想写一个简单的Windowsshell扩展添加到上下文菜单,而C#是我最近使用的语言。 这是一个不错的shell扩展select? 界面容易获得吗? 是否有额外的开销,导致菜单较慢popup?
任何人有良好的指针入门?
Raymond的文章: 不要在托pipe代码中编写进程shell扩展 。
最近的后续工作: 现在,.NET Framework的第4版支持进程内并行运行时,现在可以在托pipe代码中编写shell扩展吗?
底线是,不,这是不好的:
“ 实施进程中扩展指南”已经修订,并继续推荐使用托pipe代码编写shell扩展和Internet Explorer扩展(以及其他types的进程内扩展),即使您使用的是版本4或更高版本。
EZShellExtensions是一个非常好的(但不是免费的)在C#中进行shell扩展开发的框架。 您可以使用大约20行代码编写一个简单的上下文菜单扩展,最重要的是,不必混淆COM接口。 我的公司使用它(和它们的命名空间扩展框架)来为成千上万的客户使用一组扩展框架,我们从来没有遇到上述CLR冲突的问题。
这里有一个简单的示例来展示它是多么容易:
[Guid("00000000-0000-0000-0000-000000000000"), ComVisible(true)] [TargetExtension(".txt", true)] public class SampleExtension : ContextMenuExtension { protected override void OnGetMenuItems(GetMenuitemsEventArgs e) { e.Menu.AddItem("Sample Extension", "sampleverb", "Status/help text"); } protected override bool OnExecuteMenuItem(ExecuteItemEventArgs e) { if (e.MenuItem.Verb == "sampleverb") ; // logic return true; } [ComRegisterFunction] public static void Register(Type t) { ContextMenuExtension.RegisterExtension(typeof(SampleExtension)); } [ComUnregisterFunction] public static void UnRegister(Type t) { ContextMenuExtension.UnRegisterExtension(typeof(SampleExtension)); } }
实施进程中扩展的指导
版本冲突
版本冲突可能通过不支持在单个进程中加载多个运行时版本的运行时发生。 4.0之前版本的CLR属于这一类。 如果加载一个版本的运行时会阻止加载同一运行时的其他版本,则如果主机应用程序或其他进程内扩展使用冲突版本,则可能会产生冲突。 在与另一个进程内扩展版本冲突的情况下,冲突难以重现,因为失败需要正确的冲突扩展,失败模式取决于加载冲突扩展的顺序。
考虑使用版本4.0之前版本的CLR编写的进程内扩展。 使用文件打开对话框的计算机上的每个应用程序都可能会将对话框的托pipe代码及其随附的CLR依赖项加载到应用程序的进程中。 首先将CLR 4.0之前的版本加载到应用程序进程中的应用程序或扩展将限制该进程随后可以使用哪个版本的CLR。 如果具有“打开”对话框的托pipe应用程序构build在CLR的冲突版本上,则该扩展可能无法正确运行,并可能导致应用程序出现故障。 相反,如果扩展是第一个在进程中加载,并且托pipe代码的冲突版本试图在此之后启动(可能是托pipe应用程序或正在运行的应用程序按需加载CLR),则操作将失败。 对用户来说,应用程序的某些function似乎随机停止工作,或者应用程序神秘崩溃。
请注意,等于或晚于版本4.0的CLR版本通常不易受版本控制问题的影响,因为它们被devise为彼此共存,并与大多数4.0以前版本的CLR共存(1.0版除外,与其他版本共存)。 但是,本主题的其余部分将讨论版本冲突以外的问题。
性能问题
运行时会导致性能问题,这些问题会在加载到进程中时造成显着的性能损失。 性能损失可以是内存使用情况,CPU使用情况,已用时间,甚至是地址空间消耗。 已知CLR,JavaScript / ECMAScript和Java是高影响运行时。 由于进程内扩展可以被加载到许多进程中,并且通常在性能敏感的时刻(例如当准备显示用户的菜单时),所以高影响运行时间可以对整体响应性产生负面影响。
消耗大量资源的高影响运行时可能会导致主机进程或另一个进程内扩展失败。 例如,一个消耗数百兆字节地址空间的高影响运行时,会导致主机应用程序无法加载大型数据集。 此外,因为进程内扩展可以被加载到多个进程中,所以单个扩展中的高资源消耗可以快速地乘以整个系统的高资源消耗。
如果运行库保持加载状态,或者即使在使用该运行时库的扩展已经卸载的情况下继续占用资源,那么该运行时间也不适合在扩展中使用。
特定于.NET Framework的问题
以下各节讨论使用托pipe代码进行扩展的问题示例。 他们不是你可能遇到的所有可能的问题的完整列表。 这里讨论的问题是扩展中不支持托pipe代码的原因,也是评估其他运行时的使用时需要考虑的问题。
重入
当CLR由于Monitor.Enter,WaitHandle.WaitOne或竞争locking语句而阻塞单线程单元(STA)线程时,CLR在其标准configuration中在等待时进入嵌套消息循环。 许多扩展方法被禁止处理消息,这种不可预知和意想不到的可重入性会导致难以复制和诊断的exception行为。multithreading公寓 CLR为组件对象模型(COM)对象创build运行时可调用包装程序。 这些相同的运行时可调用包装程序稍后将由CLR的终结器(它是multithreading公寓(MTA)的一部分)销毁。 将代理从STA移动到MTA需要编组,但不是所有扩展使用的接口都可以编组。
非确定性的对象生命周期
CLR比本地代码具有较弱的对象生命周期保证。 许多扩展对对象和接口都有引用计数的要求,CLR采用的垃圾收集模型不能满足这些要求。
- 如果CLR对象获得对COM对象的引用,则运行时可调用包装程序持有的COM对象引用不会被释放,直到运行时可调用包装程序被垃圾收集为止。 非确定性释放行为可能与某些接口契约冲突。 例如,IPersistPropertyBag :: Load方法要求在Load方法返回时,不要引用属性包。
- 如果将CLR对象引用返回给本机代码,那么运行时可调用包装程序会在运行时可调用包装程序对Release的最终调用时放弃其对CLR对象的引用,但是在底层CLR对象被垃圾收集之前未定型。 非确定性定稿可能与某些接口合同冲突。 例如,缩略图处理程序需要在引用计数下降到零时立即释放所有资源。
托pipe代码和其他运行时的可接受用途
使用托pipe代码和其他运行时来实现进程外扩展是可以接受的。 进程外扩展的例子包括:
- 预览处理程序
- 基于命令行的动作,例如在shell \ verb \ command子项下注册的动作。
- 在本地服务器中实现的COM对象,用于允许进程外激活的Shell扩展点。
一些扩展可以实现为进程内或进程外扩展。 如果这些扩展不符合这些进程内扩展的要求,则可以将这些扩展实现为进程外扩展。 以下列表显示了可作为进程内或进程外扩展实现的扩展示例:
- 与在shell \ verb \ command子项下注册的DelegateExecute项关联的IExecuteCommand。
- 与在shell \ verb \ DropTarget子项下注册的CLSID关联的IDropTarget。
- 与在shell \ verb子项下注册的CommandStateHandler条目关联的IExplorerCommandState。
SharpShell
利用SharpShell,可以轻松地使用.NET Framework创buildWindows Shell扩展。
源代码托pipe在https://github.com/dwmkerr/sharpshell – 您可以在这里或那里发布问题和function要求。 支持的扩展
您可以使用SharpShell来构build下面的任何扩展:
- Shell上下文菜单
- 图标处理程序
- 信息提示处理程序
- 丢弃处理程序
- 预览处理程序
- 图标覆盖处理程序
- 缩略图Hanlders
- 属性表扩展
使用SharpShell的项目
1. Trello上下文菜单
2. 真正的随机播放器2.0
文章系列在CodeProject
- .NET Shell扩展 – Shell上下文菜单
- .NET Shell扩展 – Shell图标处理程序
- .NET Shell扩展 – Shell信息提示处理程序
- .NET Shell扩展 – Shell下拉处理程序
- .NET Shell扩展 – Shell预览处理程序
- .NET Shell扩展 – Shell Icon Overlay Handlers
- .NET Shell扩展 – Shell缩略图处理程序
- .NET Shell扩展 – Shell属性表