Использование Internet-функций Win32 API

Internet так сильно вошел в нашу жизнь, что программа, так или иначе не использующая его возможности, обречена на "вымирание" почти как динозавры. Поэтому всех программистов, вне зависимости от квалификации и специализации так и тянет дописать до порой уж е готовой программы какой-то модуль для работы с Internet. Но тут и встает вопрос - как это сделать? Давайте рассмотрим, что нам предлагает среда Borland Delphi и Win32 API.
Во-первых, можно использовать компоненты с вкладки FastNet. Все они написаны фирмой NetMasters и поставляются без исходного кода. По многочисленным откликам различных разработчиков можно сказать, что большинство из них не выдерживает никакой критики, особенно "отличились" компоненты для работы с почтой. Большинство проблем можно было бы исправить, но так как исходные тексты закрыты, то это вряд ли удастся. Даже если вы будете использовать такие вроде бы надежные компоненты как TNMHTTP, TNMFTP, то в случае распространения готовой программы перед вами встает проблема: для полноценной работы программа с этими компонентами требует наличия ряда динамических библиотек. Значит, их надо отыскать, потом поставлять вместе с приложением, копировать в системные папки … Короче говоря, все слишком запутано.
Если вам не требуется всей функциональности этих компонент, например, надо только реализовать функции GET или POST протокола HTTP, то можно поискать на сайтах с компонентами, вроде torry.ru - там обязательно сыщется много различных библиотек, по большей части бесплатных, и с исходным кодом.
Но зачем нам что-то использовать, когда есть доступ к Win32 API? Если приглядеться, то все эти компоненты всего лишь оболочка для вызова функций более низкого порядка. А раз так, то можно сразу их использовать. Кроме полного контроля над реализацией сетевых функций вы будете иметь и более компактный и быстрый код, так как устраняется прослойка между программой и API. Так что же такое Internet-функции Win32 API?
Все Internet- функции разбиты на категории:
* General Win32 Internet Functions - общие функции. 
* Automatic Dialing Functions - функции для автодозвона. 
* Uniform Resource Locator (URL) Functions - функции для работы с URL. 
* FTP Functions - FTP- функции. 
* Gopher Functions - Gopher- функции. 
* HTTP Functions - HTTP- функции. 
* Cookie Functions - Работа и управление файлами cookie. 
* Persistent URL Cache Functions - работа с офф-лайном и кешем.
Всего функций довольно много, около 80, но для средних приложений большинство из них не понадобится. Рассмотрим, что можно использовать из первой категории. Из всех функций наибольший практический интерес представляют следующие:
== InternetCheckConnection ==
// позволяет узнать, есть ли уже соединение с Internet.
// Синтаксис:
function InternetCheckConnection(lpszUrl: PAnsiChar;
 dwFlags: DWORD;
 dwReserved: DWORD): BOOL; stdcall;
Если нужно проверить, есть ли соединение по конкретному URL, то параметр lpszUrl должен содержать нужный URL; если интересует, есть ли соединение вообще, установите его в nil. DwFlags может иметь значение только FLAG_ICC_FORCE_CONNECTION. Он делает следующее: если первый параметр не nil, то происходит попытка пропинговать указанный хост. Если параметр lpszUrl установлен в nil и есть соединение с другим сервером, то пингуется этот хост.
Последнее значение, dwReserved, зарезервировано, и должно быть установлено в 0.
К сожалению, я не проверял эту функцию, когда писал статью... а жаль... вот что получается: константа FLAG_ICC_FORCE_CONNECTION вообще не описана в Дельфи. более того - ее нет ни в Microsoft Visual C++ 5 (!!!!), VBasic 5 тоже! едва нашел в C++ Builder 5.
Вот описание
 const FLAG_ICC_FORCE_CONNECTION $00000001
Но! Даже с описанной константой ничего не работает так, как надо! Вот пример:
procedure TForm1.Button1Click(Sender: TObject);
var
 h: Boolean;
begin
 h: = wininet.InternetCheckConnection(nil, $00000001, 0);
 if h then
 Label1.Caption := 'Соединение с сервером 127.0.0.1 установлено.'
 else
 Label1.Caption := 'Соединения с сервером 127.0.0.1 нет.';
end;
Запускаю вместе с сервером - вроде должно пинговать его. Но первый раз функция показывает что соединение есть несмотря на то, стоит ли сервер, или нет. Потом все время выдает false. Если кто из читателей может пролить некоторый свет на проблему этой функции, очень прошу написать мне. Благодарю Суркиза Максима, который впервые обратил мое внимание на проблему.
== InternetOpen ==
Функция возвращает значение TRUE, если компьютер соединен с Internet, и FALSE - в противном случае. Для получения более подробной информации о причинах неудачного выполнения функции вызовите GetLastError, которая возвратит код ошибки. Например, значение E RROR_NOT_CONNECTED информирует нас, что соединение не может быть установлено или компьютер работает в off-line.
Далее рассмотрим одну из самых важных функций. Ее вы будете использовать всякий раз, когда нужно получить доступ к любому из серверов – будь то HTTP, FTP или Gopher. Речь идет о InternetOpen.
function InternetOpen(lpszAgent: PChar;
 dwAccessType: DWORD;
 lpszProxy, lpszProxyBypass: PChar;
 dwFlags: DWORD): HINTERNET; stdcall;
