如何为“复制/剪切/粘贴/删除”Windows 默认上下文菜单项分配图标?

如何为“复制/剪切/粘贴/删除”Windows 默认上下文菜单项分配图标?

在 Windows 8/8.1 x64 下,我想为默认的 Windows 上下文菜单项分配一个自定义图标,例如复制粘贴删除撤消重做发给项目,默认情况下具有任意图标:

在此处输入图片描述

我可以在哪里找到注册表中这些上下文菜单项的“引用”,然后为它们添加“图标”注册表值?

或者换句话说,如何将图标分配给 shell 扩展菜单,例如发给壳牌?。

研究


正如@评论的那样Sk8erPeter,似乎:

“将Icon字符串值添加到不同的上下文菜单处理程序时,其工作方式与将其添加到自定义项目时不同,例如 HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY

答案1

隶属声明:我是本回答中提到的软件的作者。

首先,我要告诉你,我学过 C++ 和 Win32只是为了这个问题

我开发了一个 64 位 shell 扩展,它被注册为上下文菜单处理程序。当它被调用时,它会在现有菜单项中搜索,寻找有趣的条目。如果它找到一个,它会将一个图标粘贴在上面(必须先加载)。目前,它会寻找复制删除粘贴重做发给, 和撤消。您可以通过修改代码来添加自己的代码;此过程如下所述。(抱歉,我的 C++ 水平不够好,无法使其可配置。)

运行中的截图,其中有人类已知的最丑陋的图标:

实际行动

你可以下载这些图标如果你真的想要的话。

设置

下载它(来自我的 Dropbox)。注意:此文件是被一个 VirusTotal 扫描程序检测到被认为是某种形式的恶意软件。考虑到它必须做的事情来破坏现有条目,这是可以理解的。我向你保证,它不会故意损害你的电脑。如果你有疑虑和/或想要修改和扩展它,请参阅代码在 GitHub 上

在 C 盘中创建一个文件夹:C:\shellicon。创建 BMP 文件,其标题如下:copy、、、、、、、。 (希望您能一目了然地看出哪个文件的作用。)这些图像可能应该是cut16 x 16 像素(或者您的 DPI 设置使菜单边距变大),但我也可以成功使用更大的图像。如果您希望图标看起来透明,则只需将其背景设置为与上下文菜单相同的颜色即可。(Dropbox 也采用了此技巧。)我用 MS Paint 制作了这些糟糕的图标;其他程序可能会或可能不会以兼容的方式保存deletepasteredosendtoundoLoadImageA16 x 16,24 位色深,96 像素/英寸似乎是最可靠的一组图像属性。

将 DLL 放在所有用户都可以访问的地方,您刚刚创建的文件夹就是一个不错的选择。在包含 DLL 的文件夹中打开管理员提示符并执行regsvr32 ContextIcons.dll。这将为 shell 类型*DriveDirectory和创建注册信息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.hBmpItemHBMMENU_SYSTEM并将窗口句柄放入mii.dwItemData,如底部所述MSDN 文章MENUITEMINFO。我无法弄清楚如何从 shell 扩展创建窗口。LR_LOADTRANSPARENT作为 的标志看起来很有希望LoadImageA,但它也有自己的缺陷 - 具体来说,除非您使用 256 色位图,否则无法工作。

如果您在图像加载时遇到问题,请尝试LR_DEFAULTSIZELoadImageA调用中删除标志。

一些具有足够 C++ 技能的人可能可以从其他 DLL 中获取资源并将其转换为HBITMAPs,但这个人不是我。

修改它

我使用 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

相关内容