EternalWindows
独自インストール / インストーラーサンプル

今回のプログラムは、これまで取り上げてきたコードで構成されるインストーラーです。

#include <windows.h>
#include <shlobj.h>
#include <ktmw32.h>

#pragma comment (lib, "ktmw32.lib")

#define WM_ENDOPERATION WM_APP

#define ID_PATH 100
#define ID_PATH_EDIT 101
#define ID_CHECK_DESKTOP 102
#define ID_CHECK_STARTMENU 103
#define ID_INSTALL 104

WCHAR g_szProductName[] = L"My App";
WCHAR g_szMainExeName[] = L"sample.exe";
WCHAR g_szUninstExeName[] = L"uninst.exe";
WCHAR g_szDisplayName[] = L"SampleApplication";
WCHAR g_szTargetFolder[MAX_PATH];

BOOL FileCheck();
BOOL Register(LPWSTR lpszInstallDir, LPWSTR lpszUninstFilePath);
BOOL CreateStartMenuShortcut(LPWSTR lpszMainFilePath, LPWSTR lpszUninstFilePath);
BOOL CreateDesktopShortcut(LPWSTR lpszMainFilePath);
void CreateShortcut(LPWSTR lpszSrcPath, LPWSTR lpszDestDir, LPWSTR lpszShortcutName);
DWORD WINAPI ThreadProc(LPVOID lpParameter);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_CREATE: {
		LPWSTR lpszPath;
		
		CoInitialize(NULL);

		if (!FileCheck())
			return -1;

		if (!IsUserAnAdmin()) {
			MessageBox(NULL, TEXT("管理者として起動してください。"), NULL, MB_ICONWARNING);
			return -1;
		}

		CreateWindowEx(0, TEXT("BUTTON"), TEXT("参照"), WS_CHILD | WS_VISIBLE, 650, 40, 70, 30, hwnd, (HMENU)ID_PATH, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER, 30, 40, 600, 30, hwnd, (HMENU)ID_PATH_EDIT, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("デスクトップにショートカットを作成"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 30, 90, 300, 40, hwnd, (HMENU)ID_CHECK_DESKTOP, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("スタートメニューにショートカットを作成"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 30, 140, 330, 40, hwnd, (HMENU)ID_CHECK_STARTMENU, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("インストール"), WS_CHILD | WS_VISIBLE, 30, 190, 150, 40, hwnd, (HMENU)ID_INSTALL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		SHGetKnownFolderPath(FOLDERID_ProgramFiles, 0, NULL, &lpszPath);
		SetWindowTextW(GetDlgItem(hwnd, ID_PATH_EDIT), lpszPath);
		CoTaskMemFree(lpszPath);

		return 0;
	}

	case WM_COMMAND: {
		if (LOWORD(wParam) == ID_PATH) {
			IFileOpenDialog *pFileOpenDialog;
			HRESULT         hr;
			IShellItem      *psiFolder;
			IShellItem      *psiParent;
			WCHAR           sz[MAX_PATH];
			LPWSTR          lpszItem;
			DWORD           dwOptions;

			hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpenDialog));
			if (FAILED(hr))
				return 0;

			GetWindowTextW(GetDlgItem(hwnd, ID_PATH_EDIT), sz, MAX_PATH);
			SHCreateItemFromParsingName(sz, NULL, IID_PPV_ARGS(&psiFolder));
			psiFolder->GetParent(&psiParent);
			psiFolder->GetDisplayName(SIGDN_NORMALDISPLAY, &lpszItem);

			pFileOpenDialog->SetFolder(psiParent);
			pFileOpenDialog->SetFileName(lpszItem);
			pFileOpenDialog->GetOptions(&dwOptions);
			pFileOpenDialog->SetOptions(dwOptions | FOS_PICKFOLDERS);

			hr = pFileOpenDialog->Show(hwnd);
			if (SUCCEEDED(hr)) {
				LPWSTR     lpszPath;
				IShellItem *psi;

				hr = pFileOpenDialog->GetResult(&psi);
				if (SUCCEEDED(hr)) {
					psi->GetDisplayName(SIGDN_FILESYSPATH, &lpszPath);
					SetWindowTextW(GetDlgItem(hwnd, ID_PATH_EDIT), lpszPath);
					CoTaskMemFree(lpszPath);
					psi->Release();
				}
			}
			
			CoTaskMemFree(lpszItem);
			psiParent->Release();
			pFileOpenDialog->Release();
		}
		else if (LOWORD(wParam) == ID_INSTALL) {
			HANDLE hThread;
			DWORD  dwThreadId;
			
			GetWindowTextW(GetDlgItem(hwnd, ID_PATH_EDIT), g_szTargetFolder, MAX_PATH);

			EnableWindow(GetDlgItem(hwnd, ID_PATH), FALSE);
			EnableWindow(GetDlgItem(hwnd, ID_INSTALL), FALSE);
	
			hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, hwnd, 0, &dwThreadId);
		}
		return 0;
	}

	case WM_ENDOPERATION: {
		BOOL  bCancel = (BOOL)wParam;
		WCHAR szInstallDir[MAX_PATH];
		WCHAR szMainFilePath[MAX_PATH];
		WCHAR szUninstFilePath[MAX_PATH];
		
		if (bCancel) {
			MessageBox(NULL, TEXT("インストールをキャンセルしました。"), TEXT("OK"), MB_OK);
			PostMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;
		}

		wsprintfW(szInstallDir, L"%s\\%s", g_szTargetFolder, g_szProductName);
		wsprintfW(szMainFilePath, L"%s\\%s", szInstallDir, g_szMainExeName);
		wsprintfW(szUninstFilePath, L"%s\\%s", szInstallDir, g_szUninstExeName);

		if (!Register(szInstallDir, szUninstFilePath)) {
			MessageBox(NULL, TEXT("レジストリ登録に失敗しました。"), NULL, MB_ICONWARNING);
			PostMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;
		}

		if (IsDlgButtonChecked(hwnd, ID_CHECK_DESKTOP))
			CreateDesktopShortcut(szMainFilePath);

		if (IsDlgButtonChecked(hwnd, ID_CHECK_STARTMENU))
			CreateStartMenuShortcut(szMainFilePath, szUninstFilePath);

		MessageBox(hwnd, TEXT("インストールが終了しました。"), TEXT("OK"), MB_OK);

		PostMessage(hwnd, WM_CLOSE, 0, 0);

		return 0;
	}

	case WM_CTLCOLORSTATIC:
		return (LRESULT)GetStockObject(WHITE_BRUSH);

	case WM_DESTROY:
		CoUninitialize();
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL FileCheck()
{
	WCHAR szFilePath[MAX_PATH];
	WCHAR szFolderPath[MAX_PATH];
	WCHAR szProductPath[MAX_PATH];
	WCHAR szBuf[512];
	
	GetCurrentDirectoryW(sizeof(szFolderPath) / sizeof(WCHAR), szFolderPath);
	wsprintfW(szProductPath, L"%s\\%s", szFolderPath, g_szProductName);

	wsprintfW(szFilePath, L"%s\\%s", szProductPath, g_szMainExeName);
	if (GetFileAttributesW(szFilePath) == -1) {
		wsprintfW(szBuf, L"%sに%sが存在しません。", szProductPath, g_szMainExeName);
		MessageBoxW(NULL, szBuf, NULL, MB_ICONWARNING);
		return FALSE;
	}
	
	wsprintfW(szFilePath, L"%s\\%s", szProductPath, g_szUninstExeName);
	if (GetFileAttributesW(szFilePath) == -1) {
		wsprintfW(szBuf, L"%sに%sが存在しません。", szProductPath, g_szUninstExeName);
		MessageBoxW(NULL, szBuf, NULL, MB_ICONWARNING);
		return FALSE;
	}

	return TRUE;
}

