Wie kann ich ein Symbol für Windows-Standardkontextmenüelemente "Kopieren / Ausschneiden / Einfügen / Löschen" zuweisen?

12

Unter Windows 8 / 8.1 x64 möchte ich ein benutzerdefiniertes Symbol für die Standardelemente des Windows-Kontextmenüs wie " Kopieren" , " Ausschneiden" , " Einfügen" , " Löschen" , " Rückgängig" , " Wiederherstellen" und " Senden an" zuweisen , die standardmäßig ein beliebiges Symbol aufweisen:

Bildbeschreibung hier eingeben

Wo finde ich den "Verweis" auf diese Kontextmenüelemente in der Registrierung und füge einen "Symbol" -Registrierungswert für sie hinzu?

Oder mit anderen Worten, wie Sie einem Shell-Erweiterungsmenü wie dem SendTo shellex ein Symbol zuweisen ?.

Forschung


Wie von @ Sk8erPeter kommentiert , scheint das:

"Das Hinzufügen des IconZeichenfolgenwerts zu verschiedenen Kontextmenü-Handlern funktioniert nicht wie beim Hinzufügen zu einem benutzerdefinierten Element wie z. B. HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY"

ElektroStudios
quelle
Auf welches Symbol beziehen Sie sich? Hast du einen Screenshot?
Raystafarian
@ Raystafarian Ich habe die Frage mit einem Bild aktualisiert.
ElektroStudios
1
@Raystafarian: Die Frage ist, wie man ein benutzerdefiniertes Symbol zu vorhandenen grundlegenden Kontextmenüelementen wie "Ausschneiden" , "Kopieren" , "Löschen" , "Umbenennen" usw. hinzufügt. Übrigens, wenn man dem Kontextmenü ein neues benutzerdefiniertes Element hinzufügt ist sehr einfach, da Sie nur den IconString-Wert in einem Schlüssel wie hinzufügen müssen HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(und der Wert des Iconwäre wie zB %SystemRoot%\System32\shell32.dll,-133oder sg. else). ABER das Hinzufügen des IconZeichenfolgenwerts zu verschiedenen Kontextmenü- Handlern funktioniert nicht wie beim Hinzufügen zu diesen benutzerdefinierten Elementen.
Sk8erPeter
Hier ist ein weiterer Screenshot, der dies verdeutlicht (der interessante Teil ist rot umrandet ): i.imgur.com/fmewg6L.png . Übrigens, Sie sehen, ich habe einige benutzerdefinierte Elemente im Kontextmenü mit benutzerdefinierten Symbolen (wie "Öffnen mit Notepad ++" ) - genau das möchten wir mit den vorhandenen Systemkontextmenüelementen erreichen!
Sk8erPeter
1
@ Sk8erPeter Mein derzeit bester Anhaltspunkt ist die Aussicht, einen Shell-Kontextmenü-Handler zu erstellen, der als SetMenuItemInfoReaktion darauf verwendet wird QueryContextMenu.
Ben N

Antworten:

10

Hinweis zur Zugehörigkeit: Ich bin der Autor der in dieser Antwort genannten Software.

Zunächst möchte ich Ihnen mitteilen, dass ich C ++ und Win32 nur für diese Frage gelernt habe .

Ich habe eine 64-Bit-Shell-Erweiterung entwickelt, die als Kontextmenü-Handler registriert wird. Wenn es aufgerufen wird, durchsucht es die vorhandenen Menüelemente nach interessanten Einträgen. Wenn es eines findet, klebt es ein Symbol darauf (das zuvor geladen worden sein muss). Im Moment sucht es nach Kopieren , Ausschneiden , Löschen , Einfügen , Wiederholen , Senden an und Rückgängig . Sie können Ihren eigenen Code hinzufügen, indem Sie den Code ändern. Die Vorgehensweise hierfür wird im Folgenden beschrieben. (Sorry, ich bin nicht gut genug in C ++, um es konfigurierbar zu machen.)

Ein Screenshot davon in Aktion mit den hässlichsten Symbolen, die der Mensch kennt:

in Aktion

Sie können diese Symbole herunterladen, wenn Sie wirklich möchten.

Einrichten

Laden Sie es herunter (von meiner Dropbox). Hinweis : Diese Datei wird von einem VirusTotal-Scanner als Malware erkannt . Dies ist verständlich, wenn man bedenkt, wie viel man tun muss, um die vorhandenen Einträge zu schlagen. Ich versichere Ihnen, dass es Ihrem Computer keinen absichtlichen Schaden zufügt. Wenn Sie misstrauisch sind und / oder es ändern und erweitern möchten, lesen Sie den Code auf GitHub !

