Форум по Delphi программированию

Delphi Sources



Вернуться   Форум по Delphi программированию > Все о Delphi > [ "Начинающим" ]
Ник
Пароль
Регистрация <<         Правила форума         >> FAQ Пользователи Календарь Поиск Сообщения за сегодня Все разделы прочитаны

Ответ
 
Опции темы Поиск в этой теме Опции просмотра
  #1  
Старый 27.04.2012, 18:58
Yo_Asakyra Yo_Asakyra вне форума
Прохожий
 
Регистрация: 18.12.2011
Сообщения: 11
Репутация: 10
Восклицание DLL с приложением. Проблемы их взаимопонимания

Уже пол дня бъюсь на проблемой:
Функция возвращяет неправильные результаты вычисления и после закрытия приложения вываливается "Invalid Pointer Operation".
Причём если функцию описать в приложении, то всё работает как надо и ничего не вываливается.

Помогите разобраться с причаними
DLL код:
Код:
uses
  ShareMem
. . . . .

function ExtractFileName(FilePath: PChar; ShowExtension: byte): PChar;
var
  FNameLength, CopyLength: Word;
  FNameTrunc: PChar;
begin
  FNameLength := Length(FilePath);
  CopyLength  := FNameLength;
  case ShowExtension of
   0: begin
        while (FilePath[FNameLength] <> '\') and (FNameLength > 0) do
          Dec(FNameLength);
        if FNameLength <> 0 then
          FNameTrunc := PChar(Copy(FilePath, FNameLength + 2, CopyLength-FNameLength))
        else
          FNameTrunc := Pchar('');
      end;
   1: begin
        while (FilePath[FNameLength] <> '\') and (FNameLength > 0) do
          Dec(FNameLength);
        if FNameLength <> 0 then
          Begin
            FNameTrunc := PChar(Copy(FilePath, FNameLength + 2, CopyLength-FNameLength));
            FNameLength :=Length(FNameTrunc);
            while (FNameTrunc[FNameLength] <> '.') and (FNameLength > 0) do
              Dec(FNameLength);
            FNameTrunc := PChar(Copy(FNameTrunc, 1, FNameLength));
          End
        else
          FNameTrunc := PChar('');
      end;
   2: begin
        FNameTrunc := PChar(FilePath);
      end;
   else
     FNameTrunc := PChar('Incorrect input value in Case. Use 0 - Name+Ext; 1 - Name; 2 - full path; Only!');
  end;
  Result := FNameTrunc;
End;
. . . . 
exports
  ExtractFileName;

Эта функция находится в DLL и предназначения для отделения имени файла от его расширения. Как видно, она имеет 3 режима в зависимости от переданных параметров:
1)Отделить имя и расшир от пути;
2) отделить имя от пути и расшир;
3) собственно ничего не делать.


Вызываю из программы её таким способом:
Код:
procedure Button1.Click(Sender: TObject);
 var
  ExtractFileName : Function(FilePath: PChar; ShowExtension: byte) : PChar;  Stdcall;
  DLLInstance : THandle;
  GettedFileName : String;
begin
  OpenWordFile(FileOpen1.Dialog.FileName);
  @ExtractFileName := nil; //Очищаем адресс функции от мусора 
  DLLInstance := LoadLibrary(PChar('Main.dll')); //Подгружаем DLL к приложению
  if (DLLInstance = 0) then
    begin
      MessageDlg('Невозможно загрузить DLL', mtError, [mbOK], 0);
      Exit;
    end;
  try
    @ExtractFileName := GetProcAddress(DLLInstance, 'ExtractFileName'); //Пытаемся вызвать из DLL указанную фунцию
    if Assigned(@ExtractFileName) then
      Begin
        GettedFileName := 'Файл:' + '  ' + StrPas(ExtractFileName(Pchar(FileOpen1.Dialog.FileName), 0));
        AddTextInDB.StatusBar.Panels.Items[0].Text := GettedFileName;
      End
    else
      MessageDlg('Искомая функция не найдена!', mtError, [mbOK], 0);
  finally
    FreeLibrary(DLLInstance);
  end;
End;

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

Последний раз редактировалось Yo_Asakyra, 27.04.2012 в 19:04.
Ответить с цитированием
  #2  
Старый 27.04.2012, 19:20
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,093
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

1. Непонятно, зачем вообще писать такую функцию, т.к. есть уже готовые ExtractFileName, ExtractFileExt, ChangeFileExt, etc...
2. Не вижу описание типа импортируемой функции. Возможно засада именно там, т.к. тип передачи параметров должен быть одинаковый и в dll и у типа в вызывающем модуле.
3. Не стоит использовать PChar там, где этого можно избежать. Лучше WideString. А если не из Delphi использовать не требуется, то вообще используй String.
4. Основная ошибка. Память, выделенная в dll, должна освобождаться в dll же. Даже при использовании модуля ShareMem. Т.е. либо передавай заранее выделенный буфер в функцию (фактически, процедуру), либо после отработки вызывай процедуру освобождения памяти из dll. У тебя там идет неявние выделение памяти. Кстати, от него тоже лучше избавиться.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Yo_Asakyra (28.04.2012)
  #3  
