You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C#调用WinAPI的SendMessage修改窗口图标失败,求故障排查方案

排查SendMessage设置Chromium弹出窗口图标失败的问题

我来帮你排查下这个窗口图标设置失败的问题,你的代码里有几个容易踩坑的点,咱们一步步来修正:

主要问题分析

  1. GDI资源泄漏与重复句柄问题
    你两次调用bitMap.GetHbitmap()会生成两个独立的GDI位图对象,既造成资源泄漏,还可能因为传递不同句柄导致系统无法正确处理。而且这些GDI对象如果不手动释放,会一直占用系统资源。

  2. Bitmap转图标格式不兼容
    WM_SETICON要求传入的是Icon句柄,而非普通Bitmap的Hbitmap。直接传递Bitmap句柄可能因为格式、尺寸不符合系统图标规范(比如没有Alpha通道、尺寸不是标准图标大小)而无法渲染。

  3. 窗口初始化时机问题
    即使判断了!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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:56:57