Укороченная версия
Что означает флаг SHCIDS_ALLFIELDS
для IShellFolder.CompareIDs означает?
Длинная версия
В Windows 95 Microsoft представила оболочку. Вместо того, чтобы предполагать, что компьютер состоит из файлов и папок, он состоит из абстрактного пространства имен элементов.
- а не пути, начинающиеся в корне диска (например,
C:\Documents & Settings\Ian
) - пути начинаются в корне пространства имен (
Desktop
)
И чтобы разместить вещи, которые не являются файлами и папками (например, сетевые принтеры, панель управления, мой телефон Android):
- вы не используете серию имен, разделенных обратной косой чертой (например, C: \ Users \ Ian )
- вы используете pidls, серию непрозрачных BLOB-объектов (например, Desktop This PC OS (C:) Пользователи Ян)
PIDL — это непрозрачные большие двоичные объекты, каждый большой двоичный объект имеет смысл только для папки, в которой он был создан.
Чтобы расширить (или использовать) пространство имен оболочки, вы реализуете (или вызываете) интерфейс IShellFolder.
Один из методов IShellFolder используется для запроса расширения пространства имен на сравнение со списками идентификаторов (PIDL):
Метод 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.
Итак, вы сравнили два списка удостоверений личности — что бы это ни значило, и мы закончили.
В Windows 2000 добавлены дополнительные флаги параметров сортировки.
Начиная с 5-й версии оболочки, старшие 16 бит LPARAM
теперь могут содержать дополнительные флаги для управления тем, как IShellFolder должен обрабатывать сортировку.
С ShObjIdl.idl
в. SDK для 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_CANONICALONLY — это самая быстрая и эффективная сортировка, которая у нас есть.
- и это не должно быть логично с точки зрения удобства использования пользовательского интерфейса; это просто должно быть последовательно
Как отметил Рэймонд Чен, это моральный эквивалент Unicode порядковое сравнение.
В заголовочном файле даже отмечается, что мы использовали просто предположение, что столбец 0 был самой быстрой сортировкой. Но теперь мы будем использовать флаг, чтобы сказать "использовать самую быструю доступную сортировку":
До того, как у нас появилось
SHCIDS_CANONICALONLY
, мы предполагали, что столбец 0 был «эффективным» столбцом сортировки.
Также отмечается, что вы можете игнорировать младшие 16 бит LPARAM (то есть столбец), потому что нам все равно — мы используем наиболее эффективный.
Многое из этого отражено в официальной документации:
SHCIDS_CANONICALONLY
Версия 5.0. При сравнении по имени сравнивайте системные имена, а не отображаемые имена. Когда этот флаг передается, два элемента сравниваются по любым критериям, которые папка Shell определяет как наиболее эффективные, если она реализует согласованную функцию сортировки. Этот флаг полезен при сравнении на равенство или когда результаты сортировки не отображаются пользователю. Этот флаг нельзя комбинировать с другими флагами.
Но с SHCIDS_ALLFIELDS мы начинаем сходить с ума
В заголовочном файле указано, что AllFields можно комбинировать только с CanonicalOnly:
использоваться только в сочетании с SHCIDS_CANONCALONLY или столбцом 0.
Но SDK говорит, что CanonicalOnly должен отображаться отдельно:
Этот флаг нельзя комбинировать с другими флагами.
Так что это?
Мы могли бы решить, что заголовочный файл неверен, что SDK — пушка, и сделать то, что он говорит.
Но что говорит AllFields?
Существует некоторая концепция, которую AllFields пытается запросить, но она скрыта за документацией.
Сравните всю информацию, содержащуюся в структуре ITEMIDLIST, а не только отображаемые имена.
ItemIDList не содержит отображаемого имени, он содержит ItemIDList. Они пытаются сказать, что я должен только смотреть содержимое большого двоичного объекта pidl?
- Например, если два элемента являются файлами, папка должна сравнить их имена, размеры, время файла, атрибуты и любую другую информацию в структурах.
В какой ситуации две ссылки на *один и тот же** файл могут иметь разные имена, размеры, время файла, атрибуты и т. д.?
Примеры SDK делают что-то другое
Пример Windows SDK Расширение оболочки поставщика данных Explorer (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
}
Итак, у нас полностью противоречивая документация, заголовки и образцы:
SHCIDS_ALLFIELDS
- SDK: не может отображаться с SHCIDS_CANONICALONLY
- Заголовки: могут появляться в любое время.
- Примеры: могут отображаться только с SHCIDS_CANONICALONLY.
Что он пытается спросить
Windows всегда предполагала, что столбец 0 является быстрым столбцом. Это могло быть связано с тем, что авторы API оболочки Windows предполагали, что ItemID PIDL всегда будет содержать имя внутри непрозрачного большого двоичного объекта pidl.
Это подкрепляется тем фактом, что структура оболочки STRRET позволяет указать строку внутри вашего pidl.
Дополнительное чтение: странная структура STRRET
И поэтому в какой-то момент они добавили экспресс-флаг, который гласит:
- мы не заботимся о локализации, специфических для локали правилах языковой сортировки и алгоритмах нормализации юникода.
- просто отсортируйте их, потому что нам нужно найти дубликаты и проверить на равенство
И это имеет смысл для флага canonical:
- просто скажите мне, если два списка IDL указывают на один и тот же объект
Но что тогда означает пример с SDK, когда говорят об опции Все поля:
Если нас попросили провести сравнение всех полей, протестируйте любые другие поля, которые могут отличаться в элементе, который имеет один и тот же идентификатор. Например:
- если элемент представляет файл, идентификатор может быть просто именем файла
- но другими полями, содержащимися в списке idlist, могут быть размер файла и дата изменения файла, и они могут меняться со временем.
Если два PIDL представляют один и тот же файл, какой смысл сравнивать их размер, дату и т. д.? Я уже говорил вам, что это один и тот же файл, что вы хотите от меня с флагом Все поля? Почему я не могу просто выполнить бинарное сравнение капель? Почему оболочка не работает? Что делает CompareID?
MemCmp(pidl1, pidl2)
нет?
- Будет ли
SHCIDS_ALLFIELDS
только отображаться вместе сSHCIDS_CANONICALONLY
? SHCIDS_ALLFIELDS
никогда не появится рядом сSHCIDS_CANONICALONLY
?- Может ли
SHCIDS_ALLFIELDS
появляться как сSHCIDS_CANONICALONLY
, так и без него? - Что означает
SHCIDS_ALLFIELDS
сSHCIDS_CANONICALONLY
? - Что означает
SHCIDS_ALLFIELDS
безSHCIDS_CANONICALONLY
?
Что он хочет, чтобы я сделал, если SHCIDS_ALLFIELDS
пройдено? Должен ли я обратиться к основному хранилищу данных, чтобы запросить все поля, о которых я знаю?
Используются ли CompareID для сравнения идентификаторов или для сравнения объектов?
Я задавался вопросом, было ли назначение CompareID в том, чтобы абсолютно не обращаться к основному хранилищу данных (например, жесткий диск, телефон через USB, Mapi), а сравнивать только на основе того, что у вас есть под рукой< /strong> в файле pidl.
Это имеет смысл по двум причинам:
- это быстрее; многие пространства имен содержат некоторое количество метаданных в своих больших двоичных объектах PIDL - нет необходимости возвращаться к диску/Интернету
- хотя pidl могут ссылаться на один и тот же объект, возможно, их метаданные устарели.
- SHCIDS_CANONICALONLY позволяет вызывающей стороне понять, что два pidl — это одно и то же.
- но отдельный вызов с
SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS
может сказать нам, что дополнительные метаданные могут быть устаревшими (хотя я понятия не имею, какая польза от этой информации для вызывающего абонента)
Итак, возможно, SHCIDS_CANONICALONLY
означает:
- пожалуйста, ограничьтесь pidl - не прикасайтесь к диску, чтобы выполнить сравнение
- а его отсутствие означает: "Да, вы можете нажать на жесткий диск, если вам действительно нужно"
Так ли это?
- Если
SHCIDS_CANONICALONLY
означает: "Не смотрите ни на что, кроме того, что в pidl, и скажите мне, являются ли эти две вещи одним и тем же объектом" - Тогда что получает
SHCIDS_ALLFIELDS
? - Когда они будут другими?
- Что спрашивает оболочка?
Бонусный вопрос
- Если
SHCIDS_CANONICALONLY
означает выполнение наиболее эффективной сортировки, - означает ли отсутствие
SHCIDS_CANONICALONLY
возможность сортировки на основе локализации и настройки имени? - означает ли отсутствие
SHCIDS_CANONICALONLY
, что обязательна сортировка на основе локализации и настройки имени?
Что означает "сортировать" по спискам itemID?
Пример SDK выполняет switch
на основе каждого столбца и ищет значения для каждого столбца. Если это означает, что мне нужно загрузить видео из сети, чтобы загрузить частоту дискретизации звука?
- Я сравниваю PIDL
- или я сравниваю объекты, на которые указывают эти pidls?