Odczyt / zapis właściwości pliku „rozszerzone” (C #)

104

Próbuję dowiedzieć się, jak odczytywać / zapisywać rozszerzone właściwości pliku w C #, np. Komentarz, szybkość transmisji, data dostępu, kategoria itp., Które można zobaczyć w Eksploratorze Windows. Jakieś pomysły, jak to zrobić? EDYCJA: Będę głównie czytać / zapisywać do plików wideo (AVI / DIVX / ...)

David Hayes
źródło
3
Na to pytanie oczywiście nie ma odpowiedzi, ponieważ zaakceptowana odpowiedź pokazuje tylko, jak uzyskać rozszerzone właściwości, a nie jak je ustawić .
Dmitri Nesteruk,
1
Aby ustawić właściwości rozszerzone, zobacz stackoverflow.com/questions/5337683/…
VoteCoffee

Odpowiedzi:

83

Dla tych, którzy nie mają bzika na punkcie VB, oto w C #:

Zauważ, że musisz dodać odwołanie do Microsoft Shell Controls and Automation z karty COM w oknie dialogowym 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
źródło
3
jak można ustawić jedną z tych wartości? np. autor lub wydawca pliku .txt. Jestem na win 7 i użyłem tego i pokazuje pustego autora i wydawcę oraz 282 inne właściwości
Vaibhav Garg
1
@Vainbhav - Nie możesz tego ustawić.
csharptest.net
26
Musisz dodać odwołanie do Microsoft Shell Controls and Automation z zakładki COM w oknie dialogowym Refences.
csharptest.net
2
Sposób ich ustawienia jest lepiej omówiony w tym pytaniu: stackoverflow.com/questions/5337683/ ...
Nicolas Raoul
1
W systemie Windows 10 zwraca tylko 52 pola bez liczby klatek na sekundę, wysokości ramki, szerokości ...?
Minh Nguyen
27

Jest artykuł w CodeProject dla czytnika ID3. I wątek na kixtart.org, który zawiera więcej informacji na temat innych nieruchomości. Zasadniczo musisz wywołać GetDetailsOf()metodę w obiekcie powłoki folderu dla shell32.dll.

Mark Cidade
źródło
Dzięki, powinienem być w stanie wyciągnąć z tego wszystko, czego potrzebuję
David Hayes,
26

Rozwiązanie 2016

Dodaj następujące pakiety NuGet do projektu:

  • Microsoft.WindowsAPICodePack-Shell przez Microsoft
  • Microsoft.WindowsAPICodePack-Core przez Microsoft

Właściwości odczytu i zapisu

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();

Ważny:

Plik musi być prawidłowy, utworzony przez określone przypisane oprogramowanie. Każdy typ pliku ma określone rozszerzone właściwości pliku i nie wszystkie z nich są zapisywalne.

Jeśli klikniesz prawym przyciskiem myszy plik na pulpicie i nie możesz edytować właściwości, nie będziesz w stanie edytować jej również w kodzie.

Przykład:

  • Utwórz plik txt na pulpicie, zmień jego rozszerzenie na docx. Nie możesz edytować jej Authorani Titlewłaściwości.
  • Otwórz go w programie Word, edytuj i zapisz. Teraz możesz.

Więc po prostu upewnij się, że używasz niektórych try catch

Dalszy temat: MSDN: wdrażanie programów obsługi właściwości

Martin Schneider
źródło
Nie widzę żadnych pakietów o tych nazwach od firmy Microsoft
Jacob Brewer
1
@JacobBrewer W rzeczywistości jest to ten, który został przesłany przez „acdvorak”: nuget.org/packages/Microsoft.WindowsAPICodePack-Shell . W programie Visual Studio NuGet-Package-Manager jest wyświetlany jako „przez firmę Microsoft”. Inne pakiety o podobnych nazwach są jego rozwidleniami i mogą również działać poprawnie lub nawet lepiej. Nie wiem ...
Martin Schneider
25

Ten przykład w VB.NET odczytuje wszystkie rozszerzone właściwości:

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

Musisz dodać odwołanie do Microsoft Shell Controls and Automation na karcie COM w oknie dialogowym References .

Dirk Vollmar
źródło
2
Warto zauważyć, że w systemie Windows 7 (przynajmniej) można zwiększyć rozmiar arrHeaders do nieco ponad 280 i odzyskać mnóstwo dodatkowych metadanych. Dowiedziałem się o tym, gdy szukałem sposobu na uzyskanie metadanych z pliku WTV (program telewizyjny nagrany w systemie Windows 7 Media Center).
Richard
1
Pierwsza pętla dla i = 0 do 34 powinna być dla i = 0 do Integer.MaxValue. Następnie przetestuj zwracaną wartość objFolder.GetDetailsOf (objFolder.Items, i). Jeśli zwraca wartość null lub białą spację, masz wszystkie nagłówki.
Tim Murphy,
@TimMurphy, niestety, nie jest poprawne (przynajmniej w systemie Windows 10 lub 7). Niektóre nagłówki są puste, więc musisz przejrzeć wszystkie indeksy. (Oczywiście, ponieważ nie ma ich milionów, możesz ograniczyć pętlę do 1000.)
skst
8

Dziękuję wam za ten wątek! Pomogło mi to, gdy chciałem dowiedzieć się, jaka jest wersja pliku exe. Musiałem jednak sam wymyślić ostatnią część tego, co nazywa się rozszerzonymi właściwościami.

Jeśli otworzysz właściwości pliku exe (lub dll) w Eksploratorze Windows, pojawi się karta Wersja i widok Właściwości rozszerzonych tego pliku. Chciałem uzyskać dostęp do jednej z tych wartości.

Rozwiązaniem tego problemu jest indeksator właściwości FolderItem.ExtendedProperty i jeśli usuniesz wszystkie spacje w nazwie właściwości, otrzymasz wartość. Np. File Version przechodzi w FileVersion i gotowe.

Mam nadzieję, że to pomoże komukolwiek innemu, pomyślałem, że dodam te informacje do tego wątku. Twoje zdrowie!

JERKER
źródło
2
Gwarantuje to również, że Twój kod będzie (przynajmniej w większości) nadal działał na komputerach nieanglojęzycznych.
Ed Norris,
1
Jedno małe ulepszenie tutaj, FolderItem nie zawiera ExtendedProperty (). FolderItem2 robi jednak.
Mixxiphoid
Ta odpowiedź jest odrobinę prostsza niż inne.
Podałem
8

GetDetailsOf()Metoda - pobiera szczegółowe informacje o elemencie w folderze. Na przykład jego rozmiar, typ lub czas ostatniej modyfikacji. Właściwości pliku mogą się różnić w zależności od Windows-OSwersji.

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
źródło
Skopiowałem i użyłem tego samego kodu powyżej. Ale tutaj otrzymuję wyjątek COM w czasie wykonywania Folder rFolder = shell.NameSpace(_rootPath);. FYI, używam systemu operacyjnego Windows 8.
DonMax
1
Co to za błąd? Upewnij się, że używasz poprawnej wersji pliku Shell32.dll. Sprawdź to może Przydatne
RajeshKdev
1
+1 dla powyższego linku. Link, który zasugerowałeś, działa poprawnie. Potrzebuję jeszcze jednej pomocy od ciebie. tj. jak mogę przekazać pojedynczy plik, aby uzyskać metadane? ponieważ akceptuje przekazywanie tylko folderu.
DonMax
6
  • Po przyjrzeniu się kilku rozwiązaniom w tym wątku i w innych miejscach utworzono następujący kod. Służy to tylko do odczytu właściwości.
  • Nie mogłem sprawić, aby funkcja Shell32.FolderItem2.ExtendedProperty działała, powinna przyjmować wartość ciągu i zwracać poprawną wartość i typ dla tej właściwości ... zawsze było to dla mnie zerowe, a zasoby referencyjne programisty były bardzo cienkie.
  • WindowsApiCodePack wydaje się, że zostały porzucone przez Microsoft, który przynosi nam poniższy kod.

Posługiwać się:

string propertyValue = GetExtendedFileProperty("c:\\temp\\FileNameYouWant.ext","PropertyYouWant");
  1. Zwróci wartość rozszerzonej właściwości, którą chcesz jako ciąg znaków dla podanego pliku i nazwy właściwości.
  2. Zapętla się tylko do momentu znalezienia określonej właściwości - nie do momentu wykrycia wszystkich właściwości, jak przykładowy kod
  3. Będzie działać na wersjach systemu Windows, takich jak Windows Server 2008, gdzie pojawi się błąd „Nie można rzutować obiektu COM typu„ System .__ ComObject ”na interfejs typu„ Shell32.Shell ””, jeśli próbujesz tylko normalnie utworzyć obiekt Shell32.

    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
źródło
1
Zauważ, że zamiast używać InvokeMember () , możesz rzutować shellna dowolny z IShellDispatchinterfejsów (1-6) i bezpośrednio wywołać członka. Shell32.IShellDispatch ishell = (Shell32.IShellDispatch)shell; Shell32.Folder shellFolder = ishell.NameSpace(baseFolder);
skst
5

Odpowiedź Jerkera jest trochę prostsza. Oto przykładowy kod, który działa z MS :

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

Dla tych, którzy nie mogą statycznie odwoływać się do shell32, możesz wywołać go dynamicznie w następujący sposób:

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
źródło
1

Nie jestem pewien, dla jakich typów plików próbujesz zapisać właściwości, ale taglib-ostry to doskonała biblioteka tagowania typu open source, która ładnie zawiera wszystkie te funkcje. Ma wiele wbudowanej obsługi większości popularnych typów plików multimedialnych, ale pozwala także na bardziej zaawansowane tagowanie z prawie każdym plikiem.

EDYTOWAĆ: Zaktualizowałem link do taglib sharp. Stary link już nie działał.

EDYCJA: Ponownie zaktualizowano link zgodnie z komentarzem kzu.

mockobject
źródło
Wygląda to bardzo interesująco, będę głównie patrzeć na pliki wideo (AVI, DIVX itp.). Dzięki za wskazówkę
David Hayes,
Link ostry taglib wydaje się być martwy :-( - co jest dziwne, ponieważ wiki na ... developer.novell.com/wiki/index.php/TagLib_Sharp:_Examples ... wskazuje na ten adres URL
SteveC
Dzięki, SteveC, w czasie, gdy to publikowałem, oba linki były prawidłowe i nie byłem pewien, które jest oficjalnym miejscem, do którego należy się udać, wygląda na to, że Novell jest teraz właściwą stroną, na którą można przejść dla tej biblioteki.
mockobject
Dzięki za ostrzeżenie kzu, zaktualizowałem link w mojej oryginalnej odpowiedzi, aby również wskazywał lokalizację github.
mockobject
1

Oto rozwiązanie do czytania - a nie pisania - rozszerzonych właściwości na podstawie tego, co znalazłem na tej stronie i przy pomocy obiektów shell32 .

Żeby było jasne, to jest hack. Wygląda na to, że ten kod będzie nadal działał w systemie Windows 10, ale trafi na niektóre puste właściwości. Poprzednia wersja systemu Windows powinna używać:

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

W systemie Windows 10 zakładamy, że jest około 320 właściwości do odczytania i po prostu pomijamy puste wpisy:

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

Jak wspomniano, musisz odwołać się do zestawu Com Interop.Shell32.

Jeśli otrzymasz wyjątek związany z STA, rozwiązanie znajdziesz tutaj:

Wyjątek w przypadku używania Shell32 do uzyskania rozszerzonych właściwości pliku

Nie mam pojęcia, jakie nazwy właściwości wyglądałyby w obcym systemie i nie mogłem znaleźć informacji o tym, które stałe lokalizowalne użyć, aby uzyskać dostęp do słownika. Odkryłem również, że nie wszystkie właściwości z okna dialogowego Właściwości były obecne w zwróconym słowniku.

BTW jest to strasznie powolne i - przynajmniej w Windows 10 - analizowanie dat w pobranym ciągu byłoby wyzwaniem, więc używanie tego wydaje się być złym pomysłem na początek.

W systemie Windows 10 zdecydowanie powinieneś użyć biblioteki Windows.Storage, która zawiera SystemPhotoProperties, SystemMusicProperties itp. Https://docs.microsoft.com/en-us/windows/uwp/files/quickstart-getting-file-properties

I wreszcie, pisał o wiele lepsze rozwiązanie, które używa WindowsAPICodePack tam

pasx
źródło