在没有使用 netstat 的情况下检查本地端口是否不受 Inno Setup 的影响
Check local port is free from Inno Setup without netstat usage
我的问题之前曾在这里被问过,但我正在尝试为我的项目实施一个更简洁的解决方案。因此,正如标题所述,我正在创建一个复杂的服务器应用程序安装程序,它必须检查本地 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
还 returns 有关地址和端口的信息(确切地说是 TCP 连接的 table),因此尝试获取任何连接地址都有助于调试。有关此尝试的更多信息:
RecordCount
的计算方式与我用作示例的代码中的计算方式相同,因为 obtained struct there is very similar to the nasty struct I need.
- 那
i*20 + 8
是这样写的,因为20 = sizeof(single record struct)和8 = 2 * sizeof(DWORD)
。如您所见,本地 TCP 连接地址 "parsed" one-by-one-byte 偏移量为 1 DWORD。
所以,一切都很有趣...只是行不通 =((
是的,我尝试打印 所有 个字节 one-by-one,以手动搜索所需数据并了解正确的偏移量。令我失望的是,没有找到任何看起来像 IP 和端口的东西,这些数字非常神秘。
我知道有时最简单的解决方案是最好的,而不是最聪明的,但如果有人能给我一把钥匙,以正确的方式使用这个 WinAPI 函数,我将不胜感激。
你的魔法计算失败了。
portNumber := Buffer[i*20 + 8]; { Should work! }
因为Buffer
是一个字节数组,上面只提取了一个字节。但是本地端口号在TCPtable中是一个DWORD
。尽管您链接的文档指出:
The local port number in network byte order for the TCP connection on the local computer.
The maximum size of an IP port number is 16 bits, so only the lower 16 bits should be used. The upper 16 bits may contain uninitialized data.
所以我们需要两个字节。我们需要切换它们,注意上面的“网络字节顺序”。
您还忘记考虑 table 开头的 4 字节记录数。所以本地端口号应该变成:
portNumber := Buffer[i*20 + 12] * 256 +
Buffer[i*20 + 13];
我的问题之前曾在这里被问过,但我正在尝试为我的项目实施一个更简洁的解决方案。因此,正如标题所述,我正在创建一个复杂的服务器应用程序安装程序,它必须检查本地 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
还 returns 有关地址和端口的信息(确切地说是 TCP 连接的 table),因此尝试获取任何连接地址都有助于调试。有关此尝试的更多信息:
RecordCount
的计算方式与我用作示例的代码中的计算方式相同,因为 obtained struct there is very similar to the nasty struct I need.- 那
i*20 + 8
是这样写的,因为20 = sizeof(single record struct)和8 = 2 * sizeof(DWORD)
。如您所见,本地 TCP 连接地址 "parsed" one-by-one-byte 偏移量为 1 DWORD。
所以,一切都很有趣...只是行不通 =((
是的,我尝试打印 所有 个字节 one-by-one,以手动搜索所需数据并了解正确的偏移量。令我失望的是,没有找到任何看起来像 IP 和端口的东西,这些数字非常神秘。
我知道有时最简单的解决方案是最好的,而不是最聪明的,但如果有人能给我一把钥匙,以正确的方式使用这个 WinAPI 函数,我将不胜感激。
你的魔法计算失败了。
portNumber := Buffer[i*20 + 8]; { Should work! }
因为Buffer
是一个字节数组,上面只提取了一个字节。但是本地端口号在TCPtable中是一个DWORD
。尽管您链接的文档指出:
The local port number in network byte order for the TCP connection on the local computer.
The maximum size of an IP port number is 16 bits, so only the lower 16 bits should be used. The upper 16 bits may contain uninitialized data.
所以我们需要两个字节。我们需要切换它们,注意上面的“网络字节顺序”。
您还忘记考虑 table 开头的 4 字节记录数。所以本地端口号应该变成:
portNumber := Buffer[i*20 + 12] * 256 +
Buffer[i*20 + 13];