วิธีกำหนดไอคอนสำหรับรายการเมนูบริบทเริ่มต้นของ "คัดลอก / ตัด / วาง / ลบ" ของ Windows


12

ภายใต้ Windows 8 / 8.1 x 64 ผมอยากที่จะกำหนดไอคอนที่กำหนดเองสำหรับรายการเมนูบริบทเริ่มต้น Windows เช่นคัดลอก , ตัด , วาง , ลบ , Undo , Redoและส่งไปยังรายการซึ่งเริ่มต้นโดยมีไอคอน:

ป้อนคำอธิบายรูปภาพที่นี่

ที่ฉันสามารถค้นหา "อ้างอิง" ไปยังรายการเมนูบริบทเหล่านั้นในรีจิสทรีแล้วเพิ่มค่าไอคอน "ไอคอน" สำหรับพวกเขา?

หรือกล่าวอีกนัยหนึ่งว่าจะกำหนดไอคอนให้กับเมนูส่วนขยายของเชลล์เช่นSendTo shellex ได้อย่างไร

การวิจัย


ตามที่แสดงความคิดเห็นโดย @ Sk8erPeterดูเหมือนว่า:

"การเพิ่มIconค่าสตริงลงในตัวจัดการเมนูบริบทที่แตกต่างกันจะไม่ทำงานเมื่อเพิ่มลงในรายการที่กำหนดเองเช่นเช่น HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY"


คุณหมายถึงไอคอนใด คุณมีภาพหน้าจอหรือไม่?
Raystafarian

@Raystafarian ฉันได้อัปเดตคำถามด้วยภาพ
ElektroStudios

1
@ เรย์สตาเรียน: คำถามคือวิธีเพิ่มไอคอนที่กำหนดเองไปยังไอเท็มเมนูบริบทพื้นฐานที่มีอยู่เช่น"ตัด" , "คัดลอก" , "ลบ" , "เปลี่ยน" , ฯลฯเปลี่ยน BTW เมื่อเพิ่มรายการที่กำหนดเองใหม่ลงในเมนูบริบท เป็นเรื่องง่ายมากเพราะคุณเพียงแค่เพิ่มIconค่าสตริงในคีย์เช่นHKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(และค่าของIconจะเป็นเช่น%SystemRoot%\System32\shell32.dll,-133หรืออื่น ๆ ) แต่การเพิ่มIconค่าสตริงให้กับตัวจัดการเมนูบริบทที่แตกต่างกันจะไม่ทำงานเมื่อเพิ่มลงในรายการที่กำหนดเองเหล่านี้
Sk8erPeter

นี่คือภาพหน้าจอจะทำให้มันชัดเจนอื่น (ส่วนที่น่าสนใจอยู่ในเส้นขอบสีแดง): i.imgur.com/fmewg6L.png BTW อย่างที่คุณเห็นฉันมีรายการที่กำหนดเองในเมนูบริบทที่มีไอคอนที่กำหนดเอง (เช่น"Open with Notepad ++" ) - นี่คือสิ่งที่เราต้องการที่จะบรรลุด้วยรายการเมนูบริบทของระบบที่มีอยู่!
Sk8erPeter

1
@ Sk8erPeter ของฉันนำที่ดีที่สุดในขณะนี้คือโอกาสของการสร้างเปลือกบริบทจัดการเมนูที่ใช้ในการตอบสนองSetMenuItemInfo QueryContextMenu
Ben N

คำตอบ:


10

ประกาศการเป็นพันธมิตร: ฉันเป็นผู้เขียนซอฟต์แวร์ที่กล่าวถึงในคำตอบนี้

ก่อนอื่นฉันจะให้คุณรู้ว่าฉันได้เรียนรู้ C ++ และ Win32 สำหรับคำถามนี้เท่านั้น

