Como atribuir um ícone aos itens de menu de contexto padrão do Windows "Copiar / Cortar / Colar / Excluir"?

12

No Windows 8 / 8.1 x64, gostaria de atribuir um ícone personalizado aos itens de menu de contexto padrão do Windows, como Copiar , Recortar , Colar , Excluir , Desfazer , Refazer e Enviar para itens, que por padrão possuem qualquer ícone:

insira a descrição da imagem aqui

Onde posso localizar a "referência" para esses itens do menu de contexto no registro e adicionar um valor de registro "ícone" para eles?

Ou, em outras palavras, como atribuir um ícone a um menu de extensão de shell como o SendTo shellex ?.

Pesquisa


Como comentado por @ Sk8erPeter , parece que:

"Adicionar o Iconvalor da string a diferentes manipuladores do menu de contexto não funciona como ao adicioná-lo a um item personalizado, como por exemplo HKEY_CLASSES_ROOT\*\shell\MYCUSTOMKEY"

ElektroStudios
fonte
A que ícone você está se referindo? Você tem uma captura de tela?
Raystafarian
@Raystafarian Atualizei a pergunta com uma imagem.
ElektroStudios
1
@Raystafarian: a questão é como adicionar um ícone personalizado aos itens do menu de contexto básico existentes, como "Recortar" , "Copiar" , "Excluir" , "Renomear" etc. etc. Ao adicionar um novo item personalizado ao menu de contexto, é muito fácil, porque você só precisa adicionar o Iconvalor da string em uma chave como HKEY_CLASSES_ROOT\*\shell\MYCUSTOMITEM(e o valor da Iconseria como eg %SystemRoot%\System32\shell32.dll,-133ou sg. else). MAS adicionar o Iconvalor da string a diferentes manipuladores de menu de contexto não funciona como ao adicioná-lo a esses itens personalizados.
Sk8erPeter
Aqui está outra captura de tela para esclarecer (a parte interessante está em bordas vermelhas): i.imgur.com/fmewg6L.png . Como você pode ver, tenho alguns itens personalizados no menu de contexto com ícones personalizados (como "Abrir com o Notepad ++" ) - é exatamente isso que gostaríamos de obter com os itens de menu de contexto do sistema existentes!
Sk8erPeter
1
@ Sk8erPeter Meu melhor lead no momento é a perspectiva de criar um manipulador de menu de contexto de shell que seja usado SetMenuItemInfoem resposta a QueryContextMenu.
Ben N

Respostas:

10

Aviso de afiliação: sou o autor do software mencionado nesta resposta.

Primeiro, você saberá que aprendi C ++ e Win32 apenas para esta pergunta .

Eu desenvolvi uma extensão de shell de 64 bits que é registrada como um manipulador de menu de contexto. Quando invocado, ele vasculha os itens de menu existentes, procurando entradas interessantes. Se encontrar um, ele colará um ícone nele (que deve ter sido carregado anteriormente). No momento, ele procura Copiar , Recortar , Excluir , Colar , Refazer , Enviar para e Desfazer . Você pode adicionar seu próprio modificando o código; o procedimento para isso é descrito abaixo. (Desculpe, eu não sou bom o suficiente em C ++ para torná-lo configurável.)

Uma captura de tela em ação, com os ícones mais feios conhecidos pelo homem:

em ação

Você pode baixar esses ícones, se realmente quiser.

Configurando

Faça o download (do meu Dropbox). Aviso : este arquivo é detectado por um scanner VirusTotal como sendo algum tipo de malware. Isso é compreensível, dado o tipo de coisa que é necessário fazer para destruir as entradas existentes. Confio na minha palavra de que isso não causa danos intencionais ao seu computador. Se você suspeita e / ou deseja modificá-lo e ampliá-lo, consulte o código no GitHub !

Crie uma pasta na sua unidade C: C:\shellicon. Criar arquivos BMP com os seguintes títulos: copy, cut, delete, paste, redo, sendto, undo. (Espero que seja óbvio qual deles faz qual coisa.) Essas imagens provavelmente devem ter 16 por 16 pixels (ou quão grandes as configurações de DPI fazem a margem do menu), mas também tive sucesso com as maiores. Se você deseja que os ícones pareçam transparentes, basta fazer o plano de fundo da mesma cor do menu de contexto. (Esse truque também é empregado pelo Dropbox.) Fiz meus terríveis ícones com o MS Paint; outros programas podem ou não ser salvos de maneira compatível com LoadImageA. 16 por 16, com profundidade de cor de 24 bits e 96 pixels por polegada, parece ser o conjunto mais confiável de propriedades da imagem.

Coloque a DLL em algum lugar acessível a todos os usuários; essa pasta que você acabou de criar é uma boa escolha. Abra um prompt de administrador na pasta que contém a DLL e faça regsvr32 ContextIcons.dll. Isso cria informações de registro para os tipos de shell *,Drive , Directory, e Directory\Background. Se você quiser remover a extensão do shell, faça regsvr32 /u ContextIcons.dll.

Código relevante

Basicamente, a extensão consulta apenas o texto de cada item de menu de contexto com GetMenuItemInfo e, se apropriado, ajusta o ícone com SetMenuItemInfo.

O Visual Studio gera muitos códigos misteriosos mágicos para projetos ATL, mas esse é o conteúdo deIconInjector.cpp , que implementa o manipulador de menu de contexto:

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

