Как выполнить 7zip без блокировки интерфейса InnoSetup?

Мой графический интерфейс InnoSetup зависает при распаковке.

У меня естьprocedure DoUnzip(source: String; targetdir: String) с ядром

unzipTool := ExpandConstant('{tmp}\7za.exe');

Exec(unzipTool, ' x "' + source + '" -o"' + targetdir + '" -y',
     '', SW_HIDE, ewWaitUntilTerminated, ReturnCode);

Эта процедура вызывается несколько раз иExec Операция блокирует пользовательский интерфейс. Между исполнениями существует очень короткий момент, когда графический интерфейс Inno можно перетаскивать или перемещать.

Я знаю, что есть и другие вариантыTExecWait вместоewWaitUntilTerminated, лайкewNoWait а такжеewWaitUntilIdle, но, к сожалению, они не помогают в этом случае. С помощьюewNoWait приведет к выполнению нескольких операций распаковки одновременно.

Я ищу способ выполнить внешнюю операцию распаковки и дождаться ее завершения, но без блокировки пользовательского интерфейса. Как я могу это реализовать?

Вот мои заметки и идеи:

Ожидание завершения процесса - это блокировка, если только вы не будете ждать в потоке, отличном от основного. Я думаю, что нужен какой-то обратный вызов, который выполняется, когда операция распаковки заканчивается.

Я знаю, что InnoSetup не предоставляет эту функцию из коробки, смотритеhttps://github.com/jrsoftware/issrc/issues/149

При поиске связанных проблем в StackOverflow я задал вопросИспользование обратного вызова для отображения имен файлов из внешней библиотеки декомпрессии (Inno Setup)где я нашелОтвет Миралса, Он использует InnoCallback в сочетании с другой DLL.

Я думаю, в моем случае это может быть7zxa.dll для распаковки. Но он не принимает обратный вызов. Итак, следующий код является просто концепцией / идеей проекта. Одна проблема в том, что7zxa.dll не принимает звонок Другая проблема заключается в том, что API 7zxa не очень подходит для работы.

[Code]
type 
    TMyCallback = procedure(Filename: PChar);

// wrapper to tell callback function to InnoCallback
function WrapMyCallback(Callback: TMyCallback; ParamCount: Integer): LongWord;
  external 'WrapCallback@files:innocallback.dll stdcall';

// the call to the unzip dll
// P!: the 7zxa.dll doesn't accept a callback 
procedure DoUnzipDll(Blah: Integer; Foo: String; ...; Callback: LongWord);
  external 'DoUnzipDll@files:7zxa.dll stdcall';

// the actual callback action
procedure MyCallback(Filename: PChar);
begin
    // refresh the GUI
end;

//-----

var Callback : LongWord;

// tell innocallback the callback procedure as 1 parameter
Callback := WrapMyCallback(@MyCallback, 1); 

// pass the wrapped callback to the unzip DLL 
DoUnzipDll(source, target, ..., Callback);

procedure DoUnzip(src, target : String);
begin
  DoUnzipDll(ExpandConstant(src), ExpandConstant(target));
end;

Обновить

@Rik предложил объединить функцию WinAPI ShellExecuteEx () с INFINITE WaitForSingleObject.

Я реализовал и протестировал этот подход. Код ниже.

Распаковка работает, но окно InnoSetup можно перемещать / перетаскивать только на короткое время между отдельными операциями распаковки. Во время длительной разархивации графический интерфейс полностью не отвечает - не нужно перетаскивать или отменять кнопку. Я добавил BringToFrontAndRestore (), но кажется, что новый процесс имеет фокус.

const
  WAIT_OBJECT_0 = $0;
  WAIT_TIMEOUT = $00000102;
  SEE_MASK_NOCLOSEPROCESS = $00000040;
  INFINITE = $FFFFFFFF;     { Infinite timeout }

type
  TShellExecuteInfo = record
    cbSize: DWORD;
    fMask: Cardinal;
    Wnd: HWND;
    lpVerb: string;
    lpFile: string;
    lpParameters: string;
    lpDirectory: string;
    nShow: Integer;
    hInstApp: THandle;    
    lpIDList: DWORD;
    lpClass: string;
    hkeyClass: THandle;
    dwHotKey: DWORD;
    hMonitor: THandle;
    hProcess: THandle;
  end;

function ShellExecuteEx(var lpExecInfo: TShellExecuteInfo): BOOL; 
  external 'ShellExecuteEx{#AW}@shell32.dll stdcall';
function WaitForSingleObject(hHandle: THandle; dwMilliseconds: DWORD): DWORD; 
  external '[email protected] stdcall';
function CloseHandle(hObject: THandle): BOOL; external '[email protected] stdcall';

procedure DoUnzip(source: String; targetdir: String);
var
  unzipTool, unzipParams : String;     // path to unzip util
  ReturnCode  : Integer;  // errorcode
  ExecInfo: TShellExecuteInfo;
begin
    // source might contain {tmp} or {app} constant, so expand/resolve it to path name
    source := ExpandConstant(source);

    unzipTool := ExpandConstant('{tmp}\7za.exe');
    unzipParams := ' x "' + source + '" -o"' + targetdir + '" -y';

    ExecInfo.cbSize := SizeOf(ExecInfo);
    ExecInfo.fMask := SEE_MASK_NOCLOSEPROCESS;
    ExecInfo.Wnd := 0;
    ExecInfo.lpFile := unzipTool;
    ExecInfo.lpParameters := unzipParams;
    ExecInfo.nShow := SW_HIDE;

    if not FileExists(unzipTool)
    then MsgBox('UnzipTool not found: ' + unzipTool, mbError, MB_OK)
    else if not FileExists(source)
    then MsgBox('File was not found while trying to unzip: ' + source, mbError, MB_OK)
    else begin 

          // ShellExecuteEx combined with INFINITE WaitForSingleObject   

          if ShellExecuteEx(ExecInfo) then
          begin
            while WaitForSingleObject(ExecInfo.hProcess, INFINITE) <> WAIT_OBJECT_0
            do begin
                InstallPage.Surface.Update;          
                //BringToFrontAndRestore;
                WizardForm.Refresh();
            end;
          CloseHandle(ExecInfo.hProcess);
          end; 

    end;
end;

Ответы на вопрос(1)

Ваш ответ на вопрос