3

I have an Inno Setup install that execute some time consuming 'AfterInstall' action. And while this action is executed, install GUI is completely frozen (seems it's main event loop is not processed). This is not a pleasant end user experience, so maybe it's somehow possible for this operation not to freeze GUI? Like perform it in separate thread or periodically call something like handleGuiEventLoop()?

For the "action", I'm calling my function from my .dll that makes a number of HTTP requests and writes down response as file into app install folder. This is not CPU intensive, but can take a few seconds. Or, if internet connection is weak / no internet connection available it can take a minute or two. And installer GUI is frozen all this time.

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992
grigoryvp
  • 40,413
  • 64
  • 174
  • 277

2 Answers2

6

The output progress pages are designed for providing feedback on longer running operations.

But in order for this to be effective you have to be able to keep Inno updated on your current progress by periodically calling methods on this page.

There is a library that will let you pass an Inno script function as a callback to a DLL, which may be of use. You may also want to look at using the ITDownload script from the same site, which lets you do HTTP access from within Inno itself, avoiding the middle-man.

However Inno is inherently single-threaded and GUI-thread-affine, so calling blocking operations directly will always block the UI without special provision. Running code from within a separate thread is possible (but only within a DLL, and you have to be very careful); other options include making asynchronous calls only, or calls which internally maintain GUI updates, such as Exec.

Miral
  • 12,637
  • 4
  • 53
  • 93
  • If i run my code in separate thread - how to tell main InnoSetup thread that it can continue GUI event loop? Is it some API like `processEvent` or i need to call `GetMessage` / `DispatchMessage` loop manually? – grigoryvp Jan 18 '13 at 13:40
  • 1
    You can call the Set* methods on the progress output page, as I said before. Another approach that might work, but is less preferred, is to call `WizardForm.Refresh` periodically. Or failing all else, you could try running a message loop, but bear in mind that Inno itself is not really designed to expect this. – Miral Jan 19 '13 at 09:23
3

There are two ways to improve the experience (they are different from an API point of view only, internally both do the same – they pump a Windows message queue):

  1. Use TOutputProgressWizardPage to present an operation progress. Its SetProgress method internally calls VCL TApplication.ProcessMessages, which pumps Windows message queue.

    Use CreateOutputProgressPage to create the page.

    Some examples:

  2. Explicitly pump the Windows message queue by calling WinAPI DispatchMessage.

    For some examples, see AppProcessMessage function in:


In both cases, you have to add calls that trigger the message queue pumping. Usually within some loop that does the processing.

In some cases, you won't be able to do that. For example, when you use a blocking call to an external application (e.g. using Exec or ShellExec) to do the processing. You can workaround that by scheduling a timer that will be regularly triggered, while the function is running.

This approach is used by some examples linked above, namely:

Martin Prikryl
  • 188,800
  • 56
  • 490
  • 992