diff --git a/src/sbbs3/sbbsdefs.h b/src/sbbs3/sbbsdefs.h
index 42a7968d70cdda843c471ba8c05fb37815736929..4e1902cf596c7e40585dd97c11a284834f3ece73 100644
--- a/src/sbbs3/sbbsdefs.h
+++ b/src/sbbs3/sbbsdefs.h
@@ -399,6 +399,7 @@ typedef enum {						/* Values for xtrn_t.event				*/
 #define XTRN_TEMP_DIR	(1<<24)		/* Place drop files in temp dir			*/
 #define XTRN_UART		(1<<25)		/* Enable the virtual UART driver		*/
 #define XTRN_FOSSIL		(1<<26)		/* Enable the int14h/FOSSIL driver		*/
+#define XTRN_NODISPLAY	(1<<27)		/* Disable local screen/display			*/
 #define XTRN_CONIO		(1<<31)		/* Intercept Windows Console I/O (Drwy)	*/
 
 									/* Bits in user.qwk 					*/
@@ -815,6 +816,7 @@ enum {							/* readmail and delmailidx which types		*/
 #define EX_STDIO	(EX_STDIN|EX_STDOUT)
 #define EX_UART		XTRN_UART
 #define EX_FOSSIL	XTRN_FOSSIL
+#define EX_NODISPLAY XTRN_NODISPLAY
 #define EX_NOLOG	(1<<30)		/* Don't log intercepted stdio				*/
 #define EX_CONIO	(1<<31)		/* Intercept Windows console I/O (doorway)	*/
 #define EX_UNSPECIFIED	-1
diff --git a/src/sbbs3/scfg/scfgxtrn.c b/src/sbbs3/scfg/scfgxtrn.c
index adece05dcf10828ffb85fff6097d744931178455..f2e58a987573d5f61e67202def6ead5df00a708c 100644
--- a/src/sbbs3/scfg/scfgxtrn.c
+++ b/src/sbbs3/scfg/scfgxtrn.c
@@ -1220,6 +1220,8 @@ void xtrn_cfg(uint section)
 			sprintf(opt[k++],"%-27.27s%s","Execute on Event",str);
 			sprintf(opt[k++],"%-27.27s%s","Pause After Execution"
 				,cfg.xtrn[i]->misc&XTRN_PAUSE ? "Yes" : "No");
+			sprintf(opt[k++],"%-27.27s%s","Disable Local Display"
+				,cfg.xtrn[i]->misc&XTRN_NODISPLAY ? "Yes" : "No");
 			sprintf(opt[k++],"%-23.23s%-4s%s","BBS Drop File Type"
 				,cfg.xtrn[i]->misc&REALNAME ? "(R)":nulstr
 				,dropfile(cfg.xtrn[i]->type,cfg.xtrn[i]->misc));
@@ -1255,7 +1257,7 @@ void xtrn_cfg(uint section)
 				case -CIO_KEY_RIGHT-2:
 					i = next;
 					break;
-				case 0:
+				case __COUNTER__:
 					uifc.helpbuf=
 						"`Online Program Name:`\n"
 						"\n"
@@ -1266,7 +1268,7 @@ void xtrn_cfg(uint section)
 						,cfg.xtrn[i]->name,sizeof(cfg.xtrn[i]->name)-1,K_EDIT))
 						SAFECOPY(cfg.xtrn[i]->name,str);
 					break;
-				case 1:
+				case __COUNTER__:
 					uifc.helpbuf=
 						"`Online Program Internal Code:`\n"
 						"\n"
@@ -1285,7 +1287,7 @@ void xtrn_cfg(uint section)
 						uifc.helpbuf=0; 
 					}
 					break;
-				case 2:
+				case __COUNTER__:
 					uifc.helpbuf=
 						"`Online Program Start-up Directory:`\n"
 						"\n"
@@ -1298,7 +1300,7 @@ void xtrn_cfg(uint section)
 					uifc.input(WIN_MID|WIN_SAV,0,10,""
 						,cfg.xtrn[i]->path,sizeof(cfg.xtrn[i]->path)-1,K_EDIT);
 					break;
-				case 3:
+				case __COUNTER__:
 					uifc.helpbuf=
 						"`Online Program Command Line:`\n"
 						"\n"
