# ЧАВО - Часто задаваемые вопросы

### Общие вопросы

1. Как мне спрятать курсор?
2. А как перевести в нужную позицию?
3. Чтобы плагин работал и в редакторе и в shell (и на панели) что нужно ставить во флагах: Info->Flags=?????;
4. Возможно динамическое отключение/подключение плагина не перезапуская FAR?
5. Plug-in`ы в редакторе срабатывают только при активизации? А можно ли сварганить такой, чтобы в фоновом режиме работал?
6. А можно в одном DLL-модуле держать несколько плагинов...
7. ... а потом различать что хотел пользователь по параметру Item в функции OpenPlugin - правильно?
8. Возможно ли при нынешнем API сделать плагин, работающий в фоновом режиме?
9. Есть файл, который можно открыть плагином. Могy ли я как-то об этом yзнать? Лучше всего отдать командy на cd тyда и получить ответ - смогли/не смогли.
10. Как бы это из плагинового модуля получить полный путь до него самого?...
11. Можно ли из плагина управлять переключением фоновых экранов ФАРа?
12. А как фаp определяет, что в клипборде лежит именно вертикальный блок?
13. На чём стоит кypсоp в текущей панели?...
14. Нельзя ли пpи перерисовке любой панели перерисовывать заголовок окна...
15. Откуда FAR берёт процедуры для копирования/удаления/... файлов?
16. Как можно получить имя каталога, из которого запущен FAR?
17. А как уменьшить размер DLL?
18. А что за дупы в клипборде идут, зачем 2 раза одну и ту же в Clipboard помещать?
19. Как определить что есть "связь каталогов"...
20. ...что скрывается по именем '\\?\Volume{...'
21. А ещё что нить полезного про символические ссылки можно узнать?
22. Можно ли получить для редактируемого файла флаг изменён...
23. Как узнать имя файла, загруженного во вьюер?
24. Возможно ли каким-то способом в FSF.ProcessName...
25. Как Windows "сбрасывает" имя файла из Проводника в консольное окно?
26. Почему-то не работает ProcessKey...
27. Можно ли из плагина перейти на определённый файл в директории?
28. Как из плагина запустить что-нибудь? И как показать вывод этой программы под панелями?

### Диалоги

1. Если я не использую функцию Dialog работы с диалогом, то каким образом обрабатывать ввод данных? Самому писать?
2. Если я использую Dialog, а в нём строку ввода, то будет ли работать функции ProcessEditorInput и ProcessEditorEvent для этого элемента управления?
3. Можно ли динамически в Dialog менять элементы типа Static (их заголовки)?
4. Мне не хватает того набора управляющих элементов, которые обрабатывает FAR...
5. А что это ListBox и ComboBox полуфабрикатные какие-то?
6. Почему в диалоге "нового" стиля не срабатывают кнопки на закрытие окна?
7. И что - мне теперь самому закрывать диалог?
8. А если я мышкой - оно придёт или ещё ловить DN_MOUSECLICK?
9. И DefaultButton самому обрабатывать?
10. SEPARATOR не желает доходить до правого конца рамки...
11. А можно как-нибудь выключить автовыбор элемента мышью в листе...

### Общие вопросы

1. Q: Как мне спрятать курсор?
   A: Windows API console API: SetConsoleCursorInfo, только не забудь его потом вернуть на место.
2. Q: А как перевести в нужную позицию?
   A: Там же: SetConsoleCursorPosition
3. Q: Чтобы плагин работал и в редакторе и в shell (и на панели) что нужно ставить во флагах: Info->Flags=?????;
   A: PF_EDITOR
4. Q: Возможно динамическое отключение/подключение плагина не перезапуская FAR?
   A: Да.
5. Q: Плагины в редакторе срабатывают только при активизации? А можно ли сварганить такой, чтобы в фоновом режиме работал?
   A: Можно. Экспортировать ProcessEditorInputW и ему будет приходить все нажатия клавиш в редакторе.
6. Q: А можно в одном DLL-модуле держать несколько плагинов... Несколько функций, так сказать... И чтобы каждый вызывался, как будто бы находился в отдельном DLL-файле (пусть даже с полной загрузкой всего DLL`я)...
   A: Да. В PluginInfo можно указать несколько строк, добавляемых к plugins menu. Но это не является "хорошим тоном".
7. Q: ...а потом различать что хотел пользователь по параметру Item в функции OpenPlugin?
   A: Правильно.
8. Q: Возможно ли при нынешнем API сделать плагин, работающий в фоновом режиме?
   A: Разве что в редакторе. В панелях такое не поддерживается.
