Bagaimana cara menetapkan ikon untuk item menu konteks standar Windows “Salin / Potong / Tempel / Hapus”?

12

Di bawah Windows 8 / 8.1 x64, saya ingin menetapkan ikon khusus untuk item menu konteks Windows default seperti Salin , Potong , Tempel , Hapus , Batalkan , Ulangi dan Kirim Ke item, yang secara default memiliki ikon apa pun:

masukkan deskripsi gambar di sini

Di mana saya dapat menemukan "referensi" ke item-item menu konteks dalam registri kemudian menambahkan nilai registri "ikon" untuk mereka?

Atau dengan kata lain, bagaimana cara menetapkan ikon ke menu ekstensi shell seperti shellex SendTo ?.

Penelitian


Seperti yang dikomentari oleh @ Sk8erPeter , tampaknya:

"Menambahkan nilai Iconstring ke penangan menu konteks yang berbeda tidak berfungsi seperti saat menambahkannya ke item khusus seperti misalnya HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY"

ElektroStudios
sumber
Ikon apa yang Anda maksud? Apakah Anda memiliki tangkapan layar?
Raystafarian
@Raystafarian Saya telah memperbarui pertanyaan dengan sebuah gambar.
ElektroStudios
1
@Raystafarian: pertanyaannya adalah bagaimana menambahkan ikon khusus ke item menu konteks dasar yang ada seperti "Potong" , "Salin" , "Hapus" , "Ubah Nama" , dll. BTW saat menambahkan item khusus baru ke menu konteks, itu sangat mudah, karena Anda hanya perlu menambahkan IconNilai String dalam kunci seperti HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(dan nilai Iconakan seperti misalnya %SystemRoot%\System32\shell32.dll,-133atau sg. lain). TETAPI menambahkan nilai Iconstring ke penangan menu konteks yang berbeda tidak berfungsi seperti saat menambahkannya ke item kustom ini.
Sk8erPeter
Berikut ini adalah screenshot lain untuk memperjelasnya (bagian yang menarik adalah di perbatasan merah): i.imgur.com/fmewg6L.png . BTW seperti yang Anda lihat, saya memiliki beberapa item khusus di menu konteks dengan ikon khusus (seperti "Buka dengan Notepad ++" ) - ini persis yang ingin kami capai dengan item menu konteks sistem yang ada!
Sk8erPeter
1
@ Sk8erPeter Prospek terbaik saya saat ini adalah prospek membuat menu handler konteks yang digunakan SetMenuItemInfosebagai respons QueryContextMenu.
Ben N

Jawaban:

10

Pemberitahuan afiliasi: Saya adalah pembuat perangkat lunak yang disebutkan dalam jawaban ini.

Pertama, saya ingin Anda tahu bahwa saya belajar C ++ dan Win32 hanya untuk pertanyaan ini .

Saya telah mengembangkan ekstensi shell 64-bit yang terdaftar sebagai penangan menu konteks. Ketika dipanggil, ia menggeledah item menu yang ada, mencari entri yang menarik. Jika menemukan satu, itu menempel ikon di atasnya (yang harus dimuat sebelumnya). Saat ini, ia mencari Copy , Cut , Delete , Paste , Redo , Send to , dan Undo . Anda dapat menambahkan sendiri dengan mengubah kode; prosedur untuk ini dijelaskan di bawah ini. (Maaf, saya tidak cukup baik di C ++ untuk membuatnya dapat dikonfigurasi.)

Tangkapan layar itu sedang beraksi, dengan ikon-ikon paling jelek diketahui manusia:

dalam aksi

Anda dapat mengunduh ikon-ikon ini jika Anda benar-benar mau.

Menyiapkannya

Unduh (dari Dropbox saya). Perhatikan : file ini terdeteksi oleh satu pemindai VirusTotal sebagai bentuk malware. Ini bisa dimengerti, mengingat hal-hal yang harus dilakukan untuk memukul entri yang ada. Saya memberi Anda kata-kata saya bahwa itu tidak membahayakan komputer Anda. Jika Anda curiga dan / atau ingin memodifikasi dan memperluasnya, lihat kode di GitHub !

Buat folder di drive C Anda: C:\shellicon. Buat file BMP dengan judul berikut: copy, cut, delete, paste, redo, sendto, undo. (Semoga jelas yang mana melakukan hal yang mana.) Gambar-gambar ini mungkin harus 16 x 16 piksel (atau seberapa besar pengaturan DPI Anda membuat margin menu), tapi saya sudah sukses dengan yang lebih besar juga. Jika Anda ingin ikon terlihat transparan, Anda harus membuat latar belakangnya dengan warna yang sama dengan menu konteks. (Trik ini digunakan oleh Dropbox juga.) Saya membuat ikon mengerikan saya dengan MS Paint; program lain mungkin atau mungkin tidak menyimpan dengan cara yang kompatibel dengan LoadImageA. 16 dengan 16 pada kedalaman warna 24-bit pada 96 piksel per inci tampaknya merupakan properti gambar yang paling dapat diandalkan.

Letakkan DLL di tempat yang dapat diakses oleh semua pengguna, folder yang baru saja Anda buat adalah pilihan yang baik. Buka prompt admin di folder yang berisi DLL dan lakukan regsvr32 ContextIcons.dll. Hal ini menciptakan informasi pendaftaran untuk jenis shell *, Drive, Directory, dan Directory\Background. Jika Anda ingin menghapus ekstensi shell, lakukan regsvr32 /u ContextIcons.dll.

Kode yang relevan

Pada dasarnya, ekstensi hanya menanyakan teks setiap item menu konteks dengan GetMenuItemInfodan, jika sesuai, menyesuaikan ikon dengan SetMenuItemInfo.

Visual Studio menghasilkan banyak kode ajaib ajaib untuk proyek ATL, tetapi ini adalah isinya IconInjector.cpp, yang mengimplementasikan penangan menu konteks:

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

Perhatikan bahwa HBITMAPs tidak pernah dibersihkan, tetapi ini tidak terlalu penting mengingat barang-barang DLL akan hilang ketika Explorer dimatikan. Ikon hampir tidak mengambil memori apa pun.

Jika Anda mengompilasi untuk 32-bit, parameter pertama GetCommandStringadalah hanya sebagai UINTganti a UINT_PTR.

Jika Anda benar-benar ingin ikon transparan, Anda harus membuat jendela dengan ikon yang diinginkan dan kemudian mengatur mii.hBmpItemuntuk HBMMENU_SYSTEMdan menempatkan pegangan untuk jendela di mii.dwItemData, seperti yang dijelaskan di bagian bawah artikel MSDN padaMENUITEMINFO . Saya tidak dapat menemukan cara membuat windows dari ekstensi shell. LR_LOADTRANSPARENTterlihat menjanjikan sebagai bendera LoadImageA, tetapi memiliki perangkapnya sendiri - khususnya, tidak berfungsi kecuali jika Anda menggunakan bitmap 256-warna.

Jika Anda mengalami masalah dengan pemuatan gambar, coba hapus LR_DEFAULTSIZEbendera dari LoadImageApanggilan.

Seseorang yang cukup cakap dalam C ++ mungkin dapat mengambil sumber daya dari DLL lain dan mengonversinya menjadi HBITMAPs, tetapi seseorang itu bukan saya.

Memodifikasinya

Saya menulis ini di Visual Studio, yang saya yakini sebagai editor terbaik untuk Windows C ++.

Memuat file SLN ke dalam Visual Studio 2015 setelah Anda menginstal alat C ++. Di IconInjector.cpp, Anda dapat menambahkan HBITMAPentri di bagian atas dan LoadImageAmemanggil Initializeuntuk menambahkan ikon baru. Di else ifbagian bawah, gunakan wcscmppanggilan untuk mencari kecocokan yang tepat, atau wcsstrpanggilan untuk mencari keberadaan substring. Dalam kedua kasus, &mewakili posisi garis bawah / akselerator saat menggunakan Shift + F10. Atur mode Anda ke Release dan arsitektur Anda ke x64, dan lakukan BuildBuild Solution . Anda akan mendapatkan kesalahan tentang gagal mendaftarkan output, tetapi jangan khawatir; Anda tetap ingin melakukan ini secara manual. End Explorer, salin DLL baru ( \x64\Release\ContextIcons.dlldalam folder solusi) ke tempat itu, lalu lakukan regsvr32dansa.