ฉันได้พัฒนาส่วนขยายของเชลล์ 64 บิตที่ได้รับการลงทะเบียนเป็นตัวจัดการเมนูบริบท เมื่อมีการเรียกใช้มันจะค้นหาผ่านรายการเมนูที่มีอยู่เพื่อค้นหารายการที่น่าสนใจ หากพบหนึ่งไอคอนจะติดไอคอนไว้ (ซึ่งจะต้องโหลดก่อนหน้านี้) ในขณะที่จะมองหาคัดลอก , ตัด , ลบ , วาง , ทำซ้ำ , ส่งไปและเลิกทำ คุณสามารถเพิ่มของคุณเองโดยการแก้ไขรหัส; ขั้นตอนนี้อธิบายไว้ด้านล่าง (ขออภัยฉันไม่ดีพอที่ C ++ เพื่อให้สามารถกำหนดค่าได้)

ภาพหน้าจอของอุปกรณ์ที่ใช้งานโดยมีไอคอนน่าเกลียดที่สุดที่มนุษย์รู้จัก:

ในการดำเนินการ

คุณสามารถดาวน์โหลดไอคอนเหล่านี้หากคุณต้องการ

การตั้งค่า

ดาวน์โหลด (จาก Dropbox ของฉัน) ประกาศ : ไฟล์นี้ถูกตรวจจับโดยสแกนเนอร์ VirusTotal หนึ่งตัวว่าเป็นมัลแวร์บางรูปแบบ สิ่งนี้สามารถเข้าใจได้โดยกำหนดประเภทของสิ่งที่ต้องทำเพื่อตีสิ่งที่มีอยู่ ฉันให้คุณคำของฉันว่ามันไม่เป็นอันตรายต่อคอมพิวเตอร์ของคุณ หากคุณสงสัยและ / หรือคุณต้องการแก้ไขและขยายให้ดูรหัสใน GitHub !

สร้างโฟลเดอร์ในไดรฟ์ C C:\shelliconของคุณ: สร้างไฟล์ BMP ที่มีชื่อดังต่อไปนี้: copy, cut, delete, paste, redo, ,sendto undo(หวังว่าจะเห็นได้อย่างชัดเจนว่าสิ่งใดที่ทำสิ่งนั้น) ภาพเหล่านี้น่าจะเป็น 16 x 16 พิกเซล (หรือการตั้งค่า DPI ที่ยิ่งใหญ่ของคุณทำให้เมนูอยู่ด้านบน) แต่ฉันก็ประสบความสำเร็จด้วยเช่นกัน หากคุณต้องการให้ไอคอนดูโปร่งใสคุณจะต้องทำให้พื้นหลังเป็นสีเดียวกับเมนูบริบท (เคล็ดลับนี้ใช้โดย Dropbox ด้วย) ฉันสร้างไอคอนที่น่ากลัวด้วย MS Paint; โปรแกรมอื่น ๆ LoadImageAหรืออาจจะไม่บันทึกในลักษณะเข้ากันได้กับ 16 16 โดยที่ความลึกของสี 24 บิตที่ 96 พิกเซลต่อนิ้วดูเหมือนจะเป็นชุดของคุณสมบัติภาพที่น่าเชื่อถือที่สุด

วาง DLL ให้ผู้ใช้ทุกคนสามารถเข้าถึงได้โฟลเดอร์ที่คุณเพิ่งสร้างเป็นตัวเลือกที่ดี เปิดผู้ดูแลระบบพรอมต์ในโฟลเดอร์ที่มี DLL regsvr32 ContextIcons.dllและทำ นี้จะสร้างข้อมูลการลงทะเบียนประเภทเปลือก*, Drive, และDirectory ถ้าคุณเคยต้องการที่จะลบส่วนขยายของเชลล์ทำDirectory\Backgroundregsvr32 /u ContextIcons.dll

รหัสที่เกี่ยวข้อง

โดยทั่วไปส่วนขยายเพียง queries ข้อความทุกเมนูบริบทของรายการด้วยและหากเหมาะสมปรับไอคอนที่มีGetMenuItemInfoSetMenuItemInfo

Visual Studio สร้างรหัสลึกลับลึกลับจำนวนมากสำหรับโครงการ ATL แต่นี่คือเนื้อหาของIconInjector.cppซึ่งใช้ตัวจัดการเมนูบริบท:

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

โปรดทราบว่าตัวHBITMAPs นั้นไม่เคยทำความสะอาด แต่สิ่งนี้ไม่สำคัญมากนักเนื่องจากข้อมูลของ DLL จะหายไปเมื่อ Explorer ปิดตัวลง ไอคอนแทบจะใช้หน่วยความจำใด ๆ อยู่แล้ว

