Files
vServer/Backend/tools/cmd_go.go
Falknat 02ae56b78c Большое обновление GUI интерфейс
Большое обновление GUI интерфейс

- Добавлен фраемворr Walles
- Удалена консольная версия
- Проработан интерфейс и дизайн
- Добавлено кеширование для быстрой реакции.
- Сделан .ps1 сборщик для удобной сборки проекта.
- Обновлён Readme
2025-11-14 08:40:25 +07:00

192 lines
5.5 KiB
Go

//go:build windows
package tools
import (
"bufio"
"fmt"
"os"
"os/exec"
"syscall"
"unsafe"
)
var (
kernel32 = syscall.NewLazyDLL("kernel32.dll")
procSetConsoleMode = kernel32.NewProc("SetConsoleMode")
procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
procCreateMutex = kernel32.NewProc("CreateMutexW")
procCloseHandle = kernel32.NewProc("CloseHandle")
procCreateJobObject = kernel32.NewProc("CreateJobObjectW")
procAssignProcessToJobObject = kernel32.NewProc("AssignProcessToJobObject")
procSetInformationJobObject = kernel32.NewProc("SetInformationJobObject")
)
const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
const JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE = 0x2000
var mutexHandle syscall.Handle
var jobHandle syscall.Handle
func init() {
enableVirtualTerminal()
createJobObject()
}
func createJobObject() {
handle, _, _ := procCreateJobObject.Call(0, 0)
if handle == 0 {
return
}
jobHandle = syscall.Handle(handle)
// Устанавливаем флаг автоматического завершения дочерних процессов
type JOBOBJECT_EXTENDED_LIMIT_INFORMATION struct {
BasicLimitInformation struct {
PerProcessUserTimeLimit uint64
PerJobUserTimeLimit uint64
LimitFlags uint32
MinimumWorkingSetSize uintptr
MaximumWorkingSetSize uintptr
ActiveProcessLimit uint32
Affinity uintptr
PriorityClass uint32
SchedulingClass uint32
}
IoInfo [48]byte
ProcessMemoryLimit uintptr
JobMemoryLimit uintptr
PeakProcessMemoryUsed uintptr
PeakJobMemoryUsed uintptr
}
var limitInfo JOBOBJECT_EXTENDED_LIMIT_INFORMATION
limitInfo.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE
procSetInformationJobObject.Call(
uintptr(jobHandle),
9, // JobObjectExtendedLimitInformation
uintptr(unsafe.Pointer(&limitInfo)),
unsafe.Sizeof(limitInfo),
)
// Добавляем текущий процесс в Job Object
currentProcess, _ := syscall.GetCurrentProcess()
procAssignProcessToJobObject.Call(uintptr(jobHandle), uintptr(currentProcess))
}
func enableVirtualTerminal() {
handle := os.Stdout.Fd()
var mode uint32
// Получаем текущий режим консоли
_, _, _ = procGetConsoleMode.Call(uintptr(handle), uintptr(unsafe.Pointer(&mode)))
// Добавляем флаг поддержки ANSI
mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING
// Устанавливаем новый режим
_, _, _ = procSetConsoleMode.Call(uintptr(handle), uintptr(mode))
}
func RunBatScript(script string) (string, error) {
// Создание временного файла
tmpFile, err := os.CreateTemp("", "script-*.bat")
if err != nil {
return "", fmt.Errorf("ошибка создания temp-файла: %w", err)
}
defer os.Remove(tmpFile.Name())
// Запись скрипта в файл
if _, err := tmpFile.WriteString(script); err != nil {
return "", fmt.Errorf("ошибка записи в temp-файл: %w", err)
}
tmpFile.Close()
// Выполняем файл через cmd
cmd := exec.Command("cmd", "/C", tmpFile.Name())
output, err := cmd.CombinedOutput()
return string(output), err
}
// Функция для логирования вывода процесса в консоль
func Logs_console(process *exec.Cmd, check bool) error {
// Скрываем окно процесса для GUI приложений
process.SysProcAttr = &syscall.SysProcAttr{
HideWindow: true,
CreationFlags: 0x08000000, // CREATE_NO_WINDOW
}
if check {
// Настраиваем pipes для захвата вывода
stdout, err := process.StdoutPipe()
CheckError(err)
stderr, err := process.StderrPipe()
CheckError(err)
// Запускаем процесс
process.Start()
// Захватываем stdout и stderr для вывода логов
go func() {
scanner := bufio.NewScanner(stdout)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}()
go func() {
scanner := bufio.NewScanner(stderr)
for scanner.Scan() {
fmt.Println(scanner.Text())
}
}()
} else {
// Просто запускаем процесс без логирования
return process.Start()
}
return nil
}
// CheckSingleInstance проверяет, не запущена ли программа уже через мьютекс
func CheckSingleInstance() bool {
mutexName, _ := syscall.UTF16PtrFromString("Global\\vServer_SingleInstance")
handle, _, err := procCreateMutex.Call(
0,
0,
uintptr(unsafe.Pointer(mutexName)),
)
if handle == 0 {
return false // не удалось создать мьютекс
}
mutexHandle = syscall.Handle(handle)
// Если GetLastError возвращает ERROR_ALREADY_EXISTS (183), значит мьютекс уже существует
if err.(syscall.Errno) == 183 {
return false // программа уже запущена
}
return true // успешно создали мьютекс, программа не запущена
}
// ReleaseMutex освобождает мьютекс при завершении программы
func ReleaseMutex() {
if mutexHandle != 0 {
procCloseHandle.Call(uintptr(mutexHandle))
mutexHandle = 0
}
// Закрываем Job Object - это автоматически убьёт все дочерние процессы
if jobHandle != 0 {
procCloseHandle.Call(uintptr(jobHandle))
jobHandle = 0
}
}