Go

How to Do Windows Registry Functions in Go

Jan 05, 2022
hackajob Staff

You asked and we've listened - we're delving deep into how you can do Windows Registry Functions in Go. For all the Go developers out there, or even for those who don't yet know Golang but are interested, this tech tutorial will show you exactly how to do these functions so that by the end you'll feel like a pro. Ready? Let's dive in.

Well start with creating the registry key first. For this we'll use the golang.org/x/sys/windows/registry library (which you can find here).

func createRegistryKey(path string) (registry.Key, func()) {
	var access uint32 = registry.ALL_ACCESS
	key, _, err := registry.CreateKey(registry.LOCAL_MACHINE, path, access)
	if err != nil {
		panic(err)
	}

	return key, func() {
		var err error
		if err = key.Close(); err != nil {
			panic( err)
		}
	}
}

Here we create a key in the registry with the CreateKey function. We give the first parameter registry.LOCAL_MACHINE by general use. As for the second parameter, we enter the registry's path. The third parameter is with which accesses we want to create this key. Here we can grant all permissions. Now let's set the return of the function as key and again as a function. Then, we can run Close with defer where we created it.

As an example, we can set the value using the set function below, using the key as well:

    // for string
    err = regKey.SetStringValue(k, t)
    if err != nil {
        panic(err)
    }

    // for int
    err = newK.SetDWordValue(k, uint32(23))
     if err != nil {
        panic(err)
    }

So far we have created a key and value. Now let's see if the key value is set correctly:

func GetStringValue(registryKey string) (string, bool) {
	var access uint32 = registry.QUERY_VALUE
	regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, registryKey, access)
	if err != nil {
		if err != registry.ErrNotExist {
			panic(err)
		}
		return "", false
	}

	id, _, err := regKey.GetStringValue("ProductID")
	if err != nil {
		panic(err)
		return "", false
	}

	return id, true
}

First of all, we get the key object with the OpenKey method. Then, using the GetStringValue function, we get the value of the ProductID key under that key. Here our access variable is different from the previous one. If we want, we can give all access as before, but due to security concerns we'll set registry.QUERY_VALUE because we're only reading. You can find different access variables in the library according to your intended use.

So we've now examined our Get and Create methods. Let's look at the deletion process:

    registry.DeleteKey(registry.LOCAL_MACHINE, keyPath); 
    if err != nil {
		panic(err)
	}

We can simply delete by giving the path of the key we want to delete to the DeleteKey method of the library. Now let's go one step further and delete all the nested keys. In the library we use, if there are nested keys and we want to delete the root key, we do not allow it. Therefore, we need to do nested deletion recursively. Wondering how? Check it out below:

func cleanRegistry(registryKey string) error {
	var access uint32 = registry.QUERY_VALUE | registry.ENUMERATE_SUB_KEYS
	regKey, err := registry.OpenKey(registry.LOCAL_MACHINE, registryKey, access)
	if err != nil {
		if err == registry.ErrNotExist {
			return nil
		}

		fmt.Printf("Failed to open '%s' Error: %v", registryKey, err)
		return err
	}

	defer func() {
		if err := regKey.Close(); err != nil {
			fmt.Printf("Failed to close reg key '%v' Error: %v", regKey, err)
		}
	}()

	keyNames, err := regKey.ReadSubKeyNames(0)
	if err != nil {
		fmt.Printf("Failed to get %q keys from registry error: %v", regKey, err)
		return nil
	}

	if err := deleteSubKeys(keyNames, registryKey, access); err != nil {
		fmt.Printf("delete error: %v", err)
		return err
	}

	// delete itself
	if err := registry.DeleteKey(regKey, ""); err != nil {
		fmt.Printf("Cannot delete key path : %s Error: %v", registryKey, err)
		return err
	}
	return nil
}

func deleteSubKeys(keyNames []string, registryKey string, access uint32) error {
	for _, k := range keyNames {
		keyPath := fmt.Sprintf("%s\\%s", registryKey, k)

		subRegKey, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, access)
		if err != nil {
			fmt.Printf("Path %q not found on registry: %v", keyPath, err)
			return err
		}

		// delete sub keys
		if err := deleteSubKeysRecursively(subRegKey, keyPath, access); err != nil {
			fmt.Printf("deleteSubKeysRecursively error: %v", err)
		}

		if err := registry.DeleteKey(subRegKey, ""); err != nil {
			fmt.Printf("Cannot delete key path : %s Error: %v", keyPath, err)
			return err
		}
	}

	return nil
}

func deleteSubKeysRecursively(regKey registry.Key, keyPath string, access uint32) error {
	subKeyNames, err := regKey.ReadSubKeyNames(0)
	if err != nil {
		return nil
	}

	for _, subKeyName := range subKeyNames {
		path := fmt.Sprintf("%s\\%s", keyPath, subKeyName)

		subRegKey, err := registry.OpenKey(registry.LOCAL_MACHINE, path, access)
		if err != nil {
			fmt.Printf("Path %q not found on registry: %v", subKeyName, err)
			return err
		}

		if err = deleteSubKeysRecursively(subRegKey, path, access); err != nil {
			return err
		}

		if err = registry.DeleteKey(subRegKey, ""); err != nil {
			fmt.Printf("Cannot delete registry key path : %s Error: %v", path, err)
			return err
		}
	}

	return nil
}

At first glance, the code may seem a bit confusing. So let's explain this code in order.

The cleanRegistry function takes the key to be deleted as of a parameter.
Then we use the OpenKey method as explained above. Then we read the names of all subkeys by giving 0 as a parameter to the ReadSubKeyNames method. Then we call the deleteSubKeys method. In this function, we delete all sub-keys and then delete the key itself. In the deleteSubKeys function, we combine the root path and the subkey. Then we call the deleteSubKeysRecursively function and repeat the operations we have done above.

In general, we have taken a look at the registry library. Let's talk about an additional subject that we think will be very useful for you as an extra.
As you know, registry records of 32-bit applications in Windows are kept under WOW6432Node. The examples we gave here were all about 64-bit keys.
If you want to access keys under WOW6432Node, don't forget to add registry.WOW64_32KEY to access variable.

And that's it! We'll be looking into more Go tech tutorials soon so continue to check our blog as we add more content.

Like what you've read or want more like this? Let us know! Email us here or DM us: Twitter, LinkedIn, Facebook, we'd love to hear from you.