Làm cách nào để gán biểu tượng cho các mục menu Sao chép / Cắt / Dán / Xóa của Windows Windows?


12

Trong Windows 8 / 8.1 x64, tôi muốn chỉ định một biểu tượng tùy chỉnh cho các mục menu ngữ cảnh mặc định của Windows như Sao chép , Cắt , Dán , Xóa , Hoàn tác , Làm lạiGửi đến các mục, theo mặc định có bất kỳ biểu tượng nào:

nhập mô tả hình ảnh ở đây

Tôi có thể định vị "tham chiếu" cho các mục menu ngữ cảnh trong sổ đăng ký sau đó thêm giá trị đăng ký "biểu tượng" cho chúng ở đâu?

Hay nói cách khác, làm thế nào để gán biểu tượng cho menu tiện ích mở rộng như nơi trú ẩn SendTo ?.

Nghiên cứu


Theo nhận xét của @ Sk8erPeter , dường như:

"Thêm Icongiá trị chuỗi vào các trình xử lý trình đơn ngữ cảnh khác nhau sẽ không hoạt động như khi thêm nó vào một mục tùy chỉnh như vd HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY"


Biểu tượng nào bạn đang đề cập đến? Bạn có một ảnh chụp màn hình?
Raystafarian

@Raystafarian Tôi đã cập nhật câu hỏi bằng một hình ảnh.
ElektroStudios

1
@Raystafarian: câu hỏi là làm thế nào để thêm biểu tượng tùy chỉnh vào các mục menu ngữ cảnh cơ bản hiện có như "Cắt" , "Sao chép" , "Xóa" , "Đổi tên" , v.v. BTW khi thêm một mục tùy chỉnh mới vào menu ngữ cảnh, nó rất dễ dàng, bởi vì bạn chỉ phải thêm IconGiá trị chuỗi trong một khóa như HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(và giá trị của giá trị Iconsẽ giống như ví dụ %SystemRoot%\System32\shell32.dll,-133hoặc sg. khác). NHƯNG việc thêm Icongiá trị chuỗi vào các trình xử lý trình đơn ngữ cảnh khác nhau không hoạt động như khi thêm nó vào các mục tùy chỉnh này.
Sk8erPeter

Dưới đây là một ảnh chụp màn hình khác để làm cho nó rõ ràng (phần thú vị nằm ở viền đỏ): i.imgur.com/fmewg6L.png . Như bạn có thể thấy, tôi có một số mục tùy chỉnh trong menu ngữ cảnh với các biểu tượng tùy chỉnh (như "Mở bằng Notepad ++" ) - đây chính xác là những gì chúng tôi muốn đạt được với các mục menu ngữ cảnh hệ thống hiện có!
Sk8erPeter

1
@ Sk8erPeter Dẫn đầu tốt nhất của tôi tại thời điểm này là triển vọng tạo ra một trình xử lý trình đơn ngữ cảnh shell sử dụng SetMenuItemInfođể đáp ứng QueryContextMenu.
Bến N

Câu trả lời:


10

Thông báo liên kết: Tôi là tác giả của phần mềm được đề cập trong câu trả lời này.

Trước tiên, tôi sẽ cho bạn biết rằng tôi đã học C ++ và Win32 chỉ cho câu hỏi này .

Tôi đã phát triển một phần mở rộng shell 64 bit được đăng ký như một trình xử lý menu ngữ cảnh. Khi được gọi, nó lục lọi các mục menu hiện có, tìm kiếm các mục thú vị. Nếu nó tìm thấy một cái, nó sẽ dán một biểu tượng trên đó (cái này phải được tải trước đó). Hiện tại, nó tìm kiếm Sao chép , Cắt , Xóa , Dán , Làm lại , Gửi đếnHoàn tác . Bạn có thể thêm của riêng bạn bằng cách sửa đổi mã; thủ tục này được mô tả dưới đây. (Xin lỗi, tôi không đủ giỏi về C ++ để biến nó thành cấu hình.)

Một ảnh chụp màn hình của nó trong hành động, với các biểu tượng xấu nhất mà con người biết đến:

trong hành động

Bạn có thể tải xuống các biểu tượng này nếu bạn thực sự muốn.

Thiết lập nó

Tải xuống (từ Dropbox của tôi). Lưu ý : tệp này được phát hiện bởi một trình quét VirusTotal là một dạng phần mềm độc hại. Điều này là dễ hiểu, dựa trên những điều nó phải làm để đánh vào các mục hiện có. Tôi nói với bạn rằng nó không có hại cho máy tính của bạn. Nếu bạn nghi ngờ và / hoặc bạn muốn sửa đổi và mở rộng nó, hãy xem mã trên GitHub !

