Alarm - alarms and reminders for Windows

This will remain free to use but if you make money from using it a small donation via Paypal to gb@bramblingbooks.co.uk would be useful to help pay hosting costs etc.

There are no viruses ... present in the zipped source file. I know this because I wrote it. I am not to be held responsible in anyway for any of this freely given source code, resource scripts and icon. By downloading and using these files you understand this. The file is here and contains all the source files needed to build this project using MS Viz C/C++ V6. REMEMBER to set the multi-thread option!

Included in the zipped file are alarm.wav and alarms.ini - these two should go into the paths you set in main.c and alarm.c noted below.

If your system does not have a drive D: - change the D: to a C: in the following...

      In main.c you need to change the lines
      static char *alarmFileName = "d:\\al4rming\\alarms.ini";
and
      _mkdir("d:\\al4rming");

      In alarm.c you need to change the line
      static char *soundfile = "d:\\al4rming\\alarm.wav";


Here's main.c ...



// main.c

#include <windows.h>
#include <stdio.h>
#include <direct.h>
#include <shellapi.h>
#include <time.h>
#include "resource.h"
#include "alarming.h"


void AddTrayIcon(HWND hwnd, HICON hicon)	// add a tray icon - this works on win 2003
{
	NOTIFYICONDATA nid;

	memset(&nid, 0, sizeof(nid));
	nid.cbSize = sizeof(nid);
	nid.hWnd = hwnd;
	nid.uFlags = NIF_ICON | NIF_TIP | NIF_MESSAGE;
	nid.hIcon = hicon;
	nid.uCallbackMessage = NIM_CALLBACK;
	strcpy(nid.szTip, "Alarm Calls");

	Shell_NotifyIcon(NIM_ADD, &nid);
}

void DelTrayIcon(HWND hwnd, HICON hicon)	// remove tray icon
{
	NOTIFYICONDATA nid;

	memset(&nid, 0, sizeof(nid));
	nid.cbSize = sizeof(nid);
	nid.hWnd = hwnd;
	nid.uFlags = NIF_ICON;
	nid.hIcon = hicon;

	Shell_NotifyIcon(NIM_DELETE, &nid);
}

// index into alarm data saved in file

enum {A_name, A_startdate, A_starttime, A_onceonly, A_neverend, A_enddate, A_endtime, A_gap, A_gapunits,
      A_between1, A_between2, A_dayfilter, A_active, A_message, A_LAST};


typedef struct _alarm_line_			// alarm data
{
	char things[A_LAST][MAX_ASTR];
	long atime;
} ALARMLINE;

ALARMLINE AllAlarms[MAX_ALARMS];	// all allarms together

static int paused = 0;				// an alarms paused when it's edited

static char *alarmFileName = "d:\\al4rming\\alarms.ini";	// where to find the data

void AddAlarm(ALARMLINE *pa)		// add an alarm
{
	int i;

	for (i = 0;i < MAX_ALARMS;i++)
	{
		if (strlen(AllAlarms[i].things[A_name]) == 0)
		{
			memcpy(AllAlarms[i].things, pa, A_LAST * MAX_ASTR);
			break;
		}
	}
}


void LoadAlarmList()				// load from file - items are between #\n and #\n
{
	FILE *f;
	char buff[1024], *p;
	int inalarm = 0, i;
	ALARMLINE al;

	memset(AllAlarms, 0, sizeof(AllAlarms));

	f = fopen(alarmFileName, "r");
	if (f)
	{
		while (memset(buff, 0, sizeof(buff)), fgets(buff, sizeof(buff), f) != NULL)
		{
			if (buff[0] == '#') // start end alarm line
			{
				if (inalarm)
				{
					inalarm = 0;
					if (strlen(al.things[A_name]))
						AddAlarm(&al);
				} else
				{
					memset(&al, 0, sizeof(al));
					inalarm = 1;
					i = 0;
				}
			} else
			{
				if (inalarm)
				{
					p = strchr(buff, '\n'); if (p) *p = 0;
					p = strchr(buff, '\r'); if (p) *p = 0;

					if (i < A_LAST)
						strncpy(al.things[i++], buff, MAX_ASTR);
					
				}
			}
		}

		fclose(f);
	} else
	{
		_mkdir("d:\\al4rming");						// try creating the path incase its 1st time in
		f = fopen(alarmFileName, "w");
		if (f)
		{
			fputs("\n", f);
			fclose(f);
		}

	}
}

