adding/editing new extended properties to the the details tab in existing files

时间:2018-06-04 17:45:03

标签: c# attributes windows-api-code-pack dsofile

I tried looking this on SO but all the questions that attempt to answer do not provide a full answer.

I actually want to add a property to the details tab of an existing the file. File is of the sldprt extension. The property/value has to be visible in Windows Explorer.

Not sure how this be done with Windows API Code Pack Shell or DSOFile? Any other solution is fine too.

I'm using VS, C# on Windows 10.

If someone could provide a detailed solution, I would be very thankful.

The perfect solution would:

  • Add the property to the file through dsofile, ADS, or whatever means possible.
  • Edit to Windows Registery or whatever part of Windows that is responsible for showing the new property in the details tab of the file properties and in Windows Explorer
  • Show how the property's value can be edited.

This answer of this question does not add attributes to the details tab nor Windows Explorer detailed view.

I think it's possible since SOLIDWORKS (3d package) adds a property called sw last saved with to all SOLIDWORKS files. There are tons of other properties in the details window.

Please if you don't have the right solution, do not answer. Thank you very much.

I'm not sure this question is a duplicate of Add new metadata properties to a file.

Previews:

enter image description here enter image description here

Edit:

Registery for the sldprt extension (for reference purpose):

enter image description here

2 个答案:

答案 0 :(得分:6)

属性窗口中的详细信息选项卡填充了metadata property handlers。元数据属性系统是Microsoft在Windows Vista中引入的,它是开放和可扩展的,使独立开发人员(如Solidworks)能够实现和支持他们自己的文件属性。非常粗略地说,执行流程是这样的:

User clicks file properties
Look up property handler for the file format
If found property handler:
    Query property handler for properties
    Populate file details with queried properties
Else:
    Populate file details with generic file info

属性处理程序是COM对象。 COM (Component Object Model)是微软尝试一种独立于语言的面向对象框架,其起源一直追溯到九十年代,但出于这种解释的目的,可以说COM对象是一个C ++类实现IUnknown接口。属性处理程序必须在其上实现IPropertyStore接口:

struct IPropertyStore : public IUnknown
{
public:
    virtual HRESULT GetCount( 
        DWORD *cProps) = 0;

    virtual HRESULT GetAt( 
        DWORD iProp,
        PROPERTYKEY *pkey) = 0;

    virtual HRESULT GetValue( 
        REFPROPERTYKEY key,
        PROPVARIANT *pv) = 0;

    virtual HRESULT SetValue( 
        REFPROPERTYKEY key,
        REFPROPVARIANT propvar) = 0;

    virtual HRESULT Commit( void) = 0;
};

为开发人员提供CLSID_InMemoryPropertyStore这个界面的便捷实现,以简化他们自己IPropertyStore的实现。这里有趣的方法是GetValueSetValue。为属性分配了唯一的GUID,传递给这些函数的PROPERTYKEY结构包含用于标识属性的GUID。 GetValueSetValue的实现细节留给开发人员,因此由开发人员决定如何以及在何处存储每个属性的值 - 这些值可以存储在另一个文件中,备用文件流,或在注册表中列出一些选项 - 但出于可移植性原因,建议将值存储在文件本身中。这样,如果文件被压缩并通过电子邮件发送,例如,属性就会随之而来。

属性处理程序COM对象被编译为DLL并使用regsvr32向系统注册。这允许Windows知道去哪里寻找特定文件格式的属性。注册后,可以通过多种方式获取属性处理程序,其中一种方便功能SHGetPropertyStoreFromParsingName

HRESULT GetPropertyStore(PCWSTR pszFilename, GETPROPERTYSTOREFLAGS gpsFlags, IPropertyStore** ppps)
{
    WCHAR szExpanded[MAX_PATH];
    HRESULT hr = ExpandEnvironmentStrings(pszFilename, szExpanded, ARRAYSIZE(szExpanded)) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (SUCCEEDED(hr))
    {
        WCHAR szAbsPath[MAX_PATH];
        hr = _wfullpath(szAbsPath, szExpanded, ARRAYSIZE(szAbsPath)) ? S_OK : E_FAIL;
        if (SUCCEEDED(hr))
        {
            hr = SHGetPropertyStoreFromParsingName(szAbsPath, NULL, gpsFlags, IID_PPV_ARGS(ppps));
        }
    }
    return hr;
}

