call for testing of latest setup.exe snapshot

Igor Peshansky pechtcha@cs.nyu.edu
Wed Aug 16 04:39:00 GMT 2006


On Tue, 8 Aug 2006, Igor Peshansky wrote:

> On Sun, 6 Aug 2006, Brian Dessent wrote:
>
> [snip]
> > http://cygwin.com/ml/cygwin-apps/2006-02/msg00099.html
> >   So, we have a couple of issues here.  Firstly, the bug/unintended
> >   feature of -r causing the infinite retries until the file can be
> >   written (if I understand correctly.)  Second the patch by Igor that
> >   adds a dialog when trying to replace an in-use file.
>
> Right.
>
> >   Here is my opinion on the matter: I like the dialog idea, but I don't
> >   think having "Abort" as an option is appropriate, as it will
> >   potentially cause a really screwed up install, plus it was left
> >   unimplemented in the patch submitted.  So let's just have two
> >   options: "Retry" and "Replace on Reboot".  I know that this means we
> >   can't use the stock "Abort/Retry/Ignore" dialog but I think it's
> >   worth it for clarity.
>
> Agreed on all points.  However, there is a technical issue here.  Stock
> MessageBoxes come in many flavors -- there actually is a Retry/Cancel box.
> There is no Retry/Continue stock box, unfortunately.
>
> We can use the Retry/Cancel one, and perhaps play some games with the
> WNDPROC of the MessageBox class to make "Cancel" look like "Replace on
> reboot" (or "Continue", which I like better -- we can explain in the
> MessageBox text that pressing "Continue" will require a reboot later), but
> I'm not sure it'll work, and it'll be ugly.  Plus, I don't know that much
> about the WNDPROC, so it'll take me a bit of time to get something like
> this working.
>
> OTOH, I can change my patch to use the Retry/Cancel box today, and add the
> following to the text: "Pressing 'Cancel' will cause setup to use Windows
> mechanisms for replacing in-use files.  It will be necessary for you to
> reboot after setup completes."  I know, the label "Cancel" is evocative of
> aborting the whole installation, but this functionality is so useful, IMO,
> that I, for one, would put up with a little annoyance of a wrong label.
> Changing the label in a way I've described above could be a later
> enhancement.

Well, lo and behold, I overreacted.  It turned out to be much easier than
I anticipated, so attached is the patch with the Retry/Continue message
box.

> > http://cygwin.com/ml/cygwin-apps/2006-01/msg00204.html
> [snip]
> However, there was another issue in that thread (the inline patch).  It
> seems that applying that will cause the code to be simpler, but I'm afraid
> there's some little issue I'm missing.

I still have that one in my private sandbox, and had it in my running copy
of setup for a while with no observed problems.  Any opinions?

> > If we can finish off the "Retry/Replace" file-in-use thing and assuming
> > there are no reports of new issues with this snapshot then I think we
> > can push out a release.
>
> Sounds good.  If people are fine with the Retry/Cancel box, I can have a
> new patch by the end of this week.

So my weeks end on Wednesdays... :-)  Since this change involved indenting
an 80-line chunk of code, I'm also attaching a whitespace-indifferent
patch for ease of reviewing.  ChangeLog is below.
	Igor
==============================================================================
2006-08-16  Igor Peshansky  <pechtcha@cs.nyu.edu>

	* install.cc (Installer::installOne): If file is in use, ask the user
	to stop processes and retry.
	(MB_RETRYCONTINUE, IDCONTINUE): New macros.
	(hMsgBoxHook): New static field.
	(CBTProc): New window hook function.
	(_custom_MessageBox): New function.
	* CHANGES: Update with the above.

-- 
				http://cs.nyu.edu/~pechtcha/
      |\      _,,,---,,_	    pechtcha@cs.nyu.edu | igor@watson.ibm.com