void SaveAlarmList()								// write them back to the file
{
	FILE *f;
	int a, i;


	f = fopen(alarmFileName, "w");
	if (f)
	{
		for (a = 0;a < MAX_ALARMS; a++)
		{
			if (strlen(AllAlarms[a].things[A_name]))
			{
				fprintf(f, "#\n");
				for (i = 0;i < A_LAST;i++)
					fprintf(f, "%s\n", AllAlarms[a].things[i]);
				fprintf(f, "#\n\n");
			}
		}
		fclose(f);
	} else
		MessageBox(NULL, "Failed to save alarms","Alarming", MB_ICONEXCLAMATION | MB_OK);
}

void DeleteAlarm(int n)	// remove an alarm (having a blank name is the same as not existing)
{
	memset(AllAlarms[n].things, 0, A_LAST * MAX_ASTR);	
}

BOOL checkdatestr(char *pdate)					// make sure its mm/dd/yyyy
{
	int d,m,y;
	char *p;

	d = atoi(pdate);
	p = strchr(pdate, '/');
	if (p)
	{
		m = atoi(p+1);
		p++;
		p = strchr(p, '/');
		if (p)
		{
			y = atoi(p+1);
			sprintf(pdate, "%02d/%02d/%04d", d,m,y);
			return 0;
		}
	}
	strcpy(pdate, "01/01/2000");
	return 1;
}
BOOL checktimestr(char *ptim)				// make sure it's hh:mm
{
	int h, m;
	char *p;

	h = atoi(ptim);
	p = strchr(ptim, ':');
	if (p)
	{
		m = atoi(p+1);
	} else
		m = 0;
	sprintf(ptim, "%02d:%02d", h, m);
	return 0;
}
int presavecheck()						// quick check of time date formats - ranges NOT checked
{
	int a;
	ALARMLINE *pa;

	for (a = 0;a < MAX_ALARMS;a++)
	{
		pa = &AllAlarms[a];
		if (strlen(pa->things[A_name]))
		{
			if (pa->things[A_active][0] == 'Y')
			{
				if (toupper(pa->things[A_gapunits][0]) != 'H')
					strcpy(pa->things[A_endtime], pa->things[A_starttime]);
				if (atoi(pa->things[A_gap]) == 0)
					strcpy(pa->things[A_gap], "1");
				checkdatestr(pa->things[A_startdate]);
				checktimestr(pa->things[A_starttime]);
				checkdatestr(pa->things[A_enddate]);
				checktimestr(pa->things[A_endtime]);
				checktimestr(pa->things[A_between1]);
				checktimestr(pa->things[A_between2]);
			}
		}
	}

	return 0;
}

/*
	void GetMyDatePlus(LPSTR lpDest, long daystoadd)

  get future date: now + extra days to add in format dd/mm/yyy

  */
void GetMyDatePlus(LPSTR lpDest, long daystoadd)	// in dd/mm/yyyy format
{
	time_t ltime;
	struct tm *ptm;

	time(&ltime);
	ltime += daystoadd * 24L * 60L * 60L;
	ptm = localtime(&ltime);
	sprintf(lpDest,
			"%02d/%02d/%04d",	
			ptm->tm_mday,
			ptm->tm_mon+1,
			ptm->tm_year + 1900);
			
}


int InitNewAlarm()			// fill alarm with defaults	
{
	int na = -1;
	
	for (na = 0;na < MAX_ALARMS;na++)
	{
		if (strlen(AllAlarms[na].things[A_name]) == 0)
			break;
	}

	if (na == MAX_ALARMS)
		return -1;

	sprintf(AllAlarms[na].things[A_name], "Alarm %d", na+1);
	GetMyDatePlus(AllAlarms[na].things[A_startdate],0);
	strcpy(AllAlarms[na].things[A_starttime],"08:00"); 
	strcpy(AllAlarms[na].things[A_onceonly], "N");
	strcpy(AllAlarms[na].things[A_neverend], "N");
	GetMyDatePlus(AllAlarms[na].things[A_enddate], 7);
	strcpy(AllAlarms[na].things[A_endtime], "23:59");
	strcpy(AllAlarms[na].things[A_gap], "1");
	strcpy(AllAlarms[na].things[A_gapunits], "Day");
	strcpy(AllAlarms[na].things[A_between1], "00:00");
	strcpy(AllAlarms[na].things[A_between2], "23:59");
	strcpy(AllAlarms[na].things[A_dayfilter], "YYYYYYY");
	strcpy(AllAlarms[na].things[A_active], "Y");
	sprintf(AllAlarms[na].things[A_message], "This is alarm %d", na+1);
	return na;
}