Observe que os HBITMAPs nunca são limpos, mas isso não importa muito, pois o material da DLL desaparece quando o Explorer é encerrado. Os ícones mal tiram qualquer memória de qualquer maneira.

Se você está compilando para 32 bits, o primeiro parâmetro para GetCommandStringé apenas um em UINTvez de umUINT_PTR .

Se você realmente quer ícones transparentes, você terá que criar uma janela com o ícone desejado e, em seguida, definir mii.hBmpItema HBMMENU_SYSTEMe colocou a alça para a janela mii.dwItemData, como descrito na parte inferior do artigo do MSDN sobreMENUITEMINFO . Não consegui descobrir como criar janelas a partir de extensões de shell. LR_LOADTRANSPARENTparece promissor como bandeira LoadImageA, mas tem suas próprias armadilhas - especificamente, não funciona, a menos que você use bitmaps de 256 cores.

Se você tiver problemas com o carregamento de imagens, tente remover o LR_DEFAULTSIZE sinalizador das LoadImageAchamadas.

Alguém com experiência em C ++ provavelmente poderia pegar recursos de outras DLLs e convertê-los em HBITMAP s, mas esse alguém não sou eu.

Modificando-o

Eu escrevi isso no Visual Studio, que acredito ser o melhor editor para Windows C ++.

Carregue o arquivo SLN no Visual Studio 2015 depois de instalar as ferramentas C ++. Em IconInjector.cpp, você pode adicionar HBITMAPentradas na parte superior e fazer LoadImageAchamadas Initializepara adicionar novos ícones. Na else ifseção, use uma wcscmpchamada para procurar uma correspondência exata ou uma wcsstrchamada para procurar a presença de uma subsequência. Nos dois casos, o &representa a posição do sublinhado / acelerador ao usar Shift + F10. Defina seu modo como Release e sua arquitetura como x64 e faça BuildBuild Solution . Você receberá um erro ao não registrar a saída, mas não se preocupe; você faria isso manualmente de qualquer maneira. End Explorer, copie a nova DLL ( \x64\Release\ContextIcons.dllna pasta da solução) para o local e faça oregsvr32 dança.

Atribuições

Muito obrigado aos escritores do MSDN e ao criador do " Guia Completo para Idiotas de Criação de Extensões de Shell ", ao qual me referi bastante.

Elogio

Nas várias instâncias do Explorer que foram mortas na produção dessa extensão de shell: você morreu por uma grande causa, que algumas pessoas na Internet podem ter ícones ao lado de suas palavras.

Ben N
fonte
Uau! Eu realmente aprecio seus esforços, muito obrigado! (+1) Eu tentei o meu melhor, mas não consegui fazer a versão compilada funcionar no Windows 10 (Build 10240). Não sei qual é o problema, todas as imagens bmp existem no caminho certo ( C:\shellicon\copy.bmp, etc. - são ícones de 20x20 pixels no formato BMP) e registrei a dll como um administrador no prompt de comando com o regsvr32 ContextIcons.dllqual foi executado com êxito, mas Não vejo alterações no menu de contexto. Até reiniciei o computador, não registrei e registrei novamente a dll novamente, mas nenhuma alteração. Estou tentando compilar a fonte no VS2015!
precisa saber é o seguinte
O @ Sk8erPeter MSDN disse que os ícones precisam ser 16x16, mas 20x20 funciona para mim. Talvez o Windows 10 exija 16x16? Observe que você precisa reiniciar o Explorer para que as alterações tenham efeito.
Ben N
2
@ Sk8erPeter Certamente, aqui está . Vou ver sobre como colocar o código no GitHub. Trabalhando com o download do Windows 10 agora ... #
911 Ben N
2
Você não vai acreditar ... FUNCIONA com suas imagens! : D: D Isso significa que eu tenho alguns arquivos bmp que o Windows não suportava, não sei por que (mais tarde vou verificar isso também). De qualquer forma, muito obrigado, seu código realmente resolve o problema! :)
Sk8erPeter
1
@ BenN: OK, obrigado! :) Teria sido um pouco mais conveniente. Enquanto isso, percebi que, se eu abrir minhas imagens anteriormente não funcionadas no lendário Paint, e faço um "Salvar como"> "Bitmap de 24 bits (.bmp; .dip)" (salve-o em um arquivo BMP novamente) e eu uso esse novo arquivo como imagem de origem, ele FUNCIONA. Obviamente, o tamanho do bitmap precisa ser exatamente 16x16 pixels. Portanto, o Paint cria o formato de bitmap esperado, que é de 24 bits por pixel (16,7 milhões de cores), tamanho 96x96 DPI e tamanho 16x16 pixels. Anteriormente, converti e redimensionei arquivos .png no IrfanView para arquivos .bmp, esses ícones não funcionavam.
precisa saber é o seguinte
1

Eu não tenho representante suficiente para deixar um comentário, mas parece que esta informação está contida no shell32.dll. Como os arquivos foram compilados, é difícil ver quais funções estão nele, mas parece ser essa.

De interesse (exportação de registro):

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

(Padrão) REG_SZ Copiar / Mover / Renomear / Excluir / Vincular Objeto

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

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

Sob a chave InProcServer32, ele faz referência a shell32.dll. Existem outros também com nomes relevantes. Possivelmente também é interessante windows.storage.dll

nijave
fonte
1
Informação interessante. No entanto, parece mais um comentário do que uma resposta. Agora você tem rep suficiente para comentar em todos os lugares :)
Ben N