【发布时间】: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 there 与 the nasty struct I need 非常相似。 -
i*20 + 8是这样写的,因为 20 = sizeof(single record struct) 和8 = 2 * sizeof(DWORD)。如您所见,本地 TCP 连接地址正在以 1 个 DWORD 的偏移量逐个字节地“解析”。
所以,一切都很有趣......它只是不工作=((
是的,我尝试逐个打印所有字节,以手动搜索所需数据并了解正确的偏移量。令我失望的是,没有发现任何看起来像 IP 和端口的东西,这些数字非常神秘。
我知道有时最简单的解决方案是最好的,而不是最聪明的,但如果有人能给我一个以正确方式烹饪这个 WinAPI 函数的密钥,我将不胜感激。
【问题讨论】:
-
代码还能运行吗?在 pascal 中,局部变量未初始化,第一次调用可能没有 tablesize 0,因此函数可能会失败并出现完全不同的错误。
-
确实如此。
TableSize与var一起传递,因此第一次调用实际上将其设置为正确的值,这是一种常见的 winapi 技术来获得所需的缓冲区大小。在问题中提到的答案中,同样的技巧适用于其他函数调用。并且“调试消息”起作用了,这就是我知道数据混乱的方式。 -
谢谢,我熟悉winapi,这是一个输入/输出参数。如果它恰好带有足够大的值,则函数不会因缓冲区不足而失败,而是会尝试写入一些无效的内存。你需要初始化它,虽然我知道这不是唯一的问题。
-
正如@SertacAkyuz 所说,第一次调用应该使用初始化变量。您的常用 WinAPI 技术实际上很常见,但您告诉 API 调用您想要缓冲区大小的方式是在第一次调用时将 NULL(在 C 中,
0)传递给函数。未初始化的局部变量包含零 (NULL) 的可能性很小,因此 API 调用会认为您正在传递一个缓冲区,这意味着您得到的东西与它返回时所认为的不同。 总是初始化变量而不是做假设。 -
好的,很抱歉发明了“有意义的借口”而不是正确的变量初始化 =))。你们都完全正确。谢谢!这将是我要解决的第一件事。
标签: winapi inno-setup pascalscript