BOOL Register(LPWSTR lpszInstallDir, LPWSTR lpszUninstFilePath)
{
	HKEY   hKey;
	DWORD  dwDisposition;
	WCHAR  szKey[256];
	LONG   lResult;
	HANDLE hTransaction;

	hTransaction = CreateTransaction(NULL, NULL, 0, 0, 0, 0, L"MyTransaction");
	if (hTransaction == NULL)
		return FALSE;

	wsprintfW(szKey, L"SOFTWARE\\%s", g_szProductName);
	lResult = RegCreateKeyTransactedW(HKEY_LOCAL_MACHINE, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, &dwDisposition, hTransaction, NULL);
	if (lResult != ERROR_SUCCESS) {
		CloseHandle(hTransaction);
		return FALSE;
	}
	
	lResult = RegSetValueExW(hKey, L"InstallDir", 0, REG_SZ, (LPBYTE)lpszInstallDir, lstrlenW(lpszInstallDir) * sizeof(WCHAR));
	RegCloseKey(hKey);
	if (lResult != ERROR_SUCCESS) {
		RollbackTransaction(hTransaction);
		CloseHandle(hTransaction);
		return FALSE;
	}

	wsprintfW(szKey, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s", g_szProductName);
	lResult = RegCreateKeyTransactedW(HKEY_LOCAL_MACHINE, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, &dwDisposition, hTransaction, NULL);
	if (lResult != ERROR_SUCCESS) {
		RollbackTransaction(hTransaction);
		CloseHandle(hTransaction);
		return FALSE;
	}
	
	lResult = RegSetValueExW(hKey, L"DisplayName", 0, REG_SZ, (LPBYTE)g_szDisplayName, lstrlenW(g_szDisplayName) * sizeof(WCHAR));
	if (lResult != ERROR_SUCCESS) {
		RegCloseKey(hKey);
		RollbackTransaction(hTransaction);
		CloseHandle(hTransaction);
		return FALSE;
	}

	lResult = RegSetValueExW(hKey, L"UninstallString", 0, REG_SZ, (LPBYTE)lpszUninstFilePath, lstrlenW(lpszUninstFilePath) * sizeof(WCHAR));
	RegCloseKey(hKey);
	if (lResult != ERROR_SUCCESS) {
		RollbackTransaction(hTransaction);
		CloseHandle(hTransaction);
		return FALSE;
	}

	CommitTransaction(hTransaction);
	CloseHandle(hTransaction);

	return TRUE;
}

BOOL CreateDesktopShortcut(LPWSTR lpszMainFilePath)
{
	LPWSTR lpszPath;

	SHGetKnownFolderPath(FOLDERID_Desktop, 0, NULL, &lpszPath);

	CreateShortcut(lpszMainFilePath, lpszPath, g_szDisplayName);

	CoTaskMemFree(lpszPath);

	return TRUE;
}

BOOL CreateStartMenuShortcut(LPWSTR lpszMainFilePath, LPWSTR lpszUninstFilePath)
{
	LPWSTR lpszPath;
	WCHAR  szDestDir[MAX_PATH];

	SHGetKnownFolderPath(FOLDERID_CommonPrograms, 0, NULL, &lpszPath);
	wsprintfW(szDestDir, L"%s\\%s", lpszPath, g_szProductName);
	CreateDirectoryW(szDestDir, NULL);

	CreateShortcut(lpszMainFilePath, szDestDir, L"アプリケーションの起動");
	CreateShortcut(lpszUninstFilePath, szDestDir, L"アプリケーションのアンインストール");

	CoTaskMemFree(lpszPath);

	return TRUE;
}

void CreateShortcut(LPWSTR lpszSrcPath, LPWSTR lpszDestDir, LPWSTR lpszShortcutName)
{
	IShellLinkW  *pShellLink;
	IPersistFile *pPersistFile;
	WCHAR        szLinkPath[MAX_PATH];
	
	CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellLink));
	pShellLink->SetPath(lpszSrcPath);
	pShellLink->QueryInterface(IID_PPV_ARGS(&pPersistFile));

	wsprintfW(szLinkPath, L"%s\\%s.lnk", lpszDestDir, lpszShortcutName);
	pPersistFile->Save(szLinkPath, TRUE);

	pPersistFile->Release();
	pShellLink->Release();
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	IFileOperation *pFileOperation;
	IShellItem     *psiFrom, *psiTo;
	HRESULT        hr;
	WCHAR          szProductPath[MAX_PATH];
	WCHAR          szFolderPath[MAX_PATH];
	BOOL           bAnyOperationsAborted;
	HWND           hwnd = (HWND)lpParameter;
	
	CoInitialize(NULL);

	hr = CoCreateInstance(CLSID_FileOperation, NULL, CLSCTX_ALL, IID_PPV_ARGS(&pFileOperation));
	if (FAILED(hr)) {
		PostMessage(hwnd, WM_ENDOPERATION, 1, 0);
		return 0;
	}

	GetCurrentDirectoryW(sizeof(szFolderPath) / sizeof(WCHAR), szFolderPath);
	wsprintfW(szProductPath, L"%s\\%s", szFolderPath, g_szProductName);
	
	SHCreateItemFromParsingName(szProductPath, NULL, IID_PPV_ARGS(&psiFrom));
	SHCreateItemFromParsingName(g_szTargetFolder, NULL, IID_PPV_ARGS(&psiTo));

	pFileOperation->CopyItem(psiFrom, psiTo, g_szProductName, NULL);
	pFileOperation->PerformOperations();
	
	pFileOperation->GetAnyOperationsAborted(&bAnyOperationsAborted);
	PostMessage(hwnd, WM_ENDOPERATION, bAnyOperationsAborted, 0);

	psiTo->Release();
	psiFrom->Release();
	pFileOperation->Release();

	return 0;
}

