Comment attribuer une icône aux éléments du menu contextuel par défaut de «Copier / Couper / Coller / Supprimer» de Windows?

12

Sous Windows 8 / 8.1 x64, je voudrais attribuer une icône personnalisée pour les éléments du menu contextuel de Windows par défaut tels que Copier , Couper , Coller , Supprimer , Annuler , Rétablir et Envoyer vers , qui par défaut a n'importe quelle icône:

entrez la description de l'image ici

Où puis-je trouver la "référence" à ces éléments de menu contextuel dans le registre, puis ajouter une valeur de registre "icône" pour eux?

Ou en d'autres termes, comment attribuer une icône à un menu d'extension de shell comme le shellex SendTo?.

Recherche


Comme l'a commenté @ Sk8erPeter , il semble que:

"L'ajout de la Iconvaleur de chaîne à différents gestionnaires de menu contextuel ne fonctionne pas comme lors de l'ajout à un élément personnalisé comme par exemple HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY"

ElektroStudios
la source
De quelle icône parlez-vous? Avez-vous une capture d'écran?
Raystafarian
@Raystafarian J'ai mis à jour la question avec une image.
ElektroStudios
1
@Raystafarian: la question est de savoir comment ajouter une icône personnalisée aux éléments de menu contextuel de base existants comme "Couper" , "Copier" , "Supprimer" , "Renommer" , etc. BTW lors de l'ajout d'un nouvel élément personnalisé au menu contextuel, il est très facile, car vous n'avez qu'à ajouter la Iconvaleur de chaîne dans une clé comme HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(et la valeur de Iconserait comme par exemple %SystemRoot%\System32\shell32.dll,-133ou sg. else). MAIS l'ajout de la Iconvaleur de chaîne à différents gestionnaires de menu contextuel ne fonctionne pas comme lors de l'ajout à ces éléments personnalisés.
Sk8erPeter
Voici une autre capture d'écran pour le clarifier (la partie intéressante est dans les bordures rouges): i.imgur.com/fmewg6L.png . BTW comme vous pouvez le voir, j'ai quelques éléments personnalisés dans le menu contextuel avec des icônes personnalisées (comme "Ouvrir avec Notepad ++" ) - c'est exactement ce que nous aimerions réaliser avec les éléments de menu contextuel du système existants!
Sk8erPeter
1
@ Sk8erPeter Ma meilleure piste pour le moment est la perspective de créer un gestionnaire de menu contextuel shell qui utilise SetMenuItemInfoen réponse à QueryContextMenu.
Ben N

Réponses:

10

Avis d'affiliation: je suis l'auteur du logiciel mentionné dans cette réponse.

Tout d'abord, je vous ferai savoir que j'ai appris C ++ et Win32 juste pour cette question .

J'ai développé une extension shell 64 bits qui est enregistrée en tant que gestionnaire de menu contextuel. Lorsqu'il est invoqué, il fouille dans les éléments de menu existants, à la recherche d'entrées intéressantes. S'il en trouve un, il y colle une icône (qui doit avoir été chargée plus tôt). À l'heure actuelle, il recherche Copier , Couper , Supprimer , Coller , Rétablir , Envoyer vers et Annuler . Vous pouvez ajouter le vôtre en modifiant le code; la procédure à suivre est décrite ci-dessous. (Désolé, je ne suis pas assez bon en C ++ pour le rendre configurable.)

Une capture d'écran de celui-ci en action, avec les icônes les plus laides connues de l'homme:

en action

Vous pouvez télécharger ces icônes si vous le souhaitez.

Configuration

Téléchargez-le (depuis ma Dropbox). Remarque : ce fichier est détecté par un analyseur VirusTotal comme étant une forme de malware. Ceci est compréhensible, étant donné le genre de choses qu'il doit faire pour supprimer les entrées existantes. Je vous donne ma parole qu'il ne fait aucun mal intentionnel à votre ordinateur. Si vous êtes suspect et / ou que vous souhaitez le modifier et l'étendre, consultez le code sur GitHub !

