Source Code

bulletThe following code is provided purely for informational value, and remains at all times the property of RTV Software.
bulletThe source will not compile without the relevant resource and header files, which we will not be providing.
bulletThis file is from a very old version of RtvReco; newer versions are spread over multiple source files and hence too complicated to publish.
/*******************************************************************************************

	FILE:		RTV_RECO.C

	PURPOSE:	RTVReco Windows 95 button-pushing application

	VERSION:	4.2

	COPYRIGHT:	This program and source code is copyright RTV Software, 1995.
			You have no right to use this code, or any portion of it,
			without the express permission of RTV Software or Jeremy
			Gelber.

*******************************************************************************************/

#include <windows.h>  
#include <commctrl.h>   
#include "resource.h"  
#include "rtv_reco.h"   

HINSTANCE hInst;         

UINT uiPollTimer, uiBatchTimer, uiPollTimeout, uiBatchTimeout;
HWND hwndFound, hwndChild, hwndMain;
NOTIFYICONDATA tnd;
HICON hIcon;
int nStartPage = 0;
int nIndexFound = 0;

char szAppName[] = "RTVReco";   
char szIniFile[] = "RTV_RECO.INI";
 
/* Main information, which is saved in the .INI file */
WORD wActionsUsed;
DIALOGACTIONS DialogActions[MAX_ACTIONS];
BOOL bRunInvisible, bCurrentlyInvisible;
BOOL bEnabled = TRUE;

int CALLBACK WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow )
{

	MSG msg;
	HANDLE hAccelTable;
	HWND hwndPrevInstance;

	/* Figure out if we are already running */
	hwndPrevInstance = FindWindow( szAppName, szAppName );
	if( hwndPrevInstance ) {
		/* If we are running, invoke the other instance and then quit */
		PostMessage( hwndPrevInstance, WM_COMMAND, MAKEWPARAM( ID_OPTIONS, 0 ), (LPARAM) 0 );
		return( 0 );
	}

	if( !hPrevInstance ) {       
		if( !InitApplication( hInstance ) )  
			return( FALSE );     
	}

	if( !InitInstance( hInstance, nCmdShow ) ) {
		return( FALSE );
	}

	hAccelTable = LoadAccelerators( hInstance, MAKEINTRESOURCE( IDR_RTV_RECO ) );

	while( GetMessage( &msg, NULL, 0, 0 ) ) {
		if( !TranslateAccelerator( msg.hwnd, hAccelTable, &msg ) ) {
			TranslateMessage( &msg );
			DispatchMessage( &msg ); 
		}
	}

	DestroyIcon( hIcon );

	return( msg.wParam ); 

	lpCmdLine; // This will prevent 'unused formal parameter' warnings
}



BOOL InitApplication( HINSTANCE hInstance )
{

	WNDCLASS  wc;

	InitCommonControls();  /* Things like the new UI */

	hIcon = LoadIcon( hInstance, MAKEINTRESOURCE( IDI_APP ) );

	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = (WNDPROC) WndProc;       
	wc.cbClsExtra = 0;                      
	wc.cbWndExtra = 0;                      
	wc.hInstance = hInstance;             
	wc.hIcon = hIcon; 
	wc.hCursor = LoadCursor( NULL, IDC_ARROW );
	wc.hbrBackground = (HBRUSH) ( COLOR_WINDOW + 1 );
	wc.lpszMenuName = MAKEINTRESOURCE( IDR_MENU1 ); 
	wc.lpszClassName = szAppName;             

	return( RegisterClass( &wc ) );

}


BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
{

	HWND hWnd; 

	hInst = hInstance;

	hWnd = CreateWindow( szAppName, szAppName, WS_OVERLAPPEDWINDOW, 40, 40, 200, 200, NULL, NULL, hInstance, NULL );

	if( !hWnd ) 
		return( FALSE );

	hwndMain = hWnd;

	#ifdef NDEBUG
		ShowWindow( hWnd, SW_HIDE ); 
	#else
		ShowWindow( hWnd, SW_SHOWNORMAL );
	#endif
	UpdateWindow( hWnd );         

	return( TRUE );             

}


LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM uParam, LPARAM lParam )     
{

    int wmId, wmEvent;

    switch( message ) {
        case WM_COMMAND: 
			{
			wmId = LOWORD( uParam );
			wmEvent = HIWORD( uParam );

			if( wmId == ID_OPTIONS ) {

			CreatePropertySheet( hWnd );

			return( (LRESULT) NULL );

			}
			else
			if( wmId == ID_STATUS_ENABLED ) {

			bEnabled = TRUE;

			return( (LRESULT) NULL );

			}
			else
			if( wmId == ID_STATUS_DISABLED ) {

			bEnabled = FALSE;

			return( (LRESULT) NULL );

			}
			else
				return( DefWindowProc( hWnd, message, uParam, lParam ) );
			}
			break;

        case WM_TIMER:
			{

			int nCounter;
			DWORD dwCurrentTime;

			/* First find out if the timer loops are meant to do something */
			if( !bEnabled )
				return( 0 );
			
			/* Figure out the time of this message */
			dwCurrentTime = GetCurrentTime();

			/* Find out which timer we are responding to */
			if( uParam == ID_POLL_TIMER ) {
				for( nCounter = 0; nCounter < wActionsUsed; nCounter++ ) {
					if( !DialogActions[nCounter].bDelayPollAfterFind ) {
						RespondToPollTimerMessage( nCounter, dwCurrentTime );
					}  /* no delay needed */
					else {
						if( DialogActions[nCounter].dwLastFound ) {
							if( ( DialogActions[nCounter].dwLastFound + DialogActions[nCounter].uiDelayTime ) < dwCurrentTime ) {
								RespondToPollTimerMessage( nCounter, dwCurrentTime );
							}  /* we have waited long enough */
						}  /* we have found it before */
						else {
							RespondToPollTimerMessage( nCounter, dwCurrentTime );
						}  /* we haven't found it before */
					}  /* a delay was needed */
				}  /* for nCounter statement */
			}  /* this is the polling timer message */
			else
			if( uParam == ID_BATCH_TIMER ) {
				/* Run the program, and kill the timer */
				WinExec( DialogActions[nIndexFound].szBatchFile, SW_NORMAL );
				KillTimer( hWnd, uiBatchTimer );
			}  /* this is the batch program timer message */

			return( 0 );
			}
			break;

        case WM_CREATE:
			{

			/* Get the defaults, including timer values */
			ReadDefaultsFromIni();

			lstrcpy( tnd.szTip, szAppName );    
			tnd.uFlags = NIF_MESSAGE | NIF_ICON | NIF_TIP;
			tnd.uID = (UINT) IDI_WINLOGO;
			tnd.cbSize = sizeof( NOTIFYICONDATA );
			tnd.hWnd = hWnd;
			tnd.uCallbackMessage = TRAY_CALLBACK;
			tnd.hIcon = hIcon;
			
			if( !bCurrentlyInvisible )
				Shell_NotifyIcon( NIM_ADD, &tnd );

			uiPollTimer = SetTimer( hWnd, ID_POLL_TIMER, uiPollTimeout, NULL );

			return( 0 );
			}
			break;

        case WM_DESTROY:  // message: window being destroyed
			if( !bCurrentlyInvisible ) {
				tnd.uFlags = 0;
				Shell_NotifyIcon( NIM_DELETE, &tnd );
			}
			KillTimer( hWnd, uiPollTimer );
			PostQuitMessage( 0 );
			break;

        case TRAY_CALLBACK:      
            TrayCallback( hWnd, uParam, lParam );
            break;

        default:          // Passes it on if unprocessed
			return( DefWindowProc( hWnd, message, uParam, lParam ) );
    }
    return( 0 );

}

BOOL CALLBACK EnumChildProc( HWND hWnd, LPARAM lParam )
{

	char szText[50], szButtonToFind[100];
	
	/* Copy the button text to a local variable */
	if( lParam )
		lstrcpy( szButtonToFind, (LPSTR) lParam );
	else
		return( TRUE );

	/* Get the text of the currently enumerated child window */
	GetWindowText( hWnd, szText, sizeof( szText ) );

	/* Check for a match */
	if( !lstrcmp( szText, szButtonToFind ) ) {
		hwndChild = hWnd;
		return( FALSE );
	}
	
	/* No match, so keep enumerating */
	return( TRUE );

}

void TrayCallback( HWND hWnd, WPARAM wParam, LPARAM lParam )
{
	UINT uID;
	UINT uMouseMsg;

	uID = (UINT) wParam;
	uMouseMsg = (UINT) lParam;

	if( uMouseMsg == WM_LBUTTONDOWN ) {
		if( uID == (UINT) IDI_WINLOGO )	{
			CreatePropertySheet( hWnd );
		}
	}
	else
	if( uMouseMsg == WM_RBUTTONDOWN ) {
		if( uID == (UINT) IDI_WINLOGO )	{
			CreateRightClickMenu( hWnd );
		}
	}

	return;
}
  
