Wie wird 7zip ausgeführt, ohne die InnoSetup-Benutzeroberfläche zu blockieren?

Meine InnoSetup-GUI ist während des Entpackens eingefroren.

Ich habe eineprocedure DoUnzip(source: String; targetdir: String) mit dem Kern

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

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

Diese Prozedur wird mehrmals aufgerufen und dasExecie @ -Operation blockiert die Benutzeroberfläche. Es gibt nur einen sehr kurzen Moment zwischen den Ausführungen, in dem die Inno-GUI zieh- / verschiebbar ist.

Ich weiß, dass es andere Optionen für @ giTExecWait Anstatt vonewWaitUntilTerminated, mögenewNoWait undewWaitUntilIdle, aber in diesem Fall sind sie leider nicht hilfreich. @ VerwendewNoWait würde dazu führen, dass mehrere Entpackungsvorgänge gleichzeitig ausgeführt werden.

Ich suche nach einer Möglichkeit, einen externen Dekomprimierungsvorgang auszuführen und zu warten, bis er abgeschlossen ist, ohne jedoch die Benutzeroberfläche zu blockieren. Wie kann ich das umsetzen?

Hier sind meine Notizen und Ideen:

Warten, bis ein Prozess abgeschlossen ist, wird blockiert, es sei denn, Sie warten in einem anderen Thread als dem Hauptthread. Ich denke, dass eine Art Rückruf benötigt wird, der ausgeführt wird, wenn der Entpackvorgang abgeschlossen ist.

Ich bin mir bewusst, dass InnoSetup diese Funktion nicht sofort zur Verfügung stellt. Siehehttps: //github.com/jrsoftware/issrc/issues/14

ei der Suche nach verwandten Themen zu StackOverflow stellte sich die FragVerwenden eines Rückrufs zum Anzeigen von Dateinamen aus der externen Dekomprimierungs-DLL (Inno Setup), wo ich @ gefunden ha Mirals Antwort. Es verwendet InnoCallback in Kombination mit einer anderen DLL.

Ich denke, in meinem Fall könnte dies sein7zxa.dll für den Entpackvorgang. Aber es akzeptiert keinen Rückruf. Der folgende Code ist also nur ein Konzept- / Ideenentwurf. Ein Problem ist, dass7zxa.dll akzeptiert keinen Rückruf. Ein weiteres Problem ist, dass die 7zxa-API nicht wirklich zum Arbeiten einlädt.

[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;

Aktualisiere

ik schlug vor, die WinAPI-Funktion ShellExecuteEx () mit INFINITE WaitForSingleObject zu kombiniere

Ich habe diesen Ansatz implementiert und getestet. Der Code ist unten.

Das Entpacken funktioniert, aber das InnoSetup-Fenster kann zwischen den einzelnen Entpackvorgängen nur für einen kurzen Moment verschoben / gezogen werden. Während eines langen Entpackens reagiert die GUI nicht mehr - kein Ziehen / Abbrechen. Ich habe BringToFrontAndRestore () hinzugefügt, aber es scheint, dass der neue Prozess den Fokus hat.

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;

Antworten auf die Frage(2)

Ihre Antwort auf die Frage