patch 8.2.5118: MS-Windows: sending a message to another Vim may hang

Problem:    MS-Windows: sending a message to another Vim may hang if that Vim
            is halted.
Solution:   Add a timeout to serverSendToVim(). (Ken Takata, closes #10585)
diff --git a/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim b/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim
index d9877a0..5eda290 100644
--- a/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim
+++ b/runtime/pack/dist/opt/editexisting/plugin/editexisting.vim
@@ -1,6 +1,6 @@
 " Vim Plugin:	Edit the file with an existing Vim if possible
 " Maintainer:	Bram Moolenaar
-" Last Change:	2016 Mar 28
+" Last Change:	2022 Jun 17
 
 " To use add ":packadd! editexisting" in your vimrc file.
 
@@ -35,32 +35,36 @@
     endif
 
     " Check if this server is editing our file.
-    if remote_expr(servername, "bufloaded('" . fname_esc . "')")
-      " Yes, bring it to the foreground.
-      if has("win32")
-	call remote_foreground(servername)
-      endif
-      call remote_expr(servername, "foreground()")
-
-      if remote_expr(servername, "exists('*EditExisting')")
-	" Make sure the file is visible in a window (not hidden).
-	" If v:swapcommand exists and is set, send it to the server.
-	if exists("v:swapcommand")
-	  let c = substitute(v:swapcommand, "'", "''", "g")
-	  call remote_expr(servername, "EditExisting('" . fname_esc . "', '" . c . "')")
-	else
-	  call remote_expr(servername, "EditExisting('" . fname_esc . "', '')")
+    try
+      if remote_expr(servername, "bufloaded('" . fname_esc . "')")
+	" Yes, bring it to the foreground.
+	if has("win32")
+	  call remote_foreground(servername)
 	endif
-      endif
+	call remote_expr(servername, "foreground()")
 
-      if !(has('vim_starting') && has('gui_running') && has('gui_win32'))
-	" Tell the user what is happening.  Not when the GUI is starting
-	" though, it would result in a message box.
-	echomsg "File is being edited by " . servername
-	sleep 2
+	if remote_expr(servername, "exists('*EditExisting')")
+	  " Make sure the file is visible in a window (not hidden).
+	  " If v:swapcommand exists and is set, send it to the server.
+	  if exists("v:swapcommand")
+	    let c = substitute(v:swapcommand, "'", "''", "g")
+	    call remote_expr(servername, "EditExisting('" . fname_esc . "', '" . c . "')")
+	  else
+	    call remote_expr(servername, "EditExisting('" . fname_esc . "', '')")
+	  endif
+	endif
+
+	if !(has('vim_starting') && has('gui_running') && has('gui_win32'))
+	  " Tell the user what is happening.  Not when the GUI is starting
+	  " though, it would result in a message box.
+	  echomsg "File is being edited by " . servername
+	  sleep 2
+	endif
+	return 'q'
       endif
-      return 'q'
-    endif
+    catch /^Vim\%((\a\+)\)\=:E241:/
+      " Unable to send to this server, ignore it.
+    endtry
   endwhile
   return ''
 endfunc
diff --git a/src/os_mswin.c b/src/os_mswin.c
index 448b2bf..256da04 100644
--- a/src/os_mswin.c
+++ b/src/os_mswin.c
@@ -1971,6 +1971,10 @@
 # define VIM_CLASSNAME      "VIM_MESSAGES"
 # define VIM_CLASSNAME_LEN  (sizeof(VIM_CLASSNAME) - 1)
 
+// Timeout for sending a message to another Vim instance.  Normally this works
+// instantly, but it may hang when the other Vim instance is halted.
+# define SENDMESSAGE_TIMEOUT	(5 * 1000)
+
 // Communication is via WM_COPYDATA messages. The message type is sent in
 // the dwData parameter. Types are defined here.
 # define COPYDATA_KEYS		0
@@ -1992,9 +1996,9 @@
 
 /*
  * Tell the other side what encoding we are using.
- * Errors are ignored.
+ * Return -1 if timeout happens.  Other errors are ignored.
  */
-    static void
+    static int
 serverSendEnc(HWND target)
 {
     COPYDATASTRUCT data;
@@ -2002,8 +2006,11 @@
     data.dwData = COPYDATA_ENCODING;
     data.cbData = (DWORD)STRLEN(p_enc) + 1;
     data.lpData = p_enc;
-    (void)SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
-							     (LPARAM)(&data));
+    if (SendMessageTimeout(target, WM_COPYDATA,
+	    (WPARAM)message_window, (LPARAM)&data,
+	    SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, NULL) == 0)
+	return -1;
+    return 0;
 }
 
 /*
@@ -2061,6 +2068,7 @@
 	COPYDATASTRUCT	reply;
 	char_u		*res;
 	int		retval;
+	DWORD_PTR	dwret = 0;
 	char_u		*str;
 	char_u		*tofree;
 
@@ -2114,9 +2122,17 @@
 	    reply.lpData = res;
 	    reply.cbData = (DWORD)STRLEN(res) + 1;
 
-	    serverSendEnc(sender);
-	    retval = (int)SendMessage(sender, WM_COPYDATA,
-				    (WPARAM)message_window, (LPARAM)(&reply));
+	    if (serverSendEnc(sender) < 0)
+		retval = -1;
+	    else
+	    {
+		if (SendMessageTimeout(sender, WM_COPYDATA,
+			(WPARAM)message_window, (LPARAM)&reply,
+			SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, &dwret) == 0)
+		    retval = -1;
+		else
+		    retval = (int)dwret;
+	    }
 	    vim_free(tofree);
 	    vim_free(res);
 	    return retval;
@@ -2394,6 +2410,7 @@
     HWND	target;
     COPYDATASTRUCT data;
     long_u	n = 0;
+    DWORD_PTR	dwret = 0;
 
     // The "name" argument is a magic cookie obtained from expand("<client>").
     // It should be of the form 0xXXXXX - i.e. a C hex literal, which is the
@@ -2410,12 +2427,13 @@
     data.cbData = (DWORD)STRLEN(reply) + 1;
     data.lpData = reply;
 
-    serverSendEnc(target);
-    if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
-							     (LPARAM)(&data)))
-	return 0;
-
-    return -1;
+    if (serverSendEnc(target) < 0)
+	return -1;
+    if (SendMessageTimeout(target, WM_COPYDATA,
+		(WPARAM)message_window, (LPARAM)&data,
+		SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, &dwret) == 0)
+	return -1;
+    return dwret ? 0 : -1;
 }
 
     int
@@ -2432,6 +2450,7 @@
     COPYDATASTRUCT data;
     char_u	*retval = NULL;
     int		retcode = 0;
+    DWORD_PTR	dwret = 0;
     char_u	altname_buf[MAX_PATH];
 
     // Execute locally if no display or target is ourselves
@@ -2463,9 +2482,13 @@
     data.cbData = (DWORD)STRLEN(cmd) + 1;
     data.lpData = cmd;
 
-    serverSendEnc(target);
-    if (SendMessage(target, WM_COPYDATA, (WPARAM)message_window,
-							(LPARAM)(&data)) == 0)
+    if (serverSendEnc(target) < 0)
+	return -1;
+    if (SendMessageTimeout(target, WM_COPYDATA,
+		(WPARAM)message_window, (LPARAM)&data,
+		SMTO_ABORTIFHUNG, SENDMESSAGE_TIMEOUT, &dwret) == 0)
+	return -1;
+    if (dwret == 0)
 	return -1;
 
     if (asExpr)
diff --git a/src/version.c b/src/version.c
index 1b6fd28..6ea4a37 100644
--- a/src/version.c
+++ b/src/version.c
@@ -735,6 +735,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    5118,
+/**/
     5117,
 /**/
     5116,