Tạo một thư mục trong ổ C của bạn : C:\shellicon. Tạo file BMP với các chức danh sau: copy, cut, delete, paste, redo, sendto, undo. (Hy vọng rằng rõ ràng cái nào sẽ làm cái gì.) Những hình ảnh này có thể phải là 16 x 16 pixel (hoặc lớn hơn là cài đặt DPI của bạn làm lề menu), nhưng tôi cũng đã thành công với những cái lớn hơn. Nếu bạn muốn các biểu tượng trông trong suốt, bạn sẽ chỉ cần làm cho nền của chúng có cùng màu với menu ngữ cảnh. (Thủ thuật này cũng được Dropbox sử dụng.) Tôi đã tạo các biểu tượng khủng khiếp của mình bằng MS Paint; các chương trình khác có thể hoặc không thể lưu theo cách tương thích với LoadImageA. 16 x 16 ở độ sâu màu 24 bit ở 96 pixel mỗi inch dường như là tập hợp các thuộc tính hình ảnh đáng tin cậy nhất.

Đặt DLL ở đâu đó có thể truy cập được cho tất cả người dùng, thư mục bạn vừa thực hiện là một lựa chọn tốt. Mở một nhắc nhở quản trị trong thư mục chứa DLL và làm regsvr32 ContextIcons.dll. Điều này tạo ra thông tin đăng ký cho các loại vỏ *, Drive, Directory, và Directory\Background. Nếu bạn muốn loại bỏ phần mở rộng shell, hãy làm regsvr32 /u ContextIcons.dll.

Mã liên quan

Về cơ bản, tiện ích mở rộng chỉ truy vấn mọi văn bản của mục menu ngữ cảnh GetMenuItemInfovà, nếu phù hợp, sẽ điều chỉnh biểu tượng với SetMenuItemInfo.

Visual Studio tạo ra rất nhiều mã bí ẩn kỳ diệu cho các dự án ATL, nhưng đây là nội dung IconInjector.cpp, trong đó thực hiện trình xử lý trình đơn ngữ cảnh:

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

Lưu ý rằng các HBITMAPs không bao giờ được dọn sạch, nhưng điều này không quá quan trọng khi các công cụ của DLL sẽ biến mất khi Explorer tắt. Các biểu tượng hầu như không có bất kỳ bộ nhớ nào.

Nếu bạn đang biên dịch cho 32 bit, tham số đầu tiên GetCommandStringchỉ là UINTthay vì a UINT_PTR.

Nếu bạn thực sự muốn các biểu tượng trong suốt, bạn sẽ phải tạo một cửa sổ với biểu tượng mong muốn, sau đó đặt mii.hBmpItemthành HBMMENU_SYSTEMvà đặt tay cầm vào cửa sổ mii.dwItemData, như được mô tả ở cuối bài viết MSDNMENUITEMINFO . Tôi không thể tìm ra cách tạo cửa sổ từ tiện ích mở rộng. LR_LOADTRANSPARENTTrông đầy hứa hẹn như một lá cờ của LoadImageAnó, nhưng nó có những cạm bẫy riêng - đặc biệt, không hoạt động trừ khi bạn sử dụng bitmap 256 màu.

Nếu bạn gặp vấn đề với tải hình ảnh, hãy thử xóa LR_DEFAULTSIZEcờ khỏi các LoadImageAcuộc gọi.

Ai đó có đủ kỹ năng về C ++ có thể lấy tài nguyên từ các DLL khác và chuyển đổi chúng thành HBITMAPs, nhưng ai đó không phải là tôi.

Sửa đổi nó

Tôi đã viết điều này trong Visual Studio, nơi tôi tin là trình chỉnh sửa tốt nhất cho Windows C ++.

Tải tệp SLN vào Visual Studio 2015 sau khi bạn cài đặt các công cụ C ++. Trong IconInjector.cpp, bạn có thể thêm HBITMAPcác mục ở trên cùng và LoadImageAgọi Initializeđể thêm biểu tượng mới. Trong else ifphần dưới, sử dụng một wcscmpcuộc gọi để tìm kiếm một kết hợp chính xác hoặc một wcsstrcuộc gọi để tìm kiếm sự hiện diện của một chuỗi con. Trong cả hai trường hợp, &đại diện cho vị trí của gạch dưới / máy gia tốc khi sử dụng Shift + F10. Đặt chế độ của bạn thành Phát hành và kiến ​​trúc của bạn thành x64 và thực hiện BuildBuild Solution . Bạn sẽ gặp lỗi về việc không đăng ký đầu ra, nhưng đừng lo lắng; dù sao bạn cũng muốn làm điều này bằng tay. End Explorer, sao chép DLL mới ( \x64\Release\ContextIcons.dlltrong thư mục giải pháp) vào vị trí, sau đó thực hiện regsvr32điệu nhảy.

