github.com/isyscore/isc-gobase@v1.5.3-0.20231218061332-cbc7451899e9/system/process/process_windows.go (about)

     1  //go:build windows
     2  
     3  package process
     4  
     5  import (
     6  	"bufio"
     7  	"context"
     8  	"errors"
     9  	"fmt"
    10  	"github.com/isyscore/isc-gobase/system/common"
    11  	"github.com/isyscore/isc-gobase/system/cpu"
    12  	"github.com/isyscore/isc-gobase/system/net"
    13  	"golang.org/x/sys/windows"
    14  	"io"
    15  	"os"
    16  	"reflect"
    17  	"strings"
    18  	"syscall"
    19  	"time"
    20  	"unicode/utf16"
    21  	"unsafe"
    22  )
    23  
    24  var (
    25  	modntdll             = windows.NewLazySystemDLL("ntdll.dll")
    26  	procNtResumeProcess  = modntdll.NewProc("NtResumeProcess")
    27  	procNtSuspendProcess = modntdll.NewProc("NtSuspendProcess")
    28  
    29  	modpsapi                     = windows.NewLazySystemDLL("psapi.dll")
    30  	procGetProcessMemoryInfo     = modpsapi.NewProc("GetProcessMemoryInfo")
    31  	procGetProcessImageFileNameW = modpsapi.NewProc("GetProcessImageFileNameW")
    32  
    33  	advapi32                  = windows.NewLazySystemDLL("advapi32.dll")
    34  	procLookupPrivilegeValue  = advapi32.NewProc("LookupPrivilegeValueW")
    35  	procAdjustTokenPrivileges = advapi32.NewProc("AdjustTokenPrivileges")
    36  
    37  	procQueryFullProcessImageNameW = common.Modkernel32.NewProc("QueryFullProcessImageNameW")
    38  	procGetPriorityClass           = common.Modkernel32.NewProc("GetPriorityClass")
    39  	procGetProcessIoCounters       = common.Modkernel32.NewProc("GetProcessIoCounters")
    40  	procGetNativeSystemInfo        = common.Modkernel32.NewProc("GetNativeSystemInfo")
    41  
    42  	processorArchitecture uint
    43  )
    44  
    45  const processQueryInformation = windows.PROCESS_QUERY_LIMITED_INFORMATION
    46  
    47  type SystemProcessInformation struct {
    48  	NextEntryOffset   uint64
    49  	NumberOfThreads   uint64
    50  	Reserved1         [48]byte
    51  	Reserved2         [3]byte
    52  	UniqueProcessID   uintptr
    53  	Reserved3         uintptr
    54  	HandleCount       uint64
    55  	Reserved4         [4]byte
    56  	Reserved5         [11]byte
    57  	PeakPagefileUsage uint64
    58  	PrivatePageCount  uint64
    59  	Reserved6         [6]uint64
    60  }
    61  
    62  type systemProcessorInformation struct {
    63  	ProcessorArchitecture uint16
    64  	ProcessorLevel        uint16
    65  	ProcessorRevision     uint16
    66  	Reserved              uint16
    67  	ProcessorFeatureBits  uint16
    68  }
    69  
    70  type systemInfo struct {
    71  	wProcessorArchitecture      uint16
    72  	wReserved                   uint16
    73  	dwPageSize                  uint32
    74  	lpMinimumApplicationAddress uintptr
    75  	lpMaximumApplicationAddress uintptr
    76  	dwActiveProcessorMask       uintptr
    77  	dwNumberOfProcessors        uint32
    78  	dwProcessorType             uint32
    79  	dwAllocationGranularity     uint32
    80  	wProcessorLevel             uint16
    81  	wProcessorRevision          uint16
    82  }
    83  
    84  // MemoryInfoExStat Memory_info_ex is different between OSes
    85  type MemoryInfoExStat struct {
    86  }
    87  
    88  type MemoryMapsStat struct {
    89  }
    90  
    91  // ioCounters is an equivalent representation of IO_COUNTERS in the Windows API.
    92  // https://docs.microsoft.com/windows/win32/api/winnt/ns-winnt-io_counters
    93  type ioCounters struct {
    94  	ReadOperationCount  uint64
    95  	WriteOperationCount uint64
    96  	OtherOperationCount uint64
    97  	ReadTransferCount   uint64
    98  	WriteTransferCount  uint64
    99  	OtherTransferCount  uint64
   100  }
   101  
   102  type processBasicInformation32 struct {
   103  	Reserved1       uint32
   104  	PebBaseAddress  uint32
   105  	Reserved2       uint32
   106  	Reserved3       uint32
   107  	UniqueProcessId uint32
   108  	Reserved4       uint32
   109  }
   110  
   111  type processBasicInformation64 struct {
   112  	Reserved1       uint64
   113  	PebBaseAddress  uint64
   114  	Reserved2       uint64
   115  	Reserved3       uint64
   116  	UniqueProcessId uint64
   117  	Reserved4       uint64
   118  }
   119  
   120  type processEnvironmentBlock32 struct {
   121  	Reserved1         [2]uint8
   122  	BeingDebugged     uint8
   123  	Reserved2         uint8
   124  	Reserved3         [2]uint32
   125  	Ldr               uint32
   126  	ProcessParameters uint32
   127  	// More fields which we don't use so far
   128  }
   129  
   130  type processEnvironmentBlock64 struct {
   131  	Reserved1         [2]uint8
   132  	BeingDebugged     uint8
   133  	Reserved2         uint8
   134  	_                 [4]uint8 // padding, since we are 64 bit, the next pointer is 64 bit aligned (when compiling for 32 bit, this is not the case without manual padding)
   135  	Reserved3         [2]uint64
   136  	Ldr               uint64
   137  	ProcessParameters uint64
   138  	// More fields which we don't use so far
   139  }
   140  
   141  type rtlUserProcessParameters32 struct {
   142  	Reserved1                      [16]uint8
   143  	ConsoleHandle                  uint32
   144  	ConsoleFlags                   uint32
   145  	StdInputHandle                 uint32
   146  	StdOutputHandle                uint32
   147  	StdErrorHandle                 uint32
   148  	CurrentDirectoryPathNameLength uint16
   149  	_                              uint16 // Max Length
   150  	CurrentDirectoryPathAddress    uint32
   151  	CurrentDirectoryHandle         uint32
   152  	DllPathNameLength              uint16
   153  	_                              uint16 // Max Length
   154  	DllPathAddress                 uint32
   155  	ImagePathNameLength            uint16
   156  	_                              uint16 // Max Length
   157  	ImagePathAddress               uint32
   158  	CommandLineLength              uint16
   159  	_                              uint16 // Max Length
   160  	CommandLineAddress             uint32
   161  	EnvironmentAddress             uint32
   162  	// More fields which we don't use so far
   163  }
   164  
   165  type rtlUserProcessParameters64 struct {
   166  	Reserved1                      [16]uint8
   167  	ConsoleHandle                  uint64
   168  	ConsoleFlags                   uint64
   169  	StdInputHandle                 uint64
   170  	StdOutputHandle                uint64
   171  	StdErrorHandle                 uint64
   172  	CurrentDirectoryPathNameLength uint16
   173  	_                              uint16 // Max Length
   174  	_                              uint32 // Padding
   175  	CurrentDirectoryPathAddress    uint64
   176  	CurrentDirectoryHandle         uint64
   177  	DllPathNameLength              uint16
   178  	_                              uint16 // Max Length
   179  	_                              uint32 // Padding
   180  	DllPathAddress                 uint64
   181  	ImagePathNameLength            uint16
   182  	_                              uint16 // Max Length
   183  	_                              uint32 // Padding
   184  	ImagePathAddress               uint64
   185  	CommandLineLength              uint16
   186  	_                              uint16 // Max Length
   187  	_                              uint32 // Padding
   188  	CommandLineAddress             uint64
   189  	EnvironmentAddress             uint64
   190  	// More fields which we don't use so far
   191  }
   192  
   193  type winLUID struct {
   194  	LowPart  winDWord
   195  	HighPart winLong
   196  }
   197  
   198  // LUID_AND_ATTRIBUTES
   199  type winLUIDAndAttributes struct {
   200  	Luid       winLUID
   201  	Attributes winDWord
   202  }
   203  
   204  // TOKEN_PRIVILEGES
   205  type winTokenPriviledges struct {
   206  	PrivilegeCount winDWord
   207  	Privileges     [1]winLUIDAndAttributes
   208  }
   209  
   210  type winLong int32
   211  type winDWord uint32
   212  
   213  func init() {
   214  	var systemInfo systemInfo
   215  
   216  	_, _, _ = procGetNativeSystemInfo.Call(uintptr(unsafe.Pointer(&systemInfo)))
   217  	processorArchitecture = uint(systemInfo.wProcessorArchitecture)
   218  
   219  	// enable SeDebugPrivilege https://github.com/midstar/proci/blob/6ec79f57b90ba3d9efa2a7b16ef9c9369d4be875/proci_windows.go#L80-L119
   220  	handle, err := syscall.GetCurrentProcess()
   221  	if err != nil {
   222  		return
   223  	}
   224  
   225  	var token syscall.Token
   226  	err = syscall.OpenProcessToken(handle, 0x0028, &token)
   227  	if err != nil {
   228  		return
   229  	}
   230  	defer func(token syscall.Token) {
   231  		_ = token.Close()
   232  	}(token)
   233  
   234  	tokenPriviledges := winTokenPriviledges{PrivilegeCount: 1}
   235  	lpName := syscall.StringToUTF16("SeDebugPrivilege")
   236  	ret, _, _ := procLookupPrivilegeValue.Call(
   237  		0,
   238  		uintptr(unsafe.Pointer(&lpName[0])),
   239  		uintptr(unsafe.Pointer(&tokenPriviledges.Privileges[0].Luid)))
   240  	if ret == 0 {
   241  		return
   242  	}
   243  
   244  	tokenPriviledges.Privileges[0].Attributes = 0x00000002 // SE_PRIVILEGE_ENABLED
   245  
   246  	_, _, _ = procAdjustTokenPrivileges.Call(
   247  		uintptr(token),
   248  		0,
   249  		uintptr(unsafe.Pointer(&tokenPriviledges)),
   250  		unsafe.Sizeof(tokenPriviledges),
   251  		0,
   252  		0)
   253  }
   254  
   255  func pidsWithContext(ctx context.Context) ([]int32, error) {
   256  	// inspired by https://gist.github.com/henkman/3083408
   257  	// and https://github.com/giampaolo/psutil/blob/1c3a15f637521ba5c0031283da39c733fda53e4c/psutil/arch/windows/process_info.c#L315-L329
   258  	var ret []int32
   259  	var read uint32 = 0
   260  	var psSize uint32 = 1024
   261  	const dwordSize uint32 = 4
   262  
   263  	for {
   264  		ps := make([]uint32, psSize)
   265  		if err := windows.EnumProcesses(ps, &read); err != nil {
   266  			return nil, err
   267  		}
   268  		if uint32(len(ps)) == read { // ps buffer was too small to host every results, retry with a bigger one
   269  			psSize += 1024
   270  			continue
   271  		}
   272  		for _, pid := range ps[:read/dwordSize] {
   273  			ret = append(ret, int32(pid))
   274  		}
   275  		return ret, nil
   276  
   277  	}
   278  
   279  }
   280  
   281  func PidExistsWithContext(ctx context.Context, pid int32) (bool, error) {
   282  	if pid == 0 { // special case for pid 0 System Idle Process
   283  		return true, nil
   284  	}
   285  	if pid < 0 {
   286  		return false, fmt.Errorf("invalid pid %v", pid)
   287  	}
   288  	if pid%4 != 0 {
   289  		// OpenProcess will succeed even on non-existing pid here https://devblogs.microsoft.com/oldnewthing/20080606-00/?p=22043
   290  		// so we list every pid just to be sure and be future-proof
   291  		pids, err := PidsWithContext(ctx)
   292  		if err != nil {
   293  			return false, err
   294  		}
   295  		for _, i := range pids {
   296  			if i == pid {
   297  				return true, err
   298  			}
   299  		}
   300  		return false, err
   301  	}
   302  	const STILL_ACTIVE = 259 // https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-getexitcodeprocess
   303  	h, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   304  	if err == windows.ERROR_ACCESS_DENIED {
   305  		return true, nil
   306  	}
   307  	if err == windows.ERROR_INVALID_PARAMETER {
   308  		return false, nil
   309  	}
   310  	if err != nil {
   311  		return false, err
   312  	}
   313  	defer func(handle syscall.Handle) {
   314  		_ = syscall.CloseHandle(handle)
   315  	}(syscall.Handle(h))
   316  	var exitCode uint32
   317  	err = windows.GetExitCodeProcess(h, &exitCode)
   318  	return exitCode == STILL_ACTIVE, err
   319  }
   320  
   321  func (p *Process) PpidWithContext(ctx context.Context) (int32, error) {
   322  	// if cached already, return from cache
   323  	cachedPpid := p.getPpid()
   324  	if cachedPpid != 0 {
   325  		return cachedPpid, nil
   326  	}
   327  
   328  	ppid, _, _, err := getFromSnapProcess(p.Pid)
   329  	if err != nil {
   330  		return 0, err
   331  	}
   332  
   333  	// no errors and not cached already, so cache it
   334  	p.setPpid(ppid)
   335  
   336  	return ppid, nil
   337  }
   338  
   339  func (p *Process) NameWithContext(ctx context.Context) (string, error) {
   340  	ppid, _, name, err := getFromSnapProcess(p.Pid)
   341  	if err != nil {
   342  		return "", fmt.Errorf("could not get Name: %s", err)
   343  	}
   344  
   345  	// if no errors and not cached already, cache ppid
   346  	p.parent = ppid
   347  	if 0 == p.getPpid() {
   348  		p.setPpid(ppid)
   349  	}
   350  
   351  	return name, nil
   352  }
   353  
   354  func (p *Process) TgidWithContext(ctx context.Context) (int32, error) {
   355  	return 0, common.ErrNotImplementedError
   356  }
   357  
   358  func (p *Process) ExeWithContext(ctx context.Context) (string, error) {
   359  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
   360  	if err != nil {
   361  		return "", err
   362  	}
   363  	defer func(handle windows.Handle) {
   364  		_ = windows.CloseHandle(handle)
   365  	}(c)
   366  	buf := make([]uint16, syscall.MAX_LONG_PATH)
   367  	size := uint32(syscall.MAX_LONG_PATH)
   368  	if err := procQueryFullProcessImageNameW.Find(); err == nil { // Vista+
   369  		ret, _, err := procQueryFullProcessImageNameW.Call(
   370  			uintptr(c),
   371  			uintptr(0),
   372  			uintptr(unsafe.Pointer(&buf[0])),
   373  			uintptr(unsafe.Pointer(&size)))
   374  		if ret == 0 {
   375  			return "", err
   376  		}
   377  		return windows.UTF16ToString(buf[:]), nil
   378  	}
   379  	// XP fallback
   380  	ret, _, err := procGetProcessImageFileNameW.Call(uintptr(c), uintptr(unsafe.Pointer(&buf[0])), uintptr(size))
   381  	if ret == 0 {
   382  		return "", err
   383  	}
   384  	return common.ConvertDOSPath(windows.UTF16ToString(buf[:])), nil
   385  }
   386  
   387  func (p *Process) CmdlineWithContext(_ context.Context) (string, error) {
   388  	cmdline, err := getProcessCommandLine(p.Pid)
   389  	if err != nil {
   390  		return "", fmt.Errorf("could not get CommandLine: %s", err)
   391  	}
   392  	return cmdline, nil
   393  }
   394  
   395  func (p *Process) CmdlineSliceWithContext(ctx context.Context) ([]string, error) {
   396  	cmdline, err := p.CmdlineWithContext(ctx)
   397  	if err != nil {
   398  		return nil, err
   399  	}
   400  	return strings.Split(cmdline, " "), nil
   401  }
   402  
   403  func (p *Process) createTimeWithContext(ctx context.Context) (int64, error) {
   404  	ru, err := getRusage(p.Pid)
   405  	if err != nil {
   406  		return 0, fmt.Errorf("could not get CreationDate: %s", err)
   407  	}
   408  
   409  	return ru.CreationTime.Nanoseconds() / 1000000, nil
   410  }
   411  
   412  func (p *Process) CwdWithContext(_ context.Context) (string, error) {
   413  	h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(p.Pid))
   414  	if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
   415  		return "", nil
   416  	}
   417  	if err != nil {
   418  		return "", err
   419  	}
   420  	defer func(handle syscall.Handle) {
   421  		_ = syscall.CloseHandle(handle)
   422  	}(syscall.Handle(h))
   423  
   424  	procIs32Bits := is32BitProcess(h)
   425  
   426  	if procIs32Bits {
   427  		userProcParams, err := getUserProcessParams32(h)
   428  		if err != nil {
   429  			return "", err
   430  		}
   431  		if userProcParams.CurrentDirectoryPathNameLength > 0 {
   432  			cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CurrentDirectoryPathAddress), uint(userProcParams.CurrentDirectoryPathNameLength))
   433  			if len(cwd) != int(userProcParams.CurrentDirectoryPathAddress) {
   434  				return "", errors.New("cannot read current working directory")
   435  			}
   436  
   437  			return convertUTF16ToString(cwd), nil
   438  		}
   439  	} else {
   440  		userProcParams, err := getUserProcessParams64(h)
   441  		if err != nil {
   442  			return "", err
   443  		}
   444  		if userProcParams.CurrentDirectoryPathNameLength > 0 {
   445  			cwd := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CurrentDirectoryPathAddress, uint(userProcParams.CurrentDirectoryPathNameLength))
   446  			if len(cwd) != int(userProcParams.CurrentDirectoryPathNameLength) {
   447  				return "", errors.New("cannot read current working directory")
   448  			}
   449  
   450  			return convertUTF16ToString(cwd), nil
   451  		}
   452  	}
   453  
   454  	//if we reach here, we have no cwd
   455  	return "", nil
   456  }
   457  
   458  func (p *Process) ParentWithContext(ctx context.Context) (*Process, error) {
   459  	ppid, err := p.PpidWithContext(ctx)
   460  	if err != nil {
   461  		return nil, fmt.Errorf("could not get ParentProcessID: %s", err)
   462  	}
   463  
   464  	return NewProcessWithContext(ctx, ppid)
   465  }
   466  
   467  func (p *Process) StatusWithContext(ctx context.Context) (string, error) {
   468  	return "", common.ErrNotImplementedError
   469  }
   470  
   471  func (p *Process) ForegroundWithContext(ctx context.Context) (bool, error) {
   472  	return false, common.ErrNotImplementedError
   473  }
   474  
   475  func (p *Process) UsernameWithContext(ctx context.Context) (string, error) {
   476  	pid := p.Pid
   477  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   478  	if err != nil {
   479  		return "", err
   480  	}
   481  	defer func(handle windows.Handle) {
   482  		_ = windows.CloseHandle(handle)
   483  	}(c)
   484  
   485  	var token syscall.Token
   486  	err = syscall.OpenProcessToken(syscall.Handle(c), syscall.TOKEN_QUERY, &token)
   487  	if err != nil {
   488  		return "", err
   489  	}
   490  	defer func(token syscall.Token) {
   491  		_ = token.Close()
   492  	}(token)
   493  	tokenUser, err := token.GetTokenUser()
   494  	if err != nil {
   495  		return "", err
   496  	}
   497  
   498  	user, domain, _, err := tokenUser.User.Sid.LookupAccount("")
   499  	return domain + "\\" + user, err
   500  }
   501  
   502  func (p *Process) UidsWithContext(ctx context.Context) ([]int32, error) {
   503  	return nil, common.ErrNotImplementedError
   504  }
   505  
   506  func (p *Process) GidsWithContext(ctx context.Context) ([]int32, error) {
   507  	return nil, common.ErrNotImplementedError
   508  }
   509  
   510  func (p *Process) GroupsWithContext(ctx context.Context) ([]int32, error) {
   511  	return nil, common.ErrNotImplementedError
   512  }
   513  
   514  func (p *Process) TerminalWithContext(ctx context.Context) (string, error) {
   515  	return "", common.ErrNotImplementedError
   516  }
   517  
   518  // priorityClasses maps a win32 priority class to its WMI equivalent Win32_Process.Priority
   519  // https://docs.microsoft.com/en-us/windows/desktop/api/processthreadsapi/nf-processthreadsapi-getpriorityclass
   520  // https://docs.microsoft.com/en-us/windows/desktop/cimwin32prov/win32-process
   521  var priorityClasses = map[int]int32{
   522  	0x00008000: 10, // ABOVE_NORMAL_PRIORITY_CLASS
   523  	0x00004000: 6,  // BELOW_NORMAL_PRIORITY_CLASS
   524  	0x00000080: 13, // HIGH_PRIORITY_CLASS
   525  	0x00000040: 4,  // IDLE_PRIORITY_CLASS
   526  	0x00000020: 8,  // NORMAL_PRIORITY_CLASS
   527  	0x00000100: 24, // REALTIME_PRIORITY_CLASS
   528  }
   529  
   530  func (p *Process) NiceWithContext(ctx context.Context) (int32, error) {
   531  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
   532  	if err != nil {
   533  		return 0, err
   534  	}
   535  	defer func(handle windows.Handle) {
   536  		_ = windows.CloseHandle(handle)
   537  	}(c)
   538  	ret, _, err := procGetPriorityClass.Call(uintptr(c))
   539  	if ret == 0 {
   540  		return 0, err
   541  	}
   542  	priority, ok := priorityClasses[int(ret)]
   543  	if !ok {
   544  		return 0, fmt.Errorf("unknown priority class %v", ret)
   545  	}
   546  	return priority, nil
   547  }
   548  
   549  func (p *Process) IOniceWithContext(ctx context.Context) (int32, error) {
   550  	return 0, common.ErrNotImplementedError
   551  }
   552  
   553  func (p *Process) RlimitWithContext(ctx context.Context) ([]RlimitStat, error) {
   554  	return nil, common.ErrNotImplementedError
   555  }
   556  
   557  func (p *Process) RlimitUsageWithContext(ctx context.Context, gatherUsed bool) ([]RlimitStat, error) {
   558  	return nil, common.ErrNotImplementedError
   559  }
   560  
   561  func (p *Process) IOCountersWithContext(ctx context.Context) (*IOCountersStat, error) {
   562  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(p.Pid))
   563  	if err != nil {
   564  		return nil, err
   565  	}
   566  	defer func(handle windows.Handle) {
   567  		_ = windows.CloseHandle(handle)
   568  	}(c)
   569  	var ioCounters ioCounters
   570  	ret, _, err := procGetProcessIoCounters.Call(uintptr(c), uintptr(unsafe.Pointer(&ioCounters)))
   571  	if ret == 0 {
   572  		return nil, err
   573  	}
   574  	stats := &IOCountersStat{
   575  		ReadCount:  ioCounters.ReadOperationCount,
   576  		ReadBytes:  ioCounters.ReadTransferCount,
   577  		WriteCount: ioCounters.WriteOperationCount,
   578  		WriteBytes: ioCounters.WriteTransferCount,
   579  	}
   580  
   581  	return stats, nil
   582  }
   583  
   584  func (p *Process) NumCtxSwitchesWithContext(ctx context.Context) (*NumCtxSwitchesStat, error) {
   585  	return nil, common.ErrNotImplementedError
   586  }
   587  
   588  func (p *Process) NumFDsWithContext(ctx context.Context) (int32, error) {
   589  	return 0, common.ErrNotImplementedError
   590  }
   591  
   592  func (p *Process) NumThreadsWithContext(ctx context.Context) (int32, error) {
   593  	ppid, ret, _, err := getFromSnapProcess(p.Pid)
   594  	if err != nil {
   595  		return 0, err
   596  	}
   597  
   598  	// if no errors and not cached already, cache ppid
   599  	p.parent = ppid
   600  	if 0 == p.getPpid() {
   601  		p.setPpid(ppid)
   602  	}
   603  
   604  	return ret, nil
   605  }
   606  
   607  func (p *Process) ThreadsWithContext(ctx context.Context) (map[int32]*cpu.TimesStat, error) {
   608  	return nil, common.ErrNotImplementedError
   609  }
   610  
   611  func (p *Process) TimesWithContext(ctx context.Context) (*cpu.TimesStat, error) {
   612  	sysTimes, err := getProcessCPUTimes(p.Pid)
   613  	if err != nil {
   614  		return nil, err
   615  	}
   616  
   617  	// User and kernel times are represented as a FILETIME structure
   618  	// which contains a 64-bit value representing the number of
   619  	// 100-nanosecond intervals since January 1, 1601 (UTC):
   620  	// http://msdn.microsoft.com/en-us/library/ms724284(VS.85).aspx
   621  	// To convert it into a float representing the seconds that the
   622  	// process has executed in user/kernel mode I borrowed the code
   623  	// below from psutil's _psutil_windows.c, and in turn from Python's
   624  	// Modules/posixmodule.c
   625  
   626  	user := float64(sysTimes.UserTime.HighDateTime)*429.4967296 + float64(sysTimes.UserTime.LowDateTime)*1e-7
   627  	kernel := float64(sysTimes.KernelTime.HighDateTime)*429.4967296 + float64(sysTimes.KernelTime.LowDateTime)*1e-7
   628  
   629  	return &cpu.TimesStat{
   630  		User:   user,
   631  		System: kernel,
   632  	}, nil
   633  }
   634  
   635  func (p *Process) CPUAffinityWithContext(ctx context.Context) ([]int32, error) {
   636  	return nil, common.ErrNotImplementedError
   637  }
   638  
   639  func (p *Process) MemoryInfoWithContext(ctx context.Context) (*MemoryInfoStat, error) {
   640  	mem, err := getMemoryInfo(p.Pid)
   641  	if err != nil {
   642  		return nil, err
   643  	}
   644  
   645  	ret := &MemoryInfoStat{
   646  		RSS: mem.WorkingSetSize,
   647  		VMS: mem.PagefileUsage,
   648  	}
   649  
   650  	return ret, nil
   651  }
   652  
   653  func (p *Process) MemoryInfoExWithContext(ctx context.Context) (*MemoryInfoExStat, error) {
   654  	return nil, common.ErrNotImplementedError
   655  }
   656  
   657  func (p *Process) PageFaultsWithContext(ctx context.Context) (*PageFaultsStat, error) {
   658  	return nil, common.ErrNotImplementedError
   659  }
   660  
   661  func (p *Process) ChildrenWithContext(ctx context.Context) ([]*Process, error) {
   662  	var out []*Process
   663  	snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(0))
   664  	if err != nil {
   665  		return out, err
   666  	}
   667  	defer func(handle windows.Handle) {
   668  		_ = windows.CloseHandle(handle)
   669  	}(snap)
   670  	var pe32 windows.ProcessEntry32
   671  	pe32.Size = uint32(unsafe.Sizeof(pe32))
   672  	if err := windows.Process32First(snap, &pe32); err != nil {
   673  		return out, err
   674  	}
   675  	for {
   676  		if pe32.ParentProcessID == uint32(p.Pid) {
   677  			p, err := NewProcessWithContext(ctx, int32(pe32.ProcessID))
   678  			if err == nil {
   679  				out = append(out, p)
   680  			}
   681  		}
   682  		if err = windows.Process32Next(snap, &pe32); err != nil {
   683  			break
   684  		}
   685  	}
   686  	return out, nil
   687  }
   688  
   689  func (p *Process) OpenFilesWithContext(ctx context.Context) ([]OpenFilesStat, error) {
   690  	files := make([]OpenFilesStat, 0)
   691  	fileExists := make(map[string]bool)
   692  
   693  	process, err := windows.OpenProcess(common.ProcessQueryInformation, false, uint32(p.Pid))
   694  	if err != nil {
   695  		return nil, err
   696  	}
   697  
   698  	buffer := make([]byte, 1024)
   699  	var size uint32
   700  
   701  	st := common.CallWithExpandingBuffer(
   702  		func() common.NtStatus {
   703  			return common.NtQuerySystemInformation(
   704  				common.SystemExtendedHandleInformationClass,
   705  				&buffer[0],
   706  				uint32(len(buffer)),
   707  				&size,
   708  			)
   709  		},
   710  		&buffer,
   711  		&size,
   712  	)
   713  	if st.IsError() {
   714  		return nil, st.Error()
   715  	}
   716  
   717  	handlesList := (*common.SystemExtendedHandleInformation)(unsafe.Pointer(&buffer[0]))
   718  	handles := make([]common.SystemExtendedHandleTableEntryInformation, int(handlesList.NumberOfHandles))
   719  	hdr := (*reflect.SliceHeader)(unsafe.Pointer(&handles))
   720  	hdr.Data = uintptr(unsafe.Pointer(&handlesList.Handles[0]))
   721  
   722  	currentProcess, err := windows.GetCurrentProcess()
   723  	if err != nil {
   724  		return nil, err
   725  	}
   726  
   727  	for _, handle := range handles {
   728  		var file uintptr
   729  		if int32(handle.UniqueProcessId) != p.Pid {
   730  			continue
   731  		}
   732  		if windows.DuplicateHandle(process, windows.Handle(handle.HandleValue), currentProcess, (*windows.Handle)(&file),
   733  			0, true, windows.DUPLICATE_SAME_ACCESS) != nil {
   734  			continue
   735  		}
   736  		fileType, _ := windows.GetFileType(windows.Handle(file))
   737  		if fileType != windows.FILE_TYPE_DISK {
   738  			continue
   739  		}
   740  
   741  		var fileName string
   742  		ch := make(chan struct{})
   743  
   744  		go func() {
   745  			var buf [syscall.MAX_LONG_PATH]uint16
   746  			n, err := windows.GetFinalPathNameByHandle(windows.Handle(file), &buf[0], syscall.MAX_LONG_PATH, 0)
   747  			if err != nil {
   748  				return
   749  			}
   750  
   751  			fileName = string(utf16.Decode(buf[:n]))
   752  			ch <- struct{}{}
   753  		}()
   754  
   755  		select {
   756  		case <-time.NewTimer(100 * time.Millisecond).C:
   757  			continue
   758  		case <-ch:
   759  			fileInfo, _ := os.Stat(fileName)
   760  			if fileInfo.IsDir() {
   761  				continue
   762  			}
   763  
   764  			if _, exists := fileExists[fileName]; !exists {
   765  				files = append(files, OpenFilesStat{
   766  					Path: fileName,
   767  					Fd:   uint64(file),
   768  				})
   769  				fileExists[fileName] = true
   770  			}
   771  		case <-ctx.Done():
   772  			return files, ctx.Err()
   773  		}
   774  	}
   775  
   776  	return files, nil
   777  }
   778  
   779  func (p *Process) ConnectionsWithContext(ctx context.Context) ([]net.ConnectionStat, error) {
   780  	return net.ConnectionsPidWithContext(ctx, "all", p.Pid)
   781  }
   782  
   783  func (p *Process) ConnectionsMaxWithContext(ctx context.Context, max int) ([]net.ConnectionStat, error) {
   784  	return nil, common.ErrNotImplementedError
   785  }
   786  
   787  func (p *Process) NetIOCountersWithContext(ctx context.Context, pernic bool) ([]net.IOCountersStat, error) {
   788  	return nil, common.ErrNotImplementedError
   789  }
   790  
   791  func (p *Process) MemoryMapsWithContext(ctx context.Context, grouped bool) (*[]MemoryMapsStat, error) {
   792  	return nil, common.ErrNotImplementedError
   793  }
   794  
   795  func (p *Process) SendSignalWithContext(ctx context.Context, sig syscall.Signal) error {
   796  	return common.ErrNotImplementedError
   797  }
   798  
   799  func (p *Process) SuspendWithContext(ctx context.Context) error {
   800  	c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid))
   801  	if err != nil {
   802  		return err
   803  	}
   804  	defer func(handle windows.Handle) {
   805  		_ = windows.CloseHandle(handle)
   806  	}(c)
   807  
   808  	r1, _, _ := procNtSuspendProcess.Call(uintptr(c))
   809  	if r1 != 0 {
   810  		// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
   811  		return fmt.Errorf("NtStatus='0x%.8X'", r1)
   812  	}
   813  
   814  	return nil
   815  }
   816  
   817  func (p *Process) ResumeWithContext(ctx context.Context) error {
   818  	c, err := windows.OpenProcess(windows.PROCESS_SUSPEND_RESUME, false, uint32(p.Pid))
   819  	if err != nil {
   820  		return err
   821  	}
   822  	defer func(handle windows.Handle) {
   823  		_ = windows.CloseHandle(handle)
   824  	}(c)
   825  
   826  	r1, _, _ := procNtResumeProcess.Call(uintptr(c))
   827  	if r1 != 0 {
   828  		// See https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/596a1078-e883-4972-9bbc-49e60bebca55
   829  		return fmt.Errorf("NtStatus='0x%.8X'", r1)
   830  	}
   831  
   832  	return nil
   833  }
   834  
   835  func (p *Process) TerminateWithContext(ctx context.Context) error {
   836  	proc, err := windows.OpenProcess(windows.PROCESS_TERMINATE, false, uint32(p.Pid))
   837  	if err != nil {
   838  		return err
   839  	}
   840  	err = windows.TerminateProcess(proc, 0)
   841  	_ = windows.CloseHandle(proc)
   842  	return err
   843  }
   844  
   845  func (p *Process) KillWithContext(ctx context.Context) error {
   846  	process, err := os.FindProcess(int(p.Pid))
   847  	if err != nil {
   848  		return err
   849  	}
   850  	return process.Kill()
   851  }
   852  
   853  func (p *Process) EnvironWithContext(ctx context.Context) ([]string, error) {
   854  	envVars, err := getProcessEnvironmentVariables(p.Pid, ctx)
   855  	if err != nil {
   856  		return nil, fmt.Errorf("could not get environment variables: %s", err)
   857  	}
   858  	return envVars, nil
   859  }
   860  
   861  // retrieve Ppid in a thread-safe manner
   862  func (p *Process) getPpid() int32 {
   863  	p.parentMutex.RLock()
   864  	defer p.parentMutex.RUnlock()
   865  	return p.parent
   866  }
   867  
   868  // cache Ppid in a thread-safe manner (WINDOWS ONLY)
   869  // see https://psutil.readthedocs.io/en/latest/#psutil.Process.ppid
   870  func (p *Process) setPpid(ppid int32) {
   871  	p.parentMutex.Lock()
   872  	defer p.parentMutex.Unlock()
   873  	p.parent = ppid
   874  }
   875  
   876  func getFromSnapProcess(pid int32) (int32, int32, string, error) {
   877  	snap, err := windows.CreateToolhelp32Snapshot(windows.TH32CS_SNAPPROCESS, uint32(pid))
   878  	if err != nil {
   879  		return 0, 0, "", err
   880  	}
   881  	defer func(handle windows.Handle) {
   882  		_ = windows.CloseHandle(handle)
   883  	}(snap)
   884  	var pe32 windows.ProcessEntry32
   885  	pe32.Size = uint32(unsafe.Sizeof(pe32))
   886  	if err = windows.Process32First(snap, &pe32); err != nil {
   887  		return 0, 0, "", err
   888  	}
   889  	for {
   890  		if pe32.ProcessID == uint32(pid) {
   891  			szexe := windows.UTF16ToString(pe32.ExeFile[:])
   892  			return int32(pe32.ParentProcessID), int32(pe32.Threads), szexe, nil
   893  		}
   894  		if err = windows.Process32Next(snap, &pe32); err != nil {
   895  			break
   896  		}
   897  	}
   898  	return 0, 0, "", fmt.Errorf("couldn't find pid: %d", pid)
   899  }
   900  
   901  func ProcessesWithContext(ctx context.Context) ([]*Process, error) {
   902  	var out []*Process
   903  
   904  	pids, err := PidsWithContext(ctx)
   905  	if err != nil {
   906  		return out, fmt.Errorf("could not get Processes %s", err)
   907  	}
   908  
   909  	for _, pid := range pids {
   910  		p, err := NewProcessWithContext(ctx, pid)
   911  		if err != nil {
   912  			continue
   913  		}
   914  		out = append(out, p)
   915  	}
   916  
   917  	return out, nil
   918  }
   919  
   920  func getRusage(pid int32) (*windows.Rusage, error) {
   921  	var CPU windows.Rusage
   922  
   923  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   924  	if err != nil {
   925  		return nil, err
   926  	}
   927  	defer func(handle windows.Handle) {
   928  		_ = windows.CloseHandle(handle)
   929  	}(c)
   930  
   931  	if err := windows.GetProcessTimes(c, &CPU.CreationTime, &CPU.ExitTime, &CPU.KernelTime, &CPU.UserTime); err != nil {
   932  		return nil, err
   933  	}
   934  
   935  	return &CPU, nil
   936  }
   937  
   938  func getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) {
   939  	var mem PROCESS_MEMORY_COUNTERS
   940  	c, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   941  	if err != nil {
   942  		return mem, err
   943  	}
   944  	defer func(handle windows.Handle) {
   945  		_ = windows.CloseHandle(handle)
   946  	}(c)
   947  	if err := getProcessMemoryInfo(c, &mem); err != nil {
   948  		return mem, err
   949  	}
   950  
   951  	return mem, err
   952  }
   953  
   954  func getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) {
   955  	r1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem)))
   956  	if r1 == 0 {
   957  		if e1 != 0 {
   958  			err = error(e1)
   959  		} else {
   960  			err = syscall.EINVAL
   961  		}
   962  	}
   963  	return
   964  }
   965  
   966  type SYSTEM_TIMES struct {
   967  	CreateTime syscall.Filetime
   968  	ExitTime   syscall.Filetime
   969  	KernelTime syscall.Filetime
   970  	UserTime   syscall.Filetime
   971  }
   972  
   973  func getProcessCPUTimes(pid int32) (SYSTEM_TIMES, error) {
   974  	var times SYSTEM_TIMES
   975  
   976  	h, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))
   977  	if err != nil {
   978  		return times, err
   979  	}
   980  	defer func(handle windows.Handle) {
   981  		_ = windows.CloseHandle(handle)
   982  	}(h)
   983  
   984  	err = syscall.GetProcessTimes(
   985  		syscall.Handle(h),
   986  		&times.CreateTime,
   987  		&times.ExitTime,
   988  		&times.KernelTime,
   989  		&times.UserTime,
   990  	)
   991  
   992  	return times, err
   993  }
   994  
   995  func getUserProcessParams32(handle windows.Handle) (rtlUserProcessParameters32, error) {
   996  	pebAddress, err := queryPebAddress(syscall.Handle(handle), true)
   997  	if err != nil {
   998  		return rtlUserProcessParameters32{}, fmt.Errorf("cannot locate process PEB: %w", err)
   999  	}
  1000  
  1001  	buf := readProcessMemory(syscall.Handle(handle), true, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock32{})))
  1002  	if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock32{})) {
  1003  		return rtlUserProcessParameters32{}, fmt.Errorf("cannot read process PEB")
  1004  	}
  1005  	peb := (*processEnvironmentBlock32)(unsafe.Pointer(&buf[0]))
  1006  	userProcessAddress := uint64(peb.ProcessParameters)
  1007  	buf = readProcessMemory(syscall.Handle(handle), true, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters32{})))
  1008  	if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters32{})) {
  1009  		return rtlUserProcessParameters32{}, fmt.Errorf("cannot read user process parameters")
  1010  	}
  1011  	return *(*rtlUserProcessParameters32)(unsafe.Pointer(&buf[0])), nil
  1012  }
  1013  
  1014  func getUserProcessParams64(handle windows.Handle) (rtlUserProcessParameters64, error) {
  1015  	pebAddress, err := queryPebAddress(syscall.Handle(handle), false)
  1016  	if err != nil {
  1017  		return rtlUserProcessParameters64{}, fmt.Errorf("cannot locate process PEB: %w", err)
  1018  	}
  1019  
  1020  	buf := readProcessMemory(syscall.Handle(handle), false, pebAddress, uint(unsafe.Sizeof(processEnvironmentBlock64{})))
  1021  	if len(buf) != int(unsafe.Sizeof(processEnvironmentBlock64{})) {
  1022  		return rtlUserProcessParameters64{}, fmt.Errorf("cannot read process PEB")
  1023  	}
  1024  	peb := (*processEnvironmentBlock64)(unsafe.Pointer(&buf[0]))
  1025  	userProcessAddress := peb.ProcessParameters
  1026  	buf = readProcessMemory(syscall.Handle(handle), false, userProcessAddress, uint(unsafe.Sizeof(rtlUserProcessParameters64{})))
  1027  	if len(buf) != int(unsafe.Sizeof(rtlUserProcessParameters64{})) {
  1028  		return rtlUserProcessParameters64{}, fmt.Errorf("cannot read user process parameters")
  1029  	}
  1030  	return *(*rtlUserProcessParameters64)(unsafe.Pointer(&buf[0])), nil
  1031  }
  1032  
  1033  func is32BitProcess(h windows.Handle) bool {
  1034  	const (
  1035  		PROCESSOR_ARCHITECTURE_INTEL = 0
  1036  		PROCESSOR_ARCHITECTURE_ARM   = 5
  1037  		PROCESSOR_ARCHITECTURE_ARM64 = 12
  1038  		PROCESSOR_ARCHITECTURE_IA64  = 6
  1039  		PROCESSOR_ARCHITECTURE_AMD64 = 9
  1040  	)
  1041  
  1042  	var procIs32Bits bool
  1043  	switch processorArchitecture {
  1044  	case PROCESSOR_ARCHITECTURE_INTEL:
  1045  		fallthrough
  1046  	case PROCESSOR_ARCHITECTURE_ARM:
  1047  		procIs32Bits = true
  1048  	case PROCESSOR_ARCHITECTURE_ARM64:
  1049  		fallthrough
  1050  	case PROCESSOR_ARCHITECTURE_IA64:
  1051  		fallthrough
  1052  	case PROCESSOR_ARCHITECTURE_AMD64:
  1053  		var wow64 uint
  1054  
  1055  		ret, _, _ := common.ProcNtQueryInformationProcess.Call(
  1056  			uintptr(h),
  1057  			uintptr(common.ProcessWow64Information),
  1058  			uintptr(unsafe.Pointer(&wow64)),
  1059  			unsafe.Sizeof(wow64),
  1060  			uintptr(0),
  1061  		)
  1062  		if int(ret) >= 0 {
  1063  			if wow64 != 0 {
  1064  				procIs32Bits = true
  1065  			}
  1066  		} else {
  1067  			//if the OS does not support the call, we fallback into the bitness of the app
  1068  			if unsafe.Sizeof(wow64) == 4 {
  1069  				procIs32Bits = true
  1070  			}
  1071  		}
  1072  
  1073  	default:
  1074  		//for other unknown platforms, we rely on process platform
  1075  		if unsafe.Sizeof(processorArchitecture) == 8 {
  1076  			procIs32Bits = false
  1077  		} else {
  1078  			procIs32Bits = true
  1079  		}
  1080  	}
  1081  	return procIs32Bits
  1082  }
  1083  
  1084  func getProcessEnvironmentVariables(pid int32, ctx context.Context) ([]string, error) {
  1085  	h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
  1086  	if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
  1087  		return nil, nil
  1088  	}
  1089  	if err != nil {
  1090  		return nil, err
  1091  	}
  1092  	defer func(handle syscall.Handle) {
  1093  		_ = syscall.CloseHandle(handle)
  1094  	}(syscall.Handle(h))
  1095  
  1096  	procIs32Bits := is32BitProcess(h)
  1097  
  1098  	var processParameterBlockAddress uint64
  1099  
  1100  	if procIs32Bits {
  1101  		peb, err := getUserProcessParams32(h)
  1102  		if err != nil {
  1103  			return nil, err
  1104  		}
  1105  		processParameterBlockAddress = uint64(peb.EnvironmentAddress)
  1106  	} else {
  1107  		peb, err := getUserProcessParams64(h)
  1108  		if err != nil {
  1109  			return nil, err
  1110  		}
  1111  		processParameterBlockAddress = peb.EnvironmentAddress
  1112  	}
  1113  	envvarScanner := bufio.NewScanner(&processReader{
  1114  		processHandle:  h,
  1115  		is32BitProcess: procIs32Bits,
  1116  		offset:         processParameterBlockAddress,
  1117  	})
  1118  	envvarScanner.Split(func(data []byte, atEOF bool) (advance int, token []byte, err error) {
  1119  		if atEOF && len(data) == 0 {
  1120  			return 0, nil, nil
  1121  		}
  1122  		// Check for UTF-16 zero character
  1123  		for i := 0; i < len(data)-1; i += 2 {
  1124  			if data[i] == 0 && data[i+1] == 0 {
  1125  				return i + 2, data[0:i], nil
  1126  			}
  1127  		}
  1128  		if atEOF {
  1129  			return len(data), data, nil
  1130  		}
  1131  		// Request more data
  1132  		return 0, nil, nil
  1133  	})
  1134  	var envVars []string
  1135  	for envvarScanner.Scan() {
  1136  		entry := envvarScanner.Bytes()
  1137  		if len(entry) == 0 {
  1138  			break // Block is finished
  1139  		}
  1140  		envVars = append(envVars, convertUTF16ToString(entry))
  1141  		select {
  1142  		case <-ctx.Done():
  1143  			break
  1144  		default:
  1145  			continue
  1146  		}
  1147  	}
  1148  	if err := envvarScanner.Err(); err != nil {
  1149  		return nil, err
  1150  	}
  1151  	return envVars, nil
  1152  }
  1153  
  1154  type processReader struct {
  1155  	processHandle  windows.Handle
  1156  	is32BitProcess bool
  1157  	offset         uint64
  1158  }
  1159  
  1160  func (p *processReader) Read(buf []byte) (int, error) {
  1161  	processMemory := readProcessMemory(syscall.Handle(p.processHandle), p.is32BitProcess, p.offset, uint(len(buf)))
  1162  	if len(processMemory) == 0 {
  1163  		return 0, io.EOF
  1164  	}
  1165  	copy(buf, processMemory)
  1166  	p.offset += uint64(len(processMemory))
  1167  	return len(processMemory), nil
  1168  }
  1169  
  1170  func getProcessCommandLine(pid int32) (string, error) {
  1171  	h, err := windows.OpenProcess(processQueryInformation|windows.PROCESS_VM_READ, false, uint32(pid))
  1172  	if err == windows.ERROR_ACCESS_DENIED || err == windows.ERROR_INVALID_PARAMETER {
  1173  		return "", nil
  1174  	}
  1175  	if err != nil {
  1176  		return "", err
  1177  	}
  1178  	defer func(handle syscall.Handle) {
  1179  		_ = syscall.CloseHandle(handle)
  1180  	}(syscall.Handle(h))
  1181  
  1182  	procIs32Bits := is32BitProcess(h)
  1183  
  1184  	if procIs32Bits {
  1185  		userProcParams, err := getUserProcessParams32(h)
  1186  		if err != nil {
  1187  			return "", err
  1188  		}
  1189  		if userProcParams.CommandLineLength > 0 {
  1190  			cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, uint64(userProcParams.CommandLineAddress), uint(userProcParams.CommandLineLength))
  1191  			if len(cmdLine) != int(userProcParams.CommandLineLength) {
  1192  				return "", errors.New("cannot read cmdline")
  1193  			}
  1194  
  1195  			return convertUTF16ToString(cmdLine), nil
  1196  		}
  1197  	} else {
  1198  		userProcParams, err := getUserProcessParams64(h)
  1199  		if err != nil {
  1200  			return "", err
  1201  		}
  1202  		if userProcParams.CommandLineLength > 0 {
  1203  			cmdLine := readProcessMemory(syscall.Handle(h), procIs32Bits, userProcParams.CommandLineAddress, uint(userProcParams.CommandLineLength))
  1204  			if len(cmdLine) != int(userProcParams.CommandLineLength) {
  1205  				return "", errors.New("cannot read cmdline")
  1206  			}
  1207  
  1208  			return convertUTF16ToString(cmdLine), nil
  1209  		}
  1210  	}
  1211  
  1212  	//if we reach here, we have no command line
  1213  	return "", nil
  1214  }
  1215  
  1216  func convertUTF16ToString(src []byte) string {
  1217  	srcLen := len(src) / 2
  1218  
  1219  	codePoints := make([]uint16, srcLen)
  1220  
  1221  	srcIdx := 0
  1222  	for i := 0; i < srcLen; i++ {
  1223  		codePoints[i] = uint16(src[srcIdx]) | uint16(src[srcIdx+1])<<8
  1224  		srcIdx += 2
  1225  	}
  1226  	return syscall.UTF16ToString(codePoints)
  1227  }