9. Q: Есть файл, который можно открыть плагином. Могу ли я как-то об этом узнать? Лучше всего отдать команду на cd туда и получить ответ - смогли/не смогли.
   A: Нет, api для взаимодействия между плагинами в FAR отсутствует. Тут одной проверкой на принадлежность файла не обойтись.
10. Q: Как бы это из плагинового модуля получить полный путь до него самого?...
    A: В функцию SetStartupInfoW far передаёт указатель на структуру PluginStartupInfo. Полное имя модуля содержится в поле ModuleName оной структуры.
11. Q: Можно ли из плагина управлять переключением фоновых экранов FAR?
    A: Можно.
12. Q: А как FAR определяет, что в клипборде лежит именно вертикальный блок?
    A: У вертикального блока clipboard format - "FAR_VerticalBlock"
13. Q: Скажи, а что - никак нельзя узнать, на чём стоит курсор в текущей панели? Т.е. меня интересует - на каталоге или на файле...
    A:

        Control(FCTL_GETPANELINFO);
        Control(FCTL_GETPANELINFO);
        PanelInfo.PanelItems[PanelInfo.CurrentItem].FindData.dwFileAttributes

14. Q: Нельзя ли пpи перерисовке любой панели перерисовывать заголовок окна? По моему, это будет проще и быстрее, чем плагину вызывать FCTL_REDRAWPANEL.
    A: Насчёт быстрее - не факт. Если заголовок не изменён плагином, как оно обычно и бывает, FAR перерисует его вхолостую.
15. Q: Откуда FAR берёт процедуры для копирования/удаления/... файлов?
    A: Из Windows API.
    Копирование: CreateFile+ReadFile+WriteFile+CloseHandle или CopyFile(Ex), в зависимости от ОС и опции "Use system copy routine".
    Удаление: DeleteFile или SHFileOperation в зависимости от опции "Delete to Recycle Bin"
16. Q: Как можно получить имя каталога, из которого запущен FAR?
    A:

        char lpName[_MAX_PATH], lpFullPath[_MAX_PATH];
        LPTSTR lpFile;
        GetModuleFileName(NULL,lpName,sizeof(lpName));
        GetFullPathName(lpName,sizeof(lpFullPath),lpFullPath,&lpFile);
        *lpFile='\0';

17. Q: А как уменьшить размер DLL - уж больно "жирный" модуль получается?
    A:
    1. В разделе "Статьи" есть несколько заметок на эту тему.
    2. Плагинописателям, пишущим на Visual C++ и борющимся за уменьшение размера плагинов, настоятельно рекомендуется прочитать: https://msdn.microsoft.com/msdnmag/issues/01/01/hood/default.aspx
18. Q: А что за дупы в клипборде идут, зачем 2 раза одну и ту же в Clipboard помещать?
    A: Если поместить текст в clipboard в каком-то одном формате, а программа просит его в другом, то Windows занимается перекодировкой самостоятельно, и очень часто делает это коряво. Псевдографика портится, иногда вместо русских букв остаются одни '?', конкретные глюки зависят от версии Windows и комбинации исходного и запрашиваемого форматов. Если же поместить все форматы сразу, то Windows находит нужный и использует его без всяческих перекодировок.
19. Q: Как определить что есть "связь каталогов" - простая ссылка на каталог или монтированный диск.
    A: Основное правило - связи каталогов - ТОЛЬКО ДЛЯ КАТАЛОГОВ! Значит нам нужно узнать - "А не каталог ли ЭТО?". На это указывает файловый атрибут `FA_DIREC` (или `FILE_ATTRIBUTE_DIRECTORY`).
    Едем дальше.
    Нам достоверно известно, что любые связи каталогов имеют атрибут `FILE_ATTRIBUTE_REPARSE_POINT` - проверяем его.
    Вызываем

        FSF.GetReparsePointInfo(FullFolderName,DestName,sizeof(DestName))

    Эта функция возвращает нам для некоторого заданного пути "FullFolderName" некий реальный путь "DestName", который имеет суть:
    1. `"\??\D:\Junc..."`
       симлинк на каталог.
    2. `"\\?\Volume{..."`
       монтированный диск.
    ПРИЧЁМ! Нас совершенно не интересуют первые 4 символа! Следовательно достаточно сделать проверку 7 символов, начиная с четвёртого:

        if(!strncmp(JuncName+4,"Volume{",7))
        {
          // это явно монтированный диск!
        }
        else
        {
          // простая ссылка на каталог
        }

20. Q: Да. Но ведь интересно знать, что скрывается по именем '\\?\Volume{...'?
    A: Проще простого. Для этого вызовем функцию

        FSF.GetPathRoot(JuncName,Root);

    которая возвращает реальный корень в одной из двух реинкарнаций:
    1. `"D:\"`
    2. `"\\?\Volume{..."`
    второй случай - "тяжёлый" :-) - здесь видимо на уровне системы (в менеджере дисков) для данного диска удалили буковку... Ну, что бы не маячила, т.с. ;-)
