ошибка при написании wdx плагина

Только для тех, кто пишет плагины под Total Commander. Обмен опытом, решение проблем.

Модераторы: d-view, Моторокер, Avada, CaptainFlint, Nik

tonyy
Сообщения: 5
Зарегистрирован: Пн май 13, 2013 14:09

ошибка при написании wdx плагина

Сообщение tonyy »

Уважаемые участники форума!
нужна помощь. не могу найти ошибку, из-за которой не работает плагин.
вот код:

Код: Выделить всё

{$E wdx}
library Segd1Inf1;

uses
  Windows,
  SysUtils,

  ContPlug,
  Usegdread,

  Classes;

  const
  _DetectString: PAnsiChar = 'EXT="segd"';

  _FieldsNum = 3;
  _Fields: array[0.._FieldsNum-1] of PAnsiChar = (
    'prof ',
    'piket ',
    'data ');

procedure ContentGetDetectString(DetectString: PAnsiChar; maxlen: integer); stdcall;
begin
  lstrcpynA(DetectString, _DetectString,maxlen);
end;

function ContentGetSupportedField(FieldIndex: integer; FieldName: PAnsiChar;
  Units: PAnsiChar; maxlen: integer): integer; stdcall;
begin
  if FieldIndex in [0..High(_Fields)] then
  begin
    lstrcpynA(FieldName,_Fields[FieldIndex],maxlen);
    strcopy(Units,'');

    Result := FT_STRING;
  end else Result := ft_NoMoreFields;
end;                                                                                

function ContentGetValueW(fn: PWideChar; FieldIndex, UnitIndex: integer;
  FieldValue: PWideChar; MaxLen, Flags: integer): integer; stdcall;
var

  s: AnsiString;
  ext: string;
  
begin

  if (FieldIndex<0) or (FieldIndex >= _FieldsNum) then
    begin Result:= FT_NOSUCHFIELD; Exit end;

  ext:= LowerCase(ExtractFileExt(fn));

  if (Flags and CONTENT_DELAYIFSLOW)>0 then
    if (ext<>'') then begin Result:= FT_DELAYED; Exit end;

        s:= segdread(fn, FieldIndex);

        if s=''
          then
            Result:= FT_FIELDEMPTY
          else
          begin
             lstrcpynW(FieldValue, PWideChar(Widestring(s)), MaxLen);
             Result:= FT_STRINGW;
          end;

end;


function ContentGetValue(fn: PAnsiChar; FieldIndex, UnitIndex: integer;                                   
  FieldValue: PWideChar; MaxLen, Flags: integer): integer; stdcall;

begin
  //Result := ContentGetValueW(PWideChar(WideString(fn)), FieldIndex, UnitIndex, FieldValue, MaxLen, Flags);
   Result:=ft_fieldempty;


end;                                                                                                      

exports
  ContentGetDetectString,
  ContentGetSupportedField,
  ContentGetValueW,
  ContentGetValue;
                                                                                                          
end.

{$R *.res}

begin
end.
надеюсь на конструктивную помощь. это мой первый плагин. буду благодарен любой помощи.
Аватара пользователя
CaptainFlint
Сообщения: 6239
Зарегистрирован: Вт дек 14, 2004 22:34
Контактная информация:

Сообщение CaptainFlint »

Для начала хорошо бы описать, как именно проявляется "неработа" плагина. Что значит "не работает"? Не устанавливается? Устанавливается, но не выводится в списке WDX-плагинов? Выводится, но не показывает список полей? Показывает, но неправильно?..
Почему же, ё-моё, ты нигде не пишешь "ё"?
tonyy
Сообщения: 5
Зарегистрирован: Пн май 13, 2013 14:09

Сообщение tonyy »

он устанавливается. выводится в списке wdx плагинов появляются название поле, а вот сами значения не появляются
Аватара пользователя
MVV
Сообщения: 4815
Зарегистрирован: Чт окт 15, 2009 10:58
Откуда: Ростов-Дон

Сообщение MVV »

Код: Выделить всё

Result:=ft_fieldempty;
Типа, дядя Коммандир, всё ок, поле пустое... Для теста уже возвращай строчку текстовую какую-нибудь, или число. Впрочем, если есть юникодовская версия функции, тотал (современный) не будет вызывать ансишную.

А юникодовская функция вообще ничего не делает, если нет флага CONTENT_DELAYIFSLOW. А должна. В документации написано как раз, что второй раз функция вызывается без этого флага, как раз в тот момент, когда надо возвращать значение.

Советую сначала отладить плагин без фоновой обработки, обрабатывая поля напрямую. Когда заработает - тогда уже переключаться на обработку в фоне.

Добавлено спустя 6 минут:

А, это некорректные отступы сбивают с толку...

Непонятно, кстати, зачем вызывать segdread для пустого расширения. Ведь эта пара строк завершает функцию только при первом проходе И непустом расширении. При первом проходе и пустом расширении функция продолжит работу.

