Qual é a maneira correta de implementar uma extensão Shell do manipulador de propriedades gerenciadas?

StackOverflow https://stackoverflow.com/questions/3200245

Pergunta

Agora que o .NET CLR 4.0 oferece suporte à operação lado a lado (SxS), agora deve ser possível gravar shell extensões em código gerenciado.Tentei isso e codificei com êxito um manipulador de propriedades que implementa IPropertyStore, IInitializeWithStream e IPropertyStoreCapabilities.

O manipulador funciona bem e é chamado como esperado ao navegar arquivos através do Explorer.Ele também funciona bem na exibição do Propriedades personalizadas no painel Visualizar e no painel "Detalhe" das propriedades do arquivo.

No entanto, quando tento editar uma propriedade no painel de visualização e, em seguida, clique em "Salvar" Eu recebo um erro "Arquivo em uso" dizendo que o arquivo está aberto no Windows Explorer.

Algumas curiosidades:

  1. Quando o Explorer chama IInitializeWithStream.Initialize, a propriedade STGM é definida como STGM_SHARE_DENY_WRITE.
  2. E em nenhum momento o explorer chamou IPropertyStore.SetValue ou IPropertyStore.Commit.
  3. Vejo chamadas repetidas para meu manipulador em threads diferentes para as mesmas propriedades de arquivo.

Então, o que preciso alterar (ou definir no cadastro) para que o salvamento do imóvel funcione?

Atualizar:

Graças ao Ben, consegui fazer funcionar.A "parte difícil" (pelo menos para mim) foi entender que a interoperabilidade COM nunca chamaria Dispose ou Finalize no meu PropertyHandler.Isso deixava os arquivos que processei abertos até a execução do GC.

Felizmente, o "protocolo manipulador de propriedades" funciona de forma que quando IInitializeWithSream.Initialize() é chamado para ReadValue() o streamMode é ReadOnly, e quando é chamado para SetValue() o streamMode é ReadWrite e Commit() será chamado no final.

int IInitializeWithStream.Initialize( IStream stream, uint grfMode )
{
    _stream = stream;
    _streamMode = (Stgm)grfMode;

    Load();

    // We release here cause if this is a read operation we won't get called back, 
    // and our finializer isn't called. 
    if ( ( _streamMode & Stgm.ReadWrite ) != Stgm.ReadWrite )
    {
        Marshal.ReleaseComObject( _stream );
        _stream = null;
    }
    return HResult.S_OK;
}

int IPropertyStore.Commit()
{
    bool result = false;

    if ( _stream != null )
    {
        result = WriteStream( _stream );
        Marshal.ReleaseComObject( _stream );
        _stream = null;
    }

    return result ? HResult.S_OK : HResult.E_FAIL;
}
Foi útil?

Solução

Sim, você precisa AddRef() do fluxo para mantê-lo aberto e para manter a referência ativa corretamente.

Observe que o indexador também usará seu manipulador de propriedades para abrir o arquivo.Portanto, se você vazar o objeto stream, o arquivo permanecerá aberto.Você pode usar o sysinternals procexp para informar qual processo está com o arquivo aberto ou procmon para informar quais chamadas e parâmetros ele usou.

Outras dicas

O Explorer tenta garantir que não interfira em outros aplicativos que possam estar com o arquivo aberto.O arquivo poderia estar sendo usado legitimamente por outro aplicativo?Existe um gerenciador de visualização aberto?

Às vezes, vemos manipuladores de propriedades que mantêm seus fluxos abertos por mais tempo do que o necessário (ou manipuladores baseados em arquivo que abrem o arquivo com permissões restritivas).Você pode verificar se está liberando o stream em tempo hábil?

Por fim, não acho que isso esteja relacionado ao seu problema imediato, mas o uso de extensões de shell .NET não é compatível.Recomendamos que isso não seja incorporado em nenhum produto.

-Ben

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top