• Advertisement
Sign in to follow this  

Minimizing to tray

This topic is 4243 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

I was wondering if anybody had any tutorials on how to make a Win32 application and have it minimize to the system tray. All the examples I've found require MFC, so they don't work. So, if somebody knew where to get that, that would work just as well. But if not, I need an alternative, preferably with the Win32 API. I've tried MSDN, but I'm new to Win32 stuff, so it doesn't make much sense to me.

Share this post


Link to post
Share on other sites
Advertisement
Check this article on codeproject : http://www.codeproject.com/wtl/wtltrayicon.asp.
it is for WTL though.

Share this post


Link to post
Share on other sites
Quote:
Original post by meeshoo
Check this article on codeproject : http://www.codeproject.com/wtl/wtltrayicon.asp.
it is for WTL though.

Well, I'm trying to get WTL working now, so I'll see how it goes. Thanks.

Share this post


Link to post
Share on other sites
What happens is that you use Shell_NotifyIcon() to show/hide the tray icon, and then you use ShowWindow() to show/hide the main window in response to the minimize/maximize messages.

Share this post


Link to post
Share on other sites
no problem. I've used that article in one of my applications and it works, just need some tweeking to get it to your specific needs.

Share this post


Link to post
Share on other sites
Aw heck - I'll just give you my tray class - it's a little dirty, but works.

Header:


#ifndef SystemTrayH
#define SystemTrayH

#define WM_SYSTRAY (WM_APP + 100)

#include <Windows.h>
#include <ShellAPI.h>
#include <vector>

class TTrayIcon
{
private:
HWND parent;
std::vector<HICON>icons;
std::vector<char*>tips;
std::vector<UINT>state_ids;
int state;
UINT id;
UINT callback;

char Tip[128];

//if this flag is set to true (by default), certain
//messages are handled and not passed on to the main
//message loop of the owner window to be handled
//there manually. These include:
//
// WM_DRAWITEM
//
bool InterceptMessages;

NOTIFYICONDATA nid;

UINT GetState(UINT i);
void CreateNID(UINT state);

public:
TTrayIcon();
virtual ~TTrayIcon();

TTrayIcon(HWND trayParent, UINT trayId, bool intercept = true);

void AddState(HICON trayIcon, char * trayTip, const int trayState);
void Draw(LPARAM lParam);
void SetState(UINT i);
void Enable();
void Disable();
void SetCallbackMessage(UINT msg);
void SetInterceptMessages(bool intercept);
bool GetInterceptMessages();
void SetTip(char * Tip);
};

#endif



Source:


#include "SystemTray.h"

UINT TTrayIcon::GetState(UINT i)
{
for(unsigned j = 0; j < state_ids.size(); j++)
if(state_ids[j] == i)
return j;
return NULL;
}

void TTrayIcon::CreateNID(UINT state)
{
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = parent;
nid.uID = id;
nid.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
nid.uCallbackMessage = callback;
nid.hIcon = icons[state];

if(!strlen(Tip) && tips[state])
lstrcpyn(nid.szTip, tips[state], sizeof(nid.szTip));
else if(strlen(Tip))
lstrcpyn(nid.szTip, Tip, sizeof(nid.szTip));
else
nid.szTip[0] = '\0';
}

TTrayIcon::TTrayIcon() {};
TTrayIcon::~TTrayIcon() {};

TTrayIcon::TTrayIcon(HWND trayParent, UINT trayId, bool intercept)
{
parent = trayParent;
state = 0;
id = trayId;
callback = WM_SYSTRAY;
InterceptMessages = intercept;
Tip[0] ='\0';
}

void TTrayIcon::AddState(HICON trayIcon, char * trayTip, const int trayState)
{
if(trayState < 0)
return;

icons.push_back(trayIcon);
tips.push_back(trayTip);
state_ids.push_back(trayState);
}

void TTrayIcon::SetTip(char * pTip)
{
strcpy(Tip, pTip);

CreateNID(state);

Shell_NotifyIcon(NIM_MODIFY, &nid);
}


void TTrayIcon::Draw(LPARAM lParam)
{
DrawIconEx(LPDRAWITEMSTRUCT(lParam)->hDC,
LPDRAWITEMSTRUCT(lParam)->rcItem.left,
LPDRAWITEMSTRUCT(lParam)->rcItem.top,
icons[state], 16, 16, 0, NULL, DI_NORMAL);
}

void TTrayIcon::SetState(UINT i)
{
state = GetState(i);
CreateNID(state);
Shell_NotifyIcon(NIM_MODIFY, &nid);
}

