C#调用WinAPI的SendMessage修改窗口图标失败,求故障排查方案
排查SendMessage设置Chromium弹出窗口图标失败的问题
我来帮你排查下这个窗口图标设置失败的问题,你的代码里有几个容易踩坑的点,咱们一步步来修正:
主要问题分析
GDI资源泄漏与重复句柄问题
你两次调用bitMap.GetHbitmap()会生成两个独立的GDI位图对象,既造成资源泄漏,还可能因为传递不同句柄导致系统无法正确处理。而且这些GDI对象如果不手动释放,会一直占用系统资源。Bitmap转图标格式不兼容
WM_SETICON要求传入的是Icon句柄,而非普通Bitmap的Hbitmap。直接传递Bitmap句柄可能因为格式、尺寸不符合系统图标规范(比如没有Alpha通道、尺寸不是标准图标大小)而无法渲染。窗口初始化时机问题
即使判断了!browser.IsDisposed,窗口刚创建时可能还未完成初始化,此时发送消息可能无法生效。
修正后的代码
首先确保你有正确的P/Invoke声明:
using System; using System.Diagnostics; using System.Drawing; using System.Runtime.InteropServices; using System.Windows; using System.Windows.Interop; using System.Windows.Media.Imaging; using CefSharp; [DllImport("user32.dll", CharSet = CharSet.Auto)] private static extern IntPtr SendMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam); [DllImport("user32.dll")] [return: MarshalAs(UnmanagedType.Bool)] private static extern bool IsWindow(IntPtr hWnd); [DllImport("gdi32.dll")] private static extern bool DeleteObject(IntPtr hObject); private const uint WM_SETICON = 0x0080;
然后修改你的OnAfterCreated方法:
public void OnAfterCreated(IWebBrowser chromiumWebBrowser, IBrowser browser) { if (!browser.IsDisposed && browser.IsPopup) { Application.Current.Dispatcher?.BeginInvoke(new Action(() => { IntPtr windowHandle = browser.GetHost().GetWindowHandle(); // 先验证窗口句柄是否有效 if (!IsWindow(windowHandle)) { Debug.WriteLine("窗口句柄无效"); return; } try { // 处理图标路径:如果是网络地址需先下载,本地路径确保正确 string baseUrl = chromiumWebBrowser.Address.Split('#')[0]; string iconPath = $"{baseUrl}assets/images/MyPic.png"; // 处理本地路径的情况(如果是网络URL需要先下载到本地文件) Uri iconUri = new Uri(iconPath, UriKind.RelativeOrAbsolute); if (!iconUri.IsAbsoluteUri || !iconUri.Scheme.Equals(Uri.UriSchemeFile)) { // 这里如果是网络图标,需要先下载到本地临时文件,再加载 Debug.WriteLine("请确保图标路径是本地绝对路径,网络图标需先下载"); return; } using (BitmapImage bitmapImage = new BitmapImage(iconUri)) { // 转换为System.Drawing.Bitmap using (Bitmap bmp = Utilities.BitmapImage2Bitmap(bitmapImage)) { IntPtr hBitmap = bmp.GetHbitmap(); try { // 从Bitmap创建Icon using (Icon originalIcon = Icon.FromHandle(hBitmap)) { // 创建符合系统规格的大小图标 using (Icon smallIcon = new Icon(originalIcon, SystemInformation.SmallIconSize)) using (Icon largeIcon = new Icon(originalIcon, SystemInformation.IconSize)) { // 设置小图标(wParam=0)和大图标(wParam=1) SendMessage(windowHandle, WM_SETICON, (IntPtr)0, smallIcon.Handle); SendMessage(windowHandle, WM_SETICON, (IntPtr)1, largeIcon.Handle); // 强制刷新窗口,确保图标生效 SendMessage(windowHandle, 0x000A, IntPtr.Zero, IntPtr.Zero); // WM_PAINT } } } finally { // 必须手动释放GDI资源,避免泄漏 DeleteObject(hBitmap); } } } } catch (Exception ex) { Debug.WriteLine($"设置图标失败:{ex.Message}\n{ex.StackTrace}"); } }), DispatcherPriority.Loaded); // 等到窗口加载完成后再执行 } }
额外注意事项
- 路径验证:如果你的图标是网络资源(比如
http://xxx.com/MyPic.png),需要先下载到本地临时文件,再用本地路径加载,BitmapImage无法直接加载网络URL转成System.Drawing.Bitmap。 - Chromium窗口特性:部分Chromium弹出窗口可能会在后续操作中重新设置图标,此时可以考虑监听窗口的
WM_SETICON消息,确保你的自定义图标不会被覆盖。 - 图标尺寸:系统标准小图标通常是16x16,大图标是32x32或48x48,尽量让你的图标符合这个尺寸,避免拉伸变形或不显示。
内容的提问来源于stack exchange,提问作者Tomer Trojman
相关产品推荐
相关产品推荐

