Преамбула

Данной заметки не должно было быть, но мы живем не в идеальном мире.
Есть много пользователей, что предпочли стандартному проводнику Windows альтернативу и наверное еще больше пользователей архиватора 7-zip. Имеет смысл поделиться с ними, подумал я и вот мы здесь.

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

Что делает обычный эникейщик в подобной ситуации? Конечно же берет в руки первый подвернувшийся профайлер.

Результат

Итак, локальным виновником оказался метод IContextMenu::QueryContextMenu, а по факту все время съедает... 7-zip.dll
Эта библиотека является расширением оболочки и отвечает за добавление команд архиватора 7-zip в контекстное меню. Не то что ожидаешь увидеть по итогу, но стоит проверить.

Отключаем показ меню архиватора в настройках программы.

И убеждаемся, что собака порылась именно тут.
Ну раз такое дело, почему бы не проверить WinRar? Ничего похожего не наблюдается, лаг отсутствует...

Присмотримся

Для более чистого эксперимента закинем тестовую директорию на RAMDISK, а файловый менеджер заменим на что-то более очевидное, например Total Commander.

Сразу стоит отметить, что все изложенное не затрагивает проводник Windows, там своя внутренняя API магия, которая не ограничивается двухступенчатой инициализацией (сначала для первых 16 объектов, чтобы быстро отобразить меню, а после выбора команды, Invoke уже для полного набора). Все остальные файловые менеджеры почти поголовно подвержены проблеме, как впрочем и практически любой другой софт.

Что ж, попробуем вызвать меню в TC для файлов в нашей папке. Для наглядности сделаем количество побольше. Тест на 20k файлах выявил замедление более чем в х20 раз, а именно 3 секунды без 7-zip и более минуты! с ним.

Вообще, по логике вещей, расширения оболочки не должны вмешиваться в процесс создания меню настолько инвазивно.

Скучный спойлер. WinAPI и базовый алгоритм

Как обычно приложения создают контекстные меню.
Приведу упрощенное изложение стандартного алгоритма.

Небольшая отсылка к MSDN
Для большинства не секрет, что ОС для внутренних операций с объектами файловой системы использует не текстовые представления путей к которым мы привыкли, а некие альтернативные идентификаторы которые могут определять и виртуальные объекты.

Идентификатор элемента (функционально эквивалентен имени файла\папки) фактически является структурой SHITEMID. Список таких идентификаторов определяется структурой ITEMIDLIST.

Для API оболочки объекты пространства имен обычно идентифицируются указателем как раз на эту самую структуру - PIDL (Pointer to an ITEMIDLIST).

В общем, изначально, тем или иным образом приложение (например ФМ) должно преобразовать пути в список идентификаторов. Независимо от API (ILCreateFromPath, SHParseDisplayName итп) в конечном счете будет вызван метод интерфейса IShellFolder рабочего стола Desktop.IShellFolder::ParseDisplayName, внутри которого для стандартных путей идентификаторы будут собраны из WIN32_FIND_DATAW, тут (на мой взгляд) можно было бы немного оптимизировать, но разработчикам виднее.
На Win10+ этот метод вызывается из windows.storage.dll (CFSFolder::ParseDisplayName), как и ряд методов ниже.

Затем обычно следует цепочка вызовов (упрощенный вариант):

Desktop.IShellFolder::BindToObject    Получение интерфейса (IShellFolder) родительского объекта (другие подобные API все равно вызовут его)
Parent.IShellFolder::GetUIObjectOf    Получение интерфейса (IContextMenu)
IContextMenu::QueryInterface          Для получения указателя более высокого уровня (IContextMenu3,IContextMenu2)
CreatePopupMenu                       Создается само меню
IContextMenu#::QueryContextMenu       Заполнение меню
TrackPopupMenuEx                      Показываем меню

В GetUIObjectOf мы передаем массив указателей на наши относительные идентификаторы и получаем на выходе интерфейс с дефолтным меню.

При, казалось бы, таком простом действии как создание меню, в действительности "за кулисами" происходит просто огромный объем работы.
Описание всего этого "цирка с конями" потянет на цикл отдельных статей, поэтому сфокусируемся на области используемой 7-zip.

Мы уже выяснили, что инициатор проблемной цепочки это QueryContextMenu.
Внутри CDefFolderMenu::QueryContextMenu происходит много разного
В зависимости от флагов поведение различается, однако базово, к пунктам дефолтного меню добавляются новые с объединением.
По типам объектов в списке сканируются соответствующие ветки реестра на наличие статических и динамических обработчиков.

Для динамики вызываются:
CDefFolderMenu::_AddHandlersToDCA
HDXA_QueryContextMenu

Условный CDefFolderMenu:

*IContextMenu                                       ;  0x00  (00)  CDefFolderMenu::vftable{IContextMenu3}
*IServiceProvider                                   ;  0x08  (08)  CDefFolderMenu::vftable{IServiceProvider}
*IObjectProvider                                    ;  0x10  (16)  CDefFolderMenu::vftable{IObjectProvider}
*IShellExtInit                                      ;  0x18  (24)  CDefFolderMenu::vftable{IShellExtInit}
*IObjectWithSelection                               ;  0x20  (32)  CDefFolderMenu::vftable{IObjectWithSelection}
*IOleWindow                                         ;  0x28  (40)  CDefFolderMenu::vftable{IOleWindow}
*IDefaultFolderMenuInitialize                       ;  0x30  (48)  CDefFolderMenu::vftable{IDefaultFolderMenuInitialize}
*IVerbStateTaskCallBack                             ;  0x38  (56)  CDefFolderMenu::vftable{IVerbStateTaskCallBack}
*IContextMenuBaseInfo                               ;  0x40  (64)  CDefFolderMenu::vftable{IContextMenuBaseInfo}
*IContextMenuForProgInvoke                          ;  0x48  (72)  CDefFolderMenu::vftable{IContextMenuForProgInvoke}
*IDefaultFolderMenuGetStateFromWrapperInstances     ;  0x50  (80)  CDefFolderMenu::vftable{IDefaultFolderMenuGetStateFromWrapperInstances}
*IContextMenuCB                                     ;  0x68  (104) CFSFolder::vftable{IContextMenuCB}
*IDataObject                                        ;  0x70  (112) CFSIDLData::vftable{IDataObject}
*IShellFolder2                                      ;  0x88  (136) CFSFolder::vftable{IShellFolder2}
*hdsaStatics                                        ;  0xB0  (176) HDSA - массив для статики    DSA_Create(sizeof(STATICITEMINFO),  1)
*HDXA                                               ;  0xB8  (184) HDXA - массив для динамики   DSA_Create(sizeof(CONTEXTMENUINFO), 5)
*hdsaCustomInfo                                     ;  0xC0  (192) HDSA - массив SEARCHINFO's   DSA_Create(sizeof(SEARCHINFO),      1)
*pidlFolder.LPITEMIDLIST                            ;  0xC8  (200) PIDL родительской директории
*apidl.LPITEMIDLIST                                 ;  0xD0  (208) Указатель на массив структур ITEMIDLIST
cidl                                                ;  0xD8  (216) Размер массива apidl (количество структур)
*paa.IAssociationArray                              ;  0xE0  (224) CAssocArray::vftable{IQueryAssociations}
*psss.IServiceProvider                              ;  0xE8  (232) CSafeServiceSite::vftable{IServiceProvider}
nKeys                                               ;  0xF0  (240) Количество хэндлов ключей реестра в массиве hkeyClsKeys
hkeyClsKeys[16]                                     ;  0xF8  (248) HKEY - (#DEF_FOLDERMENU_MAXHKEYS = 16) - Массив открытых хэдлов реестра для класса

При сборе инфы реестра заполняются соответствующие пулы данных, в основном это:

  • Ассоциативный массив IAssociationArray (упорядоченный список путей реестра с информацией о типе, обработчиках, псевдонимах команд, иконках итп)

  • Три динамических массива структур - для статических (hdsaStatics), динамических (HDXA) обработчиков и массив SEARCHINFO (hdsaCustomInfo).

Впоследствии при вызове метода CDefFolderMenu::InvokeCommand, если команда (verb) не каноническая (из списка дефолтного меню) и не статика, функция HDXA_LetHandlerProcessCommandEx начнет перебор структур CONTEXTMENUINFO из массива HDXA и будет пробовать вызвать метод IContextMenu::InvokeCommand по указателю в начале структуры. И тут, либо какой-то метод успешно отработает, либо, если массив кончился, CDefFolderMenu::InvokeCommand вернет E_INVALIDARG

На этом этапе нужно подгрузить в адресное пространство процесса библиотеки всех востребованных динамических расширений меню, а в случае 7-zip, это практически всегда. И тут мы подходим к инициализации нашего обработчика.

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

Нужно больше тестов

Проверим сначала на единственном файле.
Создаем пустую папку и в ней пустой текстовый файл, аттачимся к TC и ставим bp 7_zip!DllGetClassObject
Запускаем выполнение процесса и вызываем контекстное меню файла. После остановки делаем трассировку. На данный момент мы увидим лишь портянку со змейкой вызовов много раз повторяющегося кода 7_zip!DllUnregisterServer+offset

Суммарный результат (не полной) трассировки:

Function Name                 Invocations MinInst MaxInst AvgInst
7_zip                                 559       6     696      38
7_zip!DllGetClassObject                 1      44      44      44
7_zip!DllUnregisterServer            1308       3    2741      23

У меня конечно есть нелюбовь к построению причинно-следственных связей, но даже мне кажется, что в текущем контексте, вызовов для модуля достаточно много. Конечно можно уже пробовать курить исходники и делать какие-то выводы, но мы пока не будем, хотя сырки все равно нужны. Чтобы было красиво и наглядно нам не хватает символов для 7-zip.dll.

Ну, это дело пяти минут.

Забираем с оффсайта архив, в CPP/Build.mak дописываем в CFLAGS /Zi, в LFLAGS /DEBUG и проставим -Od в CFLAGS_OX

Выполняем в командной строке студии (x64 Native)
cd /d X:\7z2201-src\CPP\7zip
nmake /f makefile CPU=AMD64 (Если сильно спешите, скомпилируйте только dll)

Теперь у нас есть отладочные символы. Добавим путь к ним и сыркам для отладчика.
Также подменим библиотеку в папке архиватора. Повторим трассировку и посмотрим топ.

Результат
Function Name                               Invocations MinInst MaxInst AvgInst
7_zip!UString::Len                                  436       4       4       4
7_zip!operator new                                  380      10      10      10
7_zip!_malloc_base                                  380      19      19      19
7_zip!malloc                                        380       1       1       1
7_zip!operator new[]                                361       1       1       1
7_zip!free                                          287       3       3       3
7_zip!_free_base                                    287       3      13      12
7_zip!operator delete                               287       1       1       1
7_zip!operator delete[]                             287       1       1       1
7_zip!memcpy                                        250      11      46      20
7_zip!wmemcpy                                       234      12      12      12
7_zip!UString::UString                              243      20     128      22
7_zip!UString::SetStartLen                          131      27      27      27

С символами все стало достаточно наглядно. Посмотрим стек на самой часто вызываемой функции.

стек
7_zip!UString::Len:
00007ffb`b3b71690 48894c2408      mov     qword ptr [rsp+8],rcx ss:00000000`01de7cf0=0000000001de7d38
0:000> k
 # Child-SP          RetAddr           Call Site
00 00000000`01de7ce8 00007ffb`b3b71c06 7_zip!UString::Len [X:\7z2201-src\CPP\Common\MyString.h @ 587] 
01 00000000`01de7cf0 00007ffb`b3b71897 7_zip!CLang::OpenFromString+0x2d6 [X:\7z2201-src\CPP\Common\Lang.cpp @ 71] 
02 00000000`01de7d90 00007ffb`b3b82224 7_zip!CLang::Open+0x1f7 [X:\7z2201-src\CPP\Common\Lang.cpp @ 146] 
03 00000000`01de7e20 00007ffb`b3b82493 7_zip!LangOpen+0x24 [X:\7z2201-src\CPP\7zip\UI\FileManager\LangUtils.cpp @ 30] 
04 00000000`01de7e50 00007ffb`b3b82534 7_zip!OpenDefaultLang+0x143 [X:\7z2201-src\CPP\7zip\UI\FileManager\LangUtils.cpp @ 257] 
05 00000000`01de7ee0 00007ffb`b3b82339 7_zip!ReloadLang+0x34 [X:\7z2201-src\CPP\7zip\UI\FileManager\LangUtils.cpp @ 279] 
06 00000000`01de7f40 00007ffb`b3b7fa30 7_zip!LoadLangOneTime+0x39 [X:\7z2201-src\CPP\7zip\UI\FileManager\LangUtils.cpp @ 43] 
07 00000000`01de7f80 00007ffb`b80cfe1f 7_zip!CZipContextMenu::QueryContextMenu+0x20 [X:\7z2201-src\CPP\7zip\UI\Explorer\ContextMenu.cpp @ 535] 
08 00000000`01de8ae0 00007ffb`b80a7b70 SHELL32!HDXA_QueryContextMenu+0x5e7
09 00000000`01de8d80 00000000`005ed2ce SHELL32!CDefFolderMenu::QueryContextMenu+0x5c0
0a 00000000`01de8ef0 00000000`00000001 TOTALCMD64+0x1ed2ce

Тк отладка у нас с исходным кодом, быстро пробежавшись по строчкам, понимаем что к чему. Все эти аллокации происходят при парсинге файла локализации (src\CPP\Common\Lang.cpp).

Конечно странное решение.
Почему бы не брать строки по указателям из закешированного шаблона?
Зачем каждый раз самоотверженно парсить один и тот же файл локализации (16Кб для ru) при каждом нажатии ПКМ? Медленные строковые операции стоит минимизировать прежде всего. Хотя что я докопался, это даже "не те дроиды" которых мы ищем...

Если указать профайлеру путь к полученным символам, мы увидим больше подробностей и там, но ничего принципиально нового, можно сразу открывать отладчик.

Перейдем в TC в тестовую директорию, где файлов побольше.
После срабатывания bp 7_zip!DllGetClassObject поставим точку остановки на SHELL32!DragQueryFileAorW и глянем стек.
На третьей остановке мы уже попадаем в цикл функции и можем отследить всю проблемную цепочку.

Breakpoint 1 hit (вызов #3+) SHELL32!DragQueryFileAorW:
0:000> k
 # Child-SP          RetAddr           Call Site
00 00000000`01de88d8 00007ffb`b8212f43 SHELL32!DragQueryFileAorW
01 00000000`01de88e0 00007ffb`b3b7a063 SHELL32!DragQueryFileW+0x13
02 00000000`01de8920 00007ffb`b3b7a0e9 7_zip!NWindows::NShell::CDrop::QueryFile+0x33 [X:\7z2201-src\CPP\Windows\Shell.h @ 68] 
03 00000000`01de8950 00007ffb`b3b7a18c 7_zip!NWindows::NShell::CDrop::QueryFileName+0x79 [X:\7z2201-src\CPP\Windows\Shell.cpp @ 114] 
04 00000000`01de89a0 00007ffb`b3b7e16d 7_zip!NWindows::NShell::CDrop::QueryFileNames+0x5c [X:\7z2201-src\CPP\Windows\Shell.cpp @ 130] 
05 00000000`01de89f0 00007ffb`b3b7e7f1 7_zip!CZipContextMenu::GetFileNames+0x11d [X:\7z2201-src\CPP\7zip\UI\Explorer\ContextMenu.cpp @ 146] 
06 00000000`01de8aa0 00007ffb`b80cfc44 7_zip!CZipContextMenu::Initialize+0xd1 [X:\7z2201-src\CPP\7zip\UI\Explorer\ContextMenu.cpp @ 177] 
07 00000000`01de8ae0 00007ffb`b80a7b70 SHELL32!HDXA_QueryContextMenu+0x40c
08 00000000`01de8d80 00000000`005ed2ce SHELL32!CDefFolderMenu::QueryContextMenu+0x5c0
09 00000000`01de8ef0 00000000`00004e20 TOTALCMD64+0x1ed2ce
Вся цепочка вызовов с исходным кодом функций
STDMETHODIMP CZipContextMenu::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT dataObject, HKEY /* hkeyProgID */)
{
  _dropMode = false;
  _dropPath.Empty();
  if (pidlFolder != 0)
  {
    #ifndef UNDER_CE
    if (NShell::GetPathFromIDList(pidlFolder, _dropPath))
    {
      NName::NormalizeDirPathPrefix(_dropPath);
      _dropMode = !_dropPath.IsEmpty();
    }
    else
    #endif
      _dropPath.Empty();
  }
  return GetFileNames(dataObject, _fileNames); ????
}

????

HRESULT CZipContextMenu::GetFileNames(LPDATAOBJECT dataObject, UStringVector &fileNames)
{
  fileNames.Clear();
  if (!dataObject)
    return E_INVALIDARG;

  #ifndef UNDER_CE
  FORMATETC fmte = {CF_HDROP, NULL, DVASPECT_CONTENT, -1, TYMED_HGLOBAL};
  NCOM::CStgMedium stgMedium;
  HRESULT result = dataObject->GetData(&fmte, &stgMedium);
  if (result != S_OK)
    return result;
  stgMedium._mustBeReleased = true;

  NShell::CDrop drop(false);
  NMemory::CGlobalLock globalLock(stgMedium->hGlobal);
  drop.Attach((HDROP)globalLock.GetPointer());
  drop.QueryFileNames(fileNames);  ????
  
  #endif

  return S_OK;
}

????

void CDrop::QueryFileNames(UStringVector &fileNames)
{
  UINT numFiles = QueryCountOfFiles();
  fileNames.ClearAndReserve(numFiles);
  for (UINT i = 0; i < numFiles; i++)
  {
    const UString s2 = QueryFileName(i); ????
    if (!s2.IsEmpty())
      fileNames.AddInReserved(s2);
  }
}

????

UString CDrop::QueryFileName(UINT fileIndex)
{
  UString fileName;
  {
    UINT bufferSize = QueryFile(fileIndex, (LPWSTR)NULL, 0);    ????  вызов чтобы узнать размер буфера
    const unsigned len = bufferSize + 2;
    QueryFile(fileIndex, fileName.GetBuf(len), bufferSize + 1); ????  реальный вызов
    fileName.ReleaseBuf_CalcLen(len);
  }
  return fileName;
}

????

UINT QueryFile(UINT fileIndex, LPWSTR fileName, UINT fileNameSize)
    { return ::DragQueryFileW(m_Object, fileIndex, fileName, fileNameSize); }

????

SHELL32!DragQueryFileW

????

┌─────────────────────────────────────────────────────┐
│    4     0 [  0] SHELL32!DragQueryFileW             │
│   17     0 [  1]   SHELL32!DragQueryFileAorW        │
│   15     0 [  2]     KERNEL32!GlobalLock            │
│   28     0 [  3]       KERNEL32!IsBadReadPtr        │
│   22    28 [  2]     KERNEL32!GlobalLock            │
│   40    50 [  1]   SHELL32!DragQueryFileAorW        │
│    1     0 [  2]     KERNEL32!lstrlenWStub          │
│  654     0 [  2]     KERNELBASE!lstrlenW            │
│   60   705 [  1]   SHELL32!DragQueryFileAorW        │
│ 2181     0 [  2]     SHELL32!StringCchCopyW         │
│   68  2886 [  1]   SHELL32!DragQueryFileAorW        │
│   15     0 [  2]     KERNEL32!GlobalUnlock          │
│   73  2901 [  1]   SHELL32!DragQueryFileAorW        │
│    6     0 [  2]     SHELL32!_security_check_cookie │
│   83  2907 [  1]   SHELL32!DragQueryFileAorW        │
│    6  2990 [  0] SHELL32!DragQueryFileW             │
└─────────────────────────────────────────────────────┘

Коротко по сути - мы кликаем ПКМ на файлах (например хотим посмотреть свойства, или удалить), TC инициализирует процесс создания меню и подгружает расширения, дело доходит до 7-zip...
И тут 7-zip такой:
- Минуточку! Есть мнение, что вы недостаточно страдаете от оверинжиниринга реализации контекстного меню MS и я буду в буфер получать строки файловых путей для каждого выбранного объекта. Мне ведь надо проверить, что вы там выбрали и соответствующие пункты меню показать, чтобы все "по красоте".
- Кто вам сделал удобное меню?
- Кто молодец? А?... вот то-то же!

Безусловно стояла задача сделать пользователю "удобно", но стоило бы потестить и конечную реализацию удобняшек. А по факту мы имеем много бесполезной работы съедающей время.

Также в CDrop::QueryFileName вместо того чтобы один раз выделить буфер достаточного размера, вся цепочка с DragQueryFileW для каждого объекта вызывается дважды, умножая и мою печаль.

В общем, стоит все таки иногда прислушиваться к рекомендациям MS, как это делает WinRar и не пытаться объять "необъятное".

Псевдокод из функции в rarext.dll (расширение WinRar)
ObjCount = DragQueryFileW(hDrop, 0xFFFFFFFF, 0i64, 0);
if ( !ObjCount )
   return E_FAIL;
sub_39D20(a1 + 24);
Count = 16;                                 ✓
if ( ObjCount < 16 )                        ✓
   Count = ObjCount;                        ✓
for ( i = 0; i < Count; ++i )               ✓
{
   szFile[0] = 0;
   DragQueryFileW(hDrop, i, szFile, 0x800u);
   sub_39DC0(a1 + 24, szFile);
}

Причины найдены. Делаем выводы.

Ситуация неоднозначная, поскольку с одной стороны это проблема подавляющего большинства софта, мало кто следует гайдлайнам и мы имеем медленные меню. Но с другой, 7-zip забивший на рекомендации, вносит дополнительные задержки.

Как и где поправить код для 7zip (тем кто захочет это сделать), надеюсь понятно.
Сам я еще тот гуманитарий и поэтому оставлю поиск и разбор прочих недоработок в сырках архиватора тем кто разбирается в вопросе и кому это интересно.

Тестирование производилось на

  • последнем ванильном билде 7-zip v22.01 от 2022-07-15

  • Windows 10 Pro 21H2 19044.1320 x64, CPU 3800МГц

При желании быстро воспроизвести похожий результат (задумайтесь о жизни, если оно возникло), можете воспользоваться командным файлом для создания тестовой директории.

Например, таким
@echo off
cd /d "%~dp0"
set TestDir=%~dp0TEST
md "%TestDir%"
echo "Working..."
for /L %%N in (1,1,20000) do REM. >"%TestDir%\%%N.txt"

При запуске создаст рядом с собой папку TEST с 20k текстовыми файлами нулевого размера.
Затем открываете любой ФМ кроме штатного проводника, Ctrl+A и ПКМ.

Многие пользователи TC скорее всего не используют архиватор "классическим" образом, но это совершенно не важно в рамках рассматриваемой проблемы.

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

Кто-то может сказать, что все это ерунда и можно в общем-то игнорировать при современных мощностях железа, а с действительно большими количествами файлов мало кто работает. Это ваш выбор, пусть этой зимой вам будет чуть теплее, чем остальным, просто чаще вызывайте меню ????

Мораль проста - не все динамические расширения одинаково полезны, а если вы их пишете, надеюсь, умеете "в оптимизацию".
Ну и убирайте всё неиспользуемое из меню (ShellExView в помощь).

Комментарии (65)


  1. foxyrus
    21.12.2022 13:22
    +37

    Моя главная боль это не дай макаронному монстру подключить сетевой диск, который потом окажется отключенным. Теперь любые операции с вызовом системного окна сохранения файла будут сопровождаться тормозами, ведь windows этот путь еще и бережно добавить в папку Быстрого доступа. Эту проблему так и не решили даже в win11.

    Вторая проблема с тормозами это попытка Explorer определить содержимое (мета данные) медиа файлов, и если, имея неосторожность, откроешь случайно папку, содержащую медиа файлы без метаданных, Explorer их все равно будет сканировать и он сам в это время зависнет.


    1. AVX
      21.12.2022 15:54
      +10

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


      1. foxyrus
        21.12.2022 16:09

        У меня так подвисает Explorer минут на 20 на рабочем буке с касперским и интсаллером Intellij Idea.


      1. UMenyaNeudobnieVoprosiki
        21.12.2022 17:04
        +3

        Отличная причина использовать тотал или фар и, если тотал, то отключать всякие весёленькие ненужноиконки, хотя бы на внешних носителях


        1. stalinets
          21.12.2022 19:27
          +12

          Ещё могу добавить про безобразную работу десятки с дискетами. По работе приходится перекидывать файлы с измерительного прибора через дискету. Как известно, дискета - очень капризный носитель, особенно позднего выпуска, особенно те, что остались сейчас (им уже немало лет). Поэтому чем меньше её пилить головкой флопаря операциями чтения-записи, тем лучше. На WinXP система дискету вообще не трогает пока к ней не обратишься, и при обращении, считав перечень файлов и папок, дискетник замолкает. А на десятке идут самопроизвольные обращения к дискете, а при заходе на неё система настойчиво считывает какие-то метаданные файлов, строит эскизы картинок, и прочее и прочее. Эмпирически, дискета пилится втрое активнее, чем на XP или Win9x. И сдохнет раньше.


          1. numb13
            22.12.2022 06:56
            +1

            Пора уже на эмуляторы переходить https://en.m.wikipedia.org/wiki/Floppy_disk_hardware_emulator


            1. stalinets
              22.12.2022 07:18
              +4

              Там в приборе slim-флопик, и неизвестно какая распиновка. Теоретически, конечно, можно заморочиться и подключить туда эмулятор, но этот прибор не очень часто нужен, и мне за это не заплатят. Ещё и скажут, мол, испортил прибор, он теперь поверку не пройдёт. Так что пока пользуюсь как есть, по мере надобности вырабатывая старые запасы дискет.


              1. Vilgelm
                22.12.2022 08:00
                +1

                Дискеты все еще продают, как ни странно.


          1. ZakharS
            22.12.2022 08:41
            +13

            Папа, а правда, что Windows - многозадачная система?

            Да, сынок, сейчас дискету доформатирую и покажу.


    1. stalinets
      21.12.2022 19:22
      +15

      Могу добавить от себя: бывает, что открыты два окна Проводника, из левого дрэг-энд-дропом кидаешь файлик в правое, и если в правом окне, в левой его части, в дереве проводника, курсор мышки с файлом хоть на долю секунды зайдёт на иконку недоступного сетевого диска - ещё не дав донести файл до пустой области открытой папки в правом окне, винда тут же выкинет поверх всего окно, сбив фокус ввода, мол, данный диск недоступен. И тогда нужно бережно вернуть файлы обратно, а иначе можно и потерять данные. Это поведение ещё хуже, чем когда присутствует дребезг контакта в микрике левой клавиши мыши, когда тоже можно сбросить файлы не туда. Причём это зависит от каких-то фаз Луны: порой при наведении перетаскиваемого файла на недоступный диск курсор просто показывает значок недоступности и окошко не выкидывает.


      1. granta1337
        21.12.2022 20:32
        +1

        Вдобавок, у меня на 10 при правом клике по любому элементу (папке/файлу) в проводнике делается одно слышимое обращение к дискете, видимо, для отрисовки списка дисков (в т.ч. дискеты) в меню "Send to"


      1. AndreyDmitriev
        22.12.2022 05:37
        +1

        Хорошо, если сразу выкидывает. У меня на рабочем компе секунд на пятнадцать эксплорер просто намертво встаёт колом и ч довольно часто на эти грабли наступаю, хотя в основном far manager использую.


      1. numb13
        22.12.2022 07:05
        +2

        По умолчанию ведь винда копирует а не перемещает, если кидаешь на другой диск. Файлы точно теряются? Может в дерево проводника падают куда-нибудь?


      1. Wesha
        22.12.2022 07:55
        +7

        открыты два окна Проводника, из левого дрэг-энд-дропом кидаешь файлик в правое

        Коллеги, расскажите товарищу кто-нибудь про волшебные комбинации Ctrl-C / Ctrl-V, а то он так дотаскается когда-нибудь...


        1. consalt
          22.12.2022 09:45

          Забавно, но у меня бы такая операция была бы из правого окна в левое. Психологически слева у меня всегда destination, а справа source.


          1. dunkelfalke
            22.12.2022 12:11
            +6

            С такой психологией на иврите писать просто будет.


            1. usrsse2
              22.12.2022 12:29
              +3

              Да причём тут иврит, это же как оператор присваивания.


        1. Playa
          22.12.2022 10:21
          +3

          Не совсем равнозначная замена. Я использую drag-n-drop специально, чтобы не затирать буфер обмена.


          1. consalt
            22.12.2022 10:34
            +1

            В винде по win+v — всплывет буфер обмена с историей. Не благодарите :)


            1. ksbes
              22.12.2022 11:34
              +2

              Не для того придумывли дараг-энд-дроп, чтобы приходилось ручки к клаве тянуть…


            1. DaemonGloom
              22.12.2022 12:06

              Там будет только текст, файлов не будет. Бонусом — копирование файлов через RDP не всегда любит, когда что-то ещё попадает в буфер и прекращается после такого.


              1. Wesha
                22.12.2022 22:16

                Таскать файлы мышкой — через RDP? О-о-о-о, месье тонкий извращенец!


                1. DaemonGloom
                  23.12.2022 11:14

                  Нет, из RDP на реальную машину можно копировать как угодно. Хоть перетаскиванием, хоть комбинациями. Но при попытке потом что-то поместить в буфер обмена на локальной машине в это время — копирование просто прекращается. Натыкался на этот баг, минимум, на паре компьютеров уже.


            1. axe_chita
              23.12.2022 11:25

              С какой версии win+v работает?


              1. consalt
                23.12.2022 13:15

                Пользовался со времен вин10


        1. stalinets
          22.12.2022 20:00

          Не пойдёт. Я по работе делаю в Word фотоотчёт на несколько тысяч фотографий, и у меня всё оптимизировано: кинул мышкой фотку - она встала в ячейку таблицы и смасштабировалась. Тем же методом раскидываю фотки из общей кучи в десятки заранее подготовленных папок. С клавиатуры это будет дольше и неудобнее.


  1. Didimus
    21.12.2022 14:10
    +2

    Все это редактировалось ещё в win 98 c помощью твикеров, где-то в глубине реестра есть настройки для всего этого. С тех пор этот твикер попал в неблагонадёжные, и я его больше не использую. Назывался как, уже не помню.


  1. Alexufo
    21.12.2022 14:20

    7-zip так и не научился открывать зипы из макоси... вечно какие-то глюки.


    1. UMenyaNeudobnieVoprosiki
      21.12.2022 16:03
      +6

      А на макоси вы файлы жали 7-zip'ом или каким-нибудь нескучным весёленьким архиватором, который собрал все грабли исторически заложенные в zip, начиная с кодировок?


      1. dwdraugr
        21.12.2022 16:25
        +2

        Обычно на маке жмут либо встроенным компрессором, либо через keka. Да и не припомню каких-то проблем


        1. maeris
          22.12.2022 09:10

          Из того, что я помню как получатель этих архивов, сломанная кириллица в именах файлов плюс всякие .DS_Store в них.


          1. mayorovp
            22.12.2022 09:46

            Сломанная кириллица — это и есть уже упомянутые проблемы с кодировкой, а "всякие .DS_Store" почему вообще записаны в проблемы?


            1. khajiit
              22.12.2022 10:02

              Сломанная кириллица

              Маки не умеют в юникод? О_о
              Впрочем, если и так, то это явно на стороне мака проблема.


              1. usrsse2
                22.12.2022 12:40
                +1

                Ubuntu 18.04:

                zip --version
                Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
                This is Zip 3.0 (July 5th 2008), by Info-ZIP.
                Currently maintained by E. Gordon.  Please send bug reports to
                the authors using the web page at www.info-zip.org; see README for details.
                
                Latest sources and executables are at ftp://ftp.info-zip.org/pub/infozip,
                as of above date; see http://www.info-zip.org/ for other sites.
                
                Compiled with gcc 6.3.0 20170415 for Unix (Linux ELF).
                
                Zip special compilation options:
                	USE_EF_UT_TIME       (store Universal Time)
                	BZIP2_SUPPORT        (bzip2 library version 1.0.6, 6-Sept-2010)
                	    bzip2 code and library copyright (c) Julian R Seward
                	    (See the bzip2 license for terms of use)
                	SYMLINK_SUPPORT      (symbolic links supported)
                	LARGE_FILE_SUPPORT   (can read and write large files on file system)
                	ZIP64_SUPPORT        (use Zip64 to store large files in archives)
                	UNICODE_SUPPORT      (store and read UTF-8 Unicode paths)
                	STORE_UNIX_UIDs_GIDs (store UID/GID sizes/values using new extra field)
                	UIDGID_NOT_16BIT     (old Unix 16-bit UID/GID extra field not used)
                	[encryption, version 2.91 of 05 Jan 2007] (modified for Zip 3)
                
                Encryption notice:
                	The encryption code of this program is not copyrighted and is
                	put in the public domain.  It was originally written in Europe
                	and, to the best of our knowledge, can be freely distributed
                	in both source and object forms from any country, including
                	the USA under License Exception TSU of the U.S. Export
                	Administration Regulations (section 740.13(e)) of 6 June 2002.
                
                Zip environment options:
                             ZIP:  [none]
                          ZIPOPT:  [none]
                

                macOS 13:

                zip --version
                Copyright (c) 1990-2008 Info-ZIP - Type 'zip "-L"' for software license.
                This is Zip 3.0 (July 5th 2008), by Info-ZIP.
                Currently maintained by E. Gordon.  Please send bug reports to
                the authors using the web page at www.info-zip.org; see README for details.
                
                Latest sources and executables are at ftp://ftp.info-zip.org/pub/infozip,
                as of above date; see http://www.info-zip.org/ for other sites.
                
                Compiled with gcc Apple LLVM 14.0.0 (clang-1400.0.29.2) [+internal-os, ptrauth-isa=deployment-target-based] for Unix (Mac OS X) on Nov  4 2022.
                
                Zip special compilation options:
                	USE_EF_UT_TIME       (store Universal Time)
                	SYMLINK_SUPPORT      (symbolic links supported)
                	LARGE_FILE_SUPPORT   (can read and write large files on file system)
                	ZIP64_SUPPORT        (use Zip64 to store large files in archives)
                	STORE_UNIX_UIDs_GIDs (store UID/GID sizes/values using new extra field)
                	UIDGID_16BIT         (old Unix 16-bit UID/GID extra field also used)
                	[encryption, version 2.91 of 05 Jan 2007] (modified for Zip 3)
                
                Encryption notice:
                	The encryption code of this program is not copyrighted and is
                	put in the public domain.  It was originally written in Europe
                	and, to the best of our knowledge, can be freely distributed
                	in both source and object forms from any country, including
                	the USA under License Exception TSU of the U.S. Export
                	Administration Regulations (section 740.13(e)) of 6 June 2002.
                
                Zip environment options:
                             ZIP:  [none]
                          ZIPOPT:  [none]
                

                Нет UNICODE_SUPPORT.


                1. khajiit
                  22.12.2022 16:59

                  М-да… Увы, но без поддержки юникода никаких корректных имен не будет.


                  1. usrsse2
                    22.12.2022 17:13

                    Говорят, там имена в UTF-8 на самом деле, но не проставляется флаг, что они в UTF-8.


            1. consalt
              22.12.2022 10:44
              +1

              А зачем эти дс_сторе пихать в архив?


              1. mayorovp
                22.12.2022 11:57

                Затем же, зачем в архивы регулярно попадает папка .svn и иногда даже .git. Она просто присутствует в файловой системе.


                1. consalt
                  22.12.2022 13:18

                  У меня винда и 7зип не пакуют скрытые файлы и папки :) Поэтому всякие .гиты туда не попадут


              1. usrsse2
                22.12.2022 17:17

                Чтобы после распаковки они восстановились. Там хранится, например, расположение иконок и режим отображения.


            1. Shatun
              22.12.2022 11:27

              Я первый раз узнал про DS_Store когда собрал архив доков для теста, закинул на авс в наш обработчик и получил ошибку что файлик внутри архива не мог обработаться и выкинул ошибку. Но я же сам паковал архив-весело дебажил это дело и искал проблему.


      1. Alexufo
        21.12.2022 16:36
        +1

        стандартным


        1. UMenyaNeudobnieVoprosiki
          21.12.2022 17:09
          +1

          Вот, сабжик скоро 10 лет отметит, а воз и ныне там, 7z рулит

          https://ru-linux.livejournal.com/2883307.html
          а вот ещё более старая статья про кракозябры, в т.ч. в комментариях народ делится теми же новостями про устаревшие спеки zip

          https://habr.com/en/post/147843/


    1. stalinets
      21.12.2022 19:30

      У меня есть большие запароленные rar-архивы с личными документами, с 7% информации для восстановления, некоторые solid, созданные мною в WinRAR. Он их открывает нормально, а вот 7zip не может, тупит.


      1. mk2
        21.12.2022 22:13
        +3

        Rar это весьма закрытый формат, поэтому в своём продукте они запросто могут наворотить такого, что другие архиваторы своей открытой реализацией открыть не смогут. А реверсить по лицензионному соглашению нельзя. peazip.github.io/what-is-rar-file.html
        А вот претензия к зипам — это уже другое дело. Но 7-zip в этом плане менее «всеядный», чем некоторые другие архиваторы, да. Например, если в именах файла zip использовать обратный слеш вместо прямого, который должен быть там по спецификации, 7-zip не понимает, что это разделитель имён папок.


        1. lamerAlex
          21.12.2022 22:35
          +1

          unrar.dll


  1. Fragster
    21.12.2022 14:50
    +21

    Ссылки на багрепорт почему-то нет: https://sourceforge.net/p/sevenzip/bugs/2371/

    Там более, что тут есть прямо причина тормозов - может быть её следовало бы добавить и там?


    1. Antiever Автор
      21.12.2022 15:19
      +18

      Что есть, то есть (ну, точнее - того нет).
      После отсутствия вообще какой-либо реакции в саппорте, я и решил разобраться в вопросе сам. Насчет уточнений вы правы. И раз так случилось, что Игорь Викторович наш соотечественник, думаю можно оставить там ссылку на эту заметку. Я честно говоря, просто успел забыть про тикет, да и про саму заметку. Кто ж знал, что на модерации она почти три недели провисит в песочнице...


  1. wataru
    21.12.2022 18:31
    +3

    Я так и не понял, в чем именно проблема. В том, что 7zip циклом проходится по всем файлам, когда как совестные разработчики ограничиваются 16-ю файлами?


    1. Antiever Автор
      21.12.2022 19:07
      +4

      Ну "интрига с садовником" в общем-то раскрыта в начале заметки (в сноске о проводнике Windows). Если мы посмотрим Рекомендации по обработчикам контекстного меню от MS, то там явно прописан оптимальный алгоритм. А учитывая тот адок, который начинается при вызове меню, можно понять и столь малое значение (так-то по факту можно и для 64 объектов обработку делать и даже больше, без ощутимого лага). На алтарь универсальности методов MS принесли производительность, плюс тонна легаси и желание навернуть модных "свистелок" вместо оптимальной реорганизации кода.
      По такому алгоритму работает и штатный проводник, независимо от количества выбранных файлов, он останавливает итерацию после первых 16 объектов и создает меню. 7-zip в свою очередь, просто в лоб парсит все содержимое объекта данных, сколько бы тысяч "имён" там ни было, а это не самая быстрая операция, знаете ли... И если так случилось (а это почти всегда так и бывает), что программа не ограничила число файлов для которого вызывает меню, то она сама потратила лишнее время на генерацию PIDL, а тут еще и архиватор, ничего не проверяя, начинает лопатить всё подряд. Так и живём...


      1. DrMefistO
        21.12.2022 21:21

        Тогда я не понимаю, почему тупит на одном выделенном файле.


        1. Antiever Автор
          21.12.2022 21:32

          У вас лаг в меню на одном файле? Такого обычно быть не должно (ну может какое-то совсем кривое расширение шалит).

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


          1. DrMefistO
            21.12.2022 21:36

            Да, я про заметку, она сбила с толку, что проблема имеется и на одном файле.


  1. MonkAlex
    21.12.2022 20:53
    +1

    А что вы хотите сделать, что вынуждены выделять много файлов?

    Для упаковки папки - проще выделить папку уровнем выше и это будет условно бесплатно.

    На файлах порядка 10шт тормозов тоже нет.


    1. Antiever Автор
      21.12.2022 21:25
      +8

      Я уверен, что есть достаточно кейсов когда в работе находится ощутимо большое количество файлов. В этом подмножестве будут и случаи, когда нужно выделить файлы выборочно, тогда их нужно дополнительно выпиливать из архива?
      А если другой пользователь не догадается поступить подобным образом?
      А когда в новом апдейте меню еще "притормозят" нужно будет прикладывать к монитору подорожник?

      В общем я не из любителей делать вид, что граблей не существует и искать хитрые методы не наступать на них. С подобным подходом легко не заметить, что "ствол уже направлен в ногу"... Есть очевидная проблема, затрагивающая ощутимую часть нашей сферы деятельности, и ее надо решать. Вот так все просто.


      1. MonkAlex
        21.12.2022 21:32

        Я согласен, тормозит - плохо, пропускать такое нельзя.

        Но я 7zip-ом пользуюсь лет 10 и ни разу не столкнулся с такой проблемой. Мне не приходится видимо выполнять такие операции. Поэтому и сомнения вызвал кейс.

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


        1. Antiever Автор
          21.12.2022 21:49

          Мб вы просто используете штатный проводник, а не какой-то другой ФМ?

          Как это ни странно, но я сам пользуюсь 7-zip уже более 10 лет и просто уже привык к этим микро-тормозам и "неторопливому" меню. Если на этом не фокусировать внимание, вроде все достаточно быстро работает и мысли не было что-то проверять. Но вот как раз такие пограничные случаи и выявляют всю кривизну рабочего окружения, когда копнешь чуть глубже...

          PS

          Про выделение одного файла см выше.


          1. zuek
            22.12.2022 13:20
            +1

            Хммм... а я-то думал, это Far EMenu тормозит, тоже привык - если надо слишком много файлов обработать по контекстному меню на медленной машине (ну очень-очень редко, но такое бывает), делал через проводник, и как-то не задумывался о причинах...


            1. Antiever Автор
              22.12.2022 14:12

              К сожалению даже самый свежий билд Far (как и все файловые менеджеры что я тестировал) тоже ничего не проверяет и хоть и отрисовывает "окно" сам, базовый алгоритм создающий пункты для меню у него стандартный. Я в общем-то и написал данную заметку с целью "подсветить" проблему и думаю имеет смысл просто сделать тикет на гитхабе фара, благо он до сих пор активно допиливается.


  1. DrMefistO
    21.12.2022 21:23

    Кто-то выложит патч?


    1. Antiever Автор
      22.12.2022 00:48
      +10

      Магия Хабра сделала свое дело (ну или детализация причин проблемы, кому как удобнее), тикет переведен в статус open-accepted и разработчик "хотя бы частично" исправит сабж.


  1. avz
    22.12.2022 09:07
    +5

    Можно про "первый подвернувшийся профайлер" поподробнее?


  1. domix32
    22.12.2022 11:17

    Напомнило квадратичную сортировку иконок рабочего стола.


    1. Nikita22007
      22.12.2022 23:27

      Есть перевод этой статьи на Хабре
      habr.com/ru/company/vdsina/blog/544218


  1. Shatun
    23.12.2022 00:20

    Будет ли та же проблема в 11 винде, если ты не вызываешь старое меню? Там как раз эта часть с кастомными пунктами меню переделана