21. Q: А ещё что нить полезного про символические ссылки можно узнать?
    A: А как же :-) Правда не совсем про символические ссылки, но...
    Например, мы можем узнать ещё порцию полезностей для монтированных дисков. Здесь всё просто - вызываем стандартную GetVolumeInformation()
    Т.е. нам уже известен корень - `"GetPathRoot(JuncName,Root);"`, осталось получить информацию (например, возможности файловой системы по поддержке расширенных атрибутов - сжатие и шифрование и тип файловой системы):

        DWORD FileSystemFlags;
        char FSysName[MAX_PATH];

        if (GetVolumeInformation(Root,NULL,0,NULL,NULL,
                                &FileSystemFlags,FSysName,sizeof(FSysName)))
        {
          if (FileSystemFlags & FILE_FILE_COMPRESSION)
          {
            // файловая система поддерживает сжатие файлов
          }

          if (FileSystemFlags & FILE_SUPPORTS_ENCRYPTION)
          {
            // файловая система поддерживает шифрование файлов
          }
        }

    Типичная функция проверки возможности создания... жёстких ссылок могла бы выглядеть так:

        BOOL CanCreateHardLinks(char *TargetFile,char *HardLinkName)
        {
          char RootTarget[MAX_PATH],RootHardLink[MAX_PATH],FSysName[MAX_PATH];
          GetPathRoot(TargetFile,RootTarget);
          GetPathRoot(HardLinkName,RootHardLink);

          if(!strcmp(RootTarget,RootHardLink)) // тот же корень (то же диск)?
          {
            DWORD FileSystemFlags;

            if(GetVolumeInformation(RootTarget,NULL,0,NULL,NULL,&FileSystemFlags,
                                    NULL,0))
            {
              if(FileSystemFlags&FILE_SUPPORTS_HARD_LINKS)
                return TRUE;
            }
          }
          return FALSE;
        }

22. Q: Можно ли получить для редактируемого файла флаг изменён/неизменен? Я сходу не нашёл, а ведь FAR это знает, звёздочку в верхней строке показывает.
    A: См. описание структуры EditorInfo, конкретно значения поле EditorInfo.CurState.
23. Q: Как узнать имя файла, загруженного во вьюер?
    A: Вот такой вот код запросто узнаёт текущее имя в программе просмотра:

        WindowInfo wi;
        wi.Pos=-1;
        Info.AdvControl(Info.ModuleNumber,ACTL_GETWINDOWINFO,&wi);

    всё. в wi.Name лежит имя файла.
24. Q: Возможно ли каким-то способом в FSF.ProcessName, при сравнении маски "OUTBOUND\\????????.MO?" и файла "С:\\FILES\\OUTBOUND\\0000ee2c.mod" получить TRUE, а с файлом "C:\\MUSIC\\assol_1.mod" обломаться? PN_SKIPPATH не подходит. При наличии "\\" в маске он, похоже, никогда не вернёт TRUE.
    A: Сравнивай с маской "*\\OUTBOUND\\????????.MO?" без использования PN_SKIPPATH.
25. Q: Как Windows "сбрасывает" имя файла из Проводника в консольное окно?
    A: Vasily Titsky: "...Если кратко, то когда в консольное приложение идёт вставка текста (через системное меню, или кидая ярлык) то добрая ОС делает вот что: если в текущем Keyboard Layout приложения и очередной код символа не пересекаются (текущий язык - english, а вставлять надо символ русского алфавита; или наоборот), то она "эмулирует" ввод этого символа через Alt+цифры. Например, при вставке русской 'А' , будет сгенерировано: нажатие Alt, нажатие '1', отпускание '1', нажатие '2', отпускание '2', нажатие '8', отпускание '8', отпускание Alt. При вставке символов с кодом, меньшим 99 (? - не проверял) имитируется нажатие 2-х цифр. Остаётся только правильно это обнаружить и обработать..."
26. Q: Почему-то не работает ProcessKey? Пишу в Delphi:

        function ProcessKey(hPlugin: THandle; Key: Integer; ControlState: Word): integer; stdcall;
        begin
          windows.Beep(300,200);
          //возвращаем False для дальнейшей обработки FARом
          result:=0;
        end;

    По идее должен выдаваться сигнал при любом нажатии клавиши - этого не происходит. То же самое, если делаю это для какой-то конкретной клавиши.
    A: FAR вызывает функцию ProcessPanelInputW для активной плагиновой панели.
