Inno setup: Extract XML node and show it - inno-setup

I have the following code and I want to extract it into a plain text but I cannot manage to do it.
The code works but what I need is to show it in the ExpandConstant field.
I have tried several ways but no luck so far.
function LoadValueFromXML(const AFileName, APath: string): string;
var
XMLNode: Variant;
XMLDocument: Variant;
begin
Result := '';
XMLDocument := CreateOleObject('Msxml2.DOMDocument.3.0');
try
XMLDocument.async := False;
XMLDocument.load(AFileName);
if (XMLDocument.parseError.errorCode <> 0) then
MsgBox('The XML file could not be parsed. ' +
XMLDocument.parseError.reason, mbError, MB_OK)
else
begin
XMLDocument.setProperty('SelectionLanguage', 'XPath');
XMLNode := XMLDocument.selectSingleNode(APath);
Result := XMLNode.text;
end;
except
MsgBox('An error occured!' + #13#10 + GetExceptionMessage, mbError, MB_OK);
end;
end;
procedure CurPageChanged(CurPageID: Integer);
begin
if CurPageID = CustomPageID then
CustomEdit.Text := LoadValueFromXML('C:\Games\World_of_Tanks_test\WoTLauncher.xml', '//info/patch_info_urls/item');
end;
procedure ClienteWot();
var
StaticText: TNewStaticText;
begin
StaticText := TNewStaticText.Create(WizardForm);
StaticText.Parent := WizardForm.SelectComponentsPage;
StaticText.Left := 425;
StaticText.Top := ScaleY(40);
StaticText.Font.Style := [fsBold];
//StaticText.Font.Color := clRed;
StaticText.Caption := ExpandConstant('Cliente WOT: -->>> Show XML Url <<<---');
end;

If you want to inline a value into a string that can be passed to the ExpandConstant function call, you can use e.g. the Format function:
var
...
URL: string;
begin
...
URL := LoadValueFromXML('C:\MyFile.xml', '//node/subnode');
StaticText.Caption := ExpandConstant(Format('{username} %s', [URL]));
end;
The above pseudocode example reads the XML value and assigns the returned value to the URL variable. Then it evaluates the inner statement of the second line:
Format('{username} %s', [URL])
Which inlines the URL string value into the given string (in place of %s) and produces a string like:
'{username} www.example.com'
And this string is then processed by the ExpandConstant function call (the outer statement) and assigned to the static text caption, which might be e.g.:
'MyUserName www.example.com'

Related

Inno Setup, How to search specific string in several files?

I would like to search specific string in several files.
e.g. oem1.inf oem2.inf oem5.inf oem8.inf...
All the target file names have the same format - oem*.inf
I want to search specific substring (e.g. "1234" in "ABA1234") in these files.
I have referenced Inno setup search for existing file, but it's a little different from my question.
I could get all path now:
Var
FilesFound: Integer;
FindRec: TFindRec;
Stemp: String;
begin
FilesFound := 0;
if FindFirst('C:\Path\oem*.inf', FindRec) then begin
try
repeat
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
begin
temp := 'C:\Path\' + FindRec.Name;
MsgBox(temp, mbInformation, MB_OK);
FilesFound := FilesFound + 1;
end;
until not FindNext(FindRec);
finally
FindClose(FindRec);
end;
end;
MsgBox(IntToStr(FilesFound) + ' files found in the System directory.',
mbInformation, MB_OK);
end;
I solved this question. Thanks Martin and Ken.
Var
FindRec: TFindRec;
I: Integer;
Tag: String;
Line: String;
FileLines: TStringList;
begin
if FindFirst('C:\PATH\oem*.inf', FindRec) then
begin
try
FileLines := TStringList.Create;
Tag := 'ABA1234';
repeat
if FindRec.Attributes and FILE_ATTRIBUTE_DIRECTORY = 0 then
begin
FileLines.LoadFromFile('C:\PATH\' + FindRec.Name);
for I := 0 to FileLines.Count - 1 do
begin
Line := FileLines[I];
if (Pos(Tag, Line) > 0) then
MsgBox(temp, mbInformation, MB_OK);
end;
end;
until not FindNext(FindRec);
finally
FileLines.Free;
FindClose(FindRec);
end;
end;
end;

Correct way to add items to a list box in Inno Setup?

I got a code from List all physical printers using WMI query in Inno Setup and I want to add the results to a list box. I have tried to do it before asking, but I just can't add all items. This is an my code:
var
Query, AllPrinters: string;
WbemLocator, WbemServices, WbemObjectSet: Variant;
Printer: Variant;
I: Integer;
begin
WbemLocator := CreateOleObject('WbemScripting.SWbemLocator');
WbemServices := WbemLocator.ConnectServer('.', 'root\CIMV2');
Query := 'SELECT Name FROM Win32_Printer';
WbemObjectSet := WbemServices.ExecQuery(Query);
if not VarIsNull(WbemObjectSet) and (WbemObjectSet.Count > 0) then
begin
for I := 0 to WbemObjectSet.Count - 1 do
begin
Printer := WbemObjectSet.ItemIndex(I);
if not VarIsNull(Printer) then
begin
Log(Printer.Name);
AllPrinters := Printer.Name;
end;
end;
end;
end;
Then on a custom page do this:
ListBoxPrinters.Items.Add(AllPrinters);
You add the items (printers) to the list box the same way, the original code adds them to the log: in the loop!
for I := 0 to WbemObjectSet.Count - 1 do
begin
Printer := WbemObjectSet.ItemIndex(I);
if not VarIsNull(Printer) then
begin
ListBoxPrinters.Items.Add(Printer.Name);
end;
end;
Of course, you have to create the custom page with the ListBoxPrinters before iterating the printers.
If you cannot run the query after creating the page for whatever reason, you can store a printer list into TStringList.
var
Printers: TStringList;
Printers := TStringList.Create;
for I := 0 to WbemObjectSet.Count - 1 do
begin
Printer := WbemObjectSet.ItemIndex(I);
if not VarIsNull(Printer) then
begin
Printers.Add(Printer.Name);
end;
end;
And once you have the list box ready, you just copy the list over to the box:
ListBoxPrinters.Items.Assign(Printers);
You overwrite always with the next AllPrinters := Printer.Name; the previous value !
simple build the AllPrinters string like that
....
AllPrinters := '';
....
for I := 0 to WbemObjectSet.Count - 1 do
begin
Printer := WbemObjectSet.ItemIndex(I);
if not VarIsNull(Printer) then
begin
Log(Printer.Name);
AllPrinters := AllPrinters + Printer.Name + #13#10;
end;
end;
end;
and
ListBoxPrinters.Items.Text := AllPrinters;

Inno Setup ComboBox how to store/access values

I am creating a custom InputQueryWizardPage with a ComboBox and trying to store values like so:
[Code]
var
//Define global variables
InfoPage: TInputQueryWizardPage;
ComboBox: TNewComboBox;
strComboValue: String;
//Store the ComboBox string value
procedure ComboChange(Sender: TObject);
begin
case ComboBox.ItemIndex of
0:
begin
strComboValue := 'First Entry';
end;
1:
begin
strComboValue := 'Second Entry';
end;
...
end;
end;
procedure InitializeWizard();
var
ComboLabel: TNewStaticText;
//Define the Equipment Information page
InfoPage := CreateInputQueryPage(wpSelectTasks,
'Equipment Information', 'Please enter the equipment information?',
'Please enter the equipment connected, then click Next.');
InfoPage.Add('Location:', False);
InfoPage.Add('Type:', False);
ComboBox := TNewComboBox.Create(InfoPage);
ComboBox.Parent := InfoPage.Surface;
ComboBox.Top := InfoPage.Edits[1].Top + (InfoPage.Edits[1].Top - InfoPage.Edits[0].Top);
ComboBox.Width := (InfoPage.Edits[0].Width / 2) - ScaleX(10);
ComboBox.Style := csDropDown;
ComboBox.Items.Add('First Entry');
ComboBox.Items.Add('Second Entry');
...
ComboBox.OnChange := #ComboChange;
ComboLabel := TNewStaticText.Create(WizardForm);
ComboLabel.Caption := 'Equipment:';
ComboLabel.Top := ComboBox.Top - ScaleY(16);
ComboLabel.Parent := InfoPage.Surface;
end;
The trouble is that selecting the entry in the ComboBox does not store the values into the string so that I can call them later in the installation. Could someone tell me what I'm doing wrong?
procedure ComboChange(Sender: TObject);
and
ComboBox.OnChange := #ComboChange;
are not required at all. To access the stored value from the ComboBox, simply read ComboBox.Text. Thanks #TLama.
First ComboChange should be a TNotifyEvent. #ComboChange is a pointer. It shouldn't compile unless TNewComboBox redefines it as a pointer, which it doesn't having checked the inno help.
which may be why it's not working.
as pointer :) This (when it's an event):
procedure ComboChange(Sender: TObject);
begin
case ComboBox.ItemIndex of
0:
begin
strComboValue := 'First Entry';
end;
1:
begin
strComboValue := 'Second Entry';
end;
...
end;
end;
Is going to get extremely tedious.
All you need is:
procedure ComboChange(Sender: TObject);
begin
strComboValue := 'Whatever is default';
if ComboBox.ItemIndex >= 0 then
strComboValue := ComboBox.Items[ComboBox.ItemIndex];
end;

Show text during installation process with inno-setup

I am searching for a possibility to show a user a note on how to proceed during install of a redistributable.
Background:
I have some Components that require 3rd party installations which are quit complex. If a user selects one of these components a message box with instructions are shown. After this box the resistributables are executet via exec/shellExec with 'ewWaitUntilTerminated'. Unfortunately the user cannot see the box during installation.
I tried to just open the notes in notepad and use 'ewNoWait', but than it will not close automatically after each installation of the redistributalbe. The user can chose more than one of these components and this help should only be visible during the specific installation. To kill the notepad with taskkill is not an option, it could kill opened notepad from the user.
Is there any elegant way to get such behaviour?
Create TOutputProgressWizardPage with function http://www.jrsoftware.org/ishelp/index.php?topic=isxfunc_createoutputprogresspage
function CreateOutputProgressPage(const ACaption, ADescription: String): TOutputProgressWizardPage;
[Code]
var
ProgressPage: TOutputProgressWizardPage;
procedure InitializeWizard;
begin
ProgressPage := CreateOutputProgressPage('Finalization of installation','');
end;
procedure CurPageChanged(CurPageID: Integer);
var
I: Integer;
begin
// Page is shown after installation when Finish page is shown
if CurPageID = wpFinish then begin
ProgressPage.SetText('Installing some 3rd party stuff...', '');
ProgressPage.SetProgress(0, 0);
ProgressPage.Show;
try
// Use exec/shellExec here to execute 3rd party app
// Also you can adjust progress barr position here:
for I := 0 to 10 do begin
ProgressPage.SetProgress(I, 10);
Sleep(100);
end;
finally
ProgressPage.Hide;
end;
end else
Result := True;
end;
I finally found a solution for my problem. On the basis of the answer of Slappy I used a std MsgPage and resize it for my need. Thx to TLama for his answer in a different topic for the resize code!
[Code]
var
RedistPage: TOutputMsgWizardPage;
DefaultTop,
DefaultLeft,
DefaultHeight,
DefaultBackTop,
DefaultNextTop,
DefaultCancelTop,
DefaultBevelTop,
DefaultBeveledLabelTop,
DefaultInnerHeight,
DefaultOuterHeight: Integer;
procedure InitializeWizard();
var
ReadMe: AnsiString;
begin
DefaultTop := WizardForm.Top;
DefaultLeft := WizardForm.Left;
DefaultHeight := WizardForm.Height;
DefaultBackTop := WizardForm.BackButton.Top;
DefaultNextTop := WizardForm.NextButton.Top;
DefaultCancelTop := WizardForm.CancelButton.Top;
DefaultBevelTop := WizardForm.Bevel.Top;
DefaultBeveledLabelTop := WizardForm.BeveledLabel.Top;
DefaultOuterHeight := WizardForm.OuterNotebook.Height;
DefaultInnerHeight := WizardForm.InnerNotebook.Height;
// save the contents of Readme.txt (non Unicode) in a string and build custom page
try
ExtractTemporaryFiles('{tmp}\readme.txt');
if LoadStringFromFile(ExpandConstant('{tmp}\readme.txt'), ReadMe) then
RedistPage := CreateOutputMsgPage(wpReady,
'Information', 'Please read the following important information about the installation before continuing.',ReadMe);
except
ShowExceptionMessage;
end;
function ShouldSkipPage(PageID: Integer): Boolean;
begin
Result := False; // initialize result to not skip any page (not necessary, but safer)
if PageID = RedistPage.ID then // if the page that is asked to be skipped is your custom page, then...
Result := not IsTaskSelected('dexela_API'); // if the task is not selected, skip the page
end;
procedure ChangePageSize(HeightOffset: Integer);
begin
WizardForm.Top := DefaultTop - (HeightOffset - DefaultHeight) div 2;
WizardForm.Height := WizardForm.Height + (HeightOffset - DefaultHeight);
WizardForm.InnerPage.Height := WizardForm.InnerPage.Height + (HeightOffset - DefaultHeight);
WizardForm.InnerNotebook.Height := WizardForm.InnerNotebook.Height + (HeightOffset - DefaultHeight);
WizardForm.OuterNotebook.Height := WizardForm.OuterNotebook.Height + (HeightOffset - DefaultHeight);
WizardForm.CancelButton.Top := DefaultCancelTop + (HeightOffset - DefaultHeight);
WizardForm.NextButton.Top := DefaultNextTop + (HeightOffset - DefaultHeight);
WizardForm.BackButton.Top := DefaultBackTop + (HeightOffset - DefaultHeight);
WizardForm.Bevel.Top := DefaultBevelTop + (HeightOffset - DefaultHeight);
end;
procedure CurPageChanged(CurPageID: Integer);
var
ComponentsPageTextHeight: Integer;
begin
if (CurPageID = RedistPage.ID) and (IsTaskSelected('dexela_API'))then begin
ChangePageSize(650);
//Sleep(2000); // time for the user to recognize the text, before it is hidden by installer
// Extract all Dexela files and launch them.
try
ExtractTemporaryFiles('{tmp}\Setup.msi');
except
ShowExceptionMessage;
end;
ShellExec('',ExpandConstant('{tmp}\Setup.msi'), '', '',SW_SHOW, ewWaitUntilTerminated, ResultCode);
end;
end;

Exporting data from a DBGrid to Excel

I wanted to know if anyone ones a way that I can export data from a DBGrid to Excel ? I am using Delphi 7 , Excel 2007 and ADO .
Any help will be appreciated.
If you want a fast export of raw data, just export your recordset (ADODataset.recordset) with something like that:
procedure ExportRecordsetToMSExcel(DestName: string; Data: _Recordset);
var
ovExcelApp: OleVariant;
ovExcelWorkbook: OleVariant;
ovWS: OleVariant;
ovRange: OleVariant;
begin
ovExcelApp := CreateOleObject('Excel.Application'); //If Excel isnt installed will raise an exception
try
ovExcelWorkbook := ovExcelApp.WorkBooks.Add;
ovWS := ovExcelWorkbook.Worksheets.Item[1]; // go to first worksheet
ovWS.Activate;
ovWS.Select;
ovRange := ovWS.Range['A1', 'A1']; //go to first cell
ovRange.Resize[Data.RecordCount, Data.Fields.Count];
ovRange.CopyFromRecordset(Data, Data.RecordCount, Data.Fields.Count); //this copy the entire recordset to the selected range in excel
ovWS.SaveAs(DestName, 1, '', '', False, False);
finally
ovExcelWorkbook.Close(SaveChanges := False);
ovWS := Unassigned;
ovExcelWorkbook := Unassigned;
ovExcelApp := Unassigned;
end;
end;
It is working by using Tfilestream component
procedure TForm2.ExportdatatoexcelClick(Sender: TObject);
var
Stream: TFileStream;
i: Integer;
OutLine,f: string;
sTemp,s: string;
begin
Stream := TFileStream.Create('D:\Yogesh Delphi\employee1.csv', fmCreate);
try
s := string(adotable1.Fields[0].FieldName);
for I := 1 to adotable1.FieldCount - 1 do
begin
s:= s+ ',' + string(adotable1.Fields[I].FieldName);
end;
s:= s+ #13#10;
stream.Write(s[1], Length(s) * SizeOf(Char));
{S := '';
for I := 0 to adotable1.FieldCount - 1 do
begin
S := (adotable1.Fields[I].FieldName);
outline := OutLine+S + ' ,';
end; }
while not adotable1.Eof do
begin
// You'll need to add your special handling here where OutLine is built
s:='';
OutLine := '';
for i := 0 to adotable1.FieldCount - 1 do
begin
sTemp := adotable1.Fields[i].AsString;
// Special handling to sTemp here
OutLine := OutLine + sTemp +',';
end;
// Remove final unnecessary ','
SetLength(OutLine, Length(OutLine) - 1);
// Write line to file
Stream.Write(OutLine[1], Length(OutLine) * SizeOf(Char));
// Write line ending
Stream.Write(sLineBreak, Length(sLineBreak));
adotable1.Next;
end;
finally
Stream.Free; // Saves the file
end;
showmessage('Records Successfully Exported.') ;
end;
{Yog}

Resources