获取后,可以在GetValue对象上调用SetValueIPropertyStore,以获取,更改或设置属性的新值。如果使用SetValue,请务必同时调用Commit

Microsoft提供了一个名为PropertyEdit的实用程序,用于在文件中获取和设置元数据属性,作为其Windows经典示例的一部分。很遗憾他们在帮助页面的任何地方都没有提到它。由于您已经安装了Solidworks,因此您感兴趣的文件格式的属性处理程序应该已经在系统上注册,应该编译PropertyEdit并使用它来获取和设置元数据处理程序支持的属性。它是一个简单的命令行实用程序。

如果您需要或希望支持自己的文件格式的自定义元数据,还有一个完整的样本属性处理程序:RecipePropertyHandler

供参考,按规范名称设置属性:

HRESULT GetPropertyStore(PCWSTR pszFilename, GETPROPERTYSTOREFLAGS gpsFlags, IPropertyStore** ppps)
{
    WCHAR szExpanded[MAX_PATH];
    HRESULT hr = ExpandEnvironmentStrings(pszFilename, szExpanded, ARRAYSIZE(szExpanded)) ? S_OK : HRESULT_FROM_WIN32(GetLastError());
    if (SUCCEEDED(hr))
    {
        WCHAR szAbsPath[MAX_PATH];
        hr = _wfullpath(szAbsPath, szExpanded, ARRAYSIZE(szAbsPath)) ? S_OK : E_FAIL;
        if (SUCCEEDED(hr))
        {
            hr = SHGetPropertyStoreFromParsingName(szAbsPath, NULL, gpsFlags, IID_PPV_ARGS(ppps));
        }
    }
    return hr;
}

HRESULT SetPropertyValue(PCWSTR pszFilename, PCWSTR pszCanonicalName, PCWSTR pszValue)
{
    // Convert the Canonical name of the property to PROPERTYKEY
    PROPERTYKEY key;
    HRESULT hr = PSGetPropertyKeyFromName(pszCanonicalName, &key);
    if (SUCCEEDED(hr))
    {
        IPropertyStore* pps = NULL;

        // Call the helper to get the property store for the
        // initialized item
        hr = GetPropertyStore(pszFilename, GPS_READWRITE, &pps);
        if (SUCCEEDED(hr))
        {
            PROPVARIANT propvarValue = {0};
            hr = InitPropVariantFromString(pszValue, &propvarValue);
            if (SUCCEEDED(hr))
            {
                hr = PSCoerceToCanonicalValue(key, &propvarValue);
                if (SUCCEEDED(hr))
                {
                    // Set the value to the property store of the item.
                    hr = pps->SetValue(key, propvarValue);
                    if (SUCCEEDED(hr))
                    {
                        // Commit does the actual writing back to the file stream.
                        hr = pps->Commit();
                        if (SUCCEEDED(hr))
                        {
                            wprintf(L"Property %s value %s written successfully \n", pszCanonicalName, pszValue);
                        }
                        else
                        {
                            wprintf(L"Error %x: Commit to the propertystore failed.\n", hr);
                        }
                    }
                    else
                    {
                        wprintf(L"Error %x: Set value to the propertystore failed.\n", hr);
                    }
                }
                PropVariantClear(&propvarValue);
            }
            pps->Release();
        }
        else
        {
            wprintf(L"Error %x: getting the propertystore for the item.\n", hr);
        }
    }
     else
    {
        wprintf(L"Invalid property specified: %s\n", pszCanonicalName);
    }
    return hr;
}

答案 1 :(得分:1)

有两个完整的示例项目,演示了如何在Windows 7 SDK示例包中执行此操作:RecipePropertyHandlerPlaylistPropertyHandler

主要思想是实现IPropertyStore 接口,它有点像收集/迭代器:

Commit    Saves a property change.
GetAt     Gets a property key from an item's array of properties.
GetCount  Gets the number of properties attached to the file.
GetValue  Gets data for a specific property.
SetValue  Sets a new property value, or replaces or removes an existing value.

您的代码需要通过实现IInitializeWithStream接口来初始化处理程序。然后,它需要回复GetCountGetAtGetValue调用,以提供属性名称和值。

示例项目我喜欢这样做,并附带有关如何注册属性处理程序的说明。

P.S。 mnistic的回答主要讨论了将属性(和值)添加到某些文件系统中存在的单个文件的方法。此答案提供了将属性添加到特定文件类型的一般解决方案。