Форум программистов
 

Восстановите пароль или Зарегистрируйтесь на форуме, о проблемах и с заказом рекламы пишите сюда - alarforum@yandex.ru, проверяйте папку спам!

Вернуться   Форум программистов > Delphi программирование > Общие вопросы Delphi
Регистрация

Восстановить пароль
Повторная активизация e-mail

Купить рекламу на форуме - 42 тыс руб за месяц

Ответ
 
Опции темы Поиск в этой теме
Старый 03.10.2020, 09:46   #1
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
Вопрос HttpsGet глючит в режиме Release

Здравствуйте.
Поймал странный глюк.
Код:
function Https_Get(sURL : string; var RecvText : AnsiString) : Integer;
const
  sUserAgent = 'Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101';
var
  hInet, hConnect, hRequest : HINTERNET;
  lpdwBufferLength: DWORD;
  lpdwReserved    : DWORD;
  dwBytesRead     : DWORD;
  lpdwNumberOfBytesAvailable : DWORD;
  _pos : Cardinal;
  ServerName, Resource, Header : string;
  ResponseText : AnsiString;
  p : PChar;
begin
  Result := -1;
  _pos := Pos('://', sURL);
  Header := Copy(sURL, 1, _pos + 2);
  Delete(sURL, 1, _pos + 2);
  _pos := Pos('/', sURL);
  ServerName := Copy(sURL, 1, _pos - 1);
  Delete(sURL, 1, _pos - 1);
  Resource := sURL;
  hInet := InternetOpen(PChar(sUserAgent), INTERNET_OPEN_TYPE_PRECONFIG,
                        nil, nil, 0);
  if Assigned(hInet) then
  begin
    hConnect := InternetConnect(hInet, PChar(ServerName),
                  INTERNET_DEFAULT_HTTPS_PORT, nil, nil,
                  INTERNET_SERVICE_HTTP, 0, 0);
    if Assigned(hConnect) then
    begin
      p := PChar('Accept: ' + TWITCH_V5_ACCEPT + #0);
      hRequest := HttpOpenRequest(hConnect, 'GET', PChar(Resource),
                  HTTP_VERSION, '', @p, INTERNET_FLAG_SECURE, 0);
      if Assigned(hRequest) then
      begin
        Header := 'Client-ID: ' + TWITCH_CLIENT_ID;
        if not HttpSendRequest(hRequest, PChar(Header), Length(Header), nil, 0) then
        begin
          InternetCloseHandle(hRequest);
          InternetCloseHandle(hConnect);
          InternetCloseHandle(hInet);
          Result := -1;
          Exit;
        end;

        lpdwBufferLength := SizeOf(dword);
        lpdwReserved := 0;
        if not HttpQueryInfo(hRequest,
                             HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER,
                             @result, lpdwBufferLength, lpdwReserved) then
        begin
          InternetCloseHandle(hRequest);
          InternetCloseHandle(hConnect);
          InternetCloseHandle(hInet);
          Result := -1;
          Exit;
        end;

        if Result = 200 then
        begin
          _Pos := 1;
          ResponseText := '';
          repeat
            if not InternetQueryDataAvailable(hRequest,
                                lpdwNumberOfBytesAvailable, 0, 0) then
            begin
              InternetCloseHandle(hRequest);
              InternetCloseHandle(hConnect);
              InternetCloseHandle(hInet);
              Result := -1;
              Exit;
            end;

            SetLength(ResponseText, Length(ResponseText) +
                             Integer(lpdwNumberOfBytesAvailable));
            if not InternetReadFile(hRequest, @ResponseText[_pos],
                            lpdwNumberOfBytesAvailable, dwBytesRead) then
            begin
              InternetCloseHandle(hRequest);
              InternetCloseHandle(hConnect);
              InternetCloseHandle(hInet);
              Result := -1;
              Exit;
            end;
            Inc(_Pos, dwBytesRead);
          until dwBytesRead = 0;
          RecvText := ResponseText;
        end;
        InternetCloseHandle(hRequest);
      end;
      InternetCloseHandle(hConnect);
    end;
    InternetCloseHandle(hInet);
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
begin
  t := 'https://api.twitch.tv/kraken/';
  if Https_Get(t, a) = 200 then
  ShowMessage(a);
end;
В режиме Debug работает (даже без дебуггера), а в Release выдаёт ошибку Access violation at address 74DB4C30 in module 'wininet.dll'. Read of address FFFFFFFF..
Но если закомментировать ShowMessage или ввести дополнительную переменную
Код:
procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
  e : Integer;
begin
  t := 'https://api.twitch.tv/kraken/';
  if Https_Get(t, a) = 200 then
  ShowMessage(a);
  Caption := IntToStr(e);
end;
то начинает работать.
Вот видео: https://www.youtube.com/watch?v=mp8RtnXmI4I
Там показано всё, кроме комментирования ShowMessage. Я забыл это записать.
BLACK_RAIN вне форума Ответить с цитированием
Старый 03.10.2020, 13:49   #2
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Частично разобрался.
Если попытаться вывести в консоль содержимое переменной ResponseText
Код:

          until dwBytesRead = 0;
          write(responseText);
то в дебаге всё работает, а в релизе выводит пустоту. Как будто переменной вообще не присваивается значение.

update
Код:
        lpdwBufferLength := SizeOf(dword);
        lpdwReserved := 0;
        if not HttpQueryInfo(hRequest,
                             HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER,
                             @result, lpdwBufferLength, lpdwReserved) then
        begin
          InternetCloseHandle(hRequest);
          InternetCloseHandle(hConnect);
          InternetCloseHandle(hInet);
          Result := -1;
          Exit;
        end;

        Writeln('HttpQueryInfo '+ IntToStr(Result));
эта фигня в дебаге выдаёт ошибку 200 (успех), а в релизе выдаёт 400. Это вроде Bad Request.
При этом, ошибка Access violation at address 74DB4C30 in module 'wininet.dll'. Read of address FFFFFFFF. куда-то делась. Ее теперь нет.
А в другом проекте эта ошибка появляется при вызове
Код:
      hRequest := HttpOpenRequest(hConnect, 'GET', PChar(Resource),
                  HTTP_VERSION, '', @p, INTERNET_FLAG_SECURE, 0)
Только там адрес другой.
Почему так?

Последний раз редактировалось BLACK_RAIN; 03.10.2020 в 15:36.
BLACK_RAIN вне форума Ответить с цитированием
Старый 04.10.2020, 12:18   #3
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Провёл несколько тестов. Во всех использовалась такая функция:
Код:
function Https_Get(sURL : string; var RecvText : AnsiString) : Integer;
const
  sUserAgent = 'Mozilla/5.001 (windows; U; NT4.0; en-US; rv:1.0) Gecko/25250101';
var
  hInet, hConnect, hRequest : HINTERNET;
  lpdwBufferLength, lpdwReserved, dwBytesRead, lpdwNumberOfBytesAvailable : DWORD;
  _pos : Cardinal;
  ServerName, Resource, Header : string;
  ResponseText : AnsiString;
  p : PChar;
begin
  Result := -1;
  _pos := Pos('://', sURL);
  Header := Copy(sURL, 1, _pos + 2);
  Delete(sURL, 1, _pos + 2);
  _pos := Pos('/', sURL);
  ServerName := Copy(sURL, 1, _pos - 1);
  Delete(sURL, 1, _pos - 1);
  Resource := sURL;

  hInet := InternetOpen(PChar(sUserAgent), INTERNET_OPEN_TYPE_PRECONFIG,
                        nil, nil, 0);
  if Assigned(hInet) then
  begin
    Writeln('InternetOpen');
    hConnect := InternetConnect(hInet, PChar(ServerName),
                  INTERNET_DEFAULT_HTTPS_PORT, nil, nil,
                  INTERNET_SERVICE_HTTP, 0, 0);
    if Assigned(hConnect) then
    begin
      Writeln('InternetConnect');
      p := PChar('Accept: ' + TWITCH_V5_ACCEPT + #0);
      hRequest := HttpOpenRequest(hConnect, 'GET', PChar(Resource),
                  HTTP_VERSION, nil, @p, INTERNET_FLAG_SECURE, 0);
      if Assigned(hRequest) then
      begin
        Writeln('HttpOpenRequest');
        Header := 'Client-ID: ' + TWITCH_CLIENT_ID;
        if not HttpSendRequest(hRequest, PChar(Header), Length(Header), nil, 0) then
        begin
          InternetCloseHandle(hRequest);
          InternetCloseHandle(hConnect);
          InternetCloseHandle(hInet);
          Result := -1;
          Exit;
        end;
        Writeln('HttpSendRequest');

        lpdwBufferLength := 256;
        lpdwReserved := 0;
        if not HttpQueryInfo(hRequest,
                             HTTP_QUERY_STATUS_CODE or HTTP_QUERY_FLAG_NUMBER,
                             @Result, lpdwBufferLength, lpdwReserved) then
        begin
          InternetCloseHandle(hRequest);
          InternetCloseHandle(hConnect);
          InternetCloseHandle(hInet);
          Result := -1;
          Exit;
        end;

        Writeln('HttpQueryInfo ' + IntToStr(Result));

        if Result = 200 then
        begin
          _Pos := 1;
          ResponseText := '';
          repeat
            if not InternetQueryDataAvailable(hRequest,
                                lpdwNumberOfBytesAvailable, 0, 0) then
            begin
              InternetCloseHandle(hRequest);
              InternetCloseHandle(hConnect);
              InternetCloseHandle(hInet);
              Result := -1;
              Exit;
            end;

            SetLength(ResponseText, Length(ResponseText) +
                             Integer(lpdwNumberOfBytesAvailable));
            if not InternetReadFile(hRequest, @ResponseText[_pos],
                            lpdwNumberOfBytesAvailable, dwBytesRead) then
            begin
              InternetCloseHandle(hRequest);
              InternetCloseHandle(hConnect);
              InternetCloseHandle(hInet);
              Result := -1;
              Exit;
            end;
            Inc(_Pos, dwBytesRead);
          until dwBytesRead = 0;
          Writeln(ResponseText);
          RecvText := ResponseText;
        end;
        InternetCloseHandle(hRequest);
      end;
      InternetCloseHandle(hConnect);
    end;
    InternetCloseHandle(hInet);
  end;
end;
----------------
Код:
procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
end;
выводит в консоль
Код:
InternetOpen
InternetConnect
HttpOpenRequest
HttpSendRequest
HttpQueryInfo 400
-------------------
Код:
procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
  Caption := t; //или Caption := a;
end;
выводит в консоль
Код:
InternetOpen
InternetConnect
и появляется сообщение с ошибкой Access violation at address 74DB4C30 in module 'wininet.dll'. Read of address FFFFFFFF..
Но если ввести дополнительную переменную:
Код:
procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
  e : Integer;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
  Caption := a;
  Caption := IntToStr(e);
end;
//или даже просто вот так
procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
  e : Integer;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
  Caption := a;
  IntToStr(e);
end;
или даже если не вводить никакую переменную, а просто делать IntToStr(любое число), вот так:
Код:
procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
  Caption := a;
  IntToStr(666);
end;
то в консоль выводится следующее:
Код:
InternetOpen
InternetConnect
HttpOpenRequest
HttpSendRequest
HttpQueryInfo 200
{"_total":1,"users":[{"display_name":"Miramisu","_id":"80773859","name":"miramisu","type":"user","bio":"\"Been here 5 seconds and you have come off as one of the most arrogant and annoying streamers ive seen in my 10 years on this site jesus fck\" - Random Viewer","created_at":"2015-01-24T19:35:44.763258Z","updated_at":"2020-10-04T05:22:23.237296Z","logo":"https://static-cdn.jtvnw.net/jtv_user_pictures/0aed9998-c048-42ed-b2bc-3b861425b06e-profile_image-300x300.jpg"}]}
То есть, всё сработало корректно и мы получили ответ от сервера безо всяких ошибок.
Теперь обьясните, пожалуйста, каким образом вызов IntToStr() нейтрализует ошибку? Оно же вызывается уже после всех функций, приводящих к ошибке. Как это работает?
Теперь дальше. Если создать кнопку и в ее обработчике написать
Код:
procedure TForm1.Button1Click(Sender: TObject);
var
  t : string;
  a : AnsiString;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
  Caption := a;
end;
То всё нормально срабатывает. Без ошибок. Ответ от сервера приходит и у формы меняется заголовок. Почему здесь IntToStr() не нужен, а в FormCreate() нужен? А в режиме Debug он не нужен нигде. Как это работает?
Теперь еще дальше. Переходим в другой проект. Здесь есть дополнительный код и ошибки работают уже немного по-другому.
Если в FormCreate написать
Код:
procedure TForm1.FormCreate(Sender: TObject);
var
  t : string;
  a : AnsiString;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
end;
выводит в консоль
Код:
InternetOpen
InternetConnect
HttpOpenRequest
HttpSendRequest
HttpQueryInfo 400
Добавление IntToStr() не помогает, а иногда приводит к появлению сообщения об ошибке в wininet.dll.
Если делать это по кнопке, то всё работает. А в другом проекте, где кода еще больше, то при нажатии на кнопку сразу выдаётся ошибка wininet. В консоль ничего не выводится. То есть, он падает на вызове InternetOpen() или где-то еще раньше. Даже если этот дополнительный код закомментировать.
А если создать консольный проект (без окон и кнопок) и делать это в нём, то в консоль выводится это:
Код:

C:\Users\RAIN>C:\Users\RAIN\Documents\Embarcadero\Studio\Projects\Win32\Release\Project1.exe
InternetOpen
InternetConnect
Exception EAccessViolation in module wininet.dll at 002C4C30.
Access violation at address 74DB4C30 in module 'wininet.dll'. Read of address FFFFFFFF.
Итак. Как же всё-таки послать GET-запрос, чтобы это работало во всех проектах и режимах?

Последний раз редактировалось BLACK_RAIN; 04.10.2020 в 12:20.
BLACK_RAIN вне форума Ответить с цитированием
Старый 05.10.2020, 08:50   #4
BLACK_RAIN
Форумчанин
 
Регистрация: 13.02.2012
Сообщений: 867
По умолчанию

Вот еще тесты
Код:
procedure TForm1.btnSearchChannelClick(Sender: TObject);
var
  t : string;
  a : AnsiString;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, a);
end;
работает вроде нормально
----------
Код:
function TForm1.GetChannelVideosListJSON(aChannelName: string) : Integer;
var
  buf : AnsiString;
  t : string;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, buf);
