Как это всё ускорить?
От: Hadgi Россия  
Дата: 16.10.03 13:50
Оценка:
Это кусок моего проводника.
Всё работает, но очень медленно, если появляется желание просмотреть содержимое чего-нибудь вроде system32.
Если закомментировать ::SHGetFileInfo(...) в FillFilesList, то намного быстрее (для system32 время заполнения ListCtrl-а сокращается с 6 секунд до пары сотен милисекунд).
Но при этом иконки теряются и смотрится уже совсем не так.
Посоветуйте как это всё можно ускорить без потери наглядности.
//я пробовал вариант с FindFirstFile и FindNextFile вместо CFileFind, но время исполнения
отличается совсем мало!
struct FILE_DESCRIPTION
{
DWORD dwFileAttributes;
CString FileName;
CTime lastChangeTime;
CString FileSize;
UINT iIconIndex;
};

UINT FillFilesList()
{
FILE_DESCRIPTION fAttributes;
SHFILEINFO info;
DWORD size = sizeof(SHFILEINFO);
BOOL fFound;
CFileFind finder;
CString file_to_find = m_CurDir+"*.*";

m_pOutList->DeleteAllItems();
fFound = finder.FindFile(file_to_find);
for(nFileListSize=0; fFound; nFileListSize++)
{
fFound = finder.FindNextFile();
if(finder.IsHidden()) continue;
fAttributes.FileName = finder.GetFileName();
if(fAttributes.FileName==".") continue;

if(!finder.IsDirectory())
fAttributes.FileSize.Format("%d",finder.GetLength64());
else fAttributes.FileSize = "<DIR>";
finder.GetLastWriteTime(fAttributes.lastChangeTime);

::SHGetFileInfo(finder.GetFilePath(), 0, &info, size, SHGFI_ICON | SHGFI_SMALLICON);
fAttributes.iIconIndex = finder.IsDots()?3:info.iIcon;
InsertItem(m_pOutList, fAttributes);
}
return nFileListSize;
}

#define MAX_BUFFER 255
void InsertItem(CListCtrl *pList, FILE_DESCRIPTION file)
{
UINT iItem, iActualItem;
LVITEM lvitem;
LPTSTR subItemName;
CString str;
iItem = 0;
for(UINT iSubItem = 0; iSubItem<3; iSubItem++)
{
lvitem.iItem = iSubItem==0?iItem:iActualItem;
lvitem.iSubItem = iSubItem;
lvitem.mask = LVIF_TEXT |(iSubItem==0?LVIF_IMAGE:0);
switch(iSubItem)
{
case 0: {subItemName = file.FileName.GetBuffer(MAX_BUFFER);break;}
case 1: {subItemName = file.FileSize.GetBuffer(12); break;}
case 2: {
str = file.lastChangeTime.Format("%d.%m.%Y %H:%M");
subItemName = str.GetBuffer(15);
break;
}
}
lvitem.pszText = subItemName;
lvitem.iImage = file.iIconIndex;
if(iSubItem==0)
iActualItem = pList->InsertItem(&lvitem);
else pList->SetItem(&lvitem);
}
}
Re: Как это всё ускорить?
От: AlexZu Россия  
Дата: 16.10.03 14:04
Оценка:
Здравствуйте, Hadgi, Вы писали:


H>Посоветуйте как это всё можно ускорить без потери наглядности.


Как вариант: использовать virtual listview (LVS_OWNERDATA) и начинать пробегаться не по всем файлам, а только по видимым на экране, так-скзать отложенное получение информации.
Re: Как это всё ускорить?
От: MaxK  
Дата: 16.10.03 14:09
Оценка: 18 (1)
Здравствуйте, Hadgi, Вы писали:

H>Всё работает, но очень медленно, если появляется желание просмотреть содержимое чего-нибудь вроде system32.

H>Если закомментировать ::SHGetFileInfo(...) в FillFilesList, то намного быстрее (для system32 время заполнения ListCtrl-а сокращается с 6 секунд до пары сотен милисекунд).
H>Но при этом иконки теряются и смотрится уже совсем не так.
H>Посоветуйте как это всё можно ускорить без потери наглядности.

Как Вы, наверное, замечали уже, Виндовый проводник в папках с большим числом файлов показывает сначала файлы с иконкой соотвтетствующей неопределенному типу файла. А потом постепенно файлам начинают соответствовать реальные иконки. Следовательно выход в создании дополнительного потока, который будет выполнять эту работу. А основной поток будет только заполнять листвью списком файлов. Плюс, очень похоже, что проводник кэширует эту информацию (потому что при повторном вхождении в папку обновление иконок происходит достаточно быстро). Но я думаю даже без этого "всё можно ускорить без потери наглядности". Желаю удачи!
Re: Как это всё ускорить?
От: Valerio Россия linkedin.com/in/boronin
Дата: 17.10.03 06:56
Оценка:
а попробуйте этот код (я использовал у себя чтобы получить иконку для exe например файлов, но можно и для других любых это сделать — AddIconByString):

