GUI InnoSetup của tôi bị đóng băng trong quá trình giải nén.Làm cách nào để thực thi 7zip mà không chặn giao diện người dùng InnoSetup?
Tôi đã một procedure DoUnzip(source: String; targetdir: String)
với cốt lõi
unzipTool := ExpandConstant('{tmp}\7za.exe');
Exec(unzipTool, ' x "' + source + '" -o"' + targetdir + '" -y',
'', SW_HIDE, ewWaitUntilTerminated, ReturnCode);
Thủ tục này được gọi là nhiều lần và các khối hoạt động Exec
giao diện người dùng. Chỉ có một khoảnh khắc rất ngắn giữa các lần thực thi, nơi mà giao diện đồ họa Inno có thể kéo/di chuyển được.
Tôi biết rằng có các tùy chọn khác cho TExecWait
thay vì ewWaitUntilTerminated
, như ewNoWait
và ewWaitUntilIdle
, nhưng tiếc là chúng không hữu ích trong trường hợp này. Sử dụng ewNoWait
sẽ dẫn đến việc thực thi nhiều thao tác giải nén cùng một lúc.
Tôi đang tìm cách thực hiện thao tác giải nén bên ngoài và đợi cho đến khi hoàn tất, nhưng không chặn giao diện người dùng. Làm thế nào tôi có thể thực hiện điều đó?
Dưới đây là ghi chú và ý tưởng của tôi:
Chờ đợi cho một quá trình để kết thúc, được ngăn chặn, trừ khi bạn sẽ được chờ đợi trong một chủ đề khác nhau từ một trong những chính. Tôi nghĩ rằng một số loại gọi lại là cần thiết, được thực hiện, khi các hoạt động giải nén kết thúc.
Tôi biết rằng InnoSetup không cung cấp tính năng này ra khỏi hộp, xem https://github.com/jrsoftware/issrc/issues/149
Trong khi tìm kiếm các vấn đề có liên quan trên StackOverflow, tôi đã đưa ra câu hỏi Using callback to display filenames from external decompression dll (Inno Setup), nơi tôi tìm thấy Mirals's answer. Nó sử dụng InnoCallback kết hợp với một DLL.
Tôi nghĩ, trong trường hợp của tôi, điều này có thể là 7zxa.dll
cho hoạt động giải nén. Nhưng nó không chấp nhận gọi lại. Vì vậy, đoạn mã sau chỉ là một bản thảo khái niệm/ý tưởng. Một vấn đề là, 7zxa.dll
không chấp nhận gọi lại. Một vấn đề khác là API 7zxa không thực sự mời làm việc.
[Code]
type
TMyCallback = procedure(Filename: PChar);
// wrapper to tell callback function to InnoCallback
function WrapMyCallback(Callback: TMyCallback; ParamCount: Integer): LongWord;
external '[email protected]: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 '[email protected]: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;
Cập nhật
@Rik gợi ý để kết hợp các chức năng WinAPI ShellExecuteEx() với INFINITE WaitForSingleObject.
Tôi đã triển khai và thử nghiệm phương pháp này. Mã dưới đây.
Công việc giải nén, nhưng cửa sổ InnoSetup chỉ có thể di chuyển/kéo được trong một khoảng thời gian ngắn giữa các thao tác giải nén riêng lẻ. Trong quá trình giải nén dài, GUI sẽ hoàn toàn không phản hồi - không có nút kéo/không hủy. Tôi đã thêm BringToFrontAndRestore(), nhưng có vẻ như quy trình mới có tiêu điểm.
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;
Tôi có thể là cách tắt cơ sở ở đây nhưng có thể bạn không chỉ cần sử dụng 'shellexecuteex' và 'WaitForSingleObject' với thời gian chờ INFINITE? Nếu nó vẫn chặn với thời gian chờ INFINITE, bạn có thể sử dụng thời gian chờ nhỏ hơn và lặp lại cho đến khi quá trình kết thúc. Xem [câu trả lời ở đây] (http://stackoverflow.com/a/10910780/1037511). – Rik
Đó là một ý tưởng thú vị. Cảm ơn bạn! Tôi sẽ kiểm tra cách tiếp cận đó và báo cáo lại. –
@Rik Tôi đã triển khai ý tưởng của bạn và thêm mã cho nó vào câu hỏi của tôi. Cửa sổ sẽ chỉ đáp ứng cho thời gian ngắn giữa nhiều thao tác giải nén. Trong quá trình giải nén dài, giao diện Inno vẫn bị đóng băng. –