Come assegnare un'icona per le voci del menu contestuale predefinito di Windows "Copia / Taglia / Incolla / Elimina"?


12

In Windows 8 / 8.1 x64, desidero assegnare un'icona personalizzata per le voci del menu contestuale di Windows predefinite come Copia , Taglia , Incolla , Elimina , Annulla , Ripristina e Invia a elementi, che per impostazione predefinita ha qualsiasi icona:

inserisci qui la descrizione dell'immagine

Dove posso trovare il "riferimento" a quelle voci del menu contestuale nel registro e quindi aggiungere un valore di registro "icona" per loro?

O in altre parole, come assegnare un'icona a un menu di estensione della shell come SendTo shellex ?.

Ricerca


Come commentato da @ Sk8erPeter , sembra che:

"L'aggiunta del Iconvalore di stringa a gestori di menu di scelta rapida diversi non funziona come quando lo si aggiunge a un elemento personalizzato come ad es. HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY"


A quale icona ti riferisci? Hai uno screenshot?
Raystafarian,

@Raystafarian Ho aggiornato la domanda con un'immagine.
ElektroStudios,

1
@Raystafarian: la domanda è come aggiungere un'icona personalizzata alle voci del menu contestuale di base esistenti come "Taglia" , "Copia" , "Elimina" , "Rinomina" , ecc. BTW quando si aggiunge un nuovo elemento personalizzato al menu contestuale, esso è molto semplice, perché devi solo aggiungere il Iconvalore stringa in una chiave come HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(e il valore di Iconsarebbe come eg %SystemRoot%\System32\shell32.dll,-133o sg. else). MA l'aggiunta del Iconvalore di stringa a gestori di menu di scelta rapida diversi non funziona come quando si aggiunge a questi elementi personalizzati.
Sk8erPeter,

Ecco un altro screenshot per chiarire (la parte interessante è in bordi rossi): i.imgur.com/fmewg6L.png . A proposito, come puoi vedere, ho alcuni elementi personalizzati nel menu contestuale con icone personalizzate (come "Apri con Notepad ++" ) - questo è esattamente ciò che vorremmo ottenere con le voci del menu contestuale del sistema esistente!
Sk8erPeter,

1
@ Sk8erPeter Il mio miglior vantaggio al momento è la prospettiva di creare un gestore di menu di scelta rapida della shell che usi SetMenuItemInfoin risposta a QueryContextMenu.
Ben N,

Risposte:


10

Avviso di affiliazione: sono l'autore del software menzionato in questa risposta.

Prima di tutto, ti farò sapere che ho imparato C ++ e Win32 proprio per questa domanda .

Ho sviluppato un'estensione shell a 64 bit che viene registrata come gestore del menu di scelta rapida. Quando viene invocato, fruga tra le voci di menu esistenti, cercando voci interessanti. Se ne trova una, attacca un'icona (che deve essere stata caricata in precedenza). Al momento, cerca Copia , Taglia , Elimina , Incolla , Ripeti , Invia a e Annulla . Puoi aggiungere il tuo modificando il codice; la procedura per questo è descritta di seguito. (Mi dispiace, non sono abbastanza bravo in C ++ per renderlo configurabile.)

Uno screenshot di esso in azione, con le icone più brutte conosciute dall'uomo:

in azione

Puoi scaricare queste icone se vuoi davvero.

Configurandolo

Scaricalo (dal mio Dropbox). Avviso : questo file viene rilevato da uno scanner VirusTotal come una qualche forma di malware. Questo è comprensibile, dato il tipo di cose che deve fare per colpire le voci esistenti. Ti do la mia parola che non danneggia intenzionalmente il tuo computer. Se sei sospettoso e / o vuoi modificarlo ed estenderlo, vedi il codice su GitHub !

Creare una cartella nell'unità C: C:\shellicon. Creare file BMP con i seguenti titoli: copy, cut, delete, paste, redo, sendto, undo. (Spero che sia ovvio quale fa la cosa.) Queste immagini dovrebbero probabilmente essere 16 per 16 pixel (o comunque le tue impostazioni DPI rendono il margine del menu), ma ho avuto successo anche con quelle più grandi. Se vuoi che le icone appaiano trasparenti, dovrai semplicemente rendere il loro sfondo dello stesso colore del menu contestuale. (Questo trucco è utilizzato anche da Dropbox.) Ho creato le mie icone terribili con MS Paint; altri programmi possono o meno salvare in modo compatibile con LoadImageA. 16 per 16 a una profondità di colore di 24 bit a 96 pixel per pollice sembra essere l'insieme più affidabile di proprietà dell'immagine.

Metti la DLL da qualche parte accessibile a tutti gli utenti, quella cartella che hai appena creato è una buona scelta. Aprire un prompt di amministrazione nella cartella contenente la DLL ed eseguire regsvr32 ContextIcons.dll. Questo crea le informazioni di registrazione per i tipi di shell *, Drive, Directory, e Directory\Background. Se vuoi rimuovere l'estensione della shell, fallo regsvr32 /u ContextIcons.dll.

Codice pertinente

Fondamentalmente, l'estensione interroga semplicemente il testo di ogni voce del menu contestuale con GetMenuItemInfoe, se appropriato, regola l'icona con SetMenuItemInfo.

Visual Studio genera un sacco di codice misterioso magico per i progetti ATL, ma questo è il contenuto di IconInjector.cpp, che implementa il gestore del menu di scelta rapida:

// 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;
}

Si noti che gli HBITMAPs non vengono mai ripuliti, ma questo non importa troppo dato che le cose della DLL spariranno quando Explorer si spegne. Le icone prendono a malapena qualche ricordo comunque.

