Golang如何检测二进制文件是否使用"-H windowsgui"参数编译为Windows GUI程序

Golang如何检测二进制文件是否使用"-H windowsgui"参数编译为Windows GUI程序 我想检测二进制文件是否是通过“-H windowsgui”参数构建的,如果是,则执行GUI逻辑,否则执行控制台逻辑。

这样我就可以在一个地方处理应用程序的GUI和CLI逻辑(这在Linux和Darwin上运行良好),并且我可以为Windows构建两次:

go build -ldflags="-H windowsgui" -o myapp.exe
go build -o myapp-cli.exe

请帮助我。

1 回复

更多关于Golang如何检测二进制文件是否使用"-H windowsgui"参数编译为Windows GUI程序的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Windows上,可以通过检查进程是否附加到控制台来检测二进制文件是否使用-H windowsgui参数编译。以下是实现方法:

package main

import (
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

var (
    kernel32 = syscall.NewLazyDLL("kernel32.dll")
    getConsoleWindow = kernel32.NewProc("GetConsoleWindow")
)

func isGUI() bool {
    // 尝试获取控制台窗口句柄
    hConsole, _, _ := getConsoleWindow.Call()
    
    // 如果返回0,说明没有控制台窗口(GUI模式)
    // 如果返回非0,说明有控制台窗口(控制台模式)
    return hConsole == 0
}

func main() {
    if isGUI() {
        // GUI逻辑
        fmt.Println("运行在GUI模式")
        // 这里可以调用Windows GUI相关代码
        // 例如使用walk或fyne等GUI库
    } else {
        // 控制台逻辑
        fmt.Println("运行在控制台模式")
        // 正常的控制台交互逻辑
        fmt.Print("请输入内容: ")
        var input string
        fmt.Scanln(&input)
        fmt.Printf("你输入了: %s\n", input)
    }
}

更健壮的实现可以结合检查标准输入输出:

package main

import (
    "fmt"
    "os"
    "syscall"
    "unsafe"
)

func isWindowsGUI() bool {
    // 方法1:检查控制台窗口
    kernel32 := syscall.NewLazyDLL("kernel32.dll")
    getConsoleWindow := kernel32.NewProc("GetConsoleWindow")
    
    hConsole, _, _ := getConsoleWindow.Call()
    if hConsole == 0 {
        return true
    }
    
    // 方法2:检查标准输入输出
    fi, err := os.Stdout.Stat()
    if err != nil {
        return true
    }
    
    // 如果标准输出不是字符设备,可能是GUI程序
    return (fi.Mode() & os.ModeCharDevice) == 0
}

func main() {
    if isWindowsGUI() {
        // GUI应用程序逻辑
        runGUI()
    } else {
        // 控制台应用程序逻辑
        runCLI()
    }
}

func runGUI() {
    fmt.Println("这是GUI应用程序")
    // Windows GUI初始化代码
}

func runCLI() {
    fmt.Println("这是控制台应用程序")
    // 控制台交互代码
}

对于需要处理Windows消息循环的情况:

package main

import (
    "fmt"
    "syscall"
    "unsafe"
)

func main() {
    if isCompiledAsGUI() {
        // 初始化GUI消息循环
        initGUI()
    } else {
        runConsoleApp()
    }
}

func isCompiledAsGUI() bool {
    kernel32 := syscall.NewLazyDLL("kernel32.dll")
    proc := kernel32.NewProc("GetConsoleWindow")
    
    // 调用GetConsoleWindow
    hwnd, _, _ := proc.Call()
    
    // hwnd为0表示没有控制台窗口(GUI程序)
    // hwnd非0表示有控制台窗口(控制台程序)
    return hwnd == 0
}

func initGUI() {
    user32 := syscall.NewLazyDLL("user32.dll")
    getMessage := user32.NewProc("GetMessageW")
    translateMessage := user32.NewProc("TranslateMessage")
    dispatchMessage := user32.NewProc("DispatchMessageW")
    
    var msg struct {
        hwnd    syscall.Handle
        message uint32
        wParam  uintptr
        lParam  uintptr
        time    uint32
        pt      struct{ x, y int32 }
    }
    
    for {
        ret, _, _ := getMessage.Call(uintptr(unsafe.Pointer(&msg)), 0, 0, 0)
        if ret == 0 {
            break
        }
        
        translateMessage.Call(uintptr(unsafe.Pointer(&msg)))
        dispatchMessage.Call(uintptr(unsafe.Pointer(&msg)))
    }
}

func runConsoleApp() {
    fmt.Println("控制台模式运行")
    // 控制台应用程序逻辑
}

这种方法利用了-H windowsgui参数编译的程序不会附加控制台窗口的特性。当使用该参数编译时,GetConsoleWindow()返回0,否则返回有效的窗口句柄。

回到顶部