Temporary directory cleanup on Windows for DLLs loaded from Tclkits etc.

diff -Nur tcl8.6.5.orig/generic/tclEvent.c tcl8.6.5/generic/tclEvent.c
--- tcl8.6.5.orig/generic/tclEvent.c	2016-02-25 21:12:37.000000000 +0100
+++ tcl8.6.5/generic/tclEvent.c	2016-05-03 23:13:19.997528991 +0200
@@ -985,6 +985,13 @@
 	     */
 
 	    FinalizeThread(/* quick */ 1);
+
+#ifdef _WIN32
+	    /*
+	     * Try at least to cleanup temporary DLLs when using VFSes.
+	     */
+	    TclFinalizeLoad();
+#endif
 	}
 	TclpExit(status);
 	Tcl_Panic("OS exit failed!");
diff -Nur tcl8.6.5.orig/generic/tclInt.h tcl8.6.5/generic/tclInt.h
--- tcl8.6.5.orig/generic/tclInt.h	2016-02-25 21:12:37.000000000 +0100
+++ tcl8.6.5/generic/tclInt.h	2016-05-03 23:12:35.507371431 +0200
@@ -3043,6 +3043,7 @@
 MODULE_SCOPE void	TclpFinalizeMutex(Tcl_Mutex *mutexPtr);
 MODULE_SCOPE void	TclpFinalizePipes(void);
 MODULE_SCOPE void	TclpFinalizeSockets(void);
+MODULE_SCOPE void	TclpFinalizeLoad(void);
 MODULE_SCOPE int	TclCreateSocketAddress(Tcl_Interp *interp,
 			    struct addrinfo **addrlist,
 			    const char *host, int port, int willBind,
diff -Nur tcl8.6.5.orig/generic/tclLoad.c tcl8.6.5/generic/tclLoad.c
--- tcl8.6.5.orig/generic/tclLoad.c	2016-02-25 21:12:37.000000000 +0100
+++ tcl8.6.5/generic/tclLoad.c	2016-05-01 14:48:49.244997000 +0200
@@ -1168,6 +1168,7 @@
 	ckfree(pkgPtr->packageName);
 	ckfree(pkgPtr);
     }
