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

Ele*_*ios 14 shell-extensions context-menu windows-8

在 Windows 8/8.1 x64 下,我想为默认的 Windows 上下文菜单项分配一个自定义图标,例如CopyCutPasteDeleteUndoRedoSend To项目,默认情况下有任何图标:

在此处输入图片说明

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

或者换句话说,如何将图标分配给像SendTo shellex这样的外壳扩展菜单?。

研究


正如@ Sk8erPeter所评论的,似乎:

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

Ben*_*n N 11

隶属关系通知:我是此答案中提到的软件的作者。

首先,我会让你知道我学习 C++ 和 Win32就是为了这个问题

我开发了一个 64 位 shell 扩展,它被注册为上下文菜单处理程序。当它被调用时,它会翻遍现有的菜单项,寻找有趣的条目。如果它找到一个,它会在上面贴一个图标(一定是之前加载过的)。目前,它查找CopyCutDeletePasteRedoSend toUndo。可以通过修改代码添加自己的;其程序如下所述。(抱歉,我在 C++ 方面不够好,无法使其可配置。)

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

在行动

如果您愿意,可以下载这些图标

设置它

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

在 C 盘创建一个文件夹:C:\shellicon. 创建具有以下标题的 BMP 文件:copycutdeletepasteredosendtoundo。(希望哪一个做哪件事很明显。)这些图像应该是 16 x 16 像素(或者你的 DPI 设置使菜单边距多大),但我也成功地处理了更大的图像。如果您希望图标看起来透明,您只需将它们的背景设置为与上下文菜单相同的颜色。(Dropbox 也采用了这个技巧。)我用 MS Paint 制作了我糟糕的图标;其他程序可能会也可能不会以与LoadImageA. 16 x 16、24 位色深、每英寸 96 像素似乎是最可靠的一组图像属性。

将 DLL 放在所有用户都可以访问的地方,您刚刚创建的文件夹是一个不错的选择。在包含 DLL 的文件夹中打开管理提示,然后执行regsvr32 ContextIcons.dll. 这对shell类型创建注册信息*DriveDirectory,和Directory\Background。如果您想删除 shell 扩展名,请执行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;
}
Run Code Online (Sandbox Code Playgroud)

请注意,HBITMAPs 永远不会被清除,但这并不重要,因为当资源管理器关闭时 DLL 的内容将消失。无论如何,这些图标几乎不占用任何内存。

如果您正在为 32 位编译,则 to 的第一个参数GetCommandString只是 aUINT而不是 a UINT_PTR

如果您确实需要透明图标,则必须创建一个带有所需图标的窗口,然后设置mii.hBmpItemHBMMENU_SYSTEM并将窗口句柄置于 中mii.dwItemData,如MSDN 文章MENUITEMINFO底部所述。我无法弄清楚如何从外壳扩展创建窗口。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,然后执行Build ? 构建解决方案。你会收到关于未能注册输出的错误,但别担心;无论如何,您都想手动执行此操作。结束资源管理器,将新的 DLL(\x64\Release\ContextIcons.dll在解决方案文件夹中)复制到该位置,然后开始regsvr32跳舞。

归因

非常感谢 MSDN 的作者,以及我大量引用的“ The Complete Idiot's Guide to Writing Shell Extensions ”的创建者。

对于在此 shell 扩展程序的生产过程中被杀死的许多 Explorer 实例:您为一个伟大的事业而死,因为 Internet 上的某些人可以在他们的话旁边有图标。

  • @Sk8erPeter 当然,[给你](https://dl.dropboxusercontent.com/u/3771470/shellicon.zip)。我会看看如何将代码放在 GitHub 上。现在正在下载 Windows 10... (2认同)
  • 您不会相信...它适用于您的图像!:D :D 这意味着我有一些 Windows 无法处理的 bmp 文件,不知道为什么(稍后我也会检查)。无论如何,非常感谢您,您的代码确实解决了问题!:) (2认同)