Signed-off-by: John Howard <jhoward@microsoft.com>
| ... | ... |
@@ -9,6 +9,7 @@ import ( |
| 9 | 9 |
"path/filepath" |
| 10 | 10 |
"strconv" |
| 11 | 11 |
"strings" |
| 12 |
+ "time" |
|
| 12 | 13 |
|
| 13 | 14 |
"github.com/Sirupsen/logrus" |
| 14 | 15 |
"github.com/docker/docker/daemon/execdriver" |
| ... | ... |
@@ -223,10 +224,32 @@ func (d *Driver) Run(c *execdriver.Command, pipes *execdriver.Pipes, hooks execd |
| 223 | 223 |
|
| 224 | 224 |
configuration := string(configurationb) |
| 225 | 225 |
|
| 226 |
- err = hcsshim.CreateComputeSystem(c.ID, configuration) |
|
| 227 |
- if err != nil {
|
|
| 228 |
- logrus.Debugln("Failed to create temporary container ", err)
|
|
| 229 |
- return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 226 |
+ // TODO Windows TP5 timeframe. Remove when TP4 is no longer supported. |
|
| 227 |
+ // The following a workaround for Windows TP4 which has a networking |
|
| 228 |
+ // bug which fairly frequently returns an error. Back off and retry. |
|
| 229 |
+ maxAttempts := 1 |
|
| 230 |
+ if TP4RetryHack {
|
|
| 231 |
+ maxAttempts = 5 |
|
| 232 |
+ } |
|
| 233 |
+ i := 0 |
|
| 234 |
+ for i < maxAttempts {
|
|
| 235 |
+ i++ |
|
| 236 |
+ err = hcsshim.CreateComputeSystem(c.ID, configuration) |
|
| 237 |
+ if err != nil {
|
|
| 238 |
+ if TP4RetryHack {
|
|
| 239 |
+ if !strings.Contains(err.Error(), `Win32 API call returned error r1=2147746291`) && // Invalid class string |
|
| 240 |
+ !strings.Contains(err.Error(), `Win32 API call returned error r1=2147943568`) && // Element not found |
|
| 241 |
+ !strings.Contains(err.Error(), `Win32 API call returned error r1=2147942402`) && // The system cannot find the file specified |
|
| 242 |
+ !strings.Contains(err.Error(), `Win32 API call returned error r1=2147943622`) { // The network is not present or not started
|
|
| 243 |
+ logrus.Debugln("Failed to create temporary container ", err)
|
|
| 244 |
+ return execdriver.ExitStatus{ExitCode: -1}, err
|
|
| 245 |
+ } |
|
| 246 |
+ logrus.Warnf("Invoking Windows TP4 retry hack (%d of %d)", i, maxAttempts-1)
|
|
| 247 |
+ time.Sleep(50 * time.Millisecond) |
|
| 248 |
+ } |
|
| 249 |
+ } else {
|
|
| 250 |
+ break |
|
| 251 |
+ } |
|
| 230 | 252 |
} |
| 231 | 253 |
|
| 232 | 254 |
// Start the container |
| ... | ... |
@@ -4,6 +4,7 @@ package windows |
| 4 | 4 |
|
| 5 | 5 |
import ( |
| 6 | 6 |
"fmt" |
| 7 |
+ "strconv" |
|
| 7 | 8 |
"strings" |
| 8 | 9 |
"sync" |
| 9 | 10 |
|
| ... | ... |
@@ -12,8 +13,13 @@ import ( |
| 12 | 12 |
"github.com/docker/docker/dockerversion" |
| 13 | 13 |
"github.com/docker/docker/pkg/parsers" |
| 14 | 14 |
"github.com/docker/engine-api/types/container" |
| 15 |
+ "golang.org/x/sys/windows/registry" |
|
| 15 | 16 |
) |
| 16 | 17 |
|
| 18 |
+// TP4RetryHack is a hack to retry CreateComputeSystem if it fails with |
|
| 19 |
+// known return codes from Windows due to bugs in TP4. |
|
| 20 |
+var TP4RetryHack bool |
|
| 21 |
+ |
|
| 17 | 22 |
// This is a daemon development variable only and should not be |
| 18 | 23 |
// used for running production containers on Windows. |
| 19 | 24 |
var dummyMode bool |
| ... | ... |
@@ -89,6 +95,37 @@ func NewDriver(root string, options []string) (*Driver, error) {
|
| 89 | 89 |
} |
| 90 | 90 |
} |
| 91 | 91 |
|
| 92 |
+ // TODO Windows TP5 timeframe. Remove this next block of code once TP4 |
|
| 93 |
+ // is no longer supported. Also remove the workaround in run.go. |
|
| 94 |
+ // |
|
| 95 |
+ // Hack for TP4 - determine the version of Windows from the registry. |
|
| 96 |
+ // This overcomes an issue on TP4 which causes CreateComputeSystem to |
|
| 97 |
+ // intermittently fail. It's predominantly here to make Windows to Windows |
|
| 98 |
+ // CI more reliable. |
|
| 99 |
+ TP4RetryHack = false |
|
| 100 |
+ k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) |
|
| 101 |
+ if err != nil {
|
|
| 102 |
+ return &Driver{}, err
|
|
| 103 |
+ } |
|
| 104 |
+ defer k.Close() |
|
| 105 |
+ |
|
| 106 |
+ s, _, err := k.GetStringValue("BuildLab")
|
|
| 107 |
+ if err != nil {
|
|
| 108 |
+ return &Driver{}, err
|
|
| 109 |
+ } |
|
| 110 |
+ parts := strings.Split(s, ".") |
|
| 111 |
+ if len(parts) < 1 {
|
|
| 112 |
+ return &Driver{}, err
|
|
| 113 |
+ } |
|
| 114 |
+ var val int |
|
| 115 |
+ if val, err = strconv.Atoi(parts[0]); err != nil {
|
|
| 116 |
+ return &Driver{}, err
|
|
| 117 |
+ } |
|
| 118 |
+ if val < 14250 {
|
|
| 119 |
+ TP4RetryHack = true |
|
| 120 |
+ } |
|
| 121 |
+ // End of Windows TP4 hack |
|
| 122 |
+ |
|
| 92 | 123 |
return &Driver{
|
| 93 | 124 |
root: root, |
| 94 | 125 |
activeContainers: make(map[string]*activeContainer), |
| ... | ... |
@@ -20,6 +20,7 @@ clone git github.com/mistifyio/go-zfs v2.1.1 |
| 20 | 20 |
clone git github.com/tchap/go-patricia v2.1.0 |
| 21 | 21 |
clone git github.com/vdemeester/shakers 3c10293ce22b900c27acad7b28656196fcc2f73b |
| 22 | 22 |
clone git golang.org/x/net 47990a1ba55743e6ef1affd3a14e5bac8553615d https://github.com/golang/net.git |
| 23 |
+clone git golang.org/x/sys eb2c74142fd19a79b3f237334c7384d5167b1b46 https://github.com/golang/sys.git |
|
| 23 | 24 |
clone git github.com/docker/go-units 651fc226e7441360384da338d0fd37f2440ffbe3 |
| 24 | 25 |
clone git github.com/docker/go-connections v0.1.2 |
| 25 | 26 |
clone git github.com/docker/engine-api bdbab71ec21209ef56dffdbe42c9d21843c30862 |
| ... | ... |
@@ -17,21 +17,17 @@ import ( |
| 17 | 17 |
) |
| 18 | 18 |
|
| 19 | 19 |
func (s *DockerSuite) TestPsListContainersBase(c *check.C) {
|
| 20 |
- // TODO Windows CI (TP5 timeframe). This test has a very high likelyhood |
|
| 21 |
- // of hitting a known bug in TP4. Hence disabling for now until TP5 |
|
| 22 |
- // is available. |
|
| 23 |
- testRequires(c, DaemonIsLinux) |
|
| 24 |
- out, _ := dockerCmd(c, "run", "-d", "busybox", "top") |
|
| 20 |
+ out, _ := runSleepingContainer(c, "-d") |
|
| 25 | 21 |
firstID := strings.TrimSpace(out) |
| 26 | 22 |
|
| 27 |
- out, _ = dockerCmd(c, "run", "-d", "busybox", "top") |
|
| 23 |
+ out, _ = runSleepingContainer(c, "-d") |
|
| 28 | 24 |
secondID := strings.TrimSpace(out) |
| 29 | 25 |
|
| 30 | 26 |
// not long running |
| 31 | 27 |
out, _ = dockerCmd(c, "run", "-d", "busybox", "true") |
| 32 | 28 |
thirdID := strings.TrimSpace(out) |
| 33 | 29 |
|
| 34 |
- out, _ = dockerCmd(c, "run", "-d", "busybox", "top") |
|
| 30 |
+ out, _ = runSleepingContainer(c, "-d") |
|
| 35 | 31 |
fourthID := strings.TrimSpace(out) |
| 36 | 32 |
|
| 37 | 33 |
// make sure the second is running |
| ... | ... |
@@ -172,10 +168,6 @@ func (s *DockerSuite) TestPsListContainersSize(c *check.C) {
|
| 172 | 172 |
} |
| 173 | 173 |
|
| 174 | 174 |
func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
|
| 175 |
- // TODO Windows CI (TP5 timeframe). This test has a very high likelyhood |
|
| 176 |
- // of hitting a known bug in TP4. Hence disabling for now until TP5 |
|
| 177 |
- // is available. |
|
| 178 |
- testRequires(c, DaemonIsLinux) |
|
| 179 | 175 |
// start exited container |
| 180 | 176 |
out, _ := dockerCmd(c, "run", "-d", "busybox") |
| 181 | 177 |
firstID := strings.TrimSpace(out) |
| ... | ... |
@@ -215,10 +207,6 @@ func (s *DockerSuite) TestPsListContainersFilterStatus(c *check.C) {
|
| 215 | 215 |
} |
| 216 | 216 |
|
| 217 | 217 |
func (s *DockerSuite) TestPsListContainersFilterID(c *check.C) {
|
| 218 |
- // TODO Windows CI (TP5 timeframe). This test has a very high likelyhood |
|
| 219 |
- // of hitting a known bug in TP4. Hence disabling for now until TP5 |
|
| 220 |
- // is available. |
|
| 221 |
- testRequires(c, DaemonIsLinux) |
|
| 222 | 218 |
// start container |
| 223 | 219 |
out, _ := dockerCmd(c, "run", "-d", "busybox") |
| 224 | 220 |
firstID := strings.TrimSpace(out) |
| ... | ... |
@@ -257,10 +245,6 @@ func (s *DockerSuite) TestPsListContainersFilterName(c *check.C) {
|
| 257 | 257 |
// - Run containers for each of those image (busybox, images_ps_filter_test1, images_ps_filter_test2) |
| 258 | 258 |
// - Filter them out :P |
| 259 | 259 |
func (s *DockerSuite) TestPsListContainersFilterAncestorImage(c *check.C) {
|
| 260 |
- // TODO Windows CI (TP5 timeframe). This test has a very high likelyhood |
|
| 261 |
- // of hitting a known bug in TP4. Hence disabling for now until TP5 |
|
| 262 |
- // is available. |
|
| 263 |
- testRequires(c, DaemonIsLinux) |
|
| 264 | 260 |
// Build images |
| 265 | 261 |
imageName1 := "images_ps_filter_test1" |
| 266 | 262 |
imageID1, err := buildImage(imageName1, |
| ... | ... |
@@ -356,10 +340,6 @@ func checkPsAncestorFilterOutput(c *check.C, out string, filterName string, expe |
| 356 | 356 |
} |
| 357 | 357 |
|
| 358 | 358 |
func (s *DockerSuite) TestPsListContainersFilterLabel(c *check.C) {
|
| 359 |
- // TODO Windows CI (TP5 timeframe). This test has a very high likelyhood |
|
| 360 |
- // of hitting a known bug in TP4. Hence disabling for now until TP5 |
|
| 361 |
- // is available. |
|
| 362 |
- testRequires(c, DaemonIsLinux) |
|
| 363 | 359 |
// start container |
| 364 | 360 |
out, _ := dockerCmd(c, "run", "-d", "-l", "match=me", "-l", "second=tag", "busybox") |
| 365 | 361 |
firstID := strings.TrimSpace(out) |
| ... | ... |
@@ -396,11 +376,6 @@ func (s *DockerSuite) TestPsListContainersFilterLabel(c *check.C) {
|
| 396 | 396 |
} |
| 397 | 397 |
|
| 398 | 398 |
func (s *DockerSuite) TestPsListContainersFilterExited(c *check.C) {
|
| 399 |
- // TODO Windows CI (TP5 timeframe). This test has a very high likelyhood |
|
| 400 |
- // of hitting a known bug in TP4. Hence disabling for now until TP5 |
|
| 401 |
- // is available. |
|
| 402 |
- testRequires(c, DaemonIsLinux) |
|
| 403 |
- testRequires(c, DaemonIsLinux) |
|
| 404 | 399 |
runSleepingContainer(c, "--name=sleep") |
| 405 | 400 |
|
| 406 | 401 |
dockerCmd(c, "run", "--name", "zero1", "busybox", "true") |
| 407 | 402 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,27 @@ |
| 0 |
+Copyright (c) 2009 The Go Authors. All rights reserved. |
|
| 1 |
+ |
|
| 2 |
+Redistribution and use in source and binary forms, with or without |
|
| 3 |
+modification, are permitted provided that the following conditions are |
|
| 4 |
+met: |
|
| 5 |
+ |
|
| 6 |
+ * Redistributions of source code must retain the above copyright |
|
| 7 |
+notice, this list of conditions and the following disclaimer. |
|
| 8 |
+ * Redistributions in binary form must reproduce the above |
|
| 9 |
+copyright notice, this list of conditions and the following disclaimer |
|
| 10 |
+in the documentation and/or other materials provided with the |
|
| 11 |
+distribution. |
|
| 12 |
+ * Neither the name of Google Inc. nor the names of its |
|
| 13 |
+contributors may be used to endorse or promote products derived from |
|
| 14 |
+this software without specific prior written permission. |
|
| 15 |
+ |
|
| 16 |
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
|
| 17 |
+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
|
| 18 |
+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
|
| 19 |
+A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
|
| 20 |
+OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
|
| 21 |
+SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
|
| 22 |
+LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
| 23 |
+DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
| 24 |
+THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
| 25 |
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|
| 26 |
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| 0 | 27 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,178 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build windows |
|
| 5 |
+ |
|
| 6 |
+// Package registry provides access to the Windows registry. |
|
| 7 |
+// |
|
| 8 |
+// Here is a simple example, opening a registry key and reading a string value from it. |
|
| 9 |
+// |
|
| 10 |
+// k, err := registry.OpenKey(registry.LOCAL_MACHINE, `SOFTWARE\Microsoft\Windows NT\CurrentVersion`, registry.QUERY_VALUE) |
|
| 11 |
+// if err != nil {
|
|
| 12 |
+// log.Fatal(err) |
|
| 13 |
+// } |
|
| 14 |
+// defer k.Close() |
|
| 15 |
+// |
|
| 16 |
+// s, _, err := k.GetStringValue("SystemRoot")
|
|
| 17 |
+// if err != nil {
|
|
| 18 |
+// log.Fatal(err) |
|
| 19 |
+// } |
|
| 20 |
+// fmt.Printf("Windows system root is %q\n", s)
|
|
| 21 |
+// |
|
| 22 |
+package registry |
|
| 23 |
+ |
|
| 24 |
+import ( |
|
| 25 |
+ "io" |
|
| 26 |
+ "syscall" |
|
| 27 |
+ "time" |
|
| 28 |
+) |
|
| 29 |
+ |
|
| 30 |
+const ( |
|
| 31 |
+ // Registry key security and access rights. |
|
| 32 |
+ // See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724878.aspx |
|
| 33 |
+ // for details. |
|
| 34 |
+ ALL_ACCESS = 0xf003f |
|
| 35 |
+ CREATE_LINK = 0x00020 |
|
| 36 |
+ CREATE_SUB_KEY = 0x00004 |
|
| 37 |
+ ENUMERATE_SUB_KEYS = 0x00008 |
|
| 38 |
+ EXECUTE = 0x20019 |
|
| 39 |
+ NOTIFY = 0x00010 |
|
| 40 |
+ QUERY_VALUE = 0x00001 |
|
| 41 |
+ READ = 0x20019 |
|
| 42 |
+ SET_VALUE = 0x00002 |
|
| 43 |
+ WOW64_32KEY = 0x00200 |
|
| 44 |
+ WOW64_64KEY = 0x00100 |
|
| 45 |
+ WRITE = 0x20006 |
|
| 46 |
+) |
|
| 47 |
+ |
|
| 48 |
+// Key is a handle to an open Windows registry key. |
|
| 49 |
+// Keys can be obtained by calling OpenKey; there are |
|
| 50 |
+// also some predefined root keys such as CURRENT_USER. |
|
| 51 |
+// Keys can be used directly in the Windows API. |
|
| 52 |
+type Key syscall.Handle |
|
| 53 |
+ |
|
| 54 |
+const ( |
|
| 55 |
+ // Windows defines some predefined root keys that are always open. |
|
| 56 |
+ // An application can use these keys as entry points to the registry. |
|
| 57 |
+ // Normally these keys are used in OpenKey to open new keys, |
|
| 58 |
+ // but they can also be used anywhere a Key is required. |
|
| 59 |
+ CLASSES_ROOT = Key(syscall.HKEY_CLASSES_ROOT) |
|
| 60 |
+ CURRENT_USER = Key(syscall.HKEY_CURRENT_USER) |
|
| 61 |
+ LOCAL_MACHINE = Key(syscall.HKEY_LOCAL_MACHINE) |
|
| 62 |
+ USERS = Key(syscall.HKEY_USERS) |
|
| 63 |
+ CURRENT_CONFIG = Key(syscall.HKEY_CURRENT_CONFIG) |
|
| 64 |
+) |
|
| 65 |
+ |
|
| 66 |
+// Close closes open key k. |
|
| 67 |
+func (k Key) Close() error {
|
|
| 68 |
+ return syscall.RegCloseKey(syscall.Handle(k)) |
|
| 69 |
+} |
|
| 70 |
+ |
|
| 71 |
+// OpenKey opens a new key with path name relative to key k. |
|
| 72 |
+// It accepts any open key, including CURRENT_USER and others, |
|
| 73 |
+// and returns the new key and an error. |
|
| 74 |
+// The access parameter specifies desired access rights to the |
|
| 75 |
+// key to be opened. |
|
| 76 |
+func OpenKey(k Key, path string, access uint32) (Key, error) {
|
|
| 77 |
+ p, err := syscall.UTF16PtrFromString(path) |
|
| 78 |
+ if err != nil {
|
|
| 79 |
+ return 0, err |
|
| 80 |
+ } |
|
| 81 |
+ var subkey syscall.Handle |
|
| 82 |
+ err = syscall.RegOpenKeyEx(syscall.Handle(k), p, 0, access, &subkey) |
|
| 83 |
+ if err != nil {
|
|
| 84 |
+ return 0, err |
|
| 85 |
+ } |
|
| 86 |
+ return Key(subkey), nil |
|
| 87 |
+} |
|
| 88 |
+ |
|
| 89 |
+// ReadSubKeyNames returns the names of subkeys of key k. |
|
| 90 |
+// The parameter n controls the number of returned names, |
|
| 91 |
+// analogous to the way os.File.Readdirnames works. |
|
| 92 |
+func (k Key) ReadSubKeyNames(n int) ([]string, error) {
|
|
| 93 |
+ ki, err := k.Stat() |
|
| 94 |
+ if err != nil {
|
|
| 95 |
+ return nil, err |
|
| 96 |
+ } |
|
| 97 |
+ names := make([]string, 0, ki.SubKeyCount) |
|
| 98 |
+ buf := make([]uint16, ki.MaxSubKeyLen+1) // extra room for terminating zero byte |
|
| 99 |
+loopItems: |
|
| 100 |
+ for i := uint32(0); ; i++ {
|
|
| 101 |
+ if n > 0 {
|
|
| 102 |
+ if len(names) == n {
|
|
| 103 |
+ return names, nil |
|
| 104 |
+ } |
|
| 105 |
+ } |
|
| 106 |
+ l := uint32(len(buf)) |
|
| 107 |
+ for {
|
|
| 108 |
+ err := syscall.RegEnumKeyEx(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil) |
|
| 109 |
+ if err == nil {
|
|
| 110 |
+ break |
|
| 111 |
+ } |
|
| 112 |
+ if err == syscall.ERROR_MORE_DATA {
|
|
| 113 |
+ // Double buffer size and try again. |
|
| 114 |
+ l = uint32(2 * len(buf)) |
|
| 115 |
+ buf = make([]uint16, l) |
|
| 116 |
+ continue |
|
| 117 |
+ } |
|
| 118 |
+ if err == _ERROR_NO_MORE_ITEMS {
|
|
| 119 |
+ break loopItems |
|
| 120 |
+ } |
|
| 121 |
+ return names, err |
|
| 122 |
+ } |
|
| 123 |
+ names = append(names, syscall.UTF16ToString(buf[:l])) |
|
| 124 |
+ } |
|
| 125 |
+ if n > len(names) {
|
|
| 126 |
+ return names, io.EOF |
|
| 127 |
+ } |
|
| 128 |
+ return names, nil |
|
| 129 |
+} |
|
| 130 |
+ |
|
| 131 |
+// CreateKey creates a key named path under open key k. |
|
| 132 |
+// CreateKey returns the new key and a boolean flag that reports |
|
| 133 |
+// whether the key already existed. |
|
| 134 |
+// The access parameter specifies the access rights for the key |
|
| 135 |
+// to be created. |
|
| 136 |
+func CreateKey(k Key, path string, access uint32) (newk Key, openedExisting bool, err error) {
|
|
| 137 |
+ var h syscall.Handle |
|
| 138 |
+ var d uint32 |
|
| 139 |
+ err = regCreateKeyEx(syscall.Handle(k), syscall.StringToUTF16Ptr(path), |
|
| 140 |
+ 0, nil, _REG_OPTION_NON_VOLATILE, access, nil, &h, &d) |
|
| 141 |
+ if err != nil {
|
|
| 142 |
+ return 0, false, err |
|
| 143 |
+ } |
|
| 144 |
+ return Key(h), d == _REG_OPENED_EXISTING_KEY, nil |
|
| 145 |
+} |
|
| 146 |
+ |
|
| 147 |
+// DeleteKey deletes the subkey path of key k and its values. |
|
| 148 |
+func DeleteKey(k Key, path string) error {
|
|
| 149 |
+ return regDeleteKey(syscall.Handle(k), syscall.StringToUTF16Ptr(path)) |
|
| 150 |
+} |
|
| 151 |
+ |
|
| 152 |
+// A KeyInfo describes the statistics of a key. It is returned by Stat. |
|
| 153 |
+type KeyInfo struct {
|
|
| 154 |
+ SubKeyCount uint32 |
|
| 155 |
+ MaxSubKeyLen uint32 // size of the key's subkey with the longest name, in Unicode characters, not including the terminating zero byte |
|
| 156 |
+ ValueCount uint32 |
|
| 157 |
+ MaxValueNameLen uint32 // size of the key's longest value name, in Unicode characters, not including the terminating zero byte |
|
| 158 |
+ MaxValueLen uint32 // longest data component among the key's values, in bytes |
|
| 159 |
+ lastWriteTime syscall.Filetime |
|
| 160 |
+} |
|
| 161 |
+ |
|
| 162 |
+// ModTime returns the key's last write time. |
|
| 163 |
+func (ki *KeyInfo) ModTime() time.Time {
|
|
| 164 |
+ return time.Unix(0, ki.lastWriteTime.Nanoseconds()) |
|
| 165 |
+} |
|
| 166 |
+ |
|
| 167 |
+// Stat retrieves information about the open key k. |
|
| 168 |
+func (k Key) Stat() (*KeyInfo, error) {
|
|
| 169 |
+ var ki KeyInfo |
|
| 170 |
+ err := syscall.RegQueryInfoKey(syscall.Handle(k), nil, nil, nil, |
|
| 171 |
+ &ki.SubKeyCount, &ki.MaxSubKeyLen, nil, &ki.ValueCount, |
|
| 172 |
+ &ki.MaxValueNameLen, &ki.MaxValueLen, nil, &ki.lastWriteTime) |
|
| 173 |
+ if err != nil {
|
|
| 174 |
+ return nil, err |
|
| 175 |
+ } |
|
| 176 |
+ return &ki, nil |
|
| 177 |
+} |
| 0 | 178 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,33 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build windows |
|
| 5 |
+ |
|
| 6 |
+package registry |
|
| 7 |
+ |
|
| 8 |
+import "syscall" |
|
| 9 |
+ |
|
| 10 |
+//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall.go |
|
| 11 |
+ |
|
| 12 |
+const ( |
|
| 13 |
+ _REG_OPTION_NON_VOLATILE = 0 |
|
| 14 |
+ |
|
| 15 |
+ _REG_CREATED_NEW_KEY = 1 |
|
| 16 |
+ _REG_OPENED_EXISTING_KEY = 2 |
|
| 17 |
+ |
|
| 18 |
+ _ERROR_NO_MORE_ITEMS syscall.Errno = 259 |
|
| 19 |
+) |
|
| 20 |
+ |
|
| 21 |
+func LoadRegLoadMUIString() error {
|
|
| 22 |
+ return procRegLoadMUIStringW.Find() |
|
| 23 |
+} |
|
| 24 |
+ |
|
| 25 |
+//sys regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) = advapi32.RegCreateKeyExW |
|
| 26 |
+//sys regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) = advapi32.RegDeleteKeyW |
|
| 27 |
+//sys regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) = advapi32.RegSetValueExW |
|
| 28 |
+//sys regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) = advapi32.RegEnumValueW |
|
| 29 |
+//sys regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) = advapi32.RegDeleteValueW |
|
| 30 |
+//sys regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) = advapi32.RegLoadMUIStringW |
|
| 31 |
+ |
|
| 32 |
+//sys expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) = kernel32.ExpandEnvironmentStringsW |
| 0 | 33 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,384 @@ |
| 0 |
+// Copyright 2015 The Go Authors. All rights reserved. |
|
| 1 |
+// Use of this source code is governed by a BSD-style |
|
| 2 |
+// license that can be found in the LICENSE file. |
|
| 3 |
+ |
|
| 4 |
+// +build windows |
|
| 5 |
+ |
|
| 6 |
+package registry |
|
| 7 |
+ |
|
| 8 |
+import ( |
|
| 9 |
+ "errors" |
|
| 10 |
+ "io" |
|
| 11 |
+ "syscall" |
|
| 12 |
+ "unicode/utf16" |
|
| 13 |
+ "unsafe" |
|
| 14 |
+) |
|
| 15 |
+ |
|
| 16 |
+const ( |
|
| 17 |
+ // Registry value types. |
|
| 18 |
+ NONE = 0 |
|
| 19 |
+ SZ = 1 |
|
| 20 |
+ EXPAND_SZ = 2 |
|
| 21 |
+ BINARY = 3 |
|
| 22 |
+ DWORD = 4 |
|
| 23 |
+ DWORD_BIG_ENDIAN = 5 |
|
| 24 |
+ LINK = 6 |
|
| 25 |
+ MULTI_SZ = 7 |
|
| 26 |
+ RESOURCE_LIST = 8 |
|
| 27 |
+ FULL_RESOURCE_DESCRIPTOR = 9 |
|
| 28 |
+ RESOURCE_REQUIREMENTS_LIST = 10 |
|
| 29 |
+ QWORD = 11 |
|
| 30 |
+) |
|
| 31 |
+ |
|
| 32 |
+var ( |
|
| 33 |
+ // ErrShortBuffer is returned when the buffer was too short for the operation. |
|
| 34 |
+ ErrShortBuffer = syscall.ERROR_MORE_DATA |
|
| 35 |
+ |
|
| 36 |
+ // ErrNotExist is returned when a registry key or value does not exist. |
|
| 37 |
+ ErrNotExist = syscall.ERROR_FILE_NOT_FOUND |
|
| 38 |
+ |
|
| 39 |
+ // ErrUnexpectedType is returned by Get*Value when the value's type was unexpected. |
|
| 40 |
+ ErrUnexpectedType = errors.New("unexpected key value type")
|
|
| 41 |
+) |
|
| 42 |
+ |
|
| 43 |
+// GetValue retrieves the type and data for the specified value associated |
|
| 44 |
+// with an open key k. It fills up buffer buf and returns the retrieved |
|
| 45 |
+// byte count n. If buf is too small to fit the stored value it returns |
|
| 46 |
+// ErrShortBuffer error along with the required buffer size n. |
|
| 47 |
+// If no buffer is provided, it returns true and actual buffer size n. |
|
| 48 |
+// If no buffer is provided, GetValue returns the value's type only. |
|
| 49 |
+// If the value does not exist, the error returned is ErrNotExist. |
|
| 50 |
+// |
|
| 51 |
+// GetValue is a low level function. If value's type is known, use the appropriate |
|
| 52 |
+// Get*Value function instead. |
|
| 53 |
+func (k Key) GetValue(name string, buf []byte) (n int, valtype uint32, err error) {
|
|
| 54 |
+ pname, err := syscall.UTF16PtrFromString(name) |
|
| 55 |
+ if err != nil {
|
|
| 56 |
+ return 0, 0, err |
|
| 57 |
+ } |
|
| 58 |
+ var pbuf *byte |
|
| 59 |
+ if len(buf) > 0 {
|
|
| 60 |
+ pbuf = (*byte)(unsafe.Pointer(&buf[0])) |
|
| 61 |
+ } |
|
| 62 |
+ l := uint32(len(buf)) |
|
| 63 |
+ err = syscall.RegQueryValueEx(syscall.Handle(k), pname, nil, &valtype, pbuf, &l) |
|
| 64 |
+ if err != nil {
|
|
| 65 |
+ return int(l), valtype, err |
|
| 66 |
+ } |
|
| 67 |
+ return int(l), valtype, nil |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+func (k Key) getValue(name string, buf []byte) (date []byte, valtype uint32, err error) {
|
|
| 71 |
+ p, err := syscall.UTF16PtrFromString(name) |
|
| 72 |
+ if err != nil {
|
|
| 73 |
+ return nil, 0, err |
|
| 74 |
+ } |
|
| 75 |
+ var t uint32 |
|
| 76 |
+ n := uint32(len(buf)) |
|
| 77 |
+ for {
|
|
| 78 |
+ err = syscall.RegQueryValueEx(syscall.Handle(k), p, nil, &t, (*byte)(unsafe.Pointer(&buf[0])), &n) |
|
| 79 |
+ if err == nil {
|
|
| 80 |
+ return buf[:n], t, nil |
|
| 81 |
+ } |
|
| 82 |
+ if err != syscall.ERROR_MORE_DATA {
|
|
| 83 |
+ return nil, 0, err |
|
| 84 |
+ } |
|
| 85 |
+ if n <= uint32(len(buf)) {
|
|
| 86 |
+ return nil, 0, err |
|
| 87 |
+ } |
|
| 88 |
+ buf = make([]byte, n) |
|
| 89 |
+ } |
|
| 90 |
+} |
|
| 91 |
+ |
|
| 92 |
+// GetStringValue retrieves the string value for the specified |
|
| 93 |
+// value name associated with an open key k. It also returns the value's type. |
|
| 94 |
+// If value does not exist, GetStringValue returns ErrNotExist. |
|
| 95 |
+// If value is not SZ or EXPAND_SZ, it will return the correct value |
|
| 96 |
+// type and ErrUnexpectedType. |
|
| 97 |
+func (k Key) GetStringValue(name string) (val string, valtype uint32, err error) {
|
|
| 98 |
+ data, typ, err2 := k.getValue(name, make([]byte, 64)) |
|
| 99 |
+ if err2 != nil {
|
|
| 100 |
+ return "", typ, err2 |
|
| 101 |
+ } |
|
| 102 |
+ switch typ {
|
|
| 103 |
+ case SZ, EXPAND_SZ: |
|
| 104 |
+ default: |
|
| 105 |
+ return "", typ, ErrUnexpectedType |
|
| 106 |
+ } |
|
| 107 |
+ if len(data) == 0 {
|
|
| 108 |
+ return "", typ, nil |
|
| 109 |
+ } |
|
| 110 |
+ u := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:] |
|
| 111 |
+ return syscall.UTF16ToString(u), typ, nil |
|
| 112 |
+} |
|
| 113 |
+ |
|
| 114 |
+// GetMUIStringValue retrieves the localized string value for |
|
| 115 |
+// the specified value name associated with an open key k. |
|
| 116 |
+// If the value name doesn't exist or the localized string value |
|
| 117 |
+// can't be resolved, GetMUIStringValue returns ErrNotExist. |
|
| 118 |
+// GetMUIStringValue panics if the system doesn't support |
|
| 119 |
+// regLoadMUIString; use LoadRegLoadMUIString to check if |
|
| 120 |
+// regLoadMUIString is supported before calling this function. |
|
| 121 |
+func (k Key) GetMUIStringValue(name string) (string, error) {
|
|
| 122 |
+ pname, err := syscall.UTF16PtrFromString(name) |
|
| 123 |
+ if err != nil {
|
|
| 124 |
+ return "", err |
|
| 125 |
+ } |
|
| 126 |
+ |
|
| 127 |
+ buf := make([]uint16, 1024) |
|
| 128 |
+ var buflen uint32 |
|
| 129 |
+ var pdir *uint16 |
|
| 130 |
+ |
|
| 131 |
+ err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) |
|
| 132 |
+ if err == syscall.ERROR_FILE_NOT_FOUND { // Try fallback path
|
|
| 133 |
+ |
|
| 134 |
+ // Try to resolve the string value using the system directory as |
|
| 135 |
+ // a DLL search path; this assumes the string value is of the form |
|
| 136 |
+ // @[path]\dllname,-strID but with no path given, e.g. @tzres.dll,-320. |
|
| 137 |
+ |
|
| 138 |
+ // This approach works with tzres.dll but may have to be revised |
|
| 139 |
+ // in the future to allow callers to provide custom search paths. |
|
| 140 |
+ |
|
| 141 |
+ var s string |
|
| 142 |
+ s, err = ExpandString("%SystemRoot%\\system32\\")
|
|
| 143 |
+ if err != nil {
|
|
| 144 |
+ return "", err |
|
| 145 |
+ } |
|
| 146 |
+ pdir, err = syscall.UTF16PtrFromString(s) |
|
| 147 |
+ if err != nil {
|
|
| 148 |
+ return "", err |
|
| 149 |
+ } |
|
| 150 |
+ |
|
| 151 |
+ err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) |
|
| 152 |
+ } |
|
| 153 |
+ |
|
| 154 |
+ for err == syscall.ERROR_MORE_DATA { // Grow buffer if needed
|
|
| 155 |
+ if buflen <= uint32(len(buf)) {
|
|
| 156 |
+ break // Buffer not growing, assume race; break |
|
| 157 |
+ } |
|
| 158 |
+ buf = make([]uint16, buflen) |
|
| 159 |
+ err = regLoadMUIString(syscall.Handle(k), pname, &buf[0], uint32(len(buf)), &buflen, 0, pdir) |
|
| 160 |
+ } |
|
| 161 |
+ |
|
| 162 |
+ if err != nil {
|
|
| 163 |
+ return "", err |
|
| 164 |
+ } |
|
| 165 |
+ |
|
| 166 |
+ return syscall.UTF16ToString(buf), nil |
|
| 167 |
+} |
|
| 168 |
+ |
|
| 169 |
+// ExpandString expands environment-variable strings and replaces |
|
| 170 |
+// them with the values defined for the current user. |
|
| 171 |
+// Use ExpandString to expand EXPAND_SZ strings. |
|
| 172 |
+func ExpandString(value string) (string, error) {
|
|
| 173 |
+ if value == "" {
|
|
| 174 |
+ return "", nil |
|
| 175 |
+ } |
|
| 176 |
+ p, err := syscall.UTF16PtrFromString(value) |
|
| 177 |
+ if err != nil {
|
|
| 178 |
+ return "", err |
|
| 179 |
+ } |
|
| 180 |
+ r := make([]uint16, 100) |
|
| 181 |
+ for {
|
|
| 182 |
+ n, err := expandEnvironmentStrings(p, &r[0], uint32(len(r))) |
|
| 183 |
+ if err != nil {
|
|
| 184 |
+ return "", err |
|
| 185 |
+ } |
|
| 186 |
+ if n <= uint32(len(r)) {
|
|
| 187 |
+ u := (*[1 << 29]uint16)(unsafe.Pointer(&r[0]))[:] |
|
| 188 |
+ return syscall.UTF16ToString(u), nil |
|
| 189 |
+ } |
|
| 190 |
+ r = make([]uint16, n) |
|
| 191 |
+ } |
|
| 192 |
+} |
|
| 193 |
+ |
|
| 194 |
+// GetStringsValue retrieves the []string value for the specified |
|
| 195 |
+// value name associated with an open key k. It also returns the value's type. |
|
| 196 |
+// If value does not exist, GetStringsValue returns ErrNotExist. |
|
| 197 |
+// If value is not MULTI_SZ, it will return the correct value |
|
| 198 |
+// type and ErrUnexpectedType. |
|
| 199 |
+func (k Key) GetStringsValue(name string) (val []string, valtype uint32, err error) {
|
|
| 200 |
+ data, typ, err2 := k.getValue(name, make([]byte, 64)) |
|
| 201 |
+ if err2 != nil {
|
|
| 202 |
+ return nil, typ, err2 |
|
| 203 |
+ } |
|
| 204 |
+ if typ != MULTI_SZ {
|
|
| 205 |
+ return nil, typ, ErrUnexpectedType |
|
| 206 |
+ } |
|
| 207 |
+ if len(data) == 0 {
|
|
| 208 |
+ return nil, typ, nil |
|
| 209 |
+ } |
|
| 210 |
+ p := (*[1 << 29]uint16)(unsafe.Pointer(&data[0]))[:len(data)/2] |
|
| 211 |
+ if len(p) == 0 {
|
|
| 212 |
+ return nil, typ, nil |
|
| 213 |
+ } |
|
| 214 |
+ if p[len(p)-1] == 0 {
|
|
| 215 |
+ p = p[:len(p)-1] // remove terminating null |
|
| 216 |
+ } |
|
| 217 |
+ val = make([]string, 0, 5) |
|
| 218 |
+ from := 0 |
|
| 219 |
+ for i, c := range p {
|
|
| 220 |
+ if c == 0 {
|
|
| 221 |
+ val = append(val, string(utf16.Decode(p[from:i]))) |
|
| 222 |
+ from = i + 1 |
|
| 223 |
+ } |
|
| 224 |
+ } |
|
| 225 |
+ return val, typ, nil |
|
| 226 |
+} |
|
| 227 |
+ |
|
| 228 |
+// GetIntegerValue retrieves the integer value for the specified |
|
| 229 |
+// value name associated with an open key k. It also returns the value's type. |
|
| 230 |
+// If value does not exist, GetIntegerValue returns ErrNotExist. |
|
| 231 |
+// If value is not DWORD or QWORD, it will return the correct value |
|
| 232 |
+// type and ErrUnexpectedType. |
|
| 233 |
+func (k Key) GetIntegerValue(name string) (val uint64, valtype uint32, err error) {
|
|
| 234 |
+ data, typ, err2 := k.getValue(name, make([]byte, 8)) |
|
| 235 |
+ if err2 != nil {
|
|
| 236 |
+ return 0, typ, err2 |
|
| 237 |
+ } |
|
| 238 |
+ switch typ {
|
|
| 239 |
+ case DWORD: |
|
| 240 |
+ if len(data) != 4 {
|
|
| 241 |
+ return 0, typ, errors.New("DWORD value is not 4 bytes long")
|
|
| 242 |
+ } |
|
| 243 |
+ return uint64(*(*uint32)(unsafe.Pointer(&data[0]))), DWORD, nil |
|
| 244 |
+ case QWORD: |
|
| 245 |
+ if len(data) != 8 {
|
|
| 246 |
+ return 0, typ, errors.New("QWORD value is not 8 bytes long")
|
|
| 247 |
+ } |
|
| 248 |
+ return uint64(*(*uint64)(unsafe.Pointer(&data[0]))), QWORD, nil |
|
| 249 |
+ default: |
|
| 250 |
+ return 0, typ, ErrUnexpectedType |
|
| 251 |
+ } |
|
| 252 |
+} |
|
| 253 |
+ |
|
| 254 |
+// GetBinaryValue retrieves the binary value for the specified |
|
| 255 |
+// value name associated with an open key k. It also returns the value's type. |
|
| 256 |
+// If value does not exist, GetBinaryValue returns ErrNotExist. |
|
| 257 |
+// If value is not BINARY, it will return the correct value |
|
| 258 |
+// type and ErrUnexpectedType. |
|
| 259 |
+func (k Key) GetBinaryValue(name string) (val []byte, valtype uint32, err error) {
|
|
| 260 |
+ data, typ, err2 := k.getValue(name, make([]byte, 64)) |
|
| 261 |
+ if err2 != nil {
|
|
| 262 |
+ return nil, typ, err2 |
|
| 263 |
+ } |
|
| 264 |
+ if typ != BINARY {
|
|
| 265 |
+ return nil, typ, ErrUnexpectedType |
|
| 266 |
+ } |
|
| 267 |
+ return data, typ, nil |
|
| 268 |
+} |
|
| 269 |
+ |
|
| 270 |
+func (k Key) setValue(name string, valtype uint32, data []byte) error {
|
|
| 271 |
+ p, err := syscall.UTF16PtrFromString(name) |
|
| 272 |
+ if err != nil {
|
|
| 273 |
+ return err |
|
| 274 |
+ } |
|
| 275 |
+ if len(data) == 0 {
|
|
| 276 |
+ return regSetValueEx(syscall.Handle(k), p, 0, valtype, nil, 0) |
|
| 277 |
+ } |
|
| 278 |
+ return regSetValueEx(syscall.Handle(k), p, 0, valtype, &data[0], uint32(len(data))) |
|
| 279 |
+} |
|
| 280 |
+ |
|
| 281 |
+// SetDWordValue sets the data and type of a name value |
|
| 282 |
+// under key k to value and DWORD. |
|
| 283 |
+func (k Key) SetDWordValue(name string, value uint32) error {
|
|
| 284 |
+ return k.setValue(name, DWORD, (*[4]byte)(unsafe.Pointer(&value))[:]) |
|
| 285 |
+} |
|
| 286 |
+ |
|
| 287 |
+// SetQWordValue sets the data and type of a name value |
|
| 288 |
+// under key k to value and QWORD. |
|
| 289 |
+func (k Key) SetQWordValue(name string, value uint64) error {
|
|
| 290 |
+ return k.setValue(name, QWORD, (*[8]byte)(unsafe.Pointer(&value))[:]) |
|
| 291 |
+} |
|
| 292 |
+ |
|
| 293 |
+func (k Key) setStringValue(name string, valtype uint32, value string) error {
|
|
| 294 |
+ v, err := syscall.UTF16FromString(value) |
|
| 295 |
+ if err != nil {
|
|
| 296 |
+ return err |
|
| 297 |
+ } |
|
| 298 |
+ buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2] |
|
| 299 |
+ return k.setValue(name, valtype, buf) |
|
| 300 |
+} |
|
| 301 |
+ |
|
| 302 |
+// SetStringValue sets the data and type of a name value |
|
| 303 |
+// under key k to value and SZ. The value must not contain a zero byte. |
|
| 304 |
+func (k Key) SetStringValue(name, value string) error {
|
|
| 305 |
+ return k.setStringValue(name, SZ, value) |
|
| 306 |
+} |
|
| 307 |
+ |
|
| 308 |
+// SetExpandStringValue sets the data and type of a name value |
|
| 309 |
+// under key k to value and EXPAND_SZ. The value must not contain a zero byte. |
|
| 310 |
+func (k Key) SetExpandStringValue(name, value string) error {
|
|
| 311 |
+ return k.setStringValue(name, EXPAND_SZ, value) |
|
| 312 |
+} |
|
| 313 |
+ |
|
| 314 |
+// SetStringsValue sets the data and type of a name value |
|
| 315 |
+// under key k to value and MULTI_SZ. The value strings |
|
| 316 |
+// must not contain a zero byte. |
|
| 317 |
+func (k Key) SetStringsValue(name string, value []string) error {
|
|
| 318 |
+ ss := "" |
|
| 319 |
+ for _, s := range value {
|
|
| 320 |
+ for i := 0; i < len(s); i++ {
|
|
| 321 |
+ if s[i] == 0 {
|
|
| 322 |
+ return errors.New("string cannot have 0 inside")
|
|
| 323 |
+ } |
|
| 324 |
+ } |
|
| 325 |
+ ss += s + "\x00" |
|
| 326 |
+ } |
|
| 327 |
+ v := utf16.Encode([]rune(ss + "\x00")) |
|
| 328 |
+ buf := (*[1 << 29]byte)(unsafe.Pointer(&v[0]))[:len(v)*2] |
|
| 329 |
+ return k.setValue(name, MULTI_SZ, buf) |
|
| 330 |
+} |
|
| 331 |
+ |
|
| 332 |
+// SetBinaryValue sets the data and type of a name value |
|
| 333 |
+// under key k to value and BINARY. |
|
| 334 |
+func (k Key) SetBinaryValue(name string, value []byte) error {
|
|
| 335 |
+ return k.setValue(name, BINARY, value) |
|
| 336 |
+} |
|
| 337 |
+ |
|
| 338 |
+// DeleteValue removes a named value from the key k. |
|
| 339 |
+func (k Key) DeleteValue(name string) error {
|
|
| 340 |
+ return regDeleteValue(syscall.Handle(k), syscall.StringToUTF16Ptr(name)) |
|
| 341 |
+} |
|
| 342 |
+ |
|
| 343 |
+// ReadValueNames returns the value names of key k. |
|
| 344 |
+// The parameter n controls the number of returned names, |
|
| 345 |
+// analogous to the way os.File.Readdirnames works. |
|
| 346 |
+func (k Key) ReadValueNames(n int) ([]string, error) {
|
|
| 347 |
+ ki, err := k.Stat() |
|
| 348 |
+ if err != nil {
|
|
| 349 |
+ return nil, err |
|
| 350 |
+ } |
|
| 351 |
+ names := make([]string, 0, ki.ValueCount) |
|
| 352 |
+ buf := make([]uint16, ki.MaxValueNameLen+1) // extra room for terminating null character |
|
| 353 |
+loopItems: |
|
| 354 |
+ for i := uint32(0); ; i++ {
|
|
| 355 |
+ if n > 0 {
|
|
| 356 |
+ if len(names) == n {
|
|
| 357 |
+ return names, nil |
|
| 358 |
+ } |
|
| 359 |
+ } |
|
| 360 |
+ l := uint32(len(buf)) |
|
| 361 |
+ for {
|
|
| 362 |
+ err := regEnumValue(syscall.Handle(k), i, &buf[0], &l, nil, nil, nil, nil) |
|
| 363 |
+ if err == nil {
|
|
| 364 |
+ break |
|
| 365 |
+ } |
|
| 366 |
+ if err == syscall.ERROR_MORE_DATA {
|
|
| 367 |
+ // Double buffer size and try again. |
|
| 368 |
+ l = uint32(2 * len(buf)) |
|
| 369 |
+ buf = make([]uint16, l) |
|
| 370 |
+ continue |
|
| 371 |
+ } |
|
| 372 |
+ if err == _ERROR_NO_MORE_ITEMS {
|
|
| 373 |
+ break loopItems |
|
| 374 |
+ } |
|
| 375 |
+ return names, err |
|
| 376 |
+ } |
|
| 377 |
+ names = append(names, syscall.UTF16ToString(buf[:l])) |
|
| 378 |
+ } |
|
| 379 |
+ if n > len(names) {
|
|
| 380 |
+ return names, io.EOF |
|
| 381 |
+ } |
|
| 382 |
+ return names, nil |
|
| 383 |
+} |
| 0 | 384 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,82 @@ |
| 0 |
+// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT |
|
| 1 |
+ |
|
| 2 |
+package registry |
|
| 3 |
+ |
|
| 4 |
+import "unsafe" |
|
| 5 |
+import "syscall" |
|
| 6 |
+ |
|
| 7 |
+var _ unsafe.Pointer |
|
| 8 |
+ |
|
| 9 |
+var ( |
|
| 10 |
+ modadvapi32 = syscall.NewLazyDLL("advapi32.dll")
|
|
| 11 |
+ modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
|
| 12 |
+ |
|
| 13 |
+ procRegCreateKeyExW = modadvapi32.NewProc("RegCreateKeyExW")
|
|
| 14 |
+ procRegDeleteKeyW = modadvapi32.NewProc("RegDeleteKeyW")
|
|
| 15 |
+ procRegSetValueExW = modadvapi32.NewProc("RegSetValueExW")
|
|
| 16 |
+ procRegEnumValueW = modadvapi32.NewProc("RegEnumValueW")
|
|
| 17 |
+ procRegDeleteValueW = modadvapi32.NewProc("RegDeleteValueW")
|
|
| 18 |
+ procRegLoadMUIStringW = modadvapi32.NewProc("RegLoadMUIStringW")
|
|
| 19 |
+ procExpandEnvironmentStringsW = modkernel32.NewProc("ExpandEnvironmentStringsW")
|
|
| 20 |
+) |
|
| 21 |
+ |
|
| 22 |
+func regCreateKeyEx(key syscall.Handle, subkey *uint16, reserved uint32, class *uint16, options uint32, desired uint32, sa *syscall.SecurityAttributes, result *syscall.Handle, disposition *uint32) (regerrno error) {
|
|
| 23 |
+ r0, _, _ := syscall.Syscall9(procRegCreateKeyExW.Addr(), 9, uintptr(key), uintptr(unsafe.Pointer(subkey)), uintptr(reserved), uintptr(unsafe.Pointer(class)), uintptr(options), uintptr(desired), uintptr(unsafe.Pointer(sa)), uintptr(unsafe.Pointer(result)), uintptr(unsafe.Pointer(disposition))) |
|
| 24 |
+ if r0 != 0 {
|
|
| 25 |
+ regerrno = syscall.Errno(r0) |
|
| 26 |
+ } |
|
| 27 |
+ return |
|
| 28 |
+} |
|
| 29 |
+ |
|
| 30 |
+func regDeleteKey(key syscall.Handle, subkey *uint16) (regerrno error) {
|
|
| 31 |
+ r0, _, _ := syscall.Syscall(procRegDeleteKeyW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(subkey)), 0) |
|
| 32 |
+ if r0 != 0 {
|
|
| 33 |
+ regerrno = syscall.Errno(r0) |
|
| 34 |
+ } |
|
| 35 |
+ return |
|
| 36 |
+} |
|
| 37 |
+ |
|
| 38 |
+func regSetValueEx(key syscall.Handle, valueName *uint16, reserved uint32, vtype uint32, buf *byte, bufsize uint32) (regerrno error) {
|
|
| 39 |
+ r0, _, _ := syscall.Syscall6(procRegSetValueExW.Addr(), 6, uintptr(key), uintptr(unsafe.Pointer(valueName)), uintptr(reserved), uintptr(vtype), uintptr(unsafe.Pointer(buf)), uintptr(bufsize)) |
|
| 40 |
+ if r0 != 0 {
|
|
| 41 |
+ regerrno = syscall.Errno(r0) |
|
| 42 |
+ } |
|
| 43 |
+ return |
|
| 44 |
+} |
|
| 45 |
+ |
|
| 46 |
+func regEnumValue(key syscall.Handle, index uint32, name *uint16, nameLen *uint32, reserved *uint32, valtype *uint32, buf *byte, buflen *uint32) (regerrno error) {
|
|
| 47 |
+ r0, _, _ := syscall.Syscall9(procRegEnumValueW.Addr(), 8, uintptr(key), uintptr(index), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(nameLen)), uintptr(unsafe.Pointer(reserved)), uintptr(unsafe.Pointer(valtype)), uintptr(unsafe.Pointer(buf)), uintptr(unsafe.Pointer(buflen)), 0) |
|
| 48 |
+ if r0 != 0 {
|
|
| 49 |
+ regerrno = syscall.Errno(r0) |
|
| 50 |
+ } |
|
| 51 |
+ return |
|
| 52 |
+} |
|
| 53 |
+ |
|
| 54 |
+func regDeleteValue(key syscall.Handle, name *uint16) (regerrno error) {
|
|
| 55 |
+ r0, _, _ := syscall.Syscall(procRegDeleteValueW.Addr(), 2, uintptr(key), uintptr(unsafe.Pointer(name)), 0) |
|
| 56 |
+ if r0 != 0 {
|
|
| 57 |
+ regerrno = syscall.Errno(r0) |
|
| 58 |
+ } |
|
| 59 |
+ return |
|
| 60 |
+} |
|
| 61 |
+ |
|
| 62 |
+func regLoadMUIString(key syscall.Handle, name *uint16, buf *uint16, buflen uint32, buflenCopied *uint32, flags uint32, dir *uint16) (regerrno error) {
|
|
| 63 |
+ r0, _, _ := syscall.Syscall9(procRegLoadMUIStringW.Addr(), 7, uintptr(key), uintptr(unsafe.Pointer(name)), uintptr(unsafe.Pointer(buf)), uintptr(buflen), uintptr(unsafe.Pointer(buflenCopied)), uintptr(flags), uintptr(unsafe.Pointer(dir)), 0, 0) |
|
| 64 |
+ if r0 != 0 {
|
|
| 65 |
+ regerrno = syscall.Errno(r0) |
|
| 66 |
+ } |
|
| 67 |
+ return |
|
| 68 |
+} |
|
| 69 |
+ |
|
| 70 |
+func expandEnvironmentStrings(src *uint16, dst *uint16, size uint32) (n uint32, err error) {
|
|
| 71 |
+ r0, _, e1 := syscall.Syscall(procExpandEnvironmentStringsW.Addr(), 3, uintptr(unsafe.Pointer(src)), uintptr(unsafe.Pointer(dst)), uintptr(size)) |
|
| 72 |
+ n = uint32(r0) |
|
| 73 |
+ if n == 0 {
|
|
| 74 |
+ if e1 != 0 {
|
|
| 75 |
+ err = error(e1) |
|
| 76 |
+ } else {
|
|
| 77 |
+ err = syscall.EINVAL |
|
| 78 |
+ } |
|
| 79 |
+ } |
|
| 80 |
+ return |
|
| 81 |
+} |