Как назначить значок для элементов контекстного меню Windows по умолчанию «Копировать / Вырезать / Вставить / Удалить»?

12

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

введите описание изображения здесь

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

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

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


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

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

ElektroStudios
источник
На какую иконку вы ссылаетесь? У тебя есть скриншот?
Raystafarian
@Raystafarian Я обновил вопрос с изображением.
ElektroStudios
1
@Raystafarian: вопрос в том, как добавить пользовательский значок к существующим элементам основного контекстного меню, таким как «Вырезать» , «Копировать» , «Удалить» , «Переименовать» и т. Д. Кстати, при добавлении нового пользовательского элемента в контекстное меню он это очень просто, потому что вам нужно только добавить Iconстроковое значение в ключ, например HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(и значение Iconбудет, например, например, %SystemRoot%\System32\shell32.dll,-133или sg. else). НО добавление Iconстрокового значения в разные обработчики контекстного меню не работает, как при добавлении его к этим пользовательским элементам.
Sk8erPeter
Вот еще один скриншот, чтобы прояснить его (интересная часть находится в красных границах): i.imgur.com/fmewg6L.png . Кстати, как вы можете видеть, у меня есть несколько пользовательских элементов в контекстном меню с пользовательскими значками (например, «Открыть с помощью Notepad ++» ) - это именно то, чего мы хотели бы достичь с помощью существующих элементов системного контекстного меню!
Sk8erPeter
1
@ Sk8erPeter Наилучшим лидером на данный момент является перспектива создания обработчика контекстного меню оболочки, который использует SetMenuItemInfoв ответ QueryContextMenu.
Бен Н

Ответы:

10

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

Прежде всего, я хочу, чтобы вы знали, что я изучил 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;
}

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

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

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

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

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

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

Я написал это в 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) Я старался изо всех сил, но не мог заставить скомпилированную версию работать на Windows 10 (сборка 10240). Я не знаю, в чем проблема, все образы bmp существуют в правильном пути ( C:\shellicon\copy.bmpи т. Д. - это значки размером 20x20 пикселей в формате BMP), и я зарегистрировал dll в качестве администратора в командной строке, с regsvr32 ContextIcons.dllкоторой работал успешно, но Я не вижу изменений в контекстном меню. Я даже перезагружал компьютер, незарегистрирован и снова регистрировал dll, но без изменений. Я пытаюсь скомпилировать источник в VS2015!
Sk8erPeter
@ Sk8erPeter MSDN сказал, что иконки должны быть 16x16, но у меня работает 20x20. Может быть, Windows 10 требует 16x16? Обратите внимание, что вам нужно перезапустить Проводник, чтобы изменения вступили в силу.
Бен Н
2
@ Sk8erPeter Конечно, здесь вы идете . Я посмотрю о размещении кода на GitHub. Сейчас работаю над загрузкой Windows 10 ...
Бен Н
2
Вы не поверите ... Это работает с вашими изображениями! : D: D Это означает, что у меня есть некоторые bmp-файлы, которые Windows не может обработать, не знаю почему (позже я тоже это проверю). В любом случае, большое спасибо, ваш код действительно решает проблему! :)
Sk8erPeter
1
@BenN: ОК, спасибо! :) Было бы немного удобнее. Кстати, я понял, что если я открою свои ранее не работающие изображения в легендарной программе Paint и сделаю «Сохранить как»> «24-битное растровое изображение (.bmp; .dip)» (поэтому сохраните его в файл BMP снова), и я использую этот новый файл в качестве исходного изображения, он работает. Конечно, размер растрового изображения должен быть ровно 16x16 пикселей. Таким образом, Paint создает ожидаемый растровый формат, который составляет 24 бита на пиксель (16,7 млн. Цветов), 96x96 точек на дюйм и 16x16 пикселей. Ранее я конвертировал и изменял размеры файлов .png в IrfanView в файлы .bmp, эти значки не работали.
Sk8erPeter
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

nijave
источник
1
Интересная информация. Тем не менее, это скорее комментарий, чем ответ. Теперь у вас достаточно представителей, чтобы комментировать везде :)
Бен Н