2016-02-17 12 views
14

W programie Word/Excel należy dodać opcję Właściwości niestandardowe. (Patrz obraz) Custom Properties. Jak widać, jest pole: "Właściwości:", możesz dodać informacje, które tam chcesz. Po zapisaniu pliku i przejściu do lokalizacji pliku w folderze, możesz kliknąć prawym przyciskiem myszy -> Właściwości i masz wszystkie karty: Ogólne/Bezpieczeństwo/Szczegóły/Poprzednie wersje. za pomocą tej funkcji dodajesz kartę Niestandardowy.Wyodrębnianie właściwości pliku systemu Windows (właściwości niestandardowe) C#

Teraz chcę uzyskać tę informację poprzez kodowanie: Custom Properties information. i wyodrębnij go później do notatnika. Do tej pory użyłem Shell32, ale wtedy otrzymuję tylko informacje znajdujące się w zakładce Szczegóły. Zrobiłem kilka badań i zobaczyłem pewne możliwości z DSOfile.dll. Ale chcę wiedzieć, czy istnieje możliwość zrobienia tego bez instalowania innych plików DLL? To jest mój kod do tej pory z Shell32.

 static void Main(string[] args) 
    { 

     //using (StreamWriter writer = new StreamWriter(@"filepathhere")) 
     //{ 
      //Console.SetOut(writer); 
      ReadProperties(); 
     //} 
    } 
    static void ReadProperties() 
    { 
     List<string> arrHeaders = new List<string>(); 
     Shell shell = new Shell(); 
     Folder objFolder = shell.NameSpace(@"filepathhere"); 
     FolderItem objFolderItem = objFolder.ParseName("filehere.doc"); 

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

     for (int i = 0; i < arrHeaders.Count; i++) 
     { 
      Console.WriteLine("{0}\t{1}: {2}", i, arrHeaders[i], objFolder.GetDetailsOf(objFolderItem, i)); 
     } 
     Console.ReadKey(); 
    } 

Z góry dziękuję!

Desu

+0

Co masz na myśli - instalowanie innych DLL? Ponieważ dowolna biblioteka dll, której używasz, będzie obecna w preinstalowanej wersji .NetFramework. Czy jest jakiś dll, który masz na myśli poza biblioteką .NetFramework – CarbineCoder

+0

@ CarbineCoder jak DSOfile.dll, to dobrze, jeśli mogę zainstalować każdy .dll z menedżerem pakietów NuGet w visual studio. Ale z DSOfile.dll nie można zainstalować tego z menadżerem. – Desutoroiya

+0

Niestety nie jestem tego świadomy. Dziękujemy za wyjaśnienie – CarbineCoder

Odpowiedz

8

Historycznie te właściwości zostały określone przez technologię o nazwie "Structured Storage". Pierwsza implementacja Structured Storage to pradawna (ale wciąż bardzo żywa) usługa. Następnie Microsoft dodał funkcje Structured Storage directly into NTFS. Pozwala to na zdefiniowanie właściwości, takich jak autor lub tytuł, w dowolnych plikach (nawet w txt). Chociaż interfejs użytkownika Explorera z jakiegoś powodu nie pozwala ci tego zrobić, myślę, że nadal działa programowo.

Następnie, z Vistą, Microsoft zrestartował wszystko i wprowadził nadzbiór tego wszystkiego: Windows Property System.

Niestety, nie ma w tym wszystkim platformy .NET API. Ale Microsoft stworzył otwartą bibliotekę .NET o nazwie Windows API CodePack. Więc, najprostszym sposobem, aby wyodrębnić właściwości jest dodać odwołanie do WindowsAPICodePack podstawowej NugetPackage i można go używać tak:

static void Main(string[] args) 
{ 
    foreach (var prop in new ShellPropertyCollection(@"mypath\myfile")) 
    { 
     Console.WriteLine(prop.CanonicalName + "=" + prop.ValueAsObject); 
    } 
} 

Jeśli nie chcesz dodać dodatkowe dll, a następnie można wyodrębnić kod powłoki ShellPropertyCollection ze źródła WindowsAPICodePack (Windows API Code Pack: Where is it?). To dość praca, ale wykonalna.

