Signed-off-by: John Howard <jhoward@microsoft.com>
John Howard authored on 2016/02/02 03:13:54... | ... |
@@ -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 |
+} |