Jak przypisać ikonę do pozycji „Kopiuj / Wytnij / Wklej / Usuń” Domyślne pozycje menu kontekstowego systemu Windows?

12

W systemie Windows 8 / 8.1 x64 chciałbym przypisać niestandardową ikonę do domyślnych elementów menu kontekstowego systemu Windows, takich jak Kopiuj , Wytnij , Wklej , Usuń , Cofnij , Ponów i Wyślij do , które domyślnie mają dowolną ikonę:

wprowadź opis zdjęcia tutaj

Gdzie mogę znaleźć „odniesienie” do tych pozycji menu kontekstowego w rejestrze, a następnie dodać dla nich wartość rejestru „ikona”?

Lub innymi słowy, jak przypisać ikonę do menu rozszerzenia powłoki niczym SendTo Shellex ?.

Badania


Jak komentuje @ Sk8erPeter , wydaje się, że:

„Dodanie Iconwartości ciągu do różnych programów obsługi menu kontekstowego nie działa tak, jak podczas dodawania go do elementu niestandardowego, takiego jak np. HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY

ElektroStudios
źródło
Do jakiej ikony się odnosisz? Czy masz zrzut ekranu?
Raystafarian
@Raystafarian Zaktualizowałem pytanie obrazem.
ElektroStudios,
1
@ Raystafarian: pytanie brzmi: jak dodać niestandardową ikonę do istniejących podstawowych pozycji menu kontekstowego, takich jak „Wytnij” , „Kopiuj” , „Usuń” , „Zmień nazwę” itp. BTW, dodając nowy niestandardowy element do menu kontekstowego, jest bardzo łatwe, ponieważ wystarczy dodać Iconwartość ciągu w kluczu podobnym HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(a wartość tego Iconbędzie taka jak np %SystemRoot%\System32\shell32.dll,-133. sg. else). ALE dodawanie Iconwartości ciągu do różnych programów obsługi menu kontekstowego nie działa tak, jak podczas dodawania go do tych niestandardowych elementów.
Sk8erPeter
Oto kolejny zrzut ekranu, aby wyjaśnić (interesująca część jest w czerwonych ramkach ): i.imgur.com/fmewg6L.png . BTW, jak widzisz, mam kilka niestandardowych elementów w menu kontekstowym z niestandardowymi ikonami (np. „Otwórz za pomocą Notepad ++” ) - dokładnie to chcielibyśmy osiągnąć z istniejącymi elementami menu kontekstowego systemu!
Sk8erPeter,
1
@ Sk8erPeter W tej chwili moją najlepszą szansą jest perspektywa utworzenia modułu obsługi menu kontekstowego powłoki, który będzie używany SetMenuItemInfow odpowiedzi QueryContextMenu.
Ben N

Odpowiedzi:

10

Powiadomienie o stowarzyszeniu: Jestem autorem oprogramowania wymienionego w tej odpowiedzi.

Po pierwsze, chcę wiedzieć, że nauczyłem się C ++ i Win32 tylko na to pytanie .

Opracowałem 64-bitowe rozszerzenie powłoki, które jest rejestrowane jako moduł obsługi menu kontekstowego. Po wywołaniu przeszukuje istniejące pozycje menu, szukając interesujących pozycji. Jeśli ją znajdzie, nakleja na nią ikonę (która musiała zostać wcześniej załadowana). Obecnie wyszukuje opcje Kopiuj , Wytnij , Usuń , Wklej , Ponów , Wyślij do i Cofnij . Możesz dodać własny, modyfikując kod; procedura jest opisana poniżej. (Przepraszam, nie jestem wystarczająco dobry w C ++, aby można go było konfigurować).

Zrzut ekranu przedstawiający najbrzydsze ikony znane człowiekowi:

w akcji

Możesz pobrać te ikony, jeśli naprawdę chcesz.

Konfiguracja

Pobierz go (z mojego Dropbox). Uwaga : ten plik jest wykrywany przez jeden skaner VirusTotal jako pewna forma złośliwego oprogramowania. Jest to zrozumiałe, biorąc pod uwagę rodzaj rzeczy, które należy zrobić, aby zlikwidować istniejące wpisy. Daję wam słowo, że nie wyrządza celowo szkody komputerowi. Jeśli jesteś podejrzany i / lub chcesz go zmodyfikować i rozszerzyć, zobacz kod na GitHub !

Utwórz folder w dysku C: C:\shellicon. Tworzenie plików BMP z następujących tytułów: copy, cut, delete, paste, redo, sendto, undo. (Mam nadzieję, że jest oczywiste, który z nich robi jakąś rzecz.) Te obrazy powinny prawdopodobnie mieć rozmiar 16 na 16 pikseli (lub jak duże ustawienia DPI robią margines menu), ale udało mi się również z większymi. Jeśli chcesz, aby ikony wyglądały na przezroczyste, musisz po prostu ustawić ich tło w tym samym kolorze co menu kontekstowe. (Ta sztuczka jest również stosowana przez Dropbox.) Zrobiłem moje straszne ikony za pomocą MS Paint; inne programy mogą, ale nie muszą, zapisywać w sposób zgodny z LoadImageA. 16 na 16 przy 24-bitowej głębi kolorów przy 96 pikselach na cal wydaje się być najbardziej niezawodnym zestawem właściwości obrazu.

Umieść bibliotekę DLL w miejscu dostępnym dla wszystkich użytkowników, właśnie utworzony folder jest dobrym wyborem. Otwórz monit administratora w folderze zawierającym bibliotekę DLL i wykonaj regsvr32 ContextIcons.dll. Stwarza informacji rejestracyjnych dla typów muszli *, Drive, Directory, i Directory\Background. Jeśli kiedykolwiek chcesz usunąć rozszerzenie powłoki, zrób to regsvr32 /u ContextIcons.dll.

Odpowiedni kod

Zasadniczo rozszerzenie tylko odpytuje tekst każdego elementu menu kontekstowego GetMenuItemInfoi, w razie potrzeby, dostosowuje ikonę za pomocą SetMenuItemInfo.

Visual Studio generuje wiele magicznego tajemniczego kodu dla projektów ATL, ale jest to zawartość IconInjector.cpp, która implementuje moduł obsługi menu kontekstowego:

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

Zauważ, że HBITMAPs nigdy nie są czyszczone, ale nie ma to większego znaczenia, biorąc pod uwagę, że pliki DLL znikną po wyłączeniu Explorera. Ikony i tak ledwo zajmują pamięć.

Jeśli kompilujesz dla wersji 32-bitowej, pierwszym parametrem GetCommandStringjest po prostu UINTzamiast a UINT_PTR.

Jeśli naprawdę chcesz, przejrzyste ikony, musisz utworzyć okno z żądaną ikonę, a następnie ustawić mii.hBmpItemsię HBMMENU_SYSTEMi umieścić uchwyt do okna, w mii.dwItemDatasposób opisany na dole artykułu na MSDNMENUITEMINFO . Nie byłem w stanie dowiedzieć się, jak tworzyć okna z rozszerzeń powłoki. LR_LOADTRANSPARENTwygląda obiecująco LoadImageA, ale ma swoje własne pułapki - w szczególności nie działa, chyba że użyjesz 256-bitowych map bitowych.

Jeśli występują problemy z ładowaniem obrazu, spróbuj usunąć LR_DEFAULTSIZEflagę z LoadImageApołączeń.

Ktoś wystarczająco wykwalifikowany w C ++ mógłby prawdopodobnie pobrać zasoby z innych bibliotek DLL i przekonwertować je na HBITMAPs, ale tym kimś nie jestem ja.

Modyfikując to

Napisałem to w Visual Studio, które uważam za najlepszy edytor dla Windows C ++.

Załaduj plik SLN do programu Visual Studio 2015 po zainstalowaniu narzędzi C ++. W IconInjector.cppmożesz dodać HBITMAPwpisy u góry i LoadImageAwywołać, Initializeaby dodać nowe ikony. W dolnej else ifczęści sekcji użyj wcscmppołączenia, aby wyszukać dokładne dopasowanie lub wcsstrpołączenia, aby sprawdzić obecność podłańcucha. W obu przypadkach &reprezentuje pozycję podkreślenia / akceleratora przy użyciu Shift + F10. Ustaw tryb na Release, a architekturę na x64 i wykonaj KompilacjaKompiluj rozwiązanie . Pojawi się błąd związany z niezarejestrowaniem danych wyjściowych, ale nie martw się; i tak chcesz to zrobić ręcznie. Zakończ Eksploratora, skopiuj nową bibliotekę DLL ( \x64\Release\ContextIcons.dllw folderze rozwiązania) na miejsce, a następnie wykonaj regsvr32taniec.