Erstellen Sie einen Ordner in Ihrem Laufwerk C: C:\shellicon. Erstellen Sie BMP - Dateien mit den folgenden Titeln: copy, cut, delete, paste, redo, sendto, undo. (Hoffentlich ist klar, wer was macht.) Diese Bilder sollten wahrscheinlich 16 mal 16 Pixel groß sein (oder wie groß Ihre DPI-Einstellungen auch sein mögen), aber ich hatte auch Erfolg mit größeren. Wenn Sie möchten, dass die Symbole transparent aussehen, müssen Sie lediglich den Hintergrund in der gleichen Farbe wie das Kontextmenü anzeigen. (Dieser Trick wird auch von Dropbox angewendet.) Ich habe meine schrecklichen Symbole mit MS Paint erstellt. Andere Programme werden möglicherweise auf eine Weise gespeichert, die mit kompatibel ist LoadImageA. 16 x 16 bei 24-Bit-Farbtiefe bei 96 Pixel pro Zoll scheint der zuverlässigste Satz von Bildeigenschaften zu sein.

Platzieren Sie die DLL an einem Ort, auf den alle Benutzer zugreifen können. Der Ordner, den Sie gerade erstellt haben, ist eine gute Wahl. Öffnen Sie eine Admin-Eingabeaufforderung in dem Ordner, der die DLL enthält, und führen Sie dies aus regsvr32 ContextIcons.dll. Dies schafft Registrierungsinformationen für den Shell - Typen *, Drive, Directory, und Directory\Background. Wenn Sie jemals die Shell-Erweiterung entfernen möchten, tun Sie dies regsvr32 /u ContextIcons.dll.

Relevanter Code

Grundsätzlich fragt die Erweiterung nur den Text jedes Kontextmenüeintrags mit ab GetMenuItemInfound passt gegebenenfalls das Symbol mit an SetMenuItemInfo.

Visual Studio generiert eine Menge mysteriösen Code für ATL-Projekte, aber dies ist der Inhalt von IconInjector.cpp, der den Kontextmenü-Handler implementiert:

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

Beachten Sie, dass die HBITMAPs nie bereinigt werden, dies ist jedoch nicht allzu wichtig, da die DLL-Dateien beim Herunterfahren des Explorers nicht mehr angezeigt werden. Die Icons nehmen sowieso kaum Speicherplatz in Anspruch.

Wenn Sie für 32-Bit kompilieren, ist der erste Parameter GetCommandStringnur a UINTanstelle von a UINT_PTR.

Wenn Sie wirklich transparente Symbole wollen, müssen Sie zunächst ein Fenster mit dem gewünschten Symbol und setzen Sie erstellen müssen , um mii.hBmpItemzu HBMMENU_SYSTEMund legen den Griff in die Fenster in mii.dwItemData, wie im unteren Teil des beschriebenen auf den MSDN - ArtikelMENUITEMINFO . Ich konnte nicht herausfinden, wie Fenster aus Shell-Erweiterungen erstellt werden. LR_LOADTRANSPARENTsieht vielversprechend aus als Flagge von LoadImageA, hat aber seine eigenen Fallstricke - und funktioniert nur, wenn Sie Bitmaps mit 256 Farben verwenden.

Wenn beim Laden von Bildern Probleme auftreten, entfernen Sie das LR_DEFAULTSIZEFlag aus den LoadImageAAufrufen.

Jemand, der über ausreichende Kenntnisse in C ++ verfügt, könnte wahrscheinlich Ressourcen aus anderen DLLs entnehmen und in HBITMAPs konvertieren , aber das bin nicht ich.

Ändern Sie es

Ich habe dies in Visual Studio geschrieben, das meiner Meinung nach der beste Editor für Windows C ++ ist.

Laden Sie die SLN-Datei in Visual Studio 2015, nachdem Sie die C ++ - Tools installiert haben. In IconInjector.cppkönnen Sie HBITMAPoben Einträge hinzufügen und LoadImageAanrufen Initialize, um neue Symbole hinzuzufügen. else ifVerwenden Sie unten im Abschnitt einen wcscmpAufruf, um nach einer genauen Übereinstimmung wcsstrzu suchen , oder einen Aufruf, um nach der Anwesenheit einer Teilzeichenfolge zu suchen. In beiden Fällen steht das &für die Position der Unterstreichung / Beschleunigung bei Verwendung von Umschalt + F10. Stellen Sie Ihren Modus auf Release und Ihre Architektur auf x64 ein und führen Sie BuildBuild Solution aus . Es wird eine Fehlermeldung angezeigt, dass die Ausgabe nicht registriert werden konnte, aber keine Sorge. Sie möchten dies sowieso manuell tun. Beenden Sie den Explorer, kopieren Sie die neue DLL ( \x64\Release\ContextIcons.dllim Lösungsordner) an den gewünschten Ort und regsvr32tanzen Sie dann.

