windows/registry: add code to access remote pc registry

Introduce OpenRemoteKey function that opens some
root registry keys on remote computer.

Also add PERFORMANCE_DATA key, since it is one of
root keys accessible via OpenRemoteKey.

Change-Id: I738fdfee52a34acd4dc09ddb91fcf0e4c707bd83
Reviewed-on: https://go-review.googlesource.com/33814
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Run-TryBot: Brad Fitzpatrick <bradfitz@golang.org>
TryBot-Result: Gobot Gobot <gobot@golang.org>
diff --git a/windows/registry/key.go b/windows/registry/key.go
index f087ce5..d0beb19 100644
--- a/windows/registry/key.go
+++ b/windows/registry/key.go
@@ -57,11 +57,12 @@
 	// An application can use these keys as entry points to the registry.
 	// Normally these keys are used in OpenKey to open new keys,
 	// but they can also be used anywhere a Key is required.
-	CLASSES_ROOT   = Key(syscall.HKEY_CLASSES_ROOT)
-	CURRENT_USER   = Key(syscall.HKEY_CURRENT_USER)
-	LOCAL_MACHINE  = Key(syscall.HKEY_LOCAL_MACHINE)
-	USERS          = Key(syscall.HKEY_USERS)
-	CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG)
+	CLASSES_ROOT     = Key(syscall.HKEY_CLASSES_ROOT)
+	CURRENT_USER     = Key(syscall.HKEY_CURRENT_USER)
+	LOCAL_MACHINE    = Key(syscall.HKEY_LOCAL_MACHINE)
+	USERS            = Key(syscall.HKEY_USERS)
+	CURRENT_CONFIG   = Key(syscall.HKEY_CURRENT_CONFIG)
+	PERFORMANCE_DATA = Key(syscall.HKEY_PERFORMANCE_DATA)
 )
 
 // Close closes open key k.
@@ -87,6 +88,27 @@
 	return Key(subkey), nil
 }
 
+// OpenRemoteKey opens a predefined registry key on another
+// computer pcname. The key to be opened is specified by k, but
+// can only be one of LOCAL_MACHINE, PERFORMANCE_DATA or USERS.
+// If pcname is "", OpenRemoteKey returns local computer key.
+func OpenRemoteKey(pcname string, k Key) (Key, error) {
+	var err error
+	var p *uint16
+	if pcname != "" {
+		p, err = syscall.UTF16PtrFromString(`\\` + pcname)
+		if err != nil {
+			return 0, err
+		}
+	}
+	var remoteKey syscall.Handle
+	err = regConnectRegistry(p, syscall.Handle(k), &remoteKey)
+	if err != nil {
+		return 0, err
+	}
+	return Key(remoteKey), nil
+}
+
 // ReadSubKeyNames returns the names of subkeys of key k.
 // The parameter n controls the number of returned names,
 // analogous to the way os.File.Readdirnames works.
diff --git a/windows/registry/syscall.go b/windows/registry/syscall.go
index a6525da..e66643c 100644
--- a/windows/registry/syscall.go
+++ b/windows/registry/syscall.go
@@ -27,5 +27,6 @@
 //sys	regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW
 //sys	regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW
 //sys   regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW
+//sys	regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) = advapi32.RegConnectRegistryW
 
 //sys	expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW
diff --git a/windows/registry/zsyscall_windows.go b/windows/registry/zsyscall_windows.go
index 1ea1c23..ceebdd7 100644
--- a/windows/registry/zsyscall_windows.go
+++ b/windows/registry/zsyscall_windows.go
@@ -46,6 +46,7 @@
 	procRegEnumValueW             = modadvapi32.NewProc("RegEnumValueW")
 	procRegDeleteValueW           = modadvapi32.NewProc("RegDeleteValueW")
 	procRegLoadMUIStringW         = modadvapi32.NewProc("RegLoadMUIStringW")
+	procRegConnectRegistryW       = modadvapi32.NewProc("RegConnectRegistryW")
 	procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
 )
 
@@ -97,6 +98,14 @@
 	return
 }
 
+func regConnectRegistry(machinename *uint16, key syscall.Handle, result *syscall.Handle) (regerrno error) {
+	r0, _, _ := syscall.Syscall(procRegConnectRegistryW.Addr(), 3, uintptr(unsafe.Pointer(machinename)), uintptr(key), uintptr(unsafe.Pointer(result)))
+	if r0 != 0 {
+		regerrno = syscall.Errno(r0)
+	}
+	return
+}
+
 func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
 	r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size))
 	n = uint32(r0)