Desenvolvimento - C#
Uso do par de ícones associado ao tipo de arquivo no Sistema Operacional em C#
Neste artigo, exibirei como acessar os ícones associados ao tipo de arquivo no sistema operacional, bem como executar um processo a partir do C#.
por Paulo Henrique dos Santos MonteiroSe não associado a nenhum aplicativo, ainda assim o arquivo terá um par de ícones básico, e ao tentar abri-lo o usuário será perguntado de qual aplicativo utilizar para o feito.
Solução
Diante da ocorrência de dois ou mais posts no fórum C# do MSDN, perguntando sobre como utilizar os ícones padrão do sistema operacional para exibir em listboxes ou treeviews, e ainda como disparar um programa a partir do C#, construí um programa que atende aos dois propósitos. Ao selecionar um arquivo, carrega-se internamente em dois imagelists os ícones grande (32x32) e pequeno (16x16), exibindo o arquivo numa listbox (já com o ícone do SO).
Ao executar um duplo clique no item da listbox, o arquivo é aberto com o programa associado ao mesmo no SO.
Parte I - Ler o ícone do S.O.
Para a leitura do ícone associado ao tipo de arquivo no S.O, fazemos uso da API SHGetFileInfo, da Shell32.DLL. Já utilizei outras APIs da Shell32.DLL, como no exemplo de usercontrol para abrir a common dialog de seleção de pastas, no meu artigo sobre UserControls que consta também no SharePedia.
A DLL SHGetFileInfo é uma API é poderosa, e destina-se a obter informações sobre pastas, diretórios, e drives. A API utiliza como valor para input/output uma instancia de SHFILEINFO, uma struct que iremos declarar mais adiante.
DWORD_PTR SHGetFileInfo( LPCTSTR pszPath, DWORD dwFileAttributes, SHFILEINFO *psfi, UINT cbFileInfo, UINT uFlags );
Em sendo a API poderosa, e destinada a obter uma série de detalhes sobre o objeto, de acordo com a combinação de flags no último parâmetro (uiFlags), irei focar apenas no problema que diz respeito ao artigo, e abordar as demais combinações em outros futuros artigos.
A declaração C# para a nossa API fica da seguinte maneira:
// Wrapper para a chamada a SHGetFileInfo. // Também se podia ter usado a API ExtractAssociatedIcon. [DllImport("shell32.dll")] public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlags);
Nossa estrutura SHFILEINFO é declarada da seguinte maneira:
[StructLayout(LayoutKind.Sequential)] public struct SHFILEINFO { public IntPtr hIcon; public IntPtr iIcon; public uint dwAttributes; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)] public string szDisplayName; [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)] public string szTypeName; };
Explicando a declaração, a necessidade de utilizar LayoutKind.Sequential é explicada para poder manter exatamente a ordem física dos atributos no marshalling com a API. Observe [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]. A estrutura original é declarada como:
typedef struct _SHFILEINFO { HICON hIcon; int iIcon; DWORD dwAttributes; TCHAR szDisplayName[MAX_PATH]; TCHAR szTypeName[80]; } SHFILEINFO;
Observe que temos as duas strings com tamanho fixo. É por isso que usamos SizeConst.
As constantes utilizadas para a leitura são respectivamente, SHGFI_ICON, SHGFI_LARGEICON e SHGFI_SMALLICON. A declaração dos seus valores pode ser obtida a partir do include windows.h do C++, mas segue a declaração C# para as mesmas.
public const uint SHGFI_ICON = 0x100; public const uint SHGFI_LARGEICON = 0x0; // "Large icon public const uint SHGFI_SMALLICON = 0x1; // "Small icon
A combinação necessária para fazer a leitura dos ícones via SHGetFileInfo é:
SHGFI_ICON | SHGFI_SMALLICON - para ler o ícone pequeno. SHGFI_ICON | SHGFI_LARGEICON - para ler o ícone grande.
Após a leitura, a API carrega o ícone em memória, e disponibiliza o handle para o mesmo no campo hIcon da SHFILEINFO.
Tendo o handle em mãos, podemos simplesmente criar o ícone a partir do método estático Icon.FromHandle.
Na solução, criei uma classe chamada IconInformations, que tem internamente duas imagelists, uma para ícones grandes e outra para ícones pequenos, para aproveitar a facilidade do .NET em manter ImageLists atualizáveis em runtime.
Mantenho também internamente uma Hashtable com as extensões lidas, juntamente com o índice da imagem associada nos imagelists.
O usuário manda para o método AddIcone o caminho completo do arquivo. O método separa a extensão do arquivo, e chama internamente o método GetIcone, que obtém o item da Hashtable interna (se existir) . Se não existir, o método executa a chamada interna ao método estático ObtemIconeArquivo, que se encarrega de fazer a chamada da API SHGetFileInfo, e retorna o ícone lido. Após lidas, as imagens são carregadas nas imagelists internas.
A partir deste momento, podemos utilizar as imagelists em nosso programa, associando respectivamente a LargeImageList e SmallImageList na listview.
Parte II - Executar um arquivo a partir do C#
Executar um outro programa significa executar um novo processo.
Traduzindo para C#, significa disparar (start) um novo processo (process). Esta funcionalidade é provida pelo método estático System.Diagnostics.Process.Start.
Como estamos abordando abrir o arquivo da listview a partir do programa associado a extensão no Sistema Operacional, basta chamar o método passando o nome do arquivo como parâmetro. Para executar um programa, basta colocar o path/nome do programa + extensão.
Conclusão
Neste artigo, exibi como acessar os ícones associados ao tipo de arquivo no sistema operacional, bem como executar um processo a partir do C#.
A API SHGetFileInfo é poderosa, e destina-se a obter diversas informações a respeito de um objeto (drive, pasta, arquivo, diretório). As combinações para leitura da informação são muitas, e abordamos apenas a obtenção de ícones neste artigo. Consulte o MSDN (msdn.microsoft.com) para maiores informações, ou aguarde o meu próximo artigo.