I need to check some port is usable or not? How can do that in Inno Setup?
Is there any way to use socket in to Inno Setup? Is there any library for this? If there how can import it and use it?
Thank you for your answers.
You can use my function to check, if a port is available:
function CheckPortOccupied(Port:String):Boolean;
var
ResultCode: Integer;
begin
Exec(ExpandConstant('{cmd}'), '/C netstat -na | findstr'+' /C:":'+Port+' "', '', 0,
ewWaitUntilTerminated, ResultCode);
if ResultCode <> 1 then
begin
Log('this port('+Port+') is occupied');
Result := True;
end
else
begin
Result := False;
end;
end;
Function to return (in MsgBox) service or program that use port 80.
MsgBox will not shown if output is empty.
function NextButtonClick(CurPage: Integer): Boolean;
var
TmpFileName, ExecStdout: string;
ResultCode: integer;
begin
if CurPage = wpWelcome then
begin
TmpFileName := ExpandConstant('{tmp}') + '\~pid.txt';
Exec('cmd.exe',
'/C FOR /F "usebackq tokens=5 delims= " %i IN (`netstat -ano ^|find "0.0:80"`) DO '
+ '#tasklist /fi "pid eq %i" | find "%i" > "' + TmpFileName + '"', '', SW_HIDE,
ewWaitUntilTerminated, ResultCode);
if LoadStringFromFile(TmpFileName, ExecStdout) then
begin
MsgBox('Port 80 is used by '#13 + ExecStdout, mbInformation, MB_OK);
end;
DeleteFile(TmpFileName);
end;
Result := True;
end;
Related
For a Security product I am currently writing in Delphi. I am using the below solution to use the ARP table to get MAC-Addresses from devices to detected what is on the network.
How to find MAC addresses from arp -a scan
I just do a range of Ping commands to fill the ARP table and read results from the ARP-table.
However when a Firewall on a machine is blocking Ping. Sometimes the MAC is still exposed in ARP, but not always. What is a better solution to detect all devices on a network and get MAC-Addresses from them?
//-----------------------------------------------------------------------------
{ ARP-table lists relations between remote IP and remote MAC-address.
NOTE: these are cached entries;when there is no more network traffic to a
node, entry is deleted after a few minutes.
}
//-----------------------------------------------------------------------------
procedure Get_ARPTable( aList: TStrings; aDeviceList : TObjectList<TNetworkDevice>);
var
lIPNetRow : TMibIPNetRow;
lTableSize : DWORD;
lNumEntries : DWORD;
lErrorCode : DWORD;
lIdx : Integer;
lPBuf : PAnsiChar;
lPhysAddr : TMACAddress;
lMacAddr2Str : string;
ldwAddr : string;
ldwType : string;
lNewDevice : TNetworkDevice;
begin
if not LoadIpHlp then Exit;
if not Assigned( aList ) then Exit;
if not Assigned( aDeviceList) then Exit;
Get_LocalNetworkDevices(aDeviceList);
aList.Clear;
lTableSize := 0;
lErrorCode := GetIPNetTable( Nil, #lTableSize, False );
//
if lErrorCode = ERROR_NO_DATA then
begin
aList.Add( ' ARP-cache empty.' );
EXIT;
end;
// get table
GetMem( lPBuf, lTableSize );
lNumEntries := 0;
try
lErrorCode := GetIpNetTable( PTMIBIPNetTable( lPBuf ), #lTableSize, False );
if lErrorCode = NO_ERROR then
begin
lNumEntries := PTMIBIPNetTable( lPBuf )^.dwNumEntries;
if lNumEntries > 0 then
begin
Inc( lPBuf, SizeOf( DWORD ) );
for lIdx := 1 to lNumEntries do
begin
lIPNetRow := PTMIBIPNetRow( lPBuf )^;
lMacAddr2Str := MacAddr2Str( lIPNetRow.bPhysAddr, lIPNetRow.dwPhysAddrLen );
lPhysAddr := lIPNetRow.bPhysAddr;
ldwAddr := IPAddr2StrTrunc(lIPNetRow.dwAddr);
ldwType := ARPEntryType[lIPNetRow.dwType];
lNewDevice := SeekDevice(aDeviceList, lMacAddr2Str);
if Assigned(lNewDevice) then
begin
lNewDevice.IP := ldwAddr;
lNewDevice.IsNew := False;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
if (lNewDevice.EntryType = 'Dynamic') or
(lNewDevice.EntryType = 'Static') then
lNewDevice.SetStamp;
end
else
begin
lNewDevice := TNetworkDevice.Create;
lNewDevice.IP := ldwAddr;
lNewDevice.EntryType := ARPEntryType[lIPNetRow.dwType];
lNewDevice.AddOrUpdate(lMacAddr2Str);
lNewDevice.SetFirstSeen;
lNewDevice.SetStamp;
lNewDevice.State := dtRogue;
lNewDevice.IsNew := True;
aDeviceList.Add(lNewDevice);
end;
with lIPNetRow do
begin
aList.Add( Format( '%8x | %12s | %16s| %10s',
[dwIndex, MacAddr2Str( bPhysAddr, dwPhysAddrLen ),
IPAddr2Str( dwAddr ), ARPEntryType[dwType]
]));
end;
Inc( lPBuf, SizeOf( lIPNetRow ) );
end;
end
else
aList.Add( ' ARP-cache empty.' );
end
else
aList.Add( SysErrorMessage( lErrorCode ) );
// we _must_ restore Pointer!
finally
Dec( lPBuf, SizeOf( DWORD ) + lNumEntries * SizeOf( lIPNetRow ) );
FreeMem( lPBuf );
end;
end;
To complement ping, you can try to establish a TCP connection on any port, for example port 80 (HTTP). Of course you'll get mostly connection denied but you'll still be able to find an entry in the ARP table.
Depending on the firewall, you may also simply get to response at all. Then you have to try another port. I would suggest 135 (RPC) or 445 or 139 (Both SMB) which should be open is network shares are enabled. Another one in 3389 used by RDP if enabled.
This is practical only if the subnet is small (Class-C, 256 addresses) or medium (Class-B, 16K addresses). Anyway, it would take a lot of time.
I have a MS Access app which I am planning to sell to people however it uses ODBC (MYSQL) to connect to a backend MySQL DB plus obviously needs ms access (runtime at least).
I created an Inno Setup installer to check for installed components however I have come across some issues:
ODBC 64x fails unless you also have ODBC 32x installed
ODBC (either) cant install unless the respective bit Visual C++ 2015-2019 is installed
My registry path for Visual C+ detects the install but not the Bit version!
Ms Access 365 (2019?) isn't detected using my current registry detection methods.
Code used:
function GetHKLM: Integer;
begin
if IsWin64 then
Result := HKLM64
else
Result := HKLM32;
Result := HKLM;
end;
function IsOfficeInstalled: Boolean;
begin
Result := RegKeyExists(GetHKLM, 'Software\Microsoft\Office');
end;
function NormalAccessPath: String;
var
Names: TArrayOfString;
I: Integer;
S: String;
begin
if not IsOfficeInstalled then
Result:=''
else
begin
if RegGetSubkeyNames(GetHKLM, 'Software\Microsoft\Office', Names) then
begin
for I := 0 to GetArrayLength(Names)-1 do
begin
S := 'Software\Microsoft\Office\' + Names[I]+ '\' + 'Access';
if RegKeyExists(GetHKLM, S) then
Result:=S;
S := '';
end
end
end;
end;
function IsNormalAccessInstalled: Boolean;
var
Path: String;
begin
Path:= NormalAccessPath;
if Path <> '' then
Result := True
else
Result := False
end;
function IsMySQLODBC51Installed: Boolean;
begin
// the result was inverted in the original code; the original function returned
// True if the ODBC driver was not installed, False otherwise, and according to
// the function name it should be vice-versa
Result := RegKeyExists(GetHKLM, 'Software\ODBC\ODBCINST.INI\MySQL ODBC 8.0 Unicode Driver');
end;
function IsRuntimeAccessInstalled: Boolean;
begin
Result := RegKeyExists(GetHKLM, 'Software\Microsoft\Windows\CurrentVersion\Uninstall\AccessRuntimeRetail - en-us');
end;
function IsVCInstalled: Boolean;
begin
Result := RegKeyExists(GetHKLM, 'Software\Microsoft\VisualStudio\14.0\VC');
end;
function AddAsTrusted: Boolean;
var
Path: String;
ResultCode: Integer;
begin
Path:= NormalAccessPath;
MsgBox('Adding Trusted Location', mbInformation, MB_OK);
//Exec('C:\Me\freelance\Inno setup script\fixes.bat', '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode)
if Path <> '' then
begin
//if not RegKeyExists(GetHKLM, Path + '\Security') then
// RegWriteStringValue(GetHKLM, Path + '\Security','MedidropUseless', 'MedidropUseless')
if not RegKeyExists(GetHKLM, Path + '\Security\Trusted Locations') then
RegWriteStringValue(GetHKLM, Path + '\Security\Trusted Locations\Location1','Path', 'C:\MediDrop')
end;
Result := True;
end;
So for 2 & 3:
I need to detect visualC's VERSION.
I tested the installer at a new location today and they already had visual C++ 2015 86x so my installed failed for ODBC 64x because visualC 64x was missing. Obviously:
'Software\Microsoft\VisualStudio\14.0\VC'
is not appropriate..
For 4:
As can be seen I am checking Software\Microsoft\Office however this computer did have the 15.0 and 16.0 subfolders (terminology?) but did NOT have ACCESS in them so my installer thought it was not installed.
However.. it was, so I need to find a way of finding this 365 version of access!
Finally, at the end of the install I add a path for trusted location:
function AddAsTrusted: Boolean;
var
Path: String;
ResultCode: Integer;
begin
Path:= NormalAccessPath;
MsgBox('Adding Trusted Location', mbInformation, MB_OK);
//Exec('C:\Me\freelance\Inno setup script\fixes.bat', '', '', SW_SHOW, ewWaitUntilTerminated, ResultCode)
if Path <> '' then
begin
if not RegKeyExists(GetHKLM, Path + '\Security\Trusted Locations') then
RegWriteStringValue(GetHKLM, Path + '\Security\Trusted Locations\Location1','Path', 'C:\[MyAppName]')
end;
Result := True;
end;
Once again due to the 365 issue... this did not work. I created the path correctly in 16.0 but since access wasnt there, I still needed to Enable Content when I opened the app the first time, something I don't want to do.
Does anyone know where I can start to figure the above issues out??
I want to play with MySQL process and get what ever it write to console so I write this code in FreePascal:
I want to control MySQl and read & write whatever.
Process := TProcess.Create(nil);
with Process do
begin
Executable := 'C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin\mysql.exe';
with Parameters do
begin
Options := [poUsePipes];
Add('-u');
Add('root');
Add('-p');
end;
Execute;
sleep(1000);
WriteLn(Process.Output.NumBytesAvailable); // will be 0 but it write "Enter password"
WriteLn(Process.Stderr.NumBytesAvailable); // will be 0 but it write "Enter password"
end;
TProcess is a component that control executing other programs,I even test Delphi codes but all result are the same.
But the problem is that this will freeze because there is no output but console window write :
Enter password:
How can I get this in my application and all others?
As I said I want to work with MySQL executable and read from and write in it, So I dont want to use its library or any other DB component.
EDIT:
Here is a Delphi version of my test from here with the same result :
function GetDosOutput(CommandLine: string; Work: string = 'C:\'): string;
var
SA: TSecurityAttributes;
SI: TStartupInfo;
PI: TProcessInformation;
StdOutPipeRead, StdOutPipeWrite: THandle;
WasOK: Boolean;
Buffer: array[0..255] of AnsiChar;
BytesRead: Cardinal;
WorkDir: string;
Handle: Boolean;
begin
Result := '';
with SA do begin
nLength := SizeOf(SA);
bInheritHandle := True;
lpSecurityDescriptor := nil;
end;
CreatePipe(StdOutPipeRead, StdOutPipeWrite, #SA, 0);
try
with SI do
begin
FillChar(SI, SizeOf(SI), 0);
cb := SizeOf(SI);
dwFlags := STARTF_USESHOWWINDOW or STARTF_USESTDHANDLES;
wShowWindow := SW_HIDE;
hStdInput := GetStdHandle(STD_INPUT_HANDLE); // don't redirect stdin
hStdOutput := StdOutPipeWrite;
hStdError := StdOutPipeWrite;
end;
WorkDir := Work;
Handle := CreateProcess(nil, PChar('cmd.exe /C ' + CommandLine),
nil, nil, True, 0, nil,
PChar(WorkDir), SI, PI);
CloseHandle(StdOutPipeWrite);
if Handle then
try
repeat
WasOK := ReadFile(StdOutPipeRead, Buffer, 255, BytesRead, nil);
if BytesRead > 0 then
begin
Buffer[BytesRead] := #0;
Result := Result + Buffer;
end;
until not WasOK or (BytesRead = 0);
WaitForSingleObject(PI.hProcess, INFINITE);
finally
CloseHandle(PI.hThread);
CloseHandle(PI.hProcess);
end;
finally
CloseHandle(StdOutPipeRead);
end;
end;
procedure TForm1.FormCreate(Sender: TObject);
begin
memo1.text:=GetDosOutput('"C:\Program Files (x86)\MySQL\MySQL Server 5.5\bin\mysql.exe" -u root -p');
end;
It will freeze in WasOK line .
Also I tested many other codes for this subject and neither worked.
I'n writing a small program to calculate traffic fines in FreePascal. The source code is as follows:
program TrafficFine;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes,SysUtils;
var
userInput : Char;
Fine : Integer;
TotalFine : Integer;
DaysPassed: Integer;
FineType : Integer;
begin
userInput := 'y';
while (userInput = 'Y') or (userInput = 'y') do
begin;
writeln('Enter type of fine:');
writeln('- Enter 1 for not wearing a seat-belt.');
writeln('- Enter 2 for driving without a license');
writeln('- Enter 3 for over-speeding.');
try
write('Enter value: ');
readln(FineType);
if(FineType <0) or (FineType>3) then
raise exception.Create('Fine type outside of range.');
case FineType of
1: Fine:= 500;
2: Fine:= 1000;
3: Fine:= 2000;
except
on e: exception do {line 39}
begin
Writeln('Error: '+e.message);
continue;
end;
write('Enter number of days passed since fine: ');
readln(DaysPassed);
if daysPassed<=10 then
TotalFine := Fine;
else if (daysPassed >10) and (daysPassed <=30) then
TotalFine := Fine * 2;
else
TotalFine := Fine*2 + Fine*0.5;
writeln('Total Fine is ' + IntToStr(TotalFine));
writeln('Would you like to calculate another fine: ');
readln(userInput);
end;
end.
I get the following errors:
Free Pascal Compiler version 2.4.4-2ubuntu1 [2011/09/27] for i386
Copyright (c) 1993-2010 by Florian Klaempfl Target OS: Linux for i386
Compiling /home/ubuntu/Desktop/TrafficFine.pas TrafficFine.pas(39,3)
Error: Illegal expression TrafficFine.pas(40,3) Error: Constant
Expression expected TrafficFine.pas(40,3) Fatal: Syntax error, ":"
expected but "identifier ON" found Fatal: Compilation aborted
I followed the example straight from a book so I'm not sure where I've gone wrong. Any help would be appreciated. Thanks.
You have several flaws in your code, I corrected and commented in the source. try this new version.
program TrafficFine;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes,SysUtils;
var
userInput : Char;
Fine : Integer;
TotalFine : Integer;
DaysPassed: Integer;
FineType : Integer;
begin
userInput := 'y';
while (userInput = 'Y') or (userInput = 'y') do
begin //removed semicolon
writeln('Enter type of fine:');
writeln('- Enter 1 for not wearing a seat-belt.');
writeln('- Enter 2 for driving without a license');
writeln('- Enter 3 for over-speeding.');
try
write('Enter value: ');
readln(FineType);
if(FineType <0) or (FineType>3) then
raise exception.Create('Fine type outside of range.');
case FineType of
1: Fine:= 500;
2: Fine:= 1000;
3: Fine:= 2000;
end;//added end;
except
on e: exception do {line 39}
begin
Writeln('Error: '+e.message);
continue;
end;
end; //added end;
write('Enter number of days passed since fine: ');
readln(DaysPassed);
if daysPassed<=10 then
TotalFine := Fine //removed semicolon
else if (daysPassed >10) and (daysPassed <=30) then
TotalFine := Fine * 2 //removed semicolon
else
TotalFine := (Fine*2) + (Fine div 2);//replaced this sentence (Fine*2) + (Fine*0.5)
writeln('Total Fine is ' + IntToStr(TotalFine));
writeln('Would you like to calculate another fine: ');
readln(userInput);
end;
end.
It seems like you forgot to close Case with an End;
in Inno Setup script GetExceptionMessage returns empty message (it contains only colon ":" sign). The last version of Inno Setup (5.4.2) is used.
try
Log('Create IISNamespace');
// Create IIS namespace object
if Length(virtualDirectoryName) > 0 then
begin
IIS := CreateOleObject('IISNamespace');
Log('Get IIsWebService');
WebSite := IIS.GetObject('IIsWebService', IISServerName + '/w3svc');
Log('Get IIsWebServer');
WebServer := WebSite.GetObject('IIsWebServer', IISServerNumber);
Log('Get IIsWebVirtualDir');
WebRoot := WebServer.GetObject('IIsWebVirtualDir', 'Root');
Log('Delete IIsWebVirtualDir');
WebRoot.Delete('IIsWebVirtualDir', virtualDirectoryName);
WebRoot.SetInfo();
end;
except
MsgBox(ExpandConstant('{cm:IISException,'+ GetExceptionMessage +'}'),
mbInformation, mb_Ok);
Log('Uninstall IIS 6 exception: ' + GetExceptionMessage);
end;
The exception occurs during deleting IIsWebVirtualDir.
Is there any way to get exception type or real exception message?
Thanks, Denis.
I just wrote the following example to see if either GetExceptionMessage or ShowExceptionMessage are broken. I used both Inno setup 5.4.2 Unicode and Ansi versions.
[Setup]
AppName=Test
AppVersion=1.5
DefaultDirName={pf}\test
[Code]
function InitializeSetup(): Boolean;
var
I: Integer;
begin
try
I := I div 0; // Raise an exception
except
MsgBox(GetExceptionMessage,
mbError, MB_OK);
ShowExceptionMessage;
end;
result := false;
end;
I also ran CodeAutomation.iss that ships and it worked as expected. Which is contrary to the comment made by Alex K. that it may be broken.
Now that I know that the routines should work, I took your code and made the following setup test Script and ran it and it raised an exception on not finding the ISSNamespace as I don't have it installed.
[Setup]
AppName=Test
AppVersion=1.5
DefaultDirName={pf}\test
[CustomMessages]
IISException =ISS Exception " %1 " occured.
[Code]
const
IISServerName = 'localhost';
IISServerNumber = '1';
IISURL = 'http://127.0.0.1';
function InitializeSetup(): Boolean;
var
IIS, WebSite, WebServer, WebRoot, VDir: Variant;
virtualDirectoryName : String;
begin
virtualDirectoryName := 'test';
try
Log('Create IISNamespace');
// Create IIS namespace object
if Length(virtualDirectoryName) > 0 then
begin
IIS := CreateOleObject('IISNamespace');
Log('Get IIsWebService');
WebSite := IIS.GetObject('IIsWebService', IISServerName + '/w3svc');
Log('Get IIsWebServer');
WebServer := WebSite.GetObject('IIsWebServer', IISServerNumber);
Log('Get IIsWebVirtualDir');
WebRoot := WebServer.GetObject('IIsWebVirtualDir', 'Root');
Log('Delete IIsWebVirtualDir');
WebRoot.Delete('IIsWebVirtualDir', virtualDirectoryName);
WebRoot.SetInfo();
end;
except
MsgBox(ExpandConstant('{cm:IISException,'+ GetExceptionMessage +'}'),
mbInformation, mb_Ok);
Log('Uninstall IIS 6 exception: ' + GetExceptionMessage);
end;
end;
But I made a fatal flaw during the construction of this script that could be your problem.
Check your [CustomMesssages] section make sure you have %1 in the message. Otherwise nothing is returned.