会员通知:我是此答案中提到的软件的作者。
首先,我会让您知道我只是为了这个问题而学习了C ++和Win32 。
我已经开发了一个64位的shell扩展,该扩展被注册为上下文菜单处理程序。调用它时,它会遍历现有菜单项,寻找有趣的条目。如果找到一个,它将在其上粘贴一个图标(必须早先加载)。目前,它会查找“ 复制”,“ 剪切”,“ 删除”,“ 粘贴”,“ 重做”,“ 发送到 ”和“ 撤消”。您可以通过修改代码来添加自己的代码。程序说明如下。(对不起,我对C ++的了解不足,无法对其进行配置。)
实际运行时的屏幕截图,其中包含人类已知的最丑陋的图标:
如果您确实愿意,可以下载这些图标。
设定
从我的Dropbox 下载。注意:一台VirusTotal扫描仪将该文件检测为某种形式的恶意软件。鉴于必须对现有条目进行修改,这是可以理解的。我对您说,它不会对您的计算机造成故意伤害。如果您怀疑和/或想要修改和扩展它,请参见GitHub上的代码!
在C驱动器中创建一个文件夹:C:\shellicon
。具有下列标题创建BMP文件:copy
,cut
,delete
,paste
,redo
,sendto
,undo
。(希望很明显,哪个做某事。)这些图像可能应该是16 x 16像素(或者DPI设置太大会增加菜单边距),但是我也成功使用了较大的图像。如果希望图标看起来透明,则只需使其背景与上下文菜单具有相同的颜色。(Dropbox也使用了这个技巧。)我使用MS Paint制作了可怕的图标;其他程序可能或可能不与兼容地保存LoadImageA
。每英寸96像素的24位色深下的16 x 16似乎是最可靠的图像属性集。
将DLL放置在所有用户均可访问的位置,您刚刚创建的文件夹是一个不错的选择。在包含DLL的文件夹中打开一个管理提示,然后执行do regsvr32 ContextIcons.dll
。这对shell类型创建注册信息*
,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
永远不会清除s,但这并没有太大关系,因为当Explorer关闭时,DLL的内容将消失。这些图标几乎不会占用任何内存。
如果您要针对32位进行编译,则第一个参数to GetCommandString
只是a UINT
而不是a 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,然后执行Build → Build Solution。您将收到有关未能注册输出的错误,但请不要担心;您还是想手动执行此操作。结束资源管理器,将新的DLL(\x64\Release\ContextIcons.dll
在solution文件夹中)复制到该位置,然后进行regsvr32
跳舞。
归因
非常感谢MSDN的编写者,以及我引用过多的 “ 完整的傻瓜编写Shell扩展指南 ”的创建者。
颂
对于在此shell扩展的生产中被杀死的许多Explorer实例:您之所以死是因为很大的原因,Internet上的某些人可以在其字词旁边带有图标。