Старый 27.04.2012, 20:04
Yo_Asakyra Yo_Asakyra вне форума
Прохожий
 
Регистрация: 18.12.2011
Сообщения: 11
Репутация: 10
По умолчанию

Большое спасибо за подсказки
Я читал, что PСhar необходимо всегда использовать при работе с DLL (и в качесте возвращаемых значений функций, и в качестве аргументов строкового типа). Неужели это не так?
Ответить с цитированием
  #4  
Старый 27.04.2012, 21:35
Аватар для angvelem
angvelem angvelem вне форума
.
 
Регистрация: 18.05.2011
Адрес: Омск
Сообщения: 3,970
Версия Delphi: 3,5,7,10,12,XE2
Репутация: выкл
По умолчанию

Именно так, а вот String использовать можно, но не желательно.
__________________
Je venus de nulle part
55.026263 с.ш., 73.397636 в.д.
Ответить с цитированием
  #5  
Старый 28.04.2012, 08:55
Аватар для NumLock
NumLock NumLock вне форума
Let Me Show You
 
Регистрация: 30.04.2010
Адрес: Северодвинск
Сообщения: 5,426
Версия Delphi: 7, XE5
Репутация: 59586
По умолчанию

а какой вообще смысл выносить эту функцию в DLL? сделать ее в обычном модуле, положить в папку прописаную в library path и при потребностях подключать этот модуль в дальнейшем. меньше места займет

по проблеме:
у тебя объявляется локальная переменная FNameTrunc типа указатель на Char. ей присваиваются указатели на String. память под строки выделяется и освобождается неявно компилятором. т.е. при завершении работы функции память на которую ссылается FNameTrunc "не существует". а она же еще и возвращается!

решение:
как уже написал lmikle передавай в функцию буфер и его размер для возвращаемого значения. пример Windows API функция:

Цитата:
The GetClassName function retrieves the name of the class to which the specified window belongs.

int GetClassName(

HWND hWnd, // handle of window
LPTSTR lpClassName, // address of buffer for class name
int nMaxCount // size of buffer, in characters
);


Parameters

hWnd

Identifies the window and, indirectly, the class to which the window belongs.

lpClassName

Points to the buffer that is to receive the class name string.

nMaxCount

Specifies the length, in characters, of the buffer pointed to by the lpClassName parameter. The class name string is truncated if it is longer than the buffer.

Return Values

If the function succeeds, the return value is the number of characters copied to the specified buffer.
If the function fails, the return value is zero. To get extended error information, call GetLastError.
импортируется из user32.dll и не выдает таких ошибок
__________________
Пишу программы за еду.
__________________
Ответить с цитированием
Этот пользователь сказал Спасибо NumLock за это полезное сообщение:
Yo_Asakyra (28.04.2012)
  #6  
Старый 30.04.2012, 17:50
Yo_Asakyra Yo_Asakyra вне форума
Прохожий
 
Регистрация: 18.12.2011
Сообщения: 11
Репутация: 10
Восклицание

Простите, вопрос снят - основная проблема устранена
Но всё-таки почему копилятор перепрыгивает инициализацию переменных?

begin //отсуда
N := 0;
FirstTmp := 0;
SecondTmp := 0;

//суда

Последний раз редактировалось Yo_Asakyra, 30.04.2012 в 18:11.
Ответить с цитированием
  #7  
Старый 30.04.2012, 18:58
lmikle lmikle вне форума
Модератор
 
Регистрация: 17.04.2008
Сообщения: 8,093
Версия Delphi: 7, XE3, 10.2
Репутация: 49089
По умолчанию

Цитата:
Сообщение от angvelem
Именно так, а вот String использовать можно, но не желательно.

Не совсем так. PChar остался из соображений совместимости. Более правильно, все-таки, использовать WideString.

Цитата:
Но всё-таки почему копилятор перепрыгивает инициализацию переменных?

А тебе компилятор предупреждений не дает на этих строках, типа значение не используется? Возможно, при оптимизации этот код удален из-за его излишности.
Ответить с цитированием
Этот пользователь сказал Спасибо lmikle за это полезное сообщение:
Yo_Asakyra (30.04.2012)
  #8  
Старый 30.04.2012, 19:03
Аватар для NumLock
NumLock NumLock вне форума
Let Me Show You
 
Регистрация: 30.04.2010
Адрес: Северодвинск
Сообщения: 5,426
Версия Delphi: 7, XE5
Репутация: 59586
По умолчанию

да все честно:
Код:
procedure TForm1.FormCreate(Sender: TObject);
var
  FirstTmp: Integer;
  SecondTmp: Integer;
begin
  Sleep(0);
  FirstTmp:=0;
  SecondTmp:=0;
  Tag:=FirstTmp+SecondTmp;