インストールが正しく行われているかを確認するには、該当箇所にアクセスしてみるのが一番でしょう。 たとえば、スタートメニューにショートカットを作成するようにしたならば、 スタートメニューにg_szProductNameの文字列が表示されているのを確認し、 実際にショートカットからアプリケーションを起動します。 また、「プログラムのアンインストール」にg_szDisplayNameが表示されていることを確認したら、 アンインストーラーが起動するかを確認するためにアンインストールを選択します。

旧技術のインストーラー

今回のインストーラーはWindows Vistaで登場した技術を使用していますから、 当然ながらVista以降でしか動作しないことになります。 以前のWindowsでも動作するインストーラーを望む場合は、次のコードを使用します。

#include <windows.h>
#include <shlobj.h>

#define WM_ENDOPERATION WM_APP

#define ID_PATH 100
#define ID_PATH_EDIT 101
#define ID_CHECK_DESKTOP 102
#define ID_CHECK_STARTMENU 103
#define ID_INSTALL 104

TCHAR g_szProductName[] = TEXT("My App");
TCHAR g_szMainExeName[] = TEXT("sample.exe");
TCHAR g_szUninstExeName[] = TEXT("uninst.exe");
TCHAR g_szDisplayName[] = TEXT("SampleApplication");
TCHAR g_szTargetFolder[MAX_PATH];