void SetupDlgAlarmData(HWND hwnd, int iParam)	// put indo dialog controls
{
	ALARMLINE *a;

	a = &AllAlarms[iParam];
	SetDlgItemText(hwnd, IDC_NAME, a->things[A_name]);
	SetDlgItemText(hwnd, IDC_STARTDATE, a->things[A_startdate]);
	SetDlgItemText(hwnd, IDC_STARTTIME, a->things[A_starttime]);
	CheckDlgButton(hwnd, IDC_ONCEONLY, (stricmp(a->things[A_onceonly], "Y") == 0));
	CheckDlgButton(hwnd, IDC_NEVEREND, (stricmp(a->things[A_neverend], "Y") == 0));
	SetDlgItemText(hwnd, IDC_ENDDATE, a->things[A_enddate]);
	SetDlgItemText(hwnd, IDC_ENDTIME, a->things[A_endtime]);
	SetDlgItemText(hwnd, IDC_GAPNUMBER, a->things[A_gap]);
	SetDlgItemText(hwnd, IDC_GAPUNITS, a->things[A_gapunits]);
	SetDlgItemText(hwnd, IDC_BETWEEN1, a->things[A_between1]);
	SetDlgItemText(hwnd, IDC_BETWEEN2, a->things[A_between2]);
	CheckDlgButton(hwnd, IDC_MONDAY,	(a->things[A_dayfilter][0] == 'Y'));
	CheckDlgButton(hwnd, IDC_TUESDAY,	(a->things[A_dayfilter][1] == 'Y'));
	CheckDlgButton(hwnd, IDC_WEDNESDAY, (a->things[A_dayfilter][2] == 'Y'));
	CheckDlgButton(hwnd, IDC_THURSDAY,	(a->things[A_dayfilter][3] == 'Y'));
	CheckDlgButton(hwnd, IDC_FRIDAY,	(a->things[A_dayfilter][4] == 'Y'));
	CheckDlgButton(hwnd, IDC_SATURDAY,	(a->things[A_dayfilter][5] == 'Y'));
	CheckDlgButton(hwnd, IDC_SUNDAY,	(a->things[A_dayfilter][6] == 'Y'));
	SetDlgItemText(hwnd, IDC_MESSAGE, a->things[A_message]);
	CheckDlgButton(hwnd, IDC_ACTIVE, (stricmp(a->things[A_active], "Y") == 0));
}

void makedayfilter(char *dest, HWND hwnd)
{
	dest[0] = IsDlgButtonChecked(hwnd, IDC_MONDAY) ?	'Y' : '-';
	dest[1] = IsDlgButtonChecked(hwnd, IDC_TUESDAY) ?	'Y' : '-';
	dest[2] = IsDlgButtonChecked(hwnd, IDC_WEDNESDAY) ?	'Y' : '-';
	dest[3] = IsDlgButtonChecked(hwnd, IDC_THURSDAY) ?	'Y' : '-';
	dest[4] = IsDlgButtonChecked(hwnd, IDC_FRIDAY) ?	'Y' : '-';
	dest[5] = IsDlgButtonChecked(hwnd, IDC_SATURDAY) ?	'Y' : '-';
	dest[6] = IsDlgButtonChecked(hwnd, IDC_SUNDAY) ?	'Y' : '-';
	dest[7] = 0;
}

void GrabDlgData(HWND hwnd, int iParam)
{
	ALARMLINE *a;

	a = &AllAlarms[iParam];
	GetDlgItemText(hwnd, IDC_NAME, a->things[A_name], MAX_ASTR);
	GetDlgItemText(hwnd, IDC_STARTDATE, a->things[A_startdate], MAX_ASTR);
	GetDlgItemText(hwnd, IDC_STARTTIME, a->things[A_starttime], MAX_ASTR);

	strcpy(a->things[A_onceonly], IsDlgButtonChecked(hwnd, IDC_ONCEONLY) ? "Y" : "N");
	strcpy(a->things[A_neverend], IsDlgButtonChecked(hwnd, IDC_NEVEREND) ? "Y" : "N");

	GetDlgItemText(hwnd, IDC_ENDDATE, a->things[A_enddate], MAX_ASTR);
	GetDlgItemText(hwnd, IDC_ENDTIME, a->things[A_endtime], MAX_ASTR);
	GetDlgItemText(hwnd, IDC_GAPNUMBER, a->things[A_gap], MAX_ASTR);
	GetDlgItemText(hwnd, IDC_GAPUNITS, a->things[A_gapunits], MAX_ASTR);
	GetDlgItemText(hwnd, IDC_BETWEEN1, a->things[A_between1], MAX_ASTR);
	GetDlgItemText(hwnd, IDC_BETWEEN2, a->things[A_between2], MAX_ASTR);

	makedayfilter(a->things[A_dayfilter], hwnd);
	
	GetDlgItemText(hwnd, IDC_MESSAGE, a->things[A_message], MAX_ASTR);

	strcpy(a->things[A_active], IsDlgButtonChecked(hwnd, IDC_ACTIVE) ? "Y" : "N");

}