@@ -1309,7 +1311,7 @@ void xtrn_cfg(uint section)
 					uifc.input(WIN_MID|WIN_SAV,0,10,"Command"
 						,cfg.xtrn[i]->cmd,sizeof(cfg.xtrn[i]->cmd)-1,K_EDIT);
 					break;
-				case 4:
+				case __COUNTER__:
 					uifc.helpbuf=
 						"`Online Program Clean-up Command:`\n"
 						"\n"
@@ -1321,7 +1323,7 @@ void xtrn_cfg(uint section)
 					uifc.input(WIN_MID|WIN_SAV,0,10,"Clean-up"
 						,cfg.xtrn[i]->clean,sizeof(cfg.xtrn[i]->clean)-1,K_EDIT);
 					break;
-				case 5:
+				case __COUNTER__:
 					ultoa(cfg.xtrn[i]->cost,str,10);
 					uifc.helpbuf=
 						"`Online Program Cost to Run:`\n"
@@ -1334,15 +1336,15 @@ void xtrn_cfg(uint section)
 						,str,10,K_EDIT|K_NUMBER);
 					cfg.xtrn[i]->cost=atol(str);
 					break;
-				case 6:
+				case __COUNTER__:
 					sprintf(str,"%s Access",cfg.xtrn[i]->name);
 					getar(str,cfg.xtrn[i]->arstr);
 					break;
-				case 7:
+				case __COUNTER__:
 					sprintf(str,"%s Execution",cfg.xtrn[i]->name);
 					getar(str,cfg.xtrn[i]->run_arstr);
 					break;
-				case 8:
+				case __COUNTER__:
 					k=(cfg.xtrn[i]->misc&MULTIUSER) ? 0:1;
 					uifc.helpbuf=
 						"`Supports Multiple Users:`\n"
@@ -1361,10 +1363,10 @@ void xtrn_cfg(uint section)
 						uifc.changes=TRUE; 
 					}
 					break;
-				case 9:
+				case __COUNTER__:
 					choose_io_method(&cfg.xtrn[i]->misc);
 					break;
-				case 10:
+				case __COUNTER__:
 					k=(cfg.xtrn[i]->misc&XTRN_NATIVE) ? 0:1;
 					uifc.helpbuf=native_help;
 					k=uifc.list(WIN_MID|WIN_SAV,0,0,0,&k,0
@@ -1378,7 +1380,7 @@ void xtrn_cfg(uint section)
 						uifc.changes=TRUE; 
 					}
 					break;
-				case 11:
+				case __COUNTER__:
 					k=(cfg.xtrn[i]->misc&XTRN_SH) ? 0:1;
 					uifc.helpbuf = use_shell_help;
 					k=uifc.list(WIN_MID|WIN_SAV,0,0,0,&k,0
@@ -1392,7 +1394,7 @@ void xtrn_cfg(uint section)
 						uifc.changes=TRUE; 
 					}
 					break;
-				case 12:
+				case __COUNTER__:
 					k=(cfg.xtrn[i]->misc&MODUSERDAT) ? 0:1;
 					uifc.helpbuf=
 						"`Program Can Modify User Data:`\n"
@@ -1412,7 +1414,7 @@ void xtrn_cfg(uint section)
 						uifc.changes=TRUE; 
 					}
 					break;
-				case 13:
+				case __COUNTER__:
 					k=0;
 					strcpy(opt[k++],"No");
 					strcpy(opt[k++],"Logon");
@@ -1466,7 +1468,7 @@ void xtrn_cfg(uint section)
 						uifc.changes=TRUE; 
 					}
 					break;
-				case 14:
+				case __COUNTER__:
 					k=(cfg.xtrn[i]->misc&XTRN_PAUSE) ? 0:1;
 					uifc.helpbuf=
 						"`Pause Screen After Execution:`\n"
@@ -1486,7 +1488,27 @@ void xtrn_cfg(uint section)
 						uifc.changes=TRUE; 
 					}
 					break;
