12

В Windows 8/8.1 x64 я хотел бы назначить пользовательский значок для элементов контекстного меню Windows по умолчанию, таких как элементы « Копировать», « Вырезать», « Вставить», « Удалить», « Отменить», « Повторить» и « Отправить», которые по умолчанию имеют любой значок:

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

Или, другими словами, как назначить иконку для меню расширения оболочки, например SendTo shellex?.

Исследование


Как прокомментировал @ Sk8erPeter, кажется, что:

«Добавление строкового значения Icon в разные обработчики контекстного меню не работает так, как при добавлении его в пользовательский элемент, например, HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY »

2 ответа2

9

Уведомление о присоединении: я являюсь автором программного обеспечения, упомянутого в этом ответе.

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

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

Скриншот этого в действии, с самыми уродливыми иконами, известными человеку:

В бою

Вы можете скачать эти иконки, если вы действительно хотите.

Настройка его

Загрузите его (из моего Dropbox). Примечание: этот файл обнаруживается одним сканером VirusTotal как вредоносная программа. Это понятно, учитывая то, что нужно сделать, чтобы ударить существующие записи. Я даю вам слово, что это не наносит никакого вреда вашему компьютеру. Если вы подозрительны и / или хотите изменить и расширить его, см. Код на GitHub !

Создайте папку на диске C:\shellicon . Создайте файлы BMP со следующими заголовками: copy , cut , delete , paste , redo , sendto , undo . (Надеюсь, очевидно, кто что делает.) Эти изображения, вероятно, должны быть размером 16 на 16 пикселей (или какими бы большими ни были ваши настройки DPI в меню), но я добился успеха и с более крупными. Если вы хотите, чтобы значки выглядели прозрачными, вы должны просто сделать их фон того же цвета, что и контекстное меню. (Этот прием также используется в Dropbox.) Я сделал свои ужасные иконки с MS Paint; другие программы могут сохранять или не сохранять данные способом, совместимым с LoadImageA . 16 на 16 с глубиной цвета 24 бита при 96 пикселях на дюйм, кажется, наиболее надежный набор свойств изображения.

Поместите DLL где-нибудь доступным для всех пользователей, эта папка, которую вы только что создали, является хорошим выбором. Откройте приглашение администратора в папке, содержащей DLL и выполните regsvr32 ContextIcons.dll . Это создает регистрационную информацию для типов оболочки * , Drive , Directory и Directory\Background . Если вы когда-нибудь захотите удалить расширение оболочки, сделайте regsvr32 /u ContextIcons.dll .

Соответствующий код

По сути, расширение просто запрашивает текст каждого элемента контекстного меню с помощью GetMenuItemInfo и, при необходимости, корректирует значок с помощью SetMenuItemInfo.

Visual Studio генерирует много загадочного кода для проектов ATL, но это содержимое IconInjector.cpp , которое реализует обработчик контекстного меню:

// IconInjector.cpp : Implementation of CIconInjector

#include "stdafx.h"
#include "IconInjector.h"
#include <string>

// CIconInjector