Atribusi

Terima kasih banyak kepada penulis MSDN, dan kepada pencipta " Panduan Lengkap Idiot untuk Menulis Ekstensi Shell ", yang sangat saya referensikan.

Sanjungan

Untuk banyak contoh Explorer yang terbunuh dalam produksi ekstensi shell ini: Anda mati karena suatu alasan, bahwa beberapa orang di Internet dapat memiliki ikon di sebelah kata-kata mereka.

Ben N
sumber
Wow! Saya sangat menghargai upaya Anda, terima kasih banyak! (+1) Saya mencoba yang terbaik tetapi tidak dapat membuat versi kompilasi berfungsi pada Windows 10 (Build 10240). Saya tidak tahu apa masalahnya, semua gambar bmp ada di jalur yang benar ( C:\shellicon\copy.bmp, dll. - ini adalah 20x20 piksel ikon dalam format BMP) dan saya mendaftarkan dll sebagai admin di command prompt dengan regsvr32 ContextIcons.dllyang berjalan dengan sukses, tetapi Saya tidak melihat perubahan dalam menu konteks. Saya bahkan me-restart komputer, tidak terdaftar dan mendaftar ulang dll lagi, tetapi tidak ada perubahan. Saya mencoba mengkompilasi sumber di VS2015!
Sk8erPeter
@ Sk8erPeter MSDN mengatakan bahwa ikon harus berukuran 16x16, tetapi 20x20 berfungsi untuk saya. Mungkin Windows 10 membutuhkan 16x16? Perhatikan bahwa Anda harus memulai ulang Explorer agar perubahan diterapkan.
Ben N
2
@ Sk8erPeter Tentu saja, ini dia . Saya akan melihat tentang menempatkan kode di GitHub. Bekerja untuk mengunduh Windows 10 sekarang ...
Ben N
2
Anda tidak akan percaya ... ITU BEKERJA dengan gambar Anda! : D: D Berarti saya memiliki beberapa file bmp yang tidak dapat ditangani Windows, tidak tahu mengapa (nanti saya akan memeriksanya juga). Bagaimanapun, terima kasih banyak, kode Anda benar-benar menyelesaikan masalah! :)
Sk8erPeter
1
@ Ben: OK, terima kasih! :) Itu akan menjadi sedikit lebih nyaman. BTW sementara itu saya menyadari bahwa jika saya membuka gambar saya yang sebelumnya tidak berfungsi di Paint legendaris , dan saya melakukan "Save as"> "24-bit Bitmap (.bmp; .dip)" (jadi simpan ke file BMP lagi), dan saya menggunakan file baru ini sebagai gambar sumber, itu BEKERJA. Tentu saja, ukuran bitmap harus tepat 16x16 piksel. Jadi Paint menciptakan format bitmap yang diharapkan yaitu 24 bit per piksel (16,7 juta warna), 96x96 DPI, dan ukuran 16x16 piksel. Sebelumnya saya mengonversi dan mengubah ukuran file .png di IrfanView menjadi file .bmp, ikon ini tidak berfungsi.
Sk8erPeter
1

Saya tidak memiliki cukup perwakilan untuk memberikan komentar tetapi tampaknya info ini terdapat di dalam shell32.dll. File-file telah dikompilasi sehingga sulit untuk melihat fungsi apa yang ada di dalamnya, tetapi tampaknya menjadi satu-satunya.

Yang menarik (ekspor registri):

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

(Default) REG_SZ Salin / Pindahkan / Ubah Nama / Hapus / Tautkan Objek

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

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

Di bawah kunci InProcServer32 itu merujuk shell32.dll. Ada beberapa yang lain juga dengan nama yang terdengar relevan. Mungkin juga yang menarik adalah windows.storage.dll

nijave
sumber
1
Informasi yang menarik. Namun, sepertinya itu adalah komentar dan bukan jawaban. Anda sekarang memiliki cukup perwakilan untuk berkomentar di mana saja :)
Ben N