golang.org/x/sys@v0.20.1-0.20240517151509-673e0f94c16d/windows/syscall_windows_test.go (about)

     1  // Copyright 2012 The Go Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package windows_test
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"debug/pe"
    11  	"errors"
    12  	"fmt"
    13  	"os"
    14  	"os/exec"
    15  	"path/filepath"
    16  	"runtime"
    17  	"strconv"
    18  	"strings"
    19  	"syscall"
    20  	"testing"
    21  	"time"
    22  	"unicode/utf8"
    23  	"unsafe"
    24  
    25  	"golang.org/x/sys/windows"
    26  )
    27  
    28  func TestWin32finddata(t *testing.T) {
    29  	path := filepath.Join(t.TempDir(), "long_name.and_extension")
    30  	f, err := os.Create(path)
    31  	if err != nil {
    32  		t.Fatalf("failed to create %v: %v", path, err)
    33  	}
    34  	f.Close()
    35  
    36  	type X struct {
    37  		fd  windows.Win32finddata
    38  		got byte
    39  		pad [10]byte // to protect ourselves
    40  
    41  	}
    42  	var want byte = 2 // it is unlikely to have this character in the filename
    43  	x := X{got: want}
    44  
    45  	pathp, _ := windows.UTF16PtrFromString(path)
    46  	h, err := windows.FindFirstFile(pathp, &(x.fd))
    47  	if err != nil {
    48  		t.Fatalf("FindFirstFile failed: %v", err)
    49  	}
    50  	err = windows.FindClose(h)
    51  	if err != nil {
    52  		t.Fatalf("FindClose failed: %v", err)
    53  	}
    54  
    55  	if x.got != want {
    56  		t.Fatalf("memory corruption: want=%d got=%d", want, x.got)
    57  	}
    58  }
    59  
    60  func TestFormatMessage(t *testing.T) {
    61  	dll := windows.MustLoadDLL("netevent.dll")
    62  
    63  	const TITLE_SC_MESSAGE_BOX uint32 = 0xC0001B75
    64  	const flags uint32 = syscall.FORMAT_MESSAGE_FROM_HMODULE | syscall.FORMAT_MESSAGE_ARGUMENT_ARRAY | syscall.FORMAT_MESSAGE_IGNORE_INSERTS
    65  	buf := make([]uint16, 300)
    66  	_, err := windows.FormatMessage(flags, uintptr(dll.Handle), TITLE_SC_MESSAGE_BOX, 0, buf, nil)
    67  	if err != nil {
    68  		t.Fatalf("FormatMessage for handle=%x and errno=%x failed: %v", dll.Handle, TITLE_SC_MESSAGE_BOX, err)
    69  	}
    70  }
    71  
    72  func abort(funcname string, err error) {
    73  	panic(funcname + " failed: " + err.Error())
    74  }
    75  
    76  func ExampleLoadLibrary() {
    77  	h, err := windows.LoadLibrary("kernel32.dll")
    78  	if err != nil {
    79  		abort("LoadLibrary", err)
    80  	}
    81  	defer windows.FreeLibrary(h)
    82  	proc, err := windows.GetProcAddress(h, "GetVersion")
    83  	if err != nil {
    84  		abort("GetProcAddress", err)
    85  	}
    86  	r, _, _ := syscall.Syscall(uintptr(proc), 0, 0, 0, 0)
    87  	major := byte(r)
    88  	minor := uint8(r >> 8)
    89  	build := uint16(r >> 16)
    90  	print("windows version ", major, ".", minor, " (Build ", build, ")\n")
    91  }
    92  
    93  func TestTOKEN_ALL_ACCESS(t *testing.T) {
    94  	if windows.TOKEN_ALL_ACCESS != 0xF01FF {
    95  		t.Errorf("TOKEN_ALL_ACCESS = %x, want 0xF01FF", windows.TOKEN_ALL_ACCESS)
    96  	}
    97  }
    98  
    99  func TestCreateWellKnownSid(t *testing.T) {
   100  	sid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
   101  	if err != nil {
   102  		t.Fatalf("Unable to create well known sid for administrators: %v", err)
   103  	}
   104  	if got, want := sid.String(), "S-1-5-32-544"; got != want {
   105  		t.Fatalf("Builtin Administrators SID = %s, want %s", got, want)
   106  	}
   107  }
   108  
   109  func TestPseudoTokens(t *testing.T) {
   110  	version, err := windows.GetVersion()
   111  	if err != nil {
   112  		t.Fatal(err)
   113  	}
   114  	if ((version&0xffff)>>8)|((version&0xff)<<8) < 0x0602 {
   115  		return
   116  	}
   117  
   118  	realProcessToken, err := windows.OpenCurrentProcessToken()
   119  	if err != nil {
   120  		t.Fatal(err)
   121  	}
   122  	defer realProcessToken.Close()
   123  	realProcessUser, err := realProcessToken.GetTokenUser()
   124  	if err != nil {
   125  		t.Fatal(err)
   126  	}
   127  
   128  	pseudoProcessToken := windows.GetCurrentProcessToken()
   129  	pseudoProcessUser, err := pseudoProcessToken.GetTokenUser()
   130  	if err != nil {
   131  		t.Fatal(err)
   132  	}
   133  	if !windows.EqualSid(realProcessUser.User.Sid, pseudoProcessUser.User.Sid) {
   134  		t.Fatal("The real process token does not have the same as the pseudo process token")
   135  	}
   136  
   137  	runtime.LockOSThread()
   138  	defer runtime.UnlockOSThread()
   139  
   140  	err = windows.RevertToSelf()
   141  	if err != nil {
   142  		t.Fatal(err)
   143  	}
   144  
   145  	pseudoThreadToken := windows.GetCurrentThreadToken()
   146  	_, err = pseudoThreadToken.GetTokenUser()
   147  	if err != windows.ERROR_NO_TOKEN {
   148  		t.Fatal("Expected an empty thread token")
   149  	}
   150  	pseudoThreadEffectiveToken := windows.GetCurrentThreadEffectiveToken()
   151  	pseudoThreadEffectiveUser, err := pseudoThreadEffectiveToken.GetTokenUser()
   152  	if err != nil {
   153  		t.Fatal(nil)
   154  	}
   155  	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadEffectiveUser.User.Sid) {
   156  		t.Fatal("The real process token does not have the same as the pseudo thread effective token, even though we aren't impersonating")
   157  	}
   158  
   159  	err = windows.ImpersonateSelf(windows.SecurityImpersonation)
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  	defer windows.RevertToSelf()
   164  	pseudoThreadUser, err := pseudoThreadToken.GetTokenUser()
   165  	if err != nil {
   166  		t.Fatal(err)
   167  	}
   168  	if !windows.EqualSid(realProcessUser.User.Sid, pseudoThreadUser.User.Sid) {
   169  		t.Fatal("The real process token does not have the same as the pseudo thread token after impersonating self")
   170  	}
   171  }
   172  
   173  func TestGUID(t *testing.T) {
   174  	guid, err := windows.GenerateGUID()
   175  	if err != nil {
   176  		t.Fatal(err)
   177  	}
   178  	if guid.Data1 == 0 && guid.Data2 == 0 && guid.Data3 == 0 && guid.Data4 == [8]byte{} {
   179  		t.Fatal("Got an all zero GUID, which is overwhelmingly unlikely")
   180  	}
   181  	want := fmt.Sprintf("{%08X-%04X-%04X-%04X-%012X}", guid.Data1, guid.Data2, guid.Data3, guid.Data4[:2], guid.Data4[2:])
   182  	got := guid.String()
   183  	if got != want {
   184  		t.Fatalf("String = %q; want %q", got, want)
   185  	}
   186  	guid2, err := windows.GUIDFromString(got)
   187  	if err != nil {
   188  		t.Fatal(err)
   189  	}
   190  	if guid2 != guid {
   191  		t.Fatalf("Did not parse string back to original GUID = %q; want %q", guid2, guid)
   192  	}
   193  	_, err = windows.GUIDFromString("not-a-real-guid")
   194  	if err != syscall.Errno(windows.CO_E_CLASSSTRING) {
   195  		t.Fatalf("Bad GUID string error = %v; want CO_E_CLASSSTRING", err)
   196  	}
   197  }
   198  
   199  func TestKnownFolderPath(t *testing.T) {
   200  	token, err := windows.OpenCurrentProcessToken()
   201  	if err != nil {
   202  		t.Fatal(err)
   203  	}
   204  	defer token.Close()
   205  	profileDir, err := token.GetUserProfileDirectory()
   206  	if err != nil {
   207  		t.Fatal(err)
   208  	}
   209  	want := filepath.Join(profileDir, "Desktop")
   210  	got, err := windows.KnownFolderPath(windows.FOLDERID_Desktop, windows.KF_FLAG_DEFAULT)
   211  	if err != nil {
   212  		t.Fatal(err)
   213  	}
   214  	if want != got {
   215  		t.Fatalf("Path = %q; want %q", got, want)
   216  	}
   217  }
   218  
   219  func TestRtlGetVersion(t *testing.T) {
   220  	version := windows.RtlGetVersion()
   221  	major, minor, build := windows.RtlGetNtVersionNumbers()
   222  	// Go is not explicitly added to the application compatibility database, so
   223  	// these two functions should return the same thing.
   224  	if version.MajorVersion != major || version.MinorVersion != minor || version.BuildNumber != build {
   225  		t.Fatalf("%d.%d.%d != %d.%d.%d", version.MajorVersion, version.MinorVersion, version.BuildNumber, major, minor, build)
   226  	}
   227  }
   228  
   229  func TestGetNamedSecurityInfo(t *testing.T) {
   230  	path, err := windows.GetSystemDirectory()
   231  	if err != nil {
   232  		t.Fatal(err)
   233  	}
   234  	sd, err := windows.GetNamedSecurityInfo(path, windows.SE_FILE_OBJECT, windows.OWNER_SECURITY_INFORMATION)
   235  	if err != nil {
   236  		t.Fatal(err)
   237  	}
   238  	if !sd.IsValid() {
   239  		t.Fatal("Invalid security descriptor")
   240  	}
   241  	sdOwner, _, err := sd.Owner()
   242  	if err != nil {
   243  		t.Fatal(err)
   244  	}
   245  	if !sdOwner.IsValid() {
   246  		t.Fatal("Invalid security descriptor owner")
   247  	}
   248  }
   249  
   250  func TestGetSecurityInfo(t *testing.T) {
   251  	sd, err := windows.GetSecurityInfo(windows.CurrentProcess(), windows.SE_KERNEL_OBJECT, windows.DACL_SECURITY_INFORMATION)
   252  	if err != nil {
   253  		t.Fatal(err)
   254  	}
   255  	if !sd.IsValid() {
   256  		t.Fatal("Invalid security descriptor")
   257  	}
   258  	sdStr := sd.String()
   259  	if !strings.HasPrefix(sdStr, "D:(A;") {
   260  		t.Fatalf("DACL = %q; want D:(A;...", sdStr)
   261  	}
   262  }
   263  
   264  func TestSddlConversion(t *testing.T) {
   265  	sd, err := windows.SecurityDescriptorFromString("O:BA")
   266  	if err != nil {
   267  		t.Fatal(err)
   268  	}
   269  	if !sd.IsValid() {
   270  		t.Fatal("Invalid security descriptor")
   271  	}
   272  	sdOwner, _, err := sd.Owner()
   273  	if err != nil {
   274  		t.Fatal(err)
   275  	}
   276  	if !sdOwner.IsValid() {
   277  		t.Fatal("Invalid security descriptor owner")
   278  	}
   279  	if !sdOwner.IsWellKnown(windows.WinBuiltinAdministratorsSid) {
   280  		t.Fatalf("Owner = %q; want S-1-5-32-544", sdOwner)
   281  	}
   282  }
   283  
   284  func TestBuildSecurityDescriptor(t *testing.T) {
   285  	const want = "O:SYD:(A;;GA;;;BA)"
   286  
   287  	adminSid, err := windows.CreateWellKnownSid(windows.WinBuiltinAdministratorsSid)
   288  	if err != nil {
   289  		t.Fatal(err)
   290  	}
   291  	systemSid, err := windows.CreateWellKnownSid(windows.WinLocalSystemSid)
   292  	if err != nil {
   293  		t.Fatal(err)
   294  	}
   295  
   296  	access := []windows.EXPLICIT_ACCESS{{
   297  		AccessPermissions: windows.GENERIC_ALL,
   298  		AccessMode:        windows.GRANT_ACCESS,
   299  		Trustee: windows.TRUSTEE{
   300  			TrusteeForm:  windows.TRUSTEE_IS_SID,
   301  			TrusteeType:  windows.TRUSTEE_IS_GROUP,
   302  			TrusteeValue: windows.TrusteeValueFromSID(adminSid),
   303  		},
   304  	}}
   305  	owner := &windows.TRUSTEE{
   306  		TrusteeForm:  windows.TRUSTEE_IS_SID,
   307  		TrusteeType:  windows.TRUSTEE_IS_USER,
   308  		TrusteeValue: windows.TrusteeValueFromSID(systemSid),
   309  	}
   310  
   311  	sd, err := windows.BuildSecurityDescriptor(owner, nil, access, nil, nil)
   312  	if err != nil {
   313  		t.Fatal(err)
   314  	}
   315  	sd, err = sd.ToAbsolute()
   316  	if err != nil {
   317  		t.Fatal(err)
   318  	}
   319  	err = sd.SetSACL(nil, false, false)
   320  	if err != nil {
   321  		t.Fatal(err)
   322  	}
   323  	if got := sd.String(); got != want {
   324  		t.Fatalf("SD = %q; want %q", got, want)
   325  	}
   326  	sd, err = sd.ToSelfRelative()
   327  	if err != nil {
   328  		t.Fatal(err)
   329  	}
   330  	if got := sd.String(); got != want {
   331  		t.Fatalf("SD = %q; want %q", got, want)
   332  	}
   333  
   334  	sd, err = windows.NewSecurityDescriptor()
   335  	if err != nil {
   336  		t.Fatal(err)
   337  	}
   338  	acl, err := windows.ACLFromEntries(access, nil)
   339  	if err != nil {
   340  		t.Fatal(err)
   341  	}
   342  	err = sd.SetDACL(acl, true, false)
   343  	if err != nil {
   344  		t.Fatal(err)
   345  	}
   346  	err = sd.SetOwner(systemSid, false)
   347  	if err != nil {
   348  		t.Fatal(err)
   349  	}
   350  	if got := sd.String(); got != want {
   351  		t.Fatalf("SD = %q; want %q", got, want)
   352  	}
   353  	sd, err = sd.ToSelfRelative()
   354  	if err != nil {
   355  		t.Fatal(err)
   356  	}
   357  	if got := sd.String(); got != want {
   358  		t.Fatalf("SD = %q; want %q", got, want)
   359  	}
   360  }
   361  
   362  func TestGetDiskFreeSpaceEx(t *testing.T) {
   363  	cwd, err := windows.UTF16PtrFromString(".")
   364  	if err != nil {
   365  		t.Fatalf(`failed to call UTF16PtrFromString("."): %v`, err)
   366  	}
   367  	var freeBytesAvailableToCaller, totalNumberOfBytes, totalNumberOfFreeBytes uint64
   368  	if err := windows.GetDiskFreeSpaceEx(cwd, &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes); err != nil {
   369  		t.Fatalf("failed to call GetDiskFreeSpaceEx: %v", err)
   370  	}
   371  
   372  	if freeBytesAvailableToCaller == 0 {
   373  		t.Errorf("freeBytesAvailableToCaller: got 0; want > 0")
   374  	}
   375  	if totalNumberOfBytes == 0 {
   376  		t.Errorf("totalNumberOfBytes: got 0; want > 0")
   377  	}
   378  	if totalNumberOfFreeBytes == 0 {
   379  		t.Errorf("totalNumberOfFreeBytes: got 0; want > 0")
   380  	}
   381  }
   382  
   383  func TestGetPreferredUILanguages(t *testing.T) {
   384  	tab := map[string]func(flags uint32) ([]string, error){
   385  		"GetProcessPreferredUILanguages": windows.GetProcessPreferredUILanguages,
   386  		"GetThreadPreferredUILanguages":  windows.GetThreadPreferredUILanguages,
   387  		"GetUserPreferredUILanguages":    windows.GetUserPreferredUILanguages,
   388  		"GetSystemPreferredUILanguages":  windows.GetSystemPreferredUILanguages,
   389  	}
   390  	for fName, f := range tab {
   391  		lang, err := f(windows.MUI_LANGUAGE_ID)
   392  		if err != nil {
   393  			t.Errorf(`failed to call %v(MUI_LANGUAGE_ID): %v`, fName, err)
   394  		}
   395  		for _, l := range lang {
   396  			_, err := strconv.ParseUint(l, 16, 16)
   397  			if err != nil {
   398  				t.Errorf(`%v(MUI_LANGUAGE_ID) returned unexpected LANGID: %v`, fName, l)
   399  			}
   400  		}
   401  
   402  		lang, err = f(windows.MUI_LANGUAGE_NAME)
   403  		if err != nil {
   404  			t.Errorf(`failed to call %v(MUI_LANGUAGE_NAME): %v`, fName, err)
   405  		}
   406  	}
   407  }
   408  
   409  func TestProcessWorkingSetSizeEx(t *testing.T) {
   410  	// Grab a handle to the current process
   411  	hProcess := windows.CurrentProcess()
   412  
   413  	// Allocate memory to store the result of the query
   414  	var minimumWorkingSetSize, maximumWorkingSetSize uintptr
   415  
   416  	// Make the system-call
   417  	var flag uint32
   418  	windows.GetProcessWorkingSetSizeEx(hProcess, &minimumWorkingSetSize, &maximumWorkingSetSize, &flag)
   419  
   420  	// Set the new limits to the current ones
   421  	if err := windows.SetProcessWorkingSetSizeEx(hProcess, minimumWorkingSetSize, maximumWorkingSetSize, flag); err != nil {
   422  		t.Error(err)
   423  	}
   424  }
   425  
   426  func TestJobObjectInfo(t *testing.T) {
   427  	jo, err := windows.CreateJobObject(nil, nil)
   428  	if err != nil {
   429  		t.Fatalf("CreateJobObject failed: %v", err)
   430  	}
   431  	defer windows.CloseHandle(jo)
   432  
   433  	var info windows.JOBOBJECT_EXTENDED_LIMIT_INFORMATION
   434  
   435  	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
   436  		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
   437  	if err != nil {
   438  		t.Fatalf("QueryInformationJobObject failed: %v", err)
   439  	}
   440  
   441  	const wantMemLimit = 4 * 1024
   442  
   443  	info.BasicLimitInformation.LimitFlags |= windows.JOB_OBJECT_LIMIT_PROCESS_MEMORY
   444  	info.ProcessMemoryLimit = wantMemLimit
   445  	_, err = windows.SetInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
   446  		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)))
   447  	if err != nil {
   448  		t.Fatalf("SetInformationJobObject failed: %v", err)
   449  	}
   450  
   451  	err = windows.QueryInformationJobObject(jo, windows.JobObjectExtendedLimitInformation,
   452  		uintptr(unsafe.Pointer(&info)), uint32(unsafe.Sizeof(info)), nil)
   453  	if err != nil {
   454  		t.Fatalf("QueryInformationJobObject failed: %v", err)
   455  	}
   456  
   457  	if have := info.ProcessMemoryLimit; wantMemLimit != have {
   458  		t.Errorf("ProcessMemoryLimit is wrong: want %v have %v", wantMemLimit, have)
   459  	}
   460  }
   461  
   462  func TestIsWow64Process2(t *testing.T) {
   463  	var processMachine, nativeMachine uint16
   464  	err := windows.IsWow64Process2(windows.CurrentProcess(), &processMachine, &nativeMachine)
   465  	if errors.Is(err, windows.ERROR_PROC_NOT_FOUND) {
   466  		maj, min, build := windows.RtlGetNtVersionNumbers()
   467  		if maj < 10 || (maj == 10 && min == 0 && build < 17763) {
   468  			t.Skip("not available on older versions of Windows")
   469  			return
   470  		}
   471  	}
   472  	if err != nil {
   473  		t.Fatalf("IsWow64Process2 failed: %v", err)
   474  	}
   475  	if processMachine == pe.IMAGE_FILE_MACHINE_UNKNOWN {
   476  		processMachine = nativeMachine
   477  	}
   478  	switch {
   479  	case processMachine == pe.IMAGE_FILE_MACHINE_AMD64 && runtime.GOARCH == "amd64":
   480  	case processMachine == pe.IMAGE_FILE_MACHINE_I386 && runtime.GOARCH == "386":
   481  	case processMachine == pe.IMAGE_FILE_MACHINE_ARMNT && runtime.GOARCH == "arm":
   482  	case processMachine == pe.IMAGE_FILE_MACHINE_ARM64 && runtime.GOARCH == "arm64":
   483  	default:
   484  		t.Errorf("IsWow64Process2 is wrong: want %v have %v", runtime.GOARCH, processMachine)
   485  	}
   486  }
   487  
   488  func TestNTStatusString(t *testing.T) {
   489  	want := "The name limit for the local computer network adapter card was exceeded."
   490  	got := windows.STATUS_TOO_MANY_NAMES.Error()
   491  	if want != got {
   492  		t.Errorf("NTStatus.Error did not return an expected error string - want %q; got %q", want, got)
   493  	}
   494  }
   495  
   496  func TestNTStatusConversion(t *testing.T) {
   497  	want := windows.ERROR_TOO_MANY_NAMES
   498  	got := windows.STATUS_TOO_MANY_NAMES.Errno()
   499  	if want != got {
   500  		t.Errorf("NTStatus.Errno = %q (0x%x); want %q (0x%x)", got.Error(), got, want.Error(), want)
   501  	}
   502  }
   503  
   504  func TestPEBFilePath(t *testing.T) {
   505  	peb := windows.RtlGetCurrentPeb()
   506  	if peb == nil || peb.Ldr == nil {
   507  		t.Error("unable to retrieve PEB with valid Ldr")
   508  	}
   509  	var entry *windows.LDR_DATA_TABLE_ENTRY
   510  	for cur := peb.Ldr.InMemoryOrderModuleList.Flink; cur != &peb.Ldr.InMemoryOrderModuleList; cur = cur.Flink {
   511  		e := (*windows.LDR_DATA_TABLE_ENTRY)(unsafe.Pointer(uintptr(unsafe.Pointer(cur)) - unsafe.Offsetof(windows.LDR_DATA_TABLE_ENTRY{}.InMemoryOrderLinks)))
   512  		if e.DllBase == peb.ImageBaseAddress {
   513  			entry = e
   514  			break
   515  		}
   516  	}
   517  	if entry == nil {
   518  		t.Error("unable to find Ldr entry for current process")
   519  	}
   520  	osPath, err := os.Executable()
   521  	if err != nil {
   522  		t.Errorf("unable to get path to current executable: %v", err)
   523  	}
   524  	pebPath := entry.FullDllName.String()
   525  	if osPath != pebPath {
   526  		t.Errorf("peb.Ldr.{entry}.FullDllName = %#q; want %#q", pebPath, osPath)
   527  	}
   528  	paramPath := peb.ProcessParameters.ImagePathName.String()
   529  	if osPath != paramPath {
   530  		t.Errorf("peb.ProcessParameters.ImagePathName.{entry}.ImagePathName = %#q; want %#q", paramPath, osPath)
   531  	}
   532  	osCwd, err := os.Getwd()
   533  	if err != nil {
   534  		t.Errorf("unable to get working directory: %v", err)
   535  	}
   536  	osCwd = filepath.Clean(osCwd)
   537  	paramCwd := filepath.Clean(peb.ProcessParameters.CurrentDirectory.DosPath.String())
   538  	if paramCwd != osCwd {
   539  		t.Errorf("peb.ProcessParameters.CurrentDirectory.DosPath = %#q; want %#q", paramCwd, osCwd)
   540  	}
   541  }
   542  
   543  func TestResourceExtraction(t *testing.T) {
   544  	system32, err := windows.GetSystemDirectory()
   545  	if err != nil {
   546  		t.Errorf("unable to find system32 directory: %v", err)
   547  	}
   548  	cmd, err := windows.LoadLibrary(filepath.Join(system32, "cmd.exe"))
   549  	if err != nil {
   550  		t.Errorf("unable to load cmd.exe: %v", err)
   551  	}
   552  	defer windows.FreeLibrary(cmd)
   553  	rsrc, err := windows.FindResource(cmd, windows.CREATEPROCESS_MANIFEST_RESOURCE_ID, windows.RT_MANIFEST)
   554  	if err != nil {
   555  		t.Errorf("unable to find cmd.exe manifest resource: %v", err)
   556  	}
   557  	manifest, err := windows.LoadResourceData(cmd, rsrc)
   558  	if err != nil {
   559  		t.Errorf("unable to load cmd.exe manifest resource data: %v", err)
   560  	}
   561  	if !bytes.Contains(manifest, []byte("</assembly>")) {
   562  		t.Errorf("did not find </assembly> in manifest")
   563  	}
   564  }
   565  
   566  func FuzzComposeCommandLine(f *testing.F) {
   567  	f.Add(`C:\foo.exe /bar /baz "-bag qux"`)
   568  	f.Add(`"C:\Program Files\Go\bin\go.exe" env`)
   569  	f.Add(`C:\"Program Files"\Go\bin\go.exe env`)
   570  	f.Add(`C:\"Program Files"\Go\bin\go.exe env`)
   571  	f.Add(`C:\"Pro"gram Files\Go\bin\go.exe env`)
   572  	f.Add(``)
   573  	f.Add(` `)
   574  	f.Add(`W\"0`)
   575  	f.Add("\"\f")
   576  	f.Add("\f")
   577  	f.Add("\x16")
   578  	f.Add(`"" ` + strings.Repeat("a", 8193))
   579  	f.Add(strings.Repeat(`"" `, 8193))
   580  
   581  	f.Add("\x00abcd")
   582  	f.Add("ab\x00cd")
   583  	f.Add("abcd\x00")
   584  	f.Add("\x00abcd\x00")
   585  	f.Add("\x00ab\x00cd\x00")
   586  	f.Add("\x00\x00\x00")
   587  	f.Add("\x16\x00\x16")
   588  	f.Add(`C:\Program Files\Go\bin\go.exe` + "\x00env")
   589  	f.Add(`"C:\Program Files\Go\bin\go.exe"` + "\x00env")
   590  	f.Add(`C:\"Program Files"\Go\bin\go.exe` + "\x00env")
   591  	f.Add(`C:\"Pro"gram Files\Go\bin\go.exe` + "\x00env")
   592  	f.Add("\x00" + strings.Repeat("a", 8192))
   593  	f.Add("\x00" + strings.Repeat("a", 8193))
   594  	f.Add(strings.Repeat("\x00"+strings.Repeat("a", 8192), 4))
   595  
   596  	f.Fuzz(func(t *testing.T, s string) {
   597  		// DecomposeCommandLine is the “control” for our experiment:
   598  		// if it returns a particular list of arguments, then we know
   599  		// it must be possible to create an input string that produces
   600  		// exactly those arguments.
   601  		//
   602  		// However, DecomposeCommandLine returns an error if the string
   603  		// contains a NUL byte. In that case, we will fall back to
   604  		// strings.Split, and be a bit more permissive about the results.
   605  		args, err := windows.DecomposeCommandLine(s)
   606  		argsFromSplit := false
   607  
   608  		if err == nil {
   609  			if testing.Verbose() {
   610  				t.Logf("DecomposeCommandLine(%#q) = %#q", s, args)
   611  			}
   612  		} else {
   613  			t.Logf("DecomposeCommandLine: %v", err)
   614  			if !strings.Contains(s, "\x00") {
   615  				// The documentation for CommandLineToArgv takes for granted that
   616  				// the first argument is a valid file path, and doesn't describe any
   617  				// specific behavior for malformed arguments. Empirically it seems to
   618  				// tolerate anything we throw at it, but if we discover cases where it
   619  				// actually returns an error we might need to relax this check.
   620  				t.Fatal("(error unexpected)")
   621  			}
   622  
   623  			// Since DecomposeCommandLine can't handle this string,
   624  			// interpret it as the raw arguments to ComposeCommandLine.
   625  			args = strings.Split(s, "\x00")
   626  			argsFromSplit = true
   627  			for i, arg := range args {
   628  				if !utf8.ValidString(arg) {
   629  					// We need to encode the arguments as UTF-16 to pass them to
   630  					// CommandLineToArgvW, so skip inputs that are not valid: they might
   631  					// have one or more runes converted to replacement characters.
   632  					t.Skipf("skipping: input %d is not valid UTF-8", i)
   633  				}
   634  			}
   635  			if testing.Verbose() {
   636  				t.Logf("using input: %#q", args)
   637  			}
   638  		}
   639  
   640  		// It's ok if we compose a different command line than what was read.
   641  		// Just check that we are able to compose something that round-trips
   642  		// to the same results as the original.
   643  		commandLine := windows.ComposeCommandLine(args)
   644  		t.Logf("ComposeCommandLine(_) = %#q", commandLine)
   645  
   646  		got, err := windows.DecomposeCommandLine(commandLine)
   647  		if err != nil {
   648  			t.Fatalf("DecomposeCommandLine: unexpected error: %v", err)
   649  		}
   650  		if testing.Verbose() {
   651  			t.Logf("DecomposeCommandLine(_) = %#q", got)
   652  		}
   653  
   654  		var badMatches []int
   655  		for i := range args {
   656  			if i >= len(got) {
   657  				badMatches = append(badMatches, i)
   658  				continue
   659  			}
   660  			want := args[i]
   661  			if got[i] != want {
   662  				if i == 0 && argsFromSplit {
   663  					// It is possible that args[0] cannot be encoded exactly, because
   664  					// CommandLineToArgvW doesn't unescape that argument in the same way
   665  					// as the others: since the first argument is assumed to be the name
   666  					// of the program itself, we only have the option of quoted or not.
   667  					//
   668  					// If args[0] contains a space or control character, we must quote it
   669  					// to avoid it being split into multiple arguments.
   670  					// If args[0] already starts with a quote character, we have no way
   671  					// to indicate that that character is part of the literal argument.
   672  					// In either case, if the string already contains a quote character
   673  					// we must avoid misinterpriting that character as the end of the
   674  					// quoted argument string.
   675  					//
   676  					// Unfortunately, ComposeCommandLine does not return an error, so we
   677  					// can't report existing quote characters as errors.
   678  					// Instead, we strip out the problematic quote characters from the
   679  					// argument, and quote the remainder.
   680  					// For paths like C:\"Program Files"\Go\bin\go.exe that is arguably
   681  					// what the caller intended anyway, and for other strings it seems
   682  					// less harmful than corrupting the subsequent arguments.
   683  					if got[i] == strings.ReplaceAll(want, `"`, ``) {
   684  						continue
   685  					}
   686  				}
   687  				badMatches = append(badMatches, i)
   688  			}
   689  		}
   690  		if len(badMatches) != 0 {
   691  			t.Errorf("Incorrect decomposition at indices: %v", badMatches)
   692  		}
   693  	})
   694  }
   695  
   696  func TestWinVerifyTrust(t *testing.T) {
   697  	evsignedfile := `.\testdata\ev-signed-file.exe`
   698  	evsignedfile16, err := windows.UTF16PtrFromString(evsignedfile)
   699  	if err != nil {
   700  		t.Fatalf("unable to get utf16 of %s: %v", evsignedfile, err)
   701  	}
   702  	data := &windows.WinTrustData{
   703  		Size:             uint32(unsafe.Sizeof(windows.WinTrustData{})),
   704  		UIChoice:         windows.WTD_UI_NONE,
   705  		RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity.
   706  		UnionChoice:      windows.WTD_CHOICE_FILE,
   707  		StateAction:      windows.WTD_STATEACTION_VERIFY,
   708  		FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
   709  			Size:     uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
   710  			FilePath: evsignedfile16,
   711  		}),
   712  	}
   713  	verifyErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   714  	data.StateAction = windows.WTD_STATEACTION_CLOSE
   715  	closeErr := windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   716  	if verifyErr != nil {
   717  		t.Errorf("%s did not verify: %v", evsignedfile, verifyErr)
   718  	}
   719  	if closeErr != nil {
   720  		t.Errorf("unable to free verification resources: %v", closeErr)
   721  	}
   722  
   723  	// Now that we've verified the legitimate file verifies, let's corrupt it and see if it correctly fails.
   724  
   725  	corruptedEvsignedfile := filepath.Join(t.TempDir(), "corrupted-file")
   726  	evsignedfileBytes, err := os.ReadFile(evsignedfile)
   727  	if err != nil {
   728  		t.Fatalf("unable to read %s bytes: %v", evsignedfile, err)
   729  	}
   730  	if len(evsignedfileBytes) > 0 {
   731  		evsignedfileBytes[len(evsignedfileBytes)/2-1]++
   732  	}
   733  	err = os.WriteFile(corruptedEvsignedfile, evsignedfileBytes, 0755)
   734  	if err != nil {
   735  		t.Fatalf("unable to write corrupted ntoskrnl.exe bytes: %v", err)
   736  	}
   737  	evsignedfile16, err = windows.UTF16PtrFromString(corruptedEvsignedfile)
   738  	if err != nil {
   739  		t.Fatalf("unable to get utf16 of ntoskrnl.exe: %v", err)
   740  	}
   741  	data = &windows.WinTrustData{
   742  		Size:             uint32(unsafe.Sizeof(windows.WinTrustData{})),
   743  		UIChoice:         windows.WTD_UI_NONE,
   744  		RevocationChecks: windows.WTD_REVOKE_NONE, // No revocation checking, in case the tests don't have network connectivity.
   745  		UnionChoice:      windows.WTD_CHOICE_FILE,
   746  		StateAction:      windows.WTD_STATEACTION_VERIFY,
   747  		FileOrCatalogOrBlobOrSgnrOrCert: unsafe.Pointer(&windows.WinTrustFileInfo{
   748  			Size:     uint32(unsafe.Sizeof(windows.WinTrustFileInfo{})),
   749  			FilePath: evsignedfile16,
   750  		}),
   751  	}
   752  	verifyErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   753  	data.StateAction = windows.WTD_STATEACTION_CLOSE
   754  	closeErr = windows.WinVerifyTrustEx(windows.InvalidHWND, &windows.WINTRUST_ACTION_GENERIC_VERIFY_V2, data)
   755  	if verifyErr != windows.Errno(windows.TRUST_E_BAD_DIGEST) {
   756  		t.Errorf("%s did not fail to verify as expected: %v", corruptedEvsignedfile, verifyErr)
   757  	}
   758  	if closeErr != nil {
   759  		t.Errorf("unable to free verification resources: %v", closeErr)
   760  	}
   761  
   762  }
   763  
   764  func TestEnumProcesses(t *testing.T) {
   765  	var (
   766  		pids    [2]uint32
   767  		outSize uint32
   768  	)
   769  	err := windows.EnumProcesses(pids[:], &outSize)
   770  	if err != nil {
   771  		t.Fatalf("unable to enumerate processes: %v", err)
   772  	}
   773  
   774  	// Regression check for go.dev/issue/60223
   775  	if outSize != 8 {
   776  		t.Errorf("unexpected bytes returned: %d", outSize)
   777  	}
   778  	// Most likely, this should be [0, 4].
   779  	// 0 is the system idle pseudo-process. 4 is the initial system process ID.
   780  	// This test expects that at least one of the PIDs is not 0.
   781  	if pids[0] == 0 && pids[1] == 0 {
   782  		t.Errorf("all PIDs are 0")
   783  	}
   784  }
   785  
   786  func TestProcessModules(t *testing.T) {
   787  	process, err := windows.GetCurrentProcess()
   788  	if err != nil {
   789  		t.Fatalf("unable to get current process: %v", err)
   790  	}
   791  	// NB: Assume that we're always the first module. This technically isn't documented anywhere (that I could find), but seems to always hold.
   792  	var module windows.Handle
   793  	var cbNeeded uint32
   794  	err = windows.EnumProcessModules(process, &module, uint32(unsafe.Sizeof(module)), &cbNeeded)
   795  	if err != nil {
   796  		t.Fatalf("EnumProcessModules failed: %v", err)
   797  	}
   798  
   799  	var moduleEx windows.Handle
   800  	err = windows.EnumProcessModulesEx(process, &moduleEx, uint32(unsafe.Sizeof(moduleEx)), &cbNeeded, windows.LIST_MODULES_DEFAULT)
   801  	if err != nil {
   802  		t.Fatalf("EnumProcessModulesEx failed: %v", err)
   803  	}
   804  	if module != moduleEx {
   805  		t.Fatalf("module from EnumProcessModules does not match EnumProcessModulesEx: %v != %v", module, moduleEx)
   806  	}
   807  
   808  	exePath, err := os.Executable()
   809  	if err != nil {
   810  		t.Fatalf("unable to get current executable path: %v", err)
   811  	}
   812  
   813  	modulePathUTF16 := make([]uint16, len(exePath)+1)
   814  	err = windows.GetModuleFileNameEx(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
   815  	if err != nil {
   816  		t.Fatalf("GetModuleFileNameEx failed: %v", err)
   817  	}
   818  
   819  	modulePath := windows.UTF16ToString(modulePathUTF16)
   820  	if modulePath != exePath {
   821  		t.Fatalf("module does not match executable for GetModuleFileNameEx: %s != %s", modulePath, exePath)
   822  	}
   823  
   824  	err = windows.GetModuleBaseName(process, module, &modulePathUTF16[0], uint32(len(modulePathUTF16)))
   825  	if err != nil {
   826  		t.Fatalf("GetModuleBaseName failed: %v", err)
   827  	}
   828  
   829  	modulePath = windows.UTF16ToString(modulePathUTF16)
   830  	baseExePath := filepath.Base(exePath)
   831  	if modulePath != baseExePath {
   832  		t.Fatalf("module does not match executable for GetModuleBaseName: %s != %s", modulePath, baseExePath)
   833  	}
   834  
   835  	var moduleInfo windows.ModuleInfo
   836  	err = windows.GetModuleInformation(process, module, &moduleInfo, uint32(unsafe.Sizeof(moduleInfo)))
   837  	if err != nil {
   838  		t.Fatalf("GetModuleInformation failed: %v", err)
   839  	}
   840  
   841  	peFile, err := pe.Open(exePath)
   842  	if err != nil {
   843  		t.Fatalf("unable to open current executable: %v", err)
   844  	}
   845  	defer peFile.Close()
   846  
   847  	var peSizeOfImage uint32
   848  	switch runtime.GOARCH {
   849  	case "amd64", "arm64":
   850  		peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader64).SizeOfImage
   851  	case "386", "arm":
   852  		peSizeOfImage = peFile.OptionalHeader.(*pe.OptionalHeader32).SizeOfImage
   853  	default:
   854  		t.Fatalf("unable to test GetModuleInformation on arch %v", runtime.GOARCH)
   855  	}
   856  
   857  	if moduleInfo.SizeOfImage != peSizeOfImage {
   858  		t.Fatalf("module size does not match executable: %v != %v", moduleInfo.SizeOfImage, peSizeOfImage)
   859  	}
   860  }
   861  
   862  func TestQueryWorkingSetEx(t *testing.T) {
   863  	var a int
   864  
   865  	process := windows.CurrentProcess()
   866  	information := windows.PSAPI_WORKING_SET_EX_INFORMATION{
   867  		VirtualAddress: windows.Pointer(unsafe.Pointer(&a)),
   868  	}
   869  	infos := []windows.PSAPI_WORKING_SET_EX_INFORMATION{information}
   870  
   871  	cb := uint32(uintptr(len(infos)) * unsafe.Sizeof(infos[0]))
   872  	if err := windows.QueryWorkingSetEx(process, uintptr(unsafe.Pointer(&infos[0])), cb); err != nil {
   873  		t.Fatalf("%+v", err)
   874  	}
   875  
   876  	if !infos[0].VirtualAttributes.Valid() {
   877  		t.Errorf("memory location not valid")
   878  	}
   879  }
   880  
   881  func TestReadWriteProcessMemory(t *testing.T) {
   882  	testBuffer := []byte{0xBA, 0xAD, 0xF0, 0x0D}
   883  
   884  	process, err := windows.GetCurrentProcess()
   885  	if err != nil {
   886  		t.Fatalf("unable to get current process: %v", err)
   887  	}
   888  
   889  	buffer := make([]byte, len(testBuffer))
   890  	err = windows.ReadProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil)
   891  	if err != nil {
   892  		t.Errorf("ReadProcessMemory failed: %v", err)
   893  	}
   894  	if !bytes.Equal(testBuffer, buffer) {
   895  		t.Errorf("bytes read does not match buffer: 0x%X != 0x%X", testBuffer, buffer)
   896  	}
   897  
   898  	buffer = []byte{0xDE, 0xAD, 0xBE, 0xEF}
   899  	err = windows.WriteProcessMemory(process, uintptr(unsafe.Pointer(&testBuffer[0])), &buffer[0], uintptr(len(buffer)), nil)
   900  	if err != nil {
   901  		t.Errorf("WriteProcessMemory failed: %v", err)
   902  	}
   903  	if !bytes.Equal(testBuffer, buffer) {
   904  		t.Errorf("bytes written does not match buffer: 0x%X != 0x%X", testBuffer, buffer)
   905  	}
   906  }
   907  
   908  func TestSystemModuleVersions(t *testing.T) {
   909  	var modules []windows.RTL_PROCESS_MODULE_INFORMATION
   910  	for bufferSize := uint32(128 * 1024); ; {
   911  		moduleBuffer := make([]byte, bufferSize)
   912  		err := windows.NtQuerySystemInformation(windows.SystemModuleInformation, unsafe.Pointer(&moduleBuffer[0]), bufferSize, &bufferSize)
   913  		switch err {
   914  		case windows.STATUS_INFO_LENGTH_MISMATCH:
   915  			continue
   916  		case nil:
   917  			break
   918  		default:
   919  			t.Error(err)
   920  			return
   921  		}
   922  		mods := (*windows.RTL_PROCESS_MODULES)(unsafe.Pointer(&moduleBuffer[0]))
   923  		modules = unsafe.Slice(&mods.Modules[0], int(mods.NumberOfModules))
   924  		break
   925  	}
   926  	for i := range modules {
   927  		moduleName := windows.ByteSliceToString(modules[i].FullPathName[modules[i].OffsetToFileName:])
   928  		driverPath := `\\?\GLOBALROOT` + windows.ByteSliceToString(modules[i].FullPathName[:])
   929  		var zero windows.Handle
   930  		infoSize, err := windows.GetFileVersionInfoSize(driverPath, &zero)
   931  		if err != nil {
   932  			if err != windows.ERROR_FILE_NOT_FOUND && err != windows.ERROR_RESOURCE_TYPE_NOT_FOUND {
   933  				t.Errorf("%v: %v", moduleName, err)
   934  			}
   935  			continue
   936  		}
   937  		versionInfo := make([]byte, infoSize)
   938  		if err = windows.GetFileVersionInfo(driverPath, 0, infoSize, unsafe.Pointer(&versionInfo[0])); err != nil {
   939  			t.Errorf("%v: %v", moduleName, err)
   940  			continue
   941  		}
   942  		var fixedInfo *windows.VS_FIXEDFILEINFO
   943  		fixedInfoLen := uint32(unsafe.Sizeof(*fixedInfo))
   944  		err = windows.VerQueryValue(unsafe.Pointer(&versionInfo[0]), `\`, (unsafe.Pointer)(&fixedInfo), &fixedInfoLen)
   945  		if err != nil {
   946  			t.Errorf("%v: %v", moduleName, err)
   947  			continue
   948  		}
   949  		t.Logf("%s: v%d.%d.%d.%d", moduleName,
   950  			(fixedInfo.FileVersionMS>>16)&0xff,
   951  			(fixedInfo.FileVersionMS>>0)&0xff,
   952  			(fixedInfo.FileVersionLS>>16)&0xff,
   953  			(fixedInfo.FileVersionLS>>0)&0xff)
   954  	}
   955  }
   956  
   957  type fileRenameInformation struct {
   958  	ReplaceIfExists uint32
   959  	RootDirectory   windows.Handle
   960  	FileNameLength  uint32
   961  	FileName        [1]uint16
   962  }
   963  
   964  func TestNtCreateFileAndNtSetInformationFile(t *testing.T) {
   965  	var iosb windows.IO_STATUS_BLOCK
   966  	var allocSize int64 = 0
   967  	// Open test directory with NtCreateFile.
   968  	testDirPath := t.TempDir()
   969  	objectName, err := windows.NewNTUnicodeString("\\??\\" + testDirPath)
   970  	if err != nil {
   971  		t.Fatal(err)
   972  	}
   973  	oa := &windows.OBJECT_ATTRIBUTES{
   974  		ObjectName: objectName,
   975  	}
   976  	oa.Length = uint32(unsafe.Sizeof(*oa))
   977  	var testDirHandle windows.Handle
   978  	err = windows.NtCreateFile(&testDirHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE, oa, &iosb,
   979  		&allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_OPEN,
   980  		windows.FILE_DIRECTORY_FILE, 0, 0)
   981  	if err != nil {
   982  		t.Fatalf("NtCreateFile(%v) failed: %v", testDirPath, err)
   983  	}
   984  	defer windows.CloseHandle(testDirHandle)
   985  	// Create a file in test directory with NtCreateFile.
   986  	fileName := "filename"
   987  	filePath := filepath.Join(testDirPath, fileName)
   988  	objectName, err = windows.NewNTUnicodeString(fileName)
   989  	if err != nil {
   990  		t.Fatal(err)
   991  	}
   992  	oa.RootDirectory = testDirHandle
   993  	oa.ObjectName = objectName
   994  	var fileHandle windows.Handle
   995  	err = windows.NtCreateFile(&fileHandle, windows.FILE_GENERIC_READ|windows.FILE_GENERIC_WRITE|windows.DELETE, oa, &iosb,
   996  		&allocSize, 0, windows.FILE_SHARE_READ|windows.FILE_SHARE_WRITE|windows.FILE_SHARE_DELETE, windows.FILE_CREATE,
   997  		0, 0, 0)
   998  	if err != nil {
   999  		t.Fatalf("NtCreateFile(%v) failed: %v", filePath, err)
  1000  	}
  1001  	defer windows.CloseHandle(fileHandle)
  1002  	_, err = os.Stat(filePath)
  1003  	if err != nil {
  1004  		t.Fatalf("cannot stat file created with NtCreatefile: %v", err)
  1005  	}
  1006  	// Rename file with NtSetInformationFile.
  1007  	newName := "newname"
  1008  	newPath := filepath.Join(testDirPath, newName)
  1009  	newNameUTF16, err := windows.UTF16FromString(newName)
  1010  	if err != nil {
  1011  		t.Fatal(err)
  1012  	}
  1013  	fileNameLen := len(newNameUTF16)*2 - 2
  1014  	var dummyFileRenameInfo fileRenameInformation
  1015  	bufferSize := int(unsafe.Offsetof(dummyFileRenameInfo.FileName)) + fileNameLen
  1016  	buffer := make([]byte, bufferSize)
  1017  	typedBufferPtr := (*fileRenameInformation)(unsafe.Pointer(&buffer[0]))
  1018  	typedBufferPtr.ReplaceIfExists = windows.FILE_RENAME_REPLACE_IF_EXISTS | windows.FILE_RENAME_POSIX_SEMANTICS
  1019  	typedBufferPtr.FileNameLength = uint32(fileNameLen)
  1020  	copy((*[windows.MAX_LONG_PATH]uint16)(unsafe.Pointer(&typedBufferPtr.FileName[0]))[:fileNameLen/2:fileNameLen/2], newNameUTF16)
  1021  	err = windows.NtSetInformationFile(fileHandle, &iosb, &buffer[0], uint32(bufferSize), windows.FileRenameInformation)
  1022  	if err != nil {
  1023  		t.Fatalf("NtSetInformationFile(%v) failed: %v", newPath, err)
  1024  	}
  1025  	_, err = os.Stat(newPath)
  1026  	if err != nil {
  1027  		t.Fatalf("cannot stat rename target %v: %v", newPath, err)
  1028  	}
  1029  }
  1030  
  1031  var deviceClassNetGUID = &windows.GUID{0x4d36e972, 0xe325, 0x11ce, [8]byte{0xbf, 0xc1, 0x08, 0x00, 0x2b, 0xe1, 0x03, 0x18}}
  1032  var deviceInterfaceNetGUID = &windows.GUID{0xcac88484, 0x7515, 0x4c03, [8]byte{0x82, 0xe6, 0x71, 0xa8, 0x7a, 0xba, 0xc3, 0x61}}
  1033  
  1034  func TestListLoadedNetworkDevices(t *testing.T) {
  1035  	devInfo, err := windows.SetupDiGetClassDevsEx(deviceClassNetGUID, "", 0, windows.DIGCF_PRESENT, 0, "")
  1036  	if err != nil {
  1037  		t.Fatal(err)
  1038  	}
  1039  	defer devInfo.Close()
  1040  	for i := 0; ; i++ {
  1041  		devInfoData, err := devInfo.EnumDeviceInfo(i)
  1042  		if err != nil {
  1043  			if err == windows.ERROR_NO_MORE_ITEMS {
  1044  				break
  1045  			}
  1046  			continue
  1047  		}
  1048  		friendlyName, err := devInfo.DeviceRegistryProperty(devInfoData, windows.SPDRP_DEVICEDESC)
  1049  		if err != nil {
  1050  			t.Fatal(err)
  1051  		}
  1052  		var status, problemCode uint32
  1053  		err = windows.CM_Get_DevNode_Status(&status, &problemCode, devInfoData.DevInst, 0)
  1054  		if err != nil || (status&windows.DN_DRIVER_LOADED|windows.DN_STARTED) != windows.DN_DRIVER_LOADED|windows.DN_STARTED {
  1055  			continue
  1056  		}
  1057  		instanceId, err := devInfo.DeviceInstanceID(devInfoData)
  1058  		if err != nil {
  1059  			t.Fatal(err)
  1060  		}
  1061  		interfaces, err := windows.CM_Get_Device_Interface_List(instanceId, deviceInterfaceNetGUID, windows.CM_GET_DEVICE_INTERFACE_LIST_PRESENT)
  1062  		if err != nil || len(interfaces) == 0 {
  1063  			continue
  1064  		}
  1065  		t.Logf("%s - %s", friendlyName, interfaces[0])
  1066  	}
  1067  }
  1068  
  1069  func TestListWireGuardDrivers(t *testing.T) {
  1070  	devInfo, err := windows.SetupDiCreateDeviceInfoListEx(deviceClassNetGUID, 0, "")
  1071  	if err != nil {
  1072  		t.Fatal(err)
  1073  	}
  1074  	defer devInfo.Close()
  1075  	devInfoData, err := devInfo.CreateDeviceInfo("WireGuard", deviceClassNetGUID, "", 0, windows.DICD_GENERATE_ID)
  1076  	if err != nil {
  1077  		t.Fatal(err)
  1078  	}
  1079  	err = devInfo.SetDeviceRegistryProperty(devInfoData, windows.SPDRP_HARDWAREID, []byte("W\x00i\x00r\x00e\x00G\x00u\x00a\x00r\x00d\x00\x00\x00\x00\x00"))
  1080  	if err != nil {
  1081  		t.Fatal(err)
  1082  	}
  1083  	err = devInfo.BuildDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER)
  1084  	if err != nil {
  1085  		t.Fatal(err)
  1086  	}
  1087  	defer devInfo.DestroyDriverInfoList(devInfoData, windows.SPDIT_COMPATDRIVER)
  1088  	for i := 0; ; i++ {
  1089  		drvInfoData, err := devInfo.EnumDriverInfo(devInfoData, windows.SPDIT_COMPATDRIVER, i)
  1090  		if err != nil {
  1091  			if err == windows.ERROR_NO_MORE_ITEMS {
  1092  				break
  1093  			}
  1094  			continue
  1095  		}
  1096  		drvInfoDetailData, err := devInfo.DriverInfoDetail(devInfoData, drvInfoData)
  1097  		if err != nil {
  1098  			t.Error(err)
  1099  			continue
  1100  		}
  1101  		t.Logf("%s - %s", drvInfoData.Description(), drvInfoDetailData.InfFileName())
  1102  	}
  1103  }
  1104  
  1105  func TestProcThreadAttributeHandleList(t *testing.T) {
  1106  	const sentinel = "the gopher dance"
  1107  	system32, err := windows.GetSystemDirectory()
  1108  	if err != nil {
  1109  		t.Fatal(err)
  1110  	}
  1111  	executable16, err := windows.UTF16PtrFromString(filepath.Join(system32, "cmd.exe"))
  1112  	if err != nil {
  1113  		t.Fatal(err)
  1114  	}
  1115  	args16, err := windows.UTF16PtrFromString(windows.ComposeCommandLine([]string{"/c", "echo " + sentinel}))
  1116  	if err != nil {
  1117  		t.Fatal(err)
  1118  	}
  1119  	attributeList, err := windows.NewProcThreadAttributeList(1)
  1120  	if err != nil {
  1121  		t.Fatal(err)
  1122  	}
  1123  	defer attributeList.Delete()
  1124  	si := &windows.StartupInfoEx{
  1125  		StartupInfo:             windows.StartupInfo{Cb: uint32(unsafe.Sizeof(windows.StartupInfoEx{}))},
  1126  		ProcThreadAttributeList: attributeList.List(),
  1127  	}
  1128  	pipeR, pipeW, err := os.Pipe()
  1129  	if err != nil {
  1130  		t.Fatal(err)
  1131  	}
  1132  	defer pipeR.Close()
  1133  	defer pipeW.Close()
  1134  	func() {
  1135  		// We allocate handles in a closure to provoke a UaF in the case of attributeList.Update being buggy.
  1136  		handles := []windows.Handle{windows.Handle(pipeW.Fd())}
  1137  		attributeList.Update(windows.PROC_THREAD_ATTRIBUTE_HANDLE_LIST, unsafe.Pointer(&handles[0]), uintptr(len(handles))*unsafe.Sizeof(handles[0]))
  1138  		si.Flags |= windows.STARTF_USESTDHANDLES
  1139  		si.StdOutput = handles[0]
  1140  		// Go 1.16's pipe handles aren't inheritable, so mark it explicitly as such here.
  1141  		windows.SetHandleInformation(handles[0], windows.HANDLE_FLAG_INHERIT, windows.HANDLE_FLAG_INHERIT)
  1142  	}()
  1143  	pi := new(windows.ProcessInformation)
  1144  	err = windows.CreateProcess(executable16, args16, nil, nil, true, windows.CREATE_DEFAULT_ERROR_MODE|windows.CREATE_UNICODE_ENVIRONMENT|windows.EXTENDED_STARTUPINFO_PRESENT, nil, nil, &si.StartupInfo, pi)
  1145  	if err != nil {
  1146  		t.Fatal(err)
  1147  	}
  1148  	defer windows.CloseHandle(pi.Thread)
  1149  	defer windows.CloseHandle(pi.Process)
  1150  	pipeR.SetReadDeadline(time.Now().Add(time.Minute))
  1151  	out, _, err := bufio.NewReader(pipeR).ReadLine()
  1152  	if err != nil {
  1153  		t.Fatal(err)
  1154  	}
  1155  	if string(out) != sentinel {
  1156  		t.Fatalf("got %q; want %q", out, sentinel)
  1157  	}
  1158  }
  1159  
  1160  func TestWSALookupService(t *testing.T) {
  1161  	var flags uint32 = windows.LUP_CONTAINERS
  1162  	flags |= windows.LUP_RETURN_NAME
  1163  	flags |= windows.LUP_RETURN_ADDR
  1164  
  1165  	var querySet windows.WSAQUERYSET
  1166  	querySet.NameSpace = windows.NS_BTH
  1167  	querySet.Size = uint32(unsafe.Sizeof(windows.WSAQUERYSET{}))
  1168  
  1169  	var handle windows.Handle
  1170  	err := windows.WSALookupServiceBegin(&querySet, flags, &handle)
  1171  	if err != nil {
  1172  		if errors.Is(err, windows.WSASERVICE_NOT_FOUND) {
  1173  			t.Skip("WSA Service not found, so skip this test")
  1174  		}
  1175  		t.Fatal(err)
  1176  	}
  1177  
  1178  	defer windows.WSALookupServiceEnd(handle)
  1179  
  1180  	n := int32(unsafe.Sizeof(windows.WSAQUERYSET{}))
  1181  	buf := make([]byte, n)
  1182  items_loop:
  1183  	for {
  1184  		q := (*windows.WSAQUERYSET)(unsafe.Pointer(&buf[0]))
  1185  		err := windows.WSALookupServiceNext(handle, flags, &n, q)
  1186  		switch err {
  1187  		case windows.WSA_E_NO_MORE, windows.WSAENOMORE:
  1188  			// no more data available - break the loop
  1189  			break items_loop
  1190  		case windows.WSAEFAULT:
  1191  			// buffer is too small - reallocate and try again
  1192  			buf = make([]byte, n)
  1193  		case nil:
  1194  			// found a record - display the item and fetch next item
  1195  			var addr string
  1196  			for _, e := range q.SaBuffer.RemoteAddr.Sockaddr.Addr.Data {
  1197  				if e != 0 {
  1198  					addr += fmt.Sprintf("%x", e)
  1199  				}
  1200  			}
  1201  			t.Logf("%s -> %s\n", windows.UTF16PtrToString(q.ServiceInstanceName), addr)
  1202  
  1203  		default:
  1204  			t.Fatal(err)
  1205  		}
  1206  	}
  1207  }
  1208  
  1209  func TestTimePeriod(t *testing.T) {
  1210  	if err := windows.TimeBeginPeriod(1); err != nil {
  1211  		t.Fatal(err)
  1212  	}
  1213  	if err := windows.TimeEndPeriod(1); err != nil {
  1214  		t.Fatal(err)
  1215  	}
  1216  }
  1217  
  1218  func TestGetStartupInfo(t *testing.T) {
  1219  	var si windows.StartupInfo
  1220  	err := windows.GetStartupInfo(&si)
  1221  	if err != nil {
  1222  		// see https://go.dev/issue/31316
  1223  		t.Fatalf("GetStartupInfo: got error %v, want nil", err)
  1224  	}
  1225  }
  1226  
  1227  func TestAddRemoveDllDirectory(t *testing.T) {
  1228  	if _, err := exec.LookPath("gcc"); err != nil {
  1229  		t.Skip("skipping test: gcc is missing")
  1230  	}
  1231  	dllSrc := `#include <stdint.h>
  1232  #include <windows.h>
  1233  
  1234  uintptr_t beep(void) {
  1235     return 5;
  1236  }`
  1237  	tmpdir := t.TempDir()
  1238  	srcname := "beep.c"
  1239  	err := os.WriteFile(filepath.Join(tmpdir, srcname), []byte(dllSrc), 0)
  1240  	if err != nil {
  1241  		t.Fatal(err)
  1242  	}
  1243  	name := "beep.dll"
  1244  	cmd := exec.Command("gcc", "-shared", "-s", "-Werror", "-o", name, srcname)
  1245  	cmd.Dir = tmpdir
  1246  	out, err := cmd.CombinedOutput()
  1247  	if err != nil {
  1248  		t.Fatalf("failed to build dll: %v - %v", err, string(out))
  1249  	}
  1250  
  1251  	if _, err := windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS); err == nil {
  1252  		t.Fatal("LoadLibraryEx unexpectedly found beep.dll")
  1253  	}
  1254  
  1255  	dllCookie, err := windows.AddDllDirectory(windows.StringToUTF16Ptr(tmpdir))
  1256  	if err != nil {
  1257  		t.Fatalf("AddDllDirectory failed: %s", err)
  1258  	}
  1259  
  1260  	handle, err := windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS)
  1261  	if err != nil {
  1262  		t.Fatalf("LoadLibraryEx failed: %s", err)
  1263  	}
  1264  
  1265  	if err := windows.FreeLibrary(handle); err != nil {
  1266  		t.Fatalf("FreeLibrary failed: %s", err)
  1267  	}
  1268  
  1269  	if err := windows.RemoveDllDirectory(dllCookie); err != nil {
  1270  		t.Fatalf("RemoveDllDirectory failed: %s", err)
  1271  	}
  1272  
  1273  	_, err = windows.LoadLibraryEx("beep.dll", 0, windows.LOAD_LIBRARY_SEARCH_USER_DIRS)
  1274  	if err == nil {
  1275  		t.Fatal("LoadLibraryEx unexpectedly found beep.dll")
  1276  	}
  1277  }