[1.1] 自分が今行いたい事は何か
現在ウィンドウの座標を保存して次の起動時に同じ場所に起動するプログラムを作成したいと考えています。
[1.2] どのように取り組んだか(プログラムコードがある場合記載)
#include "windows.h"
#include "resource.h"
#include "stdio.h"
#include "tchar.h"
// 定数の宣言
#define SKYPEIDEDIT 101 // SkypeID欄
#define THEMEEDIT 102 // テーマ欄
#define NAMEEDIT 103 // 凸者欄
#define LISTEDIT 104 // 過去凸者欄
#define WAITPLUSBTN 105 // 待ち時間プラスボタン
#define WAITMINUSBTN 106 // 待ち時間マイナスボタン
#define ENDBTN 107 // 凸終了ボタン
#define COUNTPLUSBTN 108 // 人数プラスボタン
#define COUNTMINUSBTN 109 // 人数マイナスボタン
#define DELETEBTN 110 // 人数カウント削除ボタン
#define WINDOWWIDTH 640 // ウィンドウの横幅
#define WINDOWHEIGHT 480 // ウィンドウの縦幅
/**
* @brief ウィンドウ状態をINIファイルの [window] セクションに保存する。
* @param[in] hWnd 対象となるウィンドウハンドル。
* @param[in] iniFilePath INIファイルのパス。
*
* WM_DESTROY メッセージ内で呼び出すこと。
*/
void SaveWindowState(HWND hWnd, LPCTSTR iniFilePath);
/**
* @brief INIファイルの [window] セクションからウィンドウ状態を復元する。
* @param[in] hWnd 対象となるウィンドウハンドル。
* @param[in] iniFilePath INIファイルのパス。
*
* WM_CREATE 又は WM_INITDIALOG メッセージ内で呼び出すこと。
*/
void LoadWindowState(HWND hWnd, LPCTSTR iniFilePath);
// ウィンドウサイズ取得用変数
RECT recWindow; // ウィンドウサイズ取得用変数
LPCTSTR iniFilePath = "dekotan.ini";
// 関数のプロトタイプ宣言
LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
// エントリポイント
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lPCmdLine,
int nCmdShow)
{
WNDCLASSEX wcex; // ウィンドウクラス構造体
HWND hWnd; // ウィンドウハンドル
MSG msg; // メッセージ構造体
// ウィンドウクラス構造体を設定します。
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 = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
wcex.hCursor = LoadCursor(NULL, IDC_ARROW);
wcex.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcex.lpszMenuName = NULL;
wcex.lpszClassName = "凸たん";
wcex.hIconSm = LoadIcon(hInstance, MAKEINTRESOURCE(IDI_ICON1));
// ウィンドウクラスを登録します。
RegisterClassEx(&wcex);
// ウィンドウを作成します。
hWnd = CreateWindow(wcex.lpszClassName, // ウィンドウクラス名
"凸たん ver1.5.0", // キャプションの文字列
WS_OVERLAPPEDWINDOW | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, // ウィンドウのスタイル
0, // 水平位置
0, // 垂直位置
WINDOWWIDTH, // 幅
WINDOWHEIGHT, // 高さ
NULL, // 親ウィンドウ
NULL, // ウィンドウメニュー
hInstance, // インスタンスハンドル
NULL); // WM_CREATE情報
// ウィンドウを表示します。
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// メッセージループ
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
// 戻り値を返します。
return msg.wParam;
}
// ウィンドウプロシージャ
LRESULT CALLBACK WndProc(HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam)
{
static HWND skypeidEdit; // SkypeID欄
static HWND themeEdit; // テーマ欄
static HWND deko_nameEdit; // 凸者欄
static HWND listEdit; // 過去凸者欄
static HWND time_plusBtn; // 待ち時間カウントプラスボタン
static HWND time_minusBtn; // 待ち時間カウントマイナスボタン
static HWND deko_endBtn; // 凸終了ボタン
static HWND deko_deleteBtn; // 人数カウント削除ボタン
static HWND count_plusBtn; // 人数カウントプラスボタン
static HWND count_minusBtn; // 人数カウントマイナスボタン
HDC hDC; // デバイスコンテキストのハンドル
PAINTSTRUCT ps; // PAINTSTRUCT構造体
// ファイルポインタ
FILE *fp;
// 文字列制御変数
static TCHAR skypeid[999]; // スカイプ文字列バッファ
static TCHAR deko_name[999]; // 凸者文字列バッファ
static TCHAR add[999]; // 追加用過去凸者文字列バッファ
static TCHAR number[999]; // ○人目のカウント数文字列バッファ
static TCHAR list[999]; // 過去凸者リスト文字列バッファ
static TCHAR theme[999]; // テーマ文字列バッファ
static TCHAR deko_time[999]; // 待ち時間文字列バッファ
static TCHAR number_wait[999]; // 待ち人数文字列バッファ
// カウンター変数
static int time = 0; // 時間カウンター
static int wait = 0; // 待ち人数カウンター
static int person = 1; // 凸人数カウンター
// 座標制御変数
// 構造体変数宣言
typedef struct data{
int x;
int y;
int w;
int h;
}DATA;
DATA skypeid_data = { 70, 5, 200, 25}; // SkypeIDデータ構造体
DATA *skype_p = &skypeid_data; // SkypeIDデータ構造体のアドレス格納ポインタ
DATA theme_data = { 70, 35, 550, 150}; // テーマデータ構造体
DATA *theme_p = &theme_data; // テーマデータ構造体のアドレス格納ポインタ
DATA deko_name_data = { 70, 190, 250, 25}; // 凸者欄データ構造体
DATA *deko_name_p = &deko_name_data; // 凸者欄データ構造体のアドレス格納ポインタ
DATA list_data = { 70, 245, 550, 180}; // 過去凸者リスト欄データ構造体
DATA *list_p = &list_data; // 過去凸者リスト欄データ構造体のアドレス格納ポインタ
DATA time_data = { 410, 8, 15, 20}; // 待ち時間ボタンデータ構造体
DATA *time_p = &time_data; // 待ち時間ボタンデータ構造体のアドレス格納ポインタ
DATA number_data = { 130, 220, 15, 20}; // 待ち人数ボタンデータ構造体
DATA *number_p = &number_data; // 待ち人数ボタンデータ構造体のアドレス格納ポインタ
DATA end_data = { 330, 190, 60, 25}; // 凸終了ボタンデータ構造体
DATA *end_p = &end_data; // 凸終了ボタンデータ構造体のアドレス格納ポインタ
int pm_btn_interval = 20; // +-ボタンの間隔
DATA delete_data = { 400, 190, 100, 25}; // 削除ボタンデータ構造体
DATA *delete_p = &delete_data; // 削除ボタンデータ構造体のアドレス格納ポインタ
char filename[] = {"data.txt"};
// メッセージの種類に応じて処理を分岐します。
switch (message)
{
case WM_CREATE:
// ウィンドウが作成されたときの処理
// 前回のウィンドウ情報をロード
LoadWindowState(hWnd,iniFilePath);
fp = fopen(filename, "r");
fgets(skypeid, 999, fp);
fclose(fp);
GetWindowRect( hWnd, &recWindow ); // ウィンドウサイズの取得
skypeidEdit = CreateWindow("EDIT",
skypeid,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER,
skype_p->x,
skype_p->y,
skype_p->w,
skype_p->h,
hWnd,
(HMENU)SKYPEIDEDIT,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
themeEdit = CreateWindow("EDIT",
theme,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE,
theme_p->x,
theme_p->y,
theme_p->w,
theme_p->h,
hWnd,
(HMENU)THEMEEDIT,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
deko_nameEdit = CreateWindow("EDIT",
deko_name,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_CENTER,
deko_name_p->x,
deko_name_p->y,
deko_name_p->w,
deko_name_p->h,
hWnd,
(HMENU)NAMEEDIT,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
listEdit = CreateWindow("EDIT",
list,
WS_CHILD | WS_VISIBLE | WS_BORDER | ES_LEFT | ES_MULTILINE,
list_p->x,
list_p->y,
list_p->w,
list_p->h,
hWnd,
(HMENU)LISTEDIT,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
time_plusBtn = CreateWindow("BUTTON",
"+",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
time_p->x,
time_p->y,
time_p->w,
time_p->h,
hWnd,
(HMENU)WAITPLUSBTN,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
time_minusBtn = CreateWindow("BUTTON",
"-",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
time_p->x + pm_btn_interval,
time_p->y,
time_p->w,
time_p->h,
hWnd,
(HMENU)WAITMINUSBTN,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
deko_endBtn = CreateWindow("BUTTON",
"凸終了",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
end_p->x,
end_p->y,
end_p->w,
end_p->h,
hWnd,
(HMENU)ENDBTN,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
/*削除ボタンクリエイト宣言
deko_deleteBtn = CreateWindow("BUTTON",
"カウント減少",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
delete_p->x,
delete_p->y,
delete_p->w,
delete_p->h,
hWnd,
(HMENU)DELETEBTN,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
*/
count_plusBtn = CreateWindow("BUTTON",
"+",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
number_p->x,
number_p->y,
number_p->w,
number_p->h,
hWnd,
(HMENU)COUNTPLUSBTN,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
count_minusBtn = CreateWindow("BUTTON",
"-",
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
number_p->x + pm_btn_interval,
number_p->y,
number_p->w,
number_p->h,
hWnd,
(HMENU)COUNTMINUSBTN,
(HINSTANCE)GetWindowLong(hWnd, GWL_HINSTANCE),
NULL);
return 0;
case WM_PAINT:
// クライアント領域の再描画処理
hDC = BeginPaint(hWnd, &ps);
TextOut(hDC, 5, 8, "skypeidID", 7);
TextOut(hDC, (280 + ((recWindow.right - recWindow.left) - WINDOWWIDTH)), 8, "凸時間", 6);
wsprintf(deko_time, "%d分位", time);
TextOut(hDC, (335 + ((recWindow.right - recWindow.left) - WINDOWWIDTH)), 8, deko_time, lstrlen(deko_time));
TextOut(hDC, 5, 38, "テーマ", 6);
wsprintf(number, "【%d人目】", person);
TextOut(hDC, 70, (170 + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)), number, lstrlen(number));
TextOut(hDC, 5, (195 + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)), "凸者", 4);
TextOut(hDC, 5, (220 + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)), "待ち", 4);
wsprintf(number_wait, "%d人", wait);
TextOut(hDC, 70, (220 + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)), number_wait, lstrlen(number_wait));
TextOut(hDC, 5, (245 + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)), "過去凸者", 8);
EndPaint(hWnd, &ps);
return 0;
case WM_COMMAND:
// ボタンがクリックされた時の処理
if (LOWORD(wParam) == WAITPLUSBTN)
{
time = time + 5;
}
if (LOWORD(wParam) == WAITMINUSBTN)
{
if (time > 0)
{
time = time - 5;
}
}
if (LOWORD(wParam) == ENDBTN)
{
GetWindowText(deko_nameEdit, deko_name, 999); // 凸者欄の入力文字を取得
GetWindowText(listEdit, add, 999); // 過去凸者欄の入力文字を取得
if (deko_name[0] != '\0') // 凸者欄が空欄でなければ名前の移動処理
{
wsprintf(number, "%d", person);
lstrcat(add, "【");
lstrcat(add, number);
lstrcat(add, "人目】");
lstrcat(add, deko_name);
lstrcat(add, ",");
SetWindowText(listEdit, add); // 凸者の名前を過去凸者欄に追加
SetWindowText(deko_nameEdit, ""); // 凸者欄を空白に変更
person++; // 凸者人数のカウントアップ
if (wait > 0) // 待ち人数のカウントが0でなければカウントダウン
{
wait--;
}
}
}
/*削除ボタン処理
if (LOWORD(wParam) == DELETEBTN)
{
if ( person > 1 )
{
person--;
}
}
*/
// 待ち人数のカウントアップ処理
if (LOWORD(wParam) == COUNTPLUSBTN)
{
wait++;
}
// 待ち人数のカウントダウン処理
if (LOWORD(wParam) == COUNTMINUSBTN)
{
if (wait > 0)
{
wait--;
}
}
InvalidateRect(hWnd, NULL, TRUE); // 再描画関数
return 0;
case WM_SIZE:
// ウィンドウのサイズが変更されたときの処理
GetWindowRect( hWnd, &recWindow ); // ウィンドウの座標取得
// コントロールを動的に座標移動処理
MoveWindow(skypeidEdit , skype_p->x , skype_p->y , (skype_p->w + (LOWORD(lParam) - WINDOWWIDTH)) , skype_p->h , TRUE);
MoveWindow(themeEdit , theme_p->x , theme_p->y , (theme_p->w + (LOWORD(lParam) - WINDOWWIDTH)) , (theme_p->h + ((HIWORD(lParam) - WINDOWHEIGHT))) , TRUE);
MoveWindow(deko_nameEdit , deko_name_p->x, (deko_name_p->y + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)) , (deko_name_p->w + (LOWORD(lParam) - WINDOWWIDTH)) , deko_name_p->h , TRUE);
MoveWindow(listEdit , list_p->x , (list_p->y + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)) , (list_p->w + (LOWORD(lParam) - WINDOWWIDTH)) , list_p->h , TRUE);
MoveWindow(time_plusBtn , (time_p->x + ((recWindow.right - recWindow.left) - WINDOWWIDTH)) , time_p->y , time_p->w , time_p->h , TRUE);
MoveWindow(time_minusBtn , (time_p->x + pm_btn_interval + ((recWindow.right - recWindow.left) - WINDOWWIDTH)) , time_p->y , time_p->w , time_p->h , TRUE);
MoveWindow(deko_endBtn , (end_p->x + ((recWindow.right - recWindow.left) - WINDOWWIDTH)) , (end_p->y + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)) , end_p->w , end_p->h , TRUE);
// 削除ボタンの座標移動
// MoveWindow(deko_deleteBtn , (delete_p->x + ((recWindow.right - recWindow.left) - WINDOWWIDTH)) , (delete_p->y + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)) , delete_p->w , delete_p->h , TRUE);
MoveWindow(count_plusBtn , number_p->x , (number_p->y + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)) , number_p->w , number_p->h , TRUE);
MoveWindow(count_minusBtn , number_p->x + pm_btn_interval , (number_p->y + ((recWindow.bottom - recWindow.top) - WINDOWHEIGHT)) , number_p->w , number_p->h , TRUE);
return 0;
case WM_DESTROY:
// ウィンドウが破棄されたときの処理
GetWindowText(skypeidEdit, skypeid, 999);
fp = fopen(filename, "w");
fputs(skypeid, fp);
fclose(fp);
// 現在のウィンドウの状態を保存
SaveWindowState(hWnd, iniFilePath);
PostQuitMessage(0);
return 0;
default:
// デフォルトの処理
return DefWindowProc(hWnd, message, wParam, lParam);
}
}
/**
* @brief RECT構造体の内容を [window] セクションに保存する。
* @param[in] pRect 対象となるRECT構造体。
* @param[in] iniFilePath INIファイルのパス。
*/
void SaveRect(const RECT* pRect, LPCTSTR iniFilePath)
{
TCHAR strNum[64];
_ltot(pRect->left, strNum, 10);
WritePrivateProfileString(
_T("window"), _T("left"), strNum, iniFilePath);
_ltot(pRect->top, strNum, 10);
WritePrivateProfileString(
_T("window"), _T("top"), strNum, iniFilePath);
_ltot(pRect->right, strNum, 10);
WritePrivateProfileString(
_T("window"), _T("right"), strNum, iniFilePath);
_ltot(pRect->bottom, strNum, 10);
WritePrivateProfileString(
_T("window"), _T("bottom"), strNum, iniFilePath);
}
/**
* @brief RECT構造体の内容を [window] セクションから復元する。
* @param[in,out] pRect 対象となるRECT構造体。
* @param[in] iniFilePath INIファイルのパス。
*
* 保存されていない場合は元データを書き換えない。
*/
void LoadRect(RECT* pRect, LPCTSTR iniFilePath)
{
pRect->left = (long)GetPrivateProfileInt(
_T("window"), _T("left"), pRect->left, iniFilePath);
pRect->top = (long)GetPrivateProfileInt(
_T("window"), _T("top"), pRect->top, iniFilePath);
pRect->right = (long)GetPrivateProfileInt(
_T("window"), _T("right"), pRect->right, iniFilePath);
pRect->bottom = (long)GetPrivateProfileInt(
_T("window"), _T("bottom"), pRect->bottom, iniFilePath);
}
void SaveWindowState(HWND hWnd, LPCTSTR iniFilePath)
{
// ウィンドウ位置・状態取得
WINDOWPLACEMENT wndPlace;
wndPlace.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hWnd, &wndPlace);
// ウィンドウ位置保存
SaveRect(&wndPlace.rcNormalPosition, iniFilePath);
// ウィンドウ最大化状態保存
if (wndPlace.showCmd == SW_SHOWMAXIMIZED)
{
WritePrivateProfileString(
_T("window"), _T("zoomed"), _T("1"), iniFilePath);
}
else
{
WritePrivateProfileString(
_T("window"), _T("zoomed"), _T("0"), iniFilePath);
}
}
void LoadWindowState(HWND hWnd, LPCTSTR iniFilePath)
{
// 既定のウィンドウ位置取得
WINDOWPLACEMENT wndPlace;
wndPlace.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(hWnd, &wndPlace);
RECT rcWnd = wndPlace.rcNormalPosition;
// ウィンドウ位置読み込み
LoadRect(&rcWnd, iniFilePath);
// 対象モニタの情報を取得
HMONITOR hMonitor = MonitorFromRect(
&rcWnd, MONITOR_DEFAULTTONEAREST);
MONITORINFO mi;
mi.cbSize = sizeof(MONITORINFO);
GetMonitorInfo(hMonitor, &mi);
// 位置補正
if (rcWnd.right > mi.rcMonitor.right)
{
rcWnd.left -= rcWnd.right - mi.rcMonitor.right;
rcWnd.right = mi.rcMonitor.right;
}
if (rcWnd.left < mi.rcMonitor.left)
{
rcWnd.right += mi.rcMonitor.left - rcWnd.left;
rcWnd.left = mi.rcMonitor.left;
}
if (rcWnd.bottom > mi.rcMonitor.bottom)
{
rcWnd.top -= rcWnd.bottom - mi.rcMonitor.bottom;
rcWnd.bottom = mi.rcMonitor.bottom;
}
if (rcWnd.top < mi.rcMonitor.top)
{
rcWnd.bottom += mi.rcMonitor.top - rcWnd.top;
rcWnd.top = mi.rcMonitor.top;
}
// ウィンドウ位置復元
SetWindowPos(
hWnd, NULL, rcWnd.left, rcWnd.top,
rcWnd.right - rcWnd.left, rcWnd.bottom - rcWnd.top,
SWP_NOZORDER);
// ウィンドウ最大化状態復元
UINT isZoomed = GetPrivateProfileInt(
_T("window"), _T("zoomed"), 0, iniFilePath);
if (isZoomed != 0)
{
ShowWindow(hWnd, SW_MAXIMIZE);
}
}
上手くウィンドウの情報がロードまたはセーブされません。エラーメッセージは出てません。
[1.4] 今何がわからないのか、知りたいのか
ネットのサンプルを参考にして実装したのですが実装した関数の使い方がわかりません。
参照元:http://www.ruche-home.net/program/tips/window-place
[2] 環境
[2.1] OS : Windows8.1
[2.2] コンパイラ名 : VisualStudio 2008
[3] その他
・どの程度C言語を理解しているか
現在は参考書などを読みながらC言語をなんとか勉強しているレベルです。
・ライブラリを使っている場合は何を使っているか
ライブラリは使用していません。