HBITMAP bmpCopy = NULL;
HBITMAP bmpCut = NULL;
HBITMAP bmpUndo = NULL;
HBITMAP bmpRedo = NULL;
HBITMAP bmpSendto = NULL;
HBITMAP bmpDel = NULL;
HBITMAP bmpPaste = NULL;
STDMETHODIMP CIconInjector::Initialize(LPCITEMIDLIST pidlFolder, LPDATAOBJECT pDataObj, HKEY hProgID) {
    // Load the images
    bmpCopy = (HBITMAP)LoadImageA(NULL, "C:\\shellicon\\copy.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
    bmpCut = (HBITMAP)LoadImageA(NULL, "C:\\shellicon\\cut.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
    bmpUndo = (HBITMAP)LoadImageA(NULL, "C:\\shellicon\\undo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
    bmpRedo = (HBITMAP)LoadImageA(NULL, "C:\\shellicon\\redo.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
    bmpSendto = (HBITMAP)LoadImageA(NULL, "C:\\shellicon\\sendto.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
    bmpDel = (HBITMAP)LoadImageA(NULL, "C:\\shellicon\\delete.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
    bmpPaste = (HBITMAP)LoadImageA(NULL, "C:\\shellicon\\paste.bmp", IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_DEFAULTSIZE);
    int err = GetLastError();
    return S_OK;
}
STDMETHODIMP CIconInjector::QueryContextMenu(HMENU hmenu, UINT uMenuIndex, UINT uidFirst, UINT uidLast, UINT flags) {
    using namespace std;
    if (flags & CMF_DEFAULTONLY) return S_OK; // Don't do anything if it's just a double-click
    int itemsCount = GetMenuItemCount(hmenu);
    for (int i = 0; i < itemsCount; i++) { // Iterate over the menu items
        MENUITEMINFO mii;
        ZeroMemory(&mii, sizeof(mii));
        mii.cbSize = sizeof(mii);
        mii.fMask = MIIM_FTYPE | MIIM_STRING;
        mii.dwTypeData = NULL;
        BOOL ok = GetMenuItemInfo(hmenu, i, TRUE, &mii); // Get the string length
        if (mii.fType != MFT_STRING) continue;
        UINT size = (mii.cch + 1) * 2; // Allocate enough space
        LPWSTR menuTitle = (LPWSTR)malloc(size);
        mii.cch = size;
        mii.fMask = MIIM_TYPE;
        mii.dwTypeData = menuTitle;
        ok = GetMenuItemInfo(hmenu, i, TRUE, &mii); // Get the actual string data
        mii.fMask = MIIM_BITMAP;
        bool chIcon = true;
        if (wcscmp(menuTitle, L"&Copy") == 0) {
            mii.hbmpItem = bmpCopy;
        }
        else if (wcscmp(menuTitle, L"Cu&t") == 0) {
            mii.hbmpItem = bmpCut;
        }
        else if (wcscmp(menuTitle, L"&Paste") == 0) {
            mii.hbmpItem = bmpPaste;
        } 
        else if (wcscmp(menuTitle, L"Se&nd to") == 0) {
            mii.hbmpItem = bmpSendto;
        }
        else if (wcsstr(menuTitle, L"&Undo") != NULL) {
            mii.hbmpItem = bmpUndo;
        }
        else if (wcsstr(menuTitle, L"&Redo") != NULL) {
            mii.hbmpItem = bmpRedo;
        }
        else if (wcscmp(menuTitle, L"&Delete") == 0) {
            mii.hbmpItem = bmpDel;
        }
        else {
            chIcon = false;
        }
        if (chIcon) SetMenuItemInfo(hmenu, i, TRUE, &mii);
        free(menuTitle);
    }
    return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); // Same as S_OK (= 0) but is The Right Thing To Do [TM]
}
STDMETHODIMP CIconInjector::InvokeCommand(LPCMINVOKECOMMANDINFO info) {
    return S_OK;
}
STDMETHODIMP CIconInjector::GetCommandString(UINT_PTR, UINT, UINT*, LPSTR, UINT) {
    return S_OK;
}

Обратите внимание, что HBITMAP никогда не очищаются, но это не имеет большого значения, учитывая, что содержимое DLL исчезнет, когда Explorer отключится. Значки в любом случае едва занимают память.

Если вы компилируете для 32-битной версии, первым параметром GetCommandString будет просто UINT вместо UINT_PTR .

Если вам действительно нужны прозрачные значки, вам нужно создать окно с нужным значком, а затем установить для mii.hBmpItem значение HBMMENU_SYSTEM и поместить указатель на окно в mii.dwItemData , как описано в нижней части статьи MSDN на MENUITEMINFO , Я не смог понять, как создавать окна из расширений оболочки. LR_LOADTRANSPARENT выглядит многообещающе как флаг LoadImageA , но у него есть свои подводные камни - в частности, он не работает, если вы не используете 256-битные растровые изображения.

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

Кто-то достаточно опытный в C++ мог бы, вероятно, извлечь ресурсы из других библиотек DLL и преобразовать их в HBITMAP , но это кто-то не я.

Модифицируя это

Я написал это в Visual Studio, который я считаю лучшим редактором для Windows C++.

Загрузите файл SLN в Visual Studio 2015 после установки инструментов C++. В IconInjector.cpp вы можете добавить записи HBITMAP вверху и вызовы LoadImageA в Initialize для добавления новых значков. Внизу в секции else if используйте вызов wcscmp для поиска точного соответствия или вызов wcsstr для поиска наличия подстроки. В обоих случаях & представляет позицию подчеркивания / ускорителя при использовании Shift+F10. Установите режим Release и свою архитектуру x64 и выполните BuildBuild Solution. Вы получите сообщение об ошибке при регистрации выхода, но не волнуйтесь; Вы бы все равно хотели сделать это вручную. Завершите работу проводника, скопируйте новую DLL-библиотеку (\x64\Release\ContextIcons.dll в папке решения) на место, затем выполните танец regsvr32 .

Атрибуции

Большое спасибо авторам MSDN и создателю « Полного идиотского руководства по написанию расширений оболочки », на которое я много ссылался.

панегирик

Для многих экземпляров Explorer, которые были убиты при создании этого расширения оболочки: вы умерли по великой причине, что некоторые люди в Интернете могут иметь значки рядом со своими словами.

1

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

Представляет интерес (экспорт реестра):

HKEY_CLASSES_ROOT\CLSID {3ad05575-8857-4850-9277-11b85bdb8e09}

(По умолчанию) REG_SZ Копировать / Переместить / Переименовать / Удалить / Связать объект

AppID REG_SZ {3ad05575-8857-4850-9277-11b85bdb8e09}

LocalizedString REG_EXPAND_SZ @% SystemRoot%\system32\shell32.dll, -50176

Под ключом InProcServer32 он ссылается на shell32.dll. Есть пара других с соответствующими звучащими именами. Возможно также интерес представляет windows.storage.dll

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .