Отменить HttpSendRequest в WinInet
Часть кода опустил для читабельности, но суть, думаю ясна. Вызываю загрузку страницы через WinInet, на форму вешаю кнопку отмены. InternetCloseHandle(FHttpRequest) не решает проблемы. Где-то читал, что отменять запросы можно только в асинхронном соединении, но при установки ключа INTERNET_FLAG_ASYNC у меня вообще не выгружает страницу...
FHttpSession:= InternetOpen(PChar(FClientName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, 0);
FHttpConnect:= InternetConnect(FHttpSession, PChar(hostname), Flags_connection, nil, nil, INTERNET_SERVICE_HTTP, 0, 0);
FHttpRequest:= HttpOpenRequest(FHttpConnect, PChar(FMethod), PChar(script), HTTP_VERSION, nil, nil, Flags_Request, 0);
HttpSendRequest(httpRequest, nil, 0, nil, 0); // Вот тут подвисает
InternetCloseHandle(FHttpRequest);
InternetCloseHandle(FHttpConnect);
InternetCloseHandle(FHttpSession);
В проекте 3 модуля: главный, модуль потока и что-то вроде обертки над wininet. На главной форме на кнопку вешаю такой обработчик:
procedure TFrmTesting.Button1Click(Sender: TObject);
begin
if Assigned(ThreadWinInetReq) then
begin
ThreadWinInetReq.StopThread;
ThreadWinInetReq.Terminate;
end;
end;
Событие StopThread:
procedure TThreadWinInetReq.StopThread;
begin
if Assigned(FClient) then
begin
FClient.Stop:= True;
FClient.CancelRequest;
end;
Terminate;
end;
Событие CancelRequest:
procedure TWinInet.CancelRequest;
begin
InternetCloseHandle(FHttpRequest);
InternetCloseHandle(FHttpConnect);
InternetCloseHandle(FHttpSession);
end;
Догадываюсь, что где-то что-то не так передаю...
Дополнение
Вот так работает, но иногда в асинхронном режиме выдает пустую страницу
unit UnitWinInet;
interface
uses
System.SysUtils, System.Types, WinInet, Winapi.Windows;
type
TWinInet = class
private
FHWND: THandle; // Хэндл вызывающего приложения
FClientName, // Имя клиента
FParam, // параметры запроса (которые после ?)
FMethod, // GET, POST и др.
FType_Access, // mime type и проч, если установлен в '', то используется по умолчанию
FLogin, // логин
FPass: string; // пароль
FPostData: boolean; //True - передача параметров запроса через post data, False - через строку запроса
FStop: boolean; // вспомогательная переменная отв. за остановку скачки
FHttpSession, FHttpConnect, FHttpRequest: HINTERNET;
FAsync: Boolean;
procedure SetMethod(AMethod: string);
public
property HWND: THandle read FHWND write FHWND;
property ClientName: string read FClientName write FClientName;
property Param: string read FParam write FParam;
property Method: string read FMethod write SetMethod;
property Type_Access: string read FType_Access write FType_Access;
property Login: string read FLogin write FLogin;
property Pass: string read FPass write FPass;
property PostData: boolean read FPostData write FPostData;
property Async: boolean read FAsync write FAsync;
function GetHTTP(AURL: string): AnsiString;
procedure CancelRequest;
constructor Create(AHWND: THandle);
end;
implementation
uses
Vcl.Dialogs, System.Classes, UnitTesting;
var
completed, BOK: boolean;
procedure TWinInet.CancelRequest;
begin
FStop:= True;
InternetCloseHandle(FHttpRequest);
InternetCloseHandle(FHttpConnect);
InternetCloseHandle(FHttpSession);
InternetSetOption(nil, INTERNET_OPTION_SETTINGS_CHANGED, nil, 0);
end;
constructor TWinInet.Create(AHWND: THandle);
begin
FHWND:= AHWND;
FClientName:= 'WinInet';
FMethod:= 'GET';
FType_Access:= 'Content-Type: application/x-www-form-urlenDELPHId' + #13#10 +
'Content-Length:' + IntToStr(length(FParam));
FPostData:= False;
FAsync:= True;
end;
procedure TWinInet.SetMethod(AMethod: string);
begin
FMethod:= UpperCase(AMethod);
end;
function TWinInet.GetHTTP(AURL: string): AnsiString;
function GetHostName(AUrl: string): string;
var
s: string;
begin // Имя хоста
if Pos('https://', AUrl) > 0 then
s:= 'https://'
else
if Pos('http://', AUrl) > 0 then
s:= 'http://'
else
s:= EmptyStr;
if s <> EmptyStr then
if Pos(s, AUrl) > 0 then
Delete(AUrl, 1, Length(s));
if Pos('/', AUrl) > 0 then
SetLength(AUrl, Pos('/', AUrl) - 1);
Result:= AUrl;
end;
function GetScriptName(AUrl, AHostname: string): string;
begin // URL после имени хоста
Result:= EmptyStr;
Delete(AUrl, 1, Pos(AHostname, AUrl) + Length(AHostname));
Result:= AUrl;
end;
procedure SetFlags(AUrl: string; out Flags_connection, Flags_Request: Cardinal);
begin // Определяем https или http
if Pos('https://', AUrl) > 0 then
begin
Flags_connection:= INTERNET_DEFAULT_HTTPS_PORT;
Flags_Request:= INTERNET_FLAG_RELOAD
or INTERNET_FLAG_NO_CACHE_WRITE
or INTERNET_FLAG_SECURE
or INTERNET_FLAG_IGNORE_CERT_CN_INVALID
or INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
//or INTERNET_FLAG_KEEP_CONNECTION
end else
begin
Flags_connection:= INTERNET_DEFAULT_HTTP_PORT;
Flags_Request:= INTERNET_FLAG_RELOAD
or INTERNET_FLAG_IGNORE_CERT_CN_INVALID
or INTERNET_FLAG_NO_CACHE_WRITE
or INTERNET_FLAG_PRAGMA_NOCACHE
or INTERNET_FLAG_KEEP_CONNECTION;
end;
end;
function GetResponseHeader(const hRequest: Pointer): string;
var
dwSize, Index: DWORD;
szBuff: array [0..1024] of Char;
begin // Возвращает заголовок ответа сервера в виде строк с CR/LF
Index:= 0;
dwSize:= SizeOf(szBuff);
HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, @szBuff, dwSize, Index);
Result:= PChar(@szBuff);
end;
function GetStatus(const hRequest: Pointer): DWORD;
var
dwSize, dwStatus, Index: DWORD;
begin // Возвращает заголовок ответа сервера в виде строк с CR/LF
// Возвращает код статуса HTTP из заголовка ответа
Index:= 0;
dwSize:= SizeOf(dwStatus);
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER, @dwStatus, dwSize, Index);
Result:= dwStatus;
end;
function AddSecurityFlags(httpReq: Pointer): Boolean;
var
dwSize, dwFlags: DWORD;
begin
Result:= False;
dwSize:= SizeOf(dwFlags); // Get the current security flags
if (InternetQueryOption(httpReq, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwSize)) then
begin // Add desired flags
dwFlags:= dwFlags
or SECURITY_FLAG_IGNORE_UNKNOWN_CA
or SECURITY_FLAG_IGNORE_CERT_CN_INVALID
or SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
or SECURITY_FLAG_IGNORE_REVOCATION;
Result:= (InternetSetOption(httpReq,
INTERNET_OPTION_SECURITY_FLAGS,
@dwFlags,
dwSize));
end
end;
function SendRequest(httpRequest: Pointer; AType_Access, AParam: string): boolean;
begin
Result:= False;
if (FStop = False) then
case FPostData of
False: Result:= HttpSendRequest(httpRequest, nil, 0, nil, 0);
True: Result:= HttpSendRequest(httpRequest, PChar(AType_Access), Length(AType_Access), PChar(AParam), Length(AParam));
end;
end;
procedure StatusCallback(hInet: HINTERNET; Context: DWORD_PTR; Status: DWORD; pInformation: Pointer;
InfoLength: DWORD); stdcall;
var
s: string;
begin
case Status of
INTERNET_STATUS_CLOSING_CONNECTION: s:= 'Closing the connection to the server';
INTERNET_STATUS_CONNECTED_TO_SERVER: s:= 'Successfully connected to the socket address: ';
INTERNET_STATUS_CONNECTING_TO_SERVER: s:= 'Connecting to the socket address';
INTERNET_STATUS_CONNECTION_CLOSED: s:= 'Successfully closed the connection to the server';
INTERNET_STATUS_CTL_RESPONSE_RECEIVED: s:= 'Not implemented';
INTERNET_STATUS_HANDLE_CLOSING: s:= 'This handle value has been terminated';
INTERNET_STATUS_HANDLE_CREATED: s:= 'InternetConnect has created the new handle';
INTERNET_STATUS_INTERMEDIATE_RESPONSE: s:=
'Received an intermediate (100 level) status code message from the server';
INTERNET_STATUS_NAME_RESOLVED: s:= 'Successfully found the IP address: ' + Format('%p',[pInformation]);
INTERNET_STATUS_PREFETCH: s:= 'Not implemented';
INTERNET_STATUS_RECEIVING_RESPONSE: s:= 'Waiting for the server to respond to a request ';
INTERNET_STATUS_REDIRECT:
begin
s:= 'HTTP request is about to automatically redirect the request ' +
Format('%p',[pInformation]);
completed:= True;
end;
INTERNET_STATUS_REQUEST_COMPLETE:
begin
s:= 'An asynchronous operation has been completed';
completed:= True;
Sleep(5000);
end;
INTERNET_STATUS_REQUEST_SENT: s:= 'Successfully sent the information request to the server: ' +
IntToStr(Integer(pInformation)) + ' Byte';
INTERNET_STATUS_RESOLVING_NAME: s:= 'Looking up the IP address: ' + Format('%p',[pInformation]);
INTERNET_STATUS_SENDING_REQUEST: s:= 'Sending the information request to the server.';
INTERNET_STATUS_RESPONSE_RECEIVED:
begin
s:= 'Successfully received a response from the server: ' + IntToStr(Integer(pInformation)) + ' Byte';
completed:= True;
end;
INTERNET_STATUS_STATE_CHANGE:
begin
s:= 'Moved between a secure (HTTPS) and a nonsecure (HTTP) site.';
case DWORD(pInformation) of
INTERNET_STATE_CONNECTED: s:= s + #13#10 + 'Connected state. Mutually exclusive with disconnected state.';
INTERNET_STATE_DISCONNECTED: s:= s + #13#10 +
'Disconnected state. No network connection could be established.';
INTERNET_STATE_DISCONNECTED_BY_USER: s:= s + #13#10 + 'Disconnected by user request.';
INTERNET_STATE_IDLE: s:= s + #13#10 + 'No network requests are being made by Windows Internet.';
INTERNET_STATE_BUSY: s:= s + #13#10 + 'Network requests are being made by Windows Internet.';
end;
end;
end;
//FrmTesting.MmHTML.Lines.Add(s);
end;
function WaitAndStop: Boolean;
begin // Цикл ожидания завершения запроса
Result:= False;
if FAsync then // применяется только в асинхронных вызовах
while True do
begin
Sleep(1000);
if FStop or completed then
begin
Result:= FStop;
break;
end;
end;
end;
var
bytes, b, pos: Cardinal;
hostname, script: string;
Flags_connection, Flags_Request: Cardinal;
DlgError, dwFlags: DWORD;
Status: PFNInternetStatusCallback;
iNetBuff : Internet_Buffers;
lpReadBuff : Array [0..255] of AnsiChar;
const
BufferSize = 1024*4;
begin
Result:= EmptyAnsiStr;
FStop:= False;
hostname:= GetHostName(AURL); // имя хоста
script:= GetScriptName(AURL, hostname); // скрипт
// установка доп. параметров
if not FPostData then // если передаем параметры через строку запроса, то
if FParam <> EmptyStr then // дополняем скрипт
if script[Length(script)] = '?' then
script:= script + FParam
else
script:= script + '?' + FParam;
try
SetFlags(AURL, Flags_connection, Flags_Request); // Устанавливаем флаги (http или https)
// Открываем сессию (инициализируем WinInet)
case FAsync of
False: dwFlags:= 0;
True: dwFlags:= INTERNET_FLAG_ASYNC;
end;
FHttpSession:= InternetOpen(PChar(FClientName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, dwFlags);
if FAsync then // Устанавливаем callback, если подключение асинхронное
begin
Status:= InternetSetStatusCallback(FHttpSession,INTERNET_STATUS_CALLBACK(@StatusCallback));
if NativeInt(Status) = INTERNET_INVALID_STATUS_CALLBACK then
begin
Result:= AnsiString('Callback function is not valid');
Exit;
end;
end;
if Assigned(FHttpSession) then // Проверяем хэндл
try // Открываем соединение
FHttpConnect:= InternetConnect(FHttpSession, PChar(hostname), Flags_connection, nil, nil, INTERNET_SERVICE_HTTP, 0, 1);
if Assigned(FHttpConnect) then // Проверяем хэндл
try // Открываем запрос (передаем остаток URL (скрипт GetScriptName) в ф-ю HttpOpenRequest под параметром lpszObjectName)
FHttpRequest:= HttpOpenRequest(FHttpConnect, PChar(FMethod), PChar(script), HTTP_VERSION, nil, nil, Flags_Request, 1);
if Assigned(FHttpRequest) then // Проверяем хэндл
try
AddSecurityFlags(FHttpRequest); // Добавляем флаги
completed:= False;
SendRequest(FHttpRequest, FType_Access, FParam); // Отправляем запрос
if WaitAndStop then // цикл ожидания вызова callback функции для асинхронных вызовов
begin
Result:= AnsiString('Остановлено');
Exit;
end;
if GetStatus(FHttpRequest) = HTTP_STATUS_DENIED then
begin // Если необходима авторизация
if FLogin <> EmptyStr then
begin
InternetSetOption(FHttpRequest, INTERNET_OPTION_USERNAME, PChar(FLogin), SizeOf(FLogin));
InternetSetOption(FHttpRequest, INTERNET_OPTION_PASSWORD, PChar(FPass), SizeOf(FPass));
end
else
begin
DlgError:= InternetErrorDlg(FHWND, FHttpRequest,
ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS
or FLAGS_ERROR_UI_FLAGS_GENERATE_DATA
//or FLAGS_ERROR_UI_SERIALIZE_DIALOGS
or FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
PPointer(nil)^ );
if DlgError = 0 then
begin
Result:= AnsiString('Доступ запрещен! Ввод учетных данных отменен.'
+ sLineBreak + SysErrorMessage(GetLastError));
Exit;
end;
end;
SendRequest(FHttpRequest, FType_Access, FParam);
if WaitAndStop then // цикл ожидания вызова callback функции для асинхронных вызовов
begin
Result:= AnsiString('Остановлено');
Exit;
end;
end;
case FAsync of
False: // Обычное чтение
if GetStatus(FHttpRequest) = HTTP_STATUS_OK then
begin
pos:= 1;
b:= 1;
while (b > 0) and (FStop = False) do
begin // Если количество данных 0 - генерируем исключение
if not InternetQueryDataAvailable(FHttpRequest, bytes, 0, 0) then
Result:= AnsiString('Сервер не вернул данные! (функция InternetQueryDataAvailable)' + sLineBreak + SysErrorMessage(GetLastError));
SetLength(Result, Cardinal(Length(Result)) + bytes);
// Получаем данные с сервера
InternetReadFile(FHttpRequest, @Result[Pos], bytes, b);
Inc(Pos, b);
end;
end else
Result:= AnsiString('ОШИБКА ' + IntToStr(GetStatus(FHttpRequest)) + sLineBreak + SysErrorMessage(GetLastError));
True: // Асинхронное чтение
begin
BOK:= True;
while BOK do
begin
FillMemory(@iNetBuff, Sizeof(Internet_Buffers),0);
inetBuff.dwStructSize := Sizeof(Internet_Buffers);
inetBuff.lpvBuffer := @lpReadBuff;
inetBuff.dwBufferLength := Sizeof(lpReadBuff)-1;
if not InternetReadFileEX(FHttpRequest, @inetBuff, 0, 1) then
if WaitAndStop then // цикл ожидания вызова callback функции для асинхронных вызовов
begin
Result:= AnsiString('Остановлено');
Exit;
end;
if FStop then
begin
Result:= AnsiString('Остановлено');
Break;
end;
lpReadBuff[inetBuff.dwBufferlength]:= #0;
Result:= Result + lpReadBuff;
if inetBuff.dwBufferLength = 0 then BOK := False;
end;
end;
end;
finally
InternetCloseHandle(FHttpRequest); // закрываем запрос
end else
Result:= AnsiString('Ошибка формирования запроса (функция HttpOpenRequest)' + sLineBreak + SysErrorMessage(GetLastError));
finally
InternetCloseHandle(FHttpConnect); // закрываем соединение
end else
Result:= AnsiString('Ошибка открытия сессии (функция InternetConnect)' + sLineBreak + SysErrorMessage(GetLastError));
finally
InternetCloseHandle(FHttpSession); // закрываем сессию
//InternetSetOption(nil, INTERNET_OPTION_SETTINGS_CHANGED, nil, 0);
if FStop then
Result:= AnsiString('Остановлено');
end else
Result:= AnsiString('Отсутствует подключение к сети (функция InternetOpen)' + sLineBreak + SysErrorMessage(GetLastError));
except
On E: Exception do
Result:= AnsiString('Ошибка! ' + E.ClassName + ': ' + E.Message);
end;
end;
end.
Ответы (1 шт):
Не дожидался утвердительного или отрицательного ответа сервера на аторизацию, поэтому пропускался этот шаг и выдавалась пустая страница. А дальше WinInet просто кэшировал авторизованное соединение и следующий запрос пропускал без проблем. Если запускать асинхронно, то завершить можно в любой момент. Вопрос решен, всем спасибо!
Приведу полный код, т.к. везде понемногу изменения:
{
Если логин с паролем не указывать (в конструкторе или после создания), то , при
необходимости будет выскакивать окно с запросом
}
unit UnitWinInet;
interface
uses
System.SysUtils, System.Types, WinInet, Winapi.Windows;
type
TWinInet = class
private
FHWND: THandle; // Хэндл вызывающего приложения
FClientName, // Имя клиента
FParam, // параметры запроса (которые после ?)
FMethod, // GET, POST и др.
FType_Access, // mime type и проч, если установлен в '', то используется по умолчанию
FLogin, // логин
FPass: string; // пароль
FPostData: boolean; //True - передача параметров запроса через post data, False - через строку запроса
FStop: boolean; // вспомогательная переменная отв. за остановку скачки
FHttpSession, FHttpConnect, FHttpRequest: HINTERNET;
FAsync: Boolean;
procedure SetMethod(AMethod: string);
public
property HWND: THandle read FHWND write FHWND;
property ClientName: string read FClientName write FClientName;
property Param: string read FParam write FParam;
property Method: string read FMethod write SetMethod;
property Type_Access: string read FType_Access write FType_Access;
property Login: string read FLogin write FLogin;
property Pass: string read FPass write FPass;
property PostData: boolean read FPostData write FPostData;
property Async: boolean read FAsync write FAsync;
function GetHTTP(AURL: string): AnsiString;
procedure CancelRequest;
constructor Create(AHWND: THandle);
end;
implementation
uses
Vcl.Dialogs, System.Classes, UnitTesting;
var
completed, BOK: boolean;
procedure TWinInet.CancelRequest;
begin
FStop:= True;
InternetCloseHandle(FHttpRequest);
InternetCloseHandle(FHttpConnect);
InternetCloseHandle(FHttpSession);
InternetSetOption(nil, INTERNET_OPTION_SETTINGS_CHANGED, nil, 0);
end;
constructor TWinInet.Create(AHWND: THandle);
begin
FHWND:= AHWND;
FClientName:= 'WinInet';
FMethod:= 'GET';
FType_Access:= 'Content-Type: application/x-www-form-urlenDELPHId' + #13#10 +
'Content-Length:' + IntToStr(length(FParam));
FPostData:= False;
FAsync:= True;
end;
procedure TWinInet.SetMethod(AMethod: string);
begin
FMethod:= UpperCase(AMethod);
end;
function TWinInet.GetHTTP(AURL: string): AnsiString;
function GetHostName(AUrl: string): string;
var
s: string;
begin // Имя хоста
if Pos('https://', AUrl) > 0 then
s:= 'https://'
else
if Pos('http://', AUrl) > 0 then
s:= 'http://'
else
s:= EmptyStr;
if s <> EmptyStr then
if Pos(s, AUrl) > 0 then
Delete(AUrl, 1, Length(s));
if Pos('/', AUrl) > 0 then
SetLength(AUrl, Pos('/', AUrl) - 1);
Result:= AUrl;
end;
function GetScriptName(AUrl, AHostname: string): string;
begin // URL после имени хоста
Result:= EmptyStr;
Delete(AUrl, 1, Pos(AHostname, AUrl) + Length(AHostname));
Result:= AUrl;
end;
procedure SetFlags(AUrl: string; out Flags_connection, Flags_Request: Cardinal);
begin // Определяем https или http
if Pos('https://', AUrl) > 0 then
begin
Flags_connection:= INTERNET_DEFAULT_HTTPS_PORT;
Flags_Request:= INTERNET_FLAG_RELOAD
or INTERNET_FLAG_NO_CACHE_WRITE
or INTERNET_FLAG_SECURE
or INTERNET_FLAG_IGNORE_CERT_CN_INVALID
or INTERNET_FLAG_IGNORE_CERT_DATE_INVALID
//or INTERNET_FLAG_KEEP_CONNECTION
end else
begin
Flags_connection:= INTERNET_DEFAULT_HTTP_PORT;
Flags_Request:= INTERNET_FLAG_RELOAD
or INTERNET_FLAG_IGNORE_CERT_CN_INVALID
or INTERNET_FLAG_NO_CACHE_WRITE
or INTERNET_FLAG_PRAGMA_NOCACHE
or INTERNET_FLAG_KEEP_CONNECTION;
end;
end;
function GetResponseHeader(const hRequest: Pointer): string;
var
dwSize, Index: DWORD;
szBuff: array [0..1024] of Char;
begin // Возвращает заголовок ответа сервера в виде строк с CR/LF
Index:= 0;
dwSize:= SizeOf(szBuff);
HttpQueryInfo(hRequest, HTTP_QUERY_RAW_HEADERS_CRLF, @szBuff, dwSize, Index);
Result:= PChar(@szBuff);
end;
function GetStatus(const hRequest: Pointer): DWORD;
var
dwSize, dwStatus, Index: DWORD;
begin // Возвращает заголовок ответа сервера в виде строк с CR/LF
// Возвращает код статуса HTTP из заголовка ответа
Index:= 0;
dwSize:= SizeOf(dwStatus);
HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER, @dwStatus, dwSize, Index);
Result:= dwStatus;
end;
function AddSecurityFlags(httpReq: Pointer): Boolean;
var
dwSize, dwFlags: DWORD;
begin
Result:= False;
dwSize:= SizeOf(dwFlags); // Get the current security flags
if (InternetQueryOption(httpReq, INTERNET_OPTION_SECURITY_FLAGS, @dwFlags, dwSize)) then
begin // Add desired flags
dwFlags:= dwFlags
or SECURITY_FLAG_IGNORE_UNKNOWN_CA
or SECURITY_FLAG_IGNORE_CERT_CN_INVALID
or SECURITY_FLAG_IGNORE_CERT_DATE_INVALID
or SECURITY_FLAG_IGNORE_REVOCATION;
Result:= (InternetSetOption(httpReq,
INTERNET_OPTION_SECURITY_FLAGS,
@dwFlags,
dwSize));
end
end;
function SendRequest(httpRequest: Pointer; AType_Access, AParam: string): boolean;
begin
Result:= False;
if (FStop = False) then
case FPostData of
False: Result:= HttpSendRequest(httpRequest, nil, 0, nil, 0);
True: Result:= HttpSendRequest(httpRequest, PChar(AType_Access), Length(AType_Access), PChar(AParam), Length(AParam));
end;
end;
procedure StatusCallback(hInet: HINTERNET; Context: DWORD_PTR; Status: DWORD; pInformation: Pointer;
InfoLength: DWORD); stdcall;
var
s: string;
begin
case Status of
INTERNET_STATUS_CLOSING_CONNECTION: s:= 'Closing the connection to the server';
INTERNET_STATUS_CONNECTED_TO_SERVER: s:= 'Successfully connected to the socket address: ';
INTERNET_STATUS_CONNECTING_TO_SERVER: s:= 'Connecting to the socket address';
INTERNET_STATUS_CONNECTION_CLOSED: s:= 'Successfully closed the connection to the server';
INTERNET_STATUS_CTL_RESPONSE_RECEIVED: s:= 'Not implemented';
INTERNET_STATUS_HANDLE_CLOSING: s:= 'This handle value has been terminated';
INTERNET_STATUS_HANDLE_CREATED: s:= 'InternetConnect has created the new handle';
INTERNET_STATUS_INTERMEDIATE_RESPONSE: s:=
'Received an intermediate (100 level) status code message from the server';
INTERNET_STATUS_NAME_RESOLVED: s:= 'Successfully found the IP address: ' + Format('%p',[pInformation]);
INTERNET_STATUS_PREFETCH: s:= 'Not implemented';
INTERNET_STATUS_RECEIVING_RESPONSE: s:= 'Waiting for the server to respond to a request ';
INTERNET_STATUS_REDIRECT:
begin
s:= 'HTTP request is about to automatically redirect the request ' +
Format('%p',[pInformation]);
completed:= True;
end;
INTERNET_STATUS_REQUEST_COMPLETE: s:= 'An asynchronous operation has been completed';
INTERNET_STATUS_REQUEST_SENT: s:= 'Successfully sent the information request to the server: ' +
IntToStr(Integer(pInformation)) + ' Byte';
INTERNET_STATUS_RESOLVING_NAME: s:= 'Looking up the IP address: ' + Format('%p',[pInformation]);
INTERNET_STATUS_SENDING_REQUEST: s:= 'Sending the information request to the server.';
INTERNET_STATUS_RESPONSE_RECEIVED:
begin
s:= 'Successfully received a response from the server: ' + IntToStr(Integer(pInformation)) + ' Byte';
completed:= True;
end;
INTERNET_STATUS_STATE_CHANGE:
begin
s:= 'Moved between a secure (HTTPS) and a nonsecure (HTTP) site.';
case DWORD(pInformation) of
INTERNET_STATE_CONNECTED: s:= s + #13#10 + 'Connected state. Mutually exclusive with disconnected state.';
INTERNET_STATE_DISCONNECTED: s:= s + #13#10 +
'Disconnected state. No network connection could be established.';
INTERNET_STATE_DISCONNECTED_BY_USER: s:= s + #13#10 + 'Disconnected by user request.';
INTERNET_STATE_IDLE: s:= s + #13#10 + 'No network requests are being made by Windows Internet.';
INTERNET_STATE_BUSY: s:= s + #13#10 + 'Network requests are being made by Windows Internet.';
end;
end;
end;
//FrmTesting.MmHTML.Lines.Add(s);
end;
function WaitAndStop(AReq: Pointer; AAuth: Boolean; out ASt: DWORD): Boolean;
begin // Цикл ожидания завершения запроса
Result:= False;
if FAsync then // применяется только в асинхронных вызовах
while True do
begin
Sleep(1000);
if FStop or completed then
begin
Result:= FStop;
if not AAuth then // обычный запрос
begin
ASt:= GetStatus(FHttpRequest);
break;
end else // авторизация
begin
ASt:= GetStatus(FHttpRequest);
if (ASt = HTTP_STATUS_OK) or (ASt = HTTP_STATUS_DENIED) then
break;
end;
end;
end;
end;
var
bytes, b, pos: Cardinal;
hostname, script: string;
Flags_connection, Flags_Request: Cardinal;
DlgError, dwFlags, Auth: DWORD;
Status: PFNInternetStatusCallback;
iNetBuff : Internet_Buffers;
lpReadBuff : Array [0..255] of AnsiChar;
const
BufferSize = 1024*4;
begin
Result:= EmptyAnsiStr;
FStop:= False;
hostname:= GetHostName(AURL); // имя хоста
script:= GetScriptName(AURL, hostname); // скрипт
// установка доп. параметров
if not FPostData then // если передаем параметры через строку запроса, то
if FParam <> EmptyStr then // дополняем скрипт
if script[Length(script)] = '?' then
script:= script + FParam
else
script:= script + '?' + FParam;
try
SetFlags(AURL, Flags_connection, Flags_Request); // Устанавливаем флаги (http или https)
// Открываем сессию (инициализируем WinInet)
case FAsync of
False: dwFlags:= 0;
True: dwFlags:= INTERNET_FLAG_ASYNC;
end;
FHttpSession:= InternetOpen(PChar(FClientName), INTERNET_OPEN_TYPE_PRECONFIG, nil, nil, dwFlags);
if FAsync then // Устанавливаем callback, если подключение асинхронное
begin
Status:= InternetSetStatusCallback(FHttpSession,INTERNET_STATUS_CALLBACK(@StatusCallback));
if NativeInt(Status) = INTERNET_INVALID_STATUS_CALLBACK then
begin
Result:= AnsiString('Callback function is not valid');
Exit;
end;
end;
if Assigned(FHttpSession) then // Проверяем хэндл
try // Открываем соединение
FHttpConnect:= InternetConnect(FHttpSession, PChar(hostname), Flags_connection, nil, nil, INTERNET_SERVICE_HTTP, 0, 1);
if Assigned(FHttpConnect) then // Проверяем хэндл
try // Открываем запрос (передаем остаток URL (скрипт GetScriptName) в ф-ю HttpOpenRequest под параметром lpszObjectName)
FHttpRequest:= HttpOpenRequest(FHttpConnect, PChar(FMethod), PChar(script), HTTP_VERSION, nil, nil, Flags_Request, 1);
if Assigned(FHttpRequest) then // Проверяем хэндл
try
AddSecurityFlags(FHttpRequest); // Добавляем флаги
completed:= False;
SendRequest(FHttpRequest, FType_Access, FParam); // Отправляем запрос
if WaitAndStop(FHttpRequest, False, Auth) then // цикл ожидания вызова callback функции для асинхронных вызовов
begin
Result:= AnsiString('Остановлено');
Exit;
end;
if GetStatus(FHttpRequest) = HTTP_STATUS_DENIED then
begin // Если необходима авторизация
if FLogin <> EmptyStr then
begin
InternetSetOption(FHttpRequest, INTERNET_OPTION_USERNAME, PChar(FLogin), SizeOf(FLogin));
InternetSetOption(FHttpRequest, INTERNET_OPTION_PASSWORD, PChar(FPass), SizeOf(FPass));
end
else
begin
DlgError:= InternetErrorDlg(FHWND, FHttpRequest,
ERROR_INTERNET_CLIENT_AUTH_CERT_NEEDED,
FLAGS_ERROR_UI_FILTER_FOR_ERRORS
or FLAGS_ERROR_UI_FLAGS_GENERATE_DATA
//or FLAGS_ERROR_UI_SERIALIZE_DIALOGS
or FLAGS_ERROR_UI_FLAGS_CHANGE_OPTIONS,
PPointer(nil)^ );
if DlgError = 0 then
begin
Result:= AnsiString('Доступ запрещен! Ввод учетных данных отменен.'
+ sLineBreak + SysErrorMessage(GetLastError));
Exit;
end;
end;
SendRequest(FHttpRequest, FType_Access, FParam);
if WaitAndStop(FHttpRequest, True, Auth) then // цикл ожидания вызова callback функции для асинхронных вызовов
begin
Result:= AnsiString('Остановлено');
Exit;
end;
end;
case FAsync of
False: // Обычное чтение
if GetStatus(FHttpRequest) = HTTP_STATUS_OK then
begin
pos:= 1;
b:= 1;
while (b > 0) and (FStop = False) do
begin // Если количество данных 0 - генерируем исключение
if not InternetQueryDataAvailable(FHttpRequest, bytes, 0, 0) then
Result:= AnsiString('Сервер не вернул данные! (функция InternetQueryDataAvailable)' + sLineBreak + SysErrorMessage(GetLastError));
SetLength(Result, Cardinal(Length(Result)) + bytes);
// Получаем данные с сервера
InternetReadFile(FHttpRequest, @Result[Pos], bytes, b);
Inc(Pos, b);
end;
end else
Result:= AnsiString('ОШИБКА ' + IntToStr(GetStatus(FHttpRequest)) + sLineBreak + SysErrorMessage(GetLastError));
True: // Асинхронное чтение
if (Auth = HTTP_STATUS_OK) or (Auth = HTTP_STATUS_NOT_FOUND) then
begin
BOK:= True;
while BOK do
begin
FillMemory(@iNetBuff, Sizeof(Internet_Buffers),0);
inetBuff.dwStructSize := Sizeof(Internet_Buffers);
inetBuff.lpvBuffer := @lpReadBuff;
inetBuff.dwBufferLength := Sizeof(lpReadBuff)-1;
if not InternetReadFileEX(FHttpRequest, @inetBuff, 0, 1) then
if WaitAndStop(FHttpRequest, False, Auth) then // цикл ожидания вызова callback функции для асинхронных вызовов
begin
Result:= AnsiString('Остановлено');
Exit;
end;
if FStop then
begin
Result:= AnsiString('Остановлено');
Break;
end;
lpReadBuff[inetBuff.dwBufferlength]:= #0;
Result:= Result + lpReadBuff;
if inetBuff.dwBufferLength = 0 then BOK := False;
end;
end else
if Auth = HTTP_STATUS_DENIED then
Result:= AnsiString('Доступ запрещен! Неверные учетные данные')
else
Result:= AnsiString('ОШИБКА ' + IntToStr(GetStatus(FHttpRequest)) + sLineBreak + SysErrorMessage(GetLastError));
end;
finally
InternetCloseHandle(FHttpRequest); // закрываем запрос
end else
Result:= AnsiString('Ошибка формирования запроса (функция HttpOpenRequest)' + sLineBreak + SysErrorMessage(GetLastError));
finally
InternetCloseHandle(FHttpConnect); // закрываем соединение
end else
Result:= AnsiString('Ошибка открытия сессии (функция InternetConnect)' + sLineBreak + SysErrorMessage(GetLastError));
finally
InternetCloseHandle(FHttpSession); // закрываем сессию
//InternetSetOption(nil, INTERNET_OPTION_SETTINGS_CHANGED, nil, 0);
if FStop then
Result:= AnsiString('Остановлено');
end else
Result:= AnsiString('Отсутствует подключение к сети (функция InternetOpen)' + sLineBreak + SysErrorMessage(GetLastError));
except
On E: Exception do
Result:= AnsiString('Ошибка! ' + E.ClassName + ': ' + E.Message);
end;
end;
end.