BOOL CALLBACK dialog2func(			// edit / creat an alarm entry
    HWND  hwnd,
    UINT  uMsg,	
    WPARAM  wParam,	
    LPARAM  lParam 	
   )
{
	static int iParam;
	char buff[MAX_ASTR], name[MAX_ASTR];

	switch (uMsg)
	{

	case WM_INITDIALOG:
		iParam = (int) lParam;
		if (iParam == -1)	// new alarm
		{
			iParam = InitNewAlarm();
			if (iParam < 0)
			{
				MessageBox(hwnd, "Too many alarms","Alarming", MB_ICONEXCLAMATION | MB_OK);
				EndDialog(hwnd, -1);
			}
		}
		SetupDlgAlarmData(hwnd, iParam);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_TEST:
			GetDlgItemText(hwnd, IDC_NAME, name, sizeof(name));
			GetDlgItemText(hwnd, IDC_MESSAGE, buff, sizeof(buff));
			AlarmFunc(name, buff, iParam);
			break;
		case IDCANCEL:			// quit dont save dont do updates
			LoadAlarmList();	// reload to dispose of unwanted edits
			EndDialog(hwnd, 0);
			return 1;
		case IDOK:					// save settings
			
			GrabDlgData( hwnd,  iParam);
			presavecheck();			
			SaveAlarmList();
			LoadAlarmList();	// reload to dispose of deletions
			EndDialog(hwnd, 1);
			return 1;
		default:
			break;
		}
		break;
	default:
		break;
	}
	return 0;
}

void fillalarmlistbox(HWND hwnd)
{
	int i;
	char buff[MAX_ASTR];
	int tabstops[] = {110, 0};

	SendMessage(GetDlgItem(hwnd, IDC_ALARMLIST), LB_RESETCONTENT, 0, 0);
	SendDlgItemMessage(hwnd, IDC_ALARMLIST, LB_SETTABSTOPS, 1, (LPARAM) tabstops);	// so 'Active' lines up

	for (i = 0;i < MAX_ALARMS;i++)
	{
		if (strlen(AllAlarms[i].things[A_name]) > 0)
		{
			if (AllAlarms[i].things[A_active][0] == 'Y')
			{
				sprintf(buff, "%-64.64s \tActive", AllAlarms[i].things[A_name]);
			} else
			{
				sprintf(buff, "%-64.64s \t-", AllAlarms[i].things[A_name]);
			}
			SendMessage(GetDlgItem(hwnd, IDC_ALARMLIST), LB_ADDSTRING, 0, (LPARAM) (char *)buff);
		}
	}
	
}


BOOL CALLBACK dialog1func(  // 1st dialog box showing drop down alarms and creation buttons
    HWND  hwnd,
    UINT  uMsg,	
    WPARAM  wParam,	
    LPARAM  lParam 	
   )
{
	int i;

	switch (uMsg)
	{

	case WM_INITDIALOG:
		
		fillalarmlistbox(hwnd);
		SendMessage(GetDlgItem(hwnd, IDC_ALARMLIST), LB_SETCURSEL, 0, 0);
		return 0;

	case WM_COMMAND:
		switch (LOWORD(wParam))
		{
		case IDC_ALARMLIST:				// something's happened to the alarm list box
			switch (HIWORD(wParam))
			{
			case LBN_DBLCLK:			// double clicked

				paused = 1;
				i = SendMessage((HWND)GetDlgItem(hwnd, IDC_ALARMLIST), LB_GETCURSEL, 0, 0);	// current selection
				DialogBoxParam(	(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), 
								MAKEINTRESOURCE(IDD_DIALOG2), 
								hwnd,
								dialog2func,
								(LPARAM) i);
				fillalarmlistbox(hwnd);
				SendMessage(GetDlgItem(hwnd, IDC_ALARMLIST), LB_SETCURSEL, i, 0);
				paused = 0;

				break;
			default:
				break;
			}
			return 1;

		case IDC_NEW:				// new alarm

			paused = 1;
			DialogBoxParam(	(HINSTANCE)GetWindowLong(hwnd, GWL_HINSTANCE), 
							MAKEINTRESOURCE(IDD_DIALOG2), 
							hwnd,
							dialog2func,
							(LPARAM) (int) -1);
			fillalarmlistbox(hwnd);
			paused = 0;
			return 1;

		case IDC_EXIT:				// shut down
			EndDialog(hwnd, -1);
			return 1;
		case IDCANCEL:				// quit dont save dont do updates
			EndDialog(hwnd, 0);
			return 1;
		case IDOK:					// save settings
			EndDialog(hwnd, 1);
			return 1;
		default:
			break;
		}
		break;
	default:
		break;
	}
	return 0;
}

