IShellFolder.CompareIDs 的 SHCIDS_ALLFIELDS 标志是什么?
What is the SHCIDS_ALLFIELDS flag of IShellFolder.CompareIDs trying to be?
精简版
IShellFolder.CompareIDs的SHCIDS_ALLFIELDS
标志是什么意思?
长版
在Windows95中,微软引入了shell。它不是假设计算机由文件和文件夹组成,而是由项目的抽象 命名空间 组成。
- 而不是从驱动器根目录开始的路径(例如
C:\Documents & Settings\Ian
)
- 路径从命名空间的根开始 (
Desktop
)
并且为了容纳不是文件和文件夹的东西(例如网络打印机、控制面板、我的 Android phone):
- 您不使用由反斜杠分隔的一系列名称(例如 C: \ Users \ Ian )
- 您使用 pidls,一系列不透明的 blob(例如 Desktop This PC OS (C:) 用户 Ian)
PIDL 是不透明的 blob,每个 blob 只对生成它的文件夹有意义。
为了扩展(或使用)shell 命名空间,您实现(或调用)IShellFolder 接口。
IShellFolder 的方法之一用于请求名称空间扩展以 compare 到 ID 列表 (PIDLs):
IShellFolder::CompareIDs method
Determines the relative order of two file objects or folders, given their item identifier lists.
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 添加了额外的排序选项标志
从 shell 的 版本 5 开始,LPARAM
的高 16 位现在可以包含额外的标志来控制 IShellFolder 应该处理排序。
来自ShObjIdl.idl
Windows 8.1 SDK:
// 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_CANONICALONLY 意味着我们拥有的最快最有效的排序
- 从 UI 可用性的角度来看,它不一定是合乎逻辑的;它必须是一致的
正如雷蒙德陈指出的那样,它是the moral equivalent of a Unicode ordinal comparison。
header 文件甚至指出我们使用 假设第 0 列是 "fastest" 排序.但是现在我们将使用一个标志来 say "use the fastest sort available":
Before we had SHCIDS_CANONICALONLY
we assumed column 0 was the "efficient" sort column.
它还指出您可以忽略 LPARAM 的低 16 位(即列),因为我们不关心 - 我们使用的是最有效的。
很多内容都反映在官方文档中:
SHCIDS_CANONICALONLY
Version 5.0. When comparing by name, compare the system names but not the display names. When this flag is passed, the two items are compared by whatever criteria the Shell folder determines are most efficient, as long as it implements a consistent sort function. This flag is useful when comparing for equality or when the results of the sort are not displayed to the user. This flag cannot be combined with other flags.
但是 SHCIDS_ALLFIELDS 我们开始 运行 关闭 rails
header 文件指出 AllFields 只能与 CanonicalOnly:
组合
only be used in conjunction with SHCIDS_CANONCALONLY or column 0.
但是SDK说CanonicalOnly必须单独出现:
This flag cannot be combined with other flags.
那是哪一个?
我们可以确定 header 文件是错误的,SDK 是大炮,然后按照它说的去做。
但是 AllFields 在说什么?
有一些概念 AllFields 试图 要求,但在文档后面被遮盖了。
Compare all the information contained in the ITEMIDLIST structure, not just the display names.
ItemIDList 不包含显示名称,它包含一个 ItemIDList。他们是想说我应该 只 查看 pidl blob 的内容吗?
- 例如,如果两个项目是文件,文件夹应该比较它们的名称、大小、文件时间、属性和结构中的任何其他信息。
在什么情况下对*同一个**文件的两个引用可能具有不同的名称、大小、文件时间、属性等?
SDK 示例做一些不同的事情
Windows SDK Explorer Data Provider Shell Extension 示例(github),看起来就像 CanonicalOnly 和 AllFields 标志会一起出现:
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
}
所以我们有完全冲突的文档、headers 和样本:
SHCIDS_ALLFIELDS
- SDK: 永远不会出现 SHCIDS_CANONICALONLY
- Headers:随时可以出现
- 例子: 只能和SHCIDS_CANONICALONLY
一起出现
它想问什么
Windows 总是假设第 0 列是 fast 列。这可能是因为 Windows shell API 作者假定 PIDL 的 ItemID 将始终包含名称 inside pidl 不透明 blob。
This is reinforced by the fact that the shell STRRET structure lets you point to a string inside your pidl.
Bonus Reading: The kooky STRRET structure
所以在某些时候他们添加了一个明确的标志,上面写着:
- 我们不关心本地化、区域特定的语言排序规则和 unicode 规范化算法
- 只需对它们进行排序,因为我们需要查找重复项并检查是否相等
这对 canonical 标志有意义
- 告诉我两个 IDList 是否指向同一个 object
但是当他们谈论 所有字段 选项时,SDK 示例意味着什么:
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.
如果两个 PIDL 表示相同的 文件 比较它们的大小、日期等有什么意义?我已经告诉过你它们是相同的 file,你用 All Fields 标志问我什么?为什么我不能对 blob 进行二进制比较? shell 为什么不呢? CompareIDs 是做什么的
MemCmp(pidl1, pidl2)
不是吗?
SHCIDS_ALLFIELDS
只有会和SHCIDS_CANONICALONLY
一起出现吗?
-
SHCIDS_ALLFIELDS
永远不会 与 SHCIDS_CANONICALONLY
一起出现吗?
SHCIDS_ALLFIELDS
是否可以同时出现 SHCIDS_CANONICALONLY
?
SHCIDS_ALLFIELDS
withSHCIDS_CANONICALONLY
是什么意思?
SHCIDS_ALLFIELDS
没有SHCIDS_CANONICALONLY
是什么意思?
如果SHCIDS_ALLFIELDS
通过了,它要我做什么?我是否应该点击底层数据存储来查询我知道的所有字段?
CompareIDs是用来比较ID的,还是用来比较objects的?
我想知道 CompareIDs 的目的是否是 绝对不 访问底层数据存储(例如硬盘,phone over USB,Mapi),并且只比较根据你在 pidl.on-hand 中的内容。
这有两个原因:
- 速度更快;许多命名空间在其 PIDL blob 中包含一定数量的元数据 - 无需返回到 disk/Internet
- 即使 pidl 可能引用相同的 object,它们的元数据也可能已过时
- SHCIDS_CANONICALONLY 让调用者意识到两个 pidl 是同一个东西
- 但是使用
SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS
的单独调用可以告诉我们额外的元数据可能已过时(尽管我不知道这些信息对调用者有什么用)
所以也许 SHCIDS_CANONICALONLY
意味着:
- 请将自己限制在 pidl - 不要触摸磁盘来执行比较
- 省略它意味着:"Yeah, you can hit the hard drive if you really need to"
是这样吗?
- 如果
SHCIDS_CANONICALONLY
表示:"Don't look at anything besides whats in the pidl, and tell me if these two things are the same object"
- 那么
SHCIDS_ALLFIELDS
的收获是什么?
- 他们什么时候会不一样?
- shell 问我什么?
奖金问题
- 如果
SHCIDS_CANONICALONLY
表示执行最有效的排序,
- 缺少
SHCIDS_CANONICALONLY
是否意味着可以根据名称的本地化和自定义进行排序?
- 缺少
SHCIDS_CANONICALONLY
是否意味着强制 根据名称的本地化和自定义进行排序?
itemID 列表 "sort" 是什么意思?
SDK 示例根据每一列执行 switch
,并查找每一列的值。如果这意味着我必须通过网络加载视频才能加载音频采样率?
- 我比较 PIDLs
- 或者我是在比较那些 pidls 指向的 objects 吗?
SDK示例基本正确(取决于pidl内容)。 if (lParam & (SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS))
显然与 if ((lParam & SHCIDS_CANONICALONLY) || (lParam & SHCIDS_ALLFIELDS))
相同,但没有告诉我们它们是否可以组合,答案是我不知道。我不明白为什么不。
只有 Microsoft shell 团队的成员知道真正的答案,但我们可以推测。
Win95基本上有4个标准字段。您可以在旧版 IShellDetails 界面的文档中看到它们:
File system folders have a large standard set of information fields.
The first four fields are standard for all file system folders.
Index | Title
-------------
0 Name
1 Size
2 Type
3 Date Modified
File system folders may support a number of additional fields.
However, they are not required to do so and the column indexes
assigned to these fields may vary.
Each virtual folder has its own unique set of information fields.
Typically, the item's display name is in column zero, but the order
and content of the available fields depend on the implementation of
the particular folder object.
然后在 Windows 2000 中,添加了对 shell 扩展 column handlers 的支持后,情况发生了变化。这是支持 Vistas 堆叠支持等的 属性 系统的基础,列索引是穷人映射 to/from 项目属性的 PROPERTYKEY
(PROPERTYKEY
已知一个 SHCOLUMNID
当时)。
SHCIDS_CANONICALONLY:
这里的重要部分是 CANONICAL。
MSDN 说
When comparing by name, compare the system names but not the display names.
shell 与其对术语显示名称的使用不一致,但它的实际含义是,比较解析名称,而不是您在资源管理器中看到的名称。
例如,文件夹视图可能包含 "foo" 和 "foo" 文件,但实际上它们是 "foo.jpg" 和 "foo.png",但 "hide file extensions" 功能隐藏了真实姓名。
IShellFolder
实现知道其 pidl 中的哪个 属性(列)对于其文件夹中的每个项目都是唯一的,应该使用它来进行比较。
SHCIDS_ALLFIELDS:
这只是意味着您要比较所有支持的列,直到找到差异。
可以实现为:
for (UINT i = 0; i < mycolumcount; ++i)
{
hr = CompareIDs(i, pidl1, pidl2);
if (hr && SUCCEEDED(hr)) break;
}
return hr;
奖金问题
SHCIDS_CANONICALONLY
不管你比的是什么,可以是localized/customized也可以不是。在 pidl 中存储本地化数据是个坏主意,因此在大多数情况下并非如此。
其他列通常也不作为本地化数据进行比较。理想情况下,您的比较函数的级别低于显示代码,并且仅当您必须 return 将字符串发送给调用者时才 return 编辑本地化字符串。
项目属性有两个消费者:
shell 视图。这些被 return 编辑为 localized/customized 字符串,通常显示为列表视图项目。旧的 IShellDetails
可用于以文件夹认为正确的任何方式格式化为纯字符串来检索它们。
属性系统。 IShellFolder2::GetDetailsEx
作为 VARIANT
返回。日期和数字由用户而不是文件夹格式化。
IShellFolder::GetDisplayNameOf
检索 "main column",其中 SHGDN_NORMAL
是 localized/customized 名称,而 SHGDN_FORPARSING
通常与 属性 相同SHCIDS_CANONICALONLY
.
实施示例
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;
}
精简版
IShellFolder.CompareIDs的SHCIDS_ALLFIELDS
标志是什么意思?
长版
在Windows95中,微软引入了shell。它不是假设计算机由文件和文件夹组成,而是由项目的抽象 命名空间 组成。
- 而不是从驱动器根目录开始的路径(例如
C:\Documents & Settings\Ian
) - 路径从命名空间的根开始 (
Desktop
)
并且为了容纳不是文件和文件夹的东西(例如网络打印机、控制面板、我的 Android phone):
- 您不使用由反斜杠分隔的一系列名称(例如 C: \ Users \ Ian )
- 您使用 pidls,一系列不透明的 blob(例如 Desktop This PC OS (C:) 用户 Ian)
PIDL 是不透明的 blob,每个 blob 只对生成它的文件夹有意义。
为了扩展(或使用)shell 命名空间,您实现(或调用)IShellFolder 接口。
IShellFolder 的方法之一用于请求名称空间扩展以 compare 到 ID 列表 (PIDLs):
IShellFolder::CompareIDs method
Determines the relative order of two file objects or folders, given their item identifier lists.
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 添加了额外的排序选项标志
从 shell 的 版本 5 开始,LPARAM
的高 16 位现在可以包含额外的标志来控制 IShellFolder 应该处理排序。
来自ShObjIdl.idl
Windows 8.1 SDK:
// 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_CANONICALONLY 意味着我们拥有的最快最有效的排序
- 从 UI 可用性的角度来看,它不一定是合乎逻辑的;它必须是一致的
正如雷蒙德陈指出的那样,它是the moral equivalent of a Unicode ordinal comparison。
header 文件甚至指出我们使用 假设第 0 列是 "fastest" 排序.但是现在我们将使用一个标志来 say "use the fastest sort available":
Before we had
SHCIDS_CANONICALONLY
we assumed column 0 was the "efficient" sort column.
它还指出您可以忽略 LPARAM 的低 16 位(即列),因为我们不关心 - 我们使用的是最有效的。
很多内容都反映在官方文档中:
SHCIDS_CANONICALONLY
Version 5.0. When comparing by name, compare the system names but not the display names. When this flag is passed, the two items are compared by whatever criteria the Shell folder determines are most efficient, as long as it implements a consistent sort function. This flag is useful when comparing for equality or when the results of the sort are not displayed to the user. This flag cannot be combined with other flags.
但是 SHCIDS_ALLFIELDS 我们开始 运行 关闭 rails
header 文件指出 AllFields 只能与 CanonicalOnly:
组合only be used in conjunction with SHCIDS_CANONCALONLY or column 0.
但是SDK说CanonicalOnly必须单独出现:
This flag cannot be combined with other flags.
那是哪一个?
我们可以确定 header 文件是错误的,SDK 是大炮,然后按照它说的去做。
但是 AllFields 在说什么?
有一些概念 AllFields 试图 要求,但在文档后面被遮盖了。
Compare all the information contained in the ITEMIDLIST structure, not just the display names.
ItemIDList 不包含显示名称,它包含一个 ItemIDList。他们是想说我应该 只 查看 pidl blob 的内容吗?
- 例如,如果两个项目是文件,文件夹应该比较它们的名称、大小、文件时间、属性和结构中的任何其他信息。
在什么情况下对*同一个**文件的两个引用可能具有不同的名称、大小、文件时间、属性等?
SDK 示例做一些不同的事情
Windows SDK Explorer Data Provider Shell Extension 示例(github),看起来就像 CanonicalOnly 和 AllFields 标志会一起出现:
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
}
所以我们有完全冲突的文档、headers 和样本:
SHCIDS_ALLFIELDS
- SDK: 永远不会出现 SHCIDS_CANONICALONLY
- Headers:随时可以出现
- 例子: 只能和SHCIDS_CANONICALONLY 一起出现
它想问什么
Windows 总是假设第 0 列是 fast 列。这可能是因为 Windows shell API 作者假定 PIDL 的 ItemID 将始终包含名称 inside pidl 不透明 blob。
This is reinforced by the fact that the shell STRRET structure lets you point to a string inside your pidl.
Bonus Reading: The kooky STRRET structure
所以在某些时候他们添加了一个明确的标志,上面写着:
- 我们不关心本地化、区域特定的语言排序规则和 unicode 规范化算法
- 只需对它们进行排序,因为我们需要查找重复项并检查是否相等
这对 canonical 标志有意义
- 告诉我两个 IDList 是否指向同一个 object
但是当他们谈论 所有字段 选项时,SDK 示例意味着什么:
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.
如果两个 PIDL 表示相同的 文件 比较它们的大小、日期等有什么意义?我已经告诉过你它们是相同的 file,你用 All Fields 标志问我什么?为什么我不能对 blob 进行二进制比较? shell 为什么不呢? CompareIDs 是做什么的
MemCmp(pidl1, pidl2)
不是吗?
SHCIDS_ALLFIELDS
只有会和SHCIDS_CANONICALONLY
一起出现吗?-
SHCIDS_ALLFIELDS
永远不会 与SHCIDS_CANONICALONLY
一起出现吗? SHCIDS_ALLFIELDS
是否可以同时出现SHCIDS_CANONICALONLY
?SHCIDS_ALLFIELDS
withSHCIDS_CANONICALONLY
是什么意思?SHCIDS_ALLFIELDS
没有SHCIDS_CANONICALONLY
是什么意思?
如果SHCIDS_ALLFIELDS
通过了,它要我做什么?我是否应该点击底层数据存储来查询我知道的所有字段?
CompareIDs是用来比较ID的,还是用来比较objects的?
我想知道 CompareIDs 的目的是否是 绝对不 访问底层数据存储(例如硬盘,phone over USB,Mapi),并且只比较根据你在 pidl.on-hand 中的内容。
这有两个原因:
- 速度更快;许多命名空间在其 PIDL blob 中包含一定数量的元数据 - 无需返回到 disk/Internet
- 即使 pidl 可能引用相同的 object,它们的元数据也可能已过时
- SHCIDS_CANONICALONLY 让调用者意识到两个 pidl 是同一个东西
- 但是使用
SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS
的单独调用可以告诉我们额外的元数据可能已过时(尽管我不知道这些信息对调用者有什么用)
所以也许 SHCIDS_CANONICALONLY
意味着:
- 请将自己限制在 pidl - 不要触摸磁盘来执行比较
- 省略它意味着:"Yeah, you can hit the hard drive if you really need to"
是这样吗?
- 如果
SHCIDS_CANONICALONLY
表示:"Don't look at anything besides whats in the pidl, and tell me if these two things are the same object" - 那么
SHCIDS_ALLFIELDS
的收获是什么? - 他们什么时候会不一样?
- shell 问我什么?
奖金问题
- 如果
SHCIDS_CANONICALONLY
表示执行最有效的排序, - 缺少
SHCIDS_CANONICALONLY
是否意味着可以根据名称的本地化和自定义进行排序? - 缺少
SHCIDS_CANONICALONLY
是否意味着强制 根据名称的本地化和自定义进行排序?
itemID 列表 "sort" 是什么意思?
SDK 示例根据每一列执行 switch
,并查找每一列的值。如果这意味着我必须通过网络加载视频才能加载音频采样率?
- 我比较 PIDLs
- 或者我是在比较那些 pidls 指向的 objects 吗?
SDK示例基本正确(取决于pidl内容)。 if (lParam & (SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS))
显然与 if ((lParam & SHCIDS_CANONICALONLY) || (lParam & SHCIDS_ALLFIELDS))
相同,但没有告诉我们它们是否可以组合,答案是我不知道。我不明白为什么不。
只有 Microsoft shell 团队的成员知道真正的答案,但我们可以推测。
Win95基本上有4个标准字段。您可以在旧版 IShellDetails 界面的文档中看到它们:
File system folders have a large standard set of information fields. The first four fields are standard for all file system folders.
Index | Title ------------- 0 Name 1 Size 2 Type 3 Date Modified
File system folders may support a number of additional fields. However, they are not required to do so and the column indexes assigned to these fields may vary.
Each virtual folder has its own unique set of information fields. Typically, the item's display name is in column zero, but the order and content of the available fields depend on the implementation of the particular folder object.
然后在 Windows 2000 中,添加了对 shell 扩展 column handlers 的支持后,情况发生了变化。这是支持 Vistas 堆叠支持等的 属性 系统的基础,列索引是穷人映射 to/from 项目属性的 PROPERTYKEY
(PROPERTYKEY
已知一个 SHCOLUMNID
当时)。
SHCIDS_CANONICALONLY:
这里的重要部分是 CANONICAL。
MSDN 说
When comparing by name, compare the system names but not the display names.
shell 与其对术语显示名称的使用不一致,但它的实际含义是,比较解析名称,而不是您在资源管理器中看到的名称。
例如,文件夹视图可能包含 "foo" 和 "foo" 文件,但实际上它们是 "foo.jpg" 和 "foo.png",但 "hide file extensions" 功能隐藏了真实姓名。
IShellFolder
实现知道其 pidl 中的哪个 属性(列)对于其文件夹中的每个项目都是唯一的,应该使用它来进行比较。
SHCIDS_ALLFIELDS:
这只是意味着您要比较所有支持的列,直到找到差异。
可以实现为:
for (UINT i = 0; i < mycolumcount; ++i)
{
hr = CompareIDs(i, pidl1, pidl2);
if (hr && SUCCEEDED(hr)) break;
}
return hr;
奖金问题
SHCIDS_CANONICALONLY
不管你比的是什么,可以是localized/customized也可以不是。在 pidl 中存储本地化数据是个坏主意,因此在大多数情况下并非如此。
其他列通常也不作为本地化数据进行比较。理想情况下,您的比较函数的级别低于显示代码,并且仅当您必须 return 将字符串发送给调用者时才 return 编辑本地化字符串。
项目属性有两个消费者:
shell 视图。这些被 return 编辑为 localized/customized 字符串,通常显示为列表视图项目。旧的
IShellDetails
可用于以文件夹认为正确的任何方式格式化为纯字符串来检索它们。属性系统。
IShellFolder2::GetDetailsEx
作为VARIANT
返回。日期和数字由用户而不是文件夹格式化。
IShellFolder::GetDisplayNameOf
检索 "main column",其中 SHGDN_NORMAL
是 localized/customized 名称,而 SHGDN_FORPARSING
通常与 属性 相同SHCIDS_CANONICALONLY
.
实施示例
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;
}