Как получить список всех доступных свойств файлов для всех типов файлов

Я хочу получить список ВСЕХ доступных свойств файла (то есть тот же список, который вы получаете, когда нажимаете More в Проводнике).

В настоящее время я могу получить желаемые результаты, запустив инструмент PropertySystemView от Nirsoft с его аргументом /AllProperties, а затем проанализировав его выходные данные, чтобы включить свойства, которые имеют непустой Display Name и где значение Viewable равно Yes.

Однако я хочу отказаться от использования PropertySystemView.exe и получить список свойств непосредственно в моем собственном коде C#. Я нашел множество примеров кода для получения свойств отдельного файла, но не нашел ничего, показывающего, как получить все свойства. Поэтому у меня нет кода, который можно было бы показать в моем вопросе.

Этот вопрос НЕ является дубликатом этого вопроса. Как вы можете видеть, я спросил Саймона Мурье в комментариях о получении ВСЕХ свойств, и он сказал, что код НЕ будет этого делать, и мне следует задать новый вопрос.

Я думаю, что на этот вопрос подробно ответил Саймон Мурье в связанном вопросе stackoverflow.com/questions/35451540/…. Проверьте это ;)

borealis-c 05.07.2024 17:28

@borealis-c Думаю, вы не заметили, что я спросил Саймона Мурье в комментариях о получении ВСЕХ свойств, и он сказал, что код НЕ будет этого делать, и мне следует задать новый вопрос.

LesFerch 05.07.2024 17:37

Я думаю, вам следовало упомянуть этот комментарий в этом вопросе, чтобы избежать именно такого обмена мнениями. Именно это подразумевается под разделом «Поиск и исследование» в Как спрашивать.

Heretic Monkey 05.07.2024 17:55

@HereticMonkey Я думал, что ясно выразился, подчеркнув ВСЕ свойства, и нашел много примеров кода для получения свойств отдельного файла, но я воспользуюсь вашим советом и добавлю дополнительные пояснения. И спасибо, что исправили мою грамматическую ошибку.

LesFerch 05.07.2024 17:57

Корневой API, который вы хотите использовать, — это PSEnumeratePropertyDescriptions (Learn.microsoft.com/en-us/windows/win32/api/propsys/… ). Однако на C# это довольно сложная задача, поскольку вам приходится использовать довольно много interop (есть множество свойств и методов). Проще использовать этот проект генератора взаимодействия github.com/microsoft/CsWin32, но требуется .NET5+ и небезопасный код. Это нормально или что именно вам нужно?

Simon Mourier 05.07.2024 19:12

Вот пример кода .NET 8, который, например, выводит все ключи свойств, каноническое имя и отображаемое имя Pastebin.com/raw/qgGPDYrX

Simon Mourier 05.07.2024 19:40

@SimonMourier Да, вот и все! Спасибо! Чтобы сократить список до тех элементов, которые мне нужны, мне также пришлось фильтровать ISVIEWABLE. Я сделал это, добавив pd.GetTypeFlags(PROPDESC_TYPE_FLAGS.PDTF_ISVIEWABLE, out var v); и bool isViewable = v == PROPDESC_TYPE_FLAGS.PDTF_ISVIEWABLE;. Если вы опубликуете ответ со своим кодом, я отмечу его как ответ.

LesFerch 06.07.2024 02:31

@SimonMourier Моя текущая программа использует .Net 4.8 и работает в Windows 7 и выше. Я могу перейти на .Net 8 (и ограничиться Win 10 и 11), но если я хочу придерживаться 4.8, можно ли адаптировать этот код дампа к .Net 4.8?

LesFerch 06.07.2024 06:50
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
8
77
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Чтобы получить все свойства, необходимо использовать функцию PSEnumeratePropertyDescriptions

Вот версия .NET Framework, которая (частично) переопределяет некоторые необходимые вам интерфейсы и типы:

internal class Program
{
    static void Main()
    {
        PSEnumeratePropertyDescriptions(PROPDESC_ENUMFILTER.PDEF_ALL, typeof(IPropertyDescriptionList).GUID, out var list);
        for (var i = 0; i < list.GetCount(); i++)
        {
            var pd = list.GetAt(i, typeof(IPropertyDescription).GUID);

            var pk = pd.GetPropertyKey();
            Console.WriteLine($"Property Key   : {pk.fmtid:B} {pk.pid}");

            pd.GetCanonicalName(out var name);
            Console.WriteLine($"Canonical Name : {Marshal.PtrToStringUni(name)}");
            Marshal.FreeCoTaskMem(name);

            pd.GetDisplayName(out name);
            if (name != IntPtr.Zero)
            {
                Console.WriteLine($"Display Name   : {Marshal.PtrToStringUni(name)}");
                Marshal.FreeCoTaskMem(name);
            }

            var viewable = pd.GetTypeFlags(PROPDESC_TYPE_FLAGS.PDTF_ISVIEWABLE) == PROPDESC_TYPE_FLAGS.PDTF_ISVIEWABLE;
            Console.WriteLine($"Viewable       : {viewable}");
            Console.WriteLine();
        }
    }

    public struct PROPERTYKEY
    {
        public Guid fmtid;
        public int pid;
    }

