4

I'm trying to create an icon that displays a piece of text in the system tray. (Obviously, it won't be longer than a couple of characters.)

So far I've tried:

#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;
}

with this code:

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); }
    }
}

but all I get is a monochrome bitmap that says Hi in white text on a black background. (If I change the RGB(255, 255, 255) even slightly, say to RGB(255, 255, 254), it becomes black, so it's monochrome.)

Any ideas?

(*Note: I'm not looking for MFC, ATL, or any other library solutions, just Win32/GDI calls.)


Edit:

Here's what it looks like currently:

user541686
  • 205,094
  • 128
  • 528
  • 886
  • Note that it's not called the "system tray", and it has never been called the "system tray". It's the taskbar **notification area**. – Cody Gray - on strike May 08 '11 at 11:49
  • 1
    @Cody: Yeah, [why use two syllables](http://support.microsoft.com/kb/162613) when nine will do? – user541686 May 08 '11 at 18:47
  • 1
    Unfortunately, in English, the number of syllables and convenience are not the primary factors in determining the correctness of a phrase. That area is called the notification area. [Raymond Chen's blog article](http://blogs.msdn.com/b/oldnewthing/archive/2003/09/10/54831.aspx) is the customary reference. Have you ever wondered why the relevant functions refer to "notify icons"? Yeah, you found a knowledge base article with an inaccurate title. A lot of those exist; there are some with simply incorrect information, especially when it comes to articles about VB. – Cody Gray - on strike May 09 '11 at 07:03
  • 1
    @Cody: The third-to-last line in the blog post sums up my feelings exactly. ;) – user541686 May 09 '11 at 07:07
  • @Cody: You're welcome. FYI, though, AFAIK the tray is not a person who can be insulted, nor am I a thing... then again, you can never tell robots from humans these days, so I guess it would be natural for you to confuse me for one. – user541686 May 09 '11 at 07:12

1 Answers1

4

If I recall correctly, a partially transparent icon (which I think is what want) has a monochrome bitmap for its mask. This mask happens to be ignored but you still have to supply it. You aren't creating a monochrome bitmap, you appear to be creating a 32bpp bitmap. I also don't see anywhere where you initialise the alpha values for you main bitmap so that the areas which you don't write to are transparent.

An example with code is provided here: How To Create an Alpha Blended Cursor or Icon in Windows XP

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • @David: By transparency yeah, I meant alpha transparency, not the traditional pink-mask-style transparency. I'm confused on how to actually code it though... all this mess with compatible bitmaps and DIBs how they're normally monochrome unless you use a memory DC (in which case it's supposed to be color, except that it doesn't seem to be) just really confuses me... do you happen to know of any examples I could follow? Also, I didn't initialize alpha values anywhere, that's true, but I'm not sure how that makes a difference because I'm having a problem with the text, not just the background. – user541686 May 08 '11 at 09:16
  • @Mehrdad I'm tied up right now so perhaps someone else can give you more detailed help, but I suspect I've highlighted at least two problems with your current code. – David Heffernan May 08 '11 at 09:19
  • @David: Okay thanks, I'll try setting the alpha bits. (I'm not sure if it'll work if the bitmap is monochrome -- hope I don't overrun some buffer lol -- but I'll try, thanks.) – user541686 May 08 '11 at 09:20
  • Only the main bitmap has alpha values. The mask is ignored but you still have to supply it. What's important is that it has 1 bit of colour per pixel, i.e. monochrome. Its contents are irrelevant. – David Heffernan May 08 '11 at 09:22
  • @David: Oh what, really?! I had no idea it's ignored... haha thanks, I'll give that a try. – user541686 May 08 '11 at 09:22
  • @David: Thanks a lot, that information really helped! The problem also turned out to be the fact that I was using `CreateCompatibleBitmap` for the mask; I used `CreateBitmap` and it worked pretty well. Thanks for the info! :) – user541686 May 08 '11 at 09:31
  • @David: Okay, so apparently it still doesn't work: When I set the bitmap's bits to `0x00000000`, all I get is a black background with my text over it (whose color I now managed to control). If I set it to `0x01000000`, it suddenly jumps to almost-complete transparency (which is what I want for the background), but the text no longer appears. If I set it to `0xFF000000` then the background becomes black again, but now the text becomes white. What's going on? D: – user541686 May 08 '11 at 20:00
  • @Mehrdad Are you sure you have created a 32bpp bitmap? Is it perhaps a 24bpp bitmap? – David Heffernan May 08 '11 at 20:07
  • @David: It *should* be... I tried it both with `CreateBitmap` as well as like in the `CreateDIBSection` example. There's obviously some kind of transparency going on here, the problem being that it also affects the font, not just the background. :\ – user541686 May 08 '11 at 20:23
  • @DavidHeffernan The link is broken, but available via internet wayback archive. – phimuemue Sep 25 '17 at 11:11