如何在运行时通过HWND获取第三方Windows应用窗口的图标位图?
获取Windows窗口图标位图(已知HWND)
要动态获取第三方Windows应用窗口的图标位图,核心是通过Win32 API先获取图标句柄,再将其转换为位图对象。以下是具体实现步骤和代码示例:
步骤1:获取窗口的图标句柄
窗口的图标存在三个层级优先级,依次尝试获取:
- 窗口自身设置的自定义图标(通过
WM_GETICON消息获取) - 窗口类默认的图标(通过
GetClassLongPtr获取) - 窗口所属进程可执行文件的默认图标(通过
ExtractIconEx提取)
C++ 示例代码
#include <windows.h> #include <shellapi.h> HICON GetWindowIcon(HWND hWnd, bool getLargeIcon = true) { // 1. 尝试获取窗口自定义图标 WPARAM wParam = getLargeIcon ? ICON_BIG : ICON_SMALL; HICON hIcon = (HICON)SendMessage(hWnd, WM_GETICON, wParam, 0); if (!hIcon) { // 2. 获取窗口类的默认图标 int ptrIndex = getLargeIcon ? GCLP_HICON : GCLP_HICONSM; hIcon = (HICON)GetClassLongPtr(hWnd, ptrIndex); } if (!hIcon) { // 3. 从进程可执行文件提取默认图标 TCHAR szExePath[MAX_PATH] = {0}; DWORD pid = 0; GetWindowThreadProcessId(hWnd, &pid); HANDLE hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid); if (hProcess) { GetModuleFileNameEx(hProcess, NULL, szExePath, MAX_PATH); CloseHandle(hProcess); ExtractIconEx(szExePath, 0, getLargeIcon ? &hIcon : NULL, getLargeIcon ? NULL : &hIcon, 1); } } return hIcon; }
步骤2:将图标句柄转换为位图
得到图标句柄后,将其绘制到内存设备上下文(DC)中,生成位图对象。
C++ 示例代码
HBITMAP IconToBitmap(HICON hIcon, bool isLargeIcon) { if (!hIcon) return NULL; // 获取系统图标标准尺寸 int cx = isLargeIcon ? GetSystemMetrics(SM_CXICON) : GetSystemMetrics(SM_CXSMICON); int cy = isLargeIcon ? GetSystemMetrics(SM_CYICON) : GetSystemMetrics(SM_CYSMICON); // 创建兼容DC和位图 HDC hdcScreen = GetDC(NULL); HDC hdcMem = CreateCompatibleDC(hdcScreen); HBITMAP hBitmap = CreateCompatibleBitmap(hdcScreen, cx, cy); HBITMAP hOldBitmap = (HBITMAP)SelectObject(hdcMem, hBitmap); // 将图标绘制到内存DC DrawIconEx(hdcMem, 0, 0, hIcon, cx, cy, 0, NULL, DI_NORMAL); // 清理资源 SelectObject(hdcMem, hOldBitmap); DeleteDC(hdcMem); ReleaseDC(NULL, hdcScreen); return hBitmap; }
.NET (C#) 实现方案
如果使用C#开发,可通过P/Invoke调用Win32 API,将图标转换为.NET的Bitmap对象:
C# 示例代码
using System; using System.Drawing; using System.Text; using System.Runtime.InteropServices; public static class WindowIconHelper { [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr GetClassLongPtr(IntPtr hWnd, int nIndex); [DllImport("shell32.dll", CharSet = CharSet.Auto)] private static extern uint ExtractIconEx(string lpszFile, int nIconIndex, IntPtr[] phiconLarge, IntPtr[] phiconSmall, uint nIcons); [DllImport("user32.dll")] private static extern bool DrawIconEx(IntPtr hdc, int xLeft, int yTop, IntPtr hIcon, int cxWidth, int cyHeight, int istepIfAniCur, IntPtr hbrFlickerFreeDraw, uint diFlags); [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId); [DllImport("kernel32.dll")] private static extern IntPtr OpenProcess(uint dwDesiredAccess, bool bInheritHandle, uint dwProcessId); [DllImport("kernel32.dll")] private static extern bool CloseHandle(IntPtr hObject); [DllImport("psapi.dll")] private static extern uint GetModuleFileNameEx(IntPtr hProcess, IntPtr hModule, StringBuilder lpFilename, uint nSize); [DllImport("gdi32.dll")] private static extern IntPtr CreateCompatibleDC(IntPtr hdc); [DllImport("gdi32.dll")] private static extern IntPtr CreateCompatibleBitmap(IntPtr hdc, int nWidth, int nHeight); [DllImport("gdi32.dll")] private static extern IntPtr SelectObject(IntPtr hdc, IntPtr hgdiobj); [DllImport("gdi32.dll")] private static extern bool DeleteDC(IntPtr hdc); [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); private const uint WM_GETICON = 0x007F; private const int ICON_BIG = 1; private const int ICON_SMALL = 0; private const int GCLP_HICON = -14; private const int GCLP_HICONSM = -34; private const uint DI_NORMAL = 0x0003; private const uint PROCESS_QUERY_INFORMATION = 0x0400; private const uint PROCESS_VM_READ = 0x0010; public static Bitmap GetWindowBitmap(IntPtr hWnd, bool largeIcon = true) { // 获取图标句柄 IntPtr hIcon = SendMessage(hWnd, WM_GETICON, (IntPtr)(largeIcon ? ICON_BIG : ICON_SMALL), IntPtr.Zero); if (hIcon == IntPtr.Zero) hIcon = GetClassLongPtr(hWnd, largeIcon ? GCLP_HICON : GCLP_HICONSM); if (hIcon == IntPtr.Zero) { uint pid = 0; GetWindowThreadProcessId(hWnd, out pid); IntPtr hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, false, pid); if (hProcess != IntPtr.Zero) { StringBuilder sb = new StringBuilder(260); GetModuleFileNameEx(hProcess, IntPtr.Zero, sb, (uint)sb.Capacity); CloseHandle(hProcess); IntPtr[] icons = new IntPtr[1]; ExtractIconEx(sb.ToString(), 0, largeIcon ? icons : null, largeIcon ? null : icons, 1); hIcon = icons[0]; } } if (hIcon == IntPtr.Zero) return null; // 转换为Bitmap int cx = largeIcon ? SystemInformation.IconSize.Width : SystemInformation.SmallIconSize.Width; int cy = largeIcon ? SystemInformation.IconSize.Height : SystemInformation.SmallIconSize.Height; using (Graphics g = Graphics.FromHwnd(IntPtr.Zero)) { IntPtr hdc = g.GetHdc(); IntPtr hdcMem = CreateCompatibleDC(hdc); IntPtr hBitmap = CreateCompatibleBitmap(hdc, cx, cy); IntPtr hOldBitmap = SelectObject(hdcMem, hBitmap); DrawIconEx(hdcMem, 0, 0, hIcon, cx, cy, 0, IntPtr.Zero, DI_NORMAL); SelectObject(hdcMem, hOldBitmap); DeleteDC(hdcMem); g.ReleaseHdc(hdc); Bitmap bitmap = Bitmap.FromHbitmap(hBitmap); DeleteObject(hBitmap); return bitmap; } } }
关键注意事项
- 资源释放:所有通过Win32 API获取的句柄(
HICON、HBITMAP、HDC等)必须调用对应销毁函数(DestroyIcon、DeleteObject、DeleteDC)释放,避免内存泄漏。 - 权限问题:若目标进程以高权限运行,你的应用需要拥有同等或更高权限才能成功读取进程信息。
- 动态图标处理:部分窗口使用动态绘制的图标(而非标准图标资源),此时
WM_GETICON可能返回空,可尝试用PrintWindow捕获窗口区域,但这并非专门获取图标的方法。
内容的提问来源于stack exchange,提问作者ivan.ukr
相关产品推荐
相关产品推荐