void SetOffAlarm(ALARMLINE *pa, int idx)	// set one-offs to inactive and spawn alarm thread
{

	if (pa->things[A_onceonly][0] == 'Y')
	{
		pa->things[A_active][0] = 'N';
		SaveAlarmList();
	}

	AlarmFunc(pa->things[A_name], pa->things[A_message], idx);
}

int compareStrdat2Struct(char *strdat, struct tm *ptm)	// -1 strdat < tm
{
	int aday, amonth, ayear;

	aday = atoi(strdat);	// dd/mm/yyyy
	amonth = atoi(strdat+3) - 1;
	ayear = atoi(strdat+6) - 1900;

	if (ayear < ptm->tm_year)
		return -1;
	if (ayear > ptm->tm_year)
		return 1;

	if (amonth < ptm->tm_mon)
		return -1;
	if (amonth > ptm->tm_mon)
		return 1;

	if (aday < ptm->tm_mday)
		return -1;
	if (aday > ptm->tm_mday)
		return 1;

	return 0;
}
int compareStrtim2Struct(char *strtim, struct tm *ptm) // -1 strtim < tm
{
	int ahour, amin;

	ahour = atoi(strtim);	// HH:ss
	amin = atoi(strtim + 3);

	if (ahour < ptm->tm_hour)
		return -1;
	if (ahour > ptm->tm_hour)
		return 1;
	if (amin < ptm->tm_min)
		return -1;
	if (amin > ptm->tm_min)
		return 1;

	return 0;
}

BOOL StartDateOk(ALARMLINE *pa, struct tm *ptm)
{

	return compareStrdat2Struct(pa->things[A_startdate], ptm) <= 0;
}
BOOL EndDateOk(ALARMLINE *pa, struct tm *ptm)
{
	if (pa->things[A_neverend][0] == 'Y')
		return 1;
	return compareStrdat2Struct(pa->things[A_enddate], ptm) >= 0;
}
BOOL StartTimeOk(ALARMLINE *pa, struct tm *ptm)
{
	int i;

	i = compareStrdat2Struct(pa->things[A_startdate], ptm);
	if (i == 0)
		return compareStrtim2Struct(pa->things[A_starttime], ptm) <= 0;
	return i < 0;
}
BOOL EndTimeOk(ALARMLINE *pa, struct tm *ptm)
{
	int i;

	if (pa->things[A_neverend][0] == 'Y')
		return 1;
	i = compareStrdat2Struct(pa->things[A_enddate], ptm);
	if (i == 0)
		return compareStrtim2Struct(pa->things[A_endtime], ptm) >= 0;
	return i > 0;
}
BOOL BetweenTimesOk(ALARMLINE *pa, struct tm *ptm)
{
	return	compareStrtim2Struct(pa->things[A_between1], ptm) <= 0 &&
			compareStrtim2Struct(pa->things[A_between2], ptm) >= 0;
}
BOOL DayFilterOk(ALARMLINE *pa, struct tm *ptm)
{
	int d;
	char c;

	d = ptm->tm_wday;	// days since sunday 
	d = (d + 6) % 7;	// convert to mtwtfss
	c = pa->things[A_dayfilter][d];
	return c == 'Y';
}

BOOL IsLeapYear(long iYear)
{
    int iQuotient;
    BOOL fLeapYr = FALSE;

    if(!(iYear%4) && (iYear%100)) 
        fLeapYr = TRUE;
    else
	{
        if(!(iYear % 4) && !(iYear % 100))
		{
            iQuotient = iYear / 100;
            if(!(iQuotient % 4))
				fLeapYr = TRUE;
        }
    }

    return (fLeapYr);
}