+    TclpFinalizeLoad();
 }
 
 /*
diff -Nur tcl8.6.5.orig/generic/tclLoadNone.c tcl8.6.5/generic/tclLoadNone.c
--- tcl8.6.5.orig/generic/tclLoadNone.c	2016-02-25 21:12:37.000000000 +0100
+++ tcl8.6.5/generic/tclLoadNone.c	2016-05-01 14:54:32.871644000 +0200
@@ -121,6 +121,28 @@
 #endif /* TCL_LOAD_FROM_MEMORY */
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * TclpFinalizeLoad --
+ *
+ *	On process termination, cleanup and finally remove the
+ *	directory used for temporary copies of DLLs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side-effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeLoad(void)
+{
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
diff -Nur tcl8.6.5.orig/unix/tclLoadDl.c tcl8.6.5/unix/tclLoadDl.c
--- tcl8.6.5.orig/unix/tclLoadDl.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclLoadDl.c	2016-05-03 23:14:50.038896663 +0200
@@ -266,6 +266,28 @@
 }
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * TclpFinalizeLoad --
+ *
+ *	On process termination, cleanup and finally remove the
+ *	directory used for temporary copies of DLLs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side-effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeLoad(void)
+{
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
diff -Nur tcl8.6.5.orig/unix/tclLoadDyld.c tcl8.6.5/unix/tclLoadDyld.c
--- tcl8.6.5.orig/unix/tclLoadDyld.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclLoadDyld.c	2016-05-03 23:14:56.048188049 +0200
@@ -709,6 +709,28 @@
 #endif /* TCL_LOAD_FROM_MEMORY */
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * TclpFinalizeLoad --
+ *
+ *	On process termination, cleanup and finally remove the
+ *	directory used for temporary copies of DLLs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side-effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeLoad(void)
+{
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
diff -Nur tcl8.6.5.orig/unix/tclLoadNext.c tcl8.6.5/unix/tclLoadNext.c
--- tcl8.6.5.orig/unix/tclLoadNext.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclLoadNext.c	2016-05-03 23:15:02.000476864 +0200
@@ -209,6 +209,28 @@
 }
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * TclpFinalizeLoad --
+ *
+ *	On process termination, cleanup and finally remove the
+ *	directory used for temporary copies of DLLs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side-effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeLoad(void)
+{
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
diff -Nur tcl8.6.5.orig/unix/tclLoadOSF.c tcl8.6.5/unix/tclLoadOSF.c
--- tcl8.6.5.orig/unix/tclLoadOSF.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclLoadOSF.c	2016-05-03 23:15:07.274732696 +0200
@@ -227,6 +227,28 @@
 }
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * TclpFinalizeLoad --
+ *
+ *	On process termination, cleanup and finally remove the
+ *	directory used for temporary copies of DLLs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side-effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeLoad(void)
+{
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
diff -Nur tcl8.6.5.orig/unix/tclLoadShl.c tcl8.6.5/unix/tclLoadShl.c
--- tcl8.6.5.orig/unix/tclLoadShl.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/unix/tclLoadShl.c	2016-05-03 23:15:13.304025295 +0200
@@ -216,6 +216,28 @@
 }
 
 /*
+ *----------------------------------------------------------------------
+ *
+ * TclpFinalizeLoad --
+ *
+ *	On process termination, cleanup and finally remove the
+ *	directory used for temporary copies of DLLs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side-effects:
+ *	None.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeLoad(void)
+{
+}
+
+/*
  * Local Variables:
  * mode: c
  * c-basic-offset: 4
diff -Nur tcl8.6.5.orig/win/tclWinLoad.c tcl8.6.5/win/tclWinLoad.c
--- tcl8.6.5.orig/win/tclWinLoad.c	2016-02-25 21:12:38.000000000 +0100
+++ tcl8.6.5/win/tclWinLoad.c	2016-05-02 03:21:04.647886000 +0200
@@ -21,6 +21,7 @@
 
 static WCHAR *dllDirectoryName = NULL;
 static Tcl_Mutex dllDirectoryNameMutex;
+static Tcl_HashTable tempDllTable;
 
 /*
  * Static functions defined within this file.
@@ -148,6 +149,22 @@
     handlePtr->unloadFileProcPtr = &UnloadFile;
     *loadHandle = handlePtr;
     *unloadProcPtr = &UnloadFile;
+
+    /*
+     * Remember handle for process termination cleanup handler
+     * if the DLL is a copy in temporary directory.
+     */
+
+    Tcl_MutexLock(&dllDirectoryNameMutex);
+    if (dllDirectoryName != NULL) {
+	int dummy, len = wcslen(dllDirectoryName);
+
+	if ((len > 0) && (_wcsnicmp(nativeName, dllDirectoryName, len) == 0)) {
+	    Tcl_CreateHashEntry(&tempDllTable, (ClientData) handlePtr, &dummy);
+	}
+    }
+    Tcl_MutexUnlock(&dllDirectoryNameMutex);
+
     return TCL_OK;
 }
 
