用golang在windows下创建窗口并显示图片

独立风华 · · 9359 次点击 · · 开始浏览    
这是一个创建于 的文章,其中的信息可能已经有所发展或是发生改变。

这是我编写的一个包(库):

github.com/jthmath/winapi

这个包实现了一些Windows API函数的golang封装。


至于说为什么不用现成的(如walk),我会另文解释。我们还是先看一看代码吧。


第1步,注册窗口类

func MyRegisterClass(hInstance winapi.HINSTANCE) (atom uint16, err error) {
    var wc winapi.WNDCLASS
    wc.Style = winapi.CS_HREDRAW | winapi.CS_VREDRAW
    wc.PfnWndProc = WndProc
    wc.CbClsExtra = 0
    wc.CbWndExtra = 0
    wc.HInstance = hInstance
    wc.HIcon = 0
    wc.HCursor, err = winapi.LoadCursorById(0, winapi.IDC_ARROW)
    if err != nil {
        return
    }
    wc.HbrBackground = winapi.COLOR_WINDOW + 1
    wc.PszMenuName = ""
    wc.PszClassName = "主窗口类"
    wc.HIconSmall = 0

    return winapi.RegisterClass(&wc)
}

第2步,创建窗口

wnd, err := winapi.CreateWindow("主窗口类", "golang windows 编程",
    winapi.WS_OVERLAPPEDWINDOW, 0,
    winapi.CW_USEDEFAULT, winapi.CW_USEDEFAULT, winapi.CW_USEDEFAULT, winapi.CW_USEDEFAULT,
    0, 0, Inst, 0)
if err != nil {
    fmt.Println("创建窗口失败")
    return 0
}
fmt.Println("创建窗口成功", wnd)
winapi.ShowWindow(wnd, winapi.SW_SHOW)
winapi.UpdateWindow(wnd)

第3步,主消息循环

var msg winapi.MSG
msg.Message = winapi.WM_QUIT + 1 // 让它不等于 winapi.WM_QUIT

for winapi.GetMessage(&msg, 0, 0, 0) > 0 {
    winapi.TranslateMessage(&msg)
    winapi.DispatchMessage(&msg)
}

这些步骤,与用C或C++编写Windows程序没什么两样,只不过,现在的函数是golang风格。


当然,还要在相应WM_PAINT消息的时候贴图。

func WndProc(hWnd winapi.HWND, message uint32, wParam uintptr, lParam uintptr) uintptr {
    var hTemp winapi.HANDLE

    switch message {
    case winapi.WM_CREATE:
        hTemp, _ = winapi.LoadImageByName(0, "D:\\0004.bmp",
            winapi.IMAGE_BITMAP, 0, 0, winapi.LR_LOADFROMFILE)
        hBitmap = winapi.HBITMAP(hTemp)
    case winapi.WM_PAINT:
        OnPaint(hWnd)
    case winapi.WM_DESTROY:
        winapi.PostQuitMessage(0)
    case winapi.WM_COMMAND:
        OnCommand(hWnd, wParam, lParam)
    default:
        return winapi.DefWindowProc(hWnd, message, wParam, lParam)
    }
    return 0
}

func OnPaint(hWnd winapi.HWND) {
    var err error
    var ps winapi.PaintStruct

    hdc, err := winapi.BeginPaint(hWnd, &ps)
    if err != nil {
        return
    }
    defer winapi.EndPaint(hWnd, &ps) // defer 终于有用武之地了

    // HDC mdc = CreateCompatibleDC(hdc);
    mdc, err := winapi.CreateCompatibleDC(hdc)
    if err != nil {
        return
    }
    defer winapi.DeleteDC(mdc)

    winapi.SelectObject(mdc, winapi.HGDIOBJ(hBitmap))

    winapi.BitBlt(hdc, 0, 0, 480, 640, mdc, 0, 0, winapi.SRCCOPY)
}

效果如下:


完整代码

package main

import (
    "fmt"
    "runtime"

    "github.com/jthmath/winapi"
)

// 全局变量
var hBitmap winapi.HBITMAP

func init() {
    runtime.GOMAXPROCS(runtime.NumCPU())
}

func main() {
    inst, err := winapi.GetModuleHandle("")
    if err != nil {
        fmt.Println("获取实例失败")
        return
    }

    r := WinMain(inst, "", 0)
    fmt.Println("WinMain函数返回", r)
}

