// bootrund.cpp : コンソール アプリケーションのエントリ ポイントを定義します。
//

#include "stdafx.h"
#include "bootrund.h"

#define APP_NAME "スタートアップレジストリ監視"
#define TIMER_ID	3639
#define ST_ICON_ID	58971
#define MYWM_NOTIFYICON 0x400
#define CO_STATUS	1
#define CO_TURNOFF	2
#define CO_TURNON	3

#define VERSION		"1.10"
#define C_YEAR		"2009"

// グローバル変数 :
HINSTANCE hInst;								// 現在のインターフェイス
const char *szTitle = APP_NAME;
const char *szWindowClass = "rnsf7bootrund";
HICON	hMainIcon;

// 文字列定数
const char *rnsf7regkey = "Software\\InaSoftAns\\Rnsf7\\CurrentVersion";
const char *runkey      = "Software\\Microsoft\\Windows\\CurrentVersion\\run";
const char *runkeyl01   = "Software\\InaSoftAns\\Rnsf7\\CurrentVersion\\lrun";
const char *runkeyl02   = "Software\\InaSoftAns\\Rnsf7\\CurrentVersion\\crun";

// このコード モジュールに含まれる関数の宣言を転送します :
ATOM				MyRegisterClass(HINSTANCE hInstance);
BOOL				InitInstance(HINSTANCE);
LRESULT CALLBACK	WndProc(HWND, UINT, WPARAM, LPARAM);

int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE, LPTSTR lpCmdLine, int)
{
	HWND	mutexhd;
	MSG		msg;
	int		iCmdOpt = 0;

	if ( _stricmp(lpCmdLine, "/status") == 0 ) {
		// コマンドラインが /status だった場合…
		//   すでに常駐している ……… エラーコード０
		//   常駐していない     ……… エラーコード１
		iCmdOpt = CO_STATUS;
	}
	else if ( _stricmp(lpCmdLine, "/off") == 0 ) {
		// コマンドラインが /off だった場合…
		//   すでに常駐している ……… 常駐解除する、エラーコード０
		//   常駐していない     ……… 何もしない（エラーメッセージも出さない）、エラーコード１
		iCmdOpt = CO_TURNOFF;
	}
	else if ( _stricmp(lpCmdLine, "/on") == 0 ) {
		// コマンドラインが /on だった場合…
		//   すでに常駐している ……… 何もしない（エラーメッセージも出さない）、エラーコード１
		//   常駐していない     ……… 常駐する
		iCmdOpt = CO_TURNON;
	}

	// R-Scriptで、本プログラムから戻り値を受け取るためには、CallExe() 関数を
	// 使います。
	// 本プログラムはGUI型のプログラムであるため、コマンドインタプリタから
	// 文字列を受け取ることはできません。

	// 二重起動の防止
	mutexhd = (HWND)CreateMutex(NULL, TRUE, szWindowClass);
	if (GetLastError() == ERROR_ALREADY_EXISTS) {
		if ( iCmdOpt == CO_STATUS ) {
			// コマンドラインが /status だった場合で、すでに常駐している ……… エラーコード０
			return 0;
		}
		else if ( iCmdOpt == CO_TURNOFF ) {
			// コマンドラインが /off だった場合で、すでに常駐している
			// 　……… 常駐解除する、エラーコード０
			HWND hWnd = FindWindow(szWindowClass, NULL);

			if ( hWnd ) {
                SendMessage( hWnd, WM_DESTROY, 0, NULL );
			}
			return 0;
		}
		else if ( iCmdOpt == CO_TURNON ) {
			// コマンドラインが /on だった場合で、すでに常駐している
			// 　……… 何もしない（エラーメッセージも出さない）、エラーコード１
			return 1;
		}

		if ( IDYES == MessageBox(NULL, APP_NAME"は、すでに起動しています。\nこのプログラムを終了しますか？", szTitle, MB_OK | MB_ICONQUESTION | MB_YESNO) ) {
			HWND hWnd = FindWindow(szWindowClass, NULL);

			if ( hWnd ) {
				SendMessage( hWnd, WM_DESTROY, 0, NULL );
			}
		}
		return 0;
	}

	if ( iCmdOpt == CO_STATUS || iCmdOpt == CO_TURNOFF ) {
		// コマンドラインが /status だった場合で、常駐していない……… エラーコード１
		// コマンドラインが /off だった場合で、　　　　　　　　　〃
		return 1;
	}

	// アイコンハンドルの取得
	hMainIcon = LoadIcon(hInstance, (LPCTSTR)IDI_BOOTRUND);

	// グローバル文字列を初期化しています。
	MyRegisterClass(hInstance);

	// アプリケーションの初期化を実行します。:
	if ( !InitInstance(hInstance) ) {
		return 1;
	}

	// メイン メッセージ ループ :
	while (GetMessage(&msg, NULL, 0, 0)) 
	{
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

ATOM MyRegisterClass(HINSTANCE hInstance)
{
	WNDCLASSEX wcex;

	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			= hMainIcon;
	wcex.hCursor		= LoadCursor(NULL, IDC_ARROW);
	wcex.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
	wcex.lpszMenuName	= NULL;
	wcex.lpszClassName	= szWindowClass;
	wcex.hIconSm		= NULL;

	return RegisterClassEx(&wcex);
}

BOOL InitInstance(HINSTANCE hInstance)
{
   HWND hWnd;

   hInst = hInstance; // グローバル変数にインスタンス処理を格納します。

   hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
      CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInstance, NULL);

   if (!hWnd) {
      return FALSE;
   }

   ShowWindow(hWnd, SW_HIDE);

   return TRUE;
}

void deleteEntrys( REG &reg2, HKEY hKey, const char *mes )
{
	char *value, *data, *data2;	// value:値格納用変数 data:値のデータ格納用変数
	DWORD len, len2;		// 値の長さ取得用
	DWORD lend, len2d;		// 値のデータの長さ取得用
	int   i;				// 繰り返し用
	bool  bResult;			// 削除結果

	RegQueryInfoKey( hKey, NULL, NULL, NULL, NULL, NULL, NULL, NULL, &len2, &len2d, NULL, NULL);

	len = len2 = (len2+1)*2; // WinNT系での対策
	lend = len2d = (len2d+1)*2;

	value = zalloc( len2 );
	data = zalloc( len2d );

	if ( value && data ) {
		i = 0;
		while ( ERROR_SUCCESS == RegEnumValue( hKey, i, value, &len, NULL, NULL, (LPBYTE)data, &lend ) ) {
			data2 = reg2.new_ReadString( value );
			if ( data2 ) {
				if ( strcmp(data, data2) == 0 ) {
					// エントリの削除
					bResult = reg2.DeleteValue( value );

					// 削除した旨をログに残す
					FILE *fp;
					char logname[MAX_PATH*2];
					char drv[_MAX_DRIVE*2], dir[_MAX_DIR*2], fn[_MAX_FNAME*2];

					// 実行ファイル名のフルパスを、とりあえずlognameに入れておく
					GetModuleFileName( NULL, logname, MAX_PATH*2 );

					_splitpath_s( logname, drv, _countof(drv), dir, _countof(dir), fn, _countof(fn), NULL, 0 );
					_makepath_s( logname, _countof(logname), drv, dir, fn, ".log" );

					if ( 0 == fopen_s( &fp, logname, "at" ) ) {
						SYSTEMTIME st;

						GetLocalTime( &st );
						fprintf( fp, "%4d/%2d/%2d %2d:%02d:%02d %s(%s) - %s\n",
							st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
							value, mes, bResult?"削除":"削除失敗" );

						fclose( fp );
					}
				}
				safedelete(data2);
			}

			len = len2;
			lend = len2d;
			i++;
		}
	}

	safefree( value );
	safefree( data );
}

void about(HWND hWnd)
{
	MessageBox(hWnd, APP_NAME " version " VERSION "\nCopyright(C) 2004-" C_YEAR " 矢吹拓也\n\n本プログラムの設定は、いじくるつくーるより行って下さい。\n終了するには、タスクトレイのアイコンを右クリックしてください。", szTitle, MB_OK | MB_ICONINFORMATION | MB_TOPMOST | MB_SETFOREGROUND);
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
	volatile static bool flag = 0; // 申し訳なさ程度のセマフォ(タイマ処理用)
	volatile static bool flag2 = 0; // 申し訳なさ程度のセマフォ(メニュー/ダイアログ用)
	static UINT interval = 10000, uNoTaskTrayIcon = 0;
	static UINT s_uTaskbarRestart;// タスクバー再作成メッセージＩＤを保持する静的変数
	static HMENU hMenuPopup, hMenuSub;	// タスクトレイアイコン用ポップアップメニュー

	switch (message) {
		case WM_CREATE:
			// タイマの設定
			{
				REG  reg;

				if ( reg.OpenKey( HKEY_CURRENT_USER, rnsf7regkey, TREG_OPEN_READONLY ) ) {
					interval = reg.ReadInteger( "rnsf7bootrund_interval", 10000 );
					uNoTaskTrayIcon = reg.ReadInteger( "rnsf7bootrund_icond", 0 );
					reg.CloseKey();
				}

				if ( !uNoTaskTrayIcon ) {
					// システムトレイにアイコンを登録
					TFI_TaskBarAddIcon(hWnd, ST_ICON_ID, hMainIcon, APP_NAME, MYWM_NOTIFYICON);
					// タスクバー再作成メッセージＩＤを取得(Win95+IE〜)
					s_uTaskbarRestart = RegisterWindowMessage(TEXT("TaskbarCreated"));
				}

				// トレイアイコン用メニュー
				hMenuPopup = LoadMenu(hInst, (LPCSTR)IDR_MENU1);
				if (hMenuPopup) {
					// デフォルト項目を太字で表示
					hMenuSub = GetSubMenu(hMenuPopup, 0);
					SetMenuDefaultItem(hMenuSub, 0, TRUE);
				}
				else {
					hMenuSub = 0;
				}

				SetTimer(hWnd, TIMER_ID, interval, NULL);
			}
			break;

		case WM_TIMER:
			if ( wParam == TIMER_ID && flag == 0 ) {
				REG  reg, reg2, reg2L;	// reg:Rnsf7のキー reg2:監視対象のキー reg2L:一時削除のキー

				flag = 1;				// セマフォ
				if ( reg.OpenKey( HKEY_CURRENT_USER, rnsf7regkey, TREG_OPEN_READONLY ) ) {
					// 全ユーザー設定の監視
					if ( reg.ReadInteger( "rnsf7bootrund_all", 0 ) ) {
						if ( reg2.OpenKey( HKEY_LOCAL_MACHINE, runkey, TREG_OPEN_CREATE ) ) {
							if ( reg2L.OpenKey( HKEY_CURRENT_USER, runkeyl01, TREG_OPEN_READONLY ) ) {
								deleteEntrys( reg2, reg2L.GetHKey(), "HKEY_LOCAL_MACHINE - run" );
								reg2L.CloseKey();
							}
							reg2.CloseKey();
						}
					}

					// 個人用設定の監視
					if ( reg.ReadInteger( "rnsf7bootrund_user", 0 ) ) {
						if ( reg2.OpenKey( HKEY_CURRENT_USER, runkey, TREG_OPEN_CREATE ) ) {
							if ( reg2L.OpenKey( HKEY_CURRENT_USER, runkeyl02, TREG_OPEN_READONLY ) ) {
								deleteEntrys( reg2, reg2L.GetHKey(), "HKEY_CURRENT_USER - run" );
								reg2L.CloseKey();
							}
							reg2.CloseKey();
						}
					}

					reg.CloseKey();
				}			
	
				KillTimer(hWnd, TIMER_ID);
				SetTimer(hWnd, TIMER_ID, interval, NULL);
				flag = 0;
			}
			break;

		case WM_DESTROY:
			// システムトレイからアイコンを削除
			TFI_TaskBarDeleteIcon(hWnd, ST_ICON_ID);
			// タイマ解除
			KillTimer(hWnd, TIMER_ID);
			// メニュー削除
			if (hMenuPopup) {
				DestroyMenu(hMenuPopup);
				hMenuPopup = 0;
			}
			// 終了
			PostQuitMessage(0);
			break;

		case MYWM_NOTIFYICON:
			switch((UINT)lParam) {
				case WM_LBUTTONUP:
					if (!flag2) {
						flag2 = 1;
						about(hWnd);
						flag2 = 0;
					}
					break;

				case WM_RBUTTONUP:
					if (!flag2) {
						POINT pt;
						flag2 = 1;
						
						GetCursorPos(&pt);
						if ( hMenuSub ) {
							SetForegroundWindow(hWnd); // 前面に持ってこないと、他所クリック時にメニューが消えてくれない

							// メニューを表示
							UINT uiCmd = (UINT)TrackPopupMenuEx(hMenuSub, TPM_RETURNCMD, pt.x, pt.y, hWnd, NULL);

							PostMessage(hWnd, WM_NULL, 0, 0); // 続けてメニューを開けるようにするためのおまじない

							switch(uiCmd) {
								case ID_TASKTRAYMENU_ABOUT: // バージョン情報ダイアログの表示
									about(hWnd);
									break;

								case ID_TASKTRAYMENU_CANCEL: // 常駐解除
									if (IDYES == MessageBox(hWnd, APP_NAME"を終了しますか?", szTitle, MB_YESNO | MB_ICONQUESTION | MB_TOPMOST | MB_SETFOREGROUND)) {
										DestroyWindow(hWnd);
									}
									break;
							}
						}
						flag2 = 0;
					}
					break;
			}
			break;

		default:
			if ( !uNoTaskTrayIcon ) {
				if(message == s_uTaskbarRestart) {
					TFI_TaskBarAddIcon(hWnd, ST_ICON_ID, hMainIcon, APP_NAME, MYWM_NOTIFYICON);
					break;
				}
			}
			return DefWindowProc(hWnd, message, wParam, lParam);
	}
	return 0;
}
