如何在Kotlin/Native中使用GetIconInfo返回的HBITMAP获取图像字节?
使用Kotlin/Native从HBITMAP提取图像字节
你的方向是对的,GetDIBits确实是Windows API中提取位图像素数据的标准方式,但之前的代码存在几个关键问题,导致无法正确获取图像字节。以下是修正后的完整实现步骤及代码:
关键修正点说明
- 必须先通过
GetObject获取BITMAP结构,拿到位图的宽、高、位深等核心信息 BITMAPINFO需要根据位图信息正确初始化,尤其是BITMAPINFOHEADER部分- 要为图像字节分配足够的内存空间,不能仅声明指针变量
- 调用
GetDIBits时需传入正确的参数(如实际位图高度,而非UInt.MAX_VALUE) - 必须及时释放Windows资源(DC、位图句柄等),避免内存泄漏
完整实现代码
memScoped { CoInitialize(null, COINIT_APARTMENTTHREADED) defer { CoUninitialize() } val fileInfo = alloc<SHFILEINFOA>() // 组合所需的SHGFI_*常量,保留你原有的标志 val flags = SHGFI_ICON or SHGFI_DISPLAYNAME or SHGFI_TYPENAME or SHGFI_ICONLOCATION or SHGFI_SMALLICON SHGetFileInfo("C:\\path\\to.file", 0, fileInfo.ptr, sizeOf<SHFILEINFOA>().toUInt(), flags) println(fileInfo.szDisplayName.toKString()) println(fileInfo.szTypeName.toKString()) val iconInfo = alloc<ICONINFO>() GetIconInfo(fileInfo.hIcon, iconInfo.ptr) defer { DeleteObject(iconInfo.hbmColor) } defer { DeleteObject(iconInfo.hbmMask) } defer { DestroyIcon(fileInfo.hIcon) } // 第一步:获取BITMAP结构,了解位图基本信息 val bitmap = alloc<BITMAP>() GetObject(iconInfo.hbmColor, sizeOf<BITMAP>().toUInt(), bitmap.ptr) // 第二步:初始化BITMAPINFO和BITMAPINFOHEADER val bmi = alloc<BITMAPINFO>() val bmiHeader = bmi.bmiHeader bmiHeader.biSize = sizeOf<BITMAPINFOHEADER>().toUInt() bmiHeader.biWidth = bitmap.bmWidth bmiHeader.biHeight = bitmap.bmHeight bmiHeader.biPlanes = 1 bmiHeader.biBitCount = bitmap.bmBitsPixel.toUInt() bmiHeader.biCompression = BI_RGB // 不压缩格式 bmiHeader.biSizeImage = 0 // 让系统自动计算大小 // 第三步:计算所需内存并分配(按Windows位图4字节对齐要求) val bytesPerLine = ((bitmap.bmWidth * bitmap.bmBitsPixel + 31) / 32) * 4 val totalBytes = bytesPerLine * bitmap.bmHeight val pixelBytes = allocArray<Byte>(totalBytes) // 第四步:调用GetDIBits提取像素数据 val hdc = GetDC(null) defer { ReleaseDC(null, hdc) } val bitsCopied = GetDIBits( hdc, iconInfo.hbmColor, 0, bitmap.bmHeight.toUInt(), pixelBytes, bmi.ptr, DIB_RGB_COLORS ) if (bitsCopied == 0u) { println("GetDIBits调用失败") return@memScoped } // 现在pixelBytes就是图像的像素字节数组,可进行后续处理 println("成功获取${totalBytes}字节的图像数据") // 示例:打印前10个字节 repeat(minOf(10, totalBytes)) { println("字节${it}: ${pixelBytes[it]}") } }
重要注意事项
- 所有Windows API句柄(如
HICON、HBITMAP、HDC)必须在使用完毕后释放,这里用defer确保memScoped块结束时自动释放 - 位图扫描线是倒序存储(从底部到顶部),若需正序图像需自行反转每行数据
- 32位ARGB位图的像素格式为BGRA(而非RGBA),注意颜色通道顺序
CoInitialize和CoUninitialize必须配对调用,确保COM环境正确初始化
内容的提问来源于stack exchange,提问作者user8681
相关产品推荐
相关产品推荐

