[ZIPFLDR] Extraction is now in its own thread (#2261)

Extraction happens now in its own thread, allowing for cancelling in the middle of extraction or moving the window during the extraction.

File extraction was previously happening in the main thread, freezing the whole window until the end. Now when the user clicks "Next", a new thread is created and the extraction starts from there, allowing to move the window during the extraction, or to cancel the extraction in the middle. (Note: after clicking "Cancel", extraction continues until the current file is extracted).

CORE-14934
This commit is contained in:
Guntha 2020-02-07 19:21:21 +01:00 committed by GitHub
parent b97977db9c
commit 645c77335c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23

View file

@ -70,12 +70,17 @@ public:
class CExtractSettingsPage : public CPropertyPageImpl<CExtractSettingsPage>
{
private:
HANDLE m_hExtractionThread;
bool m_bExtractionThreadCancel;
CZipExtract* m_pExtract;
CStringA* m_pPassword;
public:
CExtractSettingsPage(CZipExtract* extract, CStringA* password)
:CPropertyPageImpl<CExtractSettingsPage>(MAKEINTRESOURCE(IDS_WIZ_TITLE))
,m_hExtractionThread(NULL)
,m_bExtractionThreadCancel(false)
,m_pExtract(extract)
,m_pPassword(password)
{
@ -95,6 +100,28 @@ public:
int OnWizardNext()
{
if (m_hExtractionThread != NULL)
{
/* We enter here when extraction has finished, and go to next page if it succeeded */
WaitForSingleObject(m_hExtractionThread, INFINITE);
CloseHandle(m_hExtractionThread);
m_hExtractionThread = NULL;
m_pExtract->Release();
if (!m_bExtractionThreadCancel)
{
return 0;
}
else
{
SetWindowLongPtr(DWLP_MSGRESULT, -1);
return TRUE;
}
}
/* We end up here if the user manually clicks Next: start extraction */
m_bExtractionThreadCancel = false;
/* Grey out every control during extraction to prevent user interaction */
::EnableWindow(GetDlgItem(IDC_BROWSE), FALSE);
::EnableWindow(GetDlgItem(IDC_DIRECTORY), FALSE);
::EnableWindow(GetDlgItem(IDC_PASSWORD), FALSE);
@ -103,19 +130,63 @@ public:
if (m_pExtract->m_DirectoryChanged)
UpdateDirectory();
if (!m_pExtract->Extract(m_hWnd, GetDlgItem(IDC_PROGRESS)))
m_pExtract->AddRef();
m_hExtractionThread = CreateThread(NULL, 0,
&CExtractSettingsPage::ExtractEntry,
this,
0, NULL);
if (!m_hExtractionThread)
{
/* Extraction failed, do not go to the next page */
/* Extraction thread creation failed, do not go to the next page */
DWORD err = GetLastError();
DPRINT1("ERROR, m_hExtractionThread: CreateThread failed: 0x%x\n", err);
m_pExtract->Release();
SetWindowLongPtr(DWLP_MSGRESULT, -1);
::EnableWindow(GetDlgItem(IDC_BROWSE), TRUE);
::EnableWindow(GetDlgItem(IDC_DIRECTORY), TRUE);
::EnableWindow(GetDlgItem(IDC_PASSWORD), TRUE);
SetWizardButtons(PSWIZB_NEXT);
}
return TRUE;
}
static DWORD WINAPI ExtractEntry(LPVOID lpParam)
{
CExtractSettingsPage* pPage = (CExtractSettingsPage*)lpParam;
bool res = pPage->m_pExtract->Extract(pPage->m_hWnd, pPage->GetDlgItem(IDC_PROGRESS), &(pPage->m_bExtractionThreadCancel));
/* Failing and cancelling extraction both mean we stay on the same property page */
pPage->m_bExtractionThreadCancel = !res;
pPage->SetWizardButtons(PSWIZB_NEXT);
if (!res)
{
/* Extraction failed/cancelled: the page becomes interactive again */
::EnableWindow(pPage->GetDlgItem(IDC_BROWSE), TRUE);
::EnableWindow(pPage->GetDlgItem(IDC_DIRECTORY), TRUE);
::EnableWindow(pPage->GetDlgItem(IDC_PASSWORD), TRUE);
/* Reset the progress bar's appearance */
CWindow Progress(pPage->GetDlgItem(IDC_PROGRESS));
Progress.SendMessage(PBM_SETRANGE32, 0, 1);
Progress.SendMessage(PBM_SETPOS, 0, 0);
}
SendMessageCallback(pPage->GetParent().m_hWnd, PSM_PRESSBUTTON, PSBTN_NEXT, 0, NULL, NULL);
return 0;
}
BOOL OnQueryCancel()
{
if (m_hExtractionThread != NULL)
{
/* Extraction will check the value of m_bExtractionThreadCancel between each file in the archive */
m_bExtractionThreadCancel = true;
return TRUE;
}
return 0;
return FALSE;
}
struct browse_info
@ -268,7 +339,7 @@ public:
PropertySheetW(&psh);
}
bool Extract(HWND hDlg, HWND hProgress)
bool Extract(HWND hDlg, HWND hProgress, const bool* bCancel)
{
unz_global_info64 gi;
uf = unzOpen2_64(m_Filename.GetString(), &g_FFunc);
@ -301,6 +372,12 @@ public:
bool bOverwriteAll = false;
while (zipEnum.next(Name, Info))
{
if (*bCancel)
{
Close();
return false;
}
bool is_dir = Name.GetLength() > 0 && Name[Name.GetLength()-1] == '/';
char CombinedPath[MAX_PATH * 2] = { 0 };
@ -418,6 +495,16 @@ public:
do
{
if (*bCancel)
{
CloseHandle(hFile);
BOOL deleteResult = DeleteFileA(FullPath);
if (deleteResult == 0)
DPRINT1("ERROR, DeleteFileA: 0x%x\n", GetLastError());
Close();
return false;
}
err = unzReadCurrentFile(uf, Buffer, sizeof(Buffer));
if (err < 0)
@ -451,7 +538,7 @@ public:
LocalFileTimeToFileTime(&LocalFileTime, &FileTime);
SetFileTime(hFile, &FileTime, &LastAccessTime, &FileTime);
/* Done.. */
/* Done */
CloseHandle(hFile);
if (err)