Обновление папки, которой нет в файловой системе

В моем расширении оболочки есть папки, которые на самом деле не существуют в файловой системе, а видны только пользователю.

Когда содержимое этих папок изменяется, я хочу их обновить, и в настоящее время я делаю это тем же способом, что и для обычных папок:

Win32.SHChangeNotify(SHCNE_UPDATEDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);

В то время как PIDL - это список идентификаторов папок оболочки, как того требует SHCNF_IDLIST.

Проблема в том, что проводник не обрабатывает мои несуществующие папки. Вместо того, чтобы обновлять их, он отправляет меня обратно в корневую папку.

Я знаю, что создаю PIDL правильно, поскольку этот механизм работает для существующих папок, как упоминалось ранее.

Как я могу изменить обработчик на SHChangeNotify? Или есть лучший способ вызвать обновление?

Изменить:

Как генерируется мой PIDL:

    IntPtr GetPIDL(IFolderItem target)
    {
        Stack stack = new Stack(5);
        IntPtr data = IntPtr.Zero;

        byte[] rootPIDL = null;

        IFolderItem curr = target;
        while (curr != null)
        {
            if (curr.rootPIDL != null)
            {
                rootPIDL = curr.rootPIDL;
            }
            else
            {
                data = curr.SerializeInt();
                stack.Push(data);
            }

            curr = curr.ParentFolder;
        }

        if (rootPIDL == null && stack.Count == 0)
            return IntPtr.Zero;

        object[] x = stack.ToArray();

        IntPtr[] pidls = null;

        int count = stack.Count;
        if (count > 0)
        {
            pidls = new IntPtr[stack.Count];
            for (int i = 0; i < count; i++)
            {
                pidls[i] = (IntPtr)stack.Pop();
            }
        }

        return CreatePIDL(rootPIDL, pidls);
    }

Моя CreatePIDL реализация:

        internal unsafe static IntPtr CreatePIDL(byte[] rootPIDL,IntPtr[] pidls)
        {
            int headerSize = Marshal.SizeOf(typeof(ushort));
            int totalSize = headerSize;
            if (rootPIDL != null)
                totalSize += rootPIDL.Length - headerSize;

            if (pidls!=null && pidls.Length > 0)
            {
                foreach (IntPtr data in pidls)
                {
                    totalSize += PIDLSize(data);
                }
            }

            IntPtr ret = PIDLAlloc(totalSize);
            IntPtr currPos = ret;

            if(rootPIDL!=null)
            {
                Marshal.Copy(rootPIDL, 0, currPos, rootLPIFQ.Length - headerSize);
                currPos = Win32.AdvancePtr(currPos, rootLPIFQ.Length - headerSize);
            }

            if (pidls != null && pidls.Length>0)
            {
                foreach (IntPtr data in pidls)
                {
                    int dataLength = PIDLSize(data);
                    Win32.CopyMemory(currPos, data, dataLength);
                    currPos = Win32.AdvancePtr(currPos, dataLength);
                }
           }
           Marshal.WriteInt16(currPos, (short)0);

            return ret;
        }

        internal static unsafe int PIDLSize(IntPtr ptr)
        {
            return (int) (*((ushort*)ptr));
        }

        internal unsafe static IntPtr PIDLAlloc(int size)
        {
            IntPtr ret = Marshal.AllocCoTaskMem(size);
            if (ret == IntPtr.Zero)
                throw new OutOfMemoryException();

            return ret;
        }

person Mugen    schedule 12.03.2015    source источник
comment
Документация по адресу msdn.microsoft .com / en-us / library / windows / desktop / указывает, что при вызове SHCNE_UPDATEDIR каталог должен существовать.   -  person Mugen    schedule 12.03.2015
comment
Что-то не так с вашим кодом. Я использую SHChangeNotify (SHCNE_UPDATEDIR, SHCNF_IDLIST) для обновления папок виртуального пространства имен, и он работает. Не могли бы вы объяснить, что термин PIDL - это список идентификаторов папок оболочки?   -  person Denis Anisimov    schedule 12.03.2015
comment
@DenisAnisimov Моя цель - обновить заданную папку. Должен ли мой список содержать только ту папку, которую я хочу обновить? В настоящее время я возвращаю список PIDL из текущего каталога и через его родительский элемент до корня. Чтобы получить PIDL данного элемента папки (который реализует IShellFolder и IShellFolder2), я просто использую класс Marshal.   -  person Mugen    schedule 12.03.2015
comment
Список должен содержать цепочку PIDL от корня до вашей папки. Как вы создаете свой PIDL?   -  person Denis Anisimov    schedule 12.03.2015
comment
@DenisAnisimov Я создаю свой PIDL, перебирая папки и их родительские папки, пока не дойду до корневой папки. Для каждой папки я сериализую ее в IntPtr и добавляю в массив. Порядок массива таков, что моя текущая папка является последней, а корневая папка - первой. В конце я создаю единый PIDL из корневых данных и из массива.   -  person Mugen    schedule 15.03.2015
comment
Будет лучше, если вы покажете свой код.   -  person Denis Anisimov    schedule 15.03.2015
comment
@DenisAnisimov Я редактирую свой вопрос с помощью соответствующего кода. Пожалуйста, дайте мне знать, если вы тоже хотите увидеть код CreatePIDL. Я не делюсь им, потому что предпочитаю делиться как можно реже, этот код чувствителен.   -  person Mugen    schedule 15.03.2015
comment
Что за фреймворк вы используете? Есть ли документация по IFolderItem?   -  person Denis Anisimov    schedule 15.03.2015
comment
@DenisAnisimov Я сам реализую IFolderItem на основе некоторого корпоративного кода. Как вы думаете, какая область может быть актуальной? rootPIDL свойство?   -  person Mugen    schedule 16.03.2015
comment
@DenisAnisimov Я инициализирую rootPIDL, используя значение, данное мне IPersistFolder.Initialize(). Как вы думаете, проблема может быть в CreatePIDL()?   -  person Mugen    schedule 18.05.2015
comment
Не могли бы вы показать функцию CreatePIDL?   -  person Denis Anisimov    schedule 18.05.2015
comment
@DenisAnisimov Добавил, посмотрите пожалуйста   -  person Mugen    schedule 18.05.2015
comment
Я не знаю C #, так что ваш код ломает мне мозг :) Что делает curr.SerializeInt? Что информационная переменная данных хранит после curr.SerializeInt?   -  person Denis Anisimov    schedule 18.05.2015
comment
SerializeInt - это функция, которую я реализую как часть IFolderItem. Короче говоря, он просто сериализует представление папки в байтовый массив. Он используется оболочкой через многие вызовы API, не доставлял мне никаких проблем в других местах. Реализация довольно громоздкая. Как вы ожидаете, что это сработает?   -  person Mugen    schedule 18.05.2015
comment
Есть ли 2 байта с размером массива в начале массива, созданного SerializeInt?   -  person Denis Anisimov    schedule 18.05.2015
comment
@DenisAnisimov Да, я выделяю два дополнительных байта, выбранных случайным образом в соответствии с имеющейся у меня документацией   -  person Mugen    schedule 19.05.2015
comment
Случайно? Что ты имеешь в виду? 2 байта должны содержать размер данных, включая эти 2 байта.   -  person Denis Anisimov    schedule 19.05.2015
comment
О да, я пишу два байта, которые содержат размер, включая дополнительные байты, а затем два случайных байта в качестве подписи   -  person Mugen    schedule 19.05.2015
comment
rootPIDL - это указатель? Когда вы инициализируете rootPIDL, используя значение, данное вам IPersistFolder.Initialize, вы копируете все значение или просто устанавливаете rootPIDL в значение? Что такое rootLPIFQ? Не вижу никакого определения. И похоже, что totalSize не включает 2 байта, хранящиеся в Marshal.WriteInt16 (currPos, (коротко) 0)   -  person Denis Anisimov    schedule 19.05.2015
comment
@DenisAnisimov Это действительно указатель. Я использую Marshal.Copy (), чтобы скопировать IntPtr, предоставленный мне функцией Initialize (). rootLPIFQ == rootPIDL, ошибка в расшифровке :) Хммм вы правы насчет totalSize. Кажется странным, я еще раз посмотрю на это   -  person Mugen    schedule 19.05.2015
comment
Я ошибся, totalSize включает 2 дополнительных байта в конце PIDL. И теперь я не вижу ошибок в вашем коде.   -  person Denis Anisimov    schedule 20.05.2015


Ответы (1)


Я нашел обходной путь. Это не очень красиво и не оптимально, но работает хорошо.

Вместо того, чтобы вызывать уведомление с SHCNE_UPDATEDIR, я последовательно выполняю все три из следующих уведомлений:

Win32.SHChangeNotify(SHCNE_MKDIR, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_CREATE, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
Win32.SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST | SHCNF_FLUSH, PIDL, IntPtr.Zero);
person Mugen    schedule 18.03.2015