    public enum PROPDESC_ENUMFILTER
    {
        PDEF_ALL = 0,
        PDEF_SYSTEM = 1,
        PDEF_NONSYSTEM = 2,
        PDEF_VIEWABLE = 3,
        PDEF_QUERYABLE = 4,
        PDEF_INFULLTEXTQUERY = 5,
        PDEF_COLUMN = 6,
    }

    [Flags]
    public enum PROPDESC_TYPE_FLAGS
    {
        PDTF_DEFAULT = 0,
        PDTF_MULTIPLEVALUES = 0x1,
        PDTF_ISINNATE = 0x2,
        PDTF_ISGROUP = 0x4,
        PDTF_CANGROUPBY = 0x8,
        PDTF_CANSTACKBY = 0x10,
        PDTF_ISTREEPROPERTY = 0x20,
        PDTF_INCLUDEINFULLTEXTQUERY = 0x40,
        PDTF_ISVIEWABLE = 0x80,
        PDTF_ISQUERYABLE = 0x100,
        PDTF_CANBEPURGED = 0x200,
        PDTF_SEARCHRAWVALUE = 0x400,
        PDTF_DONTCOERCEEMPTYSTRINGS = 0x800,
        PDTF_ALWAYSINSUPPLEMENTALSTORE = 0x1000,
        PDTF_ISSYSTEMPROPERTY = unchecked((int)0x80000000),
        PDTF_MASK_ALL = unchecked((int)0x80001fff),
    }

    [DllImport("propsys")]
    public static extern int PSEnumeratePropertyDescriptions(PROPDESC_ENUMFILTER filterOn, [MarshalAs(UnmanagedType.LPStruct)] Guid riid, out IPropertyDescriptionList ppv);

    [ComImport, Guid("1F9FC1D0-C39B-4B26-817F-011967D3440E"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPropertyDescriptionList
    {
        int GetCount();
        [return: MarshalAs(UnmanagedType.Interface)]
        IPropertyDescription GetAt(int iElem, [MarshalAs(UnmanagedType.LPStruct)] Guid riid);
    }

    [ComImport, Guid("6F79D558-3E96-4549-A1D1-7D75D2288814"), InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IPropertyDescription
    {
        PROPERTYKEY GetPropertyKey();
        [PreserveSig] int GetCanonicalName(out IntPtr name);
        int GetPropertyType();
        [PreserveSig] int GetDisplayName(out IntPtr name);
        [PreserveSig] int GetEditInvitation(out IntPtr name);
        PROPDESC_TYPE_FLAGS GetTypeFlags(PROPDESC_TYPE_FLAGS mask);
        // following methods are undefined in this code since we don't need it
    }
}

Если вы используете .NET Core 5+, вы можете воспользоваться проектом CsWin32, который определит для вас все необходимое взаимодействие. Обратите внимание, что использование этого требует каким-то образом объявить ваш код небезопасным. Код Introp также генерируется автоматически, поэтому его использование не является строго эквивалентным (uint vs int и т. д.).

csproj:

<Project Sdk = "Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net8.0</TargetFramework>
    <Nullable>enable</Nullable>
    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
  </PropertyGroup>

  <ItemGroup>
    <None Remove = "NativeMethods.txt" />
  </ItemGroup>

  <ItemGroup>
    <AdditionalFiles Include = "NativeMethods.txt" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include = "Microsoft.Windows.CsWin32" Version = "0.3.106">
      <PrivateAssets>all</PrivateAssets>
      <IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
    </PackageReference>
  </ItemGroup>

</Project>

NativeMethods.txt:

IPropertyDescription
IPropertyDescriptionList
PSEnumeratePropertyDescriptions

Программа.cs

using System;
using System.Runtime.InteropServices;
using System.Runtime.Versioning;
using Windows.Win32;
using Windows.Win32.UI.Shell.PropertiesSystem;

[assembly: SupportedOSPlatform("windows6.0.6000")]

namespace DumpShellProps;

internal class Program
{
    static unsafe void Main()
    {
        PInvoke.PSEnumeratePropertyDescriptions(PROPDESC_ENUMFILTER.PDEF_ALL, typeof(IPropertyDescriptionList).GUID, out var ppv).ThrowOnFailure();
        var list = (IPropertyDescriptionList)Marshal.GetTypedObjectForIUnknown((nint)ppv, typeof(IPropertyDescriptionList));
        list.GetCount(out var count);
        for (uint i = 0; i < count; i++)
        {
            list.GetAt(i, typeof(IPropertyDescription).GUID, out var obj);
            var pd = (IPropertyDescription)obj;

            pd.GetPropertyKey(out var pk);
            Console.WriteLine($"Property Key   : {pk.fmtid:B} {pk.pid}");

            pd.GetCanonicalName(out var cname);
            Console.WriteLine($"Canonical Name : {cname}");
            Marshal.FreeCoTaskMem((nint)cname.Value);
            try
            {
                pd.GetDisplayName(out var dname);
                Console.WriteLine($"Display Name   : {dname}");
                Marshal.FreeCoTaskMem((nint)dname.Value);
            }
            catch { } // not all properties have a display name
            Console.WriteLine();
        }
    }
}

Другие вопросы по теме