Проверьте, свободен ли локальный порт из Inno Setup без использования netstat - PullRequest
2 голосов
/ 23 апреля 2019

Мой вопрос задавался здесь ранее, но я пытаюсь реализовать более точное решение для моего проекта.Итак, как гласит заголовок, я создаю сложный установщик серверного приложения, который должен проверить локальный IP-адрес и выбрать открытый порт, чтобы приложение могло быть правильно настроено.Используется Inno Setup 5.6.1.

Получение локальных IP-адресов не было проблемой, это решение мне очень помогло.Затем дело дошло до проверки порта, и здесь я нашел следующие три варианта:

  • Использование внешней DLL из установщика .На самом деле, предыдущее решение состояло из C ++ DLL, которая экспортировала две точные вспомогательные функции, они отлично работали, установщик использовал их, но иногда, редко на некоторых версиях Windows, эта DLL нехочу загрузить, вызывая ошибку.Вот почему все больше возиться с Pascal Script здесь.
  • Запуск netstat через cmd и получение вывода .Это все еще вариант, хотя я чувствую, что это решение похоже на ад, и хотел бы избежать его.Подробности можно найти в другом ответе SO .
  • Получение информации от вызова WinAPI .Выглядит лучше всего, если это возможно.

Как уже упоминалось выше, получение IP-адреса может быть реализовано с помощью простых (ну, не совсем, это Pascal Script) вызовов WinAPI.Итак, я попытался проделать тот же трюк с портами, пытаясь вызвать GetTcpTable():

[Code]

const
  ERROR_INSUFFICIENT_BUFFER = 122;


function GetTcpTable(pTcpTable: Array of Byte; var pdwSize: Cardinal;
  bOrder: WordBool): DWORD;
external 'GetTcpTable@IpHlpApi.dll stdcall';

{ ------------------------ }

function CheckPortIsOpen(port: Integer): Boolean;
var
  TableSize  : Cardinal;
  Buffer : Array of Byte; { Alas, no pointers here }
  RecordCount : Integer;
  i, j : Integer;
  portNumber : Cardinal;
  IpAddr : String;
begin
  Result := True;
  TableSize := 0;

  if GetTcpTable(Buffer, TableSize, False) = ERROR_INSUFFICIENT_BUFFER then
    begin
      SetLength(Buffer, TableSize);
      if GetTcpTable(Buffer, TableSize, True) = 0 then
        begin
          { some magic calculation from GetIpAddrTable calling example }
          RecordCount := (Buffer[1] * 256) + Buffer[0];
          For i := 0 to RecordCount -1 do
            begin
              portNumber := Buffer[i*20 + 8]; { Should work! }

              { Debugging code here }
              if (i < 5) then begin
                IpAddr := '';
                For J := 0 to 3 do
                 begin
                   if J > 0 then
                    IpAddr := IpAddr + '_';
                    IpAddr := IpAddr + IntToStr(Buffer[I*20+ 4 + J]);
                 end;
                SuppressibleMsgBox(IpAddr, mbError, MB_OK, MB_OK);
              end;
              { ------ }

              if port = portNumber then
                  Result := False;
            end;
        end;
    end;
end;

Этот GetTcpTable также возвращает информацию об адресах и портах (таблица соединений TCPчтобы быть точным), поэтому попытка получить любой адрес подключения хороша для целей отладки.Подробнее об этой попытке:

Итак, все очень весело ... это просто не такработает = ((

И да, я пытался напечатать все байтов один за другим, чтобы вручную найти нужные данные и понять правильное смещение. К моему разочарованиюничего похожего на IP и порты не было найдено, цифры были довольно загадочными.

Я знаю, что иногда самое простое решение лучше, а не самое умное, но если бы кто-нибудь мог дать мне ключ, готовя эту функцию WinAPI вправильно, я был бы глубоко признателен.

1 Ответ

2 голосов
/ 24 апреля 2019

Ваши магические вычисления выключены.

portNumber := Buffer[i*20 + 8]; { Should work! }

Поскольку Buffer - это байтовый массив, вышеприведенный извлекает только один байт.Но номер локального порта - DWORD в таблице TCP.Хотя документация, которую вы связали, гласит:

Номер локального порта в сетевом порядке байтов для соединения TCP на локальном компьютере.

Максимальный размер номера порта IP составляет 16 бит, поэтому должны использоваться только младшие 16 бит.Старшие 16 бит могут содержать неинициализированные данные.

Итак, нам нужно два байта.И нам нужно их переключить, отметьте «сетевой порядок байтов» выше.

Вы также забыли учесть количество записей в 4 байта в начале таблицы.Таким образом, номер локального порта должен стать:

          portNumber := Buffer[i*20 + 12] * 256 +
                        Buffer[i*20 + 13];
...