https://git.reactos.org/?p=reactos.git;a=commitdiff;h=645c77335cb466750bb8a01ed87aab35a748ecbb

commit 645c77335cb466750bb8a01ed87aab35a748ecbb
Author:     Guntha <[email protected]>
AuthorDate: Fri Feb 7 19:21:21 2020 +0100
Commit:     GitHub <[email protected]>
CommitDate: Fri Feb 7 19:21:21 2020 +0100

    [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
---
 dll/shellext/zipfldr/CZipExtract.cpp | 97 ++++++++++++++++++++++++++++++++++--
 1 file changed, 92 insertions(+), 5 deletions(-)

diff --git a/dll/shellext/zipfldr/CZipExtract.cpp 
b/dll/shellext/zipfldr/CZipExtract.cpp
index d3b3756f34e..66ac782e425 100644
--- a/dll/shellext/zipfldr/CZipExtract.cpp
+++ b/dll/shellext/zipfldr/CZipExtract.cpp
@@ -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,20 +130,64 @@ 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;
+        }
 
-                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 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)

Reply via email to