Что представляет собой флаг SHCIDS_ALLFIELDS для IShellFolder.CompareID?
Укороченная версия
Что это SHCIDS_ALLFIELDS
флаг IShellFolder.CompareIDs означает?
Длинная версия
В Windows 95 Microsoft представила оболочку. Вместо того, чтобы предполагать, что компьютер состоит из файлов и папок, он состоит из абстрактного пространства имен элементов.
- а не пути, начинающиеся в корне диска (например,
C:\Documents & Settings\Ian
) - пути начинаются с корня пространства имен (
Desktop
)
И для размещения вещей, которые не являются файлами и папками (например, сетевые принтеры, Панель управления, мой телефон на Android):
- вы не используете серию имен, разделенных обратной косой чертой (например, C: \ Users \ Ian)
- вы используете pidls, серию непрозрачных блобов (например, Desktop This PC OS (C:) Users Ian)
PIDL являются непрозрачными BLOB-объектами; каждый BLOB-объект имеет смысл только для той папки, в которой он создан.
Чтобы расширить (или использовать) пространство имен оболочки, вы реализуете (или вызываете) интерфейс IShellFolder.
Один из методов IShellFolder используется для запроса расширения пространства имен для сравнения со списками идентификаторов (PIDL):
IShellFolder:: метод CompareIDs
Определяет относительный порядок двух файловых объектов или папок, учитывая их списки идентификаторов элементов.
HRESULT CompareIDs( [in] LPARAM lParam, [in] PCUIDLIST_RELATIVE pidl1, [in] PCUIDLIST_RELATIVE pidl2 );
На протяжении многих лет LPARAM
было задокументировано почти всегда будет 0. От shlobj.h
с. 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
с. 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 - самый быстрый и эффективный вид, который у нас есть
- и это не должно быть логичным с точки зрения удобства использования пользовательского интерфейса; это просто должно быть последовательным
Как отметил Рэймонд Чен, это моральный эквивалент порядкового сравнения Юникода.
Заголовочный файл даже отмечает, что мы обычно предполагали, что столбец 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 всегда будет содержать имя внутри непрозрачного BLOB-объекта pidl.
Это подтверждается тем фактом, что структура оболочки STRRET позволяет вам указывать на строку внутри вашего pidl.
Чтение бонусов: странная структура STRRET
И вот в какой-то момент они добавили экспресс-флаг, который говорит:
- нас не волнуют локализация, локализационные правила лингвистической сортировки и алгоритмы нормализации юникода
- просто отсортируйте их, потому что нам нужно найти дубликаты и проверить на равенство
И это имеет смысл для канонического флага:
- просто скажите мне, если два ID Lists указывают на один и тот же объект
Но что означает пример SDK, когда они говорят о опции " Все поля":
Если нас попросили сравнить все поля, проверьте наличие других полей, которые могут отличаться в элементе, имеющем одинаковую идентичность. Например:
- если элемент представляет файл, идентификатором может быть только имя файла
- но другие поля, содержащиеся в списке идентификаторов, могут быть размером файла и датой изменения файла, и они могут изменяться со временем.
Если два PIDL представляют один и тот же файл, какой смысл сравнивать их размер, дату и т. Д.? Я уже говорил вам, что это один и тот же файл, что вы просите у меня с флагом All Fields? Почему я не могу просто сделать двоичное сравнение с BLOB-объектами? Почему не снаряд? Что это делает 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), а сравнивать только на основе того, что у вас есть в наличии в pidl.
Это имеет смысл по двум причинам:
- это быстрее; многие пространства имен содержат некоторое количество метаданных в своих BLOB-объектах PIDL - нет необходимости возвращаться на диск / в Интернет
- даже если pidls могут ссылаться на один и тот же объект, возможно, их метаданные устарели
- 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?
1 ответ
Пример SDK в основном правильный (зависит от содержимого pidl). if (lParam & (SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS))
очевидно так же, как if ((lParam & SHCIDS_CANONICALONLY) || (lParam & SHCIDS_ALLFIELDS))
но не говорит нам, если они могут быть объединены, и ответ на это, я не знаю. Я не понимаю, почему нет.
Только члены команды оболочки Microsoft знают верный ответ, но мы можем строить догадки.
У Win95 в основном было 4 стандартных поля. Вы можете увидеть их в документации для более старого интерфейса IShellDetails:
Папки файловой системы имеют большой стандартный набор информационных полей. Первые четыре поля являются стандартными для всех папок файловой системы.
Index | Title ------------- 0 Name 1 Size 2 Type 3 Date Modified
Папки файловой системы могут поддерживать ряд дополнительных полей. Однако они не обязаны это делать, и индексы столбцов, назначенные этим полям, могут отличаться.
Каждая виртуальная папка имеет свой уникальный набор информационных полей. Как правило, отображаемое имя элемента находится в нулевом столбце, но порядок и содержимое доступных полей зависят от реализации конкретного объекта папки.
Затем в Windows 2000 все изменилось, когда была добавлена поддержка обработчиков столбцов расширения оболочки. Это послужило основой для системы свойств, обеспечивающей поддержку стекирования Vistas и т. Д., А индекс столбца - это отображение бедняков в / из PROPERTYKEY
для свойств предметов (PROPERTYKEY
был известен SHCOLUMNID
тогда).
SHCIDS_CANONICALONLY:
Важная часть здесь - КАНОНИЧЕСКАЯ.
MSDN говорит
При сравнении по имени сравнивайте системные имена, но не отображаемые имена.
Оболочка не согласуется с тем, как она использует термин отображаемое имя, но на самом деле она означает сравнение имени разбора, а не имени, которое вы видите в проводнике.
Например, представление папки может содержать файлы "foo" и "foo", но на самом деле это "foo.jpg" и "foo.png", но функция "скрыть расширения файла" скрывает истинные имена.
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
не важно, что вы сравниваете, может быть локализовано / настроено или нет. Хранение локализованных данных в pidl - плохая идея, поэтому в большинстве случаев это не так.
Другие столбцы обычно не сравниваются как локализованные данные. В идеале ваша функция сравнения находится на более низком уровне, чем ваш код дисплея, и локализованные строки возвращаются только тогда, когда вам нужно вернуть строку вызывающей стороне.
Есть два потребителя свойств товара:
Оболочка зрения. Они возвращаются как локализованные / настраиваемые строки и обычно отображаются в виде элементов списка. Старый
IShellDetails
может использоваться для извлечения их как чистых строк, отформатированных любым способом, который папка считает корректным.Система собственности. Возвращено
IShellFolder2::GetDetailsEx
какVARIANT
, Даты и номера форматируются потребителем, а не папкой.
IShellFolder::GetDisplayNameOf
получает "основной столбец", где SHGDN_NORMAL
это локализованное / настроенное имя и 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;
}