Innym rozwiązaniem w twoim przypadku jest użycie starego natywnego API Structure Storage. Dostarczyłem próbkę, która to robi. Oto, jak możesz go użyć:

static void Main(string[] args) 
{ 
    foreach (var prop in new StructuredStorage(@"mypath\myfile").Properties) 
    { 
     Console.WriteLine(prop.Name + "=" + prop.Value); 
    }    
} 

public sealed class StructuredStorage 
{ 
    public static readonly Guid SummaryInformationFormatId = new Guid("{F29F85E0-4FF9-1068-AB91-08002B27B3D9}"); 
    public static readonly Guid DocSummaryInformationFormatId = new Guid("{D5CDD502-2E9C-101B-9397-08002B2CF9AE}"); 
    public static readonly Guid UserDefinedPropertiesId = new Guid("{D5CDD505-2E9C-101B-9397-08002B2CF9AE}"); 

    private List<StructuredProperty> _properties = new List<StructuredProperty>(); 

    public StructuredStorage(string filePath) 
    { 
     if (filePath == null) 
      throw new ArgumentNullException("filePath"); 

     FilePath = filePath; 
     IPropertySetStorage propertySetStorage; 
     int hr = StgOpenStorageEx(FilePath, STGM.STGM_READ | STGM.STGM_SHARE_DENY_NONE | STGM.STGM_DIRECT_SWMR, STGFMT.STGFMT_ANY, 0, IntPtr.Zero, IntPtr.Zero, typeof(IPropertySetStorage).GUID, out propertySetStorage); 
     if (hr == STG_E_FILENOTFOUND || hr == STG_E_PATHNOTFOUND) 
      throw new FileNotFoundException(null, FilePath); 

     if (hr != 0) 
      throw new Win32Exception(hr); 

     try 
     { 
      LoadPropertySet(propertySetStorage, SummaryInformationFormatId); 
      LoadPropertySet(propertySetStorage, DocSummaryInformationFormatId); 
     } 
     finally 
     { 
      Marshal.ReleaseComObject(propertySetStorage); 
     } 

     // for some reason we can't read this one on the same COM ref? 
     LoadProperties(UserDefinedPropertiesId); 
    } 

    public string FilePath { get; private set; } 
    public IReadOnlyList<StructuredProperty> Properties 
    { 
     get 
     { 
      return _properties; 
     } 
    } 