Atrybucje

Wielkie podziękowania dla pisarzy MSDN i twórcy „ The Complete Idiot's Guide to Writing Shell Extensions ”, do którego bardzo się odwoływałem.

Pochwała

Do wielu wystąpień Eksploratora, które zostały zabite podczas produkcji tego rozszerzenia powłoki: zginąłeś z wielkiej przyczyny, że niektóre osoby w Internecie mogą mieć ikony obok swoich słów.

Ben N.
źródło
Łał! Naprawdę doceniam twój wysiłek, dziękuję bardzo! (+1) Starałem się jak najlepiej, ale nie mogłem uruchomić skompilowanej wersji w systemie Windows 10 (kompilacja 10240). Nie wiem na czym polega problem, wszystkie obrazy bmp istnieją we właściwej ścieżce ( C:\shellicon\copy.bmpitp. - są to ikony 20 x 20 pikseli w formacie BMP) i zarejestrowałem dll jako administrator w wierszu polecenia, z regsvr32 ContextIcons.dllktórym uruchomiono pomyślnie, ale Nie widzę żadnych zmian w menu kontekstowym. Zrestartowałem nawet komputer, wyrejestrowałem i ponownie zarejestrowałem dll, ale żadnych zmian. Próbuję skompilować źródło w VS2015!
Sk8erPeter
@ Sk8erPeter MSDN powiedział, że ikony muszą mieć wymiary 16 x 16, ale 20 x 20 działa dla mnie. Może Windows 10 wymaga 16x16? Pamiętaj, że musisz ponownie uruchomić Eksploratora, aby zmiany odniosły skutek.
Ben N
2
@ Sk8erPeter Oczywiście, proszę bardzo . Zobaczę o umieszczeniu kodu w GitHub. Pracuję teraz nad pobraniem systemu Windows 10 ...
Ben N
2
Nie uwierzysz ... DZIAŁA z twoimi obrazami! : D: D To znaczy, że mam kilka plików bmp, których Windows nie mógł obsłużyć, nie wiem dlaczego (później to też sprawdzę). W każdym razie dziękuję bardzo, twój kod naprawdę rozwiązuje problem! :)
Sk8erPeter,
1
@BenN: OK, dziękuję! :) Byłoby to trochę wygodniejsze. BTW w międzyczasie zdałem sobie sprawę, że jeśli otworzę moje wcześniej niedziałające obrazy w legendarnej aplikacji Paint i wykonam 24-bitową mapę bitową (.bmp; .dip) „Zapisz jako”> (więc zapisz ją w pliku BMP ponownie) i używam tego nowego pliku jako obrazu źródłowego, DZIAŁA. Oczywiście rozmiar bitmapy musi wynosić dokładnie 16 x 16 pikseli. Tak więc Paint tworzy oczekiwany format bitmapy, który ma 24 bity na piksel (16,7 miliona kolorów), 96 x 96 DPI i 16 x 16 pikseli. Wcześniej konwertowałem i zmieniałem rozmiar plików .png w IrfanView na pliki .bmp, te ikony nie działały.
Sk8erPeter
1

Nie mam wystarczającej liczby przedstawicieli, aby zostawić komentarz, ale wygląda na to, że ta informacja jest zawarta w shell32.dll. Pliki zostały skompilowane, więc trudno jest zobaczyć, jakie funkcje są w nim zawarte, ale wydaje się, że jest to jedna.

Interesujące (eksport rejestru):

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

(Domyślnie) REG_SZ Kopiuj / Przenieś / Zmień nazwę / Usuń / Połącz obiekt

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

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

Pod kluczem InProcServer32 odwołuje się do shell32.dll. Jest też kilka innych o odpowiednich brzmieniach. Być może również interesujący jest plik windows.storage.dll

nijave
źródło
1
Interesująca informacja. Wydaje się to jednak komentarzem, a nie odpowiedzią. Masz teraz wystarczająco dużo przedstawicieli, aby móc komentować wszędzie :)
Ben N