如何使用GDI创建带透明度的32位系统托盘图标?

4

我正在尝试创建一个在系统托盘中显示文本的图标。显然,它不会超过几个字符。

到目前为止,我已经尝试了:

#include <tchar.h>
#include <Windows.h>
#include <Windowsx.h>

static HICON CreateIcon(LPCTSTR txt) {
    HICON hIcon = NULL;
    HDC hDC = NULL; {
        HDC hDCScreen = GetDC(NULL);
        if (hDCScreen != NULL) {
            __try { hDC = CreateCompatibleDC(hDCScreen); }
            __finally { ReleaseDC(NULL, hDCScreen); }
        }
    }
    if (hDC != NULL) {
        __try {
            HFONT hFont = CreateFontIndirect(&ncm.lfMessageFont);
            if (hFont != NULL) {
                __try { SelectFont(hDC, hFont); }
                __finally { DeleteFont(hFont); }
            }
            int width = GetSystemMetrics(SM_CXSMICON),
                height = GetSystemMetrics(SM_CYSMICON);
            HBITMAP hBmp = CreateCompatibleBitmap(hDC, width, height);
            if (hBmp != NULL) {
                __try {
                    HBITMAP hMonoBmp =
                        CreateCompatibleBitmap(hDC, width, height);
                    if (hMonoBmp != NULL) {
                        __try {
                            RECT rect = { 0, 0, width, height };
                            HGDIOBJ prev = SelectObject(hDC, hBmp);
                            __try {
                                SetBkMode(hDC, TRANSPARENT);
                                SetTextColor(hDC, RGB(255, 255, 255));
                                ICONINFO ii = { TRUE, 0, 0, hMonoBmp, hBmp };
                                int textHeight =
                                    DrawText(hDC, txt, _tcslen(txt), &rect, 0);
                                if (textHeight != 0) {
                                    hIcon = CreateIconIndirect(&ii);
                                }
                            } __finally { SelectObject(hDC, prev); }
                        } __finally { DeleteObject(hMonoBmp); }
                    }
                } __finally { DeleteObject(hBmp); }
            }
        } __finally { DeleteDC(hDC); }
    }
    return hIcon;
}

使用以下代码:

static void _tmain(int argc, TCHAR* argv[]) {
    HICON hIcon = CreateIcon(_T("Hi"));
    if (hIcon != NULL) {
        __try {
            NOTIFYICONDATA nid = { sizeof(nid) };
            nid.hWnd = GetConsoleWindow();
            BOOL success = Shell_NotifyIcon(NIM_ADD, &nid);
            if (success) {
                nid.uFlags = NIF_ICON;
                nid.hIcon = hIcon;
                success = Shell_NotifyIcon(NIM_MODIFY, &nid);
            }
        } __finally { DestroyIcon(hIcon); }
    }
}

但是我得到的只是一张单色位图,上面用白色字体写着“Hi”,背景为黑色。(如果我稍微更改一下RGB(255, 255, 255),比如改成RGB(255, 255, 254),它就变成了黑色,因此它是单色的。)

有什么想法吗?

(*注意:我不需要MFC、ATL或任何其他库的解决方案,只需要Win32/GDI调用。)


编辑:

目前看起来是这样的:


请注意,它不叫做“系统托盘”,也从未被称为“系统托盘”。它是任务栏的通知区域。 - Cody Gray
1
@Cody:是啊,为什么用两个音节的词语,当可以用九个音节的呢?(链接:http://support.microsoft.com/kb/162613) - user541686
1
不幸的是,在英语中,音节数量和方便性并不是确定短语正确性的主要因素。该领域称为通知区域。Raymond Chen的博客文章是惯例参考。你是否曾经想过为什么相关函数会提到“通知图标”?是的,你找到了一个标题不准确的知识库文章。存在很多这样的文章;尤其是关于VB的文章,其中一些包含明显错误的信息。 - Cody Gray
1
@Cody:博客文章中倒数第三行恰好概括了我的感受。 ;) - user541686
@Cody:不用谢。顺便提一下,据我所知,托盘并不是一个可以被侮辱的人,我也不是一个物品……不过话说回来,这些天你永远无法分辨机器人和人类,所以我猜你把我当作机器人也是很自然的事情。 - user541686
1个回答

4
如果我没记错的话,一个半透明的图标(我想这就是你想要的)有一个单色位图用于它的掩码。这个掩码恰好被忽略了,但你仍然需要提供它。你没有创建一个单色位图,你似乎正在创建一个32bpp位图。我也没有看到你在主位图中初始化alpha值的任何地方,以便你不写入的区域是透明的。
这里提供了一个带有代码示例的链接:如何在Windows XP中创建Alpha混合光标或图标

@David:透明度,我指的是alpha透明度,而不是传统的粉色掩码式透明度。但我对如何编写代码感到困惑...所有这些兼容位图和DIB的混乱,它们通常是单色的,除非你使用内存DC(在这种情况下,它应该是彩色的,但似乎并不是),这让我很困惑...你知道有没有我可以参考的示例吗?此外,我没有在任何地方初始化alpha值,这是真的,但我不确定这会有什么影响,因为我遇到的问题是文本问题,而不仅仅是背景。 - user541686
@David:好的,谢谢,我会尝试设置alpha位。(如果位图是单色的话,我不确定它是否有效--希望我不会溢出某个缓冲区哈哈--但我会尝试的,谢谢。) - user541686
@David:哦,真的吗?!我不知道它被忽略了...哈哈,谢谢,我会试一下的。 - user541686
@David:非常感谢,那些信息真的很有帮助!问题也最终被发现是我在掩码中使用了CreateCompatibleBitmap;我改用了CreateBitmap,效果非常好。感谢你的信息! :) - user541686
@DavidHeffernan 这个链接已经失效了,但可以通过Internet Wayback Archive获取。 - phimuemue
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接