Код: Выделить всё

	if (Flags and CONTENT_DELAYIFSLOW)>0 then 
		if (ext<>'') then begin Result:= FT_DELAYED; Exit end; 
Очень рекомендую подключать отладчик к тоталу и отлаживать плагин на ходу, сразу будет понятно, где что не так работает. На худой конец, отображать мессейджбоксы из функции. По коду похоже, что s пустая... Опять же, для тестов можно повозвращать строки/числа.

Кстати, если s - ансишная строка, необязательно её в юникод конвертить перед возвратом тоталу, он прекрасно поймет, если вернуть ему ансишную с результатом FT_STRING.
Аватара пользователя
CaptainFlint
Сообщения: 6239
Зарегистрирован: Вт дек 14, 2004 22:34
Контактная информация:

Сообщение CaptainFlint »

Вопрос на засыпку: а зачем имена полей плагина завершаются пробелами? Я сразу и не заметил… Я вовсе не уверен, что Тотал вообще умеет такое нормально кушать. Так что первым делом нужно эти пробелы убить, а тестовые колонки в Тотале пересоздать. И если по-прежнему содержимое будет пустым, то надо либо пошаговой отладкой проходить, как предложил MVV, либо комментировать всё содержимое функции ContentGetValueW, заменять её простейшей реализацией (например, безусловным заполнением константной строкой, без всяких segdread и отложенных выполнений), ну и последовательно приводить к желаемому виду, постоянно проверяя, работает ли код после очередного шага.
Почему же, ё-моё, ты нигде не пишешь "ё"?
tonyy
Сообщения: 5
Зарегистрирован: Пн май 13, 2013 14:09

Сообщение tonyy »

пробелы убрал. проблему это не решило, к сожалению.
как провести пошаговую отладку подключаясь к тоталу?
ProgMan13
Сообщения: 341
Зарегистрирован: Ср авг 19, 2009 16:07

Сообщение ProgMan13 »

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

Код: Выделить всё

  if (Flags and CONTENT_DELAYIFSLOW)>0 then
    if (ext<>'') then begin Result:= FT_DELAYED; Exit end;
Перед Exit должен быть вызов типа MyThreadedFunction, а её нет. Эти две строчки - фэйк - их закомментировать.

Несколько лет назад на форуме поднималась тема про расширения файлов в DetectString. Расширение должно быть написано прописными буквами :) Так что надо поменять
_DetectString: PAnsiChar = 'EXT="segd"';
на
_DetectString: PAnsiChar = 'EXT="SEGD"';
Перекомпилировать и переустановить(!) плагин.
Я не знаю, правильно ли работает функция segdread, но чтобы проверить работу плагина просто заменить
s:= segdread(fn, FieldIndex);
на
s:= 'TEST';
Аватара пользователя
MVV
Сообщения: 4815
Зарегистрирован: Чт окт 15, 2009 10:58
Откуда: Ростов-Дон

Сообщение MVV »

tonyy,
Укажи в качестве отлаживаемого в среде разработки процесса тотал, запускаемый с конфигом, который грузит дебаг-версию плагина, по идее можно будет отлаживать. В Visual Studio так прекрасно работает.
ProgMan13 писал(а):Перед Exit должен быть вызов типа MyThreadedFunction, а её нет. Эти две строчки - фэйк - их закомментировать.
Это еще зачем? По-моему, те строчки выполняют вполне определенную задачу - сообщают тоталу на первом (основном) проходе, что обработка затянется, и что нужно вызвать функцию во втором (фоновом) проходе.
ProgMan13
Сообщения: 341
Зарегистрирован: Ср авг 19, 2009 16:07

Сообщение ProgMan13 »

MVV писал(а):Это еще зачем? По-моему, те строчки выполняют вполне определенную задачу - сообщают тоталу на первом (основном) проходе, что обработка затянется, и что нужно вызвать функцию во втором (фоновом) проходе.
Хм, да это моё видение. Функция не возвращает никаких данных быстро, слишком специализированная (файлы "segd"). Вызов этой функции в фоне не должен залочить TC, как я понимаю, - тогда согласен.
Тогда добавить Enter/LeaveCriticalSection?
Аватара пользователя
MVV
Сообщения: 4815
Зарегистрирован: Чт окт 15, 2009 10:58
Откуда: Ростов-Дон

Сообщение MVV »

Критическая секция нужна только при необходимости обращения к общим глобальным ресурсам, которых тут не наблюдается.
В основном потоке функция работает со своими параметрами и завершается.
В фоновом выполняет какие-то операции, это один поток, никто не будет вызывать её одновременно больше 1 раза, поэтому проблем не будет, даже если segdread использует глобальные объекты.
ProgMan13
Сообщения: 341
Зарегистрирован: Ср авг 19, 2009 16:07

Сообщение ProgMan13 »

