Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: WinAPI и системное программирование > Создание виртуальной папки (shell extensions)...


Автор: davinchi 8.7.2012, 23:32
Всем привет!
Делаю «shell extensions» под windows 7 x64, которое добавляется «пока» в пространство имен рабочего стола, закрепляется в панели навигации проводника и реализует метод CreateViewObject интерфейса IShellFolder, т.е. отображает форму при открытии виртуальной папки. До этого момента в MSDN было все понятно, а вот далее с реализацией интерфейсов не совсем понятно… 
Первое с чем столкнулся – мне необходимо, чтобы мое расширение существовало в собственном пространстве имен (чтобы не перечисляться в составе рабочего стола или моего компьютера). Делается вроде элементарно – при регистрации СОМ объекта и создании разделов в реестре создаю собственное пространство имен и добавляю GUID объекта в него. Из пространства имен рабочего стола закрепится не проблема, а как собственное пространсво имен добавить в панель навигации проводника и при этом выше папки "Избранное"?

Далее, расширение должно просто показывать несколько виртуальных элементов, каждый из которых по клику открывает в проводнике заданный физический путь. Виртуальные элементы должны быть полностью статичны, т.е. не удаляться, не переименовываться и не иметь никаких меню. Сама виртуальная папка должна всегда открываться с отображением элементов с крупными заначками и их группировкой и эти параметры должны быть не доступны для изменения. Реализовал все это на форме в TListView, но выглядит как-то не гуд, от основной системы отличается, поэтому думаю капнуть глубже и вывести виртуальные элементы через интерейсы. Попробовал капнуть в сторону интерфейсов перечисления элементов и понял, что надо иди за помощью smile 

Подскажите на пальцах, в общих чертах, порядок и т.п.:
- Как перечислить элементы в папке?
- Как сгруппировать элементы и задать названия групп?
- Как задать начальные параметры отображения виртуальной папки и запретить их изменение?
- Как добавить вверху информационную панельку как в папке «Библиотеки»?

В идеале нужен пример, который реализует самые базовые вещи – виртуальную папу и жестким хардкодингом добавляет в нее несколько виртуальных элементов. Может есть у кого рабочий пример под Delphi XE2…

Автор: Ne1tr1n0 9.7.2012, 10:04
На http://forum.sources.ru/index.php?showtopic=286380 проводился конкурс по Delphi, там как раз одно из заданий было сделать расширение оболочки, реализующее виртуальные папки. Как вариант взять что-нить из ответов участников, есть очень толковые решения.
Кроме того, есть библиотека компонентов http://shellplus.com/, которая позволяет быстро создавать различные расширения оболочки Windows. Начиная с версии 3.6 позволяет создавать как 32 так и 64-битные расширения.

Автор: davinchi 9.7.2012, 12:51
Ne1tr1n0, спасибо конечно... но на исходниках я был, я оттуда и взял работы за основу, но там не одно задание на Deplhi XE2 под х64 не компилируется, хотя работа Bems'a видимо то что мне нужно...
Shell+ я тоже находил, я так понимаю это платные компоненты? И не факт что они сделают, то что мне нужно...

Автор: Ne1tr1n0 9.7.2012, 13:46
На рутрекере можно и бесплатно найти...

