清除相同属性后无法访问设置属性 [英] Access denied on setting property after clearing same property

查看:95
本文介绍了清除相同属性后无法访问设置属性的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我一直试图使用Windows的 IPropertyStore 方法来编辑某些音频文件的元数据,但遇到了这个奇怪的问题.通过 IPropertyStore :: SetValue()将空的 PROPVARIANT 值设置为属性存储的键之后,尝试设置该键的值的尝试失败,返回值为 0x80030005,Visual Studio通知我是 Access Denied..
这是我可以产生这种行为的最小示例:

I have been trying to edit the metadata of some audio files using Windows' IPropertyStore methods, and came across this strange problem. After setting an empty PROPVARIANT value to a property store's key via IPropertyStore::SetValue(), following attempts to set that key's value fail with a return value of 0x80030005, which Visual Studio informs me is Access Denied..
This is the smallest example with which I could produce this behaviour:

#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();
}

这似乎仅在首先设置一个空值时发生;任何其他值都会导致程序按预期运行,并且两个更改均已成功写入.

This only seems to occur when setting an empty value first; any other value results in the program running as expected, with both changes written successfully.

据我所知, 可以将一个空值写入键-我可以成功地单独写入一个空值,并且当我查看该键时会反映出更改.资源管理器中文件的属性.
此外,我不明白该错误如何可能被拒绝访问"-程序在其下运行的用户(我的标准帐户)绝对具有更改密钥的权限(我可以进入资源管理器中的属性"并手动更改密钥的值就可以了),又怎么可能只拒绝两个键访问中的一个 ?

As far as I can tell from the documentation, it is fine to write an empty value to a key - I can successfully write the empty value by itself and the changes are reflected when I view the file's properties in Explorer.
Furthermore, I don't understand how the error could possibly be "access denied" - the user the program is running under (my standard account) definitely has permissions to change the key (I can go into Properties in Explorer and manually change the key's value just fine), and how could it possibly be access denied for only one of the two key accesses?

那为什么我不能将一个空值写入一个键,然后在以后覆盖它呢?

对于那些想知道为什么我需要清除键然后立即向其写入新值的人-我实际上不是.事件序列恰好发生在较大的程序中,在该程序中所有属性都被清除,然后稍后重新填充了某些键.

更新:
此后,我对 IPropertyStore 对象如何处理文件访问进行了更多实验.
IPropertyStore 对象用文件初始化时(在这种情况下,通过 SHGetPropertyStoreFromParsingName()),似乎是专门打开文件的-就像通过调用 CreateFile(),其中 0 为共享模式.一旦打开,我通过 CreateFile()再次打开文件的各种尝试都没有成功.全部失败,并显示 ERROR_SHARING_VIOLATION .我相信,这会排除第一个属性更改后窃取"文件访问权限的另一个过程(甚至我程序的过程)(尽管正如我在注释中所讨论的那样,属性更改要等到 IPropertyStore:时才写入::Commit()被调用).

Update:
I've since experimented more with how the IPropertyStore object handles file access.
When the IPropertyStore object is initialised with a file (in this case through SHGetPropertyStoreFromParsingName()), it seems to open the file exclusively - as if through calling CreateFile() with 0 as the sharing mode. Once opened, none of my various attempts to open the file again via CreateFile() succeeded; all failed with ERROR_SHARING_VIOLATION. I believe this rules out another process (or even my program's process) from "stealing" the file access rights after the first property change (even though, as I discussed in the comments, property changes are not written until IPropertyStore::Commit() is called).

即使调用 IPropertyStore :: Commit()方法(该方法将所有待处理的属性更改都写入文件),该文件仍保持打开状态.据我观察,此时仍无法重新打开文件.这是特殊的,因为文档指出"[b]返回之前,Commit释放初始化处理程序的文件流或路径".我发现的是直到 IPropertyStore 对象被释放( IUnknown :: Release()),相关文件仍保持打开状态.

Even once the IPropertyStore::Commit() method is called, which writes all pending property changes to the file, the file remains open, exclusively. Reopening the file at this point is still not possible as far as I observed. This is peculiar, since the documentation states "[b]efore it returns, Commit releases the file stream or path with which the handler was initialized". What I found was until the IPropertyStore object was released (IUnknown::Release()), the associated file remained opened.

清除键后提交并释放 IPropertyStore 对象,然后重新创建并写入新值,似乎效果很好.密钥已成功清除,然后成功重写.但这仍然使最初的问题悬而未决:为什么我不能在属性存储的一个打开/写入/提交周期中进行清除和重写?

Committing and releasing the IPropertyStore object after clearing the key, then recreating it and writing the new value, seems to work perfectly. The key is successfully cleared, and then successfully rewritten. However this still leaves the original question open: why can't I do the clear and rewrite in one open/write/commit cycle of the property store?

推荐答案

将属性设置为VT_EMPTY时,不会设置"其值,不会写空值",也不会清除"它,您删除它.请注意,正在运行的属性存储的计数递减( GetCount ),并且您不能再使用 GetAt .

When you set the property to VT_EMPTY, you don't "set" its value, you don't "write an empty value", you don't "clear" it, you remove it. Note that the running property store's count is decremented (GetCount), and you can't use GetAt either anymore.

IPropertyStore :: SetValue方法非常清楚:

从不支持属性存储,可能会导致意外结果.

Removing a property values from a property store is not supported and could lead to unexpected results.

这篇关于清除相同属性后无法访问设置属性的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