@@ -225,9 +242,27 @@
 				 * that represents the loaded file. */
 {
     HINSTANCE hInstance = (HINSTANCE) loadHandle->clientData;
+    BOOL success, keep = 0;
 
-    FreeLibrary(hInstance);
-    ckfree(loadHandle);
+    success = FreeLibrary(hInstance);
+    Tcl_MutexLock(&dllDirectoryNameMutex);
+    if (dllDirectoryName != NULL) {
+	Tcl_HashEntry *hPtr =
+	    Tcl_FindHashEntry(&tempDllTable, (ClientData) loadHandle);
+
+	if (hPtr != NULL) {
+	    if (success) {
+		Tcl_DeleteHashEntry(hPtr);
+	    } else {
+		/* FreeLibrary() will be retried in TclpFinalizeLoad() later */
+		keep = 1;
+	    }
+	}
+    }
+    Tcl_MutexUnlock(&dllDirectoryNameMutex);
+    if (!keep) {
+	ckfree(loadHandle);
+    }
 }
 
 /*
@@ -317,6 +352,95 @@
 /*
  *----------------------------------------------------------------------
  *
+ * TclpFinalizeLoad --
+ *
+ *	On process termination, cleanup and finally remove the
+ *	directory used for temporary copies of DLLs.
+ *
+ * Results:
+ *	None.
+ *
+ * Side-effects:
+ *	Delete files in temp directory.
+ *	Remove temp directory.
+ *
+ *----------------------------------------------------------------------
+ */
+
+void
+TclpFinalizeLoad(void)
+{
+    WCHAR *dllDir = NULL;
+    int dllDirLen;
+    Tcl_DString ds;
+    Tcl_HashEntry *hPtr;
+    Tcl_HashSearch search;
+    HANDLE handle;
+    WIN32_FIND_DATAW data;
+
+    if (!TclFullFinalizationRequested()) {
+	Tcl_MutexLock(&dllDirectoryNameMutex);
+    }
+    if (dllDirectoryName != NULL) {
+	dllDir = dllDirectoryName;
+	dllDirectoryName = NULL;
+    }
+    if (dllDir != NULL) {
+
+	/*
+	 * Try unloading left-over DLLs.
+	 */
+
+	hPtr = Tcl_FirstHashEntry(&tempDllTable, &search);
+	while (hPtr != NULL) {
+	    Tcl_LoadHandle loadHandle =
+		(Tcl_LoadHandle) Tcl_GetHashKey(&tempDllTable, hPtr);
+
+	    Tcl_DeleteHashEntry(hPtr);
+	    hPtr = Tcl_NextHashEntry(&search);
+
+	    /*
+	     * Same cleanup steps as in UnloadFile(), but dangerous anyway.
+	     */
+
+	    FreeLibrary((HINSTANCE) loadHandle->clientData);
+	    ckfree(loadHandle);
+	}
+	Tcl_DeleteHashTable(&tempDllTable);
+
+	/*
+	 * Delete temporary copies of DLLs, then the temporary directory.
+	 */
+
+	Tcl_DStringInit(&ds);
+	dllDirLen = wcslen(dllDir) * sizeof (WCHAR);
+	Tcl_DStringAppend(&ds, (char *) dllDir, dllDirLen);
+	Tcl_DStringAppend(&ds, (char *) L"\\*.dll\0",
+		wcslen(L"\\*.dll\0") * sizeof (WCHAR));
+	dllDirLen += sizeof (WCHAR);	/* include trailing backslash */
+	handle = FindFirstFileW((WCHAR *) Tcl_DStringValue(&ds), &data);
+	if (handle != INVALID_HANDLE_VALUE) {
+	    do {
+		Tcl_DStringSetLength(&ds, dllDirLen);
+		Tcl_DStringAppend(&ds, (char *) data.cFileName,
+			wcslen(data.cFileName) * sizeof (WCHAR));
+		Tcl_DStringAppend(&ds, (char *) L"\0", sizeof (WCHAR));
+		DeleteFileW((WCHAR *) Tcl_DStringValue(&ds));
+	    } while (FindNextFileW(handle, &data) == TRUE);
+	    FindClose(handle);
+	}
+	Tcl_DStringFree(&ds);
+	RemoveDirectoryW(dllDir);
+	ckfree(dllDir);
+    }
+    if (!TclFullFinalizationRequested()) {
+	Tcl_MutexUnlock(&dllDirectoryNameMutex);
+    }
+}
+
+/*
+ *----------------------------------------------------------------------
+ *
  * InitDLLDirectoryName --
  *
  *	Helper for TclpTempFileNameForLibrary; builds a temporary directory
@@ -390,6 +514,7 @@
   copyToGlobalBuffer:
     dllDirectoryName = ckalloc((nameLen+1) * sizeof(WCHAR));
     wcscpy(dllDirectoryName, name);
+    Tcl_InitHashTable(&tempDllTable, TCL_ONE_WORD_KEYS);
     return TCL_OK;
 }
 
