Как назначить значок для пунктов "Копировать / Вырезать / Вставить / Удалить" в контекстном меню Windows по умолчанию?
В Windows 8/8.1 x64 я хотел бы назначить пользовательский значок для элементов контекстного меню Windows по умолчанию, таких как пункты " Копировать", " Вырезать", " Вставить", " Удалить", " Отменить", " Повторить" и " Отправить", которые по умолчанию имеют любой значок:
Где я могу найти "ссылку" на эти пункты контекстного меню в реестре, а затем добавить для них значение реестра "значок"?
Или, другими словами, как назначить иконку для меню расширения оболочки, например SendTo shellex?.
Исследование
Как прокомментировал @ Sk8erPeter, кажется, что:
"Добавление
Icon
Строковое значение для разных обработчиков контекстного меню не работает, как при добавлении его в пользовательский элемент, например,HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY
"
2 ответа
Уведомление о присоединении: я являюсь автором программного обеспечения, упомянутого в этом ответе.
Прежде всего, я хочу, чтобы вы знали, что я изучил C++ и Win32 только для этого вопроса.
Я разработал 64-битное расширение оболочки, которое регистрируется как обработчик контекстного меню. Когда он вызывается, он копается в существующих пунктах меню в поисках интересных записей. Если он находит его, он прикрепляет значок (который должен быть загружен ранее). На данный момент он ищет Копировать, Вырезать, Удалить, Вставить, Повторить, Отправить и Отменить. Вы можете добавить свой собственный, изменив код; процедура для этого описана ниже. (Извините, я недостаточно хорош в C++, чтобы его можно было настраивать.)
Скриншот этого в действии, с самыми уродливыми иконами, известными человеку:
Вы можете скачать эти иконки, если вы действительно хотите.
Настройка его
Загрузите его (из моего Dropbox). Примечание: этот файл обнаруживается одним сканером VirusTotal как вредоносная программа. Это понятно, учитывая то, что нужно сделать, чтобы ударить существующие записи. Я даю вам слово, что это не наносит преднамеренного вреда вашему компьютеру. Если вы подозрительны и / или хотите изменить и расширить его, ознакомьтесь с кодом на GitHub!
Создайте папку на диске C: 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 и выполните Build → Build Solution. Вы получите сообщение об ошибке при регистрации выхода, но не волнуйтесь; Вы бы все равно хотели сделать это вручную. End Explorer, скопируйте новую DLL (\x64\Release\ContextIcons.dll
в папке решения) на место, затем выполните regsvr32
танец.
Атрибуции
Большое спасибо авторам MSDN и создателю " Полного идиотского руководства по написанию расширений оболочки ", на которое я много ссылался.
панегирик
Для многих экземпляров Explorer, которые были убиты при создании этого расширения оболочки: вы умерли по великой причине, что некоторые люди в Интернете могут иметь значки рядом со своими словами.
У меня недостаточно репутации, чтобы оставить комментарий, но, похоже, эта информация содержится внутри 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