Using the Windows Registry from Go
I'm continuing my adventures using Go on Windows. Today: accessing the Windows Registry!
The Registry is a database that stores settings both for Windows itself and for applications. Most users won't use it directly, but as a programmer you might need to. In this article I'll go through a sample program to show how to do that in Go.
The full sample code is here: main.go. It's a command-line program that prints a list of the shared folders exported by Windows. This was the use case that made me look into using the Windows registry from Go: I wanted to get the list of shared folders on a Windows PC to help me debug some issues related to network drives. It turns out the easiest way to get that (from Go, at least) is to read the list from the Registry.
Accessing the Registry manually
Before we delve into the Go code, let's look at how to get the shared folders manually using the
Registry Editor. In case you're not familiar: the Registry Editor, also known as regedit.exe
because it comes from the days when filenames were limited to 8 characters plus an extension, is
a GUI program included with Windows that you can use to poke at the Registry. Here's what it looks
like:
The Registry is organized in a hierarchy of keys, like folders in the file system. The key we need for the example program is:
Computer\HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\LanmanServer\Shares
Under this key there's an entry for each shared folder on the computer. To test the code I've set up
two shared folders called foo
and bar
, pointing to C:\foo
and C:\bar
; here's what that looks
like in the Registry Editor:
You can see that the data stored for each contains several key=value
mappings, for example
Path=C:\bar
.
Accessing the Registry from Go
We'll be using the Go package golang.org/x/sys/windows/registry:
import "golang.org/x/sys/windows/registry"
The first step is to call OpenKey
to "open" the key we want to read from:
path := `SYSTEM\CurrentControlSet\Services\LanmanServer\Shares`
key, err := registry.OpenKey(registry.LOCAL_MACHINE, path, registry.QUERY_VALUE)
if err != nil {
fmt.Printf("error opening Windows Registry key: %v\n", err)
os.Exit(1)
}
defer key.Close()
We pass the first part of the path (Computer\HKEY_LOCAL_MACHINE
) as a constant, then the rest of
the path as a string, and another constant that defines what we want to do with the key.
Next we read the names of all the values stored under this key:
names, err := key.ReadValueNames(-1)
if err != nil {
fmt.Printf("error reading Windows Registry value names: %v\n", err)
os.Exit(1)
}
ReadValueNames
takes the maximum number of names to return; -1 means to return all of them. We'll
iterate over those names and get the value stored for each:
for _, name := range names {
values, _, err := key.GetStringsValue(name)
if err != nil {
fmt.Printf("error reading Windows Registry value: %v\n", err)
os.Exit(1)
}
Values in the Registry can have different types. You can see the type for the shared-folder entries
in the screenshot above: REG_MULTI_SZ
, which means "a sequence of null-terminated strings".
Fortunately for us the GetStringsValue
method takes care of splitting the strings and returns a
simple []string
.
As we've seen, the entries here are a set of key=value
mappings. I wrote a little helper function
called mapFromStrings
to turn that into a Go map (you can see it in
the source, it's pretty straightforward);
we'll use that to make the values easier to work with:
m := mapFromStrings(values)
We'll check the Type
to make sure the entry is really for a shared drive, and then print the name
and the Path
:
if m["Type"] != "0" {
// Type=0 means network drives; if it's not 0 it could be, for example, a shared printer
continue
}
fmt.Printf("%s -> %s\n", name, m["Path"])
That's it! We'll compile the program with
GOOS=windows GOARCH=amd64 go build -o shares.exe main.go
and run it in the terminal:
C:\Program Files>.\shares.exe
foo -> C:\foo
bar -> C:\bar
Next steps
You can see an overview of possible types on Microsoft's website: Registry Value
Types. The registry
package includes methods to retrieve the
various types called GetBinaryValue
, GetIntegerValue
, etc. It also has ways to get a key's
subkeys, to modify values, and so on -- just remember to change that third argument for OpenKey
,
otherwise you'll get a confusing Access is denied
error later on.