2014-05-19 13 views
8

Tôi có một ứng dụng mà thường chạy im lặng trong nền, vì vậy tôi biên dịch nó vớiIn ấn đầu ra vào một cửa sổ lệnh khi ứng dụng golang được biên soạn với -ldflags -H = windowsgui

go build -ldflags -H=windowsgui <gofile> 

Để kiểm tra phiên bản tại dòng lệnh, tôi muốn truyền một lá cờ -V đến dòng lệnh để lấy chuỗi giữ phiên bản được in ra dấu nhắc lệnh sau đó thoát ứng dụng. Tôi đã thêm gói và mã cờ. Khi tôi thử nghiệm nó với

go run <gofile> -V 

... nó in phiên bản tốt. Khi tôi biên dịch exe, nó chỉ thoát ra, không in được gì cả. Tôi nghi ngờ đó là cờ biên dịch khiến nó không truy cập vào bảng điều khiển và gửi văn bản của tôi vào nhóm bit.

Tôi đã thử các biến thể để in thành stderr và stdout, sử dụng println và fprintf và os.stderr.write, nhưng không có gì xuất hiện từ ứng dụng đã biên dịch. Làm thế nào tôi nên thử in một chuỗi để dấu nhắc lệnh khi biên dịch với những lá cờ?

Trả lời

14

Vấn đề là khi một quá trình được tạo ra sử dụng một thực thi trong đó có the "subsystem" variable trong PE header bộ của mình để "Windows", quá trình này có three standard handles nó đóng cửa và nó không được kết hợp với bất kỳ giao diện điều khiển — không có vấn đề nếu bạn chạy từ bảng điều khiển hay không. (Trên thực tế, nếu bạn chạy một tệp thực thi có hệ thống con được đặt thành "giao diện điều khiển" không từ bảng điều khiển, bảng điều khiển sẽ được tạo cưỡng bức cho quá trình đó và quá trình được đính kèm với nó — bạn thường thấy nó dưới dạng cửa sổ điều khiển popping Do đó, để in bất kỳ thứ gì vào bàn điều khiển từ quá trình GUI trên Windows, bạn phải kết nối rõ ràng quy trình đó với bàn điều khiển được gắn với tiến trình cha mẹ của nó (nếu có), giống như đã giải thích here chẳng hạn. Để thực hiện điều này, bạn gọi hàm AttachConsole API. Với Go, điều này có thể được thực hiện bằng cách sử dụng syscall gói:

package main 

import (
    "fmt" 
    "syscall" 
) 

const (
    ATTACH_PARENT_PROCESS = ^uint32(0) // (DWORD)-1 
) 

var (
    modkernel32 = syscall.NewLazyDLL("kernel32.dll") 

    procAttachConsole = modkernel32.NewProc("AttachConsole") 

) 

func AttachConsole(dwParentProcess uint32) (ok bool) { 
    r0, _, _ := syscall.Syscall(procAttachConsole.Addr(), 1, uintptr(dwParentProcess), 0, 0) 
    ok = bool(r0 != 0) 
    return 
} 

func main() { 
    ok := AttachConsole(ATTACH_PARENT_PROCESS) 
    if ok { 
     fmt.Println("Okay, attached") 
    } 
} 

Để thực sự hoàn chỉnh, khi AttachConsole() thất bại, mã này có lẽ nên lấy một trong hai lộ trình các tuyến:

  • Gọi AllocConsole() để có được cửa sổ giao diện điều khiển của riêng nó được tạo cho nó. Nó có thể nói rằng điều này là khá nhiều vô dụng để hiển thị thông tin phiên bản như quá trình thường thoát sau khi in nó, và kinh nghiệm người dùng kết quả sẽ là một cửa sổ giao diện điều khiển bật lên và ngay lập tức biến mất; người dùng điện sẽ nhận được một gợi ý rằng họ nên chạy lại ứng dụng từ bảng điều khiển nhưng chỉ những người chết sẽ không thể đối phó.

  • Đăng hộp thoại GUI hiển thị cùng một thông tin.

    Tôi nghĩ đây chỉ là những gì cần thiết: lưu ý rằng hiển thị thông báo trợ giúp/sử dụng để phản hồi người dùng chỉ định một số đối số dòng lệnh thường là liên kết tinh thần với bảng điều khiển. ví dụ: thử chạy msiexec.exe /? tại bảng điều khiển và xem điều gì sẽ xảy ra.

+0

[Xem thêm] (https://github.com/inconshreveable/mousetrap). – kostix

1

Trả lời ở trên rất hữu ích nhưng cũng không có tác dụng gì cho tôi.Sau khi một số nghiên cứu thêm Tôi đến mã này:

// go build -ldflags -H=windowsgui 
package main 

import "fmt" 
import "os" 
import "syscall" 

func main() { 
    modkernel32 := syscall.NewLazyDLL("kernel32.dll") 
    procAllocConsole := modkernel32.NewProc("AllocConsole") 
    r0, r1, err0 := syscall.Syscall(procAllocConsole.Addr(), 0, 0, 0, 0) 
    if r0 == 0 { // Allocation failed, probably process already has a console 
     fmt.Printf("Could not allocate console: %s. Check build flags..", err0) 
     os.Exit(1) 
    } 
    hout, err1 := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE) 
    hin, err2 := syscall.GetStdHandle(syscall.STD_INPUT_HANDLE) 
    if err1 != nil || err2 != nil { // nowhere to print the error 
     os.Exit(2) 
    } 
    os.Stdout = os.NewFile(uintptr(hout), "/dev/stdout") 
    os.Stdin = os.NewFile(uintptr(hin), "/dev/stdin") 
    fmt.Printf("Hello!\nResult of console allocation: ") 
    fmt.Printf("r0=%d,r1=%d,err=%s\nFor Goodbye press Enter..", r0, r1, err0) 
    var s string 
    fmt.Scanln(&s) 
    os.Exit(0) 
} 

Điểm mấu chốt: sau khi phân bổ/gắn các giao diện điều khiển, có cần phải nhận được stdout xử lý, tập tin mở bằng tay cầm này và gán nó vào biến os.Stdout. Nếu bạn cần stdin bạn phải lặp lại tương tự cho stdin.

0

Bạn có thể nhận được hành vi mong muốn mà không cần sử dụng -H = windowsgui; về cơ bản bạn sẽ tạo một ứng dụng chuẩn (với cửa sổ giao diện điều khiển riêng) và ẩn nó cho đến khi chương trình thoát.

func Console(show bool) { 
    var getWin = syscall.NewLazyDLL("kernel32.dll").NewProc("GetConsoleWindow") 
    var showWin = syscall.NewLazyDLL("user32.dll").NewProc("ShowWindow") 
    hwnd, _, _ := getWin.Call() 
    if hwnd == 0 { 
      return 
    } 
    if show { 
     var SW_RESTORE uintptr = 9 
     showWin.Call(hwnd, SW_RESTORE) 
    } else { 
     var SW_HIDE uintptr = 0 
     showWin.Call(hwnd, SW_HIDE) 
    } 
} 

Và sau đó sử dụng nó như thế này:

func main() { 
    Console(false) 
    defer Console(true) 
    ... 
    fmt.Println("Hello World") 
    ... 
} 
Các vấn đề liên quan