我正在尝试使用 HICON 获取图像,但背景不透明。我怎样才能使它透明?我需要一个 winapi 示例,因为代码位于 Dart 中,但它具有所有 Windows 调用/函数。
我尝试了在互联网上找到的不同版本,但没有成功。如果这可以帮助解决问题,我可以访问图标蒙版。
当前代码:
var icon = SendMessage(hWnd, WM_GETICON, 2, 0); // ICON_SMALL2 - User Made Apps
if (icon == 0) icon = GetClassLongPtr(hWnd, -14); // GCLP_HICON - Microsoft Win Apps
final int hScreen = GetDC(hWnd);
final int hDC = CreateCompatibleDC(hScreen);
final int hBitmap = CreateCompatibleBitmap(hScreen, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
SelectObject(hDC, hBitmap);
// SetBkMode(hDC, TRANSPARENT); - Works for text only
// PatBlt(hDC, 0, 0, 545, 850, WHITENESS); - only white/black;
DrawIconEx(hDC, 0, 0, icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), NULL, NULL, 3);
图标透明:
即使我不绘制图标,输出也是一个黑色方块。
你能建议如何删除背景吗?在基本的 winapi 调用中。如果代码不使用库中的特殊类,则可以在 cpp 中,我只能使用 dllCalls
这是完整的工作代码:
// ignore_for_file: depend_on_referenced_packages, non_constant_identifier_names, avoid_print, unrelated_type_equality_checks
import 'dart:ffi';
import 'dart:io';
import 'package:win32/win32.dart';
import 'package:ffi/ffi.dart';
int enumWindowsProc(int hWnd, int lparam) {
if (IsWindowVisible(hWnd) == FALSE) return TRUE;
final length = GetWindowTextLength(hWnd);
if (length == 0) return TRUE;
var icon = SendMessage(hWnd, WM_GETICON, 2, 0); // ICON_SMALL2 - User Made Apps
if (icon == 0) icon = GetClassLongPtr(hWnd, -14); // GCLP_HICON - Microsoft Win Apps
if (icon == 0) {
icon = 0;
return 1;
}
final int hScreen = GetDC(hWnd);
final int hDC = CreateCompatibleDC(hScreen);
final int hBitmap = CreateCompatibleBitmap(hScreen, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON));
SelectObject(hDC, hBitmap);
SetBkMode(hDC, TRANSPARENT); //- Works for text only
PatBlt(hDC, 0, 0, GetSystemMetrics(SM_CXICON) ~/ 2, GetSystemMetrics(SM_CYICON), WHITENESS); // test, half white half black.
DrawIconEx(hDC, 0, 0, icon, GetSystemMetrics(SM_CXICON), GetSystemMetrics(SM_CYICON), NULL, NULL, 3);
//Turn to bytes
final bmpScreen = calloc<BITMAP>();
GetObject(hBitmap, sizeOf<BITMAP>(), bmpScreen);
final bitmapFileHeader = calloc<BITMAPFILEHEADER>();
final bitmapInfoHeader = calloc<BITMAPINFOHEADER>()
..ref.biSize = sizeOf<BITMAPINFOHEADER>()
..ref.biWidth = bmpScreen.ref.bmWidth
..ref.biHeight = bmpScreen.ref.bmHeight
..ref.biPlanes = 1
..ref.biBitCount = 32
..ref.biCompression = BI_RGB;
final dwBmpSize = ((bmpScreen.ref.bmWidth * bitmapInfoHeader.ref.biBitCount + 31) / 32 * 4 * bmpScreen.ref.bmHeight).toInt();
final lpBitmap = calloc<Uint8>(dwBmpSize);
GetDIBits(hDC, hBitmap, 0, bmpScreen.ref.bmHeight, lpBitmap, bitmapInfoHeader.cast(), DIB_RGB_COLORS);
final dwSizeOfDIB = dwBmpSize + sizeOf<BITMAPFILEHEADER>() + sizeOf<BITMAPINFOHEADER>();
bitmapFileHeader.ref.bfOffBits = sizeOf<BITMAPFILEHEADER>() + sizeOf<BITMAPINFOHEADER>();
bitmapFileHeader.ref.bfSize = dwSizeOfDIB;
bitmapFileHeader.ref.bfType = 0x4D42; // BM
var b = BytesBuilder();
b.add(Pointer<Uint8>.fromAddress(bitmapFileHeader.address).asTypedList(sizeOf<BITMAPFILEHEADER>()));
b.add(Pointer<Uint8>.fromAddress(bitmapInfoHeader.address).asTypedList(sizeOf<BITMAPINFOHEADER>()));
b.add(lpBitmap.asTypedList(dwBmpSize));
// I need the Bitmap in Bytes, I save it to file just for debugging.
//capture?.icon = b.takeBytes();
//
DeleteDC(hDC);
DeleteObject(hBitmap);
free(bmpScreen);
free(bitmapFileHeader);
free(bitmapInfoHeader);
free(lpBitmap);
Directory current = Directory.current;
File("${current.path}/imgs/i_${icon.toString()}.bmp").writeAsBytes(b.takeBytes());
return 1;
}
void main() {
final imgs = "${Directory.current.path}/imgs";
if (Directory(imgs).exists() == true) {
Directory(imgs).deleteSync(recursive: true);
}
final wndProc = Pointer.fromFunction<EnumWindowsProc>(enumWindowsProc, 0);
EnumWindows(wndProc, 0);
}
final _user32 = DynamicLibrary.open('user32.dll');
int DrawIconEx(int hdc, int xLeft, int yTop, int hIcon, int cxWidth, int cyWidth, int istepIfAniCur, int hbrFlickerFreeDraw, int diFlags) =>
_DrawIconEx(hdc, xLeft, yTop, hIcon, cxWidth, cyWidth, istepIfAniCur, hbrFlickerFreeDraw, diFlags);
final _DrawIconEx = _user32.lookupFunction<
Int32 Function(IntPtr hdc, Int32 xLeft, Int32 yTop, IntPtr hIcon, Int32 cxWidth, Int32 cyWidth, Uint32 istepIfAniCur, IntPtr hbrFlickerFreeDraw, Uint32 diFlags),
int Function(int hdc, int xLeft, int yTop, int hIcon, int cxWidth, int cyWidth, int istepIfAniCur, int hbrFlickerFreeDraw, int diFlags)>('DrawIconEx');
final _gdi32 = DynamicLibrary.open('gdi32.dll');
int PatBlt(int hdc, int x, int y, int w, int h, int rop) => _PatBlt(hdc, x, y, w, h, rop);
final _PatBlt =
_gdi32.lookupFunction<Int32 Function(IntPtr hdc, Int32 x, Int32 y, Int32 w, Int32 h, Uint32 rop), int Function(int hdc, int x, int y, int w, int h, int rop)>('PatBlt');
找到了一个更好的解决方案,可以使用缓冲区并写入文件、Alpha 通道和所有内容:) https://github.com/pelayomendez/exe-icon-extractor/blob/master/src/module.cc
#include <tchar.h>
#include <iostream>
#include <windows.h>
#include <fstream>
#include <cassert>
using namespace std;
// Check windows
#if _WIN32 || _WIN64
#if _WIN64
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
// Check GCC
#if __GNUC__
#if __x86_64__ || __ppc64__
#define ENV64BIT
#else
#define ENV32BIT
#endif
#endif
typedef struct
{
WORD idReserved; // must be 0
WORD idType; // 1 = ICON, 2 = CURSOR
WORD idCount; // number of images (and ICONDIRs)
// ICONDIR [1...n]
// ICONIMAGE [1...n]
} ICONHEADER;
//
// An array of ICONDIRs immediately follow the ICONHEADER
//
typedef struct
{
BYTE bWidth;
BYTE bHeight;
BYTE bColorCount;
BYTE bReserved;
WORD wPlanes; // for cursors, this field = wXHotSpot
WORD wBitCount; // for cursors, this field = wYHotSpot
DWORD dwBytesInRes;
DWORD dwImageOffset; // file-offset to the start of ICONIMAGE
} ICONDIR;
//
// After the ICONDIRs follow the ICONIMAGE structures -
// consisting of a BITMAPINFOHEADER, (optional) RGBQUAD array, then
// the color and mask bitmap bits (all packed together
//
typedef struct
{
BITMAPINFOHEADER biHeader; // header for color bitmap (no mask header)
//RGBQUAD rgbColors[1...n];
//BYTE bXOR[1]; // DIB bits for color bitmap
//BYTE bAND[1]; // DIB bits for mask bitmap
} ICONIMAGE;
//
// Return the number of BYTES the bitmap will take ON DISK
//
static UINT NumBitmapBytes(BITMAP* pBitmap)
{
int nWidthBytes = pBitmap->bmWidthBytes;
// bitmap scanlines MUST be a multiple of 4 bytes when stored
// inside a bitmap resource, so round up if necessary
if (nWidthBytes & 3)
nWidthBytes = (nWidthBytes + 4) & ~3;
return nWidthBytes * pBitmap->bmHeight;
}
static BOOL GetIconBitmapInfo(HICON hIcon, ICONINFO* pIconInfo, BITMAP* pbmpColor, BITMAP* pbmpMask)
{
if (!GetIconInfo(hIcon, pIconInfo))
return FALSE;
if (!GetObject(pIconInfo->hbmColor, sizeof(BITMAP), pbmpColor))
return FALSE;
if (!GetObject(pIconInfo->hbmMask, sizeof(BITMAP), pbmpMask))
return FALSE;
return TRUE;
}
//
// Write one icon directory entry - specify the index of the image
//
static void WriteIconDirectoryEntry(BYTE* buffer, int* pBufferOffset, int nIdx, HICON hIcon, UINT nImageOffset)
{
ICONINFO iconInfo;
ICONDIR iconDir;
BITMAP bmpColor;
BITMAP bmpMask;
UINT nColorCount;
UINT nImageBytes;
GetIconBitmapInfo(hIcon, &iconInfo, &bmpColor, &bmpMask);
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
if (bmpColor.bmBitsPixel >= 8)
nColorCount = 0;
else
nColorCount = 1 << (bmpColor.bmBitsPixel * bmpColor.bmPlanes);
// Create the ICONDIR structure
iconDir.bWidth = (BYTE)bmpColor.bmWidth;
iconDir.bHeight = (BYTE)bmpColor.bmHeight;
iconDir.bColorCount = nColorCount;
iconDir.bReserved = 0;
iconDir.wPlanes = bmpColor.bmPlanes;
iconDir.wBitCount = bmpColor.bmBitsPixel;
iconDir.dwBytesInRes = sizeof(BITMAPINFOHEADER) + nImageBytes;
iconDir.dwImageOffset = nImageOffset;
// Write to disk
memcpy(&buffer[*pBufferOffset], &iconDir, sizeof(iconDir));
(*pBufferOffset) += sizeof(iconDir);
// Free resources
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
}
static UINT WriteIconData(BYTE* buffer, int* pBufferOffset, HBITMAP hBitmap)
{
BITMAP bmp;
int i;
BYTE* pIconData;
UINT nBitmapBytes;
GetObject(hBitmap, sizeof(BITMAP), &bmp);
nBitmapBytes = NumBitmapBytes(&bmp);
pIconData = (BYTE*)malloc(nBitmapBytes);
GetBitmapBits(hBitmap, nBitmapBytes, pIconData);
// bitmaps are stored inverted (vertically) when on disk..
// so write out each line in turn, starting at the bottom + working
// towards the top of the bitmap. Also, the bitmaps are stored in packed
// in memory - scanlines are NOT 32bit aligned, just 1-after-the-other
for (i = bmp.bmHeight - 1; i >= 0; i--)
{
memcpy(&buffer[*pBufferOffset], pIconData + (i * bmp.bmWidthBytes), bmp.bmWidthBytes);
(*pBufferOffset) += bmp.bmWidthBytes;
// extend to a 32bit boundary (in the file) if necessary
if (bmp.bmWidthBytes & 3)
{
DWORD padding = 0;
memcpy(&buffer[*pBufferOffset], &padding, 4 - bmp.bmWidthBytes);
(*pBufferOffset) += 4 - bmp.bmWidthBytes;
}
}
free(pIconData);
return nBitmapBytes;
}
//
// Create a .ICO file, using the specified array of HICON images
//
BOOL SaveIcon3(HICON hIcon[], int nNumIcons, BYTE* buffer, int* pWritten)
{
int i;
int* pImageOffset = (int*)malloc(nNumIcons * sizeof(int));
int bufferOffset = 0;
if (hIcon == 0 || nNumIcons < 1)
return 0;
//
// Write the iconheader first of all
//
ICONHEADER iconheader;
// Setup the icon header
iconheader.idReserved = 0; // Must be 0
iconheader.idType = 1; // Type 1 = ICON (type 2 = CURSOR)
iconheader.idCount = nNumIcons; // number of ICONDIRs
// Write the header to disk
memcpy(&(buffer[bufferOffset]), &iconheader, sizeof(iconheader));
bufferOffset += sizeof(iconheader);
//
// Leave space for the IconDir entries
//
bufferOffset += sizeof(ICONDIR) * nNumIcons;
//
// Now write the actual icon images!
//
for (i = 0; i < nNumIcons; i++) {
ICONINFO iconInfo;
BITMAP bmpColor, bmpMask;
// GetIconBitmapInfo
GetIconBitmapInfo(hIcon[i], &iconInfo, &bmpColor, &bmpMask);
// record the file-offset of the icon image for when we write the icon directories
pImageOffset[i] = bufferOffset;
// WriteIconImageHeader
BITMAPINFOHEADER biHeader;
UINT nImageBytes;
// calculate how much space the COLOR and MASK bitmaps take
nImageBytes = NumBitmapBytes(&bmpColor) + NumBitmapBytes(&bmpMask);
// write the ICONIMAGE to disk (first the BITMAPINFOHEADER)
ZeroMemory(&biHeader, sizeof(biHeader));
// Fill in only those fields that are necessary
biHeader.biSize = sizeof(biHeader);
biHeader.biWidth = bmpColor.bmWidth;
biHeader.biHeight = bmpColor.bmHeight * 2; // height of color+mono
biHeader.biPlanes = bmpColor.bmPlanes;
biHeader.biBitCount = bmpColor.bmBitsPixel;
biHeader.biSizeImage = nImageBytes;
// write the BITMAPINFOHEADER
memcpy(&(buffer[bufferOffset]), &biHeader, sizeof(biHeader));
bufferOffset += sizeof(biHeader);
// color and mask bitmaps
WriteIconData(buffer, &bufferOffset, iconInfo.hbmColor);
WriteIconData(buffer, &bufferOffset, iconInfo.hbmMask);
DeleteObject(iconInfo.hbmColor);
DeleteObject(iconInfo.hbmMask);
}
*pWritten = bufferOffset;
//
// Lastly, skip back and write the icon directories.
//
bufferOffset = sizeof(ICONHEADER);
for (i = 0; i < nNumIcons; i++)
{
WriteIconDirectoryEntry(buffer, &bufferOffset, i, hIcon[i], pImageOffset[i]);
}
free(pImageOffset);
return 1;
}
void main()
{
HICON hIconLarge;
HICON hIconSmall;
int extractIcon = ExtractIconExW(L"E:\\Program Files\\Microsoft VS Code Insiders\\Code - Insiders.exe", 0, &hIconLarge, &hIconSmall, 1);
if (extractIcon <= 0) {
std::cout << "No icon";
return;
}
BYTE buffer[(256 * 256) * 4]; // (256x256) Max Windows Icon Size x 4 bytes (32 bits)
int written;
SaveIcon3(&hIconLarge, 1, buffer, &written);
std::ofstream file;
file.open("E:/t.ico", std::ios_base::binary);
assert(file.is_open());
for (int i = 0; i < sizeof(buffer) / sizeof(buffer[0]); ++i)
file.write((char*)(buffer + i * sizeof(buffer[0])), sizeof(buffer[0]));
file.close();
}
找到解决方案。
static BITMAP_AND_BYTES createAlphaChannelBitmapFromIcon(HICON hIcon) {
// Get the icon info
ICONINFO iconInfo = {0};
GetIconInfo(hIcon, &iconInfo);
// Get the screen DC
HDC dc = GetDC(NULL);
// Get icon size info
BITMAP bm = {0};
GetObject( iconInfo.hbmColor, sizeof( BITMAP ), &bm );
// Set up BITMAPINFO
BITMAPINFO bmi = {0};
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
bmi.bmiHeader.biWidth = bm.bmWidth;
bmi.bmiHeader.biHeight = -bm.bmHeight;
bmi.bmiHeader.biPlanes = 1;
bmi.bmiHeader.biBitCount = 32;
bmi.bmiHeader.biCompression = BI_RGB;
// Extract the color bitmap
int nBits = bm.bmWidth * bm.bmHeight;
int32_t* colorBits = new int32_t[nBits];
GetDIBits(dc, iconInfo.hbmColor, 0, bm.bmHeight, colorBits, &bmi, DIB_RGB_COLORS);
// Check whether the color bitmap has an alpha channel.
// (On my Windows 7, all file icons I tried have an alpha channel.)
BOOL hasAlpha = FALSE;
for (int i = 0; i < nBits; i++) {
if ((colorBits[i] & 0xff000000) != 0) {
hasAlpha = TRUE;
break;
}
}
// If no alpha values available, apply the mask bitmap
if (!hasAlpha) {
// Extract the mask bitmap
int32_t* maskBits = new int32_t[nBits];
GetDIBits(dc, iconInfo.hbmMask, 0, bm.bmHeight, maskBits, &bmi, DIB_RGB_COLORS);
// Copy the mask alphas into the color bits
for (int i = 0; i < nBits; i++) {
if (maskBits[i] == 0) {
colorBits[i] |= 0xff000000;
}
}
delete[] maskBits;
}
// Release DC and GDI bitmaps
ReleaseDC(NULL, dc);
::DeleteObject(iconInfo.hbmColor);
::DeleteObject(iconInfo.hbmMask);
// Create GDI+ Bitmap
Gdiplus::Bitmap* bmp = new Gdiplus::Bitmap(bm.bmWidth, bm.bmHeight, bm.bmWidth*4, PixelFormat32bppARGB, (BYTE*)colorBits);
BITMAP_AND_BYTES ret = {bmp, colorBits};
return ret;
}
原帖:https://stackoverflow.com/a/22885412/1456151
还有 PowerToys 源代码:
HBITMAP CreateBitmapFromIcon(_In_ HICON hIcon, _In_opt_ UINT width, _In_opt_ UINT height)
{
HBITMAP hBitmapResult = NULL;
// Create compatible DC
HDC hDC = CreateCompatibleDC(NULL);
if (hDC != NULL)
{
// Get bitmap rectangle size
RECT rc = { 0 };
rc.left = 0;
rc.right = (width != 0) ? width : GetSystemMetrics(SM_CXSMICON);
rc.top = 0;
rc.bottom = (height != 0) ? height : GetSystemMetrics(SM_CYSMICON);
// Create bitmap compatible with DC
BITMAPINFO BitmapInfo;
ZeroMemory(&BitmapInfo, sizeof(BITMAPINFO));
BitmapInfo.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
BitmapInfo.bmiHeader.biWidth = rc.right;
BitmapInfo.bmiHeader.biHeight = rc.bottom;
BitmapInfo.bmiHeader.biPlanes = 1;
BitmapInfo.bmiHeader.biBitCount = 32;
BitmapInfo.bmiHeader.biCompression = BI_RGB;
HDC hDCBitmap = GetDC(NULL);
HBITMAP hBitmap = CreateDIBSection(hDCBitmap, &BitmapInfo, DIB_RGB_COLORS, NULL, NULL, 0);
ReleaseDC(NULL, hDCBitmap);
if (hBitmap != NULL)
{
// Select bitmap into DC
HBITMAP hBitmapOld = (HBITMAP)SelectObject(hDC, hBitmap);
if (hBitmapOld != NULL)
{
// Draw icon into DC
if (DrawIconEx(hDC, 0, 0, hIcon, rc.right, rc.bottom, 0, NULL, DI_NORMAL))
{
// Restore original bitmap in DC
hBitmapResult = (HBITMAP)SelectObject(hDC, hBitmapOld);
hBitmapOld = NULL;
hBitmap = NULL;
}
if (hBitmapOld != NULL)
{
SelectObject(hDC, hBitmapOld);
}
}
if (hBitmap != NULL)
{
DeleteObject(hBitmap);
}
}
DeleteDC(hDC);
}
return hBitmapResult;
}
这是我对相同/类似问题的解决方案,但我无法让上述解决方案为我工作。除了OP中带有黑色背景图标的完整代码之外。因此,经过一些测试和故障排除后,这就是我为我所做的工作。
(因为我无法使用 GetClassLongPtr 和 SendMessage 获取某些图标,所以我使用了 SHGetFileInfo。尽管如果您使用 SHGetFileInfo,您可能需要拥有文件的完整路径。)
import 'dart:ffi';
import "package:image/image.dart" as img;
import "package:ffi/ffi.dart";
import 'package:win32/win32.dart';
img.Image iconToImage(int hwnd) {
final fileInfo = calloc<SHFILEINFO>();
const SHGFI_LARGEICON = 0x000000000;
const SHGFI_ICON = 0x000000100;
SHGetFileInfo(filePath.toNativeUtf16(), 0, fileInfo, sizeOf<SHFILEINFO>(),
SHGFI_ICON | SHGFI_LARGEICON);
final hIcon = fileInfo.ref.hIcon;
final iconInfo = calloc<ICONINFO>();
GetIconInfo(hIcon, iconInfo);
final hdc = GetDC(0);
final hBitmap = iconInfo.ref.hbmColor;
final bitmap = calloc<BITMAP>();
GetObject(hBitmap, sizeOf<BITMAP>(), bitmap);
final bitmapInfo = calloc<BITMAPINFOHEADER>()
..ref.biSize = sizeOf<BITMAPINFOHEADER>()
..ref.biWidth = bitmap.ref.bmWidth
..ref.biHeight = -bitmap.ref.bmHeight
..ref.biPlanes = 1
..ref.biBitCount = 32
..ref.biCompression = BI_RGB;
int bufferSize = bitmap.ref.bmWidth * bitmap.ref.bmHeight * 4;
final buffer = calloc<Uint8>(bufferSize);
GetDIBits(hdc, hBitmap, 0, bitmapInfo.ref.biHeight, buffer, bitmapInfo.cast(), DIB_RGB_COLORS);
img.Image imageIcon = img.Image.fromBytes(
width: 32,
height: 32,
order: img.Channel.bgra
numChannels: 4,
bytes: buffer.asTypedList(bufferSize).buffer
);
free(fileInfo);
DestroyIcon(hIcon);
DeleteObject(iconInfo.ref.hbmColor);
DeleteObject(iconInfo.ref.hbmMask);
free(iconInfo);
free(bitmapInfo);
free(bitmap);
free(buffer);
return imageIcon;
}
您稍后可以使用它来转换为图像小部件或写入文件。
var iconImage = IconToImage(hwnd);
var image = Image.memory(Uint8List.fromList(img.encodePng(iconImage)));
// or write to file as png
var iconImage = IconToImage(hwnd);
File("path/iconName.png").writeAsBytesSync(img.encodePng(iconImage));
此解决方案遇到的一个问题是我无法弄清楚如何获取正确的 Windows 应用程序图标,例如计算器应用程序等。如果我弄清楚了就会编辑。