3

I'm trying to write some Pascal script for a installer I'm making with Inno Setup Compiler 5.5.1. I'm currently trying to add a custom wizard page that executes a command, taking user input from text fields (TEdit components). I defined the NextButtonClick function, and it checks that the Page ID is the custom page I defined and attempts to retrieve the user input from the field. When I get it from the components of the Page's Surface property, it gets returned as a TComponent. To get the next I need to cast it to a TEdit, so I tried casting it and it seems to be returning nil. Besides the scripting for Inno I've been doing for the past few days, I don't have much experience with Pascal, so I could possibly be doing something wrong. But I'd appreciate the help!

Here's the chunk of code giving me an issue for reference (with debugging lines left in):

function NextButtonClick(CurPageID: Integer): Boolean;
var
    ResultCode: Integer;
    CurrPage: TWizardPage;
    Server : TComponent;
    Server2: TEdit;
    SurfacePage : TNewNotebookPage;
    ServerStr : String;
begin
    if CurPageID = 100 then
    begin
      CurrPage := PageFromID(100);
      SurfacePage := CurrPage.Surface;
      Server := SurfacePage.Controls[0];
      Server2 := TEdit(Server);  // RETURNS NIL HERE
      if Server2 = nil then
        MsgBox('', mbInformation, MB_OK);
      ServerStr := Server2.Text;
      MsgBox(ServerStr, mbInformation, MB_OK);
      //ShellExec('', 'sqlcmd', '-S ' + ServerStr + ' -Q ":r setMemUsage.sql"', ExpandConstant('{app}') + '\sql', SW_SHOW, ewWaitUntilTerminated, ResultCode);

    end;
    Result := True;
end;
jtcramer
  • 107
  • 3
  • 9
  • Have you properly set the `Parent` of your edit control ? But even though it's not so good to rely on creation indexes (the `Controls[Index]` property). Better would be to store the object instance pointers for instance to an array. – TLama Jul 27 '12 at 13:57
  • 1
    To see how to store the object instance pointers look e.g. at [`this post`](http://stackoverflow.com/a/10392094/960757), at the `SerialEdits` variable. Then you'll just remember that at index 0 is the edit for host name, index 1 for something else etc. A disadvantage of the `Controls` property is that when you create a label as the first component on your page surface e.g., that label will be accessible at index 0 in the `Controls` property and in that case your casting to edit control won't work. I can write you a sample code if you want to, but I have to leave for about 2 hours now. – TLama Jul 27 '12 at 14:25

1 Answers1

1

I can't simulate your problem. I've used this minimalistic code:

[Code]
var
  CustomPageID: Integer;

procedure InitializeWizard;
var
  EditBox: TEdit;
  CustomPage: TWizardPage;
begin
  CustomPage := CreateCustomPage(wpWelcome, '', '');
  CustomPageID := CustomPage.ID;
  EditBox := TEdit.Create(WizardForm);
  EditBox.Parent := CustomPage.Surface;
end;

procedure CurPageChanged(CurPageID: Integer);
var
  EditBox: TEdit;
  Component: TComponent;
  CustomPage: TWizardPage;
begin
  if (CurPageID = CustomPageID) then
  begin
    CustomPage := PageFromID(CustomPageID);
    Component := CustomPage.Surface.Controls[0];
    if (Component is TEdit) then
    begin
      MsgBox('Controls[0] is assigned and is TEdit', mbInformation, MB_OK);
      EditBox := TEdit(Component);
      EditBox.Text := 'Hi, I''m just a modified edit text!';
    end;
  end;
end;
TLama
  • 75,147
  • 17
  • 214
  • 392
  • I made some changes basing them off of your example here, and I got it to work! Thank you! I'm not quite sure why it works now though, is it because of the testing for Component is TEdit? I made sure beforehand that the Component I got was, in fact, the TEdit box I was trying to get, and it was not nil until I casted it from a TComponent to a TEdit. – jtcramer Jul 27 '12 at 15:13
  • You're welcome! The `is` operator is used to make sure the object instance is of a certain type. It's safer to ask if some object is the type of what you're going to use before you actually typecast it. But your problem was probably somewhere else. But still consider to use e.g. array to store your object instance pointers instead of using `Controls` property due to disadvantage I've described in my comment above. – TLama Jul 27 '12 at 17:12
  • 1
    Another useful trick is to set `EditBox.Name := 'Something';` when you create the control, then you can use `EditBox := TEdit(CustomPage.FindComponent('Something'));` later to retrieve it. But probably the biggest reason why TLama's solution works is that the page id is being saved and reused instead of being hard-coded. You should always try to avoid hard-coding any of these sorts of things. – Miral Jul 28 '12 at 02:55
  • @Miral, about component naming; yet another option might be to use the mentioned array and for indexes declare constants like `EC_HOST = 0; EC_PORT = 1;` where the `EC` means Edit Control. A string search using `FindComponent` is not efficient as array can be, but have to say that here it won't be critical I guess. – TLama Jul 28 '12 at 03:15