-				case 15:
+				case __COUNTER__:
+					k=(cfg.xtrn[i]->misc&XTRN_NODISPLAY) ? 0:1;
+					uifc.helpbuf=
+						"`Disable Local Screen Display:`\n"
+						"\n"
+						"Set this option to `Yes` if you wish to prevent this program from\n"
+						"displaying on the local screen.\n"
+						"\n"
+						"This will disable 'Screen' output in the `DOOR.SYS` and `PCBOARD.SYS` drop\n"
+						"files and on Windows, stop the creation a new console window when\n"
+						"executing this program."
+					;
+					k=uifc.list(WIN_MID|WIN_SAV,0,0,0,&k,0
+						,"Disable Local Screen Display", uifcYesNoOpts);
+					if((!k && !(cfg.xtrn[i]->misc & XTRN_NODISPLAY))
+						|| (k && (cfg.xtrn[i]->misc & XTRN_NODISPLAY))) {
+						cfg.xtrn[i]->misc ^= XTRN_NODISPLAY;
+						uifc.changes=TRUE; 
+					}
+					break;
+				case __COUNTER__:
 					k=0;
 					strcpy(opt[k++],"None");
 					sprintf(opt[k++],"%-15s %s","Synchronet","XTRN.DAT");
@@ -1561,7 +1583,7 @@ void xtrn_cfg(uint section)
 						} 
 					}
 					break;
-				case 16:
+				case __COUNTER__:
 					k = (cfg.xtrn[i]->misc & STARTUPDIR) ? 1 : (cfg.xtrn[i]->misc & XTRN_TEMP_DIR) ? 2 : 0;
 					strcpy(opt[0],"Node Directory");
 					strcpy(opt[1],"Start-up Directory");
@@ -1600,7 +1622,7 @@ void xtrn_cfg(uint section)
 						uifc.changes=TRUE; 
 					}
 					break;
-				case 17:
+				case __COUNTER__:
 					while(1) {
 						k=0;
 						if(cfg.xtrn[i]->textra)
diff --git a/src/sbbs3/xtrn.cpp b/src/sbbs3/xtrn.cpp
index 28e7482e8ca9782229333ff7ccd9f743629ef4d5..daca8ae017dc5c50463ea51d33994a0e4d45a68c 100644
--- a/src/sbbs3/xtrn.cpp
+++ b/src/sbbs3/xtrn.cpp
@@ -448,6 +448,8 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 
 		SAFEPRINTF(path,"%sDOSXTRN.RET", cfg.node_dir);
 		(void)remove(path);
+		SAFEPRINTF(path,"%sDOSXTRN.ERR", cfg.node_dir);
+		(void)remove(path);
 
     	// Create temporary environment file
     	SAFEPRINTF(path,"%sDOSXTRN.ENV", node_dir);
@@ -595,13 +597,14 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 			pthread_mutex_lock(&input_thread_mutex);
 	}
 
