How to Abort Inno Setup - inno-setup

I don't know why this is not working - it's driving me nuts...
// ----------------------------------------------------------------------------
procedure InitializeWizard();
begin
{ Custom wizard pages }
CreateTheWizardPages;
if ListBox.items.count < 1 then begin
MsgBox('No File Found:'#13#10#13#10 + 'For help visit: {#myWWW}', mbInformation, mb_Ok);
Abort;
end else
{ Custom controls }
CreateAboutButtonAndURLLabel(WizardForm, WizardForm.CancelButton);
{ Custom beveled label }
WizardForm.BeveledLabel.Caption := ' My Program ';
end;
The msgbox displays - but the installer does not seem to exit silently :-(

Gah - so it turns out the code above works perfectly, just have to run it with the debugger turned off.
Hope this helps someone out there. The abort statement is used to exit the installer but only works from certain fuctions / procedures.
Cheers,

Related

Save Excel file inside Delphi service [duplicate]

I have console Delphi application which works with Excell though OLE.
The examle of code is pretty simple
procedure SaveBaseRecordsToFile(BaseName: string; PaymentRecords: TPaymentRecords);
var
i: integer;
Excel: Variant;
begin
try
Excel:=CreateOleObject('Excel.Application');
Excel.DisplayAlerts:=False;
except
on E: Exception do begin
LogWriter.WriteLog(E.ClassName + ': ' + E.Message);
exit;
end;
end;
try
Excel.Workbooks.Add;
//Excel.Worksheets.Add;
Excel.Worksheets[1].Cells[2, 1].Value := 'Account number';
Excel.Worksheets[1].Cells[2, 2].Value := 'Sum';
for i := 0 to Length(PaymentRecords) - 1 do begin
Excel.Worksheets[1].Cells[i + 3, 1].Value := PaymentRecords[i].NUMBER;
Excel.Worksheets[1].Cells[i + 3, 2].Value := PaymentRecords[i].SUMMA;
end;
Excel.ActiveSheet.Name := 'SHEET1';
Excel.Application.Workbooks[1].SaveAs(ExtractFilePath(ParamStr(0)) + BaseName + '.xls', 56);
Excel.Workbooks.Close;
Excel.Quit;
finally
Excel := Unassigned;
end;
end;
When I run this application in interactive mode (by myself) it wors perfect.
But when I try to run it through standart Microsoft Scheduler I see in log of my applications such records:
21:41:40.523: EOleSysError: Отказано в доступе, ProgID: "Excel.Application" (Access denied from Russian)
If I set in schedule task option "Run with highest privileges" I see in log of my applications such records:
20:12:04.475: EOleException: Метод SaveAs из класса Workbook завершен неверно (SaveAs method of Workbook class finished incorrectly from Russian)
Is there any way to run application with OLE call through Microsort Scheduler?
Or maybe I can work with Excel without OLE (it's pretty simple operations as you could see in my examle)? How can I do it?
The fundamental issue is that Excel needs to run in an interactive desktop. Running through the task scheduler, as you have configured it, runs it in a non-interactive desktop. That's not a supported mode of operation for Excel: http://support.microsoft.com/kb/257757
Your only real hope of success is to create the Excel file without using COM automation of Excel. There are many libraries to help you do that.

What does it mean that message boxes are being suppressed in Inno Setup?

Here's a page from documentation: SuppressibleMsgBox.
What does it mean If message boxes are being suppressed...?
There's a link to the explanation right after the part you have quoted:
If message boxes are being suppressed (see Setup Command Line Parameters), Default is returned.
In the link, there's /SUPPRESSMSGBOXES commandline parameter documented:
Instructs Setup to suppress message boxes. Only has an effect when combined with '/SILENT' or '/VERYSILENT'.
So normally, the SuppressibleMsgBox behaves as the MsgBox. But if you run the installer with the /SUPPRESSMSGBOXES parameter, the SuppressibleMsgBox does nothing, only silently returns the value of the Default parameter.
A practical example of use of the function:
function NextButtonClick(CurPageID: Integer): Boolean;
var
Dir: string;
Msg: string;
begin
Result := True;
if CurPageID = wpSelectDir then
begin
Dir := WizardForm.DirEdit.Text;
if Pos(' ', Dir) > 0 then
begin
Msg :=
'It is not recommended to install the application to a path with spaces. '
+ 'Do you want to continue anyway?';
if SuppressibleMsgBox(Msg, mbInformation, MB_YESNO, IDYES) = IDNO then
begin
Result := False;
end;
end;
end;
end;
In an interactive installation, the installer will warn, if the user tries to install to a path with spaces. But if you are automating a silent installation with /SILENT /SUPPRESSMSGBOXES, the installer will proceed.
It's good idea to use SuppressibleMsgBox whenever you do not want that particular message to break silent installation. So for most cases.

Inno Setup Log setup exit code

The Inno Setup log file does not, by default, include the setup exit code. I am looking for a way to include this in the log file. I am assuming this would be done using the Log function and including it in the DeinitializeSetup event. Something like this:
procedure DeinitializeSetup();
begin
Log('Exit code: ' + ExitCode);
end;
What I don't know, and cannot seem to find, is how to return the setup exit code, so that I can use it in the Log function. Is this the best way to do this and how do I return the setup exit code?
There's no way to retrieve the exit code in Pascal Script.
All you can do is to log, if installation was successful or not (what is logged anyway already).
One way to do that is by checking, if the GetCustomSetupExitCode event function was called or not (it's called when exit code would be 0 only).
var
ZeroExitCode: Boolean;
function GetCustomSetupExitCode: Integer;
begin
ZeroExitCode := True;
Result := 0;
end;
procedure DeinitializeSetup();
begin
if ZeroExitCode then
Log('Zero exit code')
else
Log('Non-zero exit code');
end;

WizardForm.DirEdit.Text not updating properly. Inno setup

I am trying to set the path in the 'choose install directory' form using INNO setup. Here is my code
procedure CurPageChanged(pageID: Integer);
var
sInstallDir: String;
begin
// Default install dir is the IIS install path
if (pageID = wpSelectDir) then begin
sInstallDir := GetIISInstallPath + '\MyFolder';
Log('GetIISInstallPath: '+ GetIISInstallPath);
Log('sInstallDir: ' + sInstallDir);
WizardForm.DirEdit.Text := sInstallDir;
end;
end;
The problem I am having is that 'GetIISInstallPath' returns me 'c:\inetpub\wwwroot and that is what I see in the WizardForm. It seems to not add the MyFolder bit.
I printed out the involved variables and they all have the correct value.
sInstallDir shows up as 'C:\inetpub\wwwroot\MyFolder' but it does not show in the text field. It shows (as mentioned) only 'C:\inetpub\wwwroot'.
Please advise.
Thank You
Your code works fine for me but, can I suggest you to use
[Setup]
...
DefaultDirName={code:GetDefaultDirName}
[code]
...
function GetDefaultDirName(): String;
begin
Result := GetIISInstallPath + '\MyFolder';
end;
Doing this the "GetIISInstallPath + \MyFolder" will be your default directory

reasons why excel.exe remains loaded after running a delphi client automation program?

I wrote a Delphi program that extracts and consolidates data from several different spreadsheets of a single .XLS file, to a text file for later processing. It is a Delphi 7 console program.
An excerpt of the most relevant pieces of code will show you that, apparently, my program is pretty well behaved or at least as much as it needs to be.
uses ... ActiveX, ComObj ... ;
procedure Fatal(s:string);
...
Halt(1);
var ExcelApp:Variant; (* global var *)
begin (* main program block *)
coInitialize(nil);
ExcelApp:=CreateOleObject('Excel.Application');
try
ExcelApp.Visible:=False;
ExcelApp.WorkBooks.Open(ExcelFileName);
...
XLSSheet := ExcelApp.Worksheets[ExcelSheetName];
...
try
XLSRange := XLSSheet.Range[ExcelRangeName];
except
Fatal('Range "'+ExcelRangeName+'" not found');
end;
if VarIsNull(XLSRange) then Fatal('Range '+ExcelRangeName+' not found');
for row:=XLSRange.Row to XLSRange.Rows[XLSRange.Rows.Count].Row do
for col:=XLSRange.Column to XLSRange.Columns[XLSRange.Columns.Count].Column do
CellValue:=XLSSheet.Cells[Row,Col].Value;
...
if CellValue<>'' then ...
...
ExcelApp.Workbooks.Close;
...
finally
ExcelApp.Quit;
coUninitialize;
end;
end.
Sometimes, when the program exits, the XLS remains locked. Looking at the Task Manager, I see that Excel.exe process that was started when the client program ran, is still running, eventhoug the client program has exited and succesfully unloaded.
Do you happen to know what are the usual suspects for this behaviour? have any idea where to look for always unloading excel upon client execution?
You need to release the ExcelApp variant. It still holds a reference count of 1, and therefore Excel isn't completely closed.
Add this to your code (the marked line):
finally
ExcelApp.Quit;
ExcelApp := Unassigned; // Add this line
coUninitialize;
end;
Here is some simple code to reproduce the problem, and test the solution:
// Add two buttons to a form, and declare a private form field.
// Add OnClick handlers to the two buttons, and use the code provided.
// Run the app, and click Button1. Wait until Excel is shown, and then click
// Button2 to close it. See the comments in the Button2Click event handler.
type
TForm1=class(TForm)
Button1: TButton;
Button2: TButton;
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
ExcelApp: Variant;
end;
implementation
uses
ComObj;
procedure TForm1.Button1Click(Sender: TObject);
begin
ExcelApp := CreateOleObject('Excel.Application');
ExcelApp.Visible := True;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
ExcelApp.Visible := False;
ExcelApp.Quit;
// Leave the next line commented, run the app, and click the button.
// After exiting your app NORMALLY, check Task Manager processes, and you'll
// see an instance of Excel.exe still running, even though it's not
// in the Applications tab.
//
// Do an "end process" in Task Manager to remove the orphaned instance
// of Excel.exe left from the above. Uncomment the next line of code
// and repeat the process, again closing your app normally after clicking
// Button2. You'll note that Excel.exe is no longer in
// Task Manager Processes after closing your app.
// ExcelApp := Unassigned;
end;
end.
I have encountered much the same problem in XE2 and my solution was to replace such code samples:
fExcel.ActiveWorkBook.ActiveSheet.Range[
fExcel.ActiveWorkBook.ActiveSheet.Cells[3, 2],
fExcel.ActiveWorkBook.ActiveSheet.Cells[3+i,1+XL_PT_Tip_FieldCount]
].Formula := VarArr;
with:
cl := fExcel.ActiveWorkBook.ActiveSheet.Cells[3, 2];
ch := fExcel.ActiveWorkBook.ActiveSheet.Cells[3+i,1+XL_PT_Tip_FieldCount];
fExcel.ActiveWorkBook.ActiveSheet.Range[cl, ch].Formula := VarArr;
Same happens in this case, where sheet variable is used:
sheetDynamicHb := fExcel.ActiveWorkBook.Sheets['Dynamics Hb'];
cl := sheetDynamicHb.Cells[52, 2];
ch := sheetDynamicHb.Cells[52+i, 2+3];
sheetDynamicHb.Range[cl, ch].Formula := VarArr;
Somehow introducing temp variables (cl,ch: Variant) does the trick. It seems like the nested Excel variable access does something odd. I can not explain why this works like that, but it does work..
I faced the same issue trying to close "zombie" Excel processes (the ones that stay running if I launch them from my app and then forced terminate the app). I tried all suggested actions with no luck. Finally I created a combined killer procedure that robustly does the trick using WinApi if usual COM methods do not help.
procedure KillExcel(var App: Variant);
var
ProcID: DWORD;
hProc: THandle;
hW: HWND;
begin
hW := App.Application.Hwnd;
// close with usual methods
App.DisplayAlerts := False;
App.Workbooks.Close;
App.Quit;
App := Unassigned;
// close with WinApi
if not IsWindow(hW) then Exit; // already closed?
GetWindowThreadProcessId(hW, ProcID);
hProc := OpenProcess(PROCESS_TERMINATE, False, ProcID);
TerminateProcess(hProc, 0);
end;

Resources