Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Общие вопросы > Indy и DLL и Access Violation


Автор: Kelas 31.8.2006, 17:51
Мне по учебе нужно добавить к проекту DLL. Проэкт - база данных, работающая с клиентами с помощью компонентов TIdTCPClient и TIdTCPServer - этакая удаленная база вручную. Я решил перенести все функции работы с этими компанентами в DLL. Создал новый проект DLL-ки, добавил к uses юнит с процедурками и выписал все нужные в Exports. Все нормально скомпилировалось, я подключил библиотеку к проекту функцией LoadLibrary и для начала заменил функцию которая стучится по указанному порту и адрсу к серверу и проверяет, есть ли там вто живой. 

Вот эта функция:

Код

function InitServer (var P : Pointer) : Boolean;
var
  TCP : ^TIdTCPClient;
begin
if P<>nil then
  begin
    TCP := P;
    with TCP^ do
      begin
        try
          try
            Connect;
            WriteInteger(taInitCheck); //  taInitCheak = 0;
            if (ReadInteger = trSuccess) then //trSuccess = 1;
              Result := true
            else
              Result := false;
          except
            on EIdSocksError do
              Result := false;
            on EIdException do
              Result := false;
            on E:Exception do
              ShowMessage(E.Message);
          end;
        finally
          Disconnect;
        end;
      end
  end
else
  Result := false;
end;


А вот код в проекте где вытаскивается она из DLL и вызыается:

Код

procedure TLForm.Button2Click(Sender: TObject);
var
  TIS : function(var P: Pointer):Boolean;
  P : Pointer;
begin
{...}
      @TIS := GetProcAddress(GetModuleHandle('KB_TCP.dll'),'InitServer');
      if @TIS = nil then ShowMessage('...');
      P := @CruiserForm.cTCP;
      if NOT TIS(P) then
        begin
          MsgDlg.Text := 'Сервер на отвечает';
          MsgDlg.ShowModal;
        end
{...}
end;


Во время работы функции возникает Access Violation c чтением нулевого адреса, на строчке "WriteInteger(taInitCheck);" в модуле KB_TCP.dll

Если та же функция, слово в слово будет использоваться в проекте, тоесть как бы локально, а не вызываться из DLL то никаких ошибок не возникает и все работает как часы... 




Автор: Yanis 1.9.2006, 09:08
Что такое CruiserForm.cTCP?

Автор: Matematik 1.9.2006, 09:12
CruiserForm.cTCP это кажется TIdTCPClient
По мне сомнительная операцие передавать в dll указателлльна экземпляк объекта и там с ним работать...

Автор: Snowy 1.9.2006, 09:32
dll и передача объектов - понятия несовместимые.
Используй bpl или выделяй в dll только полностью отделённый логический код.
Хотя в принципе непонятно, зачем тебе вообще dll?

Автор: Yanis 1.9.2006, 09:42
Kelas
Попробуй мой метод загрузки. Должно получиться smile

Цитата(Kelas @  31.8.2006,  18:51 Найти цитируемый пост)
@TIS := GetProcAddress(GetModuleHandle('KB_TCP.dll'),'InitServer');

Для загрузки библиотеки лучше использовать LoadLibrary. Стоит переделать процедуру нажатия на кнопку, что бы она приняла приблизительно такой вид:
Код
var
  P: Pointer;
  TIS: function(var P: Pointer): Boolean;
  hLib: DWORD;
begin
  hLIb := LoadLibrary('KB_TCP.dll');
  if hLib > 0 then
    begin // библиотека подгрузилась
      TIS := GetProcAddress(hLib, 'InitServer');

      if @TIS = nil then
        begin // не удалось получить адрес функции
          FreeLibrary(hLib);
          Exit;
        end;

      // адрес функции получен
      // можно её вызывать
      P := @IdTCPClient1;
      if not TIS(P) then
        begin
          //MsgDlg.Text := 'Сервер на отвечает';
          //MsgDlg.ShowModal;
        end;

      // если мы до сюда добрались, то
      // библиотека была подгружена и надо её отпустить
      FreeLibrary(hLib);
    end;
end;


Автор: Kelas 1.9.2006, 17:40
Спасибо Yanis, твой метод работает, однако проблема была не в этом, с загрузкой библиотеки и получением адреса функции у меня проблем не было, я тоже грузил библиотеку с помощью LoadLibrary просто происходило это не в функции обработки кнопки.

Проблема как сказал Snowy, и подозревал Matematik, в том, что я работал с методами объекта. Странно конечно, что функция Connect все равно срабатывала без ошибок, но WriteInteger он, несмотря на это, не переваривал.

Я переделал функцию - просто созал "локального клиента" в функции и передал Host и Port в него с помощью уже передававшегося указателя - читает он вроде без Access Violation'ов.

Вот итоговый код функции, если это кому-то интересно:

Код

function InitServer (var P : Pointer) : Boolean;
var
  pTCP : ^TIdTCPClient;
  TCP : TIdTCPClient;
begin
if P<>nil then
  begin
    pTCP := P;
    TCP := TIdTCPClient.Create(nil);
    TCP.Host := pTCP^.Host;
    TCP.Port := pTCP^.Port;
    with TCP do
      begin
        try
          try
            Connect;
            WriteInteger(taInitCheck);
            if (ReadInteger = trSuccess) then
              Result := true
            else
              Result := false;
          except
            on EIdSocksError do
              Result := false;
            on EIdException do
              Result := false;
            on E:Exception do
            begin
              Result := false;
              ShowMessage(E.Message);
            end;
          end;
        finally
          Disconnect;
        end;
      end;
    TCP.Destroy;
  end
else
  Result := false;
end;

Автор: Snowy 1.9.2006, 17:52
Просто класс, скомпиленный в exe и он же скомпиленный в dll - это уже 2 разных класса.
Для готового кода они совсем не равны.
Класс и работа с ним должны быть описаны в одном месте - либо в dll, либо в exe.

Добавлено @ 17:56 
Цитата(Kelas @  1.9.2006,  17:40 Найти цитируемый пост)
функция Connect все равно срабатывала без ошибок, но WriteInteger он, несмотря на это, не переваривал.
Потому что Connection прописана в виртуальной таблице, а WriteInteger фиксированно.

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)