หากคุณกำลังรวบรวมสำหรับ 32 บิตพารามิเตอร์แรกจะGetCommandStringเป็นเพียงการแทนUINTUINT_PTR

หากคุณต้องการไอคอนโปร่งใสจริงๆคุณจะต้องสร้างหน้าต่างที่มีไอคอนที่ต้องการและจากนั้นตั้งค่าmii.hBmpItemไปHBMMENU_SYSTEMและใส่หมายเลขอ้างอิงไปยังหน้าต่างในmii.dwItemDataตามที่อธิบายไว้ที่ด้านล่างของบทความ MSDN MENUITEMINFOบน ฉันไม่สามารถหาวิธีสร้าง windows จากส่วนขยายของเชลล์ได้ LR_LOADTRANSPARENTดูมีแนวโน้มว่าเป็นธงLoadImageAแต่มีข้อผิดพลาดของตัวเองโดยเฉพาะไม่ทำงานจนกว่าคุณจะใช้บิตแมป 256 สี

หากคุณประสบปัญหาในการโหลดภาพลองลบการLR_DEFAULTSIZEตั้งค่าสถานะจากการLoadImageAโทร

ใครบางคนที่มีทักษะเพียงพอใน C ++ อาจจะสามารถดึงทรัพยากรออกจาก DLLs อื่น ๆ และแปลงเป็นHBITMAPs แต่บางคนไม่ใช่ฉัน

แก้ไขมัน

ฉันเขียนสิ่งนี้ใน Visual Studio ซึ่งฉันเชื่อว่าเป็นตัวแก้ไขที่ดีที่สุดสำหรับ Windows C ++

โหลดไฟล์ SLN ลงใน Visual Studio 2015 หลังจากที่คุณติดตั้งเครื่องมือ C ++ ในIconInjector.cppคุณสามารถเพิ่มHBITMAPรายการที่ด้านบนและLoadImageAโทรเข้าInitializeเพื่อเพิ่มไอคอนใหม่ ด้านล่างในelse ifส่วนใช้การwcscmpโทรเพื่อค้นหาการจับคู่ที่ตรงกันหรือการwcsstrเรียกเพื่อค้นหาการมีอยู่ของสายอักขระย่อย ในทั้งสองกรณีนี้&แสดงถึงตำแหน่งของขีดเส้นใต้ / คันเร่งเมื่อใช้ Shift + F10 ตั้งโหมดของคุณเพื่อวางจำหน่ายและสถาปัตยกรรมของคุณเพื่อ x64 และทำรูปร่าง → สร้างโซลูชัน คุณจะได้รับข้อผิดพลาดเกี่ยวกับการไม่ลงทะเบียนเอาต์พุต แต่ไม่ต้องกังวล คุณต้องการทำสิ่งนี้ด้วยตนเอง End Explorer คัดลอก DLL ใหม่ ( \x64\Release\ContextIcons.dllในโฟลเดอร์โซลูชัน) ไปยังสถานที่จากนั้นทำการregsvr32เต้น

การอ้างเหตุผล

ขอบคุณมากสำหรับนักเขียน MSDN และผู้สร้าง " คู่มือคนโง่ที่สมบูรณ์เกี่ยวกับการเขียนส่วนขยายของเชลล์ " ซึ่งฉันอ้างอิงอย่างหนัก

คำสรรเสริญเยินยอ

สำหรับอินสแตนซ์ Explorer จำนวนมากที่ถูกฆ่าตายในการผลิตส่วนขยายของเชลล์นี้: คุณเสียชีวิตด้วยสาเหตุที่ยิ่งใหญ่ซึ่งบางคนบนอินเทอร์เน็ตสามารถมีไอคอนถัดจากคำพูดของพวกเขา


