Избегайте или удалите блокировку из StgOpenStorage () - PullRequest
1 голос
/ 02 ноября 2009

Поздравил,

Я нашел похожий вопрос на Открытие OLE-составных документов только для чтения с StgOpenStorage , но это решение не сработало для меня.

Я пытаюсь открыть файл Outlook .msg с помощью StgOpenStorage (). Моя проблема в том, что StgOpenStorage всегда блокирует мой файл.

Итак, как я могу избежать / снять блокировку, которую StgOpenStorage () добавляет в мой файл?

(см. Последний метод кода OpenStorage ())

using System;
using System.IO;
using System.Runtime.InteropServices;
using System.Runtime.InteropServices.ComTypes;

namespace IStorageLock
{
    [ComImport]
    [Guid("0000000d-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    public interface IEnumSTATSTG
    {
        // The user needs to allocate an STATSTG array whose size is celt.
        [PreserveSig]
        uint Next(uint celt,
                  [MarshalAs(UnmanagedType.LPArray), Out]
                  System.Runtime.InteropServices.ComTypes.STATSTG[] rgelt,
                  out uint pceltFetched);

        void Skip(uint celt);
        void Reset();

        [return: MarshalAs(UnmanagedType.Interface)]
        IEnumSTATSTG Clone();
    }

    [ComImport]
    [Guid("0000000b-0000-0000-C000-000000000046")]
    [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)]
    interface IStorage
    {
        void CreateStream(string pwcsName, uint grfMode, uint reserved1,
                          uint reserved2, out IStream ppstm);

        void OpenStream(string pwcsName, IntPtr reserved1, uint grfMode,
                        uint reserved2, out IStream ppstm);

        void CreateStorage(string pwcsName, uint grfMode, uint reserved1,
                           uint reserved2, out IStorage ppstg);

        void OpenStorage(string pwcsName, IStorage pstgPriority, uint grfMode,
                         IntPtr snbExclude, uint reserved, out IStorage ppstg);

        void CopyTo(uint ciidExclude, Guid rgiidExclude, IntPtr snbExclude,
                    IStorage pstgDest);

        void MoveElementTo(string pwcsName, IStorage pstgDest,
                           string pwcsNewName, uint grfFlags);

        void Commit(uint grfCommitFlags);

        void Revert();

        void EnumElements(uint reserved1, IntPtr reserved2, uint reserved3,
                          out IEnumSTATSTG ppenum);

        void DestroyElement(string pwcsName);

        void RenameElement(string pwcsOldName, string pwcsNewName);

        void SetElementTimes(string pwcsName,
                         System.Runtime.InteropServices.ComTypes.FILETIME pctime,
                             System.Runtime.InteropServices.ComTypes.FILETIME patime,
                             System.Runtime.InteropServices.ComTypes.FILETIME pmtime);

        void SetClass(Guid clsid);

        void SetStateBits(uint grfStateBits, uint grfMask);

        void Stat(out System.Runtime.InteropServices.ComTypes.STATSTG pstatstg,
                  uint grfStatFlag);
    }

    class Lock
    {
        [Flags]
        public enum STGM : int {
            DIRECT           = 0x00000000,
            TRANSACTED       = 0x00010000,
            SIMPLE           = 0x08000000,
            READ             = 0x00000000,
            WRITE            = 0x00000001,
            READWRITE        = 0x00000002,
            SHARE_DENY_NONE  = 0x00000040,
            SHARE_DENY_READ  = 0x00000030,
            SHARE_DENY_WRITE = 0x00000020,
            SHARE_EXCLUSIVE  = 0x00000010,
            PRIORITY         = 0x00040000,
            DELETEONRELEASE  = 0x04000000,
            NOSCRATCH        = 0x00100000,
            CREATE           = 0x00001000,
            CONVERT          = 0x00020000,
            FAILIFTHERE      = 0x00000000,
            NOSNAPSHOT       = 0x00200000,
            DIRECT_SWMR      = 0x00400000,
        }

        [DllImport("ole32.dll")]
        private static extern int StgIsStorageFile([MarshalAs(UnmanagedType.LPWStr)]
                                                   string pwcsName);

        [DllImport("ole32.dll")]
        static extern int StgOpenStorage([MarshalAs(UnmanagedType.LPWStr)]
                                         string       pwcsName,
                                         IStorage     pstgPriority,
                                         STGM         grfMode,
                                         IntPtr       snbExclude,
                                         uint         reserved,
                                         out IStorage ppstgOpen);

        public IStorage OpenStorage(string fileName)
        {
            if (StgIsStorageFile(fileName) != 0) {
                return null;
            }

            IStorage storage = null;

            //
            // StgOpenStorage() locks file 'fileName'
            //
            // Set flags like:
            // [https://stackoverflow.com/questions/1086814/opening-ole-compound-documents-read-only-with-stgopenstorage]
            //
            int stgOpenStorage = StgOpenStorage(fileName, null,
                                                STGM.READ            |
                                                STGM.SHARE_DENY_NONE |
                                                STGM.TRANSACTED,
                                                IntPtr.Zero, 0,
                                                out storage);

            //
            // Try to rename file (for testing purposes only)
            //
            try {
                File.Move(fileName, fileName + @".renamed");
            } catch (Exception ex) {        // exception: file alreay in use by another process
                throw;
            }

            if (stgOpenStorage != 0) {
                return null;
            } else {
                return storage;
            }
        }
    }
}

Надеюсь, вы сможете мне помочь.

С уважением,

Inno

Ответы [ 2 ]

1 голос
/ 03 ноября 2009

Вы пытались прочитать файл в память и использовать вместо него StgOpenStorageOnILockBytes?

0 голосов
/ 09 апреля 2017

Я столкнулся с подобной проблемой, и следующий код решен для меня. Я изменил имя переменной на ваш пример

storage.Commit(0);   // storage is a pointer to IStorage in OP's question
Marshal.ReleaseComObject(storage);
storage = null;

GC.Collect();
GC.Collect();  // call twice for good measure
GC.WaitForPendingFinalizers();      

Это основано на примере кода Mirosoft на Извлечение встроенных файлов из документов Office .

Так что в COM вам нужно IUnknown.Release и уменьшить счетчик ссылок до нуля, чтобы объект уничтожил себя детерминистически, но все .NET / COM Interop не всегда так просто , Для IStorage , вероятно, лучше позвонить IStorage.Commit перед вызовом Marshal.ReleaseComObject , который IUnknown.Release для вас. В следующей строке установка переменной в null явно помогает пометить объект для сборки мусора, который мы затем вызываем. Мы вызываем GC.Collect дважды, потому что существует несколько поколений объектов, ожидающих сбора, и потому что это Пример кода Microsoft. Наконец, еще одна детерминированная обработка происходит в GC.WaitForPendingFinalizers .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...