На главную Наши проекты:
Журнал   ·   Discuz!ML   ·   Wiki   ·   DRKB   ·   Помощь проекту
ПРАВИЛА FAQ Помощь Участники Календарь Избранное RSS
msm.ru
Модераторы: Rouse_, jack128, Krid
  
    > Работа с RasAPI

      Особые благодарности
      Королеве дельфийского королевства
      Елене Филипповой,
      которая подвигла меня на ваяние сего опуса.

      Вместо предисловия

      Я не являюсь профессиональным программистом и никогда не писал статьей по программированию. Поэтому "продвинутых" специалистов прошу не утруждать себя чтением ниже изложенного дабы избежать обструкции по поводу допущенных мною в настоящей статье ошибок и вольностей.

      Итак. Многие из "непродвинутых" дельфийцев, пытаясь использовать интернет-компоненты, сталкиваются с проблемой "удаленного соединения" (*) . Собственно подключаться не трудно - чаще всего такие компоненты с помощью системной библиотеки wsock32.dll сами инициализируют подключение к Интернет по "удаленному соединению". Гораздо труднее бывает после интернет-сессии отключить "удаленное соединение". Поэтому на Круглом столе "Королевства Дельфи" с регулярностью наступления "критических дней" появляются вопросы (в том числе и мои):

      "как после .... завершить соединение по удаленному доступу?"
      А "продвинутые гуру" от программирования с завидным постоянством на них отвечают:
      "смотри Ras API..."
      Давайте и посмотрим, что такое Ras API и с чем его едят :)
      Ras API - Remote Access Service Application Programming Interface. По-русски: "интерфейс программирования приложений, использующих службу удаленного доступа". Этот интерфейс как раз и служит для соединения по "удаленному доступу", а также отслеживания его состояния, отсоединения от телефонной линии, создания новых "удаленных соединений", изменения свойств уже созданных "соединений" и многого, многого другого. В библиотеку rasapi32.dll включено 97 экспортируемых функций. Даже в Help для Дельфи Microsoft любезно включило описание работы с функциями этой библиотеки. Напечатайте где-нибудь в проекте, например, RasDial и нажмите F1 - убедитесь сами. Казалось бы в чем проблемы? Ан нет! В поставке Дельфи нет интерфейсного модуля для работы с этой библиотекой. Что такое интерфейсный модуль? Это Pascal Unit - *.pas или *.dcu, в котором описаны используемые в DLL константы, типы переменных, функции и процедуры, порядок обращения к ним и др. Например, Windows.pas (обратите внимание на самую первую запись, включаемую в клаузу Uses) - это интерфейсный модуль для системных библиотек kernel32.dll, user32.dll и gdi32.dll и др., который позволяет Pascal-программам обращаться к процедурам и функциям этих библиотек.

      После некоторого скитания по Интернет мне удалось обнаружить столь нужный нам всем интерфейсный модуль для rasapi.dll - называется он RasUnit.pas.

      Весь модуль описывать здесь я не буду. Опишу только пять функций, позволяющих соединиться с Интернет по телефонной линии с помощью модема, отследить состояние "удаленного соединения" и отключиться от телефонной линии по завершению интернет-сеанса.

      1. Получение сведений о всех зарегистрированных в системе "удаленных соединениях".

      Для получения сведений о всех зарегистрированных в системе "удаленных соединений" используется библиотечная функция (**) RasEnumEntries
      Рассмотрим работу этой функции. Прежде всего определим переменные:
      BuffSize: Integer;
      размер массива из AnsiChar, в который будут помещаться cведения об "удаленных соединениях"
      Entries: Integer;
      количество зарегистрированных "удаленных соединений"
      Entry : Array[1..MaxEntries] of TRasEntryName;
      массив состоящий из переменных, в которые будут помещены сведения об "удаленных соединениях", где константа MaxEntries - количество возможных соединений, TRasEntryName - определение (type) записи состоящей из двух полей dwSize и szEntryName (определены в RasUnit.pas)
      X, Result_ : Integer;
      необходимые процедурные переменные
      AllEntries: TStrings;
      сюда мы поместим названия "удаленных соединений" для дальнейшей работы с ними
      Перейдем к описанию работы функции RasEnumEntries.
      1. Определим размер переменной типа TRasEntryName и инициализируем переменную Entry, поместив в поле dwSize полученный размер.
      Entry[1].dwSize:=SizeOf(TRasEntryName)
      2. Определим размер AnsiChar-массива, в который поместим сведения обо всех "удаленных соединениях"
      BuffSize:=SizeOf(TRasEntryName)*MaxEntries
      3. Вызовем функцию RasEnumEntries в результате чего получим искомые результаты:
      Result_:=RasEnumEntries(nil, nil, @Entry[1], BuffSize, Entries), где
      • Result_- в случае успешного выполнения возвращает 0, в противном случае получим ERROR_BUFFER_TOO_SMALL (буфер слишком маленький) или ERROR_NOT_ENOUGH_MEMORY(не хватает памяти).
      • BuffSize - указанный нами размер AnsiChar-массива.
      • @Entry[1]- получим указатель на первый элемент массива, в который поместились необходимые нам сведения.
      • Entries - получим количество зарегистрированных в системе "удаленных доступов".

      Дальше уже просто.

      В случае успешного выполнения функции и существования хотя бы одного зарегистрированного "удаленного соединения" заполним нашу переменную
      If (Result_=0) and (Entries>0) Then
      Begin
      	AllEntries:= TStringList.Create;
      	For X:=1 To Entries Do
      	Begin
      		AllEntries.Add(Entry[x].szEntryName);
      	End;
      	.....здесь мы работает со своей переменной, например, 
      	помещаем сведения об "удаленных соединениях" в ListBox......
      
      	AllEntries.Free;
      End;
      

      2. Соединение с интернет-сервером через выбранный "удаленный доступ" и получение статуса соединения.

      Для соединения с интернет-сервером используются две библиотечные функции RasGetEntryDialParams и RasDial. Для обработки ошибок, возникших в процессе соединения, используется еще одна библиотечная функция RasGetErrorString.
      Определим необходимые переменные.

      Глобальные:

      MyDialParam : TMyDialParam
      переменная состояния соединения, где
      TMyDialParam = Record
      AMsg : Integer; - код сообщения
      AState : TRasConnState; - статус соединения (тип переменной определен в  RasUnit.pas)
      AError : Integer; - код ошибки
      hRas: ThRASConn
      в эту переменную будет помещен handle (так сказать "ИНН") "удаленного соединения", к этой переменной будет обращаться функция RasHangUp для завершения соединения, тип переменной описан в RasUnit.pas

      Локальные:

      Fp:LongBool
      если в "удаленном доступе" не указан пароль пользователя, то эта переменная устанавливается в False и появляется приглашение ввести пароль, если пароль указан, то переменная устанавливается в True и приглашение не появляется.
      DialParams: TRasDialParams
      переменная, в которую будут переданы параметры "удаленного соединения", описывать тип этой переменной я не буду - он хорошо описан в Win32 Programmer's Reference (кто не знает - это один из разделов Help'а, поставляемого вместе с Delphi) и определен в RasUnit.pas
      AEntryDial:String
      переменная, в которую поместим название "удаленного соединения"
      R: Integer
      результат выполнения библиотечных функций
      C : Array[0..100] of Char
      переменная, в которую записывается текст сообщения об ошибке
      Кроме переменных необходимо также определить CallBack-процедуру, которая будет использована в функции RasDial (***).
      Procedure RasCallBack (msg: Integer;
      		state: TRasConnState; 
      		error: Integer); stdcall (****)  , где
      
      msg: Integer - код сообщения
      state: TRasConnState - состояние соединения
      error: Integer - код ошибки
       
      В этой процедуре передадим глобальной переменной MyDialParam значения указанных переменных.
      MyDialParam.AMsg:=msg;
      MyDialParam.AState:=state;
      MyDialParam.AError:=error;
      
      А также вызовем функцию GetStatusString (будет описана ниже), которая сообщит нам в Label1.Caption о состоянии соединения.
      Form1.Label1.Caption:= GetStatusString(MyDialParam.AState,MyDialParam.AError);
      Form1.Label1.Update;(на всякий случай).
      
      Все, на этом с CallBack процедурой закончено.

      Переходим к описанию процесса подключения к "удаленному соединения".
      1. Получим название выбранного нами "удаленного соединения"
        AEntryDial:=ListBox1.Items.Strings[ListBox1.ItemIndex];
      2. Заполним все поля переменной DialParams нолями.
        FillChar(DialParams, SizeOf(TRasDialParams), 0);
      3. Инициализируем переменную DialParams и поместим в поле szEntryName этой переменной название выбранного "удаленного соединения"
        With DialParams Do
        Begin
            dwSize:=Sizeof(TRasDialParams);
            StrPCopy(szEntryName, FEntry2Dial);
        End;
        
      4. Вызовем библиотечную функцию RasGetEntryDialParams, которая заполнит оставшиеся незаполненными поля переменной DialParams
        R:=RasGetEntryDialParams(nil, DialParams, Fp);
      5. Далее, если все удачно (см. значение переменной R), вызываем библиотечную функцию RasDial, поместив в нее переменную DialParams и указав на описанную выше CallBack-процедуру RasCallBack. В случае ошибок в работе функции RasDial вызовем библиотечную функцию обработки ошибок RasGetErrorString, которая даст нам текст сообщения об ошибке, и выйдем из процедуры.
        If R=0 then
        Begin
        	Application.ProcessMessages ; (*****)
        	R:=RasDial(nil, nil, DialParams, 0, @RasCallback, hRAS);
        	If R<>0 then
        	Begin
        		RasGetErrorString(R,C,100);
        		MessageBox(0,C,'Ошибка!',MB_OK);
        		Exit;
        	End;
        End;
        
      6. 6. На этом все!
      Напоследок опишем
      Function GetStatusString(State: TRasConnState; Error: Integer): String
      которая даст нам состояние соединения. Думаю, что переменные State и Error уже не требуют объяснения.
      If Error<>0 then
      Begin
          RasGetErrorString(Error, C, 100);
          Result:=C;
      End Else
      Begin
          S:='';
          Case State Of
            RASCS_OpenPort:
              S:='Opening port';
      ..................................
      вырезано....
      .................................
             RASCS_Disconnected:
              S:='Disconnected';
            End;
      Result:=S;
      End;
      
      Теперь уже совсем все с подключением "удаленного соединения" и получением его статуса!

      3. Завершение удаленного соединения.

      Самый распространенный вопрос по рассматриваемой нами теме это -
      "как после .... завершить соединение по удаленному доступу?"
      После того, что мы уже рассмотрели, ответ на этот вопрос покажется очень простым - надо вызвать библиотечную функцию RasHangUp с одной единственной уже описанной нами переменной hRas: ThRASConn.
      RasHangUp(hRas)

      Удачи!

      Интерфейсный модуль RasUnit.pas и проект, использующий рассмотренные нами функции библиотеки rasapi32.dll, прилагается:
      Файлы проекта + RasUnit : RasAPI.zip (15 K)

      Авторские права
      © Александр Терехов ™ 2001,


      Примечания:
      * - В настоящей статье под "удаленным соединением" подразумевается подключение к интернет-провайдеру по телефонной сети с помощью модема.
      (вернуться)

      ** - Далее под "библиотечной функцией" будем понимать функцию библиотеки rasapi32.dll
      (вернуться)

      *** - Для не посвященных в дебри программирования от Microsoft.
      CallBack-функция - это функция "обратного вызова". Служит для обработки некоторых функций и процедур созданных компанией Microsoft. В Pascal'е не применяется. С CallBack-функциями приходится часто сталкиваться при попытках программирования внутри Delphi на API (Application Programming Interface - интерфейс программирования приложений). К чему такие сложности? Не знаю. Возможно это такой стиль программирования от Microsoft. J
      Т.к. в нашем примере возвращать никаких "CallBack-данных" не требуется, поэтому вместо CallBack-функции определим CallBack-процедуру, да простит меня за это Билл Гейтс (кто не знает - это отец-основатель Microsoft). Библиотечная функция RasDial, вызывая эту процедуру помещает в переменную state код состояния соединения.
      (вернуться)

      **** - Stdcall - это способ передачи данных через стек CPU (справа - налево). Зарезервированное слово stdcall необходимо применять при обращении к находящимся в DLL (Dynamic Link Library - динамически подключаемая библиотека) процедурам и функциям, написанных на другом языке программирования - это из Help'а. Я однажды забыл указать этот параметр при обращении к DLL написанной на Pascal и в результате "подвесил" компьютер. Поэтому, всегда при обращении к библиотекам указывайте - stdcall.
      (вернуться)

      ***** - Это для того, чтобы в процессе соединения с сервером наше приложение могло реагировать на сообщения Windows, например, на нажатие кнопок.
      (вернуться)

        Оригинал статьи расположен по адресу http://www.delphikingdom.ru/helloworld/rasapi.htm
        0 пользователей читают эту тему (0 гостей и 0 скрытых пользователей)
        0 пользователей:


        Рейтинг@Mail.ru
        [ Script execution time: 0,0198 ]   [ 16 queries used ]   [ Generated: 2.05.24, 02:06 GMT ]