shiny/driver/internal/win32: Close window correctly to be able to re-open one after closing

Closing and re-opening a window caused an error at RegisterClassW call (class already registered). This fixes the problem by correctly unregistering the class.

Change-Id: Ie6e45354096bc1245776264bec9be0c86f53d79c
GitHub-Last-Rev: 34780c27353a35240a46fe49e3b605238d8fae57
GitHub-Pull-Request: golang/exp#6
Reviewed-on: https://go-review.googlesource.com/c/exp/+/171697
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/shiny/driver/internal/win32/win32.go b/shiny/driver/internal/win32/win32.go
index deaeb37..6925085 100644
--- a/shiny/driver/internal/win32/win32.go
+++ b/shiny/driver/internal/win32/win32.go
@@ -367,6 +367,7 @@
 }
 
 const windowClass = "shiny_Window"
+const screenWindowClass = "shiny_ScreenWindow"
 
 func initWindowClass() (err error) {
 	wcname, err := syscall.UTF16PtrFromString(windowClass)
@@ -385,8 +386,17 @@
 	return err
 }
 
+func closeWindowClass() (err error) {
+	wcname, err := syscall.UTF16PtrFromString(windowClass)
+	if err != nil {
+		return err
+	}
+	_UnregisterClass(wcname, hThisInstance)
+
+	return nil
+}
+
 func initScreenWindow() (err error) {
-	const screenWindowClass = "shiny_ScreenWindow"
 	swc, err := syscall.UTF16PtrFromString(screenWindowClass)
 	if err != nil {
 		return err
@@ -419,6 +429,20 @@
 	return nil
 }
 
+func closeScreenWindow() (err error) {
+	// first destroy window
+	_DestroyWindow(screenHWND)
+
+	// then unregister class
+	swc, err := syscall.UTF16PtrFromString(screenWindowClass)
+	if err != nil {
+		return err
+	}
+	_UnregisterClass(swc, hThisInstance)
+
+	return nil
+}
+
 var (
 	hDefaultIcon   syscall.Handle
 	hDefaultCursor syscall.Handle
@@ -461,13 +485,16 @@
 	}
 	defer func() {
 		// TODO(andlabs): log an error if this fails?
-		_DestroyWindow(screenHWND)
-		// TODO(andlabs): unregister window class
+		closeScreenWindow()
 	}()
 
 	if err := initWindowClass(); err != nil {
 		return err
 	}
+	defer func() {
+		// TODO(andlabs): log an error if this fails?
+		closeWindowClass()
+	}()
 
 	// Prime the pump.
 	mainCallback = f
diff --git a/shiny/driver/internal/win32/zsyscall_windows.go b/shiny/driver/internal/win32/zsyscall_windows.go
index 1759c01..26999f6 100644
--- a/shiny/driver/internal/win32/zsyscall_windows.go
+++ b/shiny/driver/internal/win32/zsyscall_windows.go
@@ -62,6 +62,7 @@
 	procScreenToClient    = moduser32.NewProc("ScreenToClient")
 	procToUnicodeEx       = moduser32.NewProc("ToUnicodeEx")
 	procTranslateMessage  = moduser32.NewProc("TranslateMessage")
+	procUnregisterClassW  = moduser32.NewProc("UnregisterClassW")
 )
 
 func GetDC(hwnd syscall.Handle) (dc syscall.Handle, err error) {
@@ -284,3 +285,9 @@
 	done = r0 != 0
 	return
 }
+
+func _UnregisterClass(lpClassName *uint16, hInstance syscall.Handle) (done bool) {
+	r0, _, _ := syscall.Syscall(procUnregisterClassW.Addr(), 2, uintptr(unsafe.Pointer(lpClassName)), uintptr(hInstance), 0)
+	done = r0 != 0
+	return
+}