MVV писал(а):В фоновом выполняет какие-то операции, это один поток, никто не будет вызывать её одновременно больше 1 раза, поэтому проблем не будет, даже если segdread использует глобальные объекты.
Ладно, стоит думать, что Кристиан всё сделал как надо. Но если на другой панели эта функция будет вызвана для другого файла или файла с пустым расширением?
Аватара пользователя
MVV
Сообщения: 4815
Зарегистрирован: Чт окт 15, 2009 10:58
Откуда: Ростов-Дон

Сообщение MVV »

На другой панели это будет либо основной поток, либо тот же фоновый (у тотала только один фоновый поток для загрузки контентных полей).

В любом случае, критическая секция должна быть настолько мала, насколько это возможно. Поэтому если ей где-то и место, то в недрах функции segdread (и то только в моменты обращения к глобальным объектам, и то не факт, что нужна), но уж точно не на всю ContentGetValueW.
tonyy
Сообщения: 5
Зарегистрирован: Пн май 13, 2013 14:09

Сообщение tonyy »

спасибо большое за помощь. вот что у меня получилось и что более менее работает. хочется узнать, если здесь возможность для улучшения. нет ли тут некорректностей?

Код: Выделить всё

{$E wdx}
library Segd1Inf1;

uses
  Windows,
  SysUtils,

  ContPlug,
  Usegdread,

  Classes;

  const
  _DetectString: PAnsiChar = 'EXT="SEGD"';

  _FieldsNum = 3;
  _Fields: array[0.._FieldsNum-1] of PAnsiChar = (
    'prof',
    'piket',
    'data');

procedure ContentGetDetectString(DetectString: PAnsiChar; maxlen: integer); stdcall;
begin
  lstrcpynA(DetectString, _DetectString,maxlen);
end; 

function ContentGetSupportedField(FieldIndex: integer; FieldName: PAnsiChar;
  Units: PAnsiChar; maxlen: integer): integer; stdcall;
begin
  if FieldIndex in [0..High(_Fields)] then
  begin
    lstrcpynA(FieldName,_Fields[FieldIndex],maxlen);
    strcopy(Units,'');

    Result := FT_STRING;
  end else Result := ft_NoMoreFields;
end;                                                                               

function ContentGetValue(fn: pchar; FieldIndex, UnitIndex: integer;
  FieldValue: PByte; MaxLen, Flags: integer): integer; stdcall;
 var
    s:AnsiString;
begin
     s:= segdread(fn, FieldIndex);
     Strlcopy (PAnsiChar (Fieldvalue),PAnsiChar(s),maxlen-1);
     result:=ft_string;
end;

exports
  ContentGetDetectString,
  ContentGetSupportedField,
  ContentGetValue;
                                                                                                         
end.
проверку на расширение 'segd' я сделал в модуле segdread.
еще у меня есть вопросы по поводу функции ContentGetDetectString зачем она нужна и на сколько критично, если ее убрать?
а также по поводу функции ContentGetValueW - в чем ее отличие от ContentGetValue. есть ли смысл делать все таки через ContentGetValueW?
Аватара пользователя
CaptainFlint
Сообщения: 6239
Зарегистрирован: Вт дек 14, 2004 22:34
Контактная информация:

Сообщение CaptainFlint »

tonyy писал(а):вот что у меня получилось и что более менее работает.
То есть выходит, что вся проблема была лишь в том, что segdread требует имя файла в ANSI?..
tonyy писал(а):еще у меня есть вопросы по поводу функции ContentGetDetectString зачем она нужна и на сколько критично, если ее убрать?
Зачем она нужна, описано в документации по написанию плагинов. Если кратко, позволяет отфильтровать файлы, заведомо плагином неподдерживаемые.
tonyy писал(а):а также по поводу функции ContentGetValueW - в чем ее отличие от ContentGetValue. есть ли смысл делать все таки через ContentGetValueW?
Отличие очевидно: в поддержке юникодных имён. Если попадётся SEGD-файл с юникодным именем, то Тотал не сможет его обработать через плагин, если не будет функции ContentGetValueW.
Если для функции segdread есть аналог, умеющий работать с такими файлами, то лучше сделать ContentGetValueW; если нет, то проще оставить ContentGetValue, чтобы не тратить ресурсы на перекодировку.
Почему же, ё-моё, ты нигде не пишешь "ё"?
Аватара пользователя
MVV
Сообщения: 4815
Зарегистрирован: Чт окт 15, 2009 10:58
Откуда: Ростов-Дон

Сообщение MVV »

ContentGetValueW - юникодная. Соответственно, плюс в том, что понимает файлы с путями, содержащими всякие там иероглифы и просто символы разных кодовых страниц.

ContentGetDetectString нужна, чтобы тотал автоматом определял строку детектирования. Если функции не будет, строку придется прописывать вручную. Если строки не будет, плагин будет работать для всех файлов, а не только определенных.
Ответить