func WinMain(Inst winapi.HINSTANCE, Cmd string, nCmdShow int32) int32 {
    var err error

    // 1. 注册窗口类
    atom, err := MyRegisterClass(Inst)
    if err != nil {
        fmt.Println("注册窗口类失败")
        return 0
    }
    fmt.Println("注册窗口类成功", atom)

    // 2. 创建窗口
    wnd, err := winapi.CreateWindow("主窗口类", "golang windows 编程",
        winapi.WS_OVERLAPPEDWINDOW, 0,
        winapi.CW_USEDEFAULT, winapi.CW_USEDEFAULT, winapi.CW_USEDEFAULT, winapi.CW_USEDEFAULT,
        0, 0, Inst, 0)
    if err != nil {
        fmt.Println("创建窗口失败")
        return 0
    }
    fmt.Println("创建窗口成功", wnd)
    winapi.ShowWindow(wnd, winapi.SW_SHOW)
    winapi.UpdateWindow(wnd)

    // 3. 主消息循环
    var msg winapi.MSG
    msg.Message = winapi.WM_QUIT + 1 // 让它不等于 winapi.WM_QUIT

    for winapi.GetMessage(&msg, 0, 0, 0) > 0 {
        winapi.TranslateMessage(&msg)
        winapi.DispatchMessage(&msg)
    }

    return int32(msg.WParam)
}

func WndProc(hWnd winapi.HWND, message uint32, wParam uintptr, lParam uintptr) uintptr {
    var hTemp winapi.HANDLE

    switch message {
    case winapi.WM_CREATE:
        hTemp, _ = winapi.LoadImageByName(0, "D:\\0004.bmp",
            winapi.IMAGE_BITMAP, 0, 0, winapi.LR_LOADFROMFILE)
        hBitmap = winapi.HBITMAP(hTemp)
    case winapi.WM_PAINT:
        OnPaint(hWnd)
    case winapi.WM_DESTROY:
        winapi.PostQuitMessage(0)
    case winapi.WM_COMMAND:
        OnCommand(hWnd, wParam, lParam)
    default:
        return winapi.DefWindowProc(hWnd, message, wParam, lParam)
    }
    return 0
}

func OnPaint(hWnd winapi.HWND) {
    var err error
    var ps winapi.PaintStruct

    hdc, err := winapi.BeginPaint(hWnd, &ps)
    if err != nil {
        return
    }
    defer winapi.EndPaint(hWnd, &ps) // defer 终于有用武之地了

    // HDC mdc = CreateCompatibleDC(hdc);
    mdc, err := winapi.CreateCompatibleDC(hdc)
    if err != nil {
        return
    }
    defer winapi.DeleteDC(mdc)

    winapi.SelectObject(mdc, winapi.HGDIOBJ(hBitmap))

    // 这个函数的第4、5个参数分别是图片的宽、高
    // 为了简便起见,我直接写在了这里
    // 实际项目中当然要用过程序获取一下
    winapi.BitBlt(hdc, 0, 0, 480, 640, mdc, 0, 0, winapi.SRCCOPY)
}

func OnCommand(hWnd winapi.HWND, wParam uintptr, lParam uintptr) {
    // 暂时不需要特殊处理 WM_COMMAND
    winapi.DefWindowProc(hWnd, winapi.WM_COMMAND, wParam, lParam)
}

func MyRegisterClass(hInstance winapi.HINSTANCE) (atom uint16, err error) {
    var wc winapi.WNDCLASS
    wc.Style = winapi.CS_HREDRAW | winapi.CS_VREDRAW
    wc.PfnWndProc = WndProc
    wc.CbClsExtra = 0
    wc.CbWndExtra = 0
    wc.HInstance = hInstance
    wc.HIcon = 0
    wc.HCursor, err = winapi.LoadCursorById(0, winapi.IDC_ARROW)
    if err != nil {
        return
    }
    wc.HbrBackground = winapi.COLOR_WINDOW + 1
    wc.PszMenuName = ""
    wc.PszClassName = "主窗口类"
    wc.HIconSmall = 0

    return winapi.RegisterClass(&wc)
}

有疑问加站长微信联系(非本文作者)

本文来自:开源中国博客

感谢作者:独立风华

查看原文:用golang在windows下创建窗口并显示图片

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

9359 次点击  ∙  3 赞  
加入收藏 微博
5 回复  |  直到 2022-03-19 15:51:02
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传