Levin's blog

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:

Registry Editor screenshot

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:

Registry Editor screenshot

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.