ZZZzz /,`.-'`'    -.  ;-;;,_		Igor Peshansky, Ph.D. (name changed!)
     |,4-  ) )-,_. ,\ (  `'-'		old name: Igor Pechtchanski
    '---''(_/--'  `-'\_) fL	a.k.a JaguaR-R-R-r-r-r-.-.-.  Meow!

"Las! je suis sot... -Mais non, tu ne l'es pas, puisque tu t'en rends compte."
"But no -- you are no fool; you call yourself a fool, there's proof enough in
that!" -- Rostand, "Cyrano de Bergerac"
-------------- next part --------------
Index: CHANGES
===================================================================
RCS file: /cvs/cygwin-apps/setup/CHANGES,v
retrieving revision 1.13
diff -u -p -r1.13 CHANGES
--- CHANGES	13 Jun 2006 14:00:23 -0000	1.13
+++ CHANGES	16 Aug 2006 03:56:05 -0000
@@ -3,6 +3,8 @@ Note: For easier maintenance try to keep
 
 Version 2.548 (HEAD)
  
+ - Allow interactively retrying to replace open files.
+
  - Fix unreadable chooser page due to bad background colour problem.
 
  - Make categories named with an initial "." default to expanded display.
Index: install.cc
===================================================================
RCS file: /cvs/cygwin-apps/setup/install.cc,v
retrieving revision 2.78
diff -u -p -r2.78 install.cc
--- install.cc	16 Apr 2006 15:37:49 -0000	2.78
+++ install.cc	16 Aug 2006 03:56:06 -0000
@@ -178,6 +178,44 @@ Installer::replaceOnRebootSucceeded (con
   rebootneeded = true;
 }
 
+#define MB_RETRYCONTINUE 7
+#if !defined(IDCONTINUE)
+#define IDCONTINUE IDCANCEL
+#endif
+
+static HHOOK hMsgBoxHook;
+LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
+  HWND hWnd;
+  switch (nCode) {
+    case HCBT_ACTIVATE:
+      hWnd = (HWND)wParam;
+      if (GetDlgItem(hWnd, IDCANCEL) != NULL)
+         SetDlgItemText(hWnd, IDCANCEL, "Continue");
+      UnhookWindowsHookEx(hMsgBoxHook);
+  }
+  return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam);
+}
+
+int _custom_MessageBox(HWND hWnd, LPCTSTR szText, LPCTSTR szCaption, UINT uType) {
+  int retval;
+  bool retry_continue = (uType & MB_TYPEMASK) == MB_RETRYCONTINUE;
+  if (retry_continue) {
+    uType &= ~MB_TYPEMASK; uType |= MB_RETRYCANCEL;
+    // Install a window hook, so we can intercept the message-box
+    // creation, and customize it
+    // Only install for THIS thread!!!
+    hMsgBoxHook = SetWindowsHookEx(WH_CBT, CBTProc, NULL, GetCurrentThreadId());
+  }
+  retval = MessageBox(hWnd, szText, szCaption, uType);
+  // Intercept the return value for less confusing results
+  if (retry_continue && retval == IDCANCEL)
+    return IDCONTINUE;
+  return retval;
+}
+
+#undef MessageBox
+#define MessageBox _custom_MessageBox
+
 /* install one source at a given prefix. */
 void
 Installer::installOne (packagemeta &pkgm, const packageversion &ver,
@@ -205,6 +243,7 @@ Installer::installOne (packagemeta &pkgm
   io_stream *tmp = io_stream::open (source.Cached (), "rb");
   io_stream *tmp2 = 0;
   archive *thefile = 0;
+  bool ignoreExtractErrors = unattended_mode;
   if (tmp)
     {
       tmp2 = compress::decompress (tmp);
@@ -253,74 +292,101 @@ Installer::installOne (packagemeta &pkgm
 	  Progress.SetText3 (canonicalfn.c_str());
 	  log (LOG_BABBLE) << "Installing file " << prefixURL << prefixPath 
             << fn << endLog;
-	  if (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
+	  bool firstIteration = true;
+	  while (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
 	    {
-	      if (NoReplaceOnReboot)
+	      if (!ignoreExtractErrors)
 		{
-		  ++errors;
-                  error_in_this_package = true;
-		  log (LOG_PLAIN) << "Not replacing in-use file "
-                    << prefixURL << prefixPath << fn << endLog;
+		  char msg[fn.size() + 300];
+		  sprintf (msg,
+			   "%snable to extract /%s -- the file is in use.\r\n"
+			   "Please stop %s Cygwin processes and select \"Retry\", or\r\n"
+			   "select \"Continue\" to go on anyway (you will need to reboot).\r\n",
+			   firstIteration?"U":"Still u", fn.c_str(), firstIteration?"all":"ALL");
+		  switch (MessageBox
+			  (NULL, msg, "In-use files detected",
+			   MB_RETRYCONTINUE | MB_ICONWARNING | MB_TASKMODAL))
+		    {
+		    case IDRETRY:
+		      // retry
+		      firstIteration = false;
+		      continue;
+		    case IDCONTINUE:
+		      ignoreExtractErrors = true;
+		      break;
+		    default:
+		      break;
+		    }
+		  // fall through to previous functionality
 		}
-	      else
-	      //extract to temp location
-	      if (archive::extract_file (thefile, prefixURL, prefixPath, ".new") != 0)
+	      if (NoReplaceOnReboot)
 		{
-		  log (LOG_PLAIN) << "Unable to install file "
-                    << prefixURL << prefixPath << fn << endLog;
 		  ++errors;
-                  error_in_this_package = true;
+		  error_in_this_package = true;
+		  log (LOG_PLAIN) << "Not replacing in-use file "
+		    << prefixURL << prefixPath << fn << endLog;
 		}
 	      else
-		{
-                  if (!IsWindowsNT())
-		    {
-		      /* Get the short file names */
-		      char source[MAX_PATH];
-		      unsigned int len =
-                        GetShortPathName(cygpath("/" + fn + ".new").c_str(),
-                                         source, MAX_PATH);
-		      if (!len || len > MAX_PATH)
-			{
-			  replaceOnRebootFailed(fn);
-			}
-		      else
-			{
-			  char dest[MAX_PATH];
-			  len =
-                            GetShortPathName(cygpath("/" + fn).c_str(),
-                                             dest, MAX_PATH);
-			  if (!len || len > MAX_PATH)
-			      replaceOnRebootFailed (fn);
-			  else
-			    /* trigger a replacement on reboot */
-			  if (!WritePrivateProfileString
-				("rename", dest, source, "WININIT.INI"))
-			      replaceOnRebootFailed (fn);
-			  else
-			      replaceOnRebootSucceeded (fn, rebootneeded);
-			}
-		    }
-                  else
-                    {
-		      /* XXX FIXME: prefix may not be / for in use files -
-		       * although it most likely is
-		       * - we need a io method to get win32 paths 
-		       * or to wrap this system call
-		       */
-                      if (!MoveFileEx(cygpath("/" + fn + ".new").c_str(),
-                                      cygpath("/" + fn).c_str(),
-                                      MOVEFILE_DELAY_UNTIL_REBOOT |
-                                      MOVEFILE_REPLACE_EXISTING))
-			{
-			  replaceOnRebootFailed (fn);
-			}
-		      else
-			{
-			  replaceOnRebootSucceeded (fn, rebootneeded);
-			}
-		    }
-		}
+		//extract to temp location
+		if (archive::extract_file (thefile, prefixURL, prefixPath, ".new") != 0)
+		  {
+		    log (LOG_PLAIN) << "Unable to install file "
+		      << prefixURL << prefixPath << fn << endLog;
+		    ++errors;
+		    error_in_this_package = true;
+		  }
+		else
+		  {
+		    if (!IsWindowsNT())
+		      {
+			/* Get the short file names */
+			char source[MAX_PATH];
+			unsigned int len =
+			  GetShortPathName(cygpath("/" + fn + ".new").c_str(),
+					   source, MAX_PATH);
+			if (!len || len > MAX_PATH)
+			  {
+			    replaceOnRebootFailed(fn);
+			  }
+			else
+			  {
+			    char dest[MAX_PATH];
+			    len =
+			      GetShortPathName(cygpath("/" + fn).c_str(),
+					       dest, MAX_PATH);
+			    if (!len || len > MAX_PATH)
+				replaceOnRebootFailed (fn);
+			    else
+			      /* trigger a replacement on reboot */
+			    if (!WritePrivateProfileString
+				  ("rename", dest, source, "WININIT.INI"))
+				replaceOnRebootFailed (fn);
+			    else
+				replaceOnRebootSucceeded (fn, rebootneeded);
+			  }
+		      }
+		    else
+		      {
+			/* XXX FIXME: prefix may not be / for in use files -
+			 * although it most likely is
+			 * - we need a io method to get win32 paths 
+			 * or to wrap this system call
+			 */
+			if (!MoveFileEx(cygpath("/" + fn + ".new").c_str(),
+					cygpath("/" + fn).c_str(),
+					MOVEFILE_DELAY_UNTIL_REBOOT |
+					MOVEFILE_REPLACE_EXISTING))
+			  {
+			    replaceOnRebootFailed (fn);
+			  }
+			else
+			  {
+			    replaceOnRebootSucceeded (fn, rebootneeded);
+			  }
+		      }
+		  }
+	      // We're done with this file
+	      break;
 	    }
 	  progress (tmp->tell ());
 	  num_installs++;
-------------- next part --------------
Index: CHANGES
===================================================================
RCS file: /cvs/cygwin-apps/setup/CHANGES,v
retrieving revision 1.13
diff -u -p -b -B -r1.13 CHANGES
--- CHANGES	13 Jun 2006 14:00:23 -0000	1.13
+++ CHANGES	16 Aug 2006 04:21:08 -0000
@@ -3,6 +3,8 @@ Note: For easier maintenance try to keep
 
 Version 2.548 (HEAD)
  
+ - Allow interactively retrying to replace open files.
+
  - Fix unreadable chooser page due to bad background colour problem.
 
  - Make categories named with an initial "." default to expanded display.
Index: install.cc
===================================================================
RCS file: /cvs/cygwin-apps/setup/install.cc,v
retrieving revision 2.78
diff -u -p -b -B -r2.78 install.cc
--- install.cc	16 Apr 2006 15:37:49 -0000	2.78
+++ install.cc	16 Aug 2006 04:21:09 -0000
@@ -178,6 +178,44 @@ Installer::replaceOnRebootSucceeded (con
   rebootneeded = true;
 }
 
+#define MB_RETRYCONTINUE 7
+#if !defined(IDCONTINUE)
+#define IDCONTINUE IDCANCEL
+#endif
+
+static HHOOK hMsgBoxHook;
+LRESULT CALLBACK CBTProc(int nCode, WPARAM wParam, LPARAM lParam) {
+  HWND hWnd;
+  switch (nCode) {
+    case HCBT_ACTIVATE:
+      hWnd = (HWND)wParam;
+      if (GetDlgItem(hWnd, IDCANCEL) != NULL)
+         SetDlgItemText(hWnd, IDCANCEL, "Continue");
+      UnhookWindowsHookEx(hMsgBoxHook);
+  }
+  return CallNextHookEx(hMsgBoxHook, nCode, wParam, lParam);
+}
+
+int _custom_MessageBox(HWND hWnd, LPCTSTR szText, LPCTSTR szCaption, UINT uType) {
+  int retval;
+  bool retry_continue = (uType & MB_TYPEMASK) == MB_RETRYCONTINUE;
+  if (retry_continue) {
+    uType &= ~MB_TYPEMASK; uType |= MB_RETRYCANCEL;
+    // Install a window hook, so we can intercept the message-box
+    // creation, and customize it
+    // Only install for THIS thread!!!
+    hMsgBoxHook = SetWindowsHookEx(WH_CBT, CBTProc, NULL, GetCurrentThreadId());
+  }
+  retval = MessageBox(hWnd, szText, szCaption, uType);
+  // Intercept the return value for less confusing results
+  if (retry_continue && retval == IDCANCEL)
+    return IDCONTINUE;
+  return retval;
+}
+
+#undef MessageBox
+#define MessageBox _custom_MessageBox
+
 /* install one source at a given prefix. */
 void
 Installer::installOne (packagemeta &pkgm, const packageversion &ver,
@@ -205,6 +243,7 @@ Installer::installOne (packagemeta &pkgm
   io_stream *tmp = io_stream::open (source.Cached (), "rb");
   io_stream *tmp2 = 0;
   archive *thefile = 0;
+  bool ignoreExtractErrors = unattended_mode;
   if (tmp)
     {
       tmp2 = compress::decompress (tmp);
@@ -253,8 +292,33 @@ Installer::installOne (packagemeta &pkgm
 	  Progress.SetText3 (canonicalfn.c_str());
 	  log (LOG_BABBLE) << "Installing file " << prefixURL << prefixPath 
             << fn << endLog;
-	  if (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
+	  bool firstIteration = true;
+	  while (archive::extract_file (thefile, prefixURL, prefixPath) != 0)
+	    {
+	      if (!ignoreExtractErrors)
+		{
+		  char msg[fn.size() + 300];
+		  sprintf (msg,
+			   "%snable to extract /%s -- the file is in use.\r\n"
+			   "Please stop %s Cygwin processes and select \"Retry\", or\r\n"
+			   "select \"Continue\" to go on anyway (you will need to reboot).\r\n",
+			   firstIteration?"U":"Still u", fn.c_str(), firstIteration?"all":"ALL");
+		  switch (MessageBox
+			  (NULL, msg, "In-use files detected",
+			   MB_RETRYCONTINUE | MB_ICONWARNING | MB_TASKMODAL))
 	    {
+		    case IDRETRY:
+		      // retry
+		      firstIteration = false;
+		      continue;
+		    case IDCONTINUE:
+		      ignoreExtractErrors = true;
+		      break;
+		    default:
+		      break;
+		    }
+		  // fall through to previous functionality
+		}
 	      if (NoReplaceOnReboot)
 		{
 		  ++errors;
@@ -321,6 +385,8 @@ Installer::installOne (packagemeta &pkgm
 			}
 		    }
 		}
+	      // We're done with this file
+	      break;
 	    }
 	  progress (tmp->tell ());
 	  num_installs++;


More information about the Cygwin-apps mailing list