【发布时间】:2017-12-08 00:10:48
【问题描述】:
有两种实用的方法可以从 C# 访问 Shell 命名空间,MS WindowsApiCodePack 桥接到IShellFolder 和相关的东西(在 NuGet 上可用),或者直接引用 Shell32(Microsoft Shell 控件和自动化)。这两种方法基本相似,虽然第一种功能更多(我最感兴趣的是对图像缩略图的支持和更好的文件复制功能)。
枚举 This PC 文件夹的方式如下:
string ThisPC = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}";
var folder = ShellFolder.FromParsingName(ThisPC) as ShellFolder;
foreach (var child in folder)
或
var shellAppType = Type.GetTypeFromProgID("Shell.Application");
object shell = Activator.CreateInstance(shellAppType);
var ThisPC = (Folder)shellAppType.InvokeMember("NameSpace", BindingFlags.InvokeMethod, null, shell, new object[] { 0x11 });
foreach (FolderItem child in ThisPC.Items())
但两者之间有一个非常重要的区别:第二个枚举设备,如连接的电话,而第一个不枚举。我利用 Api Code Pack 是开源的事实并修改了 ShellFolderItems 构造函数(这是 ShellFolder 迭代的内容)来尝试
HResult hr = nativeShellFolder.NativeShellFolder.EnumObjects(
IntPtr.Zero,
//ShellNativeMethods.ShellFolderEnumerationOptions.IncludeHidden | ShellNativeMethods.ShellFolderEnumerationOptions.IncludeSuperHidden |
//ShellNativeMethods.ShellFolderEnumerationOptions.Shareable |
ShellNativeMethods.ShellFolderEnumerationOptions.Folders | ShellNativeMethods.ShellFolderEnumerationOptions.NonFolders | ShellNativeMethods.ShellFolderEnumerationOptions.Storage,
out nativeEnumIdList);
代码最初过滤了文件夹和非文件夹,但这首先排除了手机。所以我加了Storage,隐藏的东西,所有我能想到的组合,但完全没有区别。某些过滤器组合会删除已枚举的项目,但不会将其增加到设备。
我该怎么做才能让第一种方法像第二种方法一样枚举?
更新:为了更容易重现,这里是整个程序,实际上并没有比上面描述的多多少:
using System;
using System.Reflection;
using Microsoft.WindowsAPICodePack.Shell;
using Shell32;
namespace ConsoleApp1 {
class Program {
private static void ShellObjects1() {
string ThisPC = "::{20D04FE0-3AEA-1069-A2D8-08002B30309D}";
var folder = ShellFolder.FromParsingName(ThisPC) as ShellFolder;
foreach (var child in folder)
Console.WriteLine($"{child.Name} = {child.ParsingName}");
}
private static void ShellObjects2() {
var shellAppType = Type.GetTypeFromProgID("Shell.Application");
object shell = Activator.CreateInstance(shellAppType);
var ThisPC = (Folder)shellAppType.InvokeMember("NameSpace", BindingFlags.InvokeMethod, null, shell, new object[] { 0x11 });
foreach (FolderItem child in ThisPC.Items())
Console.WriteLine($"{child.Name} = {child.Path}");
}
static void Main(string[] args) {
Console.WriteLine("WindowsAPICodePack");
ShellObjects1();
Console.WriteLine();
Console.WriteLine("Shell32");
ShellObjects2();
Console.WriteLine();
}
}
}
您应该从 COM 项中添加对 Microsoft Shell 的引用并安装 Microsoft.WindowsAPICodePack-Shell NuGet 包来编译它。当我运行它时,这两个列表几乎是相同的。就像在资源管理器窗口中一样,首先是文件夹,然后是驱动器。但在第二个列表中,中间会有一个额外的条目:一个设备。就像在资源管理器中一样。第一个列表中缺少该项目。
【问题讨论】:
-
您是否尝试过使用相同的位数(32 对 64)?另外你为什么要通过 ShellNativeMethods.ShellFolderEnumerationOptions.Storage?这会过滤带有 SFGAO_STORAGE 标志 (msdn.microsoft.com/en-us/library/windows/desktop/bb762589.aspx) 的项目。所有项目都没有那个标志。文件夹和非文件夹确实是默认过滤器。
-
我使用了 AnyCPU。因为,正如我所提到的,我尝试了所有组合。 MS 源最初只指定了 Folders 和 NonFolders,因为它一开始没有枚举手机,所以我也开始尝试其他标志。没有任何组合可以提供我可以从 Shell32 调用中看到的结果。
-
这是非常出乎意料的。 IShellFolder::EnumObjects 方法是所有枚举之母。没有别的办法。如果我写一个 shell 文件夹命名空间扩展,它将在这个界面上被 exclusively 调用。你能提供一些代码或步骤来帮助重现吗?我无法复制。
-
我可以复制整个程序,但实际上并没有比上面的多多少。一个简单的控制台应用程序,使用这两个枚举,
foreach都简单地打印每个项目的名称和解析名称。两个打印输出会有所不同:第一个不会报告设备,而第二个会。尝试了几台不同的机器,W10 Creator,64 位,各种手机。实际上,我从来没有见过我生活中列举的设备(除了 Win 本身在它的资源管理器中,当然,我是指以编程方式)。使用 Shell32 是我第一次看到它们出现。 -
好消息是:我可以重现,而且只有 APIPack 可以做到这一点。如果我使用 C++ IEnumShellItems 或 IShellFolder::EnumObjects,它会返回正常的电话。坏消息是:到目前为止我还不明白 APIPack 为什么会这样做。一切似乎都声明正确,实际上与我在 C++ 中所做的完全一样......我会尝试进一步挖掘......