Phân phối

Rất cám ơn các nhà văn MSDN, và người tạo ra " Hướng dẫn hoàn chỉnh của kẻ ngốc để viết phần mở rộng Shell ", mà tôi đã tham khảo rất nhiều.

Eulogy

Đối với nhiều phiên bản Explorer đã bị giết trong quá trình sản xuất tiện ích mở rộng này: bạn đã chết vì một lý do lớn, rằng một số người trên Internet có thể có các biểu tượng bên cạnh lời nói của họ.


Ồ Tôi thực sự đánh giá cao những nỗ lực của bạn, cảm ơn bạn rất nhiều! (+1) Tôi đã cố gắng hết sức nhưng không thể làm cho phiên bản được biên dịch hoạt động trên Windows 10 (Build 10240). Tôi không biết vấn đề là gì, tất cả các hình ảnh bmp tồn tại ở đúng đường dẫn ( C:\shellicon\copy.bmpv.v. - đây là các biểu tượng 20x20 pixel ở định dạng BMP) và tôi đã đăng ký dll với tư cách quản trị viên trong dấu nhắc lệnh regsvr32 ContextIcons.dllđã chạy thành công, nhưng Tôi thấy không có thay đổi trong menu ngữ cảnh. Tôi thậm chí đã khởi động lại máy tính, không đăng ký và đăng ký lại dll một lần nữa, nhưng không có thay đổi. Tôi đang cố gắng biên dịch nguồn trong VS2015!
Sk8erPeter

@ Sk8erPeter MSDN nói rằng các biểu tượng cần phải là 16x16, nhưng 20x20 hoạt động với tôi. Có lẽ Windows 10 yêu cầu 16x16? Lưu ý rằng bạn phải khởi động lại Explorer để các thay đổi có hiệu lực.
Ben N

2
@ Sk8erPeter Chắc chắn, bạn đi đây . Tôi sẽ thấy về việc đưa mã lên trên GitHub. Đang tải xuống Windows 10 ngay bây giờ ...
Ben N

2
Bạn sẽ không tin điều đó ... CÔNG TRÌNH NÓ với hình ảnh của bạn! : D: D Điều đó có nghĩa là tôi có một số tệp bmp mà Windows không thể xử lý, không biết tại sao (sau này tôi cũng sẽ kiểm tra điều đó). Dù sao, cảm ơn bạn rất nhiều, mã của bạn thực sự giải quyết vấn đề! :)
Sk8erPeter

1
@BenN: OK, cảm ơn! :) Nó sẽ thuận tiện hơn một chút. Trong khi đó, BTW tôi nhận ra rằng nếu tôi mở các hình ảnh không hoạt động trước đây của mình trong Paint huyền thoại và tôi thực hiện "Lưu dưới dạng"> "Bitmap 24 bit (.bmp; .dip)" (vì vậy hãy lưu nó vào tệp BMP một lần nữa), và tôi sử dụng tệp mới này làm hình ảnh nguồn, nó hoạt động. Tất nhiên, kích thước của bitmap phải chính xác là 16x16 pixel. Vì vậy, Paint tạo định dạng bitmap dự kiến ​​là 24 bit cho mỗi pixel (16,7 triệu màu), kích thước 96x96 DPI và 16x16 pixel. Trước đây tôi đã chuyển đổi và thay đổi kích thước các tệp .png trong IrfanView thành các tệp .bmp, các biểu tượng này không hoạt động.
Sk8erPeter

1

Tôi không có đủ đại diện để để lại nhận xét nhưng có vẻ như thông tin này được chứa trong shell32.dll. Các tập tin đã được biên dịch để thật khó để xem những chức năng nào trong đó, nhưng dường như nó là một chức năng.

Quan tâm (xuất khẩu đăng ký):

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

(Mặc định) REG_SZ Sao chép / Di chuyển / Đổi tên / Xóa / Liên kết đối tượng

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

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

Trong khóa InProcServer32, nó tham chiếu shell32.dll. Có một vài cái khác với tên âm thanh có liên quan. Cũng có thể quan tâm là windows.st Storage.dll


1
Thông tin thú vị. Tuy nhiên, nó dường như là một bình luận hơn là một câu trả lời. Bây giờ bạn có đủ đại diện để bình luận ở mọi nơi :)
Ben N
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.