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

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

  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 (archive.org)


  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

наверх