Автор: Ne1tr1n0 9.7.2012, 14:05
А насчет компиляции под x64 - в XE2 надо посмотреть, какие типы используются, и поменять объявления переменных, там есть некоторые отличия например в разрядности некоторых типов данных (Pointer'ы например у тебя будут 64-битные, Integer остался 32-битным, появился NativeInt который при компиляции на 64-битной тачке будет 8-байтным (64 бита), а на 32-битной соответственно 4-байтным (32 бита).

Автор: bems 10.7.2012, 23:19
моё решение компилится под ХЕ2 если поменять порядок полей в константе
Код

const InvariantFormatSettings: TFormatSettings =  // Настройки из LOCALE_INVARIANT
      (CurrencyString: '¤';                       // Захардкодены здесь потому что в Win2k нет LOCALE_INVARIANT
       CurrencyFormat: 0;
       CurrencyDecimals: 2;
       DateSeparator: '/';
       TimeSeparator: ':';
       ListSeparator: ',';
       ShortDateFormat: 'MM/dd/yyyy';
       LongDateFormat: 'dddd, dd MMMM yyyy';
       TimeAMString: 'AM';
       TimePMString: 'PM';
       ShortTimeFormat: 'hh:mm';
       LongTimeFormat: 'hh:mm:ss';
       ShortMonthNames: ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
       LongMonthNames: ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
       ShortDayNames: ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
       LongDayNames: ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
       ThousandSeparator: ',';
       DecimalSeparator: '.';
       TwoDigitYearCenturyWindow: 0;
       NegCurrFormat: 0);

Авторы RTL коварно всё переставили, я не виноват smile
Под х64 тоже компилится, но работу не проверял

Автор: northener 10.7.2012, 23:55
<offtop>
Цитата(bems @  10.7.2012,  23:19 Найти цитируемый пост)
Авторы RTL коварно всё переставили, я не виноват

Возможно потому, что призом тогда было изделие MS? smile
</offtop>

Автор: bems 10.7.2012, 23:56
Какая разница что тогда было? Поля в записи переставлены независимо от этого

Автор: northener 11.7.2012, 00:58
Цитата(bems @  10.7.2012,  23:56 Найти цитируемый пост)
Поля в записи переставлены

На всякий пожарный случай. 
Скажи что и где изменено в описании структуры.
При возможности укажи и "когда"(в какой версии) изменено.

Автор: bems 11.7.2012, 01:54
northener, новый порядок полей привел выше
Старый: 
Код

const InvariantFormatSettings: TFormatSettings =  // Настройки из LOCALE_INVARIANT
      (CurrencyFormat: 0;                         // Захардкодены здесь потому что в Win2k нет LOCALE_INVARIANT
       NegCurrFormat: 0;
       ThousandSeparator: ',';
       DecimalSeparator: '.';
       CurrencyDecimals: 2;
       DateSeparator: '/';
       TimeSeparator: ':';
       ListSeparator: ',';
       CurrencyString: '¤';
       ShortDateFormat: 'MM/dd/yyyy';
       LongDateFormat: 'dddd, dd MMMM yyyy';
       TimeAMString: 'AM';
       TimePMString: 'PM';
       ShortTimeFormat: 'hh:mm';
       LongTimeFormat: 'hh:mm:ss';
       ShortMonthNames: ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
       LongMonthNames: ('January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December');
       ShortDayNames: ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
       LongDayNames: ('Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday');
       TwoDigitYearCenturyWindow: 0);


старый был в 2010 новый в ХЕ2
в какой именно версии поменяли точно не знаю

Автор: davinchi 11.7.2012, 08:48
bems, рад, что ты заглянул в мою тему... я как раз на твоей работе остановился, у тебя реализация самой фабрики круче всех реализована, блокировки и т.п. все как надо...
А я всю голову сломал с этим InvariantFormatSettings, я даже и подумать не мог что там порядок важен, думал там элементы структуры все-таки по именам сопоставляются, так и не одолел, поставил веде вместо InvariantFormatSettings - UserFormatSettings, в результате виртуальная папка просто не открывается... Попробую поправить описание структуры, о результатах отпишусь...
bems, а при дерегистрации DLL она должна выгружаться или для выгрузки надо убить\запустить explorer? и есть ли возможность прицепиться к загруженной DLL для ее отладки в IDE?

Автор: davinchi 11.7.2012, 10:21
bems, правка объявления InvariantFormatSettings вернула код модуля "uCollections.pas" в первозданный вид, все свои вышеописанные привки убрал, но дело по-моему не в коллекции... Мои ковыряния показали, что проблема в методе CreateViewObject:
Код
function TFolder.CreateViewObject(hwndOwner: HWND; const riid: TIID; out ppvOut): HResult;
var
 cr: SFV_CREATE;
begin
 Pointer(ppvOut) := nil;
 if not IsEqualGUID(riid, IID_ISHELLVIEW) then Exit(E_NOINTERFACE);

 Result := QueryInterface(IID_ISHELLFOLDER, cr.pshf);
 if Result = S_OK then
  begin
   cr.cbSize := SizeOf(cr);
   Pointer(cr.psvOuter) := nil;
   Pointer(cr.psfvcb) := nil; // На самом деле IShellFolderViewCB не опцилнальный.
   Result := FCollection.QueryInterface(IID_IShellFolderViewCB, cr.psfvcb);
   if Succeeded(Result) // Без него не будет даже реакции на сообщения об изменениях
     then Result := SHCreateShellFolderView(cr, IShellView(ppvOut));
   //MyAdd
   if Succeeded(Result)
    then ShowMessage('CreateViewObject - SHCreateShellFolderView: TRUE')
    else ShowMessage('CreateViewObject - SHCreateShellFolderView: FALSE'); // <- Вот это выводится
  end;
end;

Если SHCreateShellFolderView возвращает S_FALSE это наверное не очень хорошо?

Автор: bems 11.7.2012, 17:01
Цитата(davinchi @  11.7.2012,  08:48 Найти цитируемый пост)
даже и подумать не мог что там порядок важен
компилятор же явно говорит что отличается порядок полей

Цитата(davinchi @  11.7.2012,  08:48 Найти цитируемый пост)
bems, а при дерегистрации DLL она должна выгружаться или для выгрузки надо убить\запустить explorer?
должно выгружаться, но практика показывает что этого не происходит. При  этом моя реализация DllCanUnloadNow возвращает S_OK, так что думаю просто винда откладывает выгрузку

Цитата(davinchi @  11.7.2012,  08:48 Найти цитируемый пост)
есть ли возможность прицепиться к загруженной DLL для ее отладки в IDE?
Attach to process

Цитата(davinchi @  11.7.2012,  10:21 Найти цитируемый пост)
Если SHCreateShellFolderView возвращает S_FALSE это наверное не очень хорошо? 
да, это плохо

Автор: davinchi 12.7.2012, 09:24
bems, 
Цитата(bems @  11.7.2012,  18:01 Найти цитируемый пост)
компилятор же явно говорит что отличается порядок полей

мне компилятор говорил "type mismatch", ну да и ладно, проехали уже...
Цитата(bems @  11.7.2012,  18:01 Найти цитируемый пост)
должно выгружаться, но практика показывает что этого не происходит. При  этом моя реализация DllCanUnloadNow возвращает S_OK, так что думаю просто винда откладывает выгрузку

да, непонятно как-то, я бы даже сказал что может выгрузить, а может и не выгрузить, от чего зависит непонятно...
Цитата(bems @  11.7.2012,  18:01 Найти цитируемый пост)
Attach to process

это понятно, а к какому процессу цепляться?
Цитата(bems @  11.7.2012,  18:01 Найти цитируемый пост)
да, это плохо

да, плохо, и происходит это по тому, что в win7 для успешного выполнения SHCreateShellFolderView надо чтобы был реализован интерфейс IPersistFolder2, реализующий метод GetCurFolder...
вот ломаю голову, как его прикрутить...

Автор: bems 12.7.2012, 10:41
Цитата(davinchi @  12.7.2012,  09:24 Найти цитируемый пост)
это понятно, а к какому процессу цепляться?

explorer.exe
но практика вчера показала что дельфи при таком аттаче не может подцепить исходники дллки, а отдним CPU сыт не будешь
Решения пока не нашел

Цитата(davinchi @  12.7.2012,  09:24 Найти цитируемый пост)
да, плохо, и происходит это по тому, что в win7 для успешного выполнения SHCreateShellFolderView надо чтобы был реализован интерфейс IPersistFolder2, реализующий метод GetCurFolder...

да, похоже на то. попробую прикрутить

Автор: davinchi 12.7.2012, 11:20
bems, 
Цитата(bems @  12.7.2012,  11:41 Найти цитируемый пост)
... а отдним CPU сыт не будешь

ага, и я о том же...

Цитата(bems @  12.7.2012,  11:41 Найти цитируемый пост)
да, похоже на то. попробую прикрутить

я тебе таааак благодарен буду если сделаешь рабочий пример под семерку х64, с жескозахардкоденными итемами... а то сразу еще и в реализацию итемов через коллекцию тяжело въехать...

Автор: bems 14.7.2012, 06:19
Мне не удалось заставить отладчик подцепить отладочную информацию, поэтому я воспользовался пунктом меню Load process
в дереве слева выбирал local, в строке process указал путь к проводнику C:\Windows\explorer.exe
в source path указал папку с проектом
теперь можно отлаживать, предварительно прибивши дефолтный проводник из таск менеджера

Касательно интерфейса IPersistFolder2, я сделал такую реализацию метода:
Код

function TFolder.GetCurFolder(var ppidl: PItemIdList): HResult;
begin
  ppidl := CoTaskMemAlloc(Length(FRoot));
  if ppidl = nil
     then Result := E_OUTOFMEMORY
     else begin
          Move(Pointer(FRoot)^, ppidl^, Length(FRoot));
          Result := S_OK;
          end;
end;


потом в процессе отладки выяснилось, что когда оболочки запрашивает заголовки колонок (вызов GetDetailsOf с pidl = nil) у меня еще нет этой информации, я возвращаю ошибку и отображение папки наламывается. Видимо в ХР порядок вызовов был другим и такая ситуация не возникала. Чтобы исправить это, я изменил таким образом:
Код

function TTextFileCollection.GetDetailPair(iColumn: UINT; out StringList: THashedStringList;
 out Index: Integer; out AClass: TBaseItemClass): HResult;
var
 PrevIndex: Integer;
 Groups: TDetailGroups;
 Cls: TBaseItemClass;
begin
 PrevIndex := -1;
 Groups := FCopybleFields.DetailGroups as TDetailGroups;
 if Groups.Classes.Count = 0 then LoadFromFile;

 for Cls in Groups.Classes do
  begin
   StringList := Groups.GetStringList(Cls);
   if (Integer(iColumn) > PrevIndex) and (Integer(iColumn) <= PrevIndex + StringList.Count) then
    begin
     Index := Integer(iColumn) - (PrevIndex + 1);
     AClass := Cls;
     Exit(S_OK);
    end;
   Inc(PrevIndex, StringList.Count)
  end;
 Result := E_FAIL
end;
добавлена тут строка if Groups.Classes.Count = 0 then LoadFromFile;

Теперь папка открывается, но при выборе элемента падает с эксцепшном. Дебажу дальше

Автор: bems 14.7.2012, 06:40
Не хватало IsMultithread := True; в дпр-файле
больше не падет

Автор: davinchi 15.7.2012, 16:54
bems, спасибо! папка открылась, но при смене фокуса от одного итема к другому explorer падает...
и как итемам задать иконки и реакцию на клик?

Автор: bems 16.7.2012, 01:42
Цитата(davinchi @  15.7.2012,  16:54 Найти цитируемый пост)
 при смене фокуса от одного итема к другому explorer падает

это уже с IsMultithread := True ?


Цитата(davinchi @  15.7.2012,  16:54 Найти цитируемый пост)
и как итемам задать иконки и реакцию на клик?

реализовать IContextMenu с действиями по умолчанию (CMF_DEFAULTONLY)

Автор: crazynetwork 21.7.2012, 13:04
Цитата(davinchi @ 9.7.2012,  12:51)
Ne1tr1n0, спасибо конечно... но на исходниках я был, я оттуда и взял работы за основу, но там не одно задание на Deplhi XE2 под х64 не компилируется, хотя работа Bems'a видимо то что мне нужно...
Shell+ я тоже находил, я так понимаю это платные компоненты? И не факт что они сделают, то что мне нужно...

В этих библиотеках есть компонент TSxChangeNotifier. Он следит за изменениями файлов в папках. Кто знает как его заставить следить и за папками тоже??

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)