Параметры:
* lpszAgent - строка символов, которая передается серверу и идентифицирует программное обеспечение, пославшее запрос.
* dwAccessType - задает необходимые параметры доступа. Принимает следующие значения:
** INTERNET_OPEN_TYPE_DIRECT - обрабатывает все имена хостов локально. 
** INTERNET_OPEN_TYPE_PRECONFIG - берет установки из реестра. 
** INTERNET_OPEN_TYPE_PRECONFIG_WITH_NO_AUTOPROXY - берет установки из реестра и предотвращает запуск Jscript или Internet Setup (INS) файлов. 
** INTERNET_OPEN_TYPE_PROXY - использование прокси-сервера. В случае неудачи использует INTERNET_OPEN_TYPE_DIRECT. LpszProxy - адрес прокси-сервера. Игнорируется только если параметр dwAccessType отличается от INTERNET_OPEN_TYPE_PROXY.
* lpszProxyBypass - список имен или IP- адресов, соединяться с которыми нужно в обход прокси-сервера. В списке допускаются шаблоны. Так же, как и предыдущий параметр, не может содержать пустой строки. Если dwAccessType отличен от INTERNET_OPEN_TYPE_PROXY, то значения игнорируются, и параметр можно установить в nil.
* dwFlags - задает параметры, влияющие на поведение Internet- функций . Возможно применение комбинации из следующих разрешенных значений: INTERNET_FLAG_ASYNC, INTERNET_FLAG_FROM_CACHE, INTERNET_FLAG_OFFLINE.
Функция инициализирует использование Internet-функций Win32 API. В принципе, ваше приложение может неоднократно вызывать эту функцию, например, для доступа к различным сервисам, но обычно ее достаточно вызвать один раз. При последующих вызовах других функций возвращаемый указатель HINTERNET должен передаваться им первым. Таким образом, можно дважды вызвать InternetOpen, и, имея два разных указателя HINTERNET, работать с HTTP и FTP параллельно. В случае неудачи, она возвращает nil, и для более детального анализа следует вызвать GetLastError.
Непосредственно с этой функцией связанна и еще одна, не менее важная: InternetCloseHandle.
== InternetCloseHandle ==
function InternetCloseHandle(hInet: HINTERNET): BOOL; stdcall;
Как единственный параметр, она принимает указатель, полученный функцией InternetOpen, и закрывает указанное соединение. В случае успешного закрытия сессии возвращается TRUE, иначе - FALSE. Если поток блокирует возможность вызова Wininet.dll, то другой пот ок приложения может вызвать функцию с тем же указателем, чтобы отменить последнюю команду и разблокировать поток.
Мы уже установили соединение и знаем, как его закрыть. Теперь нам нужно соединиться с конкретным сервером, используя нужный протокол. В этом нам помогут следующие функции:
== InternetConnect ==
function InternetConnect (hInet: HINTERNET;
 lpszServerName: PChar;
 nServerPort: INTERNET_PORT;
 lpszUsername: PChar;
 lpszPassword: PChar;
 dwService: DWORD;
 dwFlags: DWORD;
 dwContext: DWORD): HINTERNET; stdcall;
Функция открывает сессию с указанным сервером, используя протокол FTP, HTTP, Gopher.
Параметры:
* hInet - указатель, полученный после вызова InternetOpen.
* lpszServerName - имя сервера, с которым нужно установить соединение. Может быть как именем хоста - domain.com.ua, так и IP- адресом - 134.123.44.66.
* nServerPort - указывает на TCP/IP порт, с которым нужно соединиться. Для задания стандартных портов служат константы:
** NTERNET_DEFAULT_FTP_PORT (port 21),
** INTERNET_DEFAULT_GOPHER_PORT (port 70),
** INTERNET_DEFAULT_HTTP_PORT (port 80),
** INTERNET_DEFAULT_HTTPS_ PORT (port 443),
** INTERNET_DEFAULT_SOCKS_PORT (port 1080),
** INTERNET_INVALID_PORT_NUMBER - порт по умолчанию для сервиса, описанного в dwService.
:: Стандартные порты для различных сервисов находятся в файле SERVICES в директории Windows.
* lpszUsername - имя пользователя, желающего установить соединение. Если установлено в nil , то будет использовано имя по умолчанию, но для HTTP это вызовет исключение.
* lpszPassword - пароль пользователя для доступа к серверу. Если оба значения установить в nil, то будут использованы параметры по умолчанию.
* dwService - задает сервис, который требуется от сервера. Может принимать значения INTERNET_SERVICE_FTP, INTERNET_SERVICE_GOPHER, INTERNET_SERVICE_HTTP.
* dwFlags - Задает специфические параметры для соединения. Например, если DwService установлен в INTERNET_SERVICE_FTP, то можно установить в INTERNET_FLAG_PASSIVE для использования пассивного режима.
Функция возвращает указатель на установленную сессию или nil в случае невозможности ее установки.
Итак, мы имеем связь с сервером, нужный нам порт открыт. Теперь следует открыть соответствующй файл. Для этого определена функция InternetOpenUrl. Она принимает полный URL файла и возвращает указатель на него. Кстати, перед ее использованием не нужно вызывать InternetConnect.
== InternetOpenUrl ==
function InternetOpenUrl(hInet: HINTERNET;
 lpszUrl: PChar;
 lpszHeaders: PChar;
 dwHeadersLength: DWORD;
 dwFlags: DWORD;
 dwContext: DWORD): HINTERNET; stdcall;
