/****************************************************************************** Installer SDK Primary Installer モジュールソースファイル Coded by Wraith in Oct 22, 2006. ******************************************************************************/ // Tab幅を4文字に設定して表示させてください。 /////////////////////////////////////////////////////////////////////////////// // // ■ install.c // http://www.trickpalace.net/windows/installer/install.c // // □ 関連ファイル // Installer SDK Read Me ファイル // http://www.trickpalace.net/windows/installer/readme.txt // BCC用簡易スタートアップ アセンブラソースファイル // http://www.trickpalace.net/windows/installer/bccboot.asm // BCC用簡易スタートアップ オブジェクトファイル // http://www.trickpalace.net/windows/installer/bccboot.obj // インストーラアイコンファイル // http://www.trickpalace.net/windows/installer/install.ico // ダウンロードアイコンファイル // http://www.trickpalace.net/windows/installer/download.ico // リソースファイル // http://www.trickpalace.net/windows/installer/install.rc // サンプル .inf ファイル(ANSI版) // http://www.trickpalace.net/windows/installer/setupA.inf // サンプル .inf ファイル(UNICODE版) // http://www.trickpalace.net/windows/installer/setupW.inf // 全ファイルパック // http://www.trickpalace.net/windows/installer/install.zip // // □ リファレンス・サポートページ // http://www.trickpalace.net/windows/installer/ // // □ ライセンス情報 // http://tricklib.com/license.htm // // □ コンパイル方法 // case Borland C++ // bcc32 -c -O1 -DCOMPANY_DIR=\"YOURCOMPANY\" -DAPPLICATION_DIR=\"YOURAPPLICATION\" -DAPPLICATION_NAME=\"YourApplication\" install.c // brcc32 install.rc // ilink32 -aa bccboot.obj install.obj, install.exe, , import32.lib, , install.res // case Visual C++ // rc install.rc // cl /O1 -DCOMPANY_DIR=\"YOURCOMPANY\" -DAPPLICATION_DIR=\"YOURAPPLICATION\" -DAPPLICATION_NAME=\"YourApplication\" install.c /link /machine:IX86 /SUBSYSTEM:WINDOWS install.res // case gcc(migw) // ごめん、コンパクトにコンパイル・リンクを行う方法をまだ調べとらんです。 // case CodeWarrior // ごめん、コンパクトにコンパイル・リンクを行う方法をまだ調べとらんです。 // // #ifndef INSTALL_C #define INSTALL_C ////////////////////////////////////////////////////////////////////////////// // // 必須パラメータのデフォルト値定義 // // // アプリケーションの名前 // #if !defined(APPLICATION_NAME) #define APPLICATION_NAME "YourApplication" #endif // // "Program Files" と "スタート メニュー" に作成されるディレクトリの名前 // #if !defined(COMPANY_DIR) #define COMPANY_DIR "YOURCOMPANY" #endif // // "Program Files" に作成される COMPANY_DIR ディレクトリの下に作成されるディレクトリの名前 // #if !defined(APPLICATION_DIR) #define APPLICATION_DIR "YOURAPPLICATION" #endif // // リソースファイル内に格納している .cab ファイルのリソース名 // #if !defined(CAB_RESOURCENAME) #define CAB_RESOURCENAME "install_cab" #endif // // リソースファイル内に格納している .cab ファイルのリソースタイプ名 // #if !defined(CAB_RESOURCETYPE) #define CAB_RESOURCETYPE "CAB_FILE" #endif // // 一時ファイルとして復元する .cab ファイルのファイル名 // #if !defined(CAB_FILENAME) #define CAB_FILENAME "install.cab" #endif // // .cab ファイルの中に格納している .inf ファイルのファイル名 // #if !defined(INF_FILENAME) #define INF_FILENAME "setup.inf" #endif // // InstallHinfSection 関数の mode 引数(132 で必要に応じて再起動、130 で必ず再起動) // #if !defined(INSTALL_MODE) #define INSTALL_MODE 132 #endif // // Windows 95 系の OS でも稼動させたい場合は次のマクロを有効にしてください。 // //#define FOR_WIN95 ////////////////////////////////////////////////////////////////////////////// // // インストールパスの生成 // // "C:\Program Files" 下のディレクトリパス #if !defined(INSTALL_PATH) #define INSTALL_PATH _T(COMPANY_DIR) _T("\\") _T(APPLICATION_DIR) #endif // "C:\Program Files" 下のディレクトリパス以外の目的で使用するパス // (一時ファイルの展開先としてパスやアンインストーラのレジストリ上での登録パス) #if !defined(INSTALL_DIR) #define INSTALL_DIR _T(COMPANY_DIR) _T("_") _T(APPLICATION_DIR) #endif ////////////////////////////////////////////////////////////////////////////// // // ... // #if !defined(FOR_WIN95) #if !defined(UNICODE) #define UNICODE #endif #if !defined(_UNICODE) #define _UNICODE #endif #endif #include #include #include #include #if defined(__BORLANDC__) # pragma warn -8060 #endif #if defined(_MSC_VER) #pragma warning( push ) #pragma warning( disable : 4700 ) #pragma warning( disable : 4706 ) #pragma comment(linker, "/nodefaultlib:\"libc.lib\"") #pragma comment(linker, "/entry:\"WinMainCRTStartup\"") #pragma comment(linker, "/opt:nowin98") #pragma comment(lib, "KERNEL32.lib") #pragma comment(lib, "SHELL32.lib") #pragma comment(lib, "USER32.lib") #pragma comment(lib, "SETUPAPI.lib") // for InstallHinfSection #pragma comment(lib, "ADVAPI32.lib") // for Registry Operation #endif ////////////////////////////////////////////////////////////////////////////// // // for debug section // #if 0 void debug_message(LPCTSTR format,...) { TCHAR buffer[1024]; wvsprintf(buffer, format, (&format) +1); OutputDebugString(buffer); } #endif ////////////////////////////////////////////////////////////////////////////// // // utilities // #define SUCCESS_RETURN 0 #define ERROR_RETURN -1 #define ON_ERROR_RETURN_TEST(X) if (X) { return ERROR_RETURN; } #define ARRAY_SIZE(X) (sizeof(X) /sizeof(X[0])) // コマンドの実行 void execute_command(LPCTSTR dir_path, LPCTSTR command, LPCTSTR option, int nShow) { SHELLEXECUTEINFO sei; //ZeroMemory(&sei, sizeof(sei)); //↑これをやらないのはなんとなくムズ痒いけど、このケースでは害がないので実施しない。 sei.cbSize = sizeof(sei); sei.fMask = SEE_MASK_NOCLOSEPROCESS; sei.hwnd = NULL; sei.lpVerb = NULL; sei.lpFile = command; sei.lpParameters = option; sei.lpDirectory = dir_path; sei.nShow = nShow; ShellExecuteEx(&sei); WaitForSingleObject(sei.hProcess, INFINITE); CloseHandle(sei.hProcess); } #if !defined(FOR_WIN95) // 特殊フォルダパスの取得 LPTSTR get_shell_folder_path(int nFolder, LPTSTR path) { HWND hwndOwner = NULL; BOOL fCreate = FALSE; if (SHGetSpecialFolderPath(hwndOwner, path, nFolder, fCreate)) { return path; } else { return NULL; // faild } } #endif // DWORD型のレジストリ値の書き込み LONG RegSetDwordValue(HKEY reg_key, LPCTSTR reg_name, DWORD reg_value) { return RegSetValueEx(reg_key, reg_name, 0, REG_DWORD, (LPBYTE)®_value, sizeof(reg_value)); } //// 文字列型のレジストリ値の書き込み //LONG RegSetStringValue(HKEY reg_key, LPCTSTR reg_name, LPCTSTR reg_value) //{ // return RegSetValueEx(reg_key, reg_name, 0, REG_SZ, (LPBYTE)reg_value, lstrlen(reg_value) + 1); //} // ストリームへの書式付き出力 int wfprintf(HANDLE file, LPCTSTR format, ...) { TCHAR buffer[1024]; size_t buffer_length; DWORD write_size = 0; wvsprintf(buffer, format, (va_list)((&format) +1)); // ←他所で真似しちゃいかんよ。 buffer_length = lstrlen(buffer) *sizeof(TCHAR); ON_ERROR_RETURN_TEST(!WriteFile(file, buffer, buffer_length, &write_size, NULL) || write_size < buffer_length); return SUCCESS_RETURN; } // ファイルの存在確認(サブディレクトリのことなんか知りません。) BOOL is_exist_file(LPCTSTR file_path) { BOOL result = FALSE; WIN32_FIND_DATA X_WFD; HANDLE find_handle; find_handle = FindFirstFile(file_path, &X_WFD); if (INVALID_HANDLE_VALUE == find_handle) { return FALSE; } do { if (FILE_ATTRIBUTE_DIRECTORY != (FILE_ATTRIBUTE_DIRECTORY &X_WFD.dwFileAttributes)) { result = TRUE; break; } } while(FindNextFile(find_handle, &X_WFD)); FindClose(find_handle); return result; } // ファイルの削除(サブディレクトリのことなんか知りません。) void delete_files(LPCTSTR file_path) { WIN32_FIND_DATA X_WFD; HANDLE find_handle; find_handle = FindFirstFile(file_path, &X_WFD); if (INVALID_HANDLE_VALUE == find_handle) { return; } do { if (FILE_ATTRIBUTE_DIRECTORY != (FILE_ATTRIBUTE_DIRECTORY &X_WFD.dwFileAttributes)) { if (!DeleteFile(X_WFD.cFileName)) { #if !defined(FOR_WIN95) MoveFileEx(X_WFD.cFileName, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); #endif } } } while(FindNextFile(find_handle, &X_WFD)); FindClose(find_handle); } ////////////////////////////////////////////////////////////////////////////// // // installer sub functions // // リソースの書き出し int output_resource(HMODULE module_handle, LPCTSTR resource_name, LPCTSTR resource_type, LPCTSTR output_filename) { HRSRC resource_info; HGLOBAL mem_handle; LPVOID lp_resource; DWORD resource_size; HANDLE resource_file; DWORD write_size; // // リソースの取得 // ON_ERROR_RETURN_TEST(!(resource_info = FindResource(module_handle, resource_name, resource_type))); ON_ERROR_RETURN_TEST(!(mem_handle = LoadResource(module_handle, resource_info))); ON_ERROR_RETURN_TEST(!(lp_resource = LockResource(mem_handle))); ON_ERROR_RETURN_TEST(!(resource_size = SizeofResource(module_handle, resource_info))); // // ファイルへ出力 // ON_ERROR_RETURN_TEST(INVALID_HANDLE_VALUE == (resource_file = CreateFile(output_filename, GENERIC_WRITE, FILE_SHARE_READ |FILE_SHARE_WRITE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL))); ON_ERROR_RETURN_TEST(!WriteFile(resource_file, lp_resource, resource_size, &write_size, NULL) || write_size < resource_size); CloseHandle(resource_file); return SUCCESS_RETURN; } // .cab ファイルの展開 void develop_cab_file(LPCTSTR dir_path, LPCTSTR cab_file, int nShow) { TCHAR option[MAX_PATH]; #if !defined(FOR_WIN95) // Windows NT type OS wsprintf(option, _T("%s -F:* ."), cab_file); execute_command(dir_path, _T("EXPAND"), option, nShow); #else OSVERSIONINFO ovi; ovi.dwOSVersionInfoSize = sizeof(ovi); GetVersionEx(&ovi); if (VER_PLATFORM_WIN32_WINDOWS == ovi.dwPlatformId) { // Windows 95 type OS wsprintf(option, _T("/E %s"), cab_file); execute_command(dir_path, _T("EXTRACT"), option, nShow); } else { // Windows NT type OS wsprintf(option, _T("%s -F:* ."), cab_file); execute_command(dir_path, _T("EXPAND"), option, nShow); } #endif } // インストールファイルの一時展開先取得 int get_install_temp_dir(LPTSTR temp_dir_path, unsigned int temp_dir_path_size) { DWORD request_size = GetTempPath(temp_dir_path_size, temp_dir_path); ON_ERROR_RETURN_TEST(!request_size); request_size += ARRAY_SIZE(_T("\\") INSTALL_DIR); ON_ERROR_RETURN_TEST(temp_dir_path_size < request_size); if (request_size <= temp_dir_path_size) { lstrcat(temp_dir_path, _T("\\") INSTALL_DIR); } return request_size; } // .inf ファイルのパラメータを設定 int set_inf_params(LPCTSTR inf_file_path) { HANDLE inf_file; TCHAR program_dir[MAX_PATH]; TCHAR program_short_dir[MAX_PATH]; LPCTSTR set_param_format = _T("%s=\"%s\"\r\n"); #if !defined(FOR_WIN95) ON_ERROR_RETURN_TEST(NULL == get_shell_folder_path(CSIDL_PROGRAM_FILES, program_dir)); #else // ちょっと手抜き lstrcpy(program_dir, "C:\\Program Files"); #endif GetShortPathName(program_dir, program_short_dir, MAX_PATH); ON_ERROR_RETURN_TEST(INVALID_HANDLE_VALUE == (inf_file = CreateFile(inf_file_path, GENERIC_WRITE, FILE_SHARE_READ |FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL))); ON_ERROR_RETURN_TEST(INVALID_SET_FILE_POINTER == SetFilePointer(inf_file, 0, NULL, FILE_END)); ON_ERROR_RETURN_TEST(ERROR_RETURN == wfprintf(inf_file, set_param_format, _T("AppName"), _T(APPLICATION_NAME))); ON_ERROR_RETURN_TEST(ERROR_RETURN == wfprintf(inf_file, set_param_format, _T("AppDir"), _T(APPLICATION_DIR))); ON_ERROR_RETURN_TEST(ERROR_RETURN == wfprintf(inf_file, set_param_format, _T("CompanyDir"), _T(COMPANY_DIR))); ON_ERROR_RETURN_TEST(ERROR_RETURN == wfprintf(inf_file, set_param_format, _T("ProgramDir"), program_short_dir)); ON_ERROR_RETURN_TEST(ERROR_RETURN == wfprintf(inf_file, set_param_format, _T("InfFile"), _T(INF_FILENAME))); CloseHandle(inf_file); return SUCCESS_RETURN; } // .inf ファイルの実行 void execute_information_file(LPCTSTR inf_file_path, int mode) { // inf_file_path == .inf ファイルのフルパスで且つショートパスであること。 // mode は、再起動が必ずしもは必要でない場合は 132 に、必ず再起動が必要な場合は 130 にしてください。 TCHAR buffer[MAX_PATH]; LPCTSTR execute_section_name = _T("DefaultInstall"); #if 1 wsprintf(buffer, _T("SETUPAPI.DLL,InstallHinfSection %s %d %s"), execute_section_name, mode, inf_file_path); execute_command(NULL, _T("RUNDLL32"), buffer, 0); #else // 本来こっちのやり方のほうがよりいいはずなんだけど、 // 移植性の問題(※1)があるんでより確実(※2)な RUNDLL32 経由のやり方を採用。 // // ※1 Windows XP で試したところ ANSI 版の InstallHinfSectionA だとなぜか正常に機能せず。 // // ※2 .inf ファイルでのインストールに使われるコマンドが RUNDLL32 経由なのと //   .inf ファイルベースのアンレジストラの一般的な呼び出し方が同じく RUNDLL32 経由なので。 // wsprintf(buffer, _T("%s %d %s"), execute_section_name, mode, inf_file_path); InstallHinfSection(NULL, NULL, buffer, 0); #endif } // アンインストーラの登録 //int regist_uninstaller(LPCTSTR uninstall_reg_path, LPCTSTR application_name, LPCTSTR uninstall_command) int regist_uninstaller(void) { HKEY RegKey; DWORD dwDisp; HRESULT lResult; LPCTSTR szSubKey = _T("SoftWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\") INSTALL_DIR; //TCHAR szSubKey[1024]; //lstrcpy(szSubKey, _T("SoftWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\")); //lstrcat(szSubKey, uninstall_reg_path); ON_ERROR_RETURN_TEST( ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, KEY_SET_VALUE, &RegKey) && ERROR_SUCCESS != RegCreateKeyEx(HKEY_LOCAL_MACHINE, szSubKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &RegKey, &dwDisp)); //ON_ERROR_RETURN_TEST(ERROR_SUCCESS != RegSetStringValue(RegKey, _T("DisplayIcon"), ...)); //ON_ERROR_RETURN_TEST(ERROR_SUCCESS != RegSetStringValue(RegKey, _T("DisplayName"), application_name)); //ON_ERROR_RETURN_TEST(ERROR_SUCCESS != RegSetStringValue(RegKey, _T("ModifyPath"), ...)); //ON_ERROR_RETURN_TEST(ERROR_SUCCESS != RegSetStringValue(RegKey, _T("UninstallString"), uninstall_command)); ON_ERROR_RETURN_TEST(ERROR_SUCCESS != RegSetDwordValue(RegKey, _T("NoModify"), 1)); // ↑ここで全部の情報を登録してもいいんだけど、.inf ファイルで登録を行うから、 //  ここでは .inf ファイルではできない、NoModify の登録だけ行う。 RegCloseKey(RegKey); return SUCCESS_RETURN; } ////////////////////////////////////////////////////////////////////////////// // // installer main function // #if defined(_MSC_VER) void __cdecl WinMainCRTStartup(void) #elif defined(__BORLANDC__) void __stdcall WinMainCRTStartup(void) #else #error "非対応コンパイラですよ。コンパクトにできない以外は問題ないと思うから、それでもいい場合はこの error ディレクトリをコメントアウトしちくれ。" void main(void) // ↑「それは正しくない形式の main ですよ。」とか言うなよ。 //  void WinMainCRTStartup(void) と合わせる為に、分かっててやってんだから。 #endif { #define return ExitProcess(GetLastError()); #define ABORT(err_msg) MessageBox(NULL, err_msg, installer_name, MB_ICONSTOP |MB_OK); return; LPCTSTR installer_name = _T(APPLICATION_NAME) _T(" インストーラ"); LPCTSTR cab_resource_name = _T(CAB_RESOURCENAME); LPCTSTR cab_resource_type = _T(CAB_RESOURCETYPE); LPCTSTR cab_file_name = _T(CAB_FILENAME); TCHAR temp_dir[MAX_PATH]; TCHAR inf_file_path[MAX_PATH]; TCHAR inf_file_short_path[MAX_PATH]; // インストールファイルの一時展開先取得 if (ERROR_RETURN == get_install_temp_dir(temp_dir, ARRAY_SIZE(temp_dir))) { ABORT(_T("一時ファイル展開用ディレクトリのパスを取得できませんでした。")); } // インストールファイルの一時展開先作成 if (!CreateDirectory(temp_dir, NULL) && ERROR_ALREADY_EXISTS != GetLastError()) { ABORT(_T("一時ファイル展開用ディレクトリを作成できませんでした。")); } // リソースの書き出し SetCurrentDirectory(temp_dir); if (ERROR_RETURN == output_resource(GetModuleHandle(NULL), cab_resource_name, cab_resource_type, cab_file_name)) { ABORT(_T("アーカイブファイルを復元できませんでした。")); } // .cab ファイルの展開 develop_cab_file(temp_dir, cab_file_name, SW_HIDE); // .cab ファイルの削除( このタイミングで消さなくとも後のコードでどのみち削除されるんだけど、ファイルが展開できたかどうか確認する為。 ) delete_files(cab_file_name); if (!is_exist_file(_T("*"))) { ABORT(_T("アーカイブファイルを展開できませんでした。")); } // .inf ファイルにパラメータを設定 wsprintf(inf_file_path, _T("%s\\%s"), temp_dir, _T(INF_FILENAME)); if (ERROR_RETURN == set_inf_params(inf_file_path)) { ABORT(_T("セットアップ情報(setup.inf)を編集できませんでした。")); } // .inf ファイルの実行 GetShortPathName(inf_file_path, inf_file_short_path, MAX_PATH); execute_information_file(inf_file_short_path, INSTALL_MODE); // アンインストーラの設定(アンインストーラの登録は execute_information_file でキックされる .inf 内で行われる。) regist_uninstaller(); // 一時展開していたインストールファイルの削除 SetCurrentDirectory(temp_dir); // なにかの副作用でカレントディレクトリが変わってたら目も当てられない状況になるんで、念のため。 delete_files(_T("*")); // ↑この時点ではまだ .inf ファイルによるファイルのコピーが完了していないケースでも問題ないか確認すること。 // インストールファイルの一時展開先削除 #if !defined(FOR_WIN95) if (!RemoveDirectory(temp_dir)) { MoveFileEx(temp_dir, NULL, MOVEFILE_DELAY_UNTIL_REBOOT); } #else RemoveDirectory(temp_dir); #endif // インストールが完了したことをユーザに通知 MessageBox(NULL, _T(APPLICATION_NAME) _T(" のインストールが終了しました。"), installer_name, MB_ICONINFORMATION |MB_OK); // ↑再起動する場合にこのメッセージが変じゃないか確認すること。 // ちょっと変でもこのままにするしかないかも。 return; #undef return #undef ABORT } ////////////////////////////////////////////////////////////////////////////// // // ... // #if defined(__BORLANDC__) # pragma warn .8060 #endif #if defined(_MSC_VER) #pragma warning( pop ) #endif #endif INSTALL_C /****************************************************************************** □■□■ Wraith the Trickster □■□■ ■□■□ 〜I'll go with heaven's advantage and fool's wisdom.〜 ■□■□ ******************************************************************************/