Créez un dossier dans votre lecteur C: C:\shellicon. Créer des fichiers BMP avec les titres suivants: copy, cut, delete, paste, redo, sendto, undo. (J'espère qu'il est évident que l'on fait quelle chose.) Ces images devraient probablement être de 16 x 16 pixels (ou quelle que soit la taille de vos paramètres DPI, cela fait la marge du menu), mais j'ai également réussi avec des images plus grandes. Si vous voulez que les icônes soient transparentes, vous devrez simplement faire en sorte que leur arrière-plan soit de la même couleur que le menu contextuel. (Cette astuce est également utilisée par Dropbox.) J'ai créé mes terribles icônes avec MS Paint; d'autres programmes peuvent ou non enregistrer d'une manière compatible avec LoadImageA. 16 par 16 à une profondeur de couleur de 24 bits à 96 pixels par pouce semble être l'ensemble de propriétés d'image le plus fiable.

Mettez la DLL dans un endroit accessible à tous les utilisateurs, ce dossier que vous venez de créer est un bon choix. Ouvrez une invite d'administration dans le dossier contenant la DLL et faites regsvr32 ContextIcons.dll. Cela crée des informations d'inscription pour les types de coquillages *, Drive, Directoryet Directory\Background. Si vous souhaitez supprimer l'extension shell, faites-le regsvr32 /u ContextIcons.dll.

Code pertinent

Fondamentalement, l'extension interroge simplement le texte de chaque élément du menu contextuel avec GetMenuItemInfoet, le cas échéant, ajuste l'icône avec SetMenuItemInfo.

Visual Studio génère beaucoup de code mystérieux magique pour les projets ATL, mais voici le contenu de IconInjector.cpp, qui implémente le gestionnaire de menu contextuel:

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

Notez que les HBITMAPs ne sont jamais nettoyés, mais cela n'a pas trop d'importance étant donné que le contenu de la DLL disparaîtra lorsque l'Explorateur s'arrêtera. Les icônes prennent à peine de la mémoire de toute façon.

Si vous compilez pour 32 bits, le premier paramètre de GetCommandStringest juste un UINTau lieu d'un UINT_PTR.

Si vous voulez vraiment des icônes transparentes, vous devrez créer une fenêtre avec l'icône désirée, puis mis mii.hBmpItemà HBMMENU_SYSTEMet mettre la poignée à la fenêtre mii.dwItemData, comme décrit au bas de l'article MSDN surMENUITEMINFO . Je n'ai pas pu comprendre comment créer des fenêtres à partir d'extensions shell. LR_LOADTRANSPARENTsemble prometteur comme indicateur LoadImageA, mais il a ses propres pièges - en particulier, ne fonctionne pas à moins que vous n'utilisiez des bitmaps 256 couleurs.

Si vous rencontrez des problèmes avec le chargement de l'image, essayez de supprimer l' LR_DEFAULTSIZEindicateur des LoadImageAappels.

Quelqu'un suffisamment compétent en C ++ pourrait probablement extraire des ressources d'autres DLL et les convertir en HBITMAPs, mais ce n'est pas moi.

Le modifier

J'ai écrit cela dans Visual Studio, qui je pense être le meilleur éditeur pour Windows C ++.

Chargez le fichier SLN dans Visual Studio 2015 après avoir installé les outils C ++. Dans IconInjector.cpp, vous pouvez ajouter des HBITMAPentrées en haut et LoadImageAappeler Initializepour ajouter de nouvelles icônes. Dans la else ifsection, utilisez un wcscmpappel pour rechercher une correspondance exacte ou un wcsstrappel pour rechercher la présence d'une sous-chaîne. Dans les deux cas, le &représente la position du soulignement / accélérateur lors de l'utilisation de Maj + F10. Définissez votre mode sur Release et votre architecture sur x64, et faites BuildBuild Solution . Vous obtiendrez une erreur sur l'échec de l'enregistrement de la sortie, mais ne vous inquiétez pas; vous voudriez quand même le faire manuellement. End Explorer, copiez la nouvelle DLL ( \x64\Release\ContextIcons.dlldans le dossier de la solution) à l'endroit, puis faites la regsvr32danse.

Attributions

Un grand merci aux rédacteurs MSDN et au créateur du " Guide complet de l'idiot pour l'écriture d'extensions de shell ", auquel j'ai fait référence de manière intensive.

Éloge

Aux nombreuses instances d'Explorer qui ont été tuées lors de la production de cette extension de shell: vous êtes mort pour une bonne cause, que certaines personnes sur Internet peuvent avoir des icônes à côté de leurs mots.

Ben N
la source
Hou la la! J'apprécie vraiment vos efforts, merci beaucoup! (+1) J'ai fait de mon mieux mais je n'ai pas pu faire fonctionner la version compilée sur Windows 10 (Build 10240). Je ne sais pas quel est le problème, toutes les images bmp existent dans le bon chemin ( C:\shellicon\copy.bmp, etc. - ce sont des icônes de 20x20 pixels au format BMP) et j'ai enregistré la DLL en tant qu'administrateur dans l'invite de commande avec regsvr32 ContextIcons.dlllaquelle elle s'est déroulée avec succès, mais Je ne vois aucun changement dans le menu contextuel. J'ai même redémarré l'ordinateur, désenregistré et réenregistré à nouveau la DLL, mais aucun changement. J'essaie de compiler la source dans VS2015!
Sk8erPeter
@ Sk8erPeter MSDN a dit que les icônes doivent être 16x16, mais 20x20 fonctionne pour moi. Peut-être que Windows 10 nécessite 16x16? Notez que vous devez redémarrer l'Explorateur pour que les modifications prennent effet.
Ben N
2
@ Sk8erPeter Certes, ici vous allez . Je vais voir comment mettre le code sur GitHub. Travailler sur le téléchargement de Windows 10 maintenant ...
Ben N
2
Vous ne le croirez pas ... IL FONCTIONNE avec vos images! : D: D Cela signifie que j'ai des fichiers bmp que Windows ne peut pas gérer, je ne sais pas pourquoi (plus tard, je vérifierai cela aussi). Quoi qu'il en soit, merci beaucoup, votre code résout vraiment le problème! :)
Sk8erPeter
1
@BenN: OK, merci! :) Cela aurait été un peu plus pratique. BTW en attendant, j'ai réalisé que si j'ouvre mes images qui ne fonctionnaient pas auparavant dans le légendaire Paint, et que je fais un "Enregistrer sous"> "Bitmap 24 bits (.bmp; .dip)" (alors enregistrez-le dans un fichier BMP à nouveau), et j'utilise ce nouveau fichier comme une image source, cela fonctionne. Bien sûr, la taille du bitmap doit être exactement 16x16 pixels. Ainsi, Paint crée le format bitmap attendu qui est de 24 bits par pixel (16,7 millions de couleurs), 96 x 96 DPI et 16 x 16 pixels. Auparavant, j'ai converti et redimensionné des fichiers .png dans IrfanView en fichiers .bmp, ces icônes ne fonctionnaient pas.
Sk8erPeter
1

Je n'ai pas assez de représentant pour laisser un commentaire mais il semble que ces informations soient contenues dans shell32.dll. Les fichiers ont été compilés, il est donc difficile de voir quelles sont les fonctions, mais il semble que ce soit celle-là.

D'intérêt (exportation du registre):

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

(Par défaut) REG_SZ Copier / Déplacer / Renommer / Supprimer / Lier un objet

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

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

Sous la clé InProcServer32, il fait référence à shell32.dll. Il y en a aussi quelques autres avec des noms de sondage pertinents. Peut-être aussi intéressant est windows.storage.dll

nijave
la source
1
Une information intéressante. Cependant, cela semble être un commentaire plutôt qu'une réponse. Vous avez maintenant suffisamment de représentants pour commenter partout :)
Ben N