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

2657
ElektroStudios

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

enter image description here

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

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

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


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

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

12
На какую иконку вы ссылаетесь? У тебя есть скриншот? Raystafarian 8 лет назад 0
@Raystafarian Я обновил вопрос с изображением. ElektroStudios 8 лет назад 0
@Raystafarian: вопрос в том, как добавить пользовательский значок в существующие элементы основного контекстного меню, такие как _ "Вырезать" _, _ "Копировать" _, _ "Удалить" _, _ "Переименовать" _ и т. Д. Кстати, при добавлении * Новый пользовательский элемент * в контекстное меню, это очень просто, потому что вам нужно только добавить строковое значение Icon в ключ, такой как HKEY_CLASSES_ROOT \ * \ shell \ MYCUSTOMITEM` (и значение Icon будет равно например, `% SystemRoot% \ System32 \ shell32.dll, -133` или еще один). ** НО добавление строкового значения `Icon` в другое контекстное меню _handlers_ не работает ** как при добавлении его к этим пользовательским элементам. Sk8erPeter 8 лет назад 1
Вот еще один скриншот, чтобы прояснить его (интересная часть находится в красных границах): http://i.imgur.com/fmewg6L.png. Кстати, как вы можете видеть, у меня есть несколько пользовательских элементов в контекстном меню с пользовательскими значками (например, _ "Открыть с помощью Notepad ++" _) - это именно то, чего мы хотели бы достичь с помощью существующих элементов системного контекстного меню! Sk8erPeter 8 лет назад 0
Правильно, поэтому в настоящее время нет иконки для этих предметов, верно? Таким образом, мы не уверены, что у них есть этот конкретный атрибут для редактирования, возможно, его нужно создать? Raystafarian 8 лет назад 0
@Raystafarian: Что ты думаешь? Да, для этих элементов не установлено никаких значков, как мы уже объяснили, и я даже опубликовал снимок экрана (и ElektroStudios любезно вставил его в свой вопрос, чтобы прояснить его), и это именно то, что мы хотели бы изменить ... Sk8erPeter 8 лет назад 0
@ Sk8erPeter Суть в том, что для значка нет пустого пикселя или заполнителя. Я просто проверял это. Raystafarian 8 лет назад 0
@ Sk8erPeter На мой взгляд, лучшим вариантом на данный момент является создание обработчика контекстного меню оболочки, который использует [`SetMenuItemInfo`] (https://msdn.microsoft.com/en-us/library/windows/desktop/ms648001 (v = vs.85) .aspx) в ответ на [`QueryContextMenu`] (https://msdn.microsoft.com/en-us/library/windows/desktop/bb776097 (v = vs.85) .aspx). Ben N 8 лет назад 1
@BenN: если бы у вас было время, я был бы признателен за пример кода. :) Заранее спасибо. Sk8erPeter 8 лет назад 0
@ElektroStudios Здесь могут быть некоторые примеры того, на что ссылался Бен, например, для просмотра применимой логики [Примеры контекстного меню] (https://code.msdn.microsoft.com/windowsapps/Context-menu-sample-40840351). Здесь также есть некоторые ссылки на API-интерфейс Windows.UI.Popups и связанные с ним классы и т. Д. Это кажется немного экстремальным, просто добавить значок в один из параметров контекстного меню по умолчанию, но я попытался отследить с помощью Process Monitor и т. Д. копирование правой кнопкой мыши и просмотр путей к различным ключам реестра, стеков и т. д., и мне не особо повезло с тем небольшим количеством времени, которое я потратил на выполнение задачи. Pimp Juice IT 8 лет назад 0
@ElektroStudios Кроме того, для строковых значений ключей реестра и т. Д., Которые могут представлять интерес, и, возможно, также имя файла для того, что я видел при трассировке, но я не смог собрать кусочки, вот список тех, на случай, если вы найдете полезными любой. , , «Shell Copy Hook», «Disk Copy Extension», «Copy as Path Menu», «CTXMENU_NOVERBS`,« Shell DRM Copy Object », а также« diskcopy.dll ». , , У меня не хватило времени, чтобы сделать гораздо больше, но это мой удар. Я не был уверен, что если добавить ключ `\ Settings`, а затем применимые значения в папку реестра по умолчанию, то это то, что я и хотел проверить. Pimp Juice IT 8 лет назад 0
@ Sk8erPeter Я собираю кусочки, более или менее, во что-то, что работает для меня. Смотри мой ответ :) Ben N 8 лет назад 0
ElektroStudios: вы можете принять [ответ Бен Н] (http://superuser.com/a/1021873/62130), это действительно решает проблему! :) Спасибо, Бен, ты действительно заслуживаешь награды! И @LMFAO_A_JOKE, спасибо за ваши усилия тоже! Sk8erPeter 8 лет назад 0
@ Sk8erPeter и всем, кто может быть заинтересован: мой ответ теперь [проект GitHub] (https://github.com/Fleex255/ContextIcons). Я также обновил свой ответ ссылками и некоторыми дополнительными техническими деталями. Ben N 8 лет назад 0
@BenN: отлично, еще лучше, спасибо! :) Sk8erPeter 8 лет назад 0

2 ответа на вопрос

9
Ben N

Affiliation notice: I am the author of the software mentioned in this answer.

First up, I'll have you know that I learned C++ and Win32 just for this question.

I have developed a 64-bit shell extension that gets registered as a context menu handler. When it's invoked, it rummages through the existing menu items, looking for interesting entries. If it finds one, it sticks an icon on it (which must have been loaded earlier). At the moment, it looks for Copy, Cut, Delete, Paste, Redo, Send to, and Undo. You can add your own by modifying the code; the procedure for this is described below. (Sorry, I'm not good enough at C++ to make it configurable.)