void ReadDefaultsFromIni( void )
{

	char szText[50], szItem[50];
	char szOptions[] = "Options";
	int nCounter;
	BOOL bFirstRun;
	
	/* Global constants */
	GetPrivateProfileString( szOptions, "uiPollTimeout", "2500", szText, sizeof( szText ), szIniFile );
	uiPollTimeout = atoi( szText );
	GetPrivateProfileString( szOptions, "uiBatchTimeout", "10000", szText, sizeof( szText ), szIniFile );
	uiBatchTimeout = atoi( szText );
	GetPrivateProfileString( szOptions, "wActionsUsed", "0", szText, sizeof( szText ), szIniFile );
	wActionsUsed = atoi( szText );
	GetPrivateProfileString( szOptions, "bRunInvisible", "0", szText, sizeof( szText ), szIniFile );
	bRunInvisible = (BOOL) atoi( szText );
	bCurrentlyInvisible = bRunInvisible;

	/* Have we run this program before? */
	GetPrivateProfileString( szOptions, "bFirstRun", "1", szText, sizeof( szText ), szIniFile );
	bFirstRun = (BOOL) atoi( szText );
	if( bFirstRun ) {
		wActionsUsed = 1;
		lstrcpy( DialogActions[0].szBatchFile, "C:\\DIAL.BAT" );
		lstrcpy( DialogActions[0].szDialogTitle, "Reestablish Connection" );
		lstrcpy( DialogActions[0].szButtonName, "Reconnect..." );
		DialogActions[0].wActionToPerform = OA_PUSH_BUTTON;
		DialogActions[0].bRunBatchFile = FALSE;
	}  /* it was the first time we have run */
	else {
		/* Individual dialog box options */
		for( nCounter = 0; nCounter < wActionsUsed; nCounter++ ) {
			wsprintf( szItem, "wActionToPerform%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "0", szText, sizeof( szText ), szIniFile );
			DialogActions[nCounter].wActionToPerform = (WORD) atoi( szText );
			wsprintf( szItem, "bRunBatchFile%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "0", szText, sizeof( szText ), szIniFile );
			DialogActions[nCounter].bRunBatchFile = (BOOL) atoi( szText );
			wsprintf( szItem, "bDelayPollAfterFind%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "0", szText, sizeof( szText ), szIniFile );
			DialogActions[nCounter].bDelayPollAfterFind = (BOOL) atoi( szText );
			wsprintf( szItem, "bExactMenuItem%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "1", szText, sizeof( szText ), szIniFile );
			DialogActions[nCounter].bExactMenuItem = (BOOL) atoi( szText );
			wsprintf( szItem, "uiDelayTime%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "10000", szText, sizeof( szText ), szIniFile );
			DialogActions[nCounter].uiDelayTime = (UINT) atoi( szText );
			wsprintf( szItem, "szBatchFile%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "", DialogActions[nCounter].szBatchFile, sizeof( DialogActions[nCounter].szBatchFile ), szIniFile );
			wsprintf( szItem, "szDialogTitle%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "", DialogActions[nCounter].szDialogTitle, sizeof( DialogActions[nCounter].szDialogTitle ), szIniFile );
			wsprintf( szItem, "szButton%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "", DialogActions[nCounter].szButtonName, sizeof( DialogActions[nCounter].szButtonName ), szIniFile );
			wsprintf( szItem, "szPopupMenu%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "", DialogActions[nCounter].szPopupMenu, sizeof( DialogActions[nCounter].szPopupMenu ), szIniFile );
			wsprintf( szItem, "szMenuItem%d", nCounter );
			GetPrivateProfileString( szOptions, szItem, "", DialogActions[nCounter].szMenuItem, sizeof( DialogActions[nCounter].szMenuItem ), szIniFile );
		}  /* for nCounter statement */
	}  /* we have run RTVReco before */
	
	return;

}

void WriteDefaultsToIni( void )
{

	char szText[50], szItem[50];
	char szOptions[] = "Options";
	int nCounter;

	/* Global constants */
	itoa( uiPollTimeout, szText, 10 );
	WritePrivateProfileString( szOptions, "uiPollTimeout", szText, szIniFile );
	itoa( uiBatchTimeout, szText, 10 );
	WritePrivateProfileString( szOptions, "uiBatchTimeout", szText, szIniFile );
	itoa( wActionsUsed, szText, 10 );
	WritePrivateProfileString( szOptions, "wActionsUsed", szText, szIniFile );
	itoa( bRunInvisible, szText, 10 );
	WritePrivateProfileString( szOptions, "bRunInvisible", szText, szIniFile );

	/* It's obviously not our first run now, so set that up in the .INI file */
	WritePrivateProfileString( "Options", "bFirstRun", "0", szIniFile );

	/* Individual dialog box options */
	for( nCounter = 0; nCounter < wActionsUsed; nCounter++ ) {
		wsprintf( szItem, "wActionToPerform%d", nCounter );
		itoa( DialogActions[nCounter].wActionToPerform, szText, 10 );
		WritePrivateProfileString( szOptions, szItem, szText, szIniFile );
		wsprintf( szItem, "bRunBatchFile%d", nCounter );
		itoa( DialogActions[nCounter].bRunBatchFile, szText, 10 );
		WritePrivateProfileString( szOptions, szItem, szText, szIniFile );
		wsprintf( szItem, "bDelayPollAfterFind%d", nCounter );
		itoa( DialogActions[nCounter].bDelayPollAfterFind, szText, 10 );
		WritePrivateProfileString( szOptions, szItem, szText, szIniFile );
		wsprintf( szItem, "bExactMenuItem%d", nCounter );
		itoa( DialogActions[nCounter].bExactMenuItem, szText, 10 );
		WritePrivateProfileString( szOptions, szItem, szText, szIniFile );
		wsprintf( szItem, "uiDelayTime%d", nCounter );
		itoa( DialogActions[nCounter].uiDelayTime, szText, 10 );
		WritePrivateProfileString( szOptions, szItem, szText, szIniFile );
		wsprintf( szItem, "szBatchFile%d", nCounter );
		WritePrivateProfileString( szOptions, szItem, DialogActions[nCounter].szBatchFile, szIniFile );
		wsprintf( szItem, "szDialogTitle%d", nCounter );
		WritePrivateProfileString( szOptions, szItem, DialogActions[nCounter].szDialogTitle, szIniFile );
		wsprintf( szItem, "szButton%d", nCounter );
		WritePrivateProfileString( szOptions, szItem, DialogActions[nCounter].szButtonName, szIniFile );
		wsprintf( szItem, "szPopupMenu%d", nCounter );
		WritePrivateProfileString( szOptions, szItem, DialogActions[nCounter].szPopupMenu, szIniFile );
		wsprintf( szItem, "szMenuItem%d", nCounter );
		WritePrivateProfileString( szOptions, szItem, DialogActions[nCounter].szMenuItem, szIniFile );
	}  /* for nCounter statement */

	return;

}

int CreatePropertySheet( HWND hwndOwner )
{

	#define NUMBER_OF_TABS	7
	
	int nCounter;
	PROPSHEETPAGE psp[NUMBER_OF_TABS];
	PROPSHEETHEADER psh;
             
	/* Common properties */
	for( nCounter = 0; nCounter < NUMBER_OF_TABS; nCounter++ ) {
		psp[nCounter].dwSize = sizeof( PROPSHEETPAGE );
		psp[nCounter].dwFlags = PSP_USETITLE;
	 	psp[nCounter].hInstance = hInst;
	 	psp[nCounter].pszIcon = NULL;
		psp[nCounter].lParam = 0;
	}  /* for nCounter statement */

	psp[0].pszTemplate = MAKEINTRESOURCE( IDD_INSTRUCTIONS_TAB );
	psp[0].pfnDlgProc = InstructionsTabDB;
	psp[0].pszTitle = "Instructions";

	psp[1].pszTemplate = MAKEINTRESOURCE( IDD_ACTIONS_TAB );
	psp[1].pfnDlgProc = ActionsTabDB;
	psp[1].pszTitle = "Actions";

	psp[2].pszTemplate = MAKEINTRESOURCE( IDD_TIMINGS_TAB );
	psp[2].pfnDlgProc = TimingsTabDB;
	psp[2].pszTitle = "Timings";

	psp[3].pszTemplate = MAKEINTRESOURCE( IDD_ABOUT_TAB );
	psp[3].pfnDlgProc = AboutTabDB;
	psp[3].pszTitle = "About";

	psp[4].pszTemplate = MAKEINTRESOURCE( IDD_DONATIONS_TAB );
	psp[4].pfnDlgProc = DonationsTabDB;
	psp[4].pszTitle = "Donations";

	psp[5].pszTemplate = MAKEINTRESOURCE( IDD_CREDITS_TAB );
	psp[5].pfnDlgProc = CreditsTabDB;
	psp[5].pszTitle = "Credits";

	psp[6].pszTemplate = MAKEINTRESOURCE( IDD_SPECIAL_TAB );
	psp[6].pfnDlgProc = SpecialTabDB;
	psp[6].pszTitle = "Special";

	psh.dwSize = sizeof( PROPSHEETHEADER );
	psh.dwFlags = PSH_PROPSHEETPAGE;
	psh.hwndParent = hwndOwner;	 //hwndOwner
	psh.hInstance = hInst;
	psh.pszIcon = NULL;
	psh.pszCaption = (LPSTR) "RTVReco Properties";
	psh.nPages = NUMBER_OF_TABS;
	psh.ppsp = (LPCPROPSHEETPAGE) &psp;
	psh.nStartPage = nStartPage;

	/* This function doesn't return until the prop sheet is dismissed */
	PropertySheet( &psh );

	/* Now check if we should be invisible */
	if( bCurrentlyInvisible != bRunInvisible ) {
		if( bCurrentlyInvisible ) {
			/* Make it visible */
			bCurrentlyInvisible = FALSE;
			Shell_NotifyIcon( NIM_ADD, &tnd );
		}
		else {
			/* Make it invisible */
			bCurrentlyInvisible = TRUE;
			Shell_NotifyIcon( NIM_DELETE, &tnd );
		}
	} /* we should be invisible */
		
	return( 0 );

}		 

LRESULT CALLBACK AboutTabDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	switch( message ) {
		case WM_INITDIALOG:  
			
			nStartPage = 3;

			return( TRUE );
	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

LRESULT CALLBACK CreditsTabDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	switch( message ) {
		case WM_INITDIALOG:  

			nStartPage = 5;

			return( TRUE );

	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

LRESULT CALLBACK InstructionsTabDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	switch( message ) {
		case WM_INITDIALOG:  

			nStartPage = 0;
			
			return( TRUE );

	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

LRESULT CALLBACK TimingsTabDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	switch( message ) {
		case WM_INITDIALOG:  
			
			nStartPage = 2;

			return( TRUE );
	case WM_HSCROLL:                      
		{

		UINT uiTimeout;
		char szTimeout[50];
		
		uiTimeout = SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );
		wsprintf( szTimeout, "%d", (int) uiTimeout );
		SetDlgItemText( hDlg, IDC_POLL_TEXT, szTimeout );
		
		uiTimeout = SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );
		wsprintf( szTimeout, "%d", (int) uiTimeout );
		SetDlgItemText( hDlg, IDC_BATCH_TEXT, szTimeout );
		
		return( TRUE );

		}
		break;
    case WM_NOTIFY:
        switch( ((NMHDR FAR *) lParam)->code ) {
            case PSN_SETACTIVE:
				SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_SETRANGE, TRUE, MAKELPARAM( 1, 60 ) );
				SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_SETTICFREQ, 10, 10 );
				SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) ( uiPollTimeout / 1000 ) );
				SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_SETRANGE, TRUE, MAKELPARAM( 1, 60 ) );
				SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_SETTICFREQ, 10, 10 );
				SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) ( uiBatchTimeout / 1000 ) );
				PostMessage( hDlg, WM_HSCROLL, 0, 0 );                      
				break;
            case PSN_APPLY:
				uiPollTimeout = 1000 * SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );
				uiBatchTimeout = 1000 * SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );
				/* Restart the timer with the new setting */
				KillTimer( hwndMain, uiPollTimer );
				uiPollTimer = SetTimer( hwndMain, ID_POLL_TIMER, uiPollTimeout, NULL );
				WriteDefaultsToIni();
                SetWindowLong( hDlg, DWL_MSGRESULT, TRUE );
                break;
            case PSN_KILLACTIVE:  /* Save the stuff here as well */
				uiPollTimeout = 1000 * SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );
				uiBatchTimeout = 1000 * SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );
				/* Restart the timer with the new setting */
				KillTimer( hwndMain, uiPollTimer );
				uiPollTimer = SetTimer( hwndMain, ID_POLL_TIMER, uiPollTimeout, NULL );
				WriteDefaultsToIni();
				SetWindowLong( hDlg, DWL_MSGRESULT, FALSE );
                return( 1 );
                break;
            case PSN_RESET:
                // Reset to the original values.
				SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_SETRANGE, TRUE, MAKELPARAM( 1, 60 ) );
				SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_SETTICFREQ, 10, 10 );
				SendDlgItemMessage( hDlg, IDC_POLL_TRACK, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) ( uiPollTimeout / 1000 ) );
				SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_SETRANGE, TRUE, MAKELPARAM( 1, 60 ) );
				SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_SETTICFREQ, 10, 10 );
				SendDlgItemMessage( hDlg, IDC_BATCH_TRACK, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) ( uiBatchTimeout / 1000 ) );
                SetWindowLong( hDlg, DWL_MSGRESULT, FALSE );
                break;
    }  /* end of switch ((NMHDR FAR *) lParam)->code statement */

	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

LRESULT CALLBACK SpecialTabDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	switch( message ) {
		case WM_INITDIALOG:  
			nStartPage = 6;
			return( TRUE );

	case WM_COMMAND:                      
		if( LOWORD( uParam ) == ID_EXIT_PROGRAM ) {  

		PostMessage( hwndMain, WM_DESTROY, 0, 0 );
		EndDialog( hDlg, TRUE );       

		return( TRUE );

		}
		break;
    case WM_NOTIFY:
        switch( ((NMHDR FAR *) lParam)->code ) {
            case PSN_SETACTIVE:
				CheckDlgButton( hDlg, IDC_INVISIBLE, bRunInvisible );
				break;
            case PSN_APPLY:
				bRunInvisible = IsDlgButtonChecked( hDlg, IDC_INVISIBLE );
				WriteDefaultsToIni();
                SetWindowLong( hDlg, DWL_MSGRESULT, TRUE );
                break;
            case PSN_KILLACTIVE:  /* Save the stuff here as well */
				bRunInvisible = IsDlgButtonChecked( hDlg, IDC_INVISIBLE );
				WriteDefaultsToIni();
				SetWindowLong( hDlg, DWL_MSGRESULT, FALSE );
                return( 1 );
                break;
            case PSN_RESET:
                // Reset to the original values; not applicable for this app.
                SetWindowLong( hDlg, DWL_MSGRESULT, FALSE );
                break;
    	}  /* end of switch ((NMHDR FAR *) lParam)->code statement */

	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

LRESULT CALLBACK ActionsTabDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	switch( message ) {
		case WM_INITDIALOG:  
			nStartPage = 1;
			return( TRUE );

	case WM_COMMAND:                      
		if( LOWORD( uParam ) == IDC_ADD ) {  

		/* Check that we haven't over-run out bounds */
		if( wActionsUsed >= MAX_ACTIONS ) {
			MessageBox( hDlg, "You have run out of space to add another action. Please delete one or more actions before adding any more.", szAppName, MB_ICONSTOP );
			return( TRUE );
		}
		
		if( DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_DIALOG_ACTION ), hDlg, DialogActionDB, (LPARAM) &DialogActions[wActionsUsed] ) ) {
			wActionsUsed++;
			PostMessage( hDlg, WM_COMMAND, MAKEWPARAM( IDC_UPDATE, 0 ), (LPARAM) 0L );
		}  /* the user pressed "OK" */

		return( TRUE );

		}
		else
		if( LOWORD( uParam ) == IDC_CHANGE ) {  

		WORD wWhichAction;
		
		wWhichAction = (WORD) SendDlgItemMessage( hDlg, IDC_ACTIONS, LB_GETCURSEL, 0, 0 );

		if( DialogBoxParam( hInst, MAKEINTRESOURCE( IDD_DIALOG_ACTION ), hDlg, DialogActionDB, (LPARAM) &DialogActions[wWhichAction] ) ) {
			PostMessage( hDlg, WM_COMMAND, MAKEWPARAM( IDC_UPDATE, 0 ), (LPARAM) 0L );
		}  /* the user pressed "OK" */

		return( TRUE );

		}
		else
		if( LOWORD( uParam ) == IDC_REMOVE ) {  

		WORD wWhichAction;
		char szListBoxItem[150], szMessageBoxText[250];
		int nCounter;

		wWhichAction = (WORD) SendDlgItemMessage( hDlg, IDC_ACTIONS, LB_GETCURSEL, 0, 0 );
		SendDlgItemMessage( hDlg, IDC_ACTIONS, LB_GETTEXT, wWhichAction, (LPARAM) (LPSTR) szListBoxItem );

		wsprintf( szMessageBoxText, "Are you sure you want to remove the '%s' action?", (LPSTR) szListBoxItem );

		if( IDYES == MessageBox( hDlg, szMessageBoxText, szAppName, MB_ICONQUESTION | MB_YESNO ) ) {
			/* The user has authorised us to remove the item */
			wActionsUsed--;
			for( nCounter = wWhichAction; nCounter < wActionsUsed; nCounter++ ) 
				DialogActions[nCounter] = DialogActions[nCounter+1];
			/* Set the structure at the end to zero */
			memset( &DialogActions[wActionsUsed], 0, sizeof( DialogActions[wActionsUsed] ) );
			PostMessage( hDlg, WM_COMMAND, MAKEWPARAM( IDC_UPDATE, 0 ), (LPARAM) 0L );
		}

		return( TRUE );

		}
		else
		if( LOWORD( uParam ) == IDC_ACTIONS ) {  

		DWORD dwCurSel;
		BOOL bSomethingSelected;
		
		dwCurSel = SendDlgItemMessage( hDlg, IDC_ACTIONS, LB_GETCURSEL, 0, 0 );

		if( dwCurSel != LB_ERR )
			bSomethingSelected = TRUE;
		else
			bSomethingSelected = FALSE;

		EnableWindow( GetDlgItem( hDlg, IDC_CHANGE ), bSomethingSelected );
		EnableWindow( GetDlgItem( hDlg, IDC_REMOVE ), bSomethingSelected );

		if( bSomethingSelected ) {
			if( HIWORD( uParam ) == LBN_DBLCLK ) {
				PostMessage( hDlg, WM_COMMAND, MAKEWPARAM( IDC_CHANGE, 0 ), (LPARAM) 0L );
			}  /* we received a double-click message */
		}  /* something is selected */

		return( TRUE );

		}
		else
		if( LOWORD( uParam ) == IDC_UPDATE ) {  

		int nCounter;
		char szListItem[150], szUsedAndLeft[100];
		
		SendDlgItemMessage( hDlg, IDC_ACTIONS, LB_RESETCONTENT, 0, 0 );

		for( nCounter = 0; nCounter < wActionsUsed; nCounter++ ) {
			switch( DialogActions[nCounter].wActionToPerform ) {
				case OA_PUSH_BUTTON:
					lstrcpy( szListItem, "Push a button in " );
					break;
				case OA_MINIMIZE:
					lstrcpy( szListItem, "Minimise " );
					break;
				case OA_MAXIMIZE:
					lstrcpy( szListItem, "Maximise " );
					break;
				case OA_CHOOSE_MENU:					   
					lstrcpy( szListItem, "Select a menu in " );
					break;
				case OA_NO_ACTION:					   
					lstrcpy( szListItem, "Perform no action, but poll for " );
					break;
			}  /* end of switch wActionToPerform statement */
			lstrcat( szListItem, "\"" );
			lstrcat( szListItem, DialogActions[nCounter].szDialogTitle );
			lstrcat( szListItem, "\"" );
			SendDlgItemMessage( hDlg, IDC_ACTIONS, LB_ADDSTRING, 0, (LPARAM) (LPSTR) szListItem );
		}  /* for nCounter statement */

		wsprintf( szUsedAndLeft, "Actions To Be &Performed (%d Out Of A Maximum %d):", (int) wActionsUsed, (int) MAX_ACTIONS );
		SetDlgItemText( hDlg, IDC_USED_AND_LEFT, szUsedAndLeft );

		PostMessage( hDlg, WM_COMMAND, MAKEWPARAM( IDC_ACTIONS, 0 ), (LPARAM) 0L );

		return( TRUE );

		}
		else
		break;

    case WM_NOTIFY:
        switch( ((NMHDR FAR *) lParam)->code ) {
            case PSN_SETACTIVE:
				PostMessage( hDlg, WM_COMMAND, MAKEWPARAM( IDC_UPDATE, 0 ), (LPARAM) 0L );
				PostMessage( hDlg, WM_COMMAND, MAKEWPARAM( IDC_ACTIONS, 0 ), (LPARAM) 0L );
				break;
            case PSN_APPLY:
				WriteDefaultsToIni();
                SetWindowLong( hDlg, DWL_MSGRESULT, TRUE );
                break;
            case PSN_KILLACTIVE:  /* Save the stuff here as well */
				WriteDefaultsToIni();
				SetWindowLong( hDlg, DWL_MSGRESULT, FALSE );
                return( 1 );
                break;
            case PSN_RESET:
                // Reset to the original values; not applicable for this app.
                SetWindowLong( hDlg, DWL_MSGRESULT, FALSE );
                break;
    }  /* end of switch ((NMHDR FAR *) lParam)->code statement */

	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

LRESULT CALLBACK DialogActionDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	static PDIALOGACTIONS pDA;
	
	switch( message ) {
		case WM_INITDIALOG:
		
			pDA = (PDIALOGACTIONS) lParam;

			/* Figure out the action we want to perform */
			switch( pDA->wActionToPerform ) {
				case OA_PUSH_BUTTON:
					CheckDlgButton( hDlg, IDC_PUSH_THE_BUTTON, TRUE );
					break;
				case OA_MINIMIZE:
					CheckDlgButton( hDlg, IDC_MINIMISE, TRUE );
					break;
				case OA_MAXIMIZE:
					CheckDlgButton( hDlg, IDC_MAXIMISE, TRUE );
					break;
				case OA_CHOOSE_MENU:
					CheckDlgButton( hDlg, IDC_CHOOSE_MENU, TRUE );
					break;
				case OA_NO_ACTION:
					CheckDlgButton( hDlg, IDC_NO_ACTION, TRUE );
					break;
			}  /* end of switch wActionToPerform statement */

			/* Add the strings we have */
			SetDlgItemText( hDlg, IDC_TITLE, pDA->szDialogTitle );
			SetDlgItemText( hDlg, IDC_BUTTON, pDA->szButtonName );
			SetDlgItemText( hDlg, IDC_MENU_ITEM, pDA->szMenuItem );
			SetDlgItemText( hDlg, IDC_POPUP_MENU, pDA->szPopupMenu );
			SetDlgItemText( hDlg, IDC_BATCH_FILE, pDA->szBatchFile );
			
			/* Put in the tick frequency for our trackbar */
			SendDlgItemMessage( hDlg, IDC_POLL_DELAY_TRACK, TBM_SETRANGE, TRUE, MAKELPARAM( 10, 240 ) );
			SendDlgItemMessage( hDlg, IDC_POLL_DELAY_TRACK, TBM_SETTICFREQ, 60, 60 );
			SendDlgItemMessage( hDlg, IDC_POLL_DELAY_TRACK, TBM_SETPOS, (WPARAM) TRUE, (LPARAM) ( pDA->uiDelayTime / 1000 ) );

			/* Update display */
			EnableWindow( GetDlgItem( hDlg, IDC_BUTTON ), IsDlgButtonChecked( hDlg, IDC_PUSH_THE_BUTTON ) );
			EnableWindow( GetDlgItem( hDlg, IDC_MENU_ITEM ), IsDlgButtonChecked( hDlg, IDC_CHOOSE_MENU ) );
			EnableWindow( GetDlgItem( hDlg, IDC_POPUP_MENU ), IsDlgButtonChecked( hDlg, IDC_CHOOSE_MENU ) );
			CheckDlgButton( hDlg, IDC_RUN_BATCH, pDA->bRunBatchFile );
			CheckDlgButton( hDlg, IDC_DELAY_POLL, pDA->bDelayPollAfterFind );
			CheckDlgButton( hDlg, IDC_EXACT_MENU_ITEM, pDA->bExactMenuItem );
   			EnableWindow( GetDlgItem( hDlg, IDC_BATCH_FILE ), IsDlgButtonChecked( hDlg, IDC_RUN_BATCH ) );
			PostMessage( hDlg, WM_HSCROLL, 0, 0 );                      
			  
			return( TRUE );

	case WM_COMMAND:                      
		if( LOWORD( uParam ) == IDOK ) {  

		/* Get out values out, and signal that we need to store them */
		GetDlgItemText( hDlg, IDC_TITLE, pDA->szDialogTitle, sizeof( pDA->szDialogTitle ) );
		GetDlgItemText( hDlg, IDC_BUTTON, pDA->szButtonName, sizeof( pDA->szButtonName ) );
		GetDlgItemText( hDlg, IDC_MENU_ITEM, pDA->szMenuItem, sizeof( pDA->szMenuItem ) );
		GetDlgItemText( hDlg, IDC_POPUP_MENU, pDA->szPopupMenu, sizeof( pDA->szPopupMenu ) );
		GetDlgItemText( hDlg, IDC_BATCH_FILE, pDA->szBatchFile, sizeof( pDA->szBatchFile ) );
		
		if( IsDlgButtonChecked( hDlg, IDC_PUSH_THE_BUTTON ) )
			pDA->wActionToPerform = OA_PUSH_BUTTON;
		else 
		if( IsDlgButtonChecked( hDlg, IDC_MINIMISE ) )
			pDA->wActionToPerform = OA_MINIMIZE;
		else 
		if( IsDlgButtonChecked( hDlg, IDC_MAXIMISE ) )
			pDA->wActionToPerform = OA_MAXIMIZE;
		else 
		if( IsDlgButtonChecked( hDlg, IDC_CHOOSE_MENU ) )
			pDA->wActionToPerform = OA_CHOOSE_MENU;
		else 
		if( IsDlgButtonChecked( hDlg, IDC_NO_ACTION ) )
			pDA->wActionToPerform = OA_NO_ACTION;
		else  /* Just in case!!! */ 
			pDA->wActionToPerform = OA_PUSH_BUTTON;

		pDA->bRunBatchFile = IsDlgButtonChecked( hDlg, IDC_RUN_BATCH );
		pDA->bDelayPollAfterFind = IsDlgButtonChecked( hDlg, IDC_DELAY_POLL );
		pDA->bExactMenuItem = IsDlgButtonChecked( hDlg, IDC_EXACT_MENU_ITEM );
		pDA->uiDelayTime = 1000 * SendDlgItemMessage( hDlg, IDC_POLL_DELAY_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );

		EndDialog( hDlg, TRUE );       

		return( TRUE );

		}
		else
		if( LOWORD( uParam ) == IDCANCEL ) {  

		EndDialog( hDlg, FALSE );       

		return( TRUE );

		}
		else
		if( LOWORD( uParam ) == IDC_PUSH_THE_BUTTON || LOWORD( uParam ) == IDC_CHOOSE_MENU || LOWORD( uParam ) == IDC_MINIMISE || LOWORD( uParam ) == IDC_MAXIMISE || LOWORD( uParam ) == IDC_NO_ACTION ) {  

		EnableWindow( GetDlgItem( hDlg, IDC_BUTTON ), IsDlgButtonChecked( hDlg, IDC_PUSH_THE_BUTTON ) );
		EnableWindow( GetDlgItem( hDlg, IDC_MENU_ITEM ), IsDlgButtonChecked( hDlg, IDC_CHOOSE_MENU ) );
		EnableWindow( GetDlgItem( hDlg, IDC_POPUP_MENU ), IsDlgButtonChecked( hDlg, IDC_CHOOSE_MENU ) );

		return( TRUE );

		}
		else
		if( LOWORD( uParam ) == IDC_RUN_BATCH ) {  

		EnableWindow( GetDlgItem( hDlg, IDC_BATCH_FILE ), IsDlgButtonChecked( hDlg, IDC_RUN_BATCH ) );

		return( TRUE );

		}
		break;
	case WM_HSCROLL:                      
		{

		UINT uiTimeout;
		char szTimeout[50];
		
		uiTimeout = SendDlgItemMessage( hDlg, IDC_POLL_DELAY_TRACK, TBM_GETPOS, (WPARAM) 0, (LPARAM) 0 );
		wsprintf( szTimeout, "%d Secs", (int) uiTimeout );
		SetDlgItemText( hDlg, IDC_DELAY_POLL_TIME, szTimeout );
		
		return( TRUE );

		}
		break;
	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

LRESULT CALLBACK DonationsTabDB( HWND hDlg, UINT message, WPARAM uParam, LPARAM lParam )
{

	switch( message ) {
		case WM_INITDIALOG:  

			nStartPage = 4;
			
			return( TRUE );

	}
	return( FALSE ); 

	lParam; // This will prevent 'unused formal parameter' warnings
}

void Action_PushButton( HWND hWnd, PDIALOGACTIONS pDialogAction )
{

	LONG lControlID;
	
	/* Set the child window to NULL */
	hwndChild = NULL;

	/* Look for a child window matching the specified text */
	EnumChildWindows( hwndFound, EnumChildProc, (LPARAM) (LPSTR) pDialogAction->szButtonName );

	/* If we found it, then press it */
	if( hwndChild )	{
		lControlID = GetWindowLong( hwndChild, GWL_ID );
		SendMessage( hwndFound, WM_COMMAND, MAKEWPARAM( lControlID, BN_CLICKED ), (LPARAM) hwndChild );
	}

	return;

}

void Action_Minimise( HWND hWnd, PDIALOGACTIONS pDialogAction )
{

	POINT ptCursorPos;

	/* This isn't strictly necessary, but is here for completeness */
	GetCursorPos( &ptCursorPos );
	
	PostMessage( hWnd, WM_SYSCOMMAND, SC_MINIMIZE, MAKELPARAM( ptCursorPos.x, ptCursorPos.y ) );

	return;

}

void Action_Maximise( HWND hWnd, PDIALOGACTIONS pDialogAction )
{

	POINT ptCursorPos;

	/* This isn't strictly necessary, but is here for completeness */
	GetCursorPos( &ptCursorPos );
	
	PostMessage( hWnd, WM_SYSCOMMAND, SC_MAXIMIZE, MAKELPARAM( ptCursorPos.x, ptCursorPos.y ) );

	return;

}

void Action_ChooseMenu( HWND hWnd, PDIALOGACTIONS pDialogAction )
{

	HMENU hMenu, hSubMenu;
	int nSubMenuCounter, nMenuItemCounter, nGetMenuStringReturn;
	char szMenuItemText[100];
	WORD wMenuID;

	hMenu = GetMenu( hWnd );

	if( hMenu ) {
		nSubMenuCounter = 0;
		hSubMenu = GetSubMenu( hMenu, nSubMenuCounter );
		while( hSubMenu ) {
			GetMenuString( hMenu, nSubMenuCounter, szMenuItemText, sizeof( szMenuItemText ), MF_BYPOSITION );
			if( !lstrcmp( szMenuItemText, pDialogAction->szPopupMenu ) ) {
				nMenuItemCounter = 0;
				nGetMenuStringReturn = GetMenuString( hSubMenu, nMenuItemCounter, szMenuItemText, sizeof( szMenuItemText ), MF_BYPOSITION );
				while( nGetMenuStringReturn ) {
					if( MatchString( pDialogAction->szMenuItem, szMenuItemText, pDialogAction->bExactMenuItem ) ) {
						wMenuID = GetMenuItemID( hSubMenu, nMenuItemCounter );
						/* Make the window active, then click the menu */
						if( IsIconic( hWnd ) ) {
							POINT ptCursorPos;
							GetCursorPos( &ptCursorPos );
							SendMessage( hWnd, WM_SYSCOMMAND, SC_RESTORE, MAKELPARAM( ptCursorPos.x, ptCursorPos.y ) );
						}  /* the window was an icon, so restore it */
						SetForegroundWindow( hWnd );
						PostMessage( hWnd, WM_COMMAND, MAKEWPARAM( wMenuID, 0 ), (LPARAM) NULL );
						return;
					}  /* e matched the menu item string */
					nMenuItemCounter++;
					nGetMenuStringReturn = GetMenuString( hSubMenu, nMenuItemCounter, szMenuItemText, sizeof( szMenuItemText ), MF_BYPOSITION );
				}  /* we found a string */
			}  /* we matched the popup menu string */
			nSubMenuCounter++;
			hSubMenu = GetSubMenu( hMenu, nSubMenuCounter );
		}  /* we found a sub-menu */
	}  /* we found a main menu */
		
	return;

}

void RespondToPollTimerMessage( int nAction, DWORD dwCurrentTime )
{

	/* Try to find the window */
	hwndFound = FindWindow( NULL, DialogActions[nAction].szDialogTitle );

	if( hwndFound ) {
		/* Record the time we found this window */
		DialogActions[nAction].dwLastFound = dwCurrentTime;
		/* Figure out the action to take */
		switch( DialogActions[nAction].wActionToPerform ) {
			case OA_PUSH_BUTTON:
				Action_PushButton( hwndFound, &DialogActions[nAction] );
				break;
			case OA_MINIMIZE:
				Action_Minimise( hwndFound, &DialogActions[nAction] );
				break;
			case OA_MAXIMIZE:
				Action_Maximise( hwndFound, &DialogActions[nAction] );
				break;
			case OA_CHOOSE_MENU:					   
				Action_ChooseMenu( hwndFound, &DialogActions[nAction] );
				break;
			case OA_NO_ACTION:
				/* do nothing, obviously! */					   
				break;
		}  /* end of switch wActionToPerform statement */
		/* Check to see if we also need to run a batch file */
		if( DialogActions[nAction].bRunBatchFile ) { 
			nIndexFound = nAction;
			uiBatchTimer = SetTimer( hwndMain, ID_BATCH_TIMER, uiBatchTimeout, NULL );
		}  /* we need to run a batch file */
	}  /* we found the specified window */

	return;

}

void CreateRightClickMenu( HWND hwndOwner )
{

	POINT ptCursor;
	HMENU hPopupMenu;
	
	/* Create a popup menu and add some items */
	hPopupMenu = CreatePopupMenu();
	InsertMenu( hPopupMenu, 0, MF_STRING, ID_STATUS_ENABLED, "Enabled" );
	InsertMenu( hPopupMenu, 0, MF_STRING, ID_STATUS_DISABLED, "Disabled" );
	
	/* Put the status check-mark in */
	if( bEnabled )
		CheckMenuItem( hPopupMenu, ID_STATUS_ENABLED, MF_BYCOMMAND | MF_CHECKED );
	else
		CheckMenuItem( hPopupMenu, ID_STATUS_DISABLED, MF_BYCOMMAND | MF_CHECKED );
		
	/* Figure out where we are */
	GetCursorPos( &ptCursor );

	/* Display the menu */
	TrackPopupMenu( hPopupMenu, TPM_LEFTALIGN | TPM_RIGHTBUTTON, ptCursor.x, ptCursor.y, 0, hwndOwner, NULL );
	
	return;

}

BOOL MatchString( PSTR pszString, PSTR pszWholeString, BOOL bExactMatch )
{

	int nStringLength, nWholeStringLength, nCounter, nCounter2;
	char szString[50], szWholeString[200];
	BOOL bTemp;

	/* If we have to do an exact match, do it first and then return */
	if( bExactMatch ) 
		return( !lstrcmp( pszString, pszWholeString ) );

	/* We are still here, so first try doing an exact match */
	if( !lstrcmp( pszString, pszWholeString ) )
		return( TRUE );

	/* Now an non-case sensitive match */
	if( !lstrcmpi( pszString, pszWholeString ) )
		return( TRUE );

	/* Now a "string-in-string" match; need local copies for this */
	nStringLength = lstrlen( pszString );
	nWholeStringLength = lstrlen( pszWholeString );
	lstrcpy( szString, pszString );
	lstrcpy( szWholeString, pszWholeString );
	for( nCounter = 0; nCounter < ( nWholeStringLength - nStringLength ); nCounter++ ) {
		bTemp = TRUE;
		for( nCounter2 = 0; nCounter2 < nStringLength; nCounter2++ ) {
			if( szString[nCounter2] != szWholeString[nCounter+nCounter2] )
				bTemp = FALSE;
		}  /* for nCounter2 statement */
		if( bTemp )
			return( TRUE );
	}  /* for nCounter statement */
	
	/* Still here? We didn't find a match */
	return( FALSE );

}

 

Send mail to webmaster@rtvsoft.com with questions or comments about this web site.
Copyright 2003 RTV Software
Last modified: Tuesday, 25 February 2003 07:25