ว้าว! ฉันซาบซึ้งในความพยายามของคุณขอบคุณมาก! (+1) ฉันพยายามอย่างดีที่สุด แต่ไม่สามารถทำให้รุ่นที่คอมไพล์แล้วทำงานบน Windows 10 (Build 10240) ฉันไม่รู้ว่าปัญหาคืออะไรภาพ bmp ทั้งหมดอยู่ในเส้นทางที่ถูกต้อง ( C:\shellicon\copy.bmpและอื่น ๆ - นี่คือไอคอน 20x20 พิกเซลในรูปแบบ BMP) และฉันลงทะเบียน dll ในฐานะผู้ดูแลระบบพร้อมรับคำสั่งregsvr32 ContextIcons.dllซึ่งทำงานได้สำเร็จ แต่ ฉันไม่เห็นการเปลี่ยนแปลงในเมนูบริบท ฉันยังรีสตาร์ทคอมพิวเตอร์ไม่ลงทะเบียนและลงทะเบียน dll อีกครั้ง แต่ไม่มีการเปลี่ยนแปลง ฉันพยายามรวบรวมแหล่งที่มาใน VS2015!
Sk8erPeter

@ Sk8erPeter MSDN กล่าวว่าไอคอนจำเป็นต้องมีขนาด 16x16 แต่ 20x20 ใช้งานได้สำหรับฉัน บางที Windows 10 ต้องใช้ 16x16 ใช่ไหม โปรดทราบว่าคุณต้องรีสตาร์ท Explorer เพื่อให้การเปลี่ยนแปลงมีผล
เบ็น N

2
@ Sk8erPeter แน่นอนไปเลย ฉันจะดูเกี่ยวกับการวางโค้ดบน GitHub ทำงานเกี่ยวกับการดาวน์โหลดของ Windows 10 ตอนนี้ ...
เบน N

2
คุณจะไม่เชื่อว่ามันจะทำงานกับภาพของคุณ! : D: D หมายความว่าฉันมีไฟล์ bmp บางตัวที่ Windows ไม่สามารถจัดการได้ไม่รู้ว่าทำไม (ภายหลังฉันจะตรวจสอบด้วย) อย่างไรก็ตามขอบคุณมากรหัสของคุณแก้ปัญหาได้จริง ๆ ! :)
Sk8erPeter

1
@BenN: ตกลงขอบคุณ! :) จะสะดวกกว่านี้หน่อย BTW ในขณะเดียวกันฉันรู้ว่าถ้าฉันเปิดภาพก่อนหน้านี้ไม่ทำงานในPaint ในตำนานและฉันทำ "Save as"> "บิตแมป 24 บิต (.bmp; .dip)" (ดังนั้นให้บันทึกเป็นไฟล์ BMP อีกครั้ง) และฉันใช้ไฟล์ใหม่นี้เป็นภาพต้นฉบับมันใช้งานได้ แน่นอนขนาดของบิตแมปจะต้องเท่ากับ 16x16 พิกเซล ดังนั้น Paint จึงสร้างรูปแบบบิตแมปที่คาดหวังซึ่งเป็น 24 บิตต่อพิกเซล (16.7 ล้านสี) ขนาด 96x96 DPI และ 16x16 พิกเซล ก่อนหน้านี้ฉันแปลงและปรับขนาดไฟล์. png ในไฟล์ IrfanView เป็น. bmp ไอคอนเหล่านี้ไม่ทำงาน
Sk8erPeter

1

ฉันไม่มีตัวแทนเพียงพอที่จะแสดงความคิดเห็น แต่ดูเหมือนว่าข้อมูลนี้มีอยู่ใน shell32.dll ไฟล์ถูกคอมไพล์ดังนั้นจึงยากที่จะดูว่ามีฟังก์ชั่นอะไรบ้าง แต่ดูเหมือนจะเป็นไฟล์นั้น

ที่น่าสนใจ (การส่งออกรีจิสทรี):

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

(ค่าเริ่มต้น) REG_SZ คัดลอก / ย้าย / เปลี่ยนชื่อ / ลบ / เชื่อมโยงวัตถุ

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

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

ภายใต้คีย์ InProcServer32 นั้นอ้างอิง shell32.dll มีอีกสองสามคนรวมถึงชื่อที่เกี่ยวข้อง อาจเป็นไปได้ที่น่าสนใจคือ windows.storage.dll


1
ข้อมูลที่น่าสนใจ อย่างไรก็ตามดูเหมือนว่าจะเป็นความคิดเห็นมากกว่าคำตอบ ขณะนี้คุณมีตัวแทนพอที่จะแสดงความคิดเห็นได้ทุกที่ :)
เบน N
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.