![]() |
|
|
Регистрация | << Правила форума >> | FAQ | Пользователи | Календарь | Поиск | Сообщения за сегодня | Все разделы прочитаны |
![]() |
|
Опции темы | Поиск в этой теме | Опции просмотра |
#1
|
|||
|
|||
![]() Уже пол дня бъюсь на проблемой:
Функция возвращяет неправильные результаты вычисления и после закрытия приложения вываливается "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
|
|||
|
|||
![]() 1. Непонятно, зачем вообще писать такую функцию, т.к. есть уже готовые ExtractFileName, ExtractFileExt, ChangeFileExt, etc...
2. Не вижу описание типа импортируемой функции. Возможно засада именно там, т.к. тип передачи параметров должен быть одинаковый и в dll и у типа в вызывающем модуле. 3. Не стоит использовать PChar там, где этого можно избежать. Лучше WideString. А если не из Delphi использовать не требуется, то вообще используй String. 4. Основная ошибка. Память, выделенная в dll, должна освобождаться в dll же. Даже при использовании модуля ShareMem. Т.е. либо передавай заранее выделенный буфер в функцию (фактически, процедуру), либо после отработки вызывай процедуру освобождения памяти из dll. У тебя там идет неявние выделение памяти. Кстати, от него тоже лучше избавиться. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Yo_Asakyra (28.04.2012)
|
#3
|
|||
|
|||
![]() Большое спасибо за подсказки
![]() Я читал, что PСhar необходимо всегда использовать при работе с DLL (и в качесте возвращаемых значений функций, и в качестве аргументов строкового типа). Неужели это не так? |
#4
|
||||
|
||||
![]() Именно так, а вот String использовать можно, но не желательно.
Je venus de nulle part 55.026263 с.ш., 73.397636 в.д. |
#5
|
||||
|
||||
![]() а какой вообще смысл выносить эту функцию в DLL? сделать ее в обычном модуле, положить в папку прописаную в library path и при потребностях подключать этот модуль в дальнейшем. меньше места займет
![]() по проблеме: у тебя объявляется локальная переменная FNameTrunc типа указатель на Char. ей присваиваются указатели на String. память под строки выделяется и освобождается неявно компилятором. т.е. при завершении работы функции память на которую ссылается FNameTrunc "не существует". а она же еще и возвращается! решение: как уже написал lmikle передавай в функцию буфер и его размер для возвращаемого значения. пример Windows API функция: Цитата:
![]() Пишу программы за еду. __________________ |
Этот пользователь сказал Спасибо NumLock за это полезное сообщение: | ||
Yo_Asakyra (28.04.2012)
|
#6
|
|||
|
|||
![]() Простите, вопрос снят - основная проблема устранена
![]() Но всё-таки почему копилятор перепрыгивает инициализацию переменных? begin //отсуда N := 0; FirstTmp := 0; SecondTmp := 0; //суда Последний раз редактировалось Yo_Asakyra, 30.04.2012 в 18:11. |
#7
|
|||
|
|||
![]() Цитата:
Не совсем так. PChar остался из соображений совместимости. Более правильно, все-таки, использовать WideString. Цитата:
А тебе компилятор предупреждений не дает на этих строках, типа значение не используется? Возможно, при оптимизации этот код удален из-за его излишности. |
Этот пользователь сказал Спасибо lmikle за это полезное сообщение: | ||
Yo_Asakyra (30.04.2012)
|
#8
|
||||
|
||||
![]() да все честно:
Код:
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
|
|||
|
|||
![]() Он действительно пишет, что эти значения переменных не используются, но всё-таки лучше всегда инициализовывать все переменные, а из-за такой вот оптимизации могут быть сбои работы программы. Как быть?
|
#10
|
||||
|
||||
![]() Если компилятор пишет, что переменные не используются, значит они вообще не нужны. Просто удали их из кода
![]() Помогаю за Спасибо ![]() |
#11
|
|||
|
|||
![]() Цитата:
![]() |
#12
|
||||
|
||||
![]() Если переменные глобальные, то они в любом случае уже инициализированы 0.
Je venus de nulle part 55.026263 с.ш., 73.397636 в.д. |
Этот пользователь сказал Спасибо angvelem за это полезное сообщение: | ||
Yo_Asakyra (01.05.2012)
|
#13
|
||||
|
||||
![]() Если дальше эти переменные используются в левой части, то их обнулять не надо.
Пример: Код:
var x: Integer; begin x := 0; // обнулять переменную не имеет смысла x := y*5; end; Помогаю за Спасибо ![]() |
Этот пользователь сказал Спасибо v1s2222 за это полезное сообщение: | ||
Yo_Asakyra (01.05.2012)
|
#14
|
||||
|
||||
![]() Цитата:
Пишу программы за еду. __________________ |
Этот пользователь сказал Спасибо NumLock за это полезное сообщение: | ||
Yo_Asakyra (01.05.2012)
|
#15
|
|||
|
|||
![]() ^_^ Возник эстетический вопрос:
Нужно вызвать одно и то же действие с разными параметрами из разных частей программы (меню, кнопки). Мне видится 2 подхода: 1) Создать Action в ActionList'e, назначить его этим элементам и внутри самого Action'a проводить анализ чем был вызван этот Action. 2) В каждом GUI элементе просто прописать один и тот-же код с разными параметрами. Плюсы первого пункта в том, что нужно меньше кода. Плюсы второго пункта в том, что все действия прозрачны и удобочитаемы. По сути и в 1-м варианте будет всё удобочитаемо, но меня не оставляют сомнения, что что-то не так. Прошу расписать что лучше и эстетичнее использовать в данной ситуации, и по возможности прокомментировать ваш выбор. Спасибо ![]() |