Skip to content

Instantly share code, notes, and snippets.

@heaths
Created April 19, 2023 00:29
Show Gist options
  • Save heaths/c0dbb3431bd031ef3416842e82998825 to your computer and use it in GitHub Desktop.
Save heaths/c0dbb3431bd031ef3416842e82998825 to your computer and use it in GitHub Desktop.
Get the Windows Installer product assignment for products related by UpgradeCode
//go:build windows
package main
import (
"flag"
"fmt"
"log"
"os"
"path/filepath"
"syscall"
"unsafe"
"golang.org/x/sys/windows"
)
type INSTALLPROPERTY string
const (
INSTALLPROPERTY_ASSIGNMENTTYPE INSTALLPROPERTY = "AssignmentType"
)
var (
modmsi = windows.NewLazySystemDLL("msi.dll")
procMsiEnumRelatedProductsW = modmsi.NewProc("MsiEnumRelatedProductsW")
procMsiGetProductInfoW = modmsi.NewProc("MsiGetProductInfoW")
)
func main() {
verbose := flag.Bool("v", false, "Log verbose output")
flag.Usage = func() {
fmt.Fprintf(os.Stderr, "Check the state of a Windows Installer Product\n\n")
fmt.Fprintf(os.Stderr, "Usage of %s:\n\b", os.Args[0])
fmt.Fprintf(os.Stderr, " %s [flags] UpgradeCode\n\n", filepath.Base(os.Args[0]))
fmt.Fprintf(os.Stderr, "Arguments\n")
fmt.Fprintf(os.Stderr, " UpgradeCode\n")
fmt.Fprintf(os.Stderr, " UpgradeCode of the product(s) to check\n")
fmt.Fprintln(os.Stderr)
fmt.Fprintf(os.Stderr, "Flags\n")
flag.PrintDefaults()
}
flag.Parse()
upgradeCode := flag.Arg(0)
if upgradeCode == "" {
log.Fatal("requires UpgradeCode")
}
productCodes, err := GetRelatedProducts(upgradeCode)
if err != nil {
log.Fatalf("failed to get related products: %s", err)
}
for _, productCode := range productCodes {
if *verbose {
log.Printf("found %s\n", productCode)
}
data, err := MsiGetProductInfo(productCode, INSTALLPROPERTY_ASSIGNMENTTYPE)
if err != nil {
log.Fatalf("failed to get product assignment: %s", err)
}
var scope string
if data == "1" {
scope = "per-machine"
} else {
scope = "per-user"
}
fmt.Printf("%s is installed %s\n", productCode, scope)
}
}
func GetRelatedProducts(upgradeCode string) ([]string, error) {
productCodes := make([]string, 0)
i := uint32(0)
for {
productCode, err := MsiEnumRelatedProducts(upgradeCode, i)
if err == nil {
productCodes = append(productCodes, productCode)
} else if err == windows.ERROR_BAD_CONFIGURATION {
continue
} else if err == windows.ERROR_NO_MORE_ITEMS {
break
} else {
return nil, err
}
i++
}
return productCodes, nil
}
func MsiEnumRelatedProducts(upgradeCode string, i uint32) (string, error) {
lpUpgradeCode, err := syscall.UTF16PtrFromString(upgradeCode)
if err != nil {
return "", err
}
lpProductBuf := make([]uint16, 39)
r1, _, err := syscall.SyscallN(
procMsiEnumRelatedProductsW.Addr(),
uintptr(unsafe.Pointer(lpUpgradeCode)),
0,
uintptr(i),
uintptr(unsafe.Pointer(&lpProductBuf[0])),
)
if r1 == uintptr(windows.ERROR_SUCCESS) {
return syscall.UTF16ToString(lpProductBuf), nil
} else if r1 == uintptr(windows.ERROR_BAD_CONFIGURATION) {
return "", windows.ERROR_BAD_CONFIGURATION
} else if r1 == uintptr(windows.ERROR_NO_MORE_ITEMS) {
return "", windows.ERROR_NO_MORE_ITEMS
}
return "", err
}
func MsiGetProductInfo(productCode string, property INSTALLPROPERTY) (string, error) {
szProduct, err := syscall.UTF16PtrFromString(productCode)
if err != nil {
return "", err
}
szAttribute, err := syscall.UTF16PtrFromString(string(property))
if err != nil {
return "", err
}
var pcchValueBuf uint32
r1, _, err := syscall.SyscallN(
procMsiGetProductInfoW.Addr(),
uintptr(unsafe.Pointer(szProduct)),
uintptr(unsafe.Pointer(szAttribute)),
uintptr(unsafe.Pointer(nil)),
uintptr(unsafe.Pointer(&pcchValueBuf)),
)
if r1 != uintptr(windows.ERROR_SUCCESS) {
return "", err
}
pcchValueBuf++
lpValueBuf := make([]uint16, pcchValueBuf)
r1, _, err = syscall.SyscallN(
procMsiGetProductInfoW.Addr(),
uintptr(unsafe.Pointer(szProduct)),
uintptr(unsafe.Pointer(szAttribute)),
uintptr(unsafe.Pointer(&lpValueBuf[0])),
uintptr(unsafe.Pointer(&pcchValueBuf)),
)
if r1 == uintptr(windows.ERROR_SUCCESS) {
return syscall.UTF16ToString(lpValueBuf), nil
}
return "", nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment