Propiedades de archivo de lectura / escritura 'extendidas' (C #)

104

Estoy tratando de averiguar cómo leer / escribir en las propiedades extendidas del archivo en C #, por ejemplo, comentario, velocidad de bits, fecha de acceso, categoría, etc. que puede ver en el explorador de Windows. ¿Alguna idea de como hacer esto? EDITAR: principalmente leeré / escribiré en archivos de video (AVI / DIVX / ...)

David Hayes
fuente
3
Esta pregunta claramente no tiene respuesta, ya que la respuesta aceptada solo muestra cómo obtener las propiedades extendidas y no cómo configurarlas .
Dmitri Nesteruk
1
Para configurar las propiedades extendidas, consulte stackoverflow.com/questions/5337683/…
VoteCoffee

Respuestas:

83

Para aquellos que no están locos por VB, aquí está en c #:

Tenga en cuenta que debe agregar una referencia a Microsoft Shell Controls and Automation desde la pestaña COM del cuadro de diálogo References.

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
fuente
3
¿Cómo se establecería uno de estos valores? por ejemplo, Autor o editor para un archivo .txt. Estoy en win 7 y usé esto y muestra un autor y editor en blanco y otras 282 propiedades
Vaibhav Garg
1
@Vainbhav: no se pueden configurar.
csharptest.net
26
Debe agregar una referencia a Microsoft Shell Controls and Automation desde la pestaña COM del cuadro de diálogo Refences.
csharptest.net
2
Cómo configurarlos está mejor cubierto en esta pregunta: stackoverflow.com/questions/5337683/…
Nicolas Raoul
1
En Windows 10 solo devuelve 52 campos sin incluir la velocidad de fotogramas, la altura del fotograma, el ancho, ...
Minh Nguyen
26

Solución 2016

Agregue los siguientes paquetes de NuGet a su proyecto:

  • Microsoft.WindowsAPICodePack-Shell por Microsoft
  • Microsoft.WindowsAPICodePack-Core por Microsoft

Propiedades de lectura y escritura

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:

El archivo debe ser válido, creado por el software asignado específico. Cada tipo de archivo tiene propiedades de archivo extendidas específicas y no todos se pueden escribir.

Si hace clic con el botón derecho en un archivo en el escritorio y no puede editar una propiedad, tampoco podrá editarlo en código.

Ejemplo:

  • Cree un archivo txt en el escritorio, cambie el nombre de su extensión a docx. No puede editar su propiedad Authoro Title.
  • Ábrelo con Word, edítalo y guárdalo. Ahora usted puede.

Así que asegúrate de usar algunos try catch

Tema adicional: MSDN: implementación de controladores de propiedades

Martin Schneider
fuente
No veo ningún paquete con estos nombres de Microsoft
Jacob Brewer
1
@JacobBrewer En realidad, es el que subió "acdvorak": nuget.org/packages/Microsoft.WindowsAPICodePack-Shell . En Visual Studio NuGet-Package-Manager se muestra como "por Microsoft". Los otros paquetes con nombres similares son bifurcaciones y también podrían funcionar correctamente o incluso mejor. No lo sé ...
Martin Schneider
25

Esta muestra en VB.NET lee todas las propiedades extendidas:

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

Debe agregar una referencia a Microsoft Shell Controls and Automation desde la pestaña COM del cuadro de diálogo Referencias .

Dirk Vollmar
fuente
2
Vale la pena señalar que en Windows 7 (al menos) puede aumentar el tamaño de arrHeaders a poco más de 280 y recuperar muchos metadatos adicionales. Descubrí esto cuando estaba buscando una forma de obtener metadatos de un archivo WTV (programa de televisión grabado en Windows 7 Media Center).
Richard
1
El primero para i = 0 a 34 bucle debe ser para i = 0 a Integer.MaxValue. Luego, pruebe el valor de retorno de objFolder.GetDetailsOf (objFolder.Items, i). Si devuelve un espacio en blanco o nulo, entonces tiene todos los encabezados.
Tim Murphy
@TimMurphy, desafortunadamente, eso no es correcto (al menos en Windows 10 o 7). Algunos encabezados están vacíos, por lo que debe recorrer todos los índices. (Por supuesto, dado que no hay millones de ellos, puede limitar el ciclo a 1,000.)
skst
8

¡Gracias chicos por este hilo! Me ayudó cuando quise averiguar la versión de un archivo exe. Sin embargo, necesitaba descubrir la última parte de lo que se llama Propiedades extendidas.

Si abre las propiedades de un archivo exe (o dll) en el Explorador de Windows, obtendrá una pestaña Versión y una vista de Propiedades extendidas de ese archivo. Quería acceder a uno de esos valores.

La solución a esto es el indexador de propiedades FolderItem.ExtendedProperty y si coloca todos los espacios en el nombre de la propiedad, obtendrá el valor. Por ejemplo, File Version pasa a FileVersion, y ahí lo tiene.

Espero que esto ayude a alguien más, solo pensé en agregar esta información a este hilo. ¡Salud!

JERKER
fuente
2
Esto también asegura que su código (al menos en su mayoría) seguirá funcionando en máquinas que no estén en inglés.
Ed Norris
1
Una pequeña mejora aquí, FolderItem no contiene ExtendedProperty (). FolderItem2 sí lo hace.
Mixxiphoid
Esta respuesta es un poco más simple que otras. He dado un código de muestra que funciona aquí: stackoverflow.com/a/46648086/661933
nawfal
8

GetDetailsOf()Método: recupera detalles sobre un elemento en una carpeta. Por ejemplo, su tamaño, tipo o la hora de su última modificación. Las propiedades del archivo pueden variar según la Windows-OSversión.

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
fuente
He copiado y usado el mismo código anterior. Pero, obtengo una excepción COM en tiempo de ejecución aquí Folder rFolder = shell.NameSpace(_rootPath);. FYI, estoy usando el sistema operativo Windows 8.
DonMax
1
¿Cuál es ese error? Asegúrese de que está utilizando la versión correcta de Shell32.dll. Verifique esto, tal vez sea útil
RajeshKdev
1
+1 para el enlace anterior. El enlace que sugirió funciona bien. Necesito una ayuda más de ti. es decir, ¿cómo puedo pasar el archivo único para obtener los metadatos? ya que acepta pasar solo la carpeta.
DonMax
6
  • Después de buscar una serie de soluciones en este hilo y en otros lugares, se reunió el siguiente código. Esto es solo para leer una propiedad.
  • No pude hacer que la función Shell32.FolderItem2.ExtendedProperty funcione, se supone que debe tomar un valor de cadena y devolver el valor y el tipo correctos para esa propiedad ... esto siempre fue nulo para mí y los recursos de referencia del desarrollador eran muy delgados.
  • El WindowsApiCodePack parece haber sido abandonado por Microsoft, que nos trae el siguiente código.

Utilizar:

string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
  1. Le devolverá el valor de la propiedad extendida que desea como una cadena para el archivo y el nombre de propiedad dados.
  2. Solo se repite hasta que encuentra la propiedad especificada, no hasta que todas las propiedades se descubren como un código de muestra
  3. Funcionará en versiones de Windows como Windows Server 2008, donde obtendrá el error "No se puede convertir el objeto COM del tipo 'System .__ ComObject' al tipo de interfaz 'Shell32.Shell'" si solo intenta crear el 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
fuente
1
Tenga en cuenta que en lugar de usar InvokeMember () , puede enviarshell a cualquiera de las IShellDispatchinterfaces (1-6) y llamar al miembro directamente. Shell32.IShellDispatch ishell = (Shell32.IShellDispatch)shell; Shell32.Folder shellFolder = ishell.NameSpace(baseFolder);
skst
5

La respuesta de Jerker es un poco más simple. Aquí hay un código de muestra que funciona desde 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 aquellos que no pueden hacer referencia a shell32 estáticamente, pueden invocarlo dinámicamente de esta manera:

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
fuente
1

No estoy seguro de para qué tipos de archivos está tratando de escribir las propiedades, pero taglib-sharp es una excelente biblioteca de etiquetas de código abierto que resume muy bien toda esta funcionalidad. Tiene una gran cantidad de soporte integrado para la mayoría de los tipos de archivos multimedia populares, pero también le permite realizar un etiquetado más avanzado con prácticamente cualquier archivo.

EDITAR: He actualizado el enlace a taglib sharp. El enlace anterior ya no funcionaba.

EDITAR: Se actualizó el enlace una vez más por el comentario de kzu.

objeto simulado
fuente
Esto parece muy interesante, principalmente buscaré archivos de video (AVI, DIVX, etc.). Gracias por el puntero
David Hayes
El enlace taglib-sharp parece estar muerto :-( - lo cual es extraño como la wiki en ... developer.novell.com/wiki/index.php/TagLib_Sharp:_Examples ... apunta a esa URL
SteveC
Gracias, SteveC, en el momento en que publiqué esto, ambos enlaces eran válidos y no estaba seguro de cuál era el lugar oficial a donde ir, parece que novell es el sitio correcto para esta biblioteca ahora.
mockobject
Gracias por el aviso, kzu, he actualizado el enlace en mi respuesta original para señalar la ubicación de github también.
mockobject
1

Aquí hay una solución para leer, no escribir, las propiedades extendidas basadas en lo que encontré en esta página y en ayuda con los objetos shell32 .

Para ser claros, esto es un truco. Parece que este código aún se ejecutará en Windows 10, pero afectará algunas propiedades vacías. La versión anterior de Windows debería usar:

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

En Windows 10 asumimos que hay alrededor de 320 propiedades para leer y simplemente omitimos las entradas vacías:

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

Como se mencionó, debe hacer referencia al ensamblado Com Interop.Shell32.

Si obtiene una excepción relacionada con STA, encontrará la solución aquí:

Excepción al usar Shell32 para obtener propiedades extendidas del archivo

No tengo idea de cómo serían esos nombres de propiedades en un sistema externo y no pude encontrar información sobre qué constantes localizables usar para acceder al diccionario. También encontré que no todas las propiedades del cuadro de diálogo Propiedades estaban presentes en el diccionario devuelto.

Por cierto, esto es terriblemente lento y, al menos en Windows 10, analizar las fechas en la cadena recuperada sería un desafío, por lo que usar esto parece ser una mala idea para empezar.

En Windows 10 definitivamente debería usar la biblioteca Windows.Storage que contiene SystemPhotoProperties, SystemMusicProperties, etc. https://docs.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties

Y finalmente, publiqué una solución mucho mejor que usa WindowsAPICodePack allí

pasx
fuente