2016-02-17 78 views
14

在Word/Excel中,您必須可以添加自定義屬性。 (見圖) Custom Properties。 正如你們可以看到的那樣:「屬性:」,你可以在那裏添加你想要的任何信息。 保存文件並轉到文件夾中的文件位置時,可以右鍵單擊 - >屬性,並且您具有所有選項卡:常規/安全/詳細信息/先前版本。使用您添加選項卡Custom的功能。提取Windows文件屬性(自定義屬性)C#

現在我想通過編碼得到這個信息:Custom Properties information。並稍後將其提取到記事本中。 到目前爲止,我使用了Shell32,但我只能獲取詳細信息選項卡中的信息。我做了一些研究,並看到一些可能性DSOfile.dll。但是我想知道是否有可能在不安裝其他DLL的情況下執行此操作? 這是我到目前爲止的代碼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(); 
    } 

在此先感謝!

Desu

+0

什麼您的意思是 - 在安裝其他DLL?因爲無論你使用什麼DLL將出現在.NetFramework預裝。在.NetFramework dll – CarbineCoder

+0

@CarbineCoder之外,有沒有像DSOfile.dll這樣的dll,如果我可以在Visual Studio中安裝NuGet包管理器中的每個.dll,那也沒關係。但是對於DSOfile.dll,無法將其與經理一起安裝。 – Desutoroiya

+0

對不起,我不知道這是否。感謝您澄清 – CarbineCoder

回答

8

歷史上這些屬性是由稱爲「結構化存儲」的技術定義的。第一個結構化存儲實施是古老的(但仍然非常活躍)Compound File Format

之後,Microsoft添加了結構化存儲功能directly into NTFS。這使您可以在任何文件(甚至是.txt)文件上定義作者或標題等屬性。儘管由於某種原因,Explorer UI不再允許你這樣做,但我認爲它仍然以編程方式工作。

然後,在Vista中,微軟重新啓動了所有這一切,並引入了所有這些超集:Windows Property System

不幸的是,在這個框架中沒有.NET API。但是微軟創建了一個名爲Windows API CodePack的開源.NET庫。所以,你提取性能的最簡單的方法是添加一個引用到WindowsAPICodePack核心NugetPackage,你可以使用它像這樣:

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

如果你不想增加額外的DLL,那麼你就可以提取來自WindowsAPICodePack源的ShellPropertyCollection代碼(Windows API Code Pack: Where is it?)。這是一個相當的工作,但可行。

您的情況的另一個解決方案是使用舊的Structure Storage本機API。我提供了一個這樣的示例。以下是您如何使用它:

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

它也可以設置現有的自定義屬性的值嗎? – baru

+0

@baru - WindowsAPICodePack可以做到這一點 –

+0

你確定嗎?當我嘗試設置prop.ValueAsObject字段時,IntelliSense表示「錯誤:屬性或索引器'Microsoft.WindowsAPICodePack.Shell.PropertySystem.IShellProperty.ValueAsObject'無法分配 - 它是隻讀的」。 – baru

1

有一組叫做NetOffice的NuGet軟件包可以使用。每個Office應用程序都有一些程序包,還有幾個核心程序集。獲取NetOffice.Word和NetOffice.Excel並將其安裝到您的解決方案中。在Codeplex site有一些文檔,但我必須瀏覽源代碼才能真正理解發生了什麼。下面是一個示例程序:

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

,顯示了以下結果:

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 

對於Word和Excel這些屬性文件:

Word PropertiesExcel Properties

我沒有工作這些都很重要,所以我不能比這更深入。希望能幫助到你。

1

您可以嘗試NPOI引擎以從不同的Office文件(doc,xls,xlsx,docx等)提取屬性。此組件沒有任何第三方依賴關係,並且您不需要Office來使用它。

但是,這個庫有點棘手,因爲您需要爲不同類型的文件使用不同類型的屬性提取器。好的代碼示例可以在官方Git Hub倉庫TestHPSFPropertiesExtractor中找到。

NuGet包可以找到here