diff --git a/src/sbbs3/nopen.c b/src/sbbs3/nopen.c
index e5fe708ffe06c60103417215c7c731c8cc675611..387a0ee5798676cbcb476f5d584bb00cfd1f2c46 100644
--- a/src/sbbs3/nopen.c
+++ b/src/sbbs3/nopen.c
@@ -24,6 +24,9 @@
 #include "filewrap.h"
 #include "sockwrap.h"
 #include "nopen.h"
+#ifdef _WIN32
+	#include <io.h>
+#endif
 
 /****************************************************************************/
 /* Network open function. Opens all files DENYALL, DENYWRITE, or DENYNONE	*/
@@ -109,7 +112,8 @@ bool ftouch(const char* fname)
 	return true;
 }
 
-int fmutex_open(const char* fname, const char* text, long max_age, time_t* tp)
+// Opens a mutex file and returns its file descriptor or -1 on failure
+int fmutex_open(const char* fname, const char* text, long max_age, time_t* tp, bool atomic_remove)
 {
 	int file;
 	time_t t;
@@ -129,8 +133,28 @@ int fmutex_open(const char* fname, const char* text, long max_age, time_t* tp)
 				return -1;
 		}
 	}
+#ifdef _WIN32
+	DWORD attributes = FILE_ATTRIBUTE_NORMAL;
+	if(atomic_remove)
+		attributes |= FILE_FLAG_DELETE_ON_CLOSE;
+	HANDLE h = CreateFileA(fname,
+		GENERIC_WRITE,	// dwDesiredAccess
+		0,				// dwShareMode (deny all)
+		NULL,			// lpSecurityAttributes,
+		CREATE_NEW,		// dwCreationDisposition
+		attributes,		// dwFlagsAndAttributes,
+		NULL			// hTemplateFile
+		);
+	if(h == INVALID_HANDLE_VALUE)
+		return -1;
+	if((file = _open_osfhandle((intptr_t)h, O_WRONLY)) == -1) {
+		CloseHandle(h);
+		return -1;
+	}
+#else
 	if((file=sopen(fname, O_CREAT|O_WRONLY|O_EXCL, SH_DENYRW, DEFFILEMODE))<0)
-		return file;
+		return -1;
+#endif
 	if(text!=NULL) {
 		len = strlen(text);
 		if(write(file, text, len) != len) {
@@ -141,15 +165,26 @@ int fmutex_open(const char* fname, const char* text, long max_age, time_t* tp)
 	return file;
 }
 
+// Opens and immediately closes a mutex file
 bool fmutex(const char* fname, const char* text, long max_age, time_t* tp)
 {
-	int file = fmutex_open(fname, text, max_age, tp);
+	int file = fmutex_open(fname, text, max_age, tp, false);
 	if(file < 0)
 		return false;
 	close(file);
 	return true;
 }
 
+// Closes and atomically removes a mutex file opened with fmutex_open()
+bool fmutex_close(const char* fname, int file)
+{
+#ifndef _WIN32 // You can't delete an open file on Windows, so we open with the DELETE_ON_CLOSE attribute instead
+	if(remove(fname) != 0)
+		return false;
+#endif
+	return close(file) == 0;
+}
+
 bool fcompare(const char* fn1, const char* fn2)
 {
 	FILE*	fp1;
diff --git a/src/sbbs3/nopen.h b/src/sbbs3/nopen.h
index 4cccecd0da8cc35efdf535042b79315b7b6c95e3..9eaf9646587e0a2182794092cc3aa3086e1d1bd8 100644
--- a/src/sbbs3/nopen.h
+++ b/src/sbbs3/nopen.h
@@ -37,7 +37,8 @@ int		nopen(const char* str, uint access);
 FILE *	fnopen(int* file, const char* str, uint access);
 bool	ftouch(const char* fname);
 bool	fmutex(const char* fname, const char* text, long max_age, time_t*);
-int		fmutex_open(const char* fname, const char* text, long max_age, time_t*);
+int		fmutex_open(const char* fname, const char* text, long max_age, time_t*, bool atomic_remove);
+bool	fmutex_close(const char* fname, int file);
 bool	fcompare(const char* fn1, const char* fn2);
 bool	backup(const char* org, int backup_level, bool ren);