Se stai compilando per 32-bit, il primo parametro a GetCommandStringè solo a UINTanziché a UINT_PTR.

Se si vuole veramente icone trasparenti, dovrete creare una finestra con l'icona desiderata e quindi impostare mii.hBmpItemper HBMMENU_SYSTEMe mettere la maniglia per la finestra in mii.dwItemData, come descritto in fondo l'articolo di MSDN suMENUITEMINFO . Non sono riuscito a capire come creare finestre dalle estensioni della shell. LR_LOADTRANSPARENTsembra promettente come una bandiera di LoadImageA, ma ha le sue insidie ​​- in particolare, non funziona se non si utilizzano bitmap a 256 colori.

Se si verificano problemi con il caricamento delle immagini, provare a rimuovere il LR_DEFAULTSIZEflag dalle LoadImageAchiamate.

Qualcuno sufficientemente esperto in C ++ potrebbe probabilmente estrarre risorse da altre DLL e convertirle in HBITMAPs, ma quel qualcuno non sono io.

Modificandolo

L'ho scritto in Visual Studio, che credo sia il miglior editor per Windows C ++.

Caricare il file SLN in Visual Studio 2015 dopo aver installato gli strumenti C ++. In IconInjector.cpp, è possibile aggiungere HBITMAPvoci in alto e LoadImageAchiamate Initializeper aggiungere nuove icone. Nella parte inferiore della else ifsezione, utilizzare awcscmp chiamata per cercare una corrispondenza esatta o una wcsstrchiamata per cercare la presenza di una sottostringa. In entrambi i casi, &rappresenta la posizione della sottolineatura / dell'acceleratore quando si usa Maiusc + F10. Imposta la tua modalità su Release e la tua architettura su x64, quindi fai BuildBuild Solution . Verrà visualizzato un errore per non riuscire a registrare l'output, ma non preoccuparti; vorresti farlo manualmente comunque. End Explorer, copia la nuova DLL ( \x64\Release\ContextIcons.dllnella cartella della soluzione) nella posizione, quindi esegui la regsvr32danza.

Attribuzioni

Mille grazie agli scrittori MSDN e al creatore di " The Complete Idiot's Guide to Writing Extensions Shell ", a cui ho fatto riferimento pesantemente.

Elogio

Alle molte istanze di Explorer che sono state uccise nella produzione di questa estensione shell: sei morto per una grande causa, che alcune persone su Internet possono avere icone accanto alle loro parole.


Wow! Apprezzo molto i tuoi sforzi, grazie mille! (+1) Ho fatto del mio meglio ma non sono riuscito a far funzionare la versione compilata su Windows 10 (Build 10240). Non so quale sia il problema, tutte le immagini bmp esistono nel percorso giusto ( C:\shellicon\copy.bmp, ecc. - si tratta di icone 20x20 pixel in formato BMP) e ho registrato la DLL come amministratore nel prompt dei comandi con regsvr32 ContextIcons.dllcui è stata eseguita correttamente, ma Non vedo cambiamenti nel menu di scelta rapida. Ho anche riavviato il computer, non registrato e registrato nuovamente la dll, ma nessuna modifica. Sto cercando di compilare il sorgente in VS2015!
Sk8erPeter

@ Sk8erPeter MSDN ha detto che le icone devono essere 16x16, ma 20x20 funziona per me. Forse Windows 10 richiede 16x16? Si noti che è necessario riavviare Explorer per rendere effettive le modifiche.
Ben N

2
@ Sk8erPeter Certo, qui si va . Vedrò di mettere il codice su GitHub. Lavorando per scaricare Windows 10 ora ...
Ben N

2
Non ci crederai ... FUNZIONA con le tue immagini! : D: D Significa che ho alcuni file bmp che Windows non è in grado di gestire, non so perché (dopo lo controllerò anche io). Comunque, grazie mille, il tuo codice risolve davvero il problema! :)
Sk8erPeter

1
@BenN: OK, grazie! :) Sarebbe stato un po 'più conveniente. A proposito, nel frattempo mi sono reso conto che se apro le mie immagini precedentemente non funzionanti nel leggendario Paint, e faccio un "Salva come"> "Bitmap a 24 bit (.bmp; .dip)" (quindi salvalo in un file BMP di nuovo) e utilizzo questo nuovo file come immagine sorgente, FUNZIONA. Naturalmente, le dimensioni della bitmap devono essere esattamente 16x16 pixel. Quindi Paint crea il formato bitmap previsto, che è di 24 bit per pixel (16,7 milioni di colori), dimensioni 96x96 DPI e 16x16 pixel. In precedenza ho convertito e ridimensionato i file .png in IrfanView in file .bmp, queste icone non funzionavano.
Sk8erPeter

1

Non ho abbastanza rappresentante per lasciare un commento ma sembra che questa informazione sia contenuta in shell32.dll. I file sono stati compilati, quindi è difficile vedere quali funzioni ci sono, ma sembra essere quello.

Di interesse (esportazione del registro):

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

(Predefinito) REG_SZ Copia / Sposta / Rinomina / Elimina / Collega oggetto

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

LocalizedString REG_EXPAND_SZ @% SystemRoot% \ system32 \ shell32.dll, -50176

Sotto la chiave InProcServer32 fa riferimento a shell32.dll. Ce ne sono un paio anche con nomi che suonano rilevanti. Forse anche di interesse è windows.storage.dll


1
Informazione interessante. Tuttavia, sembra essere un commento piuttosto che una risposta. Ora hai abbastanza rappresentante per commentare ovunque :)
Ben N
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.