我一直在尝试使用Windows IPropertyStore
方法编辑某些音频文件的元数据,并遇到了这个奇怪的问题。通过PROPVARIANT
将空IPropertyStore::SetValue()
值设置为属性存储的键后,尝试设置该键的值失败并返回值为0x80030005
,Visual Studio通知我{{1} }}。
这是我可以产生这种行为的最小例子:
Access Denied.
这只在首先设置空值时才出现;任何其他值都会导致程序按预期运行,并且两个更改都已成功写入。
据我从文档中可以看出, 可以将空值写入密钥 - 我可以自己成功写入空值,当我查看时会反映更改资源管理器中的文件属性 此外,我不明白错误是如何“访问被拒绝” - 程序运行的用户(我的标准帐户)肯定有权更改密钥(我可以进入资源管理器中的属性并手动更改密钥的价值很好),怎么可能只对两个关键访问中的一个进行拒绝访问?
那么为什么我不能为一个键写一个空值然后再覆盖它呢?
对于那些想知道为什么我需要清除一个密钥然后立即给它写一个新值的人 - 我实际上并不是这样。这一系列事件恰好发生在一个更大的程序中,其中所有属性都被清除,然后重新填充某些键。
更新
我已经对#include <atlbase.h>
#include <filesystem>
#include <PropIdl.h>
#include <Propsys.h>
#include <propkey.h>
#include <propvarutil.h>
#include <ShObjIdl.h>
#include <Windows.h>
namespace fs = std::experimental::filesystem;
int main() {
HRESULT hr;
if (FAILED(hr = CoInitializeEx(NULL, COINIT_MULTITHREADED))) {
// Handle error...
}
fs::path const test_path(fs::current_path() / "test.mp3");
CComPtr<IPropertyStore> property_store;
if (FAILED(hr = SHGetPropertyStoreFromParsingName(test_path.wstring().c_str(), NULL, GPS_READWRITE, IID_PPV_ARGS(&property_store)))) {
// Handle error...
}
// Set an empty value to the key
{
PROPVARIANT property{};
PropVariantInit(&property);
if (FAILED(hr = property_store->SetValue(PKEY_Title, property))) {
// Handle error...
}
if (FAILED(hr = PropVariantClear(&property))) {
// Handle error...
}
}
// Write a new value to the same key
{
PROPVARIANT property{};
if (FAILED(hr = InitPropVariantFromString(L"test file", &property))) {
// Handle error...
}
if (FAILED(hr = property_store->SetValue(PKEY_Title, property))) {
// Always fails here with hr == 0x80030005 "Access Denied."
}
if (FAILED(hr = PropVariantClear(&property))) {
// Handle error...
}
}
if (FAILED(hr = property_store->Commit())) {
// Handle error...
}
CoUninitialize();
}
对象如何处理文件访问进行了更多的实验
当IPropertyStore
对象使用文件初始化时(在这种情况下通过IPropertyStore
),它似乎只打开文件 - 就像通过SHGetPropertyStoreFromParsingName()
调用CreateFile()
一样共享模式。打开后,我通过0
再次打开文件的各种尝试都没有成功;全部都以CreateFile()
失败了。我相信这会排除第一个属性更改后“窃取”文件访问权限的另一个进程(甚至我的程序进程)(尽管如我在评论中所讨论的那样,在ERROR_SHARING_VIOLATION
之前不会写入属性更改所谓的)。
即使调用IPropertyStore::Commit()
方法(将所有待处理的属性更改写入文件),该文件仍然是独占的。在我观察到的情况下,此时仍然无法重新打开文件。这是特殊的,因为documentation状态“[b]之前它返回,Commit释放用于初始化处理程序的文件流或路径”。我发现的是在释放IPropertyStore::Commit()
对象(IPropertyStore
)之前,相关文件仍然打开。
清除密钥后提交和释放IUnknown::Release()
对象,然后重新创建它并写入新值,似乎完美无缺。密钥已成功清除,然后成功重写。然而,这仍然使原始问题保持开放:为什么不能我在属性存储的一个打开/写入/提交周期中进行清除和重写?
答案 0 :(得分:0)
当您将属性设置为VT_EMPTY时,您不必设置&#34;它的价值,你不会写一个空值&#34;,你不清楚&#34;它,你删除它。请注意,正在运行的属性存储计数会递减(GetCount
),您不能再使用GetAt
。
IPropertyStore::SetValue method的官方文档非常明确:
从a中删除属性值 不支持属性存储,可能会导致意外结果。