Reputation: 1653
My question was asked here before, but I'm trying to implement a neater solution for my project. So, as title states, I'm creating a complex installer of the server application that has to check local IP address and choose a open port, so that the application could be properly configured. Inno Setup 5.6.1 is used.
Getting local IP addresses was not a problem, this solution helped me a lot. Then it came to the port's checking and here I've found the following three options:
As was mentioned above, getting IP address can be implemented via straightforward (ok, not really, it's a Pascal Script) WinAPI calls. So, I tried to do the same trick with ports, trying to call GetTcpTable()
:
[Code]
const
ERROR_INSUFFICIENT_BUFFER = 122;
function GetTcpTable(pTcpTable: Array of Byte; var pdwSize: Cardinal;
bOrder: WordBool): DWORD;
external '[email protected] 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;
This GetTcpTable
also returns information about addresses and ports (table of TCP connections to be exact), so trying to get any connection address is good for debugging purposes. More about this attempt:
RecordCount
is calculated the same way as in the code I used as an example, because obtained struct there is very similar to the nasty struct I need.i*20 + 8
is written that way, because 20 = sizeof(single record struct) and 8 = 2 * sizeof(DWORD)
. Local TCP connection address is being "parsed" one-by-one-byte at an offset of 1 DWORD, as you can see.So, everything is great fun... it just is not working =((
And yes, I've tried to print all the bytes one-by-one, to search for the desired data manually and understand the correct offset. To my disappointment, nothing looking like IPs and ports was found, the numbers were quite mysterious.
I know that sometimes the simplest solution is best, not the smartest, but if anyone could give me a key cooking this WinAPI function in a proper way, I would be deeply grateful.
Upvotes: 2
Views: 473
Reputation: 54822
Your magic calculations are off.
portNumber := Buffer[i*20 + 8]; { Should work! }
Since Buffer
is a byte array the above extracts one byte only. But the local port number is a DWORD
in the TCP table. Though the documentation you linked states:
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.
So we need two bytes. And we need to switch them, note "network byte order" above.
You are also forgetting to account for the 4 byte record count at the beginning of the table. So the local port number should become:
portNumber := Buffer[i*20 + 12] * 256 +
Buffer[i*20 + 13];
Upvotes: 2