BOOL FileCheck();
BOOL Register(LPTSTR lpszInstallDir, LPTSTR lpszUninstFilePath);
BOOL CreateStartMenuShortcut(LPTSTR lpszMainFilePath, LPTSTR lpszUninstFilePath);
BOOL CreateDesktopShortcut(LPTSTR lpszMainFilePath);
void CreateShortcut(LPTSTR lpszSrcPath, LPTSTR lpszDestDir, LPTSTR lpszShortcutName);
DWORD WINAPI ThreadProc(LPVOID lpParameter);
int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	switch (uMsg) {

	case WM_CREATE: {
		TCHAR szPath[MAX_PATH];
		
		CoInitialize(NULL);

		if (!FileCheck())
			return -1;

		if (!IsUserAnAdmin()) {
			MessageBox(NULL, TEXT("管理者として起動してください。"), NULL, MB_ICONWARNING);
			return -1;
		}

		CreateWindowEx(0, TEXT("BUTTON"), TEXT("参照"), WS_CHILD | WS_VISIBLE, 650, 40, 70, 30, hwnd, (HMENU)ID_PATH, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("EDIT"), TEXT(""), WS_CHILD | WS_VISIBLE | WS_BORDER, 30, 40, 600, 30, hwnd, (HMENU)ID_PATH_EDIT, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("デスクトップにショートカットを作成"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 30, 90, 300, 40, hwnd, (HMENU)ID_CHECK_DESKTOP, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("スタートメニューにショートカットを作成"), WS_CHILD | WS_VISIBLE | BS_AUTOCHECKBOX, 30, 140, 330, 40, hwnd, (HMENU)ID_CHECK_STARTMENU, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
		CreateWindowEx(0, TEXT("BUTTON"), TEXT("インストール"), WS_CHILD | WS_VISIBLE, 30, 190, 150, 40, hwnd, (HMENU)ID_INSTALL, ((LPCREATESTRUCT)lParam)->hInstance, NULL);

		SHGetFolderPath(NULL, CSIDL_PROGRAM_FILES, 0, NULL, szPath);
		SetWindowText(GetDlgItem(hwnd, ID_PATH_EDIT), szPath);

		return 0;
	}

	case WM_COMMAND: {
		if (LOWORD(wParam) == ID_PATH) {
			BROWSEINFO       browseInfo;
			TCHAR            szInitPath[MAX_PATH];
			TCHAR            szPath[MAX_PATH];
			PIDLIST_ABSOLUTE pidl;

			GetWindowText(GetDlgItem(hwnd, ID_PATH_EDIT), szInitPath, MAX_PATH);
			
			browseInfo.hwndOwner      = hwnd;
			browseInfo.pidlRoot       = NULL;
			browseInfo.pszDisplayName = NULL;
			browseInfo.lpszTitle      = TEXT("フォルダを選択してください");
			browseInfo.ulFlags        = BIF_RETURNONLYFSDIRS;
			browseInfo.lpfn           = BrowseCallbackProc;
			browseInfo.lParam         = (LPARAM)szInitPath;
			browseInfo.iImage         = 0;

			pidl = SHBrowseForFolder(&browseInfo);
			if (pidl == NULL)
				return 0;

			SHGetPathFromIDList(pidl, szPath);
			SetWindowText(GetDlgItem(hwnd, ID_PATH_EDIT), szPath);

			CoTaskMemFree(pidl);
		}
		else if (LOWORD(wParam) == ID_INSTALL) {
			HANDLE hThread;
			DWORD  dwThreadId;
			
			GetWindowText(GetDlgItem(hwnd, ID_PATH_EDIT), g_szTargetFolder, MAX_PATH);

			EnableWindow(GetDlgItem(hwnd, ID_PATH), FALSE);
			EnableWindow(GetDlgItem(hwnd, ID_INSTALL), FALSE);
	
			hThread = CreateThread(NULL, 0, (LPTHREAD_START_ROUTINE)ThreadProc, hwnd, 0, &dwThreadId);
		}
		return 0;
	}

	case WM_ENDOPERATION: {
		BOOL  bCancel = (BOOL)wParam;
		TCHAR szInstallDir[MAX_PATH];
		TCHAR szMainFilePath[MAX_PATH];
		TCHAR szUninstFilePath[MAX_PATH];
		
		if (bCancel) {
			MessageBox(NULL, TEXT("インストールをキャンセルしました。"), TEXT("OK"), MB_OK);
			PostMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;
		}

		wsprintf(szInstallDir, TEXT("%s\\%s"), g_szTargetFolder, g_szProductName);
		wsprintf(szMainFilePath, TEXT("%s\\%s"), szInstallDir, g_szMainExeName);
		wsprintf(szUninstFilePath, TEXT("%s\\%s"), szInstallDir, g_szUninstExeName);

		if (!Register(szInstallDir, szUninstFilePath)) {
			MessageBox(NULL, TEXT("レジストリ登録に失敗しました。"), NULL, MB_ICONWARNING);
			PostMessage(hwnd, WM_CLOSE, 0, 0);
			return 0;
		}
		
		if (IsDlgButtonChecked(hwnd, ID_CHECK_DESKTOP))
			CreateDesktopShortcut(szMainFilePath);

		if (IsDlgButtonChecked(hwnd, ID_CHECK_STARTMENU))
			CreateStartMenuShortcut(szMainFilePath, szUninstFilePath);

		MessageBox(hwnd, TEXT("インストールが終了しました。"), TEXT("OK"), MB_OK);

		PostMessage(hwnd, WM_CLOSE, 0, 0);

		return 0;
	}

	case WM_CTLCOLORSTATIC:
		return (LRESULT)GetStockObject(WHITE_BRUSH);

	case WM_DESTROY:
		CoUninitialize();
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

BOOL FileCheck()
{
	TCHAR szFilePath[MAX_PATH];
	TCHAR szFolderPath[MAX_PATH];
	TCHAR szProductPath[MAX_PATH];
	TCHAR szBuf[512];
	
	GetCurrentDirectory(sizeof(szFolderPath) / sizeof(TCHAR), szFolderPath);
	wsprintf(szProductPath, TEXT("%s\\%s"), szFolderPath, g_szProductName);

	wsprintf(szFilePath, TEXT("%s\\%s"), szProductPath, g_szMainExeName);
	if (GetFileAttributes(szFilePath) == -1) {
		wsprintf(szBuf, TEXT("%sに%sが存在しません。"), szProductPath, g_szMainExeName);
		MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
		return FALSE;
	}
	
	wsprintf(szFilePath, TEXT("%s\\%s"), szProductPath, g_szUninstExeName);
	if (GetFileAttributes(szFilePath) == -1) {
		wsprintf(szBuf, TEXT("%sに%sが存在しません。"), szProductPath, g_szUninstExeName);
		MessageBox(NULL, szBuf, NULL, MB_ICONWARNING);
		return FALSE;
	}

	return TRUE;
}

BOOL Register(LPTSTR lpszInstallDir, LPTSTR lpszUninstFilePath)
{
	HKEY  hKey;
	DWORD dwDisposition;
	TCHAR szKey[256];
	LONG  lResult;

	wsprintf(szKey, TEXT("SOFTWARE\\%s"), g_szProductName);
	lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, &dwDisposition);
	if (lResult != ERROR_SUCCESS)
		return FALSE;
	
	lResult = RegSetValueEx(hKey, TEXT("InstallDir"), 0, REG_SZ, (LPBYTE)lpszInstallDir, lstrlen(lpszInstallDir) * sizeof(TCHAR));
	RegCloseKey(hKey);
	if (lResult != ERROR_SUCCESS)
		return FALSE;

	wsprintf(szKey, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\%s"), g_szProductName);
	lResult = RegCreateKeyEx(HKEY_LOCAL_MACHINE, szKey, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, &hKey, &dwDisposition);
	if (lResult != ERROR_SUCCESS)
		return FALSE;
	
	lResult = RegSetValueEx(hKey, TEXT("DisplayName"), 0, REG_SZ, (LPBYTE)g_szDisplayName, lstrlen(g_szDisplayName) * sizeof(TCHAR));
	if (lResult != ERROR_SUCCESS) {
		RegCloseKey(hKey);
		return FALSE;
	}

	lResult = RegSetValueEx(hKey, TEXT("UninstallString"), 0, REG_SZ, (LPBYTE)lpszUninstFilePath, lstrlen(lpszUninstFilePath) * sizeof(TCHAR));
	RegCloseKey(hKey);
	if (lResult != ERROR_SUCCESS)
		return FALSE;

	return TRUE;
}

BOOL CreateDesktopShortcut(LPTSTR lpszMainFilePath)
{
	TCHAR szPath[MAX_PATH];
	
	SHGetFolderPath(NULL, CSIDL_DESKTOP, NULL, 0, szPath);

	CreateShortcut(lpszMainFilePath, szPath, g_szDisplayName);

	return TRUE;
}

BOOL CreateStartMenuShortcut(LPTSTR lpszMainFilePath, LPTSTR lpszUninstFilePath)
{
	TCHAR szPath[MAX_PATH];
	TCHAR szDestDir[MAX_PATH];
	
	SHGetFolderPath(NULL, CSIDL_COMMON_PROGRAMS, NULL, 0, szPath);
	wsprintf(szDestDir, TEXT("%s\\%s"), szPath, g_szProductName);
	CreateDirectory(szDestDir, NULL);

	CreateShortcut(lpszMainFilePath, szDestDir, TEXT("アプリケーションの起動"));
	CreateShortcut(lpszUninstFilePath, szDestDir, TEXT("アプリケーションのアンインストール"));

	return TRUE;
}

void CreateShortcut(LPTSTR lpszSrcPath, LPTSTR lpszDestDir, LPTSTR lpszShortcutName)
{
	IShellLink   *pShellLink;
	IPersistFile *pPersistFile;
	TCHAR        szLinkPath[MAX_PATH];
	WCHAR        szLinkPathW[MAX_PATH];
	LPWSTR       lpsz;
	
	CoCreateInstance(CLSID_ShellLink, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pShellLink));
	pShellLink->SetPath(lpszSrcPath);
	pShellLink->QueryInterface(IID_PPV_ARGS(&pPersistFile));

	wsprintf(szLinkPath, TEXT("%s\\%s.lnk"), lpszDestDir, lpszShortcutName);

#ifdef UNICODE
	lpsz = szLinkPath;
#else
	MultiByteToWideChar(CP_ACP, 0, szLinkPath, -1, szLinkPathW, MAX_PATH);
	lpsz = szLinkPathW;
#endif

	pPersistFile->Save(lpsz, TRUE);
	pPersistFile->Release();
}

DWORD WINAPI ThreadProc(LPVOID lpParameter)
{
	TCHAR          szFolderPath[MAX_PATH];
	TCHAR          szPathSrc[MAX_PATH] = {'\0'};
	TCHAR          szPathDest[MAX_PATH] = {'\0'};
	SHFILEOPSTRUCT fileOp;
	HWND           hwnd = (HWND)lpParameter;

	GetCurrentDirectory(sizeof(szFolderPath) / sizeof(TCHAR), szFolderPath);
	wsprintf(szPathSrc, TEXT("%s\\%s"), szFolderPath, g_szProductName);
	wsprintf(szPathDest, TEXT("%s\\%s"), g_szTargetFolder, g_szProductName);

	ZeroMemory(&fileOp, sizeof(SHFILEOPSTRUCT));
	fileOp.wFunc  = FO_COPY;
	fileOp.pFrom  = szPathSrc;
	fileOp.pTo    = szPathDest;
	fileOp.fFlags = FOF_SIMPLEPROGRESS;

	SHFileOperation(&fileOp);

	PostMessage(hwnd, WM_ENDOPERATION, fileOp.fAnyOperationsAborted, 0);

	return 0;
}

int CALLBACK BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData)
{
	if (uMsg == BFFM_INITIALIZED)
		SendMessage(hwnd, BFFM_SETSELECTION, TRUE, lpData);

	return 0;
}

このコードではIFileOpenDialogではなくSHBrowseForFolderを使用し、 IFileOperationではなくSHFileOperationを使用しています。 また、SHGetKnownFolderPathではなくSHGetFolderPathを使用し、 レジストリ登録にKTMは使用していません。 SHBrowseForFolderはBROWSEINFO構造体を受け取り、lpfnにはコールバック関数のアドレスを指定できます。 このコールバックでは、特定のフォルダをデフォルトで選択することができるため、 lParamを通じてフォルダパスを渡すようにしています。 ThreadProcでszPathSrcとszPathDestを\0で初期化しているのは、 文字列の終端に\0が2つ続いていなければ、SHFileOperationが失敗するからです。



戻る