end;
__________________
Пишу программы за еду.
__________________
Ответить с цитированием
Этот пользователь сказал Спасибо NumLock за это полезное сообщение:
Yo_Asakyra (30.04.2012)
  #9  
Старый 30.04.2012, 19:11
Yo_Asakyra Yo_Asakyra вне форума
Прохожий
 
Регистрация: 18.12.2011
Сообщения: 11
Репутация: 10
По умолчанию

Он действительно пишет, что эти значения переменных не используются, но всё-таки лучше всегда инициализовывать все переменные, а из-за такой вот оптимизации могут быть сбои работы программы. Как быть?
Ответить с цитированием
  #10  
Старый 30.04.2012, 19:20
Аватар для v1s2222
v1s2222 v1s2222 вне форума
Продвинутый
 
Регистрация: 07.09.2010
Сообщения: 726
Репутация: 26711
По умолчанию

Если компилятор пишет, что переменные не используются, значит они вообще не нужны. Просто удали их из кода
__________________
Помогаю за Спасибо
Ответить с цитированием
  #11  
Старый 30.04.2012, 19:38
Yo_Asakyra Yo_Asakyra вне форума
Прохожий
 
Регистрация: 18.12.2011
Сообщения: 11
Репутация: 10
Восклицание

Цитата:
Сообщение от v1s2222
Если компилятор пишет, что переменные не используются, значит они вообще не нужны. Просто удали их из кода
Не используется лишь значение "0" этих переменных, так как сами переменные в коде использованы. Поэтому и возник подобный вопрос
Ответить с цитированием
  #12  
Старый 30.04.2012, 21:24
Аватар для angvelem
angvelem angvelem вне форума
.
 
Регистрация: 18.05.2011
Адрес: Омск
Сообщения: 3,970
Версия Delphi: 3,5,7,10,12,XE2
Репутация: выкл
По умолчанию

Если переменные глобальные, то они в любом случае уже инициализированы 0.
__________________
Je venus de nulle part
55.026263 с.ш., 73.397636 в.д.
Ответить с цитированием
Этот пользователь сказал Спасибо angvelem за это полезное сообщение:
Yo_Asakyra (01.05.2012)
  #13  
Старый 30.04.2012, 22:01
Аватар для v1s2222
v1s2222 v1s2222 вне форума
Продвинутый
 
Регистрация: 07.09.2010
Сообщения: 726
Репутация: 26711
По умолчанию

Если дальше эти переменные используются в левой части, то их обнулять не надо.
Пример:
Код:
var  x: Integer;
begin
  x := 0; // обнулять переменную не имеет смысла
  x := y*5;
end;
Если переменные дальше используются, компилятор не стал бы выдавать данное предупреждение.
__________________
Помогаю за Спасибо
Ответить с цитированием
Этот пользователь сказал Спасибо v1s2222 за это полезное сообщение:
Yo_Asakyra (01.05.2012)
  #14  
Старый 01.05.2012, 08:31
Аватар для NumLock
NumLock NumLock вне форума
Let Me Show You
 
Регистрация: 30.04.2010
Адрес: Северодвинск
Сообщения: 5,426
Версия Delphi: 7, XE5
Репутация: 59586
По умолчанию

Цитата:
Сообщение от Yo_Asakyra
из-за такой вот оптимизации могут быть сбои работы программы.
нет, не будет никаких сбоев.
__________________
Пишу программы за еду.
__________________
Ответить с цитированием
Этот пользователь сказал Спасибо NumLock за это полезное сообщение:
Yo_Asakyra (01.05.2012)
  #15  
Старый 05.05.2012, 19:15
Yo_Asakyra Yo_Asakyra вне форума
Прохожий
 
Регистрация: 18.12.2011
Сообщения: 11
Репутация: 10
Подмигивание

^_^ Возник эстетический вопрос:
Нужно вызвать одно и то же действие с разными параметрами из разных частей программы (меню, кнопки).

Мне видится 2 подхода:
1) Создать Action в ActionList'e, назначить его этим элементам и внутри самого Action'a проводить анализ чем был вызван этот Action.
2) В каждом GUI элементе просто прописать один и тот-же код с разными параметрами.

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

Прошу расписать что лучше и эстетичнее использовать в данной ситуации, и по возможности прокомментировать ваш выбор.
Спасибо
Ответить с цитированием
Ответ


Delphi Sources

Опции темы Поиск в этой теме
Поиск в этой теме:

Расширенный поиск
Опции просмотра

Ваши права в разделе
Вы не можете создавать темы
Вы не можете отвечать на сообщения
Вы не можете прикреплять файлы
Вы не можете редактировать сообщения

BB-коды Вкл.
Смайлы Вкл.
[IMG] код Вкл.
HTML код Выкл.
Быстрый переход


Часовой пояс GMT +3, время: 13:59.


 

Сайт

Форум

FAQ

Соглашения

Прочее

 

Copyright © Форум "Delphi Sources" by BrokenByte Software, 2004-2025