long datetime2minutesfrom1970(char *pdate, char *ptim)	// dd/mm/yyyy hh:mm
{
	long ayear, y, d, h, m;

	ayear = atoi(pdate + 6);
	for (d = 0, y = 1970;y < ayear; y++)
	{
		if (IsLeapYear(y))
			d += 366;
		else
			d += 365;
	}
	h = atoi(ptim);
	m = atoi(ptim + 3);
	return m + (h * 60) + (d * 24 * 60);
}
long gapinminutes(char *pn, char *punit)
{
	long l, m;

	l = atoi(pn);
	switch (*punit)
	{

	case 'd':
	case 'D':
		m = 24 * 60;
		break;

	case 'w':
	case 'W':
		m = 24 * 7 * 60;
		break;

	case 'h':
	case 'H':
	default:
		m = 60;		// hours
		break;
	}
	return m * l;
}

BOOL GapOk(ALARMLINE *pa, struct tm *ptm)	// exact multiple of gap time passed?
{
	BOOL bRet = 0;
	long mSinceStart, mToNow, mGap;
	char datenow[16], timnow[16];

	sprintf(datenow, "%02d/%02d/%04d", ptm->tm_mday, ptm->tm_mon + 1, ptm->tm_year + 1900);
	sprintf(timnow, "%02d:%02d", ptm->tm_hour, ptm->tm_min);

	if (pa->things[A_gapunits][0] == 'M' || pa->things[A_gapunits][0] == 'm') // monthly gap same day?
	{
		bRet = atoi(pa->things[A_startdate]) == atoi(datenow);
	} else
	{
		mSinceStart = datetime2minutesfrom1970(pa->things[A_startdate], pa->things[A_starttime]);
		mToNow = datetime2minutesfrom1970(datenow, timnow);
		mGap = gapinminutes(pa->things[A_gap], pa->things[A_gapunits]);

		bRet =  ((mToNow - mSinceStart) % mGap) == 0;
	}
	return bRet;
}