    private void LoadPropertySet(IPropertySetStorage propertySetStorage, Guid fmtid) 
    { 
     IPropertyStorage propertyStorage; 
     int hr = propertySetStorage.Open(fmtid, STGM.STGM_READ | STGM.STGM_SHARE_EXCLUSIVE, out propertyStorage); 
     if (hr == STG_E_FILENOTFOUND || hr == STG_E_ACCESSDENIED) 
      return; 

     if (hr != 0) 
      throw new Win32Exception(hr); 

     IEnumSTATPROPSTG es; 
     propertyStorage.Enum(out es); 
     if (es == null) 
      return; 

     try 
     { 
      var stg = new STATPROPSTG(); 
      int fetched; 
      do 
      { 
       hr = es.Next(1, ref stg, out fetched); 
       if (hr != 0 && hr != 1) 
        throw new Win32Exception(hr); 

       if (fetched == 1) 
       { 
        string name = GetPropertyName(fmtid, propertyStorage, stg); 

        var propsec = new PROPSPEC[1]; 
        propsec[0] = new PROPSPEC(); 
        propsec[0].ulKind = stg.lpwstrName != null ? PRSPEC.PRSPEC_LPWSTR : PRSPEC.PRSPEC_PROPID; 
        IntPtr lpwstr = IntPtr.Zero; 
        if (stg.lpwstrName != null) 
        { 
         lpwstr = Marshal.StringToCoTaskMemUni(stg.lpwstrName); 
         propsec[0].union.lpwstr = lpwstr; 
        } 
        else 
        { 
         propsec[0].union.propid = stg.propid; 
        } 

        var vars = new PROPVARIANT[1]; 
        vars[0] = new PROPVARIANT(); 
        try 
        { 
         hr = propertyStorage.ReadMultiple(1, propsec, vars); 
         if (hr != 0) 
          throw new Win32Exception(hr); 

        } 
        finally 
        { 
         if (lpwstr != IntPtr.Zero) 
         { 
          Marshal.FreeCoTaskMem(lpwstr); 
         } 
        } 

        object value; 
        try 
        { 
         switch (vars[0].vt) 
         { 
          case VARTYPE.VT_BOOL: 
           value = vars[0].union.boolVal != 0 ? true : false; 
           break; 

          case VARTYPE.VT_BSTR: 
           value = Marshal.PtrToStringUni(vars[0].union.bstrVal); 
           break; 

          case VARTYPE.VT_CY: 
           value = decimal.FromOACurrency(vars[0].union.cyVal); 
           break; 

          case VARTYPE.VT_DATE: 
           value = DateTime.FromOADate(vars[0].union.date); 
           break; 

          case VARTYPE.VT_DECIMAL: 
           IntPtr dec = IntPtr.Zero; 
           Marshal.StructureToPtr(vars[0], dec, false); 
           value = Marshal.PtrToStructure(dec, typeof(decimal)); 
           break; 

          case VARTYPE.VT_DISPATCH: 
           value = Marshal.GetObjectForIUnknown(vars[0].union.pdispVal); 
           break; 

          case VARTYPE.VT_ERROR: 
          case VARTYPE.VT_HRESULT: 
           value = vars[0].union.scode; 
           break; 

          case VARTYPE.VT_FILETIME: 
           value = DateTime.FromFileTime(vars[0].union.filetime); 
           break; 

          case VARTYPE.VT_I1: 
           value = vars[0].union.cVal; 
           break; 

          case VARTYPE.VT_I2: 
           value = vars[0].union.iVal; 
           break; 

          case VARTYPE.VT_I4: 
           value = vars[0].union.lVal; 
           break; 

          case VARTYPE.VT_I8: 
           value = vars[0].union.hVal; 
           break; 

          case VARTYPE.VT_INT: 
           value = vars[0].union.intVal; 
           break; 

          case VARTYPE.VT_LPSTR: 
           value = Marshal.PtrToStringAnsi(vars[0].union.pszVal); 
           break; 

          case VARTYPE.VT_LPWSTR: 
           value = Marshal.PtrToStringUni(vars[0].union.pwszVal); 
           break; 

          case VARTYPE.VT_R4: 
           value = vars[0].union.fltVal; 
           break; 

          case VARTYPE.VT_R8: 
           value = vars[0].union.dblVal; 
           break; 

          case VARTYPE.VT_UI1: 
           value = vars[0].union.bVal; 
           break; 

          case VARTYPE.VT_UI2: 
           value = vars[0].union.uiVal; 
           break; 

          case VARTYPE.VT_UI4: 
           value = vars[0].union.ulVal; 
           break; 

          case VARTYPE.VT_UI8: 
           value = vars[0].union.uhVal; 
           break; 

          case VARTYPE.VT_UINT: 
           value = vars[0].union.uintVal; 
           break; 

          case VARTYPE.VT_UNKNOWN: 
           value = Marshal.GetObjectForIUnknown(vars[0].union.punkVal); 
           break; 

          default: 
           value = null; 
           break; 
         } 
        } 
        finally 
        { 
         PropVariantClear(ref vars[0]); 
        } 

        var property = new StructuredProperty(fmtid, name, stg.propid); 
        property.Value = value; 
        _properties.Add(property); 
       } 
      } 
      while (fetched == 1); 
     } 
     finally 
     { 
      Marshal.ReleaseComObject(es); 
     } 
    } 

    private static string GetPropertyName(Guid fmtid, IPropertyStorage propertyStorage, STATPROPSTG stg) 
    { 
     if (!string.IsNullOrEmpty(stg.lpwstrName)) 
      return stg.lpwstrName; 

     var propids = new int[1]; 
     propids[0] = stg.propid; 
     var names = new string[1]; 
     names[0] = null; 
     int hr = propertyStorage.ReadPropertyNames(1, propids, names); 
     if (hr == 0) 
      return names[0]; 

     return null; 
    } 

