代码之家  ›  专栏  ›  技术社区  ›  Ian Boyd

ishellfolder.compareids的shcids_AllFields标志是什么?

  •  1
  • Ian Boyd  · 技术社区  · 6 年前

    短版

    什么是 SHCIDS_ALLFIELDS 标志 IShellFolder.CompareIDs 意思是?

    长版

    在Windows95中,微软引入了 . 它不是假设计算机由文件和文件夹组成,而是由一个抽象的 命名空间

    • 而不是从驱动器根目录开始的路径(例如 C:\Documents & Settings\Ian )
    • 路径从命名空间的根目录开始( Desktop )

    为了容纳非文件和文件夹的内容(如网络打印机、控制面板、我的Android手机):

    • 您不需要使用由反斜杠分隔的一系列名称(例如 丙: \ 用户 \ 伊恩 )
    • 你使用 皮德尔 一系列不透明的斑点(例如 桌面 这台电脑 操作系统(C:) 用户 伊恩 )

    pidl是不透明的blob,每个blob只对生成它的文件夹有意义。

    为了扩展(或使用)shell命名空间,可以实现(或调用)一个 IShellFolder 接口。

    方法之一 IShellFolder文件夹 用于向请求命名空间扩展 比较 (PID) :

    IShellFolder::CompareIds方法

    根据两个文件对象或文件夹的项标识符列表确定其相对顺序。

    HRESULT CompareIDs(
          [in] LPARAM             lParam,
          [in] PCUIDLIST_RELATIVE pidl1,
          [in] PCUIDLIST_RELATIVE pidl2
    );
    

    多年来, LPARAM 几乎总是0。从 shlobj.h C.1999年:

    // IShellFolder::CompareIDs(lParam, pidl1, pidl2)
    //   This function compares two IDLists and returns the result. The shell
    //  explorer always passes 0 as lParam, which indicates "sort by name".
    //  It should return 0 (as CODE of the scode), if two id indicates the
    //  same object; negative value if pidl1 should be placed before pidl2;
    //  positive value if pidl2 should be placed before pidl1.
    

    所以你比较了两个ID列表——不管它是什么意思来比较它们,我们就这样做了。

    Windows 2000添加了其他排序选项标志

    从开始 版本5 外壳的16位 拉帕姆 现在可以包含其他标志来控制 IShellFolder文件夹 应该处理排序。

    ShObjIdl.idl c.Windows 8.1软件开发工具包:

    // IShellFolder::CompareIDs lParam flags
    // *these should only be used if the folder supports IShellFolder2*
    //
    // SHCIDS_ALLFIELDS
    //
    // only be used in conjunction with SHCIDS_CANONCALONLY or column 0.
    // This flag requests that the folder test for *pidl identity*, that is
    // "are these pidls logically the same". This implies that cached fields
    // in the pidl that would distinguish them should be tested.
    // Without this flag, you are comparing the *object* s the pidls refer to.
    //
    // SHCIDS_CANONICALONLY
    //
    // This indicates that the sort should be *the most efficient sort possible*, the implication
    // being that the result will not be displayed to the UI: the SHCIDS_COLUMNMASK portion
    // of the lParam can be ignored. (Before we had SHCIDS_CANONICALONLY
    // we assumed column 0 was the "efficient" sort column.)
    

    请注意以下要点:

    • 经典地说,Shcids是我们拥有的最快、最有效的一种
    • 从用户界面可用性的角度来看,它不必是合乎逻辑的;它只需保持一致即可。

    正如陈瑞蒙指出的那样, the moral equivalent of a Unicode ordinal comparison

    头文件甚至注意到 习惯于 “最快” 排序。但现在我们要用旗子 “使用可用的最快排序” :

    在我们之前 SHCIDS_CANONICALONLY 我们假设列0是“高效”排序列。

    它还注意到,您可以忽略lparam的低16位(即列),因为我们不在乎—我们使用的是最有效的。

    很多情况都反映在官方文件中:

    经典地

    版本5.0。 按名称比较时,比较系统名称,但不比较显示名称。当传递此标志时,只要shell文件夹实现一致的排序函数,就可以根据shell文件夹确定的最有效的标准来比较这两个项。当比较是否相等或排序结果未显示给用户时,此标志很有用。此标志不能与其他标志组合。

    但是有了希希兹·艾菲尔德,我们开始偏离轨道了。

    头文件注意到 所有字段 只能与 经典地 :

    只能与shcids_canoncalonly或第0列一起使用。

    但软件开发工具包说 经典地 必须单独出现:

    此标志不能与其他标志组合。

    那是哪一个?

    我们可以判断头文件是错误的,sdk是cannon,并按照它所说的做。

    但是Allfields在说什么?

    有一些概念认为 所有字段 尝试 要求,但在文件后面被掩盖。

    比较itemidlist结构中包含的所有信息,而不仅仅是显示名称。

    itemidlist不包含显示名称,它包含itemidlist。他们想说我应该 只有 看看pidl blob的内容?

    • 例如,如果这两个项目是文件,文件夹应该比较它们的名称、大小、文件时间、属性以及结构中的任何其他信息。

    在什么情况下,对*同一**文件的两个引用可以具有不同的名称、大小、文件时间、属性等?

    sdk示例做了一些不同的事情

    Windows SDK 资源管理器数据提供程序外壳扩展 样品( github )似乎表现得好像 经典地 所有字段 标志将同时出现:

    HRESULT CFolderViewImplFolder::CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
    {
       if (lParam & (SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS))
       {
          // First do a "canonical" comparison, meaning that we compare with the intent to determine item
          // identity as quickly as possible.  The sort order is arbitrary but it must be consistent.
          _GetName(pidl1, &psz1);
          _GetName(pidl2, &psz2);
          ResultFromShort(StrCmp(psz1, psz2));
        }
    
        // If we've been asked to do an all-fields comparison, test for any other fields that
        // may be different in an item that shares the same identity.  For example if the item
        // represents a file, the identity may be just the filename but the other fields contained
        // in the idlist may be file size and file modified date, and those may change over time.
        // In our example let's say that "level" is the data that could be different on the same item.
        if ((ResultFromShort(0) == hr) && (lParam & SHCIDS_ALLFIELDS))
        {
           //...
        }
    }
    else
    {
       //...Compares by the column number in LOWORD of LPARAM
    }
    

    因此,我们有完全冲突的文档、标题和示例:

    所有字段

    • SDK :不能与SHCIDS一起出现
    • 邮件头 :可以随时显示
    • 示例 :只能与SHCIDS一起显示

    它想问什么

    Windows始终假定列0是 快速的 列。这可能是因为Windows Shell API作者假定PIDL的itemID始终包含名称 里面 pidl不透明斑点。

    这一点得到了加强,因为shell strret结构允许您指向pidl中的字符串。

    奖励阅读: The kooky STRRET structure

    因此,在某个时刻,他们添加了一个表示:

    • 我们不关心本地化、特定于区域的语言排序规则和Unicode规范化算法。
    • 只需对它们进行排序,因为我们需要查找重复项并检查是否相等

    这对 规范的 旗帜:

    • 对象

    但是,当他们谈论 所有字段 选项:

    如果要求我们进行全字段比较,请测试共享相同标识的项中可能不同的任何其他字段。例如:

    • 如果该项表示一个文件,则标识可能只是文件名
    • 但idlist中包含的其他字段可能是文件大小和文件修改日期,这些字段可能会随着时间的推移而更改。

    如果两个pidl表示相同 文件 比较它们的大小、日期等有什么意义?我已经告诉过你了 文件 你要我做什么? 所有字段 旗帜?为什么我不能对这些斑点进行二进制比较呢?为什么贝壳不呢?做什么? 比较对象 做那件事

    MemCmp(pidl1, pidl2)
    

    不?

    • 威尔 只有 与一起出现 经典地 ?
    • 意志 所有字段 从未 与一起出现 经典地 ?
    • 罐头 所有字段 同时出现和不出现 经典地 ?
    • 做什么? 所有字段 具有 经典地 意思是?
    • 做什么? 所有字段 没有 经典地 意思是?

    如果 所有字段 通过了吗?我应该点击底层数据存储进行查询吗 所有字段 我知道吗?

    CompareID是用来比较ID,还是用来比较对象?

    我想知道Compareids的目的是为了 绝对不是 点击底层数据存储区(如硬盘、USB电话、MAPI),仅根据您拥有的数据进行比较 现有 在pidl中。

    这有两个原因:

    • 它更快;许多命名空间在其pidl blob中包含一些元数据-无需返回磁盘/Internet
    • 尽管pidl可能引用同一个对象,但它们的元数据可能已过时。
    • shcids_通常让调用者意识到两个pidl是相同的。
    • 但是另一个电话 SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS 可以告诉我们额外的元数据可能已经过时了(尽管我不知道这些信息对调用者有什么用处)

    或许如此 经典地 手段:

    • 请将自己限制在pidl-不要触摸磁盘执行比较
    • 省略意味着: “是的,如果你真的需要,你可以点击硬盘”

    是这样吗?

    • 如果 经典地 手段: “除了pidl中的内容,不要看任何东西,告诉我这两个东西是否是同一个对象。”
    • 那么从中得到什么 所有字段 ?
    • 他们什么时候会不同?
    • 壳牌在问我什么?

    奖金问题

    • 如果 经典地 意味着执行最有效的排序,
    • 是否缺少 经典地 表示可以根据名称的本地化和自定义进行排序?
    • 是否缺少 经典地 意思是 强制性的 是否根据名称的本地化和自定义进行排序?

    “排序” 到itemID列表?

    sdk示例执行 switch 基于每列,并查找每列的值。如果这意味着我必须通过网络加载视频才能加载音频采样率?

    • 我比较皮德尔吗
    • 或者我比较这些pidl指向的对象?
    1 回复  |  直到 6 年前
        1
  •  1
  •   Anders    6 年前

    sdk示例基本正确(取决于pidl内容)。 if (lParam & (SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS)) 显然和 if ((lParam & SHCIDS_CANONICALONLY) || (lParam & SHCIDS_ALLFIELDS)) 但不告诉我们它们是否可以组合,答案是我不知道。我不明白为什么。

    只有微软壳牌团队的成员知道真正的答案,但我们可以推测。

    Win95基本上有4个标准字段。您可以在旧版本的文档中看到它们 IShellDetails 接口:

    文件系统文件夹有一个大的标准信息字段集。 前四个字段是所有文件系统文件夹的标准字段。

    Index | Title
    -------------
    0       Name
    1       Size
    2       Type
    3       Date Modified
    

    文件系统文件夹可能支持许多附加字段。 但是,不需要这样做,而且列索引 分配给这些字段可能会有所不同。

    每个虚拟文件夹都有自己独特的信息字段集。 通常,项的显示名称在列0中,但顺序 可用字段的内容取决于 特定文件夹对象。

    在Windows2000中,当支持shell扩展时,情况发生了变化。 column handlers 已添加。这是物业系统为景观堆叠支持等提供动力的基础,而柱索引则是从 PROPERTYKEY 对于项目属性( 属性键 被认为是 SHCOLUMNID 回到那时)。

    经典地说:

    这里的重要部分是规范的。

    msdn说

    按名称比较时,比较系统名称,但不比较显示名称。

    shell与术语display name的用法不一致,但它的实际含义是,比较解析名称,而不是在资源管理器中看到的名称。

    例如,文件夹视图可能包含“foo”和“foo”文件,但实际上它们是“foo.jpg”和“foo.png”,但“隐藏文件扩展名”功能隐藏了真名。

    这个 IShellFolder 实现知道其pidl中的哪个属性(列)对于其文件夹中的每个项都是唯一的,应该使用它进行比较。

    所有字段:

    这只意味着您希望比较所有支持的列,直到找到差异为止。

    它可以实现为:

    for (UINT i = 0; i < mycolumcount; ++i)
    {
      hr = CompareIDs(i, pidl1, pidl2);
      if (hr && SUCCEEDED(hr)) break;
    }
    return hr;
    

    奖金问题

    SHCIDS_CANONICALONLY 不关心您的比较,它可以本地化/定制或不。在PIDL中存储本地化数据是一个坏主意,所以在大多数情况下它不是。

    其他列通常也不会与本地化数据进行比较。理想情况下,比较函数的级别低于显示代码,并且仅当必须向调用者返回字符串时才返回本地化字符串。

    项目属性有两个使用者:

    • 外壳视图。这些字符串作为本地化/自定义字符串返回,通常显示为ListView项。旧的 IShellDetails 可用于以文件夹认为正确的任何方式将其作为纯字符串进行检索。

    • 产权制度。返回者 IShellFolder2::GetDetailsEx 作为一个 VARIANT . 日期和数字由消费者格式化,而不是由文件夹格式化。

    IShellFolder::GetDisplayNameOf 检索“主列”,其中 SHGDN_NORMAL 是本地化/自定义的名称,并且 SHGDN_FORPARSING 通常与所比较的属性相同 经典地 .

    实施例

    typedef struct { UINT16 cb; WCHAR name[99]; UINT size; bool isFolder } MYITEM;
    enum { COL_NAME = 0, COL_SIZE, COLCOUNT, COLCANONICAL = COL_NAME };
    
    MYITEM* GetDataPtr(PCUIDLIST_RELATIVE pidl) { ... }
    bool IsFolder(MYITEM*p) { ... }
    
    void GetForDisplay_Name(WCHAR*buf, MYITEM*p)
    {
      lstrcpy(buf, p->name);
      SHGetSetSettings(...);
      if (!ss.fShowExtensions && !IsFolder(p)) PathRemoveExtension(buf); // Assuming p->name is a "filenameish" property.
    }
    
    void GetForDisplay_Size(WCHAR*buf, MYITEM*p)
    {
      // Localized size string returned by GetDetailsOf, not used by CompareIDs
    }
    
    HRESULT CompareIDs(LPARAM lParam, PCUIDLIST_RELATIVE pidl1, PCUIDLIST_RELATIVE pidl2)
    {
      HRESULT hr = E_FAIL; // Bad column
      MYITEM *p1 = GetDataPtr(pidl1), *p2 = GetDataPtr(pidl2); // A real implementation must validate items
    
      if (lParam & (SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS))
      {
        hr = ResultFromShort(StrCmp(p1->name, p2->name));
    
        if ((ResultFromShort(0) == hr) && (lParam & SHCIDS_ALLFIELDS))
        {
          for (UINT i = 0; i < COLCOUNT; ++i)
          {
            // if (COLCANONICAL == i) continue; // This optimization might be valid, depends on the difference between a items canonical and display name
            hr = CompareIDs(i, pidl1, pidl2);
            if (hr && SUCCEEDED(hr)) break;
          }
        }
    
        return hr;
      }
    
      WCHAR b1[99], b2[99];
      switch(LOWORD(lParam))
      {
      case COL_NAME:
        GetForDisplay_Name(b1, p1);
        GetForDisplay_Name(b2, p2);
        return ResultFromShort(StrCmp(b1, b2));
      case COL_SIZE:
        return ResultFromShort(p1->size - p2->size);
      }
      return hr;
    }