Golang中StretchDIBits在Windows 10上无法工作的问题

Golang中StretchDIBits在Windows 10上无法工作的问题 你好,

我一直在开发一个Windows应用程序,它使用Windows gdi32.dll中的StretchDIBits函数在屏幕上绘制图像。我成功地在Windows 11上使其正常工作。

但是,当我在Windows 10上运行它时,StretchDIBits返回0,我完全不清楚是什么导致了这个问题。请注意,我在C++中测试了相同的代码,它在Windows 11和Windows 10上都能运行。

你可以在以下链接找到重现此问题的最小代码:buggy_stretchdibits.go · GitHub

谢谢,


更多关于Golang中StretchDIBits在Windows 10上无法工作的问题的实战教程也可以访问 https://www.itying.com/category-94-b0.html

3 回复

面向对象,太酷了,我们可以像这样在Go中调用Windows的DLL API,你真的需要这样做吗?

更多关于Golang中StretchDIBits在Windows 10上无法工作的问题的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


嗯,除了使用cgo,还有其他调用Windows的StretchDIBits的方法吗?我的意思是,因为cgo的效果也不理想。

这是一个已知的Golang在Windows 10上的特定问题,主要与BITMAPINFOHEADER结构的内存对齐方式有关。Windows 10对结构对齐的要求比Windows 11更严格。

问题出在BITMAPINFOHEADER结构定义上。在您的代码中,结构字段使用了Go的默认对齐方式,但在Windows 10上需要显式指定Windows的1字节对齐。

以下是修复后的代码:

package main

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

// 使用1字节对齐的BITMAPINFOHEADER结构
type BITMAPINFOHEADER struct {
    BiSize          uint32
    BiWidth         int32
    BiHeight        int32
    BiPlanes        uint16
    BiBitCount      uint16
    BiCompression   uint32
    BiSizeImage     uint32
    BiXPelsPerMeter int32
    BiYPelsPerMeter int32
    BiClrUsed       uint32
    BiClrImportant  uint32
}

// 使用1字节对齐的BITMAPINFO结构
type BITMAPINFO struct {
    BmiHeader BITMAPINFOHEADER
    BmiColors *RGBQUAD
}

type RGBQUAD struct {
    RgbBlue     byte
    RgbGreen    byte
    RgbRed      byte
    RgbReserved byte
}

func main() {
    // 加载必要的DLL
    user32 := syscall.NewLazyDLL("user32.dll")
    gdi32 := syscall.NewLazyDLL("gdi32.dll")

    getDC := user32.NewProc("GetDC")
    releaseDC := user32.NewProc("ReleaseDC")
    stretchDIBits := gdi32.NewProc("StretchDIBits")

    // 获取屏幕DC
    hwnd := uintptr(0)
    hdc, _, _ := getDC.Call(hwnd)
    defer releaseDC.Call(hwnd, hdc)

    // 创建测试图像数据
    width := 100
    height := 100
    pixelData := make([]byte, width*height*4)
    
    // 填充测试数据(红色)
    for i := 0; i < len(pixelData); i += 4 {
        pixelData[i] = 0        // B
        pixelData[i+1] = 0      // G
        pixelData[i+2] = 255    // R
        pixelData[i+3] = 0      // Reserved
    }

    // 设置BITMAPINFO结构
    var bmi BITMAPINFO
    bmi.BmiHeader.BiSize = uint32(unsafe.Sizeof(bmi.BmiHeader))
    bmi.BmiHeader.BiWidth = int32(width)
    bmi.BmiHeader.BiHeight = int32(-height) // 负值表示从上到下的DIB
    bmi.BmiHeader.BiPlanes = 1
    bmi.BmiHeader.BiBitCount = 32
    bmi.BmiHeader.BiCompression = 0 // BI_RGB
    bmi.BmiHeader.BiSizeImage = uint32(len(pixelData))

    // 调用StretchDIBits
    ret, _, err := stretchDIBits.Call(
        hdc,                    // hdc
        0,                      // xDest
        0,                      // yDest
        uintptr(width),         // wDest
        uintptr(height),        // hDest
        0,                      // xSrc
        0,                      // ySrc
        uintptr(width),         // wSrc
        uintptr(height),        // hSrc
        uintptr(unsafe.Pointer(&pixelData[0])), // lpBits
        uintptr(unsafe.Pointer(&bmi)),          // lpBitsInfo
        0,                      // iUsage (DIB_RGB_COLORS)
        0x00CC0020,             // rop (SRCCOPY)
    )

    if ret == 0 {
        fmt.Printf("StretchDIBits failed with error: %v\n", err)
    } else {
        fmt.Printf("StretchDIBits succeeded, returned: %d\n", ret)
    }
}

关键修复是确保结构体使用正确的对齐方式。如果您需要更精确的控制,可以使用//go:packed指令或使用字节数组手动构建结构:

// 替代方案:使用字节数组手动构建BITMAPINFOHEADER
func createBitmapInfoHeader(width, height int) []byte {
    header := make([]byte, 40) // BITMAPINFOHEADER大小为40字节
    
    // BiSize
    binary.LittleEndian.PutUint32(header[0:4], 40)
    // BiWidth
    binary.LittleEndian.PutUint32(header[4:8], uint32(width))
    // BiHeight
    binary.LittleEndian.PutUint32(header[8:12], uint32(-height))
    // BiPlanes
    binary.LittleEndian.PutUint16(header[12:14], 1)
    // BiBitCount
    binary.LittleEndian.PutUint16(header[14:16], 32)
    // 其余字段保持为0
    
    return header
}

这个修复确保了结构在内存中的布局与Windows 10期望的完全一致,从而解决了StretchDIBits返回0的问题。

回到顶部