void TTrayIcon::Enable()
{
SetState(state_ids[state]);
Shell_NotifyIcon(NIM_ADD, &nid);
}

void TTrayIcon::Disable()
{
Shell_NotifyIcon(NIM_DELETE, &nid);
}

void TTrayIcon::SetCallbackMessage(UINT msg)
{
callback = msg;
}

void TTrayIcon::SetInterceptMessages(bool intercept)
{
InterceptMessages = intercept;
}

bool TTrayIcon::GetInterceptMessages()
{
return InterceptMessages;
}





Sample usage:



//the id (12) is really not used internally, so you can essentially ignore it
tray = new TTrayIcon(handle_of_owner, 12);

//add an icon and text for state 0;
//you can use any icon, either loaded from disk or resource;
//in this example a default windows icon is loaded
tray->AddState(LoadIcon(NULL, IDI_EXCLAMATION), "idle...", 0);
//ditto for state 1
tray->AddState(LoadIcon(NULL, IDI_ERROR), "working...", 1);
//tray->Enable();
//set state 0
tray->SetState(0);


//in owner window's message loop

if(msg == WM_SYSTRAY)
{
if(lParam == WM_LBUTTONDOWN)
{
tray->Disable();
}
else if(lParam == WM_MOUSEMOVE)
{
expressUtterSorrow(true);
}
else
tray->SetTip("Click to disable");
}




This doesn't compass all of the functionality of the tray icon, but it will give you easy access to it.

Hope you find it useful.

Share this post


Link to post
Share on other sites
After reading MSDN, I've got it to display a custom icon all the time and it removes it on the program's exit, so I have everything working good there. However, I'm really stumped again on what I thought would be a simple solution.

I figured I could just use GetMessage to check if the minimize button was pressed, then just hide the window. But, apparantly I'm either completely missing something on MSDN or it's not as easy as I expected. I scoured CodeProject and found even more examples with tray icons, but none of them make it clear on how it's done and the fact that they use MFC along with huge classes doesn't help. So, once again, I would appreciate any links, tips, or help.

Thanks in advance.

Share this post


Link to post
Share on other sites
Quote:
Original post by slippnslide
After reading MSDN, I've got it to display a custom icon all the time and it removes it on the program's exit, so I have everything working good there. However, I'm really stumped again on what I thought would be a simple solution.

I figured I could just use GetMessage to check if the minimize button was pressed, then just hide the window. But, apparantly I'm either completely missing something on MSDN or it's not as easy as I expected. I scoured CodeProject and found even more examples with tray icons, but none of them make it clear on how it's done and the fact that they use MFC along with huge classes doesn't help. So, once again, I would appreciate any links, tips, or help.

Thanks in advance.


Look at the source I posted - it has everything you need!

Share this post


Link to post
Share on other sites
Quote:
Original post by slippnslide
Yes, I see it, but it's not working when I implement it into my code.


That's not very specific. What does the compiler say? Does it compile at all?

Share this post


Link to post
Share on other sites
Well, I'm not using your class because it's more than I need. I just wrote my own few lines of code to create the icon and display it based off of MSDN and some of your code. After that, I don't see anywhere where it detects if the window was minimized. I see where it checks if the icon was clicked, ect. What I tried was what I thought was the code to detect the minimized window, but I was wrong.

Edit: Got it. For those that care, put this in your message loop:

case WM_SYSCOMMAND:
{
if(wParam == SC_MINIMIZE)
{
ShowWindow(hWnd, SW_HIDE);
} break;

But, apparantly the window doesn't stay hidden. So, does anyone know why?

[Edited by - slippnslide on July 12, 2006 12:19:37 PM]

Share this post


Link to post
Share on other sites
You need to respond to the WM_SIZE message. Check it out on MSDN.

For quick reference, try this:


if(uMsg == WM_SIZE)
{
switch(wParam)
{
case SIZE_MAXIMIZED:
break;
case SIZE_MINIMIZED:
break;
case SIZE_RESTORED:
break;
}
}

Share this post


Link to post
Share on other sites
Great! That fixed that problem, but I have another. I don't understand why it's not working. I have the window minimizing to the tray icon, but when I click on it, the window won't maximize. This is the error I'm getting. If somebody is willing to take a look at my error and my code to see if you can see why it won't maximize, it would be greatly appreciated (you don't need to do it for me, just a few pointers would do). If not, that's fine too.
Quote:
Project : Win32 Application
Compiler : GNU GCC Compiler (called directly)
Directory : C:\Documents and Settings\John\Desktop\derp--------------------------------------------------------------------------------
Switching to target: default
Compiling: main.cpp
main.cpp: In function `int WinMain(HINSTANCE__*, HINSTANCE__*, CHAR*, int)':
main.cpp:57: warning: passing NULL used for non-pointer converting 1 of `HWND__* CreateWindowExA(DWORD, const CHAR*, const CHAR*, DWORD, int, int, int, int, HWND__*, HMENU__*, HINSTANCE__*, void*)'
Compiling: Win32GUI.rc
Linking executable: C:\Documents and Settings\John\Desktop\derp\Win32GUI.exe
Process terminated with status 0 (0 minutes, 0 seconds)
0 errors, 1 warnings