Zuschreibungen

Vielen Dank an die MSDN-Autoren und an den Schöpfer von " The Complete Idiot's Guide to Writing Shell Extensions ", auf das ich stark verwiesen habe.

Lobrede

Zu den vielen Explorer-Instanzen, die bei der Produktion dieser Shell-Erweiterung getötet wurden: Sie sind für eine große Sache gestorben, dass einige Leute im Internet Symbole neben ihren Worten haben können.

Ben N
quelle
Beeindruckend! Ich schätze Ihre Bemühungen sehr, vielen Dank! (+1) Ich habe mein Bestes versucht, aber die kompilierte Version funktioniert nicht unter Windows 10 (Build 10240). Ich weiß nicht, was das Problem ist, alle C:\shellicon\copy.bmpBMP- Bilder befinden sich im richtigen Pfad ( usw. - das sind 20x20 Pixel-Symbole im BMP-Format) und ich habe die DLL als Administrator in der Eingabeaufforderung registriert, mit regsvr32 ContextIcons.dllder sie erfolgreich ausgeführt wurde, aber Ich sehe keine Änderungen im Kontextmenü. Ich habe sogar den Computer neu gestartet, die Registrierung aufgehoben und die DLL erneut registriert, aber keine Änderungen vorgenommen. Ich versuche die Quelle in VS2015 zu kompilieren!
Sk8erPeter
@ Sk8erPeter MSDN sagte, dass die Symbole 16x16 sein müssen, aber 20x20 für mich funktioniert. Möglicherweise erfordert Windows 10 16x16? Beachten Sie, dass Sie den Explorer neu starten müssen, damit die Änderungen wirksam werden.
Ben N
2
@ Sk8erPeter Na klar, los geht's . Ich werde sehen, wie der Code auf GitHub veröffentlicht wird.
Ben N
2
Sie werden es nicht glauben ... ES FUNKTIONIERT mit Ihren Bildern! : D: D Es bedeutet, dass ich einige BMP-Dateien habe, die Windows nicht verarbeiten konnte. Ich weiß nicht warum (das werde ich später auch überprüfen). Wie auch immer, vielen Dank, Ihr Code löst das Problem wirklich! :)
Sk8erPeter
1
@BenN: OK, danke! :) Es wäre etwas bequemer gewesen. Übrigens ist mir in der Zwischenzeit aufgefallen, dass ich beim Öffnen meiner zuvor nicht funktionierenden Bilder im legendären Paint eine "Speichern unter"> "24-Bit-Bitmap (.bmp; .dip)" (also in einer BMP-Datei) speichere wieder), und ich benutze diese neue Datei als Quellbild, es funktioniert. Natürlich muss die Bitmap genau 16x16 Pixel groß sein. Paint erstellt also das erwartete Bitmap-Format, das 24 Bit pro Pixel (16,7 Millionen Farben), 96 x 96 DPI und 16 x 16 Pixel groß ist. Bisher habe ich PNG-Dateien in IrfanView in BMP-Dateien konvertiert und deren Größe geändert. Diese Symbole funktionierten nicht.
Sk8erPeter
1

Ich habe nicht genug Repräsentanten, um einen Kommentar zu hinterlassen, aber anscheinend sind diese Informationen in der Datei shell32.dll enthalten. Die Dateien wurden kompiliert, so dass es schwierig ist zu erkennen, welche Funktionen darin enthalten sind, aber es scheint die einzige zu sein.

Interessant (Registrierungsexport):

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

(Standard) REG_SZ Objekt kopieren / verschieben / umbenennen / löschen / verknüpfen

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

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

Unter dem InProcServer32-Schlüssel wird auf shell32.dll verwiesen. Es gibt noch ein paar andere mit relevanten klingenden Namen. Möglicherweise ist auch windows.storage.dll von Interesse

Nijave
quelle
1
Interessante Information. Es scheint jedoch eher ein Kommentar als eine Antwort zu sein. Sie haben jetzt genug Repräsentanten, um überall zu kommentieren :)
Ben N