void CheckAlarm(ALARMLINE *pa, struct tm *ptm, int idx)
{
	if (StartDateOk(pa, ptm))
		if (EndDateOk(pa, ptm))
			if (StartTimeOk(pa, ptm))
				if (EndTimeOk(pa, ptm))
					if (BetweenTimesOk(pa, ptm))
						if (DayFilterOk(pa, ptm))
							if (GapOk(pa, ptm))
								SetOffAlarm(pa, idx);
}
void DoAlarmPoll()
{
	int a;
	ALARMLINE *al;
	long tnow;
	struct tm *ptm;

	tnow = time(NULL);
	ptm = localtime(&tnow);

	for (a = 0;a < MAX_ALARMS;a++)
	{
		al = &AllAlarms[a];
		if (strlen(al->things[A_name]))
		{
			if (al->things[A_active][0] == 'Y')
			{
				if (tnow > (al->atime + 50))	// at least 50 secs since last checked
				{
					CheckAlarm(al, ptm, a);
					al->atime = tnow;
				}
			}
		}
	}
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	HDC hdc;
	int wmId, wmEvent;
	static HICON hicon;
	static int nimGate = 0;
	PAINTSTRUCT ps;
	
	switch (message) 
	{
		case NIM_CALLBACK:
			if (nimGate == 0)
			{
				nimGate++;
				if (lParam == WM_LBUTTONDOWN || lParam == WM_RBUTTONDOWN)
				{
					if (DialogBox(	(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), 
									MAKEINTRESOURCE(IDD_DIALOG1), 
									hWnd,
									dialog1func ) == -1)
					{
						PostMessage(hWnd, WM_CLOSE, 0, 0);
					}
				}
				nimGate--;
			} else
			{
				SetForegroundWindow(hWnd );
				ShowOwnedPopups(hWnd, 1);
			}
			break;
		case WM_CREATE:
			AlarmsOn();
			hicon = LoadIcon((HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE), MAKEINTRESOURCE(IDI_ICON3));
			AddTrayIcon( hWnd, hicon);
			LoadAlarmList();
			SetTimer(hWnd, 1, 5000, NULL);
			break;
		case WM_TIMER:
			KillTimer(hWnd, 1);
			if (paused == 0)
			{
				DoAlarmPoll();
			}
			SetTimer(hWnd, 1, 5000, NULL);
			break;
		case WM_COMMAND:
			wmId    = LOWORD(wParam); 
			wmEvent = HIWORD(wParam); 
			switch (wmId)
			{
				case WM_CLOSE:
					PostMessage(hWnd, WM_DESTROY,0,0);
					break;
				default:
					return DefWindowProc(hWnd, message, wParam, lParam);
			}
			break;
		case WM_PAINT:
			hdc = BeginPaint(hWnd, &ps);
			EndPaint(hWnd, &ps);
			break;
		case WM_DESTROY:
			AlarmsOff();
			PostQuitMessage(0);
			DelTrayIcon( hWnd, hicon);
			break;
		default:
			return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}


int WINAPI WinMain(
    HINSTANCE  hInstance,		// handle to current instance
    HINSTANCE  hPrevInstance,	// handle to previous instance
    LPSTR  lpCmdLine,			// pointer to command line
    int  nShowCmd 				// show state of window
   )
{
	static char *szWindowClass = "Alarming";
	static char *szTitle = "Alarming";
	WNDCLASSEX wcex;
	HWND hWnd;
	MSG msg;

	wcex.cbSize = sizeof(WNDCLASSEX); 

	wcex.style			= CS_HREDRAW | CS_VREDRAW;
	wcex.lpfnWndProc	= (WNDPROC)WndProc;
	wcex.cbClsExtra		= 0;
	wcex.cbWndExtra		= 0;
	wcex.hInstance		= hInstance;
	wcex.hIcon			= LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= NULL;

	RegisterClassEx(&wcex);

	hWnd = CreateWindow(szWindowClass, szTitle,WS_OVERLAPPEDWINDOW,
						10,10, 200, 300, NULL, NULL, hInstance, NULL);

   ShowWindow(hWnd, SW_HIDE);
   UpdateWindow(hWnd);

	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	return 0;
}



Here's alarm.c ...

// alarm.c

#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <mmsystem.h>
#include <time.h>
#include <process.h>
#include "alarming.h"

// a pale yellow background

#define POPBACKGROUND RGB(255,255,183)

#define SOUNDGAP 10000		// deleay between alarm noises

// the alarm noise which goes off every SOUNDGAP millisecs - this file should be kept small

static char *soundfile = "d:\\al4rming\\alarm.wav";

static char *szWindowClass = "Alarm";
static char *szTitle = "Alarm";

static unsigned long cReg = 0;			// window class registered flag
static int aoff = 0;					// alarms off flag
static int activea[MAX_ALARMS];			// which alarms are active


typedef struct _adata_					// data passed to each alarm window
{
	int idx;
	char msg[MAX_ASTR + MAX_ASTR + 32];
	long tim;
} ADATA;

static CRITICAL_SECTION cs1;			// for thread syncs

static int SoundOwner = -1;				// I want only 1 alarm to play the sounds even if several are active

static BOOL GrabTheSound(int idx)		// grab and keep ownership. retunr TRUE if okay to play sound
{
	EnterCriticalSection(&cs1);
	if (SoundOwner == -1)
		SoundOwner = idx;
	LeaveCriticalSection(&cs1);
	return (SoundOwner == idx);
}

static void ReleaseTheSound(int idx)	// release ownership for any other thread
{
	EnterCriticalSection(&cs1);
	if (SoundOwner == idx)
		SoundOwner = -1;
	LeaveCriticalSection(&cs1);
}

LRESULT CALLBACK AlarmWndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) // callback function
{
	HDC hdc;
	PAINTSTRUCT ps;
	RECT r;
	ADATA *pa;
	long tim;
	int cx, x;

	switch (message) 
	{
		
		case WM_CREATE:
			SetTimer(hwnd, 1, 100, NULL);		// timer for window motion across screen
			SetTimer(hwnd, 2, 200, NULL);		// timer for playing sound
			break;
		case WM_TIMER:

			if (aoff)							// told to turn off
			{
				PostMessage(hwnd, WM_CLOSE, 0, 0);
				break;
			}

			pa = (ADATA *) GetWindowLong(hwnd, GWL_USERDATA);
			tim = time(NULL);

			if (pa->tim < (tim - (60 * 15)))				// 15 minutes has gone by so close it
				PostMessage(hwnd, WM_CLOSE, 0, 0);

			if (wParam == 2)
			{
				KillTimer(hwnd, 2);
				
				if (GrabTheSound(pa->idx))					// play sound if this thread owns it
					PlaySound(soundfile, NULL, SND_ASYNC | SND_FILENAME );

				SetTimer(hwnd, 2, SOUNDGAP, NULL);
			} else
			if (wParam == 1)
			{
				KillTimer(hwnd, 1);							// march window across screen
				GetWindowRect(hwnd, &r);
				cx = GetSystemMetrics(SM_CXSCREEN);
				x = (r.right < cx) ? r.left + 1 : 1;
				SetWindowPos(hwnd, HWND_TOPMOST, x, r.top, 0,0, SWP_SHOWWINDOW | SWP_NOSIZE);
				SetTimer(hwnd, 1, 100, NULL);
			}
			break;
		case WM_RBUTTONDOWN:
		case WM_LBUTTONDOWN:
			PostMessage(hwnd, WM_CLOSE, 0, 0);				// any click in the area closes it
			break;
		case WM_PAINT:

			pa = (ADATA *) GetWindowLong(hwnd, GWL_USERDATA);

			GetClientRect(hwnd, &r);
			
			hdc = BeginPaint(hwnd, &ps);
		
			SelectObject (hdc, GetStockObject (ANSI_VAR_FONT));

			SetBkColor(hdc, POPBACKGROUND); 
			TextOut(hdc, 2,2,pa->msg, strlen(pa->msg));

			FrameRect(hdc, &r, GetStockObject(BLACK_BRUSH));

			EndPaint(hwnd, &ps);
			break;
		case WM_CLOSE:
			PostMessage(hwnd, WM_DESTROY,0,0);
			break;
		case WM_DESTROY:
			pa = (ADATA *) GetWindowLong(hwnd, GWL_USERDATA);
			ReleaseTheSound(pa->idx);
			KillTimer(hwnd, 1);
			KillTimer(hwnd, 2);
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hwnd, message, wParam, lParam);
   }
   return 0;
}