end;

procedure TForm1.btnSearchChannelClick(Sender: TObject);
var
  ChannelName : string;
begin
  ChannelName := 'miramisu';
  GetChannelVideosListJSON(ChannelName);
end;
либо выводит в консоль это
Код:
InternetConnect
HttpOpenRequest
HttpSendRequest
HttpQueryInfo 400
и не выдаёт ошибку.
Либо выводит это
Код:
InternetConnect
HttpOpenRequest
и выдаёт ошибку Access violation at address 74DB4C30 in module 'wininet.dll'. Read of address 00000009.
------------
Код:
function TForm1.GetChannelVideosListJSON(aChannelName: string) : Integer;
var
  buf : AnsiString;
  t : string;
begin
  t := 'https://api.twitch.tv/kraken/users?login=miramisu';
  Https_Get(t, buf);
end;

procedure TForm1.btnSearchChannelClick(Sender: TObject);
begin
  GetChannelVideosListJSON('miramisu');
end;
всегда выводит это
Код:
InternetOpen
InternetConnect
HttpOpenRequest
HttpSendRequest
HttpQueryInfo 400
и ошибку не выдаёт.
В чём дело? Как правильно посылать GET-запросы? Как это загуглить?
BLACK_RAIN вне форума Ответить с цитированием
Ответ


Купить рекламу на форуме - 42 тыс руб за месяц



Похожие темы
Тема Автор Раздел Ответов Последнее сообщение
Не компилируется Release .exe-файл erslgoeirjh Visual C++ 4 25.09.2018 16:45
проблема с release Ulex Gamedev - cоздание игр: Unity, OpenGL, DirectX 0 04.03.2016 19:58
Как создать Release(Builder) sawer5 Помощь студентам 1 06.03.2010 17:18
Qt, запуск release igroman Qt и кроссплатформенное программирование С/С++ 1 11.12.2009 13:08
??? release компиляция Assassin Общие вопросы C/C++ 1 26.09.2008 02:57