DLLINFO

Расширение оболочки для изменения иконок у dll в зависимости от их типа.

Авторы: Алексей Кирюшкин
The RSDN Group
Владимир Смирнов
Опубликовано: 06.09.2002
Версия текста: 1.1

Что мне с этого будет
Как установить
Подробности реализации
Если вам не понравились иконки…

Исполняемый модуль, dllinfo.dll
Исходные тексты (VC7)

Что мне с этого будет

А будет вот что – в зависимости от типа dll (COM, .NET, “обычная” dll) в окне проводника для неё будет показываться своя иконка:


Крупные значки


Мелкие значки

Ага, не знали что mfc42.dll – COM сервер :-)

Как установить

Скопируйте dllinfo.dll на локальный диск и зарегистрируйте командой

Regsvr32 dllinfo.dll

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

Подробности реализации

О написании расширения, устанавливающего иконку для файла в зависимости от его содержимого можно прочитать в Руководстве по написанию расширения для настройки иконок, отображаемых для файлов заданного типа от Michael Dunn, здесь же имеет смысл остановиться на способе определения типа dll:

// получаем тип dll
DLLTYPE Cdlltester::GetDllType()
{
    WORD ImageType = 0;
    DLLTYPE result = ITISNOTDLL;
    HRESULT ( STDAPICALLTYPE * pfn ) ();

    // загружаем, но DllMain не вызываем, ни к чему
    HINSTANCE hi = ::LoadLibraryEx ( m_szFilename, NULL,
                                     DONT_RESOLVE_DLL_REFERENCES );

    if ( hi )
    {
        // пробуем на COM
        ( FARPROC& ) pfn = ::GetProcAddress ( hi, "DllGetClassObject" );

        if ( pfn )       // это COM dll !
            result = COMDLL;
        else
        {
            // DLL, но не COM
            // может .NET ?
            ImageType = ::GetExecutableImageType( hi );

            if ( ! EIT_IS_DLL( ImageType ) )
                result = ITISNOTDLL;
            else
            {
                if ( EIT_IS_DOTNET( ImageType ) )
                    result = NETDLL;
                else
                    // если, неCOM, неNET, значит просто dll
                    result = WIN32DLL;
            }

        }

        ::Sleep( 0 );
        ::FreeLibrary ( hi );
    }

    return result;
}

Как видно из текста программы, для проверки на COM просто проверяется наличие экспортируемой функции DllGetClassObject. Исполняемые файлы .NET - это обычные файлы формата PE (Portable Executable). Для получения адреса той области, где располагается специфичная для .NET информация, необходимо обратиться к элементу IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR массива IMAGE_DATA_DIRECTORY, расположенному в конце заголовка PE-файла (структуры IMAGE_NT_HEADERS).

В статье Matt Pietrek “An In-Depth Look into the Win32 Portable Executable File Format” есть упоминание о том, что в более новых версиях системных заголовочных файлов IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR переименован в IMAGE_DIRECTORY_ENTRY_COMHEADER, однако в ноябрьском 2001 года Platform SDK нового названия еще нет.

Каждый элемент массива IMAGE_DATA_DIRECTORY содержит информацию о расположении данных, определяемых этим элементом.

 IMAGE_DATA_DIRECTORY DataDirectory[IMAGE_NUMBEROF_DIRECTORY_ENTRIES];
 typedef struct _IMAGE_DATA_DIRECTORY
 {
   DWORD VirtualAddress; //относительный виртуальный адрес данных
   DWORD Size; //размер данных
 }

Таким образом, для определения принадлежности файла к .NET достаточно проверить поле Size элемента IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR массива DataDirectory. Если в файле есть .NET информация, размер данных, Size будет отличным от нуля.

#define EIT_IS_DLL(t) ((-1!=(t))&&((t) & 0x1))
#define EIT_IS_DOTNET(t) ((-1!=(t))&&((t) & 0x2))

//-------------------------------------------------------------------
// Функция для проверки типа уже загруженного исполняемого файла
//
// Параметры:
//
//   hinst - HINSTANCE загруженного модуля
//
// Возвращаемое значение:
//
//   Если младший бит возвр.значения установлен, то файл является
//   динамической библиотекой (dll), иначе это exe-файл. Для
//   проверки можно использовать макрос EIT_IS_DLL();
//
//   Второй бит возвр.значения установлен, то файл является
//   файлом .NET. Для проверки можно использовать макрос
//   EIT_IS_DOTNET();
//
//   В случае неудачи возвращается -1.
//-------------------------------------------------------------------
WORD GetExecutableImageType( HINSTANCE hinst )
{
    PBYTE pFileBase;
    PIMAGE_DOS_HEADER pDosHeader;
    PIMAGE_NT_HEADERS pNtHeader;
    WORD result = 0;

    if ( !hinst )
        return -1;

    pFileBase = ( PBYTE ) hinst;

    // Убеждаемся в корректности
    pDosHeader = ( PIMAGE_DOS_HEADER ) pFileBase;

    if ( IMAGE_DOS_SIGNATURE == pDosHeader->e_magic )
    {
        pNtHeader = ( PIMAGE_NT_HEADERS ) ( pFileBase + pDosHeader->e_lfanew );

        if ( ( !IsBadReadPtr( pNtHeader, sizeof( pNtHeader->Signature ) ) ) &&
             ( IMAGE_NT_SIGNATURE == pNtHeader->Signature ) &&
             ( pNtHeader->FileHeader.Characteristics &
               IMAGE_FILE_EXECUTABLE_IMAGE ) )
        {
            // Делаем необходимые проверки
            if ( pNtHeader->FileHeader.
                 Characteristics & IMAGE_FILE_DLL )
                result |= 0x1;

            if ( pNtHeader->OptionalHeader.
                 DataDirectory[ IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR ].Size )
                result |= 0x2;
        }
    }

    return result;
}

Спасибо Erusov D. Sergeevich за справедливое замечание по первому варианту DLLINFO – загружать dll два раза было совершенно не обязательно.

Если вам не понравились иконки…

… то они нарисованы Виталием Брусенцевым (Виталий, привет! :-) ). Cможете нарисовать значки более точно и красиво отражающие понятия COM, .NET и Win32 dll – не забудьте поделиться со всеми.


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