void AlarmThread(void *pl)						// the alarm thread itself
{

	HWND hwnd;
	MSG msg;
	ADATA *pa;
	SIZE si;
	HDC hdc;

	pa = (ADATA *) pl;

	hwnd = CreateWindow(szWindowClass, szTitle, WS_POPUP, 10,10, 200, 300, NULL, NULL, NULL, NULL); // dummy poz and size

	SetWindowLong(hwnd, GWL_USERDATA, (long) pa);

	hdc = GetDC(hwnd);
	SelectObject (hdc, GetStockObject (ANSI_VAR_FONT));
	GetTextExtentPoint32(hdc, pa->msg, strlen(pa->msg), &si);		// calculate size of window
	ReleaseDC(hwnd, hdc);
	
	MoveWindow(hwnd, 1, (pa->idx * (si.cy + 8)) + 2, si.cx + 8, si.cy + 6, 0);	// place on screen

	ShowWindow(hwnd, SW_SHOWDEFAULT);
	UpdateWindow(hwnd);

	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	activea[pa->idx] = 0;
	free(pa);
}

void AlarmFunc(char *pname, char *pmsg, int alarmindex)
{
	static BOOL bFirst = 1;
	ADATA *p;
	WNDCLASSEX wcex;
	char tbuff[16];

	if (bFirst)
	{
		bFirst = 0;
		InitializeCriticalSection(&cs1);

		wcex.cbSize = sizeof(WNDCLASSEX); 

		wcex.style			= CS_HREDRAW | CS_VREDRAW;
		wcex.lpfnWndProc	= (WNDPROC)AlarmWndProc;
		wcex.cbClsExtra		= 0;
		wcex.cbWndExtra		= 0;
		wcex.hInstance		= NULL;
		wcex.hIcon			= NULL;
		wcex.hCursor		= LoadCursor(NULL, IDC_UPARROW);
	
		wcex.lpszMenuName	= NULL;
		wcex.lpszClassName	= szWindowClass;
		wcex.hIconSm		= NULL;

		wcex.hbrBackground	= CreateSolidBrush(POPBACKGROUND);
		RegisterClassEx(&wcex);

	}

	if (activea[alarmindex] == 0)					// if alarm is not already active
	{
		activea[alarmindex] = 1;
		p = calloc(1, sizeof(ADATA));				// set up data

		_strtime(tbuff);
		sprintf(p->msg, "%s - %s: %s", tbuff, pname, pmsg);

		p->idx = alarmindex;
		p->tim = time(NULL);
		_beginthread(AlarmThread, 0, (void *) p);	// start alarm thread
	}
}

void AlarmsOff()
{ 
	aoff = 1;
	Sleep(250);
}
void AlarmsOn()	
{ 
	aoff = 0;
}


Have fun ...





Copyright (c) Gary Baker July 2009 - All rights reserved