// ----------------------------------------------------------------------
//
// Function:    AddIconByString(CString& str,CListCtrl& refList,CImageList* pImageList)
//
// Purpose:        add item with icon, if it's unique, to the list view
//
// Arguments:    sz            [IN]    text of item to add
//                refList        [IN]    reference to the ListView control
//                pImageList    [IN]    pointer to image list to add icon
//
// Returns:        None
//
// Notes:        
//
bool AddIconByString(CString& str,CListCtrl& refList,CImageList* pImageList)
{
    //check for duplicates
    LVFINDINFO info;
    info.flags = LVFI_STRING;
    info.psz = str;
    if (refList.FindItem(&info) == -1) 
    {
        refList.InsertItem(0,str,GetProcessIcon(str,pImageList));
        return 1;
    }
    return 0;
}

// ----------------------------------------------------------------------
//
// Function:    GetProcessIcon(const CString& strPath, CImageList* pImageList)
//
// Purpose:        get icon from process path and add it to the image list
//
// Arguments:    strPath    [IN]    path of process
//
// Returns:        on success index of found icon in the image list, 
//                otherwise -1
//
// Notes:        
//
int GetProcessIcon(const CString& strPath, CImageList* pImageList)
{
    SHFILEINFO shFinfo;

    int pos=strPath.ReverseFind(_T('.'));
    if (pos!=-1 && strPath.ReverseFind(_T('\\'))<pos) //don't forget about User.Domain\Folder\ case
    {
        CString ext;
        //new way: ask default icon for extension
        ext=strPath.Right(strPath.GetLength()-pos);
        ext.MakeUpper();
        if (ext!=_T(".EXE"))
        {
            if (!SHGetFileInfo(ext,FILE_ATTRIBUTE_NORMAL,&shFinfo,sizeof(shFinfo),
                SHGFI_USEFILEATTRIBUTES|SHGFI_ICON|SHGFI_SMALLICON)) return 0;
        }
    }

    //old way: try to get icon from path
    if (!SHGetFileInfo(strPath,0,&shFinfo,sizeof(shFinfo),SHGFI_ICON|SHGFI_SMALLICON)) 
        return 0;

    //add to image list 
    return pImageList->Add(shFinfo.hIcon);
}


// ----------------------------------------------------------------------
//
// Function:    AddIconByString(CString& str,CListCtrl& refList,CImageList* pImageList)
//
// Purpose:        add item with icon, if it's unique, to the list view
//
// Arguments:    sz            [IN]    text of item to add
//                refList        [IN]    reference to the ListView control
//                pImageList    [IN]    pointer to image list to add icon
//
// Returns:        None
//
// Notes:        
//
bool AddIconByString(CString& str,CListCtrl& refList,CImageList* pImageList)
{
    //check for duplicates
    LVFINDINFO info;
    info.flags = LVFI_STRING;
    info.psz = str;
    if (refList.FindItem(&info) == -1) 
    {
        refList.InsertItem(0,str,GetProcessIcon(str,pImageList));
        return 1;
    }
    else 
    {
        return 0;
    }
}


давно писал но вроде бы гораздо быстрее стандартного вызова как раз за счет того что берется из кэша иконка ассоциированная с типом файла в первую очередь
... << RSDN@Home 1.1 beta 2 >>
Valery A. Boronin, RSDN Team, linkedin.com\in\boronin
R&D Mgmt & Security. AppSec & SDL. Data Protection and Systems Programming. FDE, DLP, Incident Management. Windows Filesystems and Drivers.
Re: Как это всё ускорить?
От: algol Россия about:blank
Дата: 17.10.03 07:33
Оценка:
Здравствуйте, Hadgi, Вы писали:

H>Это кусок моего проводника.

H>Всё работает, но очень медленно, если появляется желание просмотреть содержимое чего-нибудь вроде system32.
H>Если закомментировать ::SHGetFileInfo(...) в FillFilesList, то намного быстрее (для system32 время заполнения ListCtrl-а сокращается с 6 секунд до пары сотен милисекунд).
H>Но при этом иконки теряются и смотрится уже совсем не так.
H>Посоветуйте как это всё можно ускорить без потери наглядности.
H>//я пробовал вариант с FindFirstFile и FindNextFile вместо CFileFind, но время исполнения
H>отличается совсем мало!

Можно вызывать SHGetFileInfo() только тогда, когда иконка выводится на экран. Для этого нужно при добавлении элемента указать iIconIndex = I_IMAGECALLBACK, и возвращать индекс иконки из обработчика LVN_GETDISPINFO. При этом можно еще поставить lvItem.mask |= LVIF_DI_SETITEM, чтобы индекс иконки запоминался и повторно не запрашивался.

Для ускорения SHGetFileInfo() можно еще указать флаг SHGFI_USEFILEATTRIBUTES в uFlags и указать атрибуты файла в dwFileAttributes. Это исключит обращение к указанному файлу и вернет зерегистрированную иконку для данного типа файлов. Поскольку система использует различные иконки для каталогов типа Мои рисунки, Моя музыка и т.д., то лучше ставить этот флаг только для файлов и не ставить для каталогов.
Re: Как это всё ускорить?
От: AlexZu Россия  
Дата: 17.10.03 07:43
Оценка:
Здравствуйте, Hadgi, Вы писали:

H>Это кусок моего проводника.

H>Всё работает, но очень медленно, если появляется желание просмотреть содержимое чего-нибудь вроде system32.

Если у вас нет желания сделать круче ms windows explorer, то идеальный вариант это использховать Shell Namespace, и для вашего случая IShellFolder в частности.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.