[ Статья ] Находим местоположение WINCMD.INI
Select messages from
# through # FAQ
[/[Print]\]
Goto page 1, 2, 3  Next  :| |:
Total Commander -> Написание плагинов для Total Commander

#1: [ Статья ] Находим местоположение WINCMD.INI Author: mozersLocation: с Нижнего PostPosted: Sat Feb 05, 2005 08:43
    —
Уважаемые авторы замечательных плагинов и полезных утилит!
Большинству из ваших программ требуется указание точного местоположения файла TOTALCMD.EXE и INI файлов настройки Коммандера.

Каждый автор ищет решение этой проблемы по своему:
Одни (самые умные Wink вообще ничего не ищут, а требуют от пользователя вручную заводить эти пути. Решение, конечно, крайне примитивное. И пользователь может ошибиться с указанием путей, да и тулза оказывается жестко привязанной к одному месту. Если пользователь имеет несколько настроенных профилей ТС, то он вынужден держать несколько копий вашей программы - для каждого профиля отдельно Sad
Другие (слава Богу - таких уже почти не осталось) тупо считают, что эти файлы могут называться только wincmd.ini и wcx_ftp.ini и лежат они исключительно в %WINDIR%. Просто нет слов...
Третьи (кто посообразительней), пытаются анализировать содержимое ветки [HKEY_CURRENT_USER\Software\Ghisler\Total Commander] и увы, тоже не всегда бывают правы.
Этой мини-статейкой я попытаюсь проанализировать опыт тех и других и сделать некоторые выводы. Надеюсь они пригодятся вам в дальнейших разработках.

Итак, начнем с самого простого - TOTALCMD.EXE
Он ВСЕГДА находится по %COMMANDER_PATH%/TOTALCMD.EXE
Учтите только что %COMMANDER_PATH% имеется в переменных окружения только тогда, когда ваша прога стартует из ТС. Для авторов плагинов это всегда - факт, утилита же может стартовать и не из ТС.
Уважаемые авторы, заставьте своего пользователя хотя бы в первый раз запустить вашу тулзу из ТС - она запишет найденный путь, и в дальнейшем ее можно будет стартовать откуда угодно. Поверьте, 99% пользователей будут запускать вашу утилиту именно из ТС, а автоматически найденный ею путь будет лишним доказательством вашего профессионализма!

Теперь про поиск INI файлов
Единственно правильный вариант на сегодня:
  1. Анализируем командную строку ТС, ищем в ней ключи /I и /F и считываем пути к INI оттуда.
  2. Если в ком.строке ключи не найдены, то проверяем, есть ли в файле %COMMANDER_PATH%/wincmd.ini ключ UseIniInProgramDir (Еще одна беда, свалившаяся на нашу голову вместе с версией 6.5). Если есть - то ищем INI файлы в соответствии со значением этого ключа (подробности - в HELP).
  3. Если там - пусто, то смотрим в [HKEY_CURRENT_USER\Software\Ghisler\Total Commander].
  4. Если и там - пусто, то INI лежат в %WINDIR% и названия их - wincmd.ini и wcx_ftp.ini
Важные замечания
  1. Ключи ком.строки /I и /F могут быть в любом регистре, причем второй по очереди ключ может быть и без слеша.
  2. Пути в ключах ком.строки и реестра могут задаваться как полным путем, так и относительным. В путях можно использовать любые переменные окружения, включая %COMMANDER_PATH%. Пути могут быть заданы и как LongNames и в формате 8.3
  3. Если пути к файлам заданы явно (ключах ком.строки или реестре), то файлы инициализации могут иметь ЛЮБОЕ имя.
Ну а теперь - поподробнее про анализ ком.строки
Для плагина TC все решается сравнительно просто. А все потому, что он выполняется внутри процесса TC и поэтому получить командную строку можно просто вызвав API-шную функцию GetCommandLine. Кто понимает, о чем я говорю, тому не составит труда извлечь из нее нужные параметры.
Для внешней утилиты все значительно сложнее Sad Решается эта проблема с использованием ловушек (hooks), труб (pipes) и прочих мудреных вещей. Пройти все круги ада и построить рабочую программу смог только Alexander Asyabrik aka Shura (я о ReloadTC). Других, увы, не знаю Sad

Так вот обломали нас в самом начале пути Sad
Однако, есть возможность получить вожделенные пути и несколько иным способом, если мы вспомним о том, что сам ТС прекрастно осведомлен о местоположении своих INI файлов - достаточно взглянуть на окошко "About".
Итак, вкратце: Находим окно активного экземпляра ТС, шлем ему через SendMessage команду cm_About, считываем через GetWindowText драгоценную инфу, закрываем окошко About. Если на время выполнения этой процедуры запретить обновление окна, то получится почти незаметно Wink
Намеренно не говорю о подводных камнях (а то обсуждать будет нечего Wink, а вот положительные стороны данного подхода - налицо. Одним махом находим местоположения обоих INI файлов совершенно независимо от того, как и где задал их пользователь.

P.S. Всего этого разговора могло бы и не быть, если бы глубокоуважаемый Christian Ghisler ввел в обиход пару псевдо-переменных
$MAIN_INI и $FTP_INI (подобно имеющимся уже $MYPICTURES, $LOCAL_APPDATA, $COMMON_APPDATA, и т.п.). Ему то это сделать - плевое дело!
Представьте, как было бы здорово!
Как вам, к примеру, такая команда в меню "Запуск" ?:
Code:

  Команда: disk:/path/MySuperEditor.exe
Параметры: $MAIN_INI $FTP_INI

Кому так же больно как мне - отпишите Орлу нашему...

#2:  Author: TSergey PostPosted: Sat Feb 05, 2005 19:01
    —
Может сюда же выложить функции для плагинов?
Сам сразу и начну:

Code:
function GetFtpIniFileName: string;
var
  s, s1, s2 :string;
  a: array [0..MAX_PATH] of char;
  i, j: integer;
  EscEn, QuoteStr, UseProgDir: boolean;
  c: char;
  reg: TRegistry;
begin
  // Разбор командной строки
  s := StrPas(GetCommandLine);

  i := pos('f=', s);
  if i  = 0 then  i := pos('F=', s);

  s1 := '';

  if i > 0
  then begin
    i := i + 2;
    EscEn := false;
    QuoteStr := false;
    j := length(s);
    // Разворачиваем параметр с поддержкой кавычек
    while (i <= j) do
    try
      c := s[i];
      case c of
        #9, ' ':
          if QuoteStr
          then if EscEn
            then begin
              s1 := s1 + '\'+ c;
              EscEn := false;
            end
            else s1 := s1 + c
          else break;

        '"':
          if EscEn
          then s1 := s1 + '"'
          else QuoteStr := not QuoteStr;

        '\':
          if EscEn
          then begin
            s1 := s1 + '\';
            EscEn := false;
          end
          else EscEn := true;

        else if EscEn
          then begin
            s1 := s1 + '\'+ c;
            EscEn := false;
          end
          else s1 := s1 + c
      end;
    finally
      inc(i);
    end;

    if EscEn then s1 := s1 + '\';

  end;


  UseProgDir := false;

  if s1 = ''
  then begin
    // Обрабатываем каталог Тотал Командера
    s := '%COMMANDER_PATH%\wincmd.ini';
    s2 := '%COMMANDER_PATH%\wcx_ftp.ini';

    ExpandEnvironmentStrings(PChar(s), a, MAX_PATH);
    i := GetPrivateProfileInt('Configuration', 'UseIniInProgramDir', 0, a);
    UseProgDir := (i and 2) <> 0;
    if (i and 4) <> 0 then s1 := s2;
  end;

  if s1 = ''
  then begin
    // Проверяем реестр

    try
      Reg := TRegistry.Create(KEY_READ);
      try
        Reg.RootKey := HKEY_CURRENT_USER;
        if Reg.OpenKeyReadOnly('\Software\Ghisler\Total Commander')
        then s1 := Reg.ReadString('FtpIniName')
        else s1 := '';
      finally
        Reg.Free;
      end;
    except
      s1 := '';
    end;

    if (s1 = '')
    then if UseProgDir
    then s1 := s2
    else s1 := 'wcx_ftp.ini';
  end;

  if (copy(s1, 1, 2) = '.\')
  then begin
    delete(s1, 1, 2);
    s1 := '%COMMANDER_PATH%\' + s1;
  end;

  ExpandEnvironmentStrings(PChar(s1), a, MAX_PATH);
  result := strpas(a);
end;



ЗЫ. Вроде проверил работу на своем плагине, но ошибки могут быть.
ЗЗЫ. Анекдот про "длинный_мнемоничный_идентификатор_номер_один" - знаю. Twisted Evil


Last edited by TSergey on Tue Feb 15, 2005 08:31; edited 2 times in total

#3:  Author: SCHMasterLocation: Киев PostPosted: Sun Feb 06, 2005 04:59
    —
%USERPROFILE%
Еще такая хрень бывает... Под ХР и 2К.

#4:  Author: mozersLocation: с Нижнего PostPosted: Sun Feb 06, 2005 14:03
    —
SCHMaster
Угу. С %USERPROFILE%, как, впрочем, и со всеми остальными переменными окружения (%TEMP%, %ProgramFiles%, ...) все однозначно.
Если они присутствуют в ключах ком.строки или реестра (в ком.строке, в отличии от реестра, длинные пути надо обязательно задавать в кавычках), то Тотал их понимает, а значит и ваша прога должна их знать...
В общем все учесть - с ума сойти можно... И я все больше склоняюсь к тому, чтоб грабить инфу с About.

#5:  Author: NikLocation: Киров PostPosted: Sun Feb 06, 2005 15:13
    —
В интерфейсе Lister-плагинов есть такая структура:

Code:
 
 type

  tListDefaultParamStruct=record

    size,

    PluginInterfaceVersionLow,

    PluginInterfaceVersionHi:longint;

    DefaultIniName:array[0..MAX_PATH-1] of char;

  end;


Так вот, есть извлечь путь к DefaultIniName, то получите каталог, где лежат Ini файлы TC. И никакой лишней мороки... Если конечно имена файлов стандартные.

#6:  Author: CaptainFlintLocation: Москва PostPosted: Sun Feb 06, 2005 15:52
    —
SAM
Грабить из About'а может не получиться, если именно в это время, например, в Тотале открыт модальный диалог. В лучшем случае ничего не произойдёт, в худшем - крэш.
Nik
Это путь к INI-файлу настроек самого плагина, а не всего Тотала.

#7: Пример кода Author: Alextp PostPosted: Sun Feb 06, 2005 18:37
    —
Предлагаю Delphi-функции нахождения путей к wincmd.ini и wcx_ftp.ini.
Из исходника TC Plugins Manager, в нем все работает. Если путь, возвращаемый функцией, не найден, то программа запрашивает путь у пользователя.

Модули RegProc.pas/SProc.pas, которые здесь используются, высылаются емейлом.

Code:
function tcDefDir: string;
begin
  //поправка SAM-а, чтобы путь брался из запущенного TC
  Result:= SProc.SExpandVars('%COMMANDER_PATH%');
  //брать из реестра
  if Pos(':\', Result)=0 then
  Result:=
    GetRegKeyStr(HKEY_CURRENT_USER, 'Software\Ghisler\Total Commander', 'InstallDir',
    GetRegKeyStr(HKEY_LOCAL_MACHINE, 'Software\Ghisler\Total Commander', 'InstallDir',
      'C:\TotalCmd'));
end;

function tcDefExe: string;
begin
  Result:= tcDefDir+'\Totalcmd.exe';
end;

function tcDefIni: string;
begin
  Result:=
    GetRegKeyStr(HKEY_CURRENT_USER, 'SOFTWARE\Ghisler\Total Commander', 'IniFileName',
    GetRegKeyStr(HKEY_LOCAL_MACHINE, 'SOFTWARE\Ghisler\Total Commander', 'IniFileName',
    'wincmd.ini'));
  if Pos('\', Result)=0 then Insert('%windir%\', Result, 1);
  //если путь записан как ".\Wincmd.ini":
  if Pos('.\', Result)=1 then
    SReplace(Result, '.', tcDefDir);
  Result:= SExpandVars(Result);
end;

function tcDefIniFtp: string;
begin
  Result:=
    GetRegKeyStr(HKEY_CURRENT_USER, 'SOFTWARE\Ghisler\Total Commander', 'FtpIniName',
    GetRegKeyStr(HKEY_LOCAL_MACHINE, 'SOFTWARE\Ghisler\Total Commander', 'FtpIniName',
    'wcx_ftp.ini'));
  if Pos('\', Result)=0 then Insert('%windir%\', Result, 1);
  //если путь записан как ".\Wincmd.ini":
  if Pos('.\', Result)=1 then
    SReplace(Result, '.', tcDefDir);
  Result:= SExpandVars(Result);
end;


Last edited by Alextp on Wed Oct 19, 2005 23:40; edited 2 times in total

#8:  Author: TSergey PostPosted: Sun Feb 06, 2005 18:43
    —
Alextp, а где обработка ситуации, когда ini-файлы передаются в командной строке?

SCHMaster, SAM: Все переменные легко разворачиваются в функции ExpandEnvironmentStrings.

#9:  Author: Alextp PostPosted: Sun Feb 06, 2005 18:52
    —
TSergey wrote:
Alextp, а где обработка ситуации, когда ini-файлы передаются в командной строке?

Нету. Smile

Подразумевается, что как и в комстроке TC, пользователь сам укажет в PlugMan-е этот ini-файл (через кнопку "Обзор").

#10:  Author: TSergey PostPosted: Sun Feb 06, 2005 18:54
    —
Alextp wrote:
TSergey wrote:
Alextp, а где обработка ситуации, когда ini-файлы передаются в командной строке?

Нету. Smile

Подразумевается, что как и в комстроке TC, пользователь сам укажет в PlugMan-е этот ini-файл (через кнопку "Обзор").
А смысл заставлять пользователя что-то лишнее вводить, если есть вполне нормальный автоматический способ?

#11:  Author: Alextp PostPosted: Sun Feb 06, 2005 18:57
    —
Ну, ты прав, надо бы еще в ф-ции tcDefIni обрабатывать комстроку TC...

#12:  Author: NikLocation: Киров PostPosted: Sun Feb 06, 2005 20:45
    —
CaptainFlint wrote:

Nik
Это путь к INI-файлу настроек самого плагина, а не всего Тотала.


Да, но по-умолчанию Total предлагает размещать файлы настройки плагина в той же папке, где и свои собственные... Проверено на AmpView: ранние версии читали цвет фона Lister из файлов конфигурации, а путь к ним я определял именно таким способом...

#13:  Author: Gosha PostPosted: Sun Feb 06, 2005 21:57
    —
Разрешите высказать свое мнение.
Если кому либо было не лень писать командную строку для запуска TC и в ней указать полный путь до INI файлов, то может его не очень смутит тот факт, что левые программы не всегда находят их. И может быть им даже будет также не лень указать где эти файла всетаки находятся.

#14:  Author: TSergey PostPosted: Sun Feb 06, 2005 22:12
    —
Gosha wrote:
Разрешите высказать свое мнение.
Если кому либо было не лень писать командную строку для запуска TC и в ней указать полный путь до INI файлов, то может его не очень смутит тот факт, что левые программы не всегда находят их. И может быть им даже будет также не лень указать где эти файла всетаки находятся.
Хороший подход: пользователи будут довольны и благодарны. Twisted Evil

#15:  Author: mozersLocation: с Нижнего PostPosted: Mon Feb 07, 2005 00:25
    —
CaptainFlint
Quote:
Грабить из About'а может не получиться, если именно в это время, например, в Тотале открыт модальный диалог

Да вроде такой проблемы не заметил :-/ Ничего не вешается, независимо от открытых окон. Попробуй сам...

Nik
Лишь путь Sad А имя?

Gosha
Quote:
Если кому либо было не лень писать командную строку для запуска TC

Если кому то было не лень написать в своем плагине/утилите процедуру обнаружения пути к INI файлам, то он получил еще плюсом добрую половину благодарных пользователей, покоренных мастерством автора.
Кстати, по моим данным, у более чем 50 тысяч пользователей (большинство из которых - новички), путь к INI задается с ярлыка и выглядит приблизительно так:
С:\Program Files\Total Commander XP\Profiles\Prof\main.ini

А, впрочем, не хватает ума определить правильно - не определяйте вообще! Лучше позволить пользователю задать путь вручную, чем мучится с неработоспособной прогой.



Total Commander -> Написание плагинов для Total Commander


output generated using printer-friendly topic mod. All times are GMT + 4 Hours

Goto page 1, 2, 3  Next  :| |:
Page 1 of 3

Powered by phpBB © 2001, 2005 phpBB Group