    public void LoadProperties(Guid formatId) 
    { 
     IPropertySetStorage propertySetStorage; 
     int hr = StgOpenStorageEx(FilePath, STGM.STGM_READ | STGM.STGM_SHARE_DENY_NONE | STGM.STGM_DIRECT_SWMR, STGFMT.STGFMT_ANY, 0, IntPtr.Zero, IntPtr.Zero, typeof(IPropertySetStorage).GUID, out propertySetStorage); 
     if (hr == STG_E_FILENOTFOUND || hr == STG_E_PATHNOTFOUND) 
      throw new FileNotFoundException(null, FilePath); 

     if (hr != 0) 
      throw new Win32Exception(hr); 

     try 
     { 
      LoadPropertySet(propertySetStorage, formatId); 
     } 
     finally 
     { 
      Marshal.ReleaseComObject(propertySetStorage); 
     } 
    } 

    private const int STG_E_FILENOTFOUND = unchecked((int)0x80030002); 
    private const int STG_E_PATHNOTFOUND = unchecked((int)0x80030003); 
    private const int STG_E_ACCESSDENIED = unchecked((int)0x80030005); 

    private enum PRSPEC 
    { 
     PRSPEC_LPWSTR = 0, 
     PRSPEC_PROPID = 1 
    } 

    private enum STGFMT 
    { 
     STGFMT_ANY = 4, 
    } 

    [Flags] 
    private enum STGM 
    { 
     STGM_READ = 0x00000000, 
     STGM_READWRITE = 0x00000002, 
     STGM_SHARE_DENY_NONE = 0x00000040, 
     STGM_SHARE_DENY_WRITE = 0x00000020, 
     STGM_SHARE_EXCLUSIVE = 0x00000010, 
     STGM_DIRECT_SWMR = 0x00400000 
    } 

    // we only define what we handle 
    private enum VARTYPE : short 
    { 
     VT_I2 = 2, 
     VT_I4 = 3, 
     VT_R4 = 4, 
     VT_R8 = 5, 
     VT_CY = 6, 
     VT_DATE = 7, 
     VT_BSTR = 8, 
     VT_DISPATCH = 9, 
     VT_ERROR = 10, 
     VT_BOOL = 11, 
     VT_UNKNOWN = 13, 
     VT_DECIMAL = 14, 
     VT_I1 = 16, 
     VT_UI1 = 17, 
     VT_UI2 = 18, 
     VT_UI4 = 19, 
     VT_I8 = 20, 
     VT_UI8 = 21, 
     VT_INT = 22, 
     VT_UINT = 23, 
     VT_HRESULT = 25, 
     VT_LPSTR = 30, 
     VT_LPWSTR = 31, 
     VT_FILETIME = 64, 
    } 

    [StructLayout(LayoutKind.Explicit)] 
    private struct PROPVARIANTunion 
    { 
     [FieldOffset(0)] 
     public sbyte cVal; 
     [FieldOffset(0)] 
     public byte bVal; 
     [FieldOffset(0)] 
     public short iVal; 
     [FieldOffset(0)] 
     public ushort uiVal; 
     [FieldOffset(0)] 
     public int lVal; 
     [FieldOffset(0)] 
     public uint ulVal; 
     [FieldOffset(0)] 
     public int intVal; 
     [FieldOffset(0)] 
     public uint uintVal; 
     [FieldOffset(0)] 
     public long hVal; 
     [FieldOffset(0)] 
     public ulong uhVal; 
     [FieldOffset(0)] 
     public float fltVal; 
     [FieldOffset(0)] 
     public double dblVal; 
     [FieldOffset(0)] 
     public short boolVal; 
     [FieldOffset(0)] 
     public int scode; 
     [FieldOffset(0)] 
     public long cyVal; 
     [FieldOffset(0)] 
     public double date; 
     [FieldOffset(0)] 
     public long filetime; 
     [FieldOffset(0)] 
     public IntPtr bstrVal; 
     [FieldOffset(0)] 
     public IntPtr pszVal; 
     [FieldOffset(0)] 
     public IntPtr pwszVal; 
     [FieldOffset(0)] 
     public IntPtr punkVal; 
     [FieldOffset(0)] 
     public IntPtr pdispVal; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct PROPSPEC 
    { 
     public PRSPEC ulKind; 
     public PROPSPECunion union; 
    } 

