題 如何為“複製/剪切/粘貼/刪除”Windows默認上下文菜單項分配圖標?


在Windows 8 / 8.1 x64下,我想為默認的Windows上下文菜單項分配自定義圖標,例如 複製刪除解開重做 和 發送至 項目,默認情況下有任何圖標:

enter image description here

在哪裡可以找到註冊表中這些上下文菜單項的“引用”,然後為它們添加“圖標”註冊表值?

或者換句話說,如何將圖標分配給shell擴展菜單,如 發送至 的shellex?

研究


由@評論Sk8erPeter,似乎:

“添加 Icon 字符串值到不同的上下文菜單處理程序   將其添加到自定義項目(例如自定義項目)時不起作用    HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY


12
2017-09-23 13:53


起源


你指的是什麼圖標?你有截圖嗎? - Raystafarian
@Raystafarian我用圖片更新了問題。 - ElektroStudios
@Raystafarian:問題是如何將自定義圖標添加到現有的基本上下文菜單項中 “切”, “複製”, “刪除”, “改名”等。添加時BTW 新的自定義項目 到上下文菜單,這很容易,因為你只需要添加 Icon String中的字符串值 HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM (和的價值 Icon 就像是 %SystemRoot%\System32\shell32.dll,-133 或者sg。其他)。 但是添加了 Icon 字符串值到不同的上下文菜單 處理器 不起作用 比如將它添加到這些自定義項目時。 - Sk8erPeter
這是另一個明確的截圖(有趣的部分是紅色邊框): i.imgur.com/fmewg6L.png。 BTW,你可以看到,我在上下文菜單中有一些自定義項目與自定義圖標(如 “用Notepad ++打開”) - 這正是我們希望用現有的系統上下文菜單項實現的! - Sk8erPeter
@ Sk8erPeter目前我最好的領導是創建一個使用的shell上下文菜單處理程序的前景 SetMenuItemInfo 回應 QueryContextMenu。 - Ben N


答案:


附屬聲明:我是本答復中提到的軟件的作者。

首先,我會告訴你我學習了C ++和Win32 只是為了這個問題

我開發了一個64位的shell擴展,它被註冊為上下文菜單處理程序。當它被調用時,它會通過現有的菜單項進行搜索,尋找有趣的條目。如果找到一個,它會在上面貼一個圖標(必須先加載)。目前,它尋找 複製刪除重做發送至,和 解開。您可以通過修改代碼來添加自己的代碼;對此的程序如下所述。 (對不起,我在C ++上做得不夠好以使其可配置。)

它的實際屏幕截圖,以及人類已知的最醜陋的圖標:

in action

您可以 下載這些圖標 如果你真的想。

設置它

下載它 (來自我的Dropbox)。 注意:這個文件是 由一個VirusTotal掃描程序檢測到 作為某種形式的惡意軟件。考慮到打擊現有條目必須採取的措施,這是可以理解的。我告訴你,它不會故意傷害你的電腦。如果您懷疑和/或想要修改和擴展它,請參閱代碼 在GitHub上

在C盤中創建一個文件夾: C:\shellicon。使用以下標題創建BMP文件: copycutdeletepasteredosendtoundo。 (希望顯而易見的是哪一個做了哪些事情。)這些圖像可能應該是16 x 16像素(或者你的DPI設置有多大使得菜單餘量),但我也有成功的大圖片。如果您希望圖標看起來透明,則必須使其背景與上下文菜單的顏色相同。 (Dropbox也採用了這個技巧。)我用MS Paint製作了可怕的圖標;其他程序可能會或可能不會以兼容的方式保存 LoadImageA16位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;
}

請注意 HBITMAP從來沒有清理過,但這並不重要,因為當資源管理器關閉時,DLL的內容會消失。無論如何,這些圖標幾乎沒有任何記憶。

如果您正在編譯32位,則第一個參數為 GetCommandString 只是一個 UINT 代替 UINT_PTR

如果您真的想要透明圖標,則必須創建一個帶有所需圖標的窗口,然後進行設置 mii.hBmpItem 至 HBMMENU_SYSTEM 並將把手放在窗口中 mii.dwItemData,如底部所述 關於MSDN的文章 MENUITEMINFO。我無法弄清楚如何從shell擴展創建窗口。 LR_LOADTRANSPARENT 看起來很有希望作為一面旗幟 LoadImageA,但它有自己的陷阱 - 特別是,除非你使用256色位圖,否則無法工作。

如果您遇到圖像加載問題,請嘗試刪除 LR_DEFAULTSIZE 來自的旗幟 LoadImageA 調用。

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擴展的生產中被殺死的許多資源管理器實例:你死於一個偉大的事業,互聯網上的一些人可以在他們的單詞旁邊有圖標。


9
2018-01-05 01:54



哇!非常感謝您的努力,非常感謝! (+1)我盡我所能,但無法使編譯版本在Windows 10(Build 10240)上運行。我不知道問題是什麼,所有bmp圖像都存在於正確的路徑中(C:\shellicon\copy.bmp等等 - 這些是BMP格式的20x20像素圖標)我在命令提示符下註冊了dll作為管理員 regsvr32 ContextIcons.dll 哪個成功運行,但我在上下文菜單中看不到任何變化。我甚至重新啟動了計算機,取消註冊並重新註冊了dll,但沒有任何變化。我正在嘗試在VS2015中編譯源代碼! - Sk8erPeter
@ Sk8erPeter MSDN表示圖標需要16x16,但20x20適合我。也許Windows 10需要16x16?請注意,您必須重新啟動資源管理器才能使更改生效。 - Ben N
@ Sk8erPeter當然, 幹得好。我將看到將代碼放在GitHub上。現在正在下載Windows 10 ... - Ben N
你不會相信它...它適用於你的圖像! :D:D這意味著我有一些Windows無法處理的bmp文件,不知道為什麼(以後我也會檢查)。無論如何,非常感謝,你的代碼真的解決了這個問題! :) - Sk8erPeter
@BenN:好的,謝謝! :)這會更方便一點。順便說一句,在此期間我意識到,如果我打開我之前沒有工作的圖像 傳奇的 畫,我做一個“另存為”>“24位位圖(.bmp; .dip)”(所以再次將它保存到BMP文件),我使用這個新文件作為源圖像,它工作。當然,位圖的大小必須精確為16x16像素。因此,Paint創建了預期的位圖格式,即每像素24位(1670萬色),96x96 DPI和16x16像素大小。以前我將IrfanView中的.png文件轉換並重新調整為.bmp文件,這些圖標不起作用。 - Sk8erPeter


我沒有足夠的代表留下評論,但似乎這個信息包含在shell32.dll中。這些文件已被編譯,因此很難看到它中包含哪些函數,但它似乎就是那個。

感興趣的(註冊表導出):

HKEY_CLASSES_ROOT \ CLSID {} 3ad05575-8857-4850-9277-11b85bdb8e09

(默認)  REG_SZ    複製/移動/重命名/刪除/鏈接對象

的AppID  REG_SZ    {} 3ad05575-8857-4850-9277-11b85bdb8e09

LocalizedString  REG_EXPAND_SZ    @%SYSTEMROOT%\ SYSTEM32 \ SHELL32.DLL,-50176

在InProcServer32項下,它引用shell32.dll。還有一些其他的以及相關的聲音名稱。也許有趣的是windows.storage.dll


1
2018-01-04 06:42



有趣的信息。然而,它似乎是一個評論而不是一個答案。你現在有足夠的代表到處評論:) - Ben N