meideru blog

家電メーカーで働いているmeideruのブログです。主に技術系・ガジェット系の話を書いています。

【Windows API】住所録のソフトを作ってみた

      2016/11/18

少し前に勉強のためにWindows API (Win32API)を利用して住所録(電話帳?)っぽいソフトウェアを作っているという話をしました。

最近は、Windows API(Win32API)の勉強中です。Windows APIとは、Windows上で動くアプリケーションのAPIのことです。これさえ扱えれば、基本的に、どんなアプリケーションでも開発可能です(・∀・)今、これを用いて「住所録」のソフトウェアを開発しています。今日は...

 

とりあえず、一通り完成したのでソースコードをアップしたいと思います!

GUIなど

GUI(グラフィカルユーザインタフェース)は、以下のような感じです。

@スクリーンショット1
住所録のスクリーンショット その1

@スクリーンショット2
住所録のスクリーンショット その2

@スクリーンショット3
住所録のスクリーンショット その3

@スクリーンショット4
住所録のスクリーンショット その4

ソースコード

詳細はGitHubに公開中

このソフトウェアに関するソースコードは、すべて私のGitHubに公開しています。

GitHub → https://github.com/meideru/AddressBookWin32API

ソースコード

このプログラムは主に3つのコードからできています。

 

winmain.cppはwinmain関数を含むコードです。

// 住所録
// モードレスダイアログボックスを作成

#define _CRT_SECURE_NO_WARNINGS

#include <Windows.h>
#include <stdio.h>
#include <commctrl.h>
#include "resource.h"
#include "UserDataClass.h"

// ------------------- プロトタイプ宣言 -------------------- //

// メインとなるダイアログプロシージャ(モードレスダイアログボックス)
INT_PTR CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp);

// 編集フォームのダイアログプロシージャ(モーダルダイアログボックス)
INT_PTR CALLBACK MyDlgProc2(HWND hDlg2, UINT msg, WPARAM wp, LPARAM lp);

// ファイルが存在するかどうかの確認
bool CheckFiles();
// 実際にファイルが存在するかどうか確認
bool DoCheckFiles();

// -------- MyDlgProc -------- //

// ユーザリストを初期化する
bool InitUserList(HWND hDlg);
// 実際にユーザリストを初期化する
bool DoInitUserList(HWND hDlg);

// リストコントロールを設定する
bool InitListCtrl(HWND hDlg);
// 実際にリストコントロールを設定する
bool DoInitListCtrl(HWND hDlg);

// 登録ボタンが押されたときの処理
void PushAddButton(HWND hDlg);

// 編集ボタンが押されたときの処理
void PushEditButton(HWND hDlg);

// 検索ボタンが押されたときの処理
void PushSearchButton(HWND hDlg);

// 削除ボタンが押されたときの処理
void PushDeleteButton(HWND hDlg);

// リセットボタンが押されたときの処理
void PushResetButton(HWND hDlg);
// 実際にリセットを行う
bool DoResetText(HWND hDlg);

// ユーザリストを更新するとき
void UpdateUserList(HWND hDlg);
// 実際にユーザリストを更新する
bool DoUpdateUserList(HWND hDlg);

// -------- MyDlgProc2 -------- //

// エディットボックスを指定の文字列で初期化する
bool InitEditBox(HWND hDlg2);
// 実際にエディットボックスを指定の文字列で初期化する
bool DoInitEditBox(HWND hDlg2);

// 確定ボタンが押されたときの処理
void PushOkButton(HWND hDlg2);
// 実際に編集を行うときの処理
bool DoEditUserdata(HWND hDlg2);


// winmain関数
int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,
	LPSTR lpsCmdLine, int nCmdShow)
{
	// メッセージに関する変数
	MSG msg;
	BOOL bRet;

	// ウィンドウハンドル
	HWND hDlg;

	// モードレスダイアログボックスを作成
	hDlg = CreateDialog(
		hCurInst,						// インスタンスハンドル
		MAKEINTRESOURCE(IDD_DIALOG1),	// リソース名
		NULL,							// 親ハンドル
		(DLGPROC)MyDlgProc);			// プロシージャ名
	
	// ダイアログボックスを表示
	ShowWindow(hDlg, nCmdShow);

	// メッセージ処理
	while ((bRet = GetMessage(&msg, NULL, 0, 0)) != 0)
	{
		// メッセージの取得に失敗したとき
		if (bRet == -1){
			break;
		}
		else{	// メッセージの取得に成功したとき
			// ダイアログ宛のメッセージのとき
			if (!IsDialogMessage(hDlg, &msg)){
				TranslateMessage(&msg);		// メッセージを翻訳
				DispatchMessage(&msg);		// メッセージをプロシージャに転送
			}
		}
	}
}


// メインとなるダイアログプロシージャ(モードレスダイアログボックス)
INT_PTR CALLBACK MyDlgProc(HWND hDlg, UINT msg, WPARAM wp, LPARAM lp)
{
	// メッセージを処理
	switch (msg)
	{

	// ダイアログが呼び出されるとき
	case WM_INITDIALOG:
		
		// ファイルが存在するか確認
		if (!CheckFiles()){
			DestroyWindow(hDlg);	// ダイアログを閉じる
			PostQuitMessage(0);		// メッセージキューに0を送る
		}

		// ユーザリストを初期化
		if (!InitUserList(hDlg))
		{
			DestroyWindow(hDlg);	// ダイアログを閉じる
			PostQuitMessage(0);		// メッセージキューに0を送る
		}

		// リストコントロールを設定
		if (!InitListCtrl(hDlg))
		{
			DestroyWindow(hDlg);	// ダイアログを閉じる
			PostQuitMessage(0);		// メッセージキューに0を送る
		}

		// ユーザリストを更新する
		UpdateUserList(hDlg);

		return TRUE;

	// ボタンが押されたとき
	case WM_COMMAND:

		// コマンドを取得
		switch (LOWORD(wp))
		{

		// グループ名が入力された
		case IDC_EDIT_GROUP:
			return TRUE;
		
		// 名前が入力された
		case IDC_EDIT_NAME:
			return TRUE;

		// 住所が入力された
		case IDC_EDIT_ADDRESS:
			return TRUE;

		// 電話番号が入力された
		case IDC_EDIT_TEL:
			return TRUE;

		// 名簿が選択された
		case IDC_LIST_NAMELIST:
			return TRUE;

		// 登録ボタンが押された
		case IDC_BUTTON_ADD:
			// 登録を行う
			PushAddButton(hDlg);
			return TRUE;

		// 編集ボタンが押された
		case IDC_BUTTON_EDIT:
			// 編集ボタンが押されたとき
			PushEditButton(hDlg);
			return TRUE;

		// 検索ボタンが押された
		case IDC_BUTTON_SEARCH:
			// 検索ボタンが押されたとき
			PushSearchButton(hDlg);
			return TRUE;

		// 削除ボタンが押された
		case IDC_BUTTON_DELETE:
			// 登録を行う
			PushDeleteButton(hDlg);
			return TRUE;

		// リセットボタンが押された
		case IDC_BUTTON_RESET:
			// リセットボタンが押されたとき
			PushResetButton(hDlg);
			return TRUE;

		// メニューの終了が押された
		case ID_END:
			DestroyWindow(hDlg);	// ダイアログを閉じる
			PostQuitMessage(0);		// メッセージキューに0を送る
			return TRUE;

		// 想定外の処理
		default:
			return TRUE;
		}

	// 閉じるボタンが押された
	case WM_CLOSE:
		DestroyWindow(hDlg);	// ダイアログを閉じる
		PostQuitMessage(0);		// メッセージキューに0を送る
		return TRUE;

	// デフォルトプロシージャ
	default:
		return FALSE;
	}

	return FALSE;
}


// 編集フォームのダイアログプロシージャ(モーダルダイアログボックス)
INT_PTR CALLBACK MyDlgProc2(HWND hDlg2, UINT msg, WPARAM wp, LPARAM lp)
{
	// メッセージを処理
	switch (msg)
	{

	// ダイアログが呼び出されるとき
	case WM_INITDIALOG:

		// エディットボックスを指定の文字列で初期化する
		InitEditBox(hDlg2);

		return TRUE;

	// ボタンが押されたとき
	case WM_COMMAND:

		// コマンドを取得
		switch (LOWORD(wp))
		{

		// 確定ボタンが押されたとき
		case IDOK:

			// 確定ボタンが押されたときの処理
			PushOkButton(hDlg2);

			return TRUE;

		// キャンセルボタンが押されたとき
		case IDC_BUTTON_CANCEL2:
			EndDialog(hDlg2, TRUE);
			return TRUE;

		// 想定外の処理
		default:
			return TRUE;
		}

	// 閉じるボタンが押された
	case WM_CLOSE:
		EndDialog(hDlg2, TRUE);
		return TRUE;

	// デフォルトプロシージャ
	default:
		return FALSE;
	}

	return FALSE;
}


// ファイルが存在するかどうかを確認する
bool CheckFiles()
{
	// ファイルが存在せず、かつファイルの作成に失敗
	if (!(DoCheckFiles()))
	{
		// ファイル作成失敗に関するメッセージ
		MessageBox(
			NULL,
			TEXT("ファイルの作成に失敗しました。"),
			TEXT("エラー"),
			MB_ICONINFORMATION | MB_OK);
	}

	return true;
}


// 実際にファイルが存在するかどうか確認
bool DoCheckFiles()
{
	// ファイルポインタ
	FILE *fp;

	// ユーザ名の保存パスをファイルオープン(読み込みモード)
	if ((fp = fopen(userPath, "rb")) == NULL)
	{
		// ファイルが存在しないので作成する
		if ((fp = fopen(userPath, "wb")) == NULL){
			return false;
		}

		// ファイル作成成功に関するメッセージボックス
		MessageBox(
			NULL,
			TEXT("ユーザデータが存在しないのでファイルを作成しました。"),
			TEXT("作成しました"),
			MB_ICONINFORMATION | MB_OK);
	}

	// ファイルクローズ
	fclose(fp);

	return true;
}


// ユーザリストを初期化する
bool InitUserList(HWND hDlg)
{
	// 失敗したとき
	if (!(DoInitUserList(hDlg)))
	{
		// リスト更新に失敗したときのメッセージボックス
		MessageBox(
			hDlg,
			TEXT("ユーザリストのカラムの初期化に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return false;
	}

	return true;
}

// 実際にユーザリストを初期化する
bool DoInitUserList(HWND hDlg)
{
	// リストコントロールのハンドルを取得
	HWND hList = GetDlgItem(hDlg, IDC_LIST_NAMELIST);

	// カラム
	LV_COLUMN lvcol;

	// 番号(カラム)
	lvcol.mask = LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
	lvcol.fmt = LVCFMT_LEFT;
	lvcol.cx = 80;
	lvcol.pszText = TEXT("番号");
	ListView_InsertColumn(hList, 0, &lvcol);

	// グループ(カラム)
	lvcol.cx = 150;
	lvcol.pszText = TEXT("グループ");
	ListView_InsertColumn(hList, 1, &lvcol);

	// 名前(カラム)
	lvcol.cx = 140;
	lvcol.pszText = TEXT("名前");
	ListView_InsertColumn(hList, 2, &lvcol);

	// 住所(カラム)
	lvcol.cx = 270;
	lvcol.pszText = TEXT("住所");
	ListView_InsertColumn(hList, 3, &lvcol);

	// 電話番号(カラム)
	lvcol.cx = 170;
	lvcol.pszText = TEXT("電話番号");
	ListView_InsertColumn(hList, 4, &lvcol);

	return true;
}


// リストコントロールを設定する
bool InitListCtrl(HWND hDlg)
{
	// 失敗したとき
	if (!(DoInitListCtrl(hDlg)))
	{
		// リスト更新に失敗したときのメッセージボックス
		MessageBox(
			hDlg,
			TEXT("リストコントロールの設定に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return false;
	}

	return true;
}


// 実際にリストコントロールを設定する
bool DoInitListCtrl(HWND hDlg)
{
	// リストコントロールのハンドルを取得
	HWND hList = GetDlgItem(hDlg, IDC_LIST_NAMELIST);

	// 一行選択可能・グリッド線を表示
	ListView_SetExtendedListViewStyle(
		hList,
		LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES);

	return true;
}


// ユーザリストを更新するとき
void UpdateUserList(HWND hDlg)
{
	// 失敗したとき
	if (!(DoUpdateUserList(hDlg)))
	{
		// リスト更新に失敗したときのメッセージボックス
		MessageBox(
			hDlg,
			TEXT("ユーザリストの更新に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return;
	}
}

// 実際にユーザリストを更新する
bool DoUpdateUserList(HWND hDlg)
{
	// リストコントロールのハンドルを取得
	HWND hList = GetDlgItem(hDlg, IDC_LIST_NAMELIST);

	// データクラスのインスタンス
	DATA data;

	// 更新する
	if (!(data.ShowUserList(hList))){
		return false;
	}

	return true;
}


// 登録ボタンが押されたときの処理
void PushAddButton(HWND hDlg)
{
	// バッファ
	char group[256];	// グループ名
	char name[256];		// 名前
	char address[256];	// 住所
	char tel[256];		// 電話番号

	// 入力文字を取得
	GetDlgItemText(hDlg, IDC_EDIT_GROUP, group, sizeof(group));		// グループ名
	GetDlgItemText(hDlg, IDC_EDIT_NAME, name, sizeof(name));			// 名前
	GetDlgItemText(hDlg, IDC_EDIT_ADDRESS, address, sizeof(address));	// 住所
	GetDlgItemText(hDlg, IDC_EDIT_TEL, tel, sizeof(tel));				// 電話番号

	// エディットコントロールにおいてグループ名以外の空欄が1つでもあるとき
	if ((strcmp(name, "") == 0) || (strcmp(address, "") == 0) || (strcmp(tel, "") == 0))
	{
		// 1つでも入力されていないときのメッセージ
		MessageBox(
			hDlg,
			TEXT("入力されていない項目があります。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return;
	}

	// DATAクラスのインスタンス
	DATA userdata;

	// 追加
	if (userdata.UserAdd(group, name, address, tel))
	{
		// ユーザリストを更新する
		UpdateUserList(hDlg);

		// 登録成功のメッセージボックスを表示
		MessageBox(
			hDlg,
			TEXT("登録が完了しました。"),
			TEXT("登録完了"),
			MB_ICONINFORMATION | MB_OK);

		// 入力フォームをリセットする
		PushResetButton(hDlg);
	}
	else
	{
		// ユーザリストを更新する
		UpdateUserList(hDlg);

		// 登録失敗のメッセージボックスを表示
		MessageBox(
			hDlg,
			TEXT("登録に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);
	}
}


// 編集ボタンが押されたときの処理
void PushEditButton(HWND hDlg)
{
	// 【方針】
	// ・編集ボタンが押されるとダイアログボックスを表示する
	// ・ダイアログボックスにて編集を行う

	// 親ウィンドウのインスタンスハンドル
	HINSTANCE hInst;
	// リストコントロールのハンドル
	HWND hList;
	// リストコントロールで選択されている項目の番号
	int index;

	// リストコントロールのハンドル・親ウィンドウのウィンドウハンドルを取得
	if (((hList = GetDlgItem(hDlg, IDC_LIST_NAMELIST)) == NULL)
		|| ((hInst = (HINSTANCE)GetWindowLong(hDlg, GWL_HINSTANCE)) == NULL))
	{
		// 失敗したときのメッセージ
		MessageBox(
			hDlg,
			TEXT("初期化に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return;
	}

	// リストコントロールで選択中のアイテムを取得
	if ((index = ListView_GetNextItem(hList, -1, LVNI_ALL | LVNI_SELECTED)) == -1)
	{
		// 選択していないときのメッセージ
		MessageBox(
			hDlg,
			TEXT("編集する項目を選択してください。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);
		
		return;
	}

	// 編集フォームのダイアログボックスを表示する
	DialogBox(hInst, MAKEINTRESOURCE(IDD_DIALOG2), hDlg, MyDlgProc2);
}


// 検索ボタンが押されたときの処理
void PushSearchButton(HWND hDlg)
{
	//DATAクラスのインスタンス
	DATA data;
	// リストコントロールのハンドル
	HWND hList;

	// ファイルの検索結果の件数
	long searchCount;
	
	// 検索キーを保存するバッファ
	char group[256];	// グループ名
	char name[256];		// 名前
	char address[256];	// 住所
	char tel[256];		// 電話番号

	// リストコントロールのハンドルを取得
	if ((hList = GetDlgItem(hDlg, IDC_LIST_NAMELIST)) == NULL)
	{
		// 検索に失敗したときのメッセージ
		MessageBox(
			hDlg,
			TEXT("検索に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return;
	}
	
	// 入力文字を取得
	GetDlgItemText(hDlg, IDC_EDIT_GROUP, group, sizeof(group));		// グループ名
	GetDlgItemText(hDlg, IDC_EDIT_NAME, name, sizeof(name));			// 名前
	GetDlgItemText(hDlg, IDC_EDIT_ADDRESS, address, sizeof(address));	// 住所
	GetDlgItemText(hDlg, IDC_EDIT_TEL, tel, sizeof(tel));				// 電話番号

	// 検索キーが1つも入力されていないとき
	if ((strcmp(group, "") == 0) && (strcmp(name, "") == 0)
		&& (strcmp(address, "") == 0) && (strcmp(tel, "") == 0))
	{
		// 検索キーが1つも入力されていないときのメッセージ
		MessageBox(
			hDlg,
			TEXT("検索キーを入力してください。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return;
	}

	// 検索を行う
	if (!data.UserSearch(group, name, address, tel))
	{
		// 検索に失敗したときのメッセージ
		MessageBox(
			hDlg,
			TEXT("検索に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return;
	}

	// リストコントロールに表示する
	data.ShowUserSearchList(hList);

	// 検索結果の件数を取得する
	searchCount = data.GetUserSearchFileCount();

	// 検索結果の件数をメッセージボックスで表示する
	if (searchCount == 0)
	{
		// 一致するものが見つからなかったとき
		MessageBox(
			hDlg,
			TEXT("一致するものが見つかりませんでした。"),
			TEXT("検索結果"),
			MB_ICONINFORMATION | MB_OK);
	}
	else
	{
		// 一致するものが見つかったとき
		char lpText[256];		// メッセージボックス内のテキスト
		char countText[4];		// long型をchar型に変換した件数

		// long型をchar型に変換する
		sprintf(countText, "%d", searchCount);

		// lpTextにコピーする
		strcpy(lpText, countText);				// 件数をコピー
		strcat(lpText, "件見つかりました。");	// 文字列を連結	

		// メッセージボックスを表示
		MessageBox(
			hDlg,
			lpText,
			TEXT("検索結果"),
			MB_ICONINFORMATION | MB_OK);
	}

	// userPath3のファイルを削除する
	data.DeleteSearchFile();
}


// 削除ボタンが押されたときの処理
void PushDeleteButton(HWND hDlg)
{
	// リストコントロールのハンドルを取得
	HWND hList = GetDlgItem(hDlg, IDC_LIST_NAMELIST);

	// DATAクラスのインスタンス
	DATA userdata;

	// 削除するユーザデータの番号(プライマリキー)
	long number = ListView_GetNextItem(hList, -1, LVNI_ALL | LVNI_SELECTED);

	// 削除
	if (number == -1)
	{
		// 何も選択されていないときのメッセージボックス
		MessageBox(
			hDlg,
			TEXT("削除する項目を選択してください。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);
	}
	else if (userdata.UserDelete(number))
	{
		// ユーザリストを更新する
		UpdateUserList(hDlg);

		// 削除成功のメッセージボックスを表示
		MessageBox(
			hDlg,
			TEXT("削除が完了しました。"),
			TEXT("削除完了"),
			MB_ICONINFORMATION | MB_OK);
	}
	else
	{
		// ユーザリストを更新する
		UpdateUserList(hDlg);

		// 削除失敗のメッセージボックスを表示
		MessageBox(
			hDlg,
			TEXT("削除に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);
	}
}


// リセットボタンが押されたときの処理
void PushResetButton(HWND hDlg)
{
	// リセットに失敗したとき
	if (!DoResetText(hDlg))
	{
		// 削除失敗のメッセージボックスを表示
		MessageBox(
			hDlg,
			TEXT("リセットに失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);
	}
}

// 実際にリセットを行う
bool DoResetText(HWND hDlg)
{
	// それぞれをリセット
	if (!SetDlgItemText(hDlg, IDC_EDIT_GROUP, TEXT(""))
		|| !SetDlgItemText(hDlg, IDC_EDIT_NAME, TEXT(""))
		|| !SetDlgItemText(hDlg, IDC_EDIT_ADDRESS, TEXT(""))
		|| !SetDlgItemText(hDlg, IDC_EDIT_TEL, TEXT(""))){
		return false;
	}

	return true;
}


// エディットボックスを指定の文字列で初期化する
bool InitEditBox(HWND hDlg2)
{
	// 初期化に失敗したとき
	if (!DoInitEditBox(hDlg2))
	{
		// 初期化失敗のメッセージボックスを表示
		MessageBox(
			hDlg2,
			TEXT("初期化に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return false;
	}

	return true;
}

// 実際にエディットボックスを指定の文字列で初期化する
bool DoInitEditBox(HWND hDlg2)
{
	// hDlg(親ウィンドウ)のウィンドウハンドル
	HWND hDlg;
	// リストコントロールのハンドル
	HWND hList;
	// リストコントロールで選択されている項目の番号
	int index;
	// 取得したUSERDATA構造体
	USERDATA userdata;
	// 番号をint→charに変換した文字列のバッファ
	char numStr[4];


	// 親ウィンドウhDlgのウィンドウハンドルを取得
	if ((hDlg = GetWindow(hDlg2, GW_OWNER)) == NULL){
		return false;
	}

	// リストコントロールのハンドルを取得
	if ((hList = GetDlgItem(hDlg, IDC_LIST_NAMELIST)) == NULL){
		return false;
	}

	// リストコントロールで選択中のアイテムを取得
	if ((index = ListView_GetNextItem(hList, -1,
		LVNI_ALL | LVNI_SELECTED)) == -1){
		return false;
	}

	// USERDATA構造体を取得
	DATA data;									// DATAクラスのインスタンス
	userdata = data.GetUserDataIndex(index);	// ファイルからindex番目の構造体を取得する

	// USERDATA構造体が正しく取得できていないとき
	if (userdata.number == -1){
		return false;
	}

	// 番号を設定
	_itoa(userdata.number, numStr, 10);	// 番号をint→charに変換
	if (!SetDlgItemText(hDlg2, IDC_EDIT_NUMBER2, numStr)){
		return false;
	}

	// グループ名を設定
	if (!SetDlgItemText(hDlg2, IDC_EDIT_GROUP2, userdata.group)){
		return false;
	}

	// 名前を設定
	if (!SetDlgItemText(hDlg2, IDC_EDIT_NAME2, userdata.name)){
		return false;
	}

	// 住所を設定
	if (!SetDlgItemText(hDlg2, IDC_EDIT_ADDRESS2, userdata.address)){
		return false;
	}

	// 電話番号を設定
	if (!SetDlgItemText(hDlg2, IDC_EDIT_TEL2, userdata.tel)){
		return false;
	}

	return true;
}


// 確定ボタンが押されたときの処理
void PushOkButton(HWND hDlg2)
{
	// 失敗したとき
	if (!(DoEditUserdata(hDlg2)))
	{
		// 編集にに失敗したときのメッセージボックス
		MessageBox(
			hDlg2,
			TEXT("編集に失敗しました。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);
	}
	else
	{
		// 削除成功のメッセージボックスを表示
		MessageBox(
			hDlg2,
			TEXT("編集が完了しました。"),
			TEXT("編集完了"),
			MB_ICONINFORMATION | MB_OK);
	
		// 編集フォーム(ダイアログ)を閉じる
		EndDialog(hDlg2, TRUE);
	}
}

// 実際に編集を行うときの処理
bool DoEditUserdata(HWND hDlg2)
{
	// DATAクラスのインスタンス
	DATA data;
	// USERDATAの構造体(Newが編集後・Oldが編集前)
	USERDATA userDataNew, userDataOld;
	// 文字列の番号
	char numStr[4];
	// hDlg(親ウィンドウ)のウィンドウハンドル
	HWND hDlg;

	// エディットコントロールから番号を取得する
	GetDlgItemText(hDlg2, IDC_EDIT_NUMBER2, numStr, sizeof(numStr));	// バッファに保存
	userDataNew.number = atol(numStr);									// longに変換
	// エディットコントロールからグループ名を取得
	GetDlgItemText(hDlg2, IDC_EDIT_GROUP2, userDataNew.group, sizeof(userDataNew.group));
	// エディットコントロールから名前を取得
	GetDlgItemText(hDlg2, IDC_EDIT_NAME2, userDataNew.name, sizeof(userDataNew.name));
	// エディットコントロールから住所を取得
	GetDlgItemText(hDlg2, IDC_EDIT_ADDRESS2, userDataNew.address, sizeof(userDataNew.address));
	// エディットコントロールから電話番号を取得
	GetDlgItemText(hDlg2, IDC_EDIT_TEL2, userDataNew.tel, sizeof(userDataNew.tel));

	// ファイルからUSERDATA構造体を取得する
	userDataOld = data.GetUserDataNumber(userDataNew.number);

	// ファイルからの取得に失敗したとき
	if (userDataOld.number == -1){
		return false;
	}

	// 変更前と変更後が変わっていないとき
	if (!strcmp(userDataNew.group, userDataOld.group)			// グループ名を比較
		&& !strcmp(userDataNew.name, userDataOld.name)			// 名前を比較
		&& !strcmp(userDataNew.address, userDataOld.address)	// 住所を比較
		&& !strcmp(userDataNew.tel, userDataOld.tel))			// 電話番号を比較
	{
		// 編集箇所がないとき
		MessageBox(
			hDlg2,
			TEXT("変更箇所がありません。"),
			TEXT("エラー"),
			MB_ICONEXCLAMATION | MB_OK);

		return false;
	}

	// 編集を行う
	if (!data.UserEdit(userDataNew)){
		return false;
	}

	// 親ウィンドウhDlgのウィンドウハンドルを取得
	if ((hDlg = GetWindow(hDlg2, GW_OWNER)) == NULL){
		return false;
	}

	// ユーザリストを更新する
	UpdateUserList(hDlg);

	return true;
}

 

UserDataClass.cppとUserDataClass.hは、主にデータファイル(外部ファイル)とやり取りするためのクラスが記述されたソースコードです。

// ユーザデータクラスのソースファイル

#define _CRT_SECURE_NO_WARNINGS

#include <Windows.h>
#include <commctrl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "resource.h"
#include "UserDataClass.h"

// ↓各自書き換える
// ユーザデータ保存パス
char* userPath = "C:\\";
// ユーザデータのコピー先の保存パス
char* userPath2 = "C:\\";
// ユーザデータの検索結果の保存パス
char* userPath3 = "C:\\";
// ↑各自書き換える

// コンストラクタ
DATA::DATA()
{

}

// デストラクタ
DATA::~DATA()
{

}


// 自動で番号を作成(エラーは-1を返却)
long DATA::MakeNumber()
{
	// ファイルポインタ
	FILE *fp;

	// 番号(プライマリキー)の最大値
	long max = 1;

	// 返却する番号
	long primaryNumber = 1;

	// データの個数を求める
	long dataCount = GetUserFileCount();

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL){
		return -1;
	}

	// 登録件数が0のとき
	if (dataCount == 0)
	{
		// ファイルクローズ
		fclose(fp);
		// 番号は1を返却
		return 1;
	}

	// ユーザデータの番号(プライマリキー)の最大値を取得する
	for (long count = 1; count <= dataCount; count++)
	{
		// USDRDATA構造体
		USERDATA userdata;

		// sizeof(DATA)分のメモリを読み出す
		fread(&userdata, sizeof(USERDATA), 1, fp);

		// 最大値と比較する
		if (max < userdata.number){
			max = userdata.number;
		}
	}

	// ユーザデータの番号(プライマリキー)が1~maxまで
	// すべて埋まっていたとき
	if (dataCount == max)
	{
		// ファイルクローズ
		fclose(fp);
		// (max + 1)を返却
		return max + 1;
	}

	// シークを先頭に戻す
	fseek(fp, 0, SEEK_SET);

	// 重複しない番号(プライマリキー)を取得する
	// 候補の番号を動かす(候補は1~max)
	for (long count = 1; count <= max; count++)
	{
		// 登録してある番号(プライマリキー)と重複しないか確認する
		for (long count2 = 1; count <= dataCount; count2++)
		{
			// USDRDATA構造体
			USERDATA userdata;

			// count番目のからsizeof(DATA)分のメモリを読み出す
			fread(&userdata, sizeof(USERDATA), 1, fp);

			// 候補の番号が存在したとき
			if (count == userdata.number){
				goto CORRECT;
			}

			// 候補の番号が最後まで存在しなかったとき
			if (count2 == dataCount){
				primaryNumber = count;
				goto END;
			}
		}

		// 候補の番号が存在したとき
		CORRECT:

		// シークを先頭に戻す
		fseek(fp, 0, SEEK_SET);
	}

	// 候補の番号が最後まで存在しなかったとき
	END:

	// ファイルクローズ
	fclose(fp);

	return primaryNumber;
}


// ファイルから番号(上から)を指定してUSERDATAを取得(失敗時はnumber = -1)(一番上はindex = 0)
USERDATA DATA::GetUserDataIndex(int index)
{
	// ファイルポインタ
	FILE *fp;

	// 返却するUSERDATA構造体
	USERDATA userdata;

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL){
		//userdataを初期化
		userdata.number = -1;
		return userdata;
	}

	// シークを指定のindexに移動
	fseek(fp, sizeof(USERDATA) * index, SEEK_SET);

	// バッファにデータを移す
	fread(&userdata, sizeof(USERDATA), 1, fp);

	// ファイルクローズ
	fclose(fp);

	return userdata;
}


// ファイルから番号(プライマリキー)を指定してUSERDATAを取得(失敗時はnumber = -1)
USERDATA DATA::GetUserDataNumber(int number)
{
	// ファイルポインタ
	FILE *fp;
	// USERDATA構造体
	USERDATA userdata;
	// ユーザデータの登録件数
	long userCount = GetUserFileCount();

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL)
	{
		// 失敗したとき
		userdata.number = -1;
		return userdata;
	}

	// 番号(プライマリキー)のファイルを取得する
	for (long count = 1; count <= userCount; count++)
	{
		// USERDATA構造体を初期化
		userdata.number = -1;
		memset(userdata.group, '\0', sizeof(userdata.group));
		memset(userdata.name, '\0', sizeof(userdata.name));
		memset(userdata.address, '\0', sizeof(userdata.address));
		memset(userdata.tel, '\0', sizeof(userdata.tel));

		// USERDATA構造体を取得
		fread(&userdata, sizeof(USERDATA), 1, fp);

		// プライマリキーと一致したとき
		if (userdata.number == number){
			fclose(fp);
			return userdata;
		}
	}

	// ファイルクローズ
	fclose(fp);

	// どれとも一致しなかったとき
	userdata.number = -1;
	return userdata;
}


// userPath3のファイルを削除する
bool DATA::DeleteSearchFile()
{
	// 【方針】
	// ・セキュリティに注意する
	// ・ファイルを削除するときは、ファイルが存在するのを確認してからすること

	// ファイルポインタ
	FILE *fp;

	// ファイルが存在しないとき(ファイルオープンに失敗したとき)
	if ((fp = fopen(userPath3, "rb")) == NULL){
		return true;
	}

	// ファイルクローズ
	fclose(fp);

	// ファイルを削除する
	if (remove(userPath3)){
		return false;
	}

	return true;
}


// ユーザデータを表示する
bool DATA::ShowUserList(HWND hList)
{
	// ファイルポインタ
	FILE *fp;
	// ユーザデータのファイルの件数を取得
	long userCount = GetUserFileCount();

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL){
		return false;
	}

	// アイテムをすべて削除する
	if (!(ListView_DeleteAllItems(hList))){
		return false;
	}

	// 表示する
	for (long count = 0; count < userCount; count++)
	{
		// USERDATAのバッファ
		USERDATA userdata;
		// アイテムの変数
		LV_ITEM item;

		// バッファに読み出す
		fread(&userdata, sizeof(USERDATA), 1, fp);

		// 番号を追加
		item.mask = LVIF_TEXT;
		char number[4];
		_itoa(userdata.number, number, 10);
		item.pszText = number;
		item.iItem = count;
		item.iSubItem = 0;
		ListView_InsertItem(hList, &item);	// アイテムを挿入する

		// グループを追加
		item.pszText = userdata.group;
		item.iItem = count;
		item.iSubItem = 1;
		ListView_SetItem(hList, &item);	// アイテムをセットする

		// 名前を追加
		item.pszText = userdata.name;
		item.iItem = count;
		item.iSubItem = 2;
		ListView_SetItem(hList, &item);	// アイテムをセットする

		// 住所を追加
		item.pszText = userdata.address;
		item.iItem = count;
		item.iSubItem = 3;
		ListView_SetItem(hList, &item);	// アイテムをセットする

		// 電話番号を追加
		item.pszText = userdata.tel;
		item.iItem = count;
		item.iSubItem = 4;
		ListView_SetItem(hList, &item);	// アイテムをセットする
	}

	// ファイルクローズ
	fclose(fp);

	return true;
}


// ユーザデータの検索結果を外部ファイルを参照してリストに表示する
bool DATA::ShowUserSearchList(HWND hList)
{
	// 【方針】
	// ・userPath3のファイル内容を引数のハンドルのリストコントロールに表示します。
	// ファイルポインタ
	FILE *fp;
	// ユーザデータのファイルの件数を取得
	long userCount = GetUserSearchFileCount();

	// ファイルオープン
	if ((fp = fopen(userPath3, "rb")) == NULL){
		return false;
	}

	// アイテムをすべて削除する
	if (!(ListView_DeleteAllItems(hList))){
		return false;
	}

	// 表示する
	for (long count = 0; count < userCount; count++)
	{
		// USERDATAのバッファ
		USERDATA userdata;
		// アイテムの変数
		LV_ITEM item;

		// バッファに読み出す
		fread(&userdata, sizeof(USERDATA), 1, fp);

		// 番号を追加
		item.mask = LVIF_TEXT;
		char number[4];
		_itoa(userdata.number, number, 10);
		item.pszText = number;
		item.iItem = count;
		item.iSubItem = 0;
		ListView_InsertItem(hList, &item);	// アイテムを挿入する

		// グループを追加
		item.pszText = userdata.group;
		item.iItem = count;
		item.iSubItem = 1;
		ListView_SetItem(hList, &item);	// アイテムをセットする

		// 名前を追加
		item.pszText = userdata.name;
		item.iItem = count;
		item.iSubItem = 2;
		ListView_SetItem(hList, &item);	// アイテムをセットする

		// 住所を追加
		item.pszText = userdata.address;
		item.iItem = count;
		item.iSubItem = 3;
		ListView_SetItem(hList, &item);	// アイテムをセットする

		// 電話番号を追加
		item.pszText = userdata.tel;
		item.iItem = count;
		item.iSubItem = 4;
		ListView_SetItem(hList, &item);	// アイテムをセットする
	}

	// ファイルクローズ
	fclose(fp);

	return true;
}


// ユーザデータのファイルサイズを取得する
size_t DATA::GetUserFileSize()
{
	// ファイルポインタ
	FILE *fp;

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL){
		// 失敗したとき
		return 0;
	}

	// シークを移動
	fseek(fp, 0, SEEK_END);

	// ファイルのサイズを計算
	size_t fileSize = ftell(fp);

	// ファイルクローズ
	fclose(fp);

	return fileSize;
}


// ユーザデータファイルの件数を取得する
long DATA::GetUserFileCount()
{
	return GetUserFileSize() / sizeof(USERDATA);
}


// 番号(プライマリキー)からインデックスを取得(上はindex = 0)(失敗時はindex = -1)
long DATA::GetUserFileIndex(long number)
{
	// ファイルポインタ
	FILE *fp;
	// USERDATA構造体
	USERDATA userdata;
	// 登録件数
	long userCount = GetUserFileCount();

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL){
		return -1;
	}

	// 検索する
	for (long index = 0; index < userCount; index++)
	{
		// USERDATA構造体を初期化
		userdata.number = -1;
		memset(userdata.group, '\0', sizeof(userdata.group));
		memset(userdata.name, '\0', sizeof(userdata.name));
		memset(userdata.address, '\0', sizeof(userdata.address));
		memset(userdata.tel, '\0', sizeof(userdata.tel));

		// USERDATA構造体を取得
		fread(&userdata, sizeof(USERDATA), 1, fp);

		// 番号と一致したとき
		if (userdata.number == number){
			fclose(fp);
			return index;
		}
	}

	// ファイルクローズ
	fclose(fp);

	return -1;
}


// ユーザデータのファイルサイズを取得する
size_t DATA::GetUserSearchFileSize()
{
	// ファイルポインタ
	FILE *fp;

	// ファイルオープン
	if ((fp = fopen(userPath3, "rb")) == NULL){
		// 失敗したとき
		return 0;
	}

	// シークを移動
	fseek(fp, 0, SEEK_END);

	// ファイルのサイズを計算
	size_t fileSize = ftell(fp);

	// ファイルクローズ
	fclose(fp);

	return fileSize;
}


// ユーザデータの検索用ファイルから件数を取得する
long DATA::GetUserSearchFileCount()
{
	return GetUserSearchFileSize() / sizeof(USERDATA);
}


// データを追加する
bool DATA::UserAdd(
	char* group,
	char* name,
	char* address,
	char* tel)
{
	// 文字列を構造体に移す
	USERDATA data;						// 構造体
	if ((data.number = MakeNumber()) == -1){	// 番号を設定
		return false;
	}
	strcpy(data.group, group);		// グループ名をコピー
	strcpy(data.name, name);		// 名前をコピー
	strcpy(data.address, address);	// 住所をコピー
	strcpy(data.tel, tel);			// 電話番号をコピー

	// ファイルポインタ
	FILE *fp;

	// ファイルオープン
	if ((fp = fopen(userPath, "ab")) == NULL){
		return false;
	}

	// ファイルを書き込む
	fwrite(&data, sizeof(USERDATA), 1, fp);

	// ファイルクローズ
	fclose(fp);

	return true;
}

// ユーザデータを編集する(引数の構造体の番号(プライマリキー)は一意である必要があります)
bool DATA::UserEdit(USERDATA userdata)
{
	// 【方針】
	// ・このメソッドにはバグがあります。
	// ・ファイルをabで開いたときは、シークは末尾に固定され
	//   fseekで移動することはできないようです。

	// ファイルポインタ
	FILE *fp, *fp2;
	// インデックスを取得
	long index = GetUserFileIndex(userdata.number);
	// 登録件数を取得
	long userCount = GetUserFileCount();

	// インデックスの取得に失敗したとき
	if (index == -1){
		return false;
	}

	// 登録件数の取得に失敗したとき
	if (userCount == -1){
		return false;
	}

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL){
		return false;
	}

	// ユーザデータのコピーを作成ファイルを作成
	if ((fp2 = fopen(userPath2, "wb")) == NULL){
		fclose(fp);
		return false;
	}

	// ユーザファイルをコピー先に書き込む(indexのときは引数を書き込む)
	for (long index2 = 0; index2 < userCount; index2++)
	{
		// 編集するファイルのとき
		if (index2 == index)
		{
			// 編集後のUSERDATA構造体を書き込む
			fwrite(&userdata, sizeof(USERDATA), 1, fp2);

			// 元ファイルのシークを移動する
			fseek(fp, sizeof(USERDATA), SEEK_CUR);
		}
		// 編集しないファイルのとき
		else
		{
			// USERDATA構造体のバッファ
			USERDATA userdata_buf;

			// 元ファイルよりバッファに移す
			fread(&userdata_buf, sizeof(USERDATA), 1, fp);

			// コピー先のファイルに書き込む
			fwrite(&userdata_buf, sizeof(USERDATA), 1, fp2);
		}
	}

	// ファイルクローズ
	fclose(fp);
	fclose(fp2);

	// userPathのファイルを削除
	if (remove(userPath)){
		return false;
	}

	// userPath2のファイル名をuserPathに変更
	if (rename(userPath2, userPath)){
		return false;
	}

	return true;
}


// ユーザデータを検索する(引数は検索キー)
bool DATA::UserSearch(char* group, char* name, char* address, char* tel)
{
	// 【方針】
	// ・引数の検索キーのファイルをuserPath3に外部ファイルとして書き出す
	// ・ShowUserSearchList関数でリストコントロールに検索結果を表示する
	// ・検索結果用の外部ファイルの使用が終わったらDeleteSearchFileメソッドで削除する

	// ファイルポインタ
	FILE *fp, *fp2;
	// ファイルの件数を取得
	long userCount = GetUserFileCount();
	
	// 検索フラッグ(検索キーが入力されている項目にフラッグを立てる)
	bool groupFlag = true;		// グループ名
	bool nameFlag = true;		// 名前
	bool addressFlag = true;	// 住所
	bool telFlag = true;		// 電話番号
	
	// userPath3のファイルを削除する
	if (!DeleteSearchFile()){
		return false;
	}

	// 検索キーのフラッグを立てる
	if (!strcmp(group, "")){	// グループ名が入力されていないとき
		groupFlag = false;
	}
	if (!strcmp(name, "")){		// 名前が入力されていないとき
		nameFlag = false;
	}
	if (!strcmp(address, "")){	// 住所が入力されていないとき
		addressFlag = false;
	}
	if (!strcmp(tel, "")){		// 電話番号が入力されていないとき
		telFlag = false;
	}

	// ファイルオープン(userPath)
	if ((fp = fopen(userPath, "rb")) == NULL){
		return false;
	}

	// userPath3のファイル作成(ファイルオープン)
	if ((fp2 = fopen(userPath3, "wb")) == NULL){
		fclose(fp);
		return false;
	}

	// userPath3に該当項目を書き込む
	for (long count = 0; count < userCount; count++)
	{
		// USERDATA構造体のバッファ
		USERDATA userdata;

		// バッファに読み出す
		fread(&userdata, sizeof(USERDATA), 1, fp);

		// 検索キーと一致したとき
		if (((strstr(userdata.group, group) != NULL) || !groupFlag)
			&& ((strstr(userdata.name, name) != NULL) || !nameFlag)
			&& ((strstr(userdata.address, address) != NULL) || !addressFlag)
			&& ((strstr(userdata.tel, tel) != NULL) || !telFlag))
		{
			// 検索用ファイルに書き込む
			fwrite(&userdata, sizeof(USERDATA), 1, fp2);
		}
	}

	// ファイルクローズ
	fclose(fp);
	fclose(fp2);

	return true;
}


// ユーザデータを削除する(引数はリストコントロールの先頭からの番号)
bool DATA::UserDelete(long number)
{
	// 【方針】
	// ・userPathの内容をsizeof(DATA)バイトずつuserPath2に書き込む
	// ・userPathのファイルを削除する
	// ・userPath2のファイル名をuserPathに変更する

	// userPathの件数を取得する
	long userCount = GetUserFileCount();

	// ファイルポインタ
	FILE *fp, *fp2;

	// ファイルオープン
	if ((fp = fopen(userPath, "rb")) == NULL){
		return false;
	}

	// ファイルオープン
	if ((fp2 = fopen(userPath2, "ab")) == NULL){
		fclose(fp);
		return false;
	}

	// size(DATA) バイトずつ読み出す
	for (long count = 0; count < userCount; count++)
	{
		// USERDATA構造体
		USERDATA userdata;

		// userPathのsizeof(DATA)分ををuserdataにコピー
		fread(&userdata, sizeof(USERDATA), 1, fp);

		// 削除する番号ではないとき
		if (count != number)
		{
			// userPath2にコピー
			fwrite(&userdata, sizeof(USERDATA), 1, fp2);
		}
	}

	// ファイルクローズ
	fclose(fp);
	fclose(fp2);

	// userPathのファイルを削除
	if (remove(userPath)){
		return false;
	}

	// userPath2のファイル名をuserPathに変更
	if (rename(userPath2, userPath)){
		return false;
	}
	
	return true;
}
// ユーザデータクラスのヘッダ

#include <Windows.h>

// 保存パス
extern char* userPath;		// ユーザデータの保存パス
extern char* userPath2;		// ユーザデータのコピー先の保存パス
extern char* userPath3;		// ユーザデータの検索結果の保存パス

// ユーザデータ構造体
struct USERDATA
{
	long number;		// 番号(プライマリキー)
	char group[256];	// グループ名
	char name[256];		// 名前
	char address[256];	// 住所
	char tel[256];		// 電話番号
};

// グループデータ構造体
struct GROUPDATA
{
	long number;		// 番号(プライマリキー)
	char group[256];	// グループ名
};

// データのクラス
class DATA
{
	// コンストラクタ・デストラクタ
public:
	DATA();		// コンストラクタ
	~DATA();	// デストラクタ

	// 非公開メソッド
private:
	long MakeNumber();			// 自動で番号を作成(エラーは-1を返却)

	// 構造体に関するゲッタ
public:
	// ファイルから番号(上から)を指定してUSERDATAを取得(失敗時はnumber = -1)(一番上はindex = 0)
	USERDATA GetUserDataIndex(int index);
	// ファイルから番号を(プライマリキー)を指定してUSERDATAを取得(失敗時はnumber = -1)
	USERDATA GetUserDataNumber(int number);

	// 表示に関して
public:
	// ユーザデータをリストに表示する
	bool ShowUserList(HWND hList);
	// ユーザデータの検索結果を外部ファイルを参照してリストに表示する
	bool ShowUserSearchList(HWND hList);
	
	// 削除に関して
public:
	// userPath3のファイルを削除する
	bool DeleteSearchFile();

	// ファイルのサイズ・個数に関するゲッタ
public:
	// ユーザデータのファイルサイズを取得する
	size_t GetUserFileSize();
	// ユーザデータファイルの件数を取得する
	long GetUserFileCount();
	// 番号(プライマリキー)からインデックスを取得(上はindex = 0)(失敗時はindex = -1)
	long GetUserFileIndex(long number);
	// ユーザデータのファイルサイズを取得する
	size_t GetUserSearchFileSize();
	// ユーザデータの検索用ファイルから件数を取得する
	long GetUserSearchFileCount();


	// 登録・編集・検索・削除
public:
	// ユーザデータを追加する
	bool UserAdd(
		char* group, 
		char* name,
		char* address, 
		char* tel);
	// ユーザデータを編集する(引数の構造体の番号(プライマリキー)は一意である必要があります)
	bool UserEdit(USERDATA userdata);
	// ユーザデータを検索する(引数は検索キー)
	bool UserSearch(char* group, char* name, char* address, char* tel);
	// ユーザデータを削除する(引数はリストコントロールの先頭からの番号)
	bool UserDelete(long number);
};

問題点

問題点は2つあります。

検索してから元の画面に戻れない

検索ボタンを押すと、下のリストに結果を表示するんですが、見終わった後に元の画面に戻れません。

それに関するコーディングをしていませんでした(;´・ω・)

ファイルの保存先を指定できない

ファイルの保存先を指定できないという致命的な欠点があります。

ファイルの保存先はコンパイルするときにソースコードに書いてある内容で固定されてしまいます(@_@;)

これは、困った困ったこまどり姉妹(古いw)

 

ファイルの保存先を変更できるということは、変更された情報をどこかに保存する必要があるのだと思います。

ということは、インストールが必要なプログラムということになるんでしょうか?

もうよくわかりません( ;∀;)

まとめ

とりあえず、このソフトウェア開発は、これで終わりにしたいと思います。

全体的な仕組みはできたのでこれで十分です。Windows APIの勉強になりました。

 

以上です!

ノシ

 

PS

ソフトウェア開発が終わったので、次はマイコンをやります!PICマイコンですよ!!

 - 技術系