    [StructLayout(LayoutKind.Explicit)] 
    private struct PROPSPECunion 
    { 
     [FieldOffset(0)] 
     public int propid; 
     [FieldOffset(0)] 
     public IntPtr lpwstr; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct PROPVARIANT 
    { 
     public VARTYPE vt; 
     public ushort wReserved1; 
     public ushort wReserved2; 
     public ushort wReserved3; 
     public PROPVARIANTunion union; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct STATPROPSTG 
    { 
     [MarshalAs(UnmanagedType.LPWStr)] 
     public string lpwstrName; 
     public int propid; 
     public VARTYPE vt; 
    } 

    [StructLayout(LayoutKind.Sequential)] 
    private struct STATPROPSETSTG 
    { 
     public Guid fmtid; 
     public Guid clsid; 
     public uint grfFlags; 
     public System.Runtime.InteropServices.ComTypes.FILETIME mtime; 
     public System.Runtime.InteropServices.ComTypes.FILETIME ctime; 
     public System.Runtime.InteropServices.ComTypes.FILETIME atime; 
     public uint dwOSVersion; 
    } 

    [DllImport("ole32.dll")] 
    private static extern int StgOpenStorageEx([MarshalAs(UnmanagedType.LPWStr)] string pwcsName, STGM grfMode, STGFMT stgfmt, int grfAttrs, IntPtr pStgOptions, IntPtr reserved2, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IPropertySetStorage ppObjectOpen); 

    [DllImport("ole32.dll")] 
    private static extern int PropVariantClear(ref PROPVARIANT pvar); 

    [Guid("0000013B-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    private interface IEnumSTATPROPSETSTG 
    { 
     [PreserveSig] 
     int Next(int celt, ref STATPROPSETSTG rgelt, out int pceltFetched); 
     // rest ommited 
    } 

    [Guid("00000139-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    private interface IEnumSTATPROPSTG 
    { 
     [PreserveSig] 
     int Next(int celt, ref STATPROPSTG rgelt, out int pceltFetched); 
     // rest ommited 
    } 

    [Guid("00000138-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    private interface IPropertyStorage 
    { 
     [PreserveSig] 
     int ReadMultiple(uint cpspec, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPSPEC[] rgpspec, [Out, MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPVARIANT[] rgpropvar); 
     [PreserveSig] 
     int WriteMultiple(uint cpspec, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPSPEC[] rgpspec, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPVARIANT[] rgpropvar, uint propidNameFirst); 
     [PreserveSig] 
     int DeleteMultiple(uint cpspec, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] PROPSPEC[] rgpspec); 
     [PreserveSig] 
     int ReadPropertyNames(uint cpropid, [MarshalAs(UnmanagedType.LPArray, SizeParamIndex = 0)] int[] rgpropid, [Out, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPWStr, SizeParamIndex = 0)] string[] rglpwstrName); 
     [PreserveSig] 
     int NotDeclared1(); 
     [PreserveSig] 
     int NotDeclared2(); 
     [PreserveSig] 
     int Commit(uint grfCommitFlags); 
     [PreserveSig] 
     int NotDeclared3(); 
     [PreserveSig] 
     int Enum(out IEnumSTATPROPSTG ppenum); 
     // rest ommited 
    } 

    [Guid("0000013A-0000-0000-C000-000000000046"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] 
    private interface IPropertySetStorage 
    { 
     [PreserveSig] 
     int Create([MarshalAs(UnmanagedType.LPStruct)] Guid rfmtid, [MarshalAs(UnmanagedType.LPStruct)] Guid pclsid, uint grfFlags, STGM grfMode, out IPropertyStorage ppprstg); 
     [PreserveSig] 
     int Open([MarshalAs(UnmanagedType.LPStruct)] Guid rfmtid, STGM grfMode, out IPropertyStorage ppprstg); 
     [PreserveSig] 
     int NotDeclared3(); 
     [PreserveSig] 
     int Enum(out IEnumSTATPROPSETSTG ppenum); 
    } 
} 

public sealed class StructuredProperty 
{ 
    public StructuredProperty(Guid formatId, string name, int id) 
    { 
     FormatId = formatId; 
     Name = name; 
     Id = id; 
    } 

    public Guid FormatId { get; private set; } 
    public string Name { get; private set; } 
    public int Id { get; private set; } 
    public object Value { get; set; } 

    public override string ToString() 
    { 
     return Name; 
    } 
} 
+0

Czy działa również ustawienie wartości istniejącej właściwości niestandardowej? – baru

+0

@baru - WindowsAPICodePack może to zrobić –

+0

Jesteś pewien? Kiedy próbuję ustawić pole prop.ValueAsObject, IntelliSense mówi "Błąd: Property lub Indexer" Microsoft.WindowsAPICodePack.Shell.PropertySystem.IShellProperty.ValueAsObject "nie można przypisać - jest tylko do odczytu". – baru

1

Jest to zestaw pakietów Nuget zwanych NetOffice, które można wykorzystać. Istnieją pakiety dla każdej aplikacji Office, a także kilka podstawowych złożeń. Pobierz NetOffice.Word i NetOffice.Excel i zainstaluj je w swoim rozwiązaniu. Jest trochę dokumentacji na Codeplex site, ale musiałem przeglądać źródło, aby naprawdę zrozumieć, co się dzieje. Oto przykładowy program:

using NetOffice.OfficeApi; 
using System; 

namespace Office_Doc_Reader 
{ 
    class Program 
    { 
     static void Main(string[] args) 
     { 
      using (var wordApp = new NetOffice.WordApi.Application()) 
      using (var excelApp = new NetOffice.ExcelApi.Application()) 
      { 
       var doc = wordApp.Documents.Open("C:\\Users\\John\\Desktop\\test.docx"); 
       var xls = excelApp.Workbooks.Open("C:\\Users\\John\\Desktop\\test.xlsx"); 

       var customProperties = (DocumentProperties)doc.CustomDocumentProperties; 

       foreach (var property in customProperties) 
       { 
        Console.WriteLine(String.Format("Name: {0}, Value: {1}, Type: {2}", property.Name, property.Value, property.Type)); 
       } 

       customProperties = (DocumentProperties)xls.CustomDocumentProperties; 

       foreach (var property in customProperties) 
       { 
        Console.WriteLine(String.Format("Name: {0}, Value: {1}, Type: {2}", property.Name, property.Value, property.Type)); 
       } 
      } 

      Console.ReadKey(); 
     } 
    } 
} 

To pokazuje następujące wyniki:

Name: Custom prop 1, Value: Text Value, Type: msoPropertyTypeString 
Name: Custom prop 2, Value: 2/21/2016 12:00:00 AM, Type: msoPropertyTypeDate 
Name: Custom prop 3, Value: 42, Type: msoPropertyTypeNumber 
Name: Custom prop 4, Value: True, Type: msoPropertyTypeBoolean 
Name: Foo, Value: abc, Type: msoPropertyTypeString 
Name: Bar, Value: 1/1/1970 12:00:00 AM, Type: msoPropertyTypeDate 
Name: Baz, Value: 3.14159, Type: msoPropertyTypeFloat 
Name: Qux, Value: False, Type: msoPropertyTypeBoolean 

za słowo i Excel plik z tych właściwości:

Word PropertiesExcel Properties

ja nie pracuję z tymi wszystkimi w ogóle, więc nie będę mógł pójść o wiele głębiej. Mam nadzieję, że to pomoże.

1

Możesz wypróbować silnik NPOI, aby wyodrębnić właściwości z różnych plików pakietu Office (doc, xls, xlsx, docx itd.). Ten komponent nie ma żadnych zależności od podmiotów zewnętrznych i nie potrzebujesz pakietu Office, aby z niego korzystać.

Jednak ta biblioteka jest nieco skomplikowana, ponieważ do różnego typu plików należy używać różnych typów ekstraktorów. Dobrą próbkę kodu można znaleźć w oficjalnym repozytorium Git Hub TestHPSFPropertiesExtractor.

Pakiet NuGet można znaleźć here.

Powiązane problemy