+	DWORD creation_flags = (mode & EX_NODISPLAY) ? CREATE_NO_WINDOW : CREATE_NEW_CONSOLE;
     success=CreateProcess(
 		NULL,			// pointer to name of executable module
 		fullcmdline,  	// pointer to command line string
 		NULL,  			// process security attributes
 		NULL,   		// thread security attributes
 		native && !(mode&EX_OFFLINE),	 			// handle inheritance flag
-		CREATE_NEW_CONSOLE/*|CREATE_SEPARATE_WOW_VDM*/, // creation flags
+		creation_flags, // creation flags
         env_block, 		// pointer to new environment block
 		p_startup_dir,	// pointer to current directory name
 		&startup_info,  // pointer to STARTUPINFO
@@ -791,8 +794,7 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
 
 					if(online && hangup_event!=NULL
 						&& WaitForSingleObject(hangup_event,0)==WAIT_OBJECT_0) {
-						lprintf(LOG_NOTICE,"Node %d External program requested hangup (dropped DTR)"
-							,cfg.node_num);
+						lputs(LOG_NOTICE, "External program requested hangup (dropped DTR)");
 						hangup();
 					}
 
@@ -831,21 +833,40 @@ int sbbs_t::external(const char* cmdline, long mode, const char* startup_dir)
             errormsg(WHERE, ERR_CHK, "ExitCodeProcess",(DWORD)process_info.hProcess);
 
 		if(retval==STILL_ACTIVE) {
-			lprintf(LOG_INFO,"Node %d Terminating process from line %d",cfg.node_num,__LINE__);
+			lprintf(LOG_INFO,"Terminating process from line %d", __LINE__);
 			TerminateProcess(process_info.hProcess, GetLastError());
 		}
 
 	 	// Get return value
 		if(!native) {
-    		sprintf(str,"%sDOSXTRN.RET", cfg.node_dir);
-			FILE* fp=fopen(str,"r");
-			if(fp!=NULL) {
+    		SAFEPRINTF(path, "%sDOSXTRN.RET", cfg.node_dir);
+			FILE* fp=fopen(path,"r");
+			if(fp == NULL) {
+				lprintf(LOG_ERR, "Error %d opening %s", errno, path);
+			} else {
 				if(fscanf(fp,"%d",&retval) != 1) {
-					lprintf(LOG_ERR, "Node %d Error reading return value from %s", cfg.node_num, str);
-					retval = -1;
+					lprintf(LOG_ERR, "Error reading return value from %s", path);
+					retval = -2;
 				}
 				fclose(fp);
 			}
+			if(retval == -1) {
+				SAFEPRINTF(path, "%sDOSXTRN.ERR", cfg.node_dir);
+				fp = fopen(path, "r");
+				if(fp == NULL) {
+					lprintf(LOG_ERR, "Error %d opening %s after DOSXTRN.RET contained -1", errno, path);
+				} else {
+					char errstr[256] = "";
+					int errval = 0;
+					if(fscanf(fp, "%d\n", &errval) == 1) {
+						fgets(errstr, sizeof(errstr), fp);
+						truncsp(errstr);
+						lprintf(LOG_ERR, "DOSXTRN Error %d (%s) executing: %s", errval, errstr, cmdline);
+					} else
+						lprintf(LOG_ERR, "DOSXTRN.RET contained -1 and we failed to parse: %s", path);
+					fclose(fp);
+				}
+			}
 		}
 	}
 
diff --git a/src/sbbs3/xtrn_sec.cpp b/src/sbbs3/xtrn_sec.cpp
index 26972cb304304f6f0633318cbb3b6b23fb6dbf5a..02c6e90eb526dd677075ec3514f3f4a67d989841 100644
--- a/src/sbbs3/xtrn_sec.cpp
+++ b/src/sbbs3/xtrn_sec.cpp
@@ -381,7 +381,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 			,8									/* 03: Data bits */
 			,cfg.node_num						/* 04: Node number */
 			,dte_rate							/* 05: DTE rate */
-			,'Y'								/* 06: Screen display */
+			,(misc & XTRN_NODISPLAY) ? 'N': 'Y'	/* 06: Screen display */
 			,'Y'                                /* 07: Printer toggle */
 			,'Y'                                /* 08: Page bell */
 			,'Y');                              /* 09: Caller alarm */
@@ -712,7 +712,7 @@ void sbbs_t::xtrndat(const char *name, const char *dropdir, uchar type, ulong tl
 			return; 
 		}
 		PCBoard::sys sys{};
-		sys.Screen = true;
+		sys.Screen = !(misc & XTRN_NODISPLAY);
 		sys.PageBell = sys_status & SS_SYSPAGE;
 		sys.Alarm = startup->sound.answer[0] && !sound_muted(&cfg);
 		sys.ErrorCorrected = true;
@@ -1393,7 +1393,7 @@ bool sbbs_t::exec_xtrn(uint xtrnnum)
 		mode|=EX_UART;
 	else if(cfg.xtrn[xtrnnum]->misc&XTRN_FOSSIL)
 		mode|=EX_FOSSIL;
-	mode|=(cfg.xtrn[xtrnnum]->misc&(XTRN_CHKTIME|XTRN_NATIVE|XTRN_NOECHO|WWIVCOLOR));
+	mode|=(cfg.xtrn[xtrnnum]->misc&(XTRN_CHKTIME|XTRN_NATIVE|XTRN_NOECHO|XTRN_NODISPLAY|WWIVCOLOR));
 	if(cfg.xtrn[xtrnnum]->misc&MODUSERDAT) {		/* Delete MODUSER.DAT */
 		SAFEPRINTF(str,"%sMODUSER.DAT",dropdir);	/* if for some weird  */
 		(void)removecase(str);						/* reason it's there  */