Ler / gravar propriedades de arquivo 'estendido' (C #)

104

Estou tentando descobrir como ler / gravar nas propriedades estendidas do arquivo em C #, por exemplo, comentário, taxa de bits, data de acesso, categoria etc. que você pode ver no Windows Explorer. Alguma idéia de como fazer isso? EDITAR: Estarei principalmente lendo / gravando em arquivos de vídeo (AVI / DIVX / ...)

David Hayes
fonte
3
Essa questão claramente não foi respondida, pois a resposta aceita apenas mostra como obter as propriedades estendidas e não como defini-las .
Dmitri Nesteruk
1
Para definir as propriedades estendidas, consulte stackoverflow.com/questions/5337683/…
VoteCoffee

Respostas:

83

Para quem não gosta de VB, aqui está em c #:

Observe que você deve adicionar uma referência a Controles e automação do Microsoft Shell na guia COM da caixa de diálogo Referências.

public static void Main(string[] args)
{
    List<string> arrHeaders = new List<string>();

    Shell32.Shell shell = new Shell32.Shell();
    Shell32.Folder objFolder;

    objFolder = shell.NameSpace(@"C:\temp\testprop");

    for( int i = 0; i < short.MaxValue; i++ )
    {
        string header = objFolder.GetDetailsOf(null, i);
        if (String.IsNullOrEmpty(header))
            break;
        arrHeaders.Add(header);
    }

    foreach(Shell32.FolderItem2 item in objFolder.Items())
    {
        for (int i = 0; i < arrHeaders.Count; i++)
        {
            Console.WriteLine(
              $"{i}\t{arrHeaders[i]}: {objFolder.GetDetailsOf(item, i)}");
        }
    }
}
csharptest.net
fonte
3
como alguém definiria um desses valores? por exemplo, autor ou editor de um arquivo .txt. Estou na win 7 e usei isso e mostra um autor e editor em branco e 282 outras propriedades
Vaibhav Garg
1
@Vainbhav - Você não pode definir isso.
csharptest.net
26
Você deve adicionar uma referência aos controles e automação do Microsoft Shell na guia COM da caixa de diálogo Refences.
csharptest.net
2
Como CONFIGURÁ-los é melhor abordado nesta questão: stackoverflow.com/questions/5337683/…
Nicolas Raoul
1
No Windows 10, ele retorna apenas 52 campos não incluindo taxa de quadros, altura do quadro, largura, ....?
Minh Nguyen
27

um artigo CodeProject para um leitor ID3. E um tópico em kixtart.org que tem mais informações para outras propriedades. Basicamente, você precisa chamar o GetDetailsOf()método no objeto de shell da pasta para shell32.dll.

Mark Cidade
fonte
Obrigado, devo ser capaz de reunir o que preciso com isso
David Hayes,
26

Solução 2016

Adicione os seguintes pacotes NuGet ao seu projeto:

  • Microsoft.WindowsAPICodePack-Shell pela Microsoft
  • Microsoft.WindowsAPICodePack-Core pela Microsoft

Propriedades de leitura e gravação

using Microsoft.WindowsAPICodePack.Shell;
using Microsoft.WindowsAPICodePack.Shell.PropertySystem;

string filePath = @"C:\temp\example.docx";
var file = ShellFile.FromFilePath(filePath);

// Read and Write:

string[] oldAuthors = file.Properties.System.Author.Value;
string oldTitle = file.Properties.System.Title.Value;

file.Properties.System.Author.Value = new string[] { "Author #1", "Author #2" };
file.Properties.System.Title.Value = "Example Title";

// Alternate way to Write:

ShellPropertyWriter propertyWriter =  file.Properties.GetPropertyWriter();
propertyWriter.WriteProperty(SystemProperties.System.Author, new string[] { "Author" });
propertyWriter.Close();

Importante:

O arquivo deve ser válido, criado pelo software específico atribuído. Cada tipo de arquivo tem propriedades de arquivo estendidas específicas e nem todos são graváveis.

Se você clicar com o botão direito do mouse em um arquivo na área de trabalho e não conseguir editar uma propriedade, não será possível editá-lo no código também.

Exemplo:

  • Crie o arquivo txt no desktop, renomeie sua extensão para docx. Você não pode editar sua propriedade Authorou Title.
  • Abra-o com o Word, edite e salve. Agora você pode.

Portanto, certifique-se de usar alguns try catch

Tópico adicional: MSDN: Implementando Manipuladores de Propriedades

Martin Schneider
fonte
Não vejo nenhum pacote com esses nomes da Microsoft
Jacob Brewer
1
@JacobBrewer Na verdade, é aquele que foi carregado por "acdvorak": nuget.org/packages/Microsoft.WindowsAPICodePack-Shell . No Visual Studio NuGet-Package-Manager, ele é mostrado como "pela Microsoft". Os outros pacotes com nomes semelhantes são garfos dele e também podem funcionar corretamente ou até melhor. Não sei ...
Martin Schneider
25

Este exemplo em VB.NET lê todas as propriedades estendidas:

Sub Main()
        Dim arrHeaders(35)

        Dim shell As New Shell32.Shell
        Dim objFolder As Shell32.Folder

        objFolder = shell.NameSpace("C:\tmp")

        For i = 0 To 34
            arrHeaders(i) = objFolder.GetDetailsOf(objFolder.Items, i)
        Next
        For Each strFileName In objfolder.Items
            For i = 0 To 34
                Console.WriteLine(i & vbTab & arrHeaders(i) & ": " & objfolder.GetDetailsOf(strFileName, i))
            Next
        Next

    End Sub

Você deve adicionar uma referência a Controles e automação do Microsoft Shell na guia COM da caixa de diálogo Referências .

Dirk Vollmar
fonte
2
Vale a pena notar que no Windows 7 (pelo menos) você pode aumentar o tamanho de arrHeaders para pouco mais de 280 e obter muitos metadados adicionais. Eu descobri isso quando estava procurando uma maneira de obter metadados de um arquivo WTV (programa de televisão gravado do Windows 7 Media Center).
Richard,
1
O primeiro para i = 0 a 34 loop deve ser para i = 0 para Integer.MaxValue. Em seguida, teste o valor de retorno de objFolder.GetDetailsOf (objFolder.Items, i). Se retornar nulo ou espaço em branco, você terá todos os cabeçalhos.
Tim Murphy
@TimMurphy, infelizmente, isso não está correto (pelo menos no Windows 10 ou 7). Alguns cabeçalhos estão vazios, então você precisa percorrer todos os índices. (Claro, como não há milhões deles, você poderia limitar o loop a 1.000.)
skst
8

Obrigado pessoal por este tópico! Isso me ajudou quando eu queria descobrir a versão de um arquivo exe. No entanto, eu mesmo precisava descobrir a última parte do que é chamado de Propriedades estendidas.

Se você abrir as propriedades de um arquivo exe (ou dll) no Windows Explorer, obterá uma guia Versão e uma visualização das Propriedades estendidas desse arquivo. Eu queria acessar um desses valores.

A solução para isso é o indexador de propriedade FolderItem.ExtendedProperty e se você descartar todos os espaços no nome da propriedade, obterá o valor. Por exemplo, a versão do arquivo se torna FileVersion, e aí está.

Espero que isso ajude mais alguém, apenas pensei em adicionar esta informação a este tópico. Felicidades!

JERKER
fonte
2
Isso também garante que seu código (pelo menos a maior parte) ainda funcionará em máquinas de idioma diferente do inglês.
Ed Norris
1
Uma pequena melhoria aqui, FolderItem não contém ExtendedProperty (). FolderItem2, entretanto.
Mixxiphoid
Essa resposta é um pouco mais simples do que outras.
Forneci um
8

GetDetailsOf()Método - recupera detalhes sobre um item em uma pasta. Por exemplo, seu tamanho, tipo ou a hora de sua última modificação. As propriedades do arquivo podem variar de acordo com a Windows-OSversão.

List<string> arrHeaders = new List<string>();

 Shell shell = new ShellClass();
 Folder rFolder = shell.NameSpace(_rootPath);
 FolderItem rFiles = rFolder.ParseName(filename);

 for (int i = 0; i < short.MaxValue; i++)
 {
      string value = rFolder.GetDetailsOf(rFiles, i).Trim();
      arrHeaders.Add(value);
 }
RajeshKdev
fonte
Copiei e usei o mesmo código acima. Mas, estou recebendo exceção COM em tempo de execução aqui Folder rFolder = shell.NameSpace(_rootPath);. Para sua informação, estou usando o sistema operacional Windows 8.
DonMax
1
Que erro é esse? Verifique se você está usando a versão correta do Shell32.dll. Verifique isto talvez seja útil
RajeshKdev
1
+1 para o link acima. O link que você sugeriu está funcionando bem. Preciso de mais uma ajuda sua. ou seja, como posso passar o arquivo único para obter os metadados ?? já que aceita passar apenas a pasta.
DonMax
6
  • Depois de examinar várias soluções neste segmento e em outros lugares, o código a seguir foi colocado junto. Isso é apenas para ler uma propriedade.
  • Não consegui fazer com que a função Shell32.FolderItem2.ExtendedProperty funcionasse, ela deveria pegar um valor de string e retornar o valor e o tipo correto para essa propriedade ... isso sempre foi nulo para mim e os recursos de referência do desenvolvedor eram muito limitados.
  • O WindowsApiCodePack parece ter sido abandonado pela Microsoft, o que nos traz o código abaixo.

Usar:

string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
  1. Retornará a você o valor da propriedade estendida que você deseja como uma string para o arquivo e o nome da propriedade fornecidos.
  2. Só faz um loop até encontrar a propriedade especificada - não até que todas as propriedades sejam descobertas como algum código de amostra
  3. Funcionará em versões do Windows como o Windows server 2008, onde você obterá o erro "Não foi possível lançar o objeto COM do tipo 'System .__ ComObject' para o tipo de interface 'Shell32.Shell'" se apenas tentar criar o objeto Shell32 normalmente.

    public static string GetExtendedFileProperty(string filePath, string propertyName)
    {
        string value = string.Empty;
        string baseFolder = Path.GetDirectoryName(filePath);
        string fileName = Path.GetFileName(filePath);
    
        //Method to load and execute the Shell object for Windows server 8 environment otherwise you get "Unable to cast COM object of type 'System.__ComObject' to interface type 'Shell32.Shell'"
        Type shellAppType = Type.GetTypeFromProgID("Shell.Application");
        Object shell = Activator.CreateInstance(shellAppType);
        Shell32.Folder shellFolder = (Shell32.Folder)shellAppType.InvokeMember("NameSpace", System.Reflection.BindingFlags.InvokeMethod, null, shell, new object[] { baseFolder });
    
        //Parsename will find the specific file I'm looking for in the Shell32.Folder object
        Shell32.FolderItem folderitem = shellFolder.ParseName(fileName);
        if (folderitem != null)
        {
            for (int i = 0; i < short.MaxValue; i++)
            {
                //Get the property name for property index i
                string property = shellFolder.GetDetailsOf(null, i);
    
                //Will be empty when all possible properties has been looped through, break out of loop
                if (String.IsNullOrEmpty(property)) break;
    
                //Skip to next property if this is not the specified property
                if (property != propertyName) continue;    
    
                //Read value of property
                value = shellFolder.GetDetailsOf(folderitem, i);
            }
        }
        //returns string.Empty if no value was found for the specified property
        return value;
    }
Rohan
fonte
1
Observe que em vez de usar InvokeMember () , você pode lançar shellpara qualquer uma das IShellDispatchinterfaces (1-6) e chamar o membro diretamente. Shell32.IShellDispatch ishell = (Shell32.IShellDispatch)shell; Shell32.Folder shellFolder = ishell.NameSpace(baseFolder);
skst
5

A resposta de Jerker é um pouco mais simples. Aqui está um exemplo de código que funciona da MS :

var folder = new Shell().NameSpace(folderPath);
foreach (FolderItem2 item in folder.Items())
{
    var company = item.ExtendedProperty("Company");
    var author = item.ExtendedProperty("Author");
    // Etc.
}

Para aqueles que não podem fazer referência ao shell32 estaticamente, você pode invocá-lo dinamicamente desta forma:

var shellAppType = Type.GetTypeFromProgID("Shell.Application");
dynamic shellApp = Activator.CreateInstance(shellAppType);
var folder = shellApp.NameSpace(folderPath);
foreach (var item in folder.Items())
{
    var company = item.ExtendedProperty("Company");
    var author = item.ExtendedProperty("Author");
    // Etc.
}
Nawfal
fonte
1

Não tenho certeza para quais tipos de arquivos você está tentando escrever as propriedades, mas taglib-sharp é uma excelente biblioteca de etiquetagem de código aberto que engloba todas essas funcionalidades de maneira adequada. Ele tem muito suporte integrado para a maioria dos tipos de arquivos de mídia populares, mas também permite que você faça marcações mais avançadas com praticamente qualquer arquivo.

EDIT: Eu atualizei o link para taglib sharp. O link antigo não funcionava mais.

EDIT: Atualizado o link mais uma vez com o comentário de kzu.

mockobject
fonte
Isso parece muito interessante, estarei olhando principalmente para arquivos de vídeo (AVI, DIVX etc). Obrigado pela indicação
David Hayes,
O link taglib-sharp parece estar morto :-( - o que é estranho como o wiki em ... developer.novell.com/wiki/index.php/TagLib_Sharp:_Examples ... aponta para esse URL
SteveC
Obrigado, SteveC, no momento em que postei este ambos os links eram válidos e eu não tinha certeza de qual era o lugar oficial para ir, parece que a novell é o site certo para ir para esta lib agora.
mockobject
Obrigado pelo aviso kzu, atualizei o link na minha resposta original para apontar para a localização do github também.
mockobject
1

Aqui está uma solução para ler - não escrever - as propriedades estendidas com base no que encontrei nesta página e na ajuda com objetos shell32 .

Para ficar claro, isso é um hack. Parece que esse código ainda será executado no Windows 10, mas chegará a algumas propriedades vazias. A versão anterior do Windows deve usar:

        var i = 0;
        while (true)
        {
            ...
            if (String.IsNullOrEmpty(header)) break;
            ...
            i++;

No Windows 10, presumimos que haja cerca de 320 propriedades para ler e simplesmente ignoramos as entradas vazias:

    private Dictionary<string, string> GetExtendedProperties(string filePath)
    {
        var directory = Path.GetDirectoryName(filePath);
        var shell = new Shell32.Shell();
        var shellFolder = shell.NameSpace(directory);
        var fileName = Path.GetFileName(filePath);
        var folderitem = shellFolder.ParseName(fileName);
        var dictionary = new Dictionary<string, string>();
        var i = -1;
        while (++i < 320)
        {
            var header = shellFolder.GetDetailsOf(null, i);
            if (String.IsNullOrEmpty(header)) continue;
            var value = shellFolder.GetDetailsOf(folderitem, i);
            if (!dictionary.ContainsKey(header)) dictionary.Add(header, value);
            Console.WriteLine(header +": " + value);
        }
        Marshal.ReleaseComObject(shell);
        Marshal.ReleaseComObject(shellFolder);
        return dictionary;
    }

Conforme mencionado, você precisa fazer referência ao assembly Com Interop.Shell32.

Se você obtiver uma exceção relacionada ao STA, encontrará a solução aqui:

Exceção ao usar Shell32 para obter propriedades estendidas de arquivo

Não tenho ideia de como seriam esses nomes de propriedades em um sistema estrangeiro e não consegui encontrar informações sobre quais constantes localizáveis ​​usar para acessar o dicionário. Também descobri que nem todas as propriedades da caixa de diálogo Propriedades estavam presentes no dicionário retornado.

Aliás, isso é terrivelmente lento e - pelo menos no Windows 10 - analisar datas na string recuperada seria um desafio, portanto, usar isso parece uma má ideia para começar.

No Windows 10, você definitivamente deve usar a biblioteca Windows.Storage que contém SystemPhotoProperties, SystemMusicProperties etc. https://docs.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties

E, finalmente, postei uma solução muito melhor que usa WindowsAPICodePack

pasx
fonte