Windows 11文件资源管理器自定义属性列无值显示求助
问题:Windows 11文件资源管理器自定义列无显示值
我现在感觉自己蠢透了,在这事上耗了太多时间,肯定是漏了某个基础环节。到底哪里错了?
我想在Windows 11的文件资源管理器里加个新列。查资料得知需要注册属性描述文件和属性处理程序(COM服务器)。我几乎没接触过COM,用C# .NET 8开发,所以选了这条路线而非C/C++(用CsWin32库生成Windows类型和PInvoke方法)。我只想做个类似“Hello World”的功能,让新列对所有文件都显示“Kyle”。我能在文件资源管理器里添加这个列,但列里始终没有任何文件的值。我加了日志,但完全没输出,事件查看器里也没错误。
属性描述文件
<?xml version="1.0" encoding="utf-8"?> <schema xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://schemas.microsoft.com/windows/2006/propertydescription" schemaVersion="1.0"> <propertyDescriptionList publisher="Kyle" product="Custom Properties"> <propertyDescription name="Kyle.CustomProperty.Kyle" formatID="{4F4F4F4F-FFFF-FFFF-FFFF-FFFFFFFFFFFF}" propID="100"> <description>Always displays the name Kyle.</description> <searchInfo inInvertedIndex="false" isColumn="true" /> <typeInfo type="String" multipleValues="false" isViewable="true" isQueryable="false" /> <labelInfo label="Kyle" invitationText="My name" /> <displayInfo alignment="left" /> </propertyDescription> </propertyDescriptionList> </schema>
属性描述注册工具
项目文件
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <OutputType>Exe</OutputType> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <AllowUnsafeBlocks>true</AllowUnsafeBlocks> </PropertyGroup> <ItemGroup> <None Remove="NativeMethods.json" /> <None Remove="NativeMethods.txt" /> </ItemGroup> <ItemGroup> <AdditionalFiles Include="NativeMethods.json" /> <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>
代码文件
using System.Runtime.InteropServices; using Windows.Win32; public class Program { private static void Main(string[] args) { if (args.Length < 2) { Console.WriteLine("Usage: RegisterPropertySchema.exe <register|unregister> <schemaPath>"); return; } var action = args[0]; var schemaPath = args[1]; if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || !OperatingSystem.IsWindowsVersionAtLeast(5, 1, 2600)) { throw new PlatformNotSupportedException("PSRegisterPropertySchema and PSUnregisterPropertySchema are only supported on Windows."); } switch (action.ToLower()) { case "register": RegisterPropertySchema(schemaPath); break; case "unregister": UnregisterPropertySchema(schemaPath); break; default: Console.WriteLine("Invalid action. Use 'register' or 'unregister'."); break; } } private static void RegisterPropertySchema(string schemaPath) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || !OperatingSystem.IsWindowsVersionAtLeast(5, 1, 2600)) { throw new PlatformNotSupportedException("PSRegisterPropertySchema and PSUnregisterPropertySchema are only supported on Windows."); } var result = PInvoke.PSRegisterPropertySchema(schemaPath); if (result == 0) { Console.WriteLine("Property schema registered successfully."); } else if (result == 0x000401A0) // INPLACE_S_TRUNCATED { Console.WriteLine("Property schema PARTIALLY registered. INPLACE_S_TRUNCATED"); } else { Console.WriteLine($"Failed to register property schema. HRESULT: {result:X}"); } } private static void UnregisterPropertySchema(string schemaPath) { if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || !OperatingSystem.IsWindowsVersionAtLeast(5, 1, 2600)) { throw new PlatformNotSupportedException("PSRegisterPropertySchema and PSUnregisterPropertySchema are only supported on Windows."); } var result = PInvoke.PSUnregisterPropertySchema(schemaPath); if (result == 0) { Console.WriteLine("Property schema unregistered successfully."); } else { Console.WriteLine($"Failed to unregister property schema. HRESULT: {result:X}"); } } }
注册命令
.\RegisterPropertySchema.exe register "C:\Program Files\Kyle\CustomProperty\CustomPropertyKyle.propdesc"
注册表添加内容
[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\PropertySystem\PropertySchema\0003] @="C:\\Program Files\\Kyle\\CustomProperty\\CustomPropertyKyle.propdesc" "SchemaId"="{D84D0B56-EAB4-4E60-A125-DD699A684B3C}" "URI"="custompropertykyle.propdesc" "CompactURI"="66a088697b1669173504a8c3d90b3614e147d922d8f298de8116bbcd74e5c6b4"
属性处理程序
项目文件
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net8.0</TargetFramework> <ImplicitUsings>enable</ImplicitUsings> <Nullable>enable</Nullable> <EnableComHosting>true</EnableComHosting> <EnableRegFreeCom>true</EnableRegFreeCom> </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>
代码文件
using System.Runtime.InteropServices; using Windows.Win32.System.Com.StructuredStorage; using Windows.Win32.System.Variant; using Windows.Win32.UI.Shell.PropertiesSystem; [ComVisible(true)] [Guid("12345678-1234-1234-1234-1234567890AB")] public unsafe class CustomPropertyHandler : IPropertyStore { private static string LogFilePath { get; } = @"C:\abc\CustomPropertyHandler.txt"; private static PROPERTYKEY PropertyKeyTitle { get; } = new() { fmtid = new ("4F4F4F4F-FFFF-FFFF-FFFF-FFFFFFFFFFFF") , pid = 100 }; public void GetCount(out uint propertyCount) { try { Log(nameof(GetCount), "START"); propertyCount = 1; } catch (Exception ex) { Log(nameof(GetCount), $" EXCEPTION: {ex.Message}"); propertyCount = 1; } finally { Log(nameof(GetCount), " END"); } } public void GetAt(uint propertyIndex, PROPERTYKEY* key) { try { Log(nameof(GetAt), $"START propertyIndex: {propertyIndex}"); ArgumentOutOfRangeException.ThrowIfNotEqual<uint>(propertyIndex, 0); *key = PropertyKeyTitle; } catch (Exception ex) { Log(nameof(GetAt), $" EXCEPTION: {ex.Message}"); } finally { Log(nameof(GetAt), " END"); } } public void GetValue(PROPERTYKEY* key, out PROPVARIANT pv) { try { Log(nameof(GetValue), $"START key: fmtid={key->fmtid} pid={key->pid}"); pv = new (); if (key->fmtid == PropertyKeyTitle.fmtid && key->pid == PropertyKeyTitle.pid) { Log(nameof(GetValue), " MATCH"); pv.Anonymous.Anonymous.vt = VARENUM.VT_LPWSTR; pv.Anonymous.Anonymous.Anonymous.pwszVal = (char*)Marshal.StringToCoTaskMemUni("Kyle"); } else { Log(nameof(GetValue), " NO MATCH"); pv.Anonymous.Anonymous.vt = VARENUM.VT_EMPTY; } } catch (Exception ex) { Log(nameof(GetValue), $" EXCEPTION: {ex.Message}"); pv = new(); } Log(nameof(GetValue), " END"); } public void SetValue(PROPERTYKEY* key, in PROPVARIANT propvar) => Log(nameof(SetValue), $"START key: {key->fmtid} {key->pid} propvar: vt={propvar.Anonymous.Anonymous.vt} pwszVal={propvar.Anonymous.Anonymous.Anonymous.pwszVal}"); public void Commit() => Log(nameof(Commit), "START"); private static void Log(string method, string message) { using var writer = new StreamWriter(LogFilePath, append: true); writer.WriteLine($"{DateTime.Now:HH:mm:ss.sss} {method} {message}"); } }
注册命令
regsvr32 "C:\Program Files\Kyle\CustomProperty\CustomPropertyKyle.comhost.dll"
注册表添加内容
[HKEY_CLASSES_ROOT\CLSID\{12345678-1234-1234-1234-1234567890AB}] @=".CustomPropertyHandler" [HKEY_CLASSES_ROOT\CLSID\{12345678-1234-1234-1234-1234567890AB}\InProcServer32] @="C:\\Program Files\\Kyle\\CustomProperty\\CustomPropertyKyle.comhost.dll" "ThreadingModel"="Both" [HKEY_CLASSES_ROOT\CLSID\{12345678-1234-1234-1234-1234567890AB}\ProgID] @=".CustomPropertyHandler"
内容的提问来源于stack exchange,提问作者Kyle K
相关产品推荐
相关产品推荐