Параметры:
* hInet - указатель, полученный после вызова InternetOpen. 
* lpszUrl - URL , до которого нужно получить доступ. Обязательно должен начинаться с указания протокола, по которому будет происходить соединение. Поддерживаются следующие протоколы - ftp:, gopher:, http:, https:. 
* lpszHeaders - содержит заголовок HTTP запроса. 
* dwHeadersLength - длина заголовка. Если заголовок nil, то можно установить значение -1, и длина будет вычислена автоматически. 
* dwFlags - флаг, задающий дополнительные параметры перед выполнением функции. Вот некоторые его значения:
** INTERNET_ FLAG_EXISTING_CONNECT,
** INTERNET_FLAG_HYPERLINK,
** INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP,
** INTERNET_FLAG_NO_AUTO_REDIRECT,
** INTERNET_FLAG_NO_CACHE_WRITE,
** INTERNET_FLAG_NO_COOKIES.
Возвращается значение TRUE, если соединение успешно, или FELSE - в противном случае. Теперь можно спокойно считывать нужный файл функцией InternetReadFile.
== InternetReadFile ==
function InternetReadFile(hFile: HINTERNET;
 lpBuffer: Pointer;
 dwNumberOfBytesToRead: DWORD;
 var lpdwNumberOfBytesRead: DWORD): BOOL; stdcall;
Параметры:
* hFile - указатель на файл, полученный после вызова функции InternetOpenUrl. 
* lpBuffer - указатель на буфер, куда будут заноситься данные. 
* dwNumberOfBytesToRead - число байт, которое нужно причитать. 
* lpdwNumberOfBytesRead - содержит количество прочитанных байтов. Устанавливается в 0 перед проверкой ошибок.
Функция позволяет считывать данные, используя указатель, полученный в результате вызова InternetOpenUrl, FtpOpenFile, GopherOpenFile, или HttpOpenRequest. Так же, как и все остальные функции, возвращает TRUE или FALSE. После завершения работы функции нужно освободить указатель Hfile, вызвав InternetCloseHandle(hUrlFile) .
Вот, в принципе, и все об самых основных функциях. Для простейшего приложения можно определить примерно такой упрощенный алгоритм использования Internet- функций Win32 API взамен стандартным компонентов.
hSession := InternetOpen(); // открывает сессию.
hConnect := InternetConnect(); // устанавливает соединение.
hHttpFile := HttpOpenRequest();
== HttpSendRequest ==
HttpOpenRequest и HttpSendRequest используются вместе для получения доступа к файлу по HTTP-протоколу. Вызов HttpOpenRequest создает указатель и определяет необходимые параметры, а HttpSendRequest отсылает запрос HTTP серверу, используя эти параметры.
function HttpOpenRequest(hConnect: HINTERNET;
 lpszVerb: PChar;
 lpszObjectName: PChar;
 lpszVersion: PChar;
 lpszReferrer: PChar;
 lplpszAcceptTypes: PLPSTR;
 dwFlags: DWORD;
 dwContext: DWORD): HINTERNET; stdcall;
function HttpSendRequest(hRequest: HINTERNET;
 lpszHeaders: PChar;
 dwHeadersLength: DWORD;
 lpOptional: Pointer;
 dwOptionalLength: DWORD): BOOL; stdcall;
== HttpQueryInfo ==
HttpQueryInfo - используется для получения информации о файле. Вызывается после вызова HttpOpenRequest.
function HttpQueryInfo(hRequest: HINTERNET;
 dwInfoLevel: DWORD;
 lpvBuffer: Pointer;
 var lpdwBufferLength: DWORD;
 var lpdwReserved: DWORD): BOOL; stdcall;
* InternetReadFile - считывает нужный файл. 
* InternetCloseHandle(hHttpFile) - освобождает указатель на файл. 
* InternetCloseHandle(hConnect) - освобождает указатель на соединение. 
* InternetCloseHandle(hSession) - освобождает указатель на сессию.
Объем статьи не позволяет подробно рассмотреть все множество функций, предоставляемых Win32 API. Это введение показало вам только вершину айсберга, а дальше дело за вами - внутренний мир WinAPI очень богат и большинство из того, что обеспечивают сторонние компоненты, можно отыскать в его недрах. Удачи вам!
Source: http://delphiworld.narod.ru
ID: 03581
Category:Связь
Category:Web
Category:Статьи
Category:Справочники