【问题标题】:Check local port is free from Inno Setup without netstat usage在不使用 netstat 的情况下检查本地端口是否从 Inno Setup 中释放
【发布时间】:2019-04-23 19:20:34
【问题描述】:

我的问题之前在这里被问过,但我正在尝试为我的项目实施一个更简洁的解决方案。因此,正如标题所述,我正在创建一个复杂的服务器应用程序安装程序,它必须检查本地 IP 地址并选择一个开放端口,以便正确配置应用程序。使用 Inno Setup 5.6.1。

获取本地 IP 地址不是问题,this solution 帮助了我很多。然后到了端口检查,在这里我找到了以下三个选项:

  • 使用安装程序中的外部 DLL。实际上,以前的解决方案由一个 C++ DLL 组成,它导出了两个精确的便利函数,它们运行良好,安装程序使用它们,但有时,很少某些 Windows 版本上这个 DLL 没有'不想加​​载导致错误。这就是为什么Pascal Script 在这里更加混乱的原因。
  • 通过 cmd 启动 netstat 并获取输出。这仍然是一个选择,尽管我觉得这个解决方案像地狱一样古怪并且想避免它。详情请见other SO answer
  • 从 WinAPI 调用中获取信息。如果可能,看起来最好。

如上所述,获取 IP 地址可以通过简单的(好吧,不是真的,它是一个 Pascal 脚本)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 连接表),因此尝试获取任何连接地址都有助于调试目的。有关此尝试的更多信息:

  • RecordCount 的计算方式与我用作示例的代码相同,因为 obtained struct therethe nasty struct I need 非常相似。
  • i*20 + 8 是这样写的,因为 20 = sizeof(single record struct) 和 8 = 2 * sizeof(DWORD)。如您所见,本地 TCP 连接地址正在以 1 个 DWORD 的偏移量逐个字节地“解析”。

所以,一切都很有趣......它只是不工作=((

是的,我尝试逐个打印所有字节,以手动搜索所需数据并了解正确的偏移量。令我失望的是,没有发现任何看起来像 IP 和端口的东西,这些数字非常神秘。

我知道有时最简单的解决方案是最好的,而不是最聪明的,但如果有人能给我一个以正确方式烹饪这个 WinAPI 函数的密钥,我将不胜感激。

【问题讨论】:

  • 代码还能运行吗?在 pascal 中,局部变量未初始化,第一次调用可能没有 tablesize 0,因此函数可能会失败并出现完全不同的错误。
  • 确实如此。 TableSizevar 一起传递,因此第一次调用实际上将其设置为正确的值,这是一种常见的 winapi 技术来获得所需的缓冲区大小。在问题中提到的答案中,同样的技巧适用于其他函数调用。并且“调试消息”起作用了,这就是我知道数据混乱的方式。
  • 谢谢,我熟悉winapi,这是一个输入/输出参数。如果它恰好带有足够大的值,则函数不会因缓冲区不足而失败,而是会尝试写入一些无效的内存。你需要初始化它,虽然我知道这不是唯一的问题。
  • 正如@SertacAkyuz 所说,第一次调用应该使用初始化变量。您的常用 WinAPI 技术实际上很常见,但您告诉 API 调用您想要缓冲区大小的方式是在第一次调用时将 NULL(在 C 中,0)传递给函数。未初始化的局部变量包含零 (NULL) 的可能性很小,因此 API 调用会认为您正在传递一个缓冲区,这意味着您得到的东西与它返回时所认为的不同。 总是初始化变量而不是做假设。
  • 好的,很抱歉发明了“有意义的借口”而不是正确的变量初始化 =))。你们都完全正确。谢谢!这将是我要解决的第一件事。

标签: winapi inno-setup pascalscript


【解决方案1】:

你的魔法计算已关闭。

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

由于Buffer 是一个字节数组,上面只提取一个字节。但是本地端口号是 TCP 表中的DWORD。尽管您链接的文档指出:

本地计算机上 TCP 连接的网络字节顺序的本地端口号。

IP 端口号的最大长度为 16 位,因此只能使用低 16 位。高 16 位可能包含未初始化的数据。

所以我们需要两个字节。我们需要切换它们,注意上面的“网络字节顺序”。

您还忘记了表开头的 4 字节记录计数。所以本地端口号应该变成:

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

【讨论】:

  • 天哪,就是这样!非常感谢!乍一看,您的解释看起来像是一个可行的解决方案,只是我花了一些时间来解决奇怪的 pascalscript 行为——它没有进行正确的计算,我向整数添加了显式强制转换,比如portNumber := integer(Buffer[i*20 + 12]) * 256 + integer(Buffer[i*20 + 13]);。我想知道,为什么RecordCount 不需要相同的正确性,尽管我将两个变量都声明为整数。但无论如何,现在这些绝对是正确的端口号,就像在netstat -an 中一样。再次感谢您 =))
  • 不客气。记录计数不是网络字节顺序(我猜),但实际上是一个 DWORD。如果您期望超过 max(word) 连接(我不知道这是否可能),请更正它。 Buffer[3] * 65536 + Buffer[2] * 4096 + Buffer[1] * 256 + Buffer[0] 。在类型转换方面做得很好,我不知道这是必要的......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多