答案1
隶属声明:我是本回答中提到的软件的作者。
首先,我要告诉你,我学过 C++ 和 Win32只是为了这个问题。
我开发了一个 64 位 shell 扩展,它被注册为上下文菜单处理程序。当它被调用时,它会在现有菜单项中搜索,寻找有趣的条目。如果它找到一个,它会将一个图标粘贴在上面(必须先加载)。目前,它会寻找复制,切,删除,粘贴,重做,发给, 和撤消。您可以通过修改代码来添加自己的代码;此过程如下所述。(抱歉,我的 C++ 水平不够好,无法使其可配置。)
运行中的截图,其中有人类已知的最丑陋的图标:
你可以下载这些图标如果你真的想要的话。
设置
下载它(来自我的 Dropbox)。注意:此文件是被一个 VirusTotal 扫描程序检测到被认为是某种形式的恶意软件。考虑到它必须做的事情来破坏现有条目,这是可以理解的。我向你保证,它不会故意损害你的电脑。如果你有疑虑和/或想要修改和扩展它,请参阅代码在 GitHub 上!
在 C 盘中创建一个文件夹:C:\shellicon
。创建 BMP 文件,其标题如下:copy
、、、、、、、。 (希望您能一目了然地看出哪个文件的作用。)这些图像可能应该是cut
16 x 16 像素(或者您的 DPI 设置使菜单边距变大),但我也可以成功使用更大的图像。如果您希望图标看起来透明,则只需将其背景设置为与上下文菜单相同的颜色即可。(Dropbox 也采用了此技巧。)我用 MS Paint 制作了这些糟糕的图标;其他程序可能会或可能不会以兼容的方式保存delete
paste
redo
sendto
undo
LoadImageA
。16 x 16,24 位色深,96 像素/英寸似乎是最可靠的一组图像属性。
将 DLL 放在所有用户都可以访问的地方,您刚刚创建的文件夹就是一个不错的选择。在包含 DLL 的文件夹中打开管理员提示符并执行regsvr32 ContextIcons.dll
。这将为 shell 类型*
、Drive
、Directory
和创建注册信息Directory\Background
。如果您想删除 shell 扩展,请执行regsvr32 /u ContextIcons.dll
。
相关代码
基本上,扩展只是查询每个上下文菜单项的文本GetMenuItemInfo
并根据需要调整图标SetMenuItemInfo
。
Visual Studio 生成很多IconInjector.cpp
对于 ATL 项目来说,这是一个神奇而神秘的代码,但这就是实现上下文菜单处理程序的内容:
// 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
。我无法弄清楚如何从 shell 扩展创建窗口。LR_LOADTRANSPARENT
作为 的标志看起来很有希望LoadImageA
,但它也有自己的缺陷 - 具体来说,除非您使用 256 色位图,否则无法工作。
如果您在图像加载时遇到问题,请尝试LR_DEFAULTSIZE
从LoadImageA
调用中删除标志。
一些具有足够 C++ 技能的人可能可以从其他 DLL 中获取资源并将其转换为HBITMAP
s,但这个人不是我。
修改它
我使用 Visual Studio 编写了此程序,我认为它是 Windows C++ 的最佳编辑器。
安装 C++ 工具后,将 SLN 文件加载到 Visual Studio 2015 中。在 中IconInjector.cpp
,您可以HBITMAP
在顶部添加条目,并LoadImageA
调用Initialize
来添加新图标。在else if
部分中,使用wcscmp
调用来查找精确匹配,或wcsstr
使用调用来查找子字符串的存在。在这两种情况下, 表示&
使用 Shift+F10 时下划线/加速器的位置。将模式设置为 Release,将体系结构设置为 x64,然后执行建造→构建解决方案。您将收到有关无法注册输出的错误,但不要担心;无论如何您都希望手动执行此操作。结束资源管理器,将新的 DLL(\x64\Release\ContextIcons.dll
在解决方案文件夹中)复制到该位置,然后执行regsvr32
操作。
归因
非常感谢 MSDN 的作者,以及“编写 Shell 扩展的完整指南“,我大量引用过它。
颂
对于在生产此 shell 扩展时被终止的许多 Explorer 实例:你们为了一个伟大的事业而牺牲,以便互联网上某些人的话语旁边可以出现图标。
答案2
我没有足够的声誉来发表评论,但似乎此信息包含在 shell32.dll 中。文件已编译,因此很难看出其中有哪些功能,但它似乎就是其中之一。
感兴趣的(注册表导出):
HKEY_CLASSES_ROOT\CLSID{3ad05575-8857-4850-9277-11b85bdb8e09}
(默认) REG_SZ 复制/移动/重命名/删除/链接对象
应用程序ID REG_SZ {3ad05575-8857-4850-9277-11b85bdb8e09}
本地化字符串 REG_EXPAND_SZ @%SystemRoot%\ system32 \ shell32.dll,-50176
在 InProcServer32 键下,它引用了 shell32.dll。还有其他几个具有相关名称的键。可能还感兴趣的是 windows.storage.dll