chore: Move private packages to internal (#1664)

This commit is contained in:
Jan-Otto Kröpke
2024-10-03 20:23:56 +02:00
committed by GitHub
parent bcfe6df24d
commit 5d95610c84
181 changed files with 1680 additions and 1867 deletions

View File

@@ -0,0 +1,6 @@
package iphlpapi
const (
TCPTableClass uint32 = 5
TCP6TableClass uint32 = 5
)

View File

@@ -0,0 +1,77 @@
package iphlpapi
import (
"fmt"
"unsafe"
"golang.org/x/sys/windows"
)
var (
modiphlpapi = windows.NewLazySystemDLL("iphlpapi.dll")
procGetExtendedTcpTable = modiphlpapi.NewProc("GetExtendedTcpTable")
)
func GetTCPConnectionStates(family uint32) (map[MIB_TCP_STATE]uint32, error) {
var size uint32
stateCounts := make(map[MIB_TCP_STATE]uint32)
rowSize := uint32(unsafe.Sizeof(MIB_TCPROW_OWNER_PID{}))
tableClass := TCPTableClass
if family == windows.AF_INET6 {
rowSize = uint32(unsafe.Sizeof(MIB_TCP6ROW_OWNER_PID{}))
tableClass = TCP6TableClass
}
ret := getExtendedTcpTable(0, &size, true, family, tableClass, 0)
if ret != 0 && ret != uintptr(windows.ERROR_INSUFFICIENT_BUFFER) {
return nil, fmt.Errorf("getExtendedTcpTable (size query) failed with code %d", ret)
}
buf := make([]byte, size)
ret = getExtendedTcpTable(uintptr(unsafe.Pointer(&buf[0])), &size, true, family, tableClass, 0)
if ret != 0 {
return nil, fmt.Errorf("getExtendedTcpTable (data query) failed with code %d", ret)
}
numEntries := *(*uint32)(unsafe.Pointer(&buf[0]))
for i := range numEntries {
var state MIB_TCP_STATE
if family == windows.AF_INET6 {
row := (*MIB_TCP6ROW_OWNER_PID)(unsafe.Pointer(&buf[4+i*rowSize]))
state = row.dwState
} else {
row := (*MIB_TCPROW_OWNER_PID)(unsafe.Pointer(&buf[4+i*rowSize]))
state = row.dwState
}
stateCounts[state]++
}
return stateCounts, nil
}
func getExtendedTcpTable(pTCPTable uintptr, pdwSize *uint32, bOrder bool, ulAf uint32, tableClass uint32, reserved uint32) uintptr {
ret, _, _ := procGetExtendedTcpTable.Call(
pTCPTable,
uintptr(unsafe.Pointer(pdwSize)),
uintptr(boolToInt(bOrder)),
uintptr(ulAf),
uintptr(tableClass),
uintptr(reserved),
)
return ret
}
func boolToInt(b bool) int {
if b {
return 1
}
return 0
}

View File

@@ -0,0 +1,76 @@
package iphlpapi
import "fmt"
// MIB_TCPROW_OWNER_PID structure for IPv4.
// https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ns-tcpmib-mib_tcprow_owner_pid
type MIB_TCPROW_OWNER_PID struct {
dwState MIB_TCP_STATE
dwLocalAddr uint32
dwLocalPort uint32
dwRemoteAddr uint32
dwRemotePort uint32
dwOwningPid uint32
}
// MIB_TCP6ROW_OWNER_PID structure for IPv6.
// https://learn.microsoft.com/en-us/windows/win32/api/tcpmib/ns-tcpmib-mib_tcp6row_owner_pid
type MIB_TCP6ROW_OWNER_PID struct {
ucLocalAddr [16]byte
dwLocalScopeId uint32
dwLocalPort uint32
ucRemoteAddr [16]byte
dwRemoteScopeId uint32
dwRemotePort uint32
dwState MIB_TCP_STATE
dwOwningPid uint32
}
type MIB_TCP_STATE uint32
const (
_ MIB_TCP_STATE = iota
TCPStateClosed
TCPStateListening
TCPStateSynSent
TCPStateSynRcvd
TCPStateEstablished
TCPStateFinWait1
TCPStateFinWait2
TCPStateCloseWait
TCPStateClosing
TCPStateLastAck
TCPStateTimeWait
TCPStateDeleteTcb
)
func (state MIB_TCP_STATE) String() string {
switch state {
case TCPStateClosed:
return "CLOSED"
case TCPStateListening:
return "LISTENING"
case TCPStateSynSent:
return "SYN_SENT"
case TCPStateSynRcvd:
return "SYN_RECEIVED"
case TCPStateEstablished:
return "ESTABLISHED"
case TCPStateFinWait1:
return "FIN_WAIT1"
case TCPStateFinWait2:
return "FIN_WAIT2"
case TCPStateCloseWait:
return "CLOSE_WAIT"
case TCPStateClosing:
return "CLOSING"
case TCPStateLastAck:
return "LAST_ACK"
case TCPStateTimeWait:
return "TIME_WAIT"
case TCPStateDeleteTcb:
return "DELETE_TCB"
default:
return fmt.Sprintf("UNKNOWN_%d", state)
}
}

View File

@@ -0,0 +1,62 @@
package kernel32
import (
"unsafe"
"golang.org/x/sys/windows"
)
var (
kernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGetDynamicTimeZoneInformationSys = kernel32.NewProc("GetDynamicTimeZoneInformation")
kernelLocalFileTimeToFileTime = kernel32.NewProc("LocalFileTimeToFileTime")
)
// SYSTEMTIME contains a date and time.
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/minwinbase/ns-minwinbase-systemtime
type SYSTEMTIME struct {
WYear uint16
WMonth uint16
WDayOfWeek uint16
WDay uint16
WHour uint16
WMinute uint16
WSecond uint16
WMilliseconds uint16
}
// DynamicTimezoneInformation contains the current dynamic daylight time settings.
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/ns-timezoneapi-dynamic_time_zone_information
type DynamicTimezoneInformation struct {
Bias int32
standardName [32]uint16
StandardDate SYSTEMTIME
StandardBias int32
DaylightName [32]uint16
DaylightDate SYSTEMTIME
DaylightBias int32
TimeZoneKeyName [128]uint16
DynamicDaylightTimeDisabled uint8 // BOOLEAN
}
// GetDynamicTimeZoneInformation retrieves the current dynamic daylight time settings.
// 📑 https://docs.microsoft.com/en-us/windows/win32/api/timezoneapi/nf-timezoneapi-getdynamictimezoneinformation
func GetDynamicTimeZoneInformation() (DynamicTimezoneInformation, error) {
var tzi DynamicTimezoneInformation
r0, _, err := procGetDynamicTimeZoneInformationSys.Call(uintptr(unsafe.Pointer(&tzi)))
if uint32(r0) == 0xffffffff {
return tzi, err
}
return tzi, nil
}
func LocalFileTimeToFileTime(localFileTime, utcFileTime *windows.Filetime) uint32 {
ret, _, _ := kernelLocalFileTimeToFileTime.Call(
uintptr(unsafe.Pointer(localFileTime)),
uintptr(unsafe.Pointer(utcFileTime)))
return uint32(ret)
}

View File

@@ -0,0 +1,112 @@
package netapi32
import (
"errors"
"unsafe"
"golang.org/x/sys/windows"
)
// WKSTAInfo102 is a wrapper of WKSTA_Info_102
// https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/ns-lmwksta-wksta_info_102
type wKSTAInfo102 struct {
wki102_platform_id uint32
wki102_computername *uint16
wki102_langroup *uint16
wki102_ver_major uint32
wki102_ver_minor uint32
wki102_lanroot *uint16
wki102_logged_on_users uint32
}
// WorkstationInfo is an idiomatic wrapper of WKSTAInfo102.
type WorkstationInfo struct {
PlatformId uint32
ComputerName string
LanGroup string
VersionMajor uint32
VersionMinor uint32
LanRoot string
LoggedOnUsers uint32
}
var (
netapi32 = windows.NewLazySystemDLL("netapi32")
procNetWkstaGetInfo = netapi32.NewProc("NetWkstaGetInfo")
procNetApiBufferFree = netapi32.NewProc("NetApiBufferFree")
)
// NetApiStatus is a map of Network Management Error Codes.
// https://docs.microsoft.com/en-gb/windows/win32/netmgmt/network-management-error-codes?redirectedfrom=MSDN
var NetApiStatus = map[uint32]string{
// Success
0: "NERR_Success",
// This computer name is invalid.
2351: "NERR_InvalidComputer",
// This operation is only allowed on the primary domain controller of the domain.
2226: "NERR_NotPrimary",
// This operation is not allowed on this special group.
2234: "NERR_SpeGroupOp",
// This operation is not allowed on the last administrative account.
2452: "NERR_LastAdmin",
// The password parameter is invalid.
2203: "NERR_BadPassword",
// The password does not meet the password policy requirements.
// Check the minimum password length, password complexity and password history requirements.
2245: "NERR_PasswordTooShort",
// The user name could not be found.
2221: "NERR_UserNotFound",
// Errors
5: "ERROR_ACCESS_DENIED",
8: "ERROR_NOT_ENOUGH_MEMORY",
87: "ERROR_INVALID_PARAMETER",
123: "ERROR_INVALID_NAME",
124: "ERROR_INVALID_LEVEL",
234: "ERROR_MORE_DATA",
1219: "ERROR_SESSION_CREDENTIAL_CONFLICT",
}
// NetApiBufferFree frees the memory other network management functions use internally to return information.
// https://docs.microsoft.com/en-us/windows/win32/api/lmapibuf/nf-lmapibuf-netapibufferfree
func netApiBufferFree(buffer *wKSTAInfo102) {
procNetApiBufferFree.Call(uintptr(unsafe.Pointer(buffer))) //nolint:errcheck
}
// NetWkstaGetInfo returns information about the configuration of a workstation.
// https://docs.microsoft.com/en-us/windows/win32/api/lmwksta/nf-lmwksta-netwkstagetinfo
func netWkstaGetInfo() (wKSTAInfo102, uint32, error) {
var lpwi *wKSTAInfo102
pLevel := uintptr(102)
r1, _, _ := procNetWkstaGetInfo.Call(0, pLevel, uintptr(unsafe.Pointer(&lpwi)))
defer netApiBufferFree(lpwi)
if ret := *(*uint32)(unsafe.Pointer(&r1)); ret != 0 {
return wKSTAInfo102{}, ret, errors.New(NetApiStatus[ret])
}
deref := *lpwi
return deref, 0, nil
}
// GetWorkstationInfo is an idiomatic wrapper for netWkstaGetInfo.
func GetWorkstationInfo() (WorkstationInfo, error) {
info, _, err := netWkstaGetInfo()
if err != nil {
return WorkstationInfo{}, err
}
workstationInfo := WorkstationInfo{
PlatformId: info.wki102_platform_id,
ComputerName: windows.UTF16PtrToString(info.wki102_computername),
LanGroup: windows.UTF16PtrToString(info.wki102_langroup),
VersionMajor: info.wki102_ver_major,
VersionMinor: info.wki102_ver_minor,
LanRoot: windows.UTF16PtrToString(info.wki102_lanroot),
LoggedOnUsers: info.wki102_logged_on_users,
}
return workstationInfo, nil
}

View File

@@ -0,0 +1,45 @@
package psapi
import (
"unsafe"
"golang.org/x/sys/windows"
)
// PerformanceInformation is a wrapper of the PERFORMANCE_INFORMATION struct.
// https://docs.microsoft.com/en-us/windows/win32/api/psapi/ns-psapi-performance_information
type PerformanceInformation struct {
cb uint32
CommitTotal uint
CommitLimit uint
CommitPeak uint
PhysicalTotal uint
PhysicalAvailable uint
SystemCache uint
KernelTotal uint
KernelPaged uint
KernelNonpaged uint
PageSize uint
HandleCount uint32
ProcessCount uint32
ThreadCount uint32
}
var (
psapi = windows.NewLazySystemDLL("psapi.dll")
procGetPerformanceInfo = psapi.NewProc("GetPerformanceInfo")
)
// GetPerformanceInfo returns the dereferenced version of GetLPPerformanceInfo.
func GetPerformanceInfo() (PerformanceInformation, error) {
var lppi PerformanceInformation
size := (uint32)(unsafe.Sizeof(lppi))
lppi.cb = size
r1, _, err := procGetPerformanceInfo.Call(uintptr(unsafe.Pointer(&lppi)), uintptr(size))
if ret := *(*bool)(unsafe.Pointer(&r1)); !ret {
return PerformanceInformation{}, err
}
return lppi, nil
}

View File

@@ -0,0 +1,41 @@
package slc
import (
"errors"
"unsafe"
"golang.org/x/sys/windows"
)
var (
slc = windows.NewLazySystemDLL("slc.dll")
procSLIsWindowsGenuineLocal = slc.NewProc("SLIsWindowsGenuineLocal")
)
// SL_GENUINE_STATE enumeration
//
// https://learn.microsoft.com/en-us/windows/win32/api/slpublic/ne-slpublic-sl_genuine_state
type SL_GENUINE_STATE uint32
const (
SL_GEN_STATE_IS_GENUINE SL_GENUINE_STATE = iota
SL_GEN_STATE_INVALID_LICENSE
SL_GEN_STATE_TAMPERED
SL_GEN_STATE_OFFLINE
SL_GEN_STATE_LAST
)
// SLIsWindowsGenuineLocal function wrapper.
func SLIsWindowsGenuineLocal() (SL_GENUINE_STATE, error) {
var genuineState SL_GENUINE_STATE
_, _, err := procSLIsWindowsGenuineLocal.Call(
uintptr(unsafe.Pointer(&genuineState)),
)
if !errors.Is(err, windows.NTE_OP_OK) {
return 0, err
}
return genuineState, nil
}

View File

@@ -0,0 +1,170 @@
package sysinfoapi
import (
"unicode/utf16"
"unsafe"
"golang.org/x/sys/windows"
)
// MemoryStatusEx is a wrapper for MEMORYSTATUSEX
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-memorystatusex
type memoryStatusEx struct {
dwLength uint32
DwMemoryLoad uint32
UllTotalPhys uint64
UllAvailPhys uint64
UllTotalPageFile uint64
UllAvailPageFile uint64
UllTotalVirtual uint64
UllAvailVirtual uint64
UllAvailExtendedVirtual uint64
}
// MemoryStatus is an idiomatic wrapper for MemoryStatusEx.
type MemoryStatus struct {
MemoryLoad uint32
TotalPhys uint64
AvailPhys uint64
TotalPageFile uint64
AvailPageFile uint64
TotalVirtual uint64
AvailVirtual uint64
AvailExtendedVirtual uint64
}
// wProcessorArchitecture is a wrapper for the union found in LP_SYSTEM_INFO
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
type wProcessorArchitecture struct {
WProcessorArchitecture uint16
WReserved uint16
}
// ProcessorArchitecture is an idiomatic wrapper for wProcessorArchitecture.
type ProcessorArchitecture uint16
// Idiomatic values for wProcessorArchitecture.
const (
AMD64 ProcessorArchitecture = 9
ARM ProcessorArchitecture = 5
ARM64 ProcessorArchitecture = 12
IA64 ProcessorArchitecture = 6
INTEL ProcessorArchitecture = 0
UNKNOWN ProcessorArchitecture = 0xffff
)
// LpSystemInfo is a wrapper for LPSYSTEM_INFO
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/ns-sysinfoapi-system_info
type lpSystemInfo struct {
Arch wProcessorArchitecture
DwPageSize uint32
LpMinimumApplicationAddress uintptr
LpMaximumApplicationAddress uintptr
DwActiveProcessorMask uint
DwNumberOfProcessors uint32
DwProcessorType uint32
DwAllocationGranularity uint32
WProcessorLevel uint16
WProcessorRevision uint16
}
// SystemInfo is an idiomatic wrapper for LpSystemInfo.
type SystemInfo struct {
Arch ProcessorArchitecture
PageSize uint32
MinimumApplicationAddress uintptr
MaximumApplicationAddress uintptr
ActiveProcessorMask uint
NumberOfProcessors uint32
ProcessorType uint32
AllocationGranularity uint32
ProcessorLevel uint16
ProcessorRevision uint16
}
// WinComputerNameFormat is a wrapper for COMPUTER_NAME_FORMAT.
type WinComputerNameFormat int
// Definitions for WinComputerNameFormat constants.
const (
ComputerNameNetBIOS WinComputerNameFormat = iota
ComputerNameDNSHostname
ComputerNameDNSDomain
ComputerNameDNSFullyQualified
ComputerNamePhysicalNetBIOS
ComputerNamePhysicalDNSHostname
ComputerNamePhysicalDNSDomain
ComputerNamePhysicalDNSFullyQualified
ComputerNameMax
)
var (
kernel32 = windows.NewLazySystemDLL("kernel32.dll")
procGetSystemInfo = kernel32.NewProc("GetSystemInfo")
procGlobalMemoryStatusEx = kernel32.NewProc("GlobalMemoryStatusEx")
procGetComputerNameExW = kernel32.NewProc("GetComputerNameExW")
)
// GlobalMemoryStatusEx retrieves information about the system's current usage of both physical and virtual memory.
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-globalmemorystatusex
func GlobalMemoryStatusEx() (MemoryStatus, error) {
var mse memoryStatusEx
mse.dwLength = (uint32)(unsafe.Sizeof(mse))
r1, _, err := procGlobalMemoryStatusEx.Call(uintptr(unsafe.Pointer(&mse)))
if ret := *(*bool)(unsafe.Pointer(&r1)); !ret {
return MemoryStatus{}, err
}
return MemoryStatus{
MemoryLoad: mse.DwMemoryLoad,
TotalPhys: mse.UllTotalPhys,
AvailPhys: mse.UllAvailPhys,
TotalPageFile: mse.UllTotalPageFile,
AvailPageFile: mse.UllAvailPageFile,
TotalVirtual: mse.UllTotalVirtual,
AvailVirtual: mse.UllAvailVirtual,
AvailExtendedVirtual: mse.UllAvailExtendedVirtual,
}, nil
}
// GetSystemInfo is an idiomatic wrapper for the GetSystemInfo function from sysinfoapi
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getsysteminfo
func GetSystemInfo() SystemInfo {
var info lpSystemInfo
procGetSystemInfo.Call(uintptr(unsafe.Pointer(&info))) //nolint:errcheck
return SystemInfo{
Arch: ProcessorArchitecture(info.Arch.WProcessorArchitecture),
PageSize: info.DwPageSize,
MinimumApplicationAddress: info.LpMinimumApplicationAddress,
MaximumApplicationAddress: info.LpMinimumApplicationAddress,
ActiveProcessorMask: info.DwActiveProcessorMask,
NumberOfProcessors: info.DwNumberOfProcessors,
ProcessorType: info.DwProcessorType,
AllocationGranularity: info.DwAllocationGranularity,
ProcessorLevel: info.WProcessorLevel,
ProcessorRevision: info.WProcessorRevision,
}
}
// GetComputerName wraps the GetComputerNameW function in a more Go-like way
// https://docs.microsoft.com/en-us/windows/win32/api/sysinfoapi/nf-sysinfoapi-getcomputernameexw
func GetComputerName(f WinComputerNameFormat) (string, error) {
// 1kb buffer to accept computer name. This should be more than enough as the maximum size
// returned is the max length of a DNS name, which this author believes is 253 characters.
size := 1024
var buffer [1024]uint16
r1, _, err := procGetComputerNameExW.Call(uintptr(f), uintptr(unsafe.Pointer(&buffer)), uintptr(unsafe.Pointer(&size)))
if r1 == 0 {
return "", err
}
bytes := buffer[0:size]
out := utf16.Decode(bytes)
return string(out), nil
}

View File

@@ -0,0 +1,203 @@
package wtsapi32
import (
"fmt"
"log/slog"
"unsafe"
"golang.org/x/sys/windows"
)
type WTSTypeClass int
// The valid values for the WTSTypeClass enumeration.
const (
WTSTypeProcessInfoLevel0 WTSTypeClass = iota
WTSTypeProcessInfoLevel1
WTSTypeSessionInfoLevel1
)
type WTSConnectState uint32
const (
// wtsActive A user is logged on to the WinStation. This state occurs when a user is signed in and actively connected to the device.
wtsActive WTSConnectState = iota
// wtsConnected The WinStation is connected to the client.
wtsConnected
// wtsConnectQuery The WinStation is in the process of connecting to the client.
wtsConnectQuery
// wtsShadow The WinStation is shadowing another WinStation.
wtsShadow
// wtsDisconnected The WinStation is active but the client is disconnected.
// This state occurs when a user is signed in but not actively connected to the device, such as when the user has chosen to exit to the lock screen.
wtsDisconnected
// wtsIdle The WinStation is waiting for a client to connect.
wtsIdle
// wtsListen The WinStation is listening for a connection. A listener session waits for requests for new client connections.
// No user is logged on a listener session. A listener session cannot be reset, shadowed, or changed to a regular client session.
wtsListen
// wtsReset The WinStation is being reset.
wtsReset
// wtsDown The WinStation is down due to an error.
wtsDown
// wtsInit The WinStation is initializing.
wtsInit
)
// WTSSessionInfo1w contains information about a session on a Remote Desktop Session Host (RD Session Host) server.
// docs: https://docs.microsoft.com/en-us/windows/win32/api/wtsapi32/ns-wtsapi32-wts_session_info_1w
type wtsSessionInfo1 struct {
// ExecEnvID An identifier that uniquely identifies the session within the list of sessions returned by the WTSEnumerateSessionsEx function.
ExecEnvID uint32
// State A value of the WTSConnectState enumeration type that specifies the connection state of a Remote Desktop Services session.
State uint32
// SessionID A session identifier assigned by the RD Session Host server, RD Virtualization Host server, or virtual machine.
SessionID uint32
// pSessionName A pointer to a null-terminated string that contains the name of this session. For example, "services", "console", or "RDP-Tcp#0".
pSessionName *uint16
// pHostName A pointer to a null-terminated string that contains the name of the computer that the session is running on.
// If the session is running directly on an RD Session Host server or RD Virtualization Host server, the string contains NULL.
// If the session is running on a virtual machine, the string contains the name of the virtual machine.
pHostName *uint16
// pUserName A pointer to a null-terminated string that contains the name of the user who is logged on to the session.
// If no user is logged on to the session, the string contains NULL.
pUserName *uint16
// pDomainName A pointer to a null-terminated string that contains the domain name of the user who is logged on to the session.
// If no user is logged on to the session, the string contains NULL.
pDomainName *uint16
// pFarmName A pointer to a null-terminated string that contains the name of the farm that the virtual machine is joined to.
// If the session is not running on a virtual machine that is joined to a farm, the string contains NULL.
pFarmName *uint16
}
type WTSSession struct {
ExecEnvID uint32
State WTSConnectState
SessionID uint32
SessionName string
HostName string
UserName string
DomainName string
FarmName string
}
var (
wtsapi32 = windows.NewLazySystemDLL("wtsapi32.dll")
procWTSOpenServerEx = wtsapi32.NewProc("WTSOpenServerExW")
procWTSEnumerateSessionsEx = wtsapi32.NewProc("WTSEnumerateSessionsExW")
procWTSFreeMemoryEx = wtsapi32.NewProc("WTSFreeMemoryExW")
procWTSCloseServer = wtsapi32.NewProc("WTSCloseServer")
WTSSessionStates = map[WTSConnectState]string{
wtsActive: "active",
wtsConnected: "connected",
wtsConnectQuery: "connect_query",
wtsShadow: "shadow",
wtsDisconnected: "disconnected",
wtsIdle: "idle",
wtsListen: "listen",
wtsReset: "reset",
wtsDown: "down",
wtsInit: "init",
}
)
func WTSOpenServer(server string) (windows.Handle, error) {
var (
err error
serverName *uint16
)
if server != "" {
serverName, err = windows.UTF16PtrFromString(server)
if err != nil {
return windows.InvalidHandle, err
}
}
r1, _, err := procWTSOpenServerEx.Call(uintptr(unsafe.Pointer(serverName)))
serverHandle := windows.Handle(r1)
if serverHandle == windows.InvalidHandle {
return windows.InvalidHandle, err
}
return serverHandle, nil
}
func WTSCloseServer(server windows.Handle) error {
r1, _, err := procWTSCloseServer.Call(uintptr(server))
if r1 != 1 {
return fmt.Errorf("failed to close server: %w", err)
}
return nil
}
func WTSFreeMemoryEx(class WTSTypeClass, pMemory uintptr, numberOfEntries uint32) error {
r1, _, err := procWTSFreeMemoryEx.Call(
uintptr(class),
pMemory,
uintptr(numberOfEntries),
)
if r1 != 1 {
return fmt.Errorf("failed to free memory: %w", err)
}
return nil
}
func WTSEnumerateSessionsEx(server windows.Handle, logger *slog.Logger) ([]WTSSession, error) {
var sessionInfoPointer uintptr
var count uint32
pLevel := uint32(1)
r1, _, err := procWTSEnumerateSessionsEx.Call(
uintptr(server),
uintptr(unsafe.Pointer(&pLevel)),
uintptr(0),
uintptr(unsafe.Pointer(&sessionInfoPointer)),
uintptr(unsafe.Pointer(&count)),
)
if r1 != 1 {
return nil, err
}
if sessionInfoPointer != 0 {
defer func(class WTSTypeClass, pMemory uintptr, NumberOfEntries uint32) {
err := WTSFreeMemoryEx(class, pMemory, NumberOfEntries)
if err != nil {
logger.Warn("failed to free memory", "err", fmt.Errorf("WTSEnumerateSessionsEx: %w", err))
}
}(WTSTypeSessionInfoLevel1, sessionInfoPointer, count)
}
var sizeTest wtsSessionInfo1
sessionSize := unsafe.Sizeof(sizeTest)
sessions := make([]WTSSession, 0, count)
for i := range count {
curPtr := unsafe.Pointer(sessionInfoPointer + (uintptr(i) * sessionSize))
data := (*wtsSessionInfo1)(curPtr)
sessionInfo := WTSSession{
ExecEnvID: data.ExecEnvID,
State: WTSConnectState(data.State),
SessionID: data.SessionID,
SessionName: windows.UTF16PtrToString(data.pSessionName),
HostName: windows.UTF16PtrToString(data.pHostName),
UserName: windows.UTF16PtrToString(data.pUserName),
DomainName: windows.UTF16PtrToString(data.pDomainName),
FarmName: windows.UTF16PtrToString(data.pFarmName),
}
sessions = append(sessions, sessionInfo)
}
return sessions, nil
}