27. Q: Можно ли из плагина перейти на определённый файл в директории?
    A:

        {
          struct PanelInfo PInfo;
          Info.PanelControl(INVALID_HANDLE_VALUE,FCTL_GETPANELINFO,&PInfo);

          // установить позицию на элементе панели selectItem

          struct PanelRedrawInfo PRI;
          char Name[MAX_PATH], Dir[MAX_PATH*5];
          int pathlen;

          strcpy(Name,Info.FSF->PointToName(selectItem));
          pathlen=Info.FSF->PointToName(selectItem)-selectItem;

          if(pathlen)
            memcpy(Dir,selectItem,pathlen);

          Dir[pathlen]=0;
          Info.FSF->Trim(Name);
          Info.FSF->Trim(Dir);
          Info.FSF->Unquote(Name);
          Info.FSF->Unquote(Dir);

          if(*Dir)
            Info.PanelControl(INVALID_HANDLE_VALUE,FCTL_SETPANELDIR,&Dir);
          Info.PanelControl(INVALID_HANDLE_VALUE,FCTL_GETPANELINFO,&PInfo);

          PRI.CurrentItem=PInfo.CurrentItem;
          PRI.TopPanelItem=PInfo.TopPanelItem;

          for(int J=0; J < PInfo.ItemsNumber; J++)
          {
            if(!Info.FSF->LStricmp(Name,Info.FSF->PointToName(PInfo.PanelItems[J].FindData.cFileName)))
            {
              PRI.CurrentItem=J;
              PRI.TopPanelItem=J;
              break;
            }
          }
          Info.PanelControl(INVALID_HANDLE_VALUE,FCTL_REDRAWPANEL,&PRI);
        }

28. Q: Как из плагина запустить что-нибудь? И как показать вывод этой программы под панелями?
    A:

        Info.PanelControl(INVALID_HANDLE_VALUE,FCTL_GETUSERSCREEN,NULL);

        if (CreateProcess(NULL,"ls.exe",NULL,NULL,TRUE,0,NULL,NULL,&si,&pi))
        {
          WaitForSingleObject( pi.hProcess, INFINITE );
          CloseHandle( pi.hProcess );
          CloseHandle( pi.hThread );
        }

        Info.PanelControl(INVALID_HANDLE_VALUE,FCTL_SETUSERSCREEN,NULL);

наверх

### Диалоги

1. Q: Если я не использую функцию DialogRun работы с диалогом, то каким образом обрабатывать ввод данных? Самому писать?
   A: Можно и самому :-) А можно использовать DialogAPI...
2. Q: Можно ли динамически в Dialog менять элементы типа Static (их заголовки)?
   A: Если воспользоваться функцией-обработчиком диалога DialogInit - то можно - в функции-обработчике диалога послать ядру DialogAPI сообщение DM_SETTEXT.
3. Q: Ну ладно, воспользовался этим самым DialogAPI, но мне не хватает того набора управляющих элементов, которые обрабатывает FAR. Ась?
   A: Воспользуйся элементом DI_USERCONTROL - отрисовка элемента и управление им - всё во власти плагина!
4. Q: А что это ListBox и ComboBox полуфабрикатные какие-то? Нормально работать с ними ну никак нельзя - для добавления/удаления приходится изголяться.
   A: А что ты хотел от первой версии DialogAPI?
5. Q: Почему в диалоге не срабатывают кнопки на закрытие окна
   A: Ты сам должен рулить моментом закрытия диалога, исключения: Ctrl-Break - закрывается всегда, Ctrl-Enter - если есть хотя бы одна кнопка с полем DefaultButton=1, то запрос на закрытие с Param1 = номеру текущего элемента, Esc и F10 - здесь можно рулить из обработчика - закрывать или нет.
6. Q: И что - мне теперь самому закрывать диалог?
   A: Если используешь обработчик диалога - то ДА: поймал событие DN_BTNCLICK, проверил Param1 на нужный номер и заслал ядру диалога сообщение DM_CLOSE.
7. Q: А если я мышкой - оно придёт или ещё ловить DN_MOUSECLICK?
   A: На твоё усмотрение :-) Сначала мышиное сообщение придёт (проигнорируй его), а за ним и DN_BTNCLICK не задержится...
8. Q: И DefaultButton самому обрабатывать?
   A: Нет. Для DefaultButton получится следующая цепочка событий: [DN_CONTROLINPUT -> ] DN_BTNCLICK -> DM_CLOSE.
9. Q: Каким-то образом у меня получилось, что SEPARATOR, что бы я не делал не желал доходить до правого конца рамки, когда сделал с нуля - исправилось.
   A: См. замечания для флага DIF_SEPARATOR

наверх