A screenshot of it in action, with the ugliest icons known to man:

in action

You can download these icons if you really want to.

Setting it up

Download it (from my Dropbox). Notice: this file is detected by one VirusTotal scanner as being some form of malware. This is understandable, given the kind of things it has to do to whack the existing entries. I give you my word that it does no intentional harm to your computer. If you're suspicious and/or you want to modify and extend it, see the code on GitHub!

Create a folder in your C drive: C:\shellicon. Create BMP files with the following titles: copy, cut, delete, paste, redo, sendto, undo. (Hopefully it's obvious which one does which thing.) These images should probably be 16 by 16 pixels (or however big your DPI settings make the menu margin), but I've had success with larger ones as well. If you want the icons to look transparent, you'll have to just make their background the same color as the context menu. (This trick is employed by Dropbox as well.) I made my terrible icons with MS Paint; other programs may or may not save in a manner compatible with LoadImageA. 16 by 16 at 24-bit color depth at 96 pixels per inch seems to be the most reliable set of image properties.

Put the DLL somewhere accessible to all users, that folder you just made is a good choice. Open an admin prompt in the folder containing the DLL and do regsvr32 ContextIcons.dll. This creates registration information for the shell types *, Drive, Directory, and Directory\Background. If you ever want to remove the shell extension, do regsvr32 /u ContextIcons.dll.

Relevant code

Basically, the extension just queries every context menu item's text with GetMenuItemInfo and, if appropriate, adjusts the icon with SetMenuItemInfo.

Visual Studio generates a lot of magic mysterious code for ATL projects, but this is the contents of IconInjector.cpp, which implements the context menu handler:

// 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; } 

Note that the HBITMAPs are never cleaned up, but this doesn't matter too much given that the DLL's stuff will go away when Explorer shuts down. The icons barely take any memory anyway.

If you're compiling for 32-bit, the first parameter to GetCommandString is just a UINT instead of a UINT_PTR.

If you really want transparent icons, you'll have to create a window with the desired icon and then set mii.hBmpItem to HBMMENU_SYSTEM and put the handle to the window in mii.dwItemData, as described at the bottom of the MSDN article on MENUITEMINFO. I wasn't able to figure out how to create windows from shell extensions. LR_LOADTRANSPARENT looks promising as a flag of LoadImageA, but it has its own pitfalls - specifically, not working unless you use 256-color bitmaps.

If you experience problems with image loading, try removing the LR_DEFAULTSIZE flag from the LoadImageA calls.

Somebody sufficiently skilled in C++ could probably grab resources out of other DLLs and convert them to HBITMAPs, but that somebody is not me.

Modifying it

I wrote this in Visual Studio, which I believe to be the best editor for Windows C++.

Load up the SLN file into Visual Studio 2015 after you install the C++ tools. In IconInjector.cpp, you can add HBITMAP entries at the top and LoadImageA calls in Initialize to add new icons. Down in the else if section, use a wcscmp call to look for an exact match, or a wcsstr call to look for the presence of a substring. In both cases, the & represents the position of the underline/accelerator when using Shift+F10. Set your mode to Release and your architecture to x64, and do BuildBuild Solution. You'll get an error about failing to register the output, but don't worry; you'd want to do this manually anyway. End Explorer, copy the new DLL (\x64\Release\ContextIcons.dll in the solution folder) to the place, then do the regsvr32 dance.

Attributions

Many thanks to the MSDN writers, and to the creator of "The Complete Idiot's Guide to Writing Shell Extensions", which I referenced heavily.

Eulogy

To the many Explorer instances that were killed in the production of this shell extension: you died for a great cause, that some people on the Internet can have icons next to their words.

Вот Это Да! Я очень ценю ваши усилия, большое спасибо! (+1) Я старался изо всех сил, но не мог заставить скомпилированную версию работать на Windows 10 (сборка 10240). Я не знаю, в чем проблема, все изображения bmp существуют по правильному пути (`C: \ shellicon \ copy.bmp` и т. Д. - это значки размером 20x20 пикселей в формате BMP), и я зарегистрировал dll как admin в командной строке с `regsvr32 ContextIcons.dll`, который успешно запущен, но я не вижу изменений в контекстном меню. Я даже перезагружал компьютер, незарегистрирован и снова регистрировал dll, но без изменений. Я пытаюсь скомпилировать источник в VS2015! Sk8erPeter 8 лет назад 0
@ Sk8erPeter MSDN сказал, что иконки должны быть 16x16, но у меня работает 20x20. Может быть, Windows 10 требует 16x16? Обратите внимание, что вам нужно перезапустить Проводник, чтобы изменения вступили в силу. Ben N 8 лет назад 0
Я только что попробовал то же самое с изображениями BMP 16x16 (после отмены регистрации и повторной регистрации библиотеки DLL), и, к сожалению, изменений по-прежнему нет. Sk8erPeter 8 лет назад 0
@ Sk8erPeter У вас есть машина с Windows 8, чтобы попробовать ее? Я работаю над подготовкой компьютера с Windows 10 для отладки. Кроме того, убедитесь, что запись `IconInjector` появилась в` HKCR \ * \ shellex \ ContextMenuHandlers`. Наконец, возможно, проверьте ваши антивирусные журналы? Это может быть немного подозрительно к неподписанному характеру расширения оболочки. Наконец, возможно, попробуйте добавить строковую запись в `HKLM \ Software \ Microsoft \ Windows \ CurrentVersion \ Shell Extensions \ Approved` с именем` `. (Возможно, в Win10 включена политика, требующая регистрации там.) Ben N 8 лет назад 0
Да, конечно, я проверил новые записи реестра, и они существуют! Я также добавил новую строковую запись под ключом `Approved`, как вы упомянули. Все еще без изменений (незарегистрированные и повторно зарегистрированные после этого тоже). К сожалению, в настоящее время у меня нет компьютера с Windows 8, но я постараюсь получить его. Sk8erPeter 8 лет назад 0
Во всяком случае, я даю вам награду! :) Я не могу проверить все другие обстоятельства до полуночи, когда он истекает, и я считаю, что это действительно сработало для вас. В любом случае, если у вас есть шанс, я был бы признателен, если бы вы тоже могли протестировать его под Windows 10. :) Спасибо! Sk8erPeter 8 лет назад 0
Кстати, вы не могли бы загрузить свои файлы BMP в Dropbox тоже? :) Просто из любопытства я хотел бы протестировать его с теми изображениями, которые были видны в вашем контекстном меню (хотя я не думаю, что изображения являются источником проблемы, но кто знает ...). Было бы очень хорошо, если бы вы могли загрузить исходный код в GitHub / Bitbucket. :) Sk8erPeter 8 лет назад 0
@ Sk8erPeter Конечно, [здесь вы идете] (https://dl.dropboxusercontent.com/u/3771470/shellicon.zip). Я посмотрю о размещении кода на GitHub. Работаю над загрузкой Windows 10 сейчас ... Ben N 8 лет назад 2
Вы не поверите ... Это работает с вашими изображениями! : D: D Это означает, что у меня есть несколько bmp-файлов, которые Windows не может обработать, не знаю почему (позже я тоже это проверю). В любом случае, большое спасибо, ваш код действительно решает проблему! :) Sk8erPeter 8 лет назад 2
@BenN Довольно здесь! Великие усилия человек, хорошая работа !! +1 от меня Pimp Juice IT 8 лет назад 0
Ответ @Ben NA на языке программирования не является ожидаемым решением, в любом случае я парень .Net, поэтому я могу перевести неуправляемый код в управляемый код в Vb.Net, поэтому я более чем благодарен за ваш ответ, СПАСИБО, также возможно, отслеживание изменений реестра при отладке примененного поля ** hbmpItem **, возможно, в этот момент мы могли бы найти простой способ сделать это с помощью изменений реестра, чтобы избежать запуска приложения в фоновом режиме, я до сих пор не исследовал, что происходит в реестра в этот момент, но если кто-то хочет, я рекомендую программу ** X-RegShot v2.0 **. ElektroStudios 8 лет назад 0
@BenN: разве вы не знаете, может ли использоваться даже значок ресурса (например, значки из `shell32.dll` или exe) или нет в этом случае? Я нашел только способы загрузки BMP (`HBITMAP`s), потому что это то, что нужно для` MENUITEMINFO` (`hbmpItem`), и он не принимает экземпляры` HICON`. Есть ли другой способ сделать это, или мы должны принять тот факт, что он работает только с BMP? Например, если я хочу отобразить значок рядом с пунктом меню «Открыть командное окно здесь», мне нужно только отредактировать `HKEY_CLASSES_ROOT \ Directory \ Background \ shell \ cmd`, поместить здесь строковое значение с именем` Icon` со значением `cmd.exe, 0`. Sk8erPeter 8 лет назад 0
@ Sk8erPeter Я считаю, что загрузка ресурсов DLL работает, потому что Explorer делает пункт меню нарисованным владельцем. К сожалению, я не вижу способа олицетворения окна * Explorer * из расширения оболочки (рисование владельца инициируется сообщением окна). Похоже, что поле `hBmpItem` принимает только HBITMAP. Ben N 8 лет назад 0
@ElektroStudios Инъекция пиктограмм этой программы не вносит изменений в реестр, прямо или косвенно, все это во время выполнения; там, кажется, нет никакого параметра реестра, который управляет значком для этих записей. Поскольку я предоставил как исходный код, так и скомпилированный, я думаю, что перевод на другой язык / среду выполнения не имеет особой цели. Кроме того, [обычно нежелательно] (https://blogs.msdn.microsoft.com/oldnewthing/20130222-01/?p=5163) писать расширения оболочки в .NET. Наконец, DLL не столько работает в фоновом режиме, сколько становится частью Explorer - практически не снижается производительность. Ben N 8 лет назад 0
@BenN: ОК, спасибо! :) Было бы немного удобнее. Кстати, я понял, что если я открою свои ранее не работающие изображения в _legendary_ Paint и сделаю «Сохранить как»> «24-битное растровое изображение (.bmp; .dip)» (так что сохраните его в файл BMP снова), и я использую этот новый файл в качестве исходного изображения, он работает. Конечно, размер растрового изображения должен быть ровно 16x16 пикселей. Таким образом, Paint создает ожидаемый растровый формат, который составляет 24 бита на пиксель (16,7 млн. Цветов), 96x96 точек на дюйм и 16x16 пикселей. Ранее я преобразовывал и изменял размеры файлов .png в IrfanView в файлы .bmp, эти значки не работали. Sk8erPeter 8 лет назад 1
Возможно, вы могли бы упомянуть об этом в посте, чтобы избежать связанных с этим проблем и вопросов, и это может быть важной информацией о том, какие изображения принимаются. :) Sk8erPeter 8 лет назад 0
1
nijave

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

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

HKEY_CLASSES_ROOT \ CLSID

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

AppID REG_SZ

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

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

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

Похожие вопросы