属性窗口中的详细信息选项卡填充了元数据属性处理程序。元数据属性系统是 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(组件对象模型)是微软对独立于语言的面向对象框架的尝试,其起源可以追溯到九十年代,但为了解释的目的,只要说 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
. 这里有趣的方法是GetValue
和SetValue
。属性被分配一个唯一的 GUID,PROPERTYKEY
传递给这些函数的结构包含该 GUID 以识别属性。GetValue
和的实现细节SetValue
留给开发人员,因此由开发人员决定如何以及在何处存储每个属性的值——这些值可以存储在另一个文件、备用文件流或注册表中以列举几个选项——但出于可传输性的原因,建议将值存储在文件本身中。这样,如果文件被压缩并通过电子邮件发送,例如,属性就会随之而来。
属性处理程序 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
就SetValue
可以在IPropertyStore
对象上调用以获取、更改或设置属性的新值。如果使用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;
}