清除同一属性后,设置属性时拒绝访问
Access denied on setting property after clearing same property
我一直在尝试使用Windows的IPropertyStore
方法编辑一些音频文件的元数据,并遇到了这个奇怪的问题。通过IPropertyStore::SetValue()
将空PROPVARIANT
值设置为属性存储的键后,以下尝试设置该键的值失败,返回值为0x80030005
,Visual Studio 通知我这是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()
),它似乎以独占方式打开文件 - 就像通过调用0
作为共享模式的CreateFile()
一样。打开后,我通过CreateFile()
再次打开文件的各种尝试都没有成功;都失败了,ERROR_SHARING_VIOLATION
.我相信这排除了另一个进程(甚至是我的程序的进程)在第一次属性更改后"窃取"文件访问权限(即使,正如我在评论中所讨论的,属性更改直到调用IPropertyStore::Commit()
才会写入)。
即使调用了IPropertyStore::Commit()
方法(该方法将所有挂起的属性更改写入文件),该文件仍保持打开状态,独占。据我观察,此时重新打开文件仍然是不可能的。这很奇怪,因为文档指出"在它返回之前,Commit 会释放初始化处理程序的文件流或路径"。我发现直到IPropertyStore
对象被释放(IUnknown::Release()
),关联的文件仍然处于打开状态。
清除密钥后提交并释放IPropertyStore
对象,然后重新创建它并写入新值,似乎可以完美运行。密钥已成功清除,然后成功重写。但是,这仍然使原始问题悬而未决:为什么我不能在属性存储的一个打开/写入/提交周期中进行清除和重写?
当您将属性设置为 VT_EMPTY 时,您不会"设置"其值,不会"写入空值",不会"清除"它,而是删除它。请注意,正在运行的属性存储的计数递减 (GetCount
),也不能再使用GetAt
了。
IPropertyStore::SetValue 方法的官方文档非常明确:
从 不支持属性存储,这可能会导致意外结果。
- C++ 命名参数习惯用语 - 未设置字符串属性
- 如何将字符串属性设置为 QTreeWidgetItem?
- 由 JOB 中的进程启动的子进程是否可以将 JOB 属性设置为脱离作业?
- 是否可以在单独的线程中将 QObject 设置为 QML 上下文属性?
- CMake 设置使用不正确的参数数调用的目标属性
- 如何使用C++获取/设置OBS中的垂直滚动过滤器属性?
- 设置使用 Cereal 序列化库时可以在序列化函数中访问的属性
- 设置类的 char* 属性
- 创建新对象并立即为其设置属性时出现编译器错误
- boost::log 设置"Channel"通道记录器中的属性
- property_tree:无法设置默认属性值
- 将 QObject* 设置为 QMLEngine 根上下文属性
- 设置文件属性成功,但检索其失败
- 如何将项目属性设置为删除警告MSB8004:中间目录不会以落后的斜线结束
- 如何根据道具名称动态设置对象的属性?
- 重载>>运算符未在输入上设置属性
- 如何将按钮的属性设置为从Qt中的小部件可见
- 将公共属性设置为私有属性
- 使用 boost.accumulators 对将特定属性设置为值的对象进行计数
- 如何将成员属性设置为QGraphicsItem的工具提示?