And this is my basic code. It's not neat because I'm trying to just get this to work.

// include the basic windows header file
#include <windows.h>
#include <windowsx.h>
#include <shellapi.h>
#include "resource.h"

#define WM_TRAYMESSAGE (WM_USER + 1)

// for NOTIFYICONDATA
NOTIFYICONDATA nid;

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam);


// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
// the handle for the window, filled by a function
HWND hWnd;
// this struct holds information for the window class
WNDCLASSEX wc;

// clear out the window class for use
ZeroMemory(&wc, sizeof(WNDCLASSEX));

// fill in the struct with the needed information
wc.cbSize = sizeof(WNDCLASSEX);
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = (WNDPROC)WindowProc;
wc.hInstance = hInstance;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
wc.hbrBackground = (HBRUSH)COLOR_WINDOW;
wc.lpszClassName = "WindowClass1";

// register the window class
RegisterClassEx(&wc);

// create the window and use the result as the handle
hWnd = CreateWindowEx(NULL,
"WindowClass1", // name of the window class
"Our First Windowed Program", // title of the window
WS_OVERLAPPEDWINDOW, // window style
300, // x-position of the window
300, // y-position of the window
500, // width of the window
400, // height of the window
NULL, // we have no parent window, NULL
NULL, // we aren't using menus, NULL
hInstance, // application handle
NULL); // used with multiple windows, NULL

// fill the struct for NOTIFYICONDATA with the needed info
nid.cbSize = sizeof(NOTIFYICONDATA);
nid.hWnd = hWnd;
nid.uID = 0;
nid.uFlags = NIF_ICON | NIF_TIP;
nid.uCallbackMessage = WM_TRAYMESSAGE;
HICON hIco = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_MYICON));
nid.hIcon = hIco;
strncpy( nid.szTip, "App name", 64 );

// display the window on the screen
ShowWindow(hWnd, nCmdShow);
Shell_NotifyIcon(NIM_ADD, &nid);

if(hIco) { DestroyIcon(hIco); }

// enter the main loop:

// this struct holds Windows event messages
MSG msg;

// wait for the next message in the queue, store the result in 'msg'
while(GetMessage(&msg, NULL, 0, 0))
{
// translate keystroke messages into the right format
TranslateMessage(&msg);

// send the message to the WindowProc function
DispatchMessage(&msg);
}

// return this part of the WM_QUIT message to Windows
return msg.wParam;
}

// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
// sort through and find what code to run for the message given
switch(message)
{
// this message is read when the window is closed
case WM_DESTROY:
{
// close the application entirely
Shell_NotifyIcon(NIM_DELETE, &nid);
PostQuitMessage(0);
return 0;
} break;

/* case WM_TRAYMESSAGE:
{
if(lParam == WM_LBUTTONDOWN)
{
Shell_NotifyIcon(NIM_DELETE, &nid);
}
} break;*/

case WM_SIZE:
{
if(wParam == SIZE_MINIMIZED)
{
ShowWindow(hWnd, SW_HIDE);
}
} break;
}

// Handle any messages the switch statement didn't
return DefWindowProc (hWnd, message, wParam, lParam);
}


The error points to the last NULL in CreateWindowEx.

Thanks in advance to anyone who is willing to help.

Edit: I got the warning fixed. Right now, I'm just trying to figure out why my window won't maximize. I have the code to remove the icon from the tray there for testing purposes.

Edit 2: It seems my case WM_TRAYMESSAGE: statement isn't working.

Edit 3: Solved. Just forgot to add in NIF_MESSAGE to my nid.uFlags variable. Such a stupid mistake.

[Edited by - slippnslide on July 12, 2006 11:46:00 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement