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

#include "stdafx.h"
#include "resource.h"

static HINSTANCE hInst;
static LRESULT CALLBACK DlgProc_rstrct( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam );
static char **fnames;
static unsigned num_found;
static volatile bool abort_flag = 0;
static HWND rstrct_hDlg, hStaticPath;

#define RSTRCT_CLOSEDIALOG	WM_APP

// アイコンをつける（Rnsf7.exeからrstrctアイコンを移設）

BOOL APIENTRY DllMain( HINSTANCE hInstance, DWORD what, LPVOID )
{
	switch (what) {
		case DLL_PROCESS_ATTACH:
            hInst = hInstance;
			break;

		case DLL_PROCESS_DETACH:
			break;
	}
    return TRUE;
}

static int args_typecheck( struct ARGS *args, char *types ) // 引数のチェック
{
	struct ARGS *p;

	p = args;

	if (p == NULL) {
		if (types == NULL || types[0] == 0)
			return 1; // OK
		else
			return 0; // NG
	}

	do {
		switch ( p->type ) {
			case UFCT_ARG_INT:
				if (*types != 'i')
					return 0; // NG
				break;

			case UFCT_ARG_STRING:
				if (*types != 's')
					return 0; // NG
				break;

			case UFCT_ARG_INTS:
				if (*types != 'a')
					return 0; // NG
				break;

			case UFCT_ARG_STRINGS:
				if (*types != 'r')
					return 0; // NG
				break;
		}
		
		types++;
	} while( (p = p->next) != NULL );

	if ( *types == 0 ) // argsが終端に達したとき、typeも終端に達したか?
		return 1; // OK
	else
		return 0; // NG
}

// 実行ファイルのサーチ開始
DllExport UINT findexes( HWND hWnd, struct ARGS *args )
{
	if ( args_typecheck( args, "" ) ) {
		if ( fnames ) {
			for(unsigned i=0 ; i<num_found ; i++) {
				if ( fnames[i] ) {
					free( fnames[i] );
				}
			}
			num_found = 0;
			free( fnames );
			fnames = NULL;
		}
		return (UINT)DialogBox(hInst, (LPCTSTR)IDD_DIALOG1, hWnd, (DLGPROC)DlgProc_rstrct);
	}

	return 0;
}

// 実行ファイル名を返却
DllExport HGLOBAL getexes( HWND hWnd, struct ARGS *args )
{
	HGLOBAL hMem;
	char *buf, *p;

	if ( args_typecheck( args, "i" ) ) {
		if ( num_found > args->v.integer ) {
			p = fnames[args->v.integer];
			if ( !p ) {
                p = "";
			}
		}
		else {
			p = "";
		}
		if ( NULL != (hMem = GlobalAlloc( GMEM_MOVEABLE, lstrlen(p)+1 )) ) {
			if ( NULL != (buf = (char *)GlobalLock( hMem ) ) ) {
				lstrcpy(buf, p);
				GlobalUnlock( hMem );
				return hMem;
			}
		}
	}

	return NULL;
}

// 実行ファイルのサーチ
// (再帰呼び出しにより、各ディレクトリの実行ファイルをサーチ)
static bool searchexes_aux( char *dir ) // 戻り値がfalseだと、中止の伝達
{
	HANDLE hFind;
	WIN32_FIND_DATA520 wfd;
	char old_cd[ MAX_PATH*2 ];
	int len;
	unsigned i;
	char **p;

	if ( GetCurrentDirectory( _countof(old_cd), old_cd ) ) {
		SetWindowText(hStaticPath, old_cd);
		if ( SetCurrentDirectory( dir ) ) {
			hFind = FindFirstFile520("*.*", &wfd);
			if ( hFind != INVALID_HANDLE_VALUE ) {
				do {
					if ( abort_flag ) { // 中止ボタンが押されたら
						SetCurrentDirectory( old_cd );
						return false; // 中止伝達
					}
					else {
						if ( wfd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
							// フォルダ
							if ( strcmp(wfd.cFileName, ".") != 0 && strcmp(wfd.cFileName, "..") != 0 ) {
								// 再帰呼び出しでツリーを探索
								if ( false == searchexes_aux( wfd.cFileName ) ) {
									// falseが返ってきたら中止伝達
									SetCurrentDirectory( old_cd );
									return false;
								}
							}
						}
						else {
							// ファイル
							len = lstrlen( wfd.cFileName );
							if ( len > 3 ) {
								if ( _stricmp( &(wfd.cFileName[len-4]), ".exe" ) == 0 ||
									 _stricmp( &(wfd.cFileName[len-4]), ".com" ) == 0 ||
									 _stricmp( &(wfd.cFileName[len-4]), ".bat" ) == 0 ||
									 _stricmp( &(wfd.cFileName[len-4]), ".pif" ) == 0 ||
									 _stricmp( &(wfd.cFileName[len-4]), ".cmd" ) == 0 ) {

									// 過去に同じものを追加していた場合は、追加しない
									unsigned j;
									for(j=0 ; j<num_found ; j++) {
										if ( 0 == _stricmp(fnames[j], wfd.cFileName) ) {
											break;
										}
									}
									if ( j == num_found ) { // 過去に一致がなかった
										i = num_found + 1;
										p = (char **)realloc(fnames, sizeof(char *)*i);
										if ( p ) {
											fnames = p;
											fnames[i-1] = _strdup( wfd.cFileName );
											num_found = i;
										}
									}
								}
							}
						}
					}
				} while( 0 != FindNextFile520(hFind, &wfd) );
				FindClose( hFind );
			}

			SetCurrentDirectory( old_cd );
		}
	}
	return true;
}

// 実行ファイルのサーチ
// (ハードディスクドライブをサーチ)
static void searchexes( void* )
{
	char rootpath[4] = "x:\\";
	num_found = 0;
	abort_flag = 0;
	UINT uOldErrMode = SetErrorMode(SEM_FAILCRITICALERRORS);

	for(rootpath[0]='A' ; rootpath[0]<='Z' ; rootpath[0]++) {
		if ( DRIVE_FIXED == GetDriveType( rootpath ) ) { // ハードディスクだったら
			// サーチ開始
			if( false == searchexes_aux( rootpath ) ) {
				// 終了伝達
				break;
			}
		}
	}

	SetErrorMode(uOldErrMode);
	SendMessage( rstrct_hDlg, RSTRCT_CLOSEDIALOG, 0, 0 );
	_endthread();
}

// ウィンドウ用ダイアログプロシージャ
static LRESULT CALLBACK DlgProc_rstrct( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
{
	switch( message ) {
		case WM_INITDIALOG:
			hStaticPath = GetDlgItem(hDlg, IDC_STATIC_PATH);
			// 検索用にスレッドを生成
			rstrct_hDlg = hDlg;
			
			// Windows 7なら、タスクバーが進捗バーになる。
			// (ただし、ディレクトリ数の全量はわからないので、TBPF_INDETERMINATE で回す)
			w7tbProgress(GetAncestor(hDlg, GA_ROOTOWNER), 1 /*TBPF_INDETERMINATE*/, 0, 0);

			if( -1 == _beginthread(searchexes, 0, NULL) ) {
				MessageBox( hDlg, "スレッドの生成に失敗しました。", "プログラムの起動許可設定", MB_OK | MB_ICONSTOP );
				EndDialog(hDlg, 0);
			}
			return TRUE;

		case WM_COMMAND:
			switch( LOWORD(wParam) ) {
				case IDCANCEL:
					// Windows 7のタスクバー上の進捗バーを初期化
					w7tbProgress(GetAncestor(hDlg, GA_ROOTOWNER), 0 /*TBPF_NOPROGRESS*/ , 0, 0);
					w7tbProgress(NULL, 0, 0, 0);

					abort_flag = 1; // フラグがセットされると、sratchexes/_auxはすみやかに終了する
					return TRUE;
			}
			return FALSE;

		case RSTRCT_CLOSEDIALOG:
			// Windows 7のタスクバー上の進捗バーを初期化
			w7tbProgress(GetAncestor(hDlg, GA_ROOTOWNER), 0 /*TBPF_NOPROGRESS*/ , 0, 0);
			w7tbProgress(NULL, 0, 0, 0);

			EndDialog(hDlg, (INT_PTR)((abort_flag != 0)?0:1));
			return TRUE;
	}
    return FALSE;
}
