Уведомление о присоединении: я являюсь автором программного обеспечения, упомянутого в этом ответе.
Прежде всего, я хочу, чтобы вы знали, что я изучил 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 и выполните Build → Build Solution. Вы получите сообщение об ошибке при регистрации выхода, но не волнуйтесь; Вы бы все равно хотели сделать это вручную. Завершите работу проводника, скопируйте новую DLL-библиотеку (\x64\Release\ContextIcons.dll
в папке решения) на место, затем выполните танец regsvr32
.
Атрибуции
Большое спасибо авторам MSDN и создателю « Полного идиотского руководства по написанию расширений оболочки », на которое я много ссылался.
панегирик
Для многих экземпляров Explorer, которые были убиты при создании этого расширения оболочки: вы умерли по великой причине, что некоторые люди в Интернете могут иметь значки рядом со своими словами.