web-dev-qa-db-ja.com

Windowsファイルプロパティの抽出(カスタムプロパティ)C#

Word/Excelでは、カスタムプロパティを追加する可能性があります。 (画像を参照) カスタムプロパティ 。ご覧のとおり、「プロパティ:」フィールドがあり、そこに必要な情報を追加できます。ファイルを保存してフォルダ内のファイルの場所に移動すると、右クリック-> [プロパティ]が表示され、[一般]/[セキュリティ]/[詳細]/[以前のバージョン]のすべてのタブが表示されます。この機能を使用して、[カスタム]タブを追加します。

ここで、コーディングを通じてこの情報を取得したいと思います: カスタムプロパティ情報 。後でメモ帳に抽出します。これまでは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();
    }

前もって感謝します!

です

18
Desutoroiya

歴史的に、これらのプロパティは「StructuredStorage」と呼ばれるテクノロジーによって定義されていました。最初のStructuredStorageの実装は古いものです(しかしまだ非常に生きています) 複合ファイル形式

その後、MicrosoftはStructured Storage機能を追加しました NTFSに直接 。これにより、任意のファイル(.txtも含む)ファイルに作成者やタイトルなどのプロパティを定義できます。 Explorer UIでは、何らかの理由でこれを実行できなくなりましたが、プログラムで機能すると思います。

そして、Vistaで、Microsoftはそれらすべてを再起動し、これらすべてのスーパーセットを導入しました: Windowsプロパティシステム

残念ながら、これらすべてのフレームワークには.NETAPIはありません。しかし、Microsoftは、Windows APICodePackと呼ばれるオープンソースの.NETライブラリを作成しました。したがって、プロパティを抽出する最も簡単な方法は、WindowsAPICodePack Core 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コードパック:どこにありますか? )。それはかなりの作業ですが、実行可能です。

あなたの場合の別の解決策は、古いStructureStorageネイティブ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;
    }
}
16
Simon Mourier

[〜#〜] npoi [〜#〜] エンジンを試して、さまざまなOfficeファイル(doc、xls、xlsx、docxなど)からプロパティを抽出できます。このコンポーネントにはサードパーティの依存関係がなく、Officeで使用する必要はありません。

ただし、このライブラリは、ファイルの種類ごとに異なる種類のプロパティエクストラクタを使用する必要があるため、少し注意が必要です。優れたコードサンプルは、公式のGit Hubリポジトリ TestHPSFPropertiesExtractor にあります。

NuGetパッケージは ここ にあります。

2
Nikita

使用できるNetOfficeと呼ばれるNuGetパッケージのセットがあります。各Officeアプリケーション用のパッケージと、いくつかのコアアセンブリがあります。 NetOffice.WordとNetOffice.Excelを入手して、ソリューションにインストールします。 Codeplexサイト に小さなドキュメントがありますが、何が起こっているのかを実際に理解するには、ソースを参照する必要がありました。サンプルプログラムは次のとおりです。

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
John Riehl

MicrosoftのOpenXmlSDKを使用できます。 Word/Excelファイルからどんな情報でも取得できると思います。これを見てください https://msdn.Microsoft.com/en-us/library/office/hh674468.aspx これも https://msdn.Microsoft.com/en -us/library/office/bb448854.aspx または http://openxmldeveloper.org/

1
VDN

より一般的なアプローチがあるかもしれません。ソースが... xファイルの場合、ドキュメントはXMLクエリを介して抽出できます。ファイルを解凍すると(たとえば、7-Zipを使用)、サブフォルダーdocPropsにcustom.xmlというファイルがあります。プロパティはXMLファイル内で簡単に見つけることができます

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">
    <property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="2" name="SAPDOKNR">
        <vt:lpwstr>10002210058</vt:lpwstr>
    </property>
    ...
0
Stefan