Что представляет собой флаг SHCIDS_ALLFIELDS для IShellFolder.CompareID? - PullRequest
0 голосов
/ 28 мая 2018

Короткая версия

Что означает флаг SHCIDS_ALLFIELDS IShellFolder.CompareIDs ?? 1008 *

Длинная версия

В Windows 95 Microsoft представила оболочку .Вместо того, чтобы предполагать, что компьютер состоит из файлов и папок, он состоит из абстрактного пространства имен элементов.

  • , а не путей, начинающихся в корне диска (например, C:\Documents & Settings\Ian)
  • пути начинаются в корне пространства имен (Desktop)

И для размещения вещей, которые не являются файлами и папками (например, сетевые принтеры,Панель управления, мой телефон Android):

  • вы не используете серию имен, разделенных обратной косой чертой (например, C: \ Users \ Ian )
  • Вы используете pidls , серию непрозрачных блобов (например, Рабочий стол Этот ПК ОС (C:) Пользователи 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 c.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.Они пытаются сказать, что я должен только посмотреть содержимое BLOB-объекта pidl?

  • Например, если два элемента являются файлами, папка должна сравнить их имена и размеры, время файла, атрибуты и любую другую информацию в структурах.

В какой ситуации две ссылки на * один и тот же ** файл могут иметь разные имена, размеры, время файла, атрибуты и т. Д.?

Примеры SDK делают что-то другое

Windows Shell SDK Расширение оболочки провайдера данных Explorer * Пример 1187 * ( 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 был столбцом fast .Возможно, это связано с тем, что авторы API оболочки Windows предполагали, что ItemID PIDL всегда будет содержать имя внутри непрозрачного BLOB-объекта pidl.

Это подтверждается тем фактом, что структура оболочки STRRET позволяет вам указывать на строку внутри вашего pidl.

Бонусное чтение: Странная структура STRRET

И вот в какой-то момент они добавили экспресс-флаг, который говорит:

  • мы не заботимся о локализации, о региональных правилах лингвистической сортировки и алгоритмах нормализации Юникода
  • , просто сортируем их, потому что нам нужно найти дубликаты и проверить на равенство

И это делаетсмысл для канонического флага:

  • просто скажите мне, если два IDL-списка указывают на один и тот же объект

Но тогда чтоОзначает ли пример SDK, когда они говорят о опции Все поля :

Если нас попросили провести сравнение по всем полям, проверьте любые другие поля, которые могутотличаться в элементе, который разделяет ту же самую идентичность.Например:

  • , если элемент представляет файл, идентификатором может быть просто имя файла
  • , но другими полями, содержащимися в списке, могут быть размер файла и дата изменения файла, исо временем они могут измениться.

Если два PIDL представляют один и тот же файл , какой смысл сравнивать их размер, дату и т. д.?Я уже говорил вам, что это один и тот же файл , что вы спрашиваете меня с флагом Все поля ?Почему я не могу просто сделать двоичное сравнение с BLOB-объектами?Почему не снаряд?Что CompareIDs делает это

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) и сравнивалось только на основе того, что у вас есть нарука в пидл.

Это имеет смысл по двум причинам:

  • это быстрее;многие пространства имен содержат некоторое количество метаданных в своих BLOB-объектах PIDL - нет необходимости возвращаться на диск / Интернет
  • , даже если pidls могут ссылаться на один и тот же объект, возможно, их метаданные устарели
  • SHCIDS_CANONICALONLY позволяет вызывающему понять, что два pidls - это одно и то же
  • , но отдельный вызов с SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS может сказать нам, что дополнительные метаданные могут быть устаревшими (хотя я понятия не имею, для чего эта информация используется длявызывающий)

И, возможно, SHCIDS_CANONICALONLY означает:

  • пожалуйста, ограничьте себя pidl - не трогайте диск, чтобы выполнить сравнение
  • и опускание означает: «Да, вы можете нажать на жесткий диск, если вам действительно нужно»

Это так?

  • Если SHCIDS_CANONICALONLY означает: «Не смотрите ни на что, кроме того, что написано в pidl, и скажите мне, являются ли эти две вещи одним и тем же объектом»
  • Что тогда получает SHCIDS_ALLFIELDS?
  • Когда они будут отличаться?
  • Что спрашивает меня оболочка?

Бонусный вопрос

  • Если SHCIDS_CANONICALONLY означает, что выполнять наиболее эффективную сортировку,
  • означает, что отсутствие SHCIDS_CANONICALONLY означает, что можно сортировать на основе локализации и настройки имени?
  • означает ли отсутствие SHCIDS_CANONICALONLY, что обязательна сортировка по локализации и настройке имени?

Что означает "сортировка" в списки itemID?

В примере SDK switch основывается на каждом столбце и ищет значения для каждого столбца.Если это означает, что мне нужно загрузить видео из сети, чтобы загрузить частоту дискретизации звука?

  • Я сравниваю PIDL
  • или я сравниваю объекты, на которые указывают эти pidls?к?

1 Ответ

0 голосов
/ 29 мая 2018

Пример SDK в основном правильный (зависит от содержимого pidl).if (lParam & (SHCIDS_CANONICALONLY | SHCIDS_ALLFIELDS)), очевидно, совпадает с if ((lParam & SHCIDS_CANONICALONLY) || (lParam & SHCIDS_ALLFIELDS)), но не говорит нам, могут ли они быть объединены, и ответ на этот вопрос я не знаю.Я не понимаю, почему нет.

Только члены команды Microsoft Shell знают правильный ответ, но мы можем предположить.

В Win95 в основном было 4 стандартных поля.Вы можете увидеть их в документации для более старого интерфейса IShellDetails :

Папки файловой системы имеют большой стандартный набор информационных полей.Первые четыре поля являются стандартными для всех папок файловой системы.

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

Папки файловой системы могут поддерживать ряд дополнительных полей.Однако они не обязаны это делать, и индексы столбцов, назначенные этим полям, могут различаться.

Каждая виртуальная папка имеет свой уникальный набор информационных полей.Как правило, отображаемое имя элемента находится в нулевом столбце, но порядок и содержимое доступных полей зависят от реализации конкретного объекта папки.

Затем в Windows 2000 все изменилось, когда появилась поддержка расширения оболочки обработчики столбцов были добавлены.Это послужило основой для системы свойств, обеспечивающей поддержку стекирования Vistas и т. Д., А индекс столбца - это отображение плохого человека в PROPERTYKEY для свойств элементов (PROPERTYKEY было известно SHCOLUMNID тогда).

SHCIDS_CANONICALONLY:

Важным элементом здесь является CANONICAL.

MSDN говорит:

При сравнении по имени сравнивайте имена систем, но не отображаемые имена.

Оболочка не согласуется с тем, как она использует термин отображаемое имя, но на самом деле это означает сравнение имени разбора, а не имени, которое вы видите в Проводнике.

ДляНапример, представление папки может содержать файлы «foo» и «foo», но в действительности это «foo.jpg» и «foo.png», но функция «скрыть расширения файла» скрывает истинные имена.

Реализация IShellFolder знает, какое свойство (столбец) из его pidl уникально для каждого элемента в своей папке, и должна использовать это для сравнения.

SHCIDS_ALLFIELDS:

Это просто означает, что вы хотитеКолорадоMPare все поддерживаемые столбцы, пока вы не найдете разницу.

Это может быть реализовано как:

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;
}
...