初版 2014/06/07

概要

指定したフォルダーに含まれるjpgファイルを拡大縮小して表示するプログラムです。
マウスホイールによる拡大縮小及び左クリックしながら中心位置の移動ができます。
イメージがウィンドウに収まらない場合は、スクロールバーが表示されます。
jpgファイルの読み込みはGDI+を用いています。
表示するファイルは、ファイルメニューの読み込みにより選択します。
表示位置・拡大縮小・スクロール処理を行うためにImageWindowクラスを作成しています。
また拡大縮小は、jpgファイルを拡大縮小表示(32/64bit)をベースにしており、これにスライドショー及びスリープ復帰後、モニターの電源断時等にスライドショーを停止するようにしました。

テスト環境

コンパイラ

Visual C++ 2008/2013 Express 32/64bit マルチバイト/UNICODE

実行環境

Windows XP Professional Service Pack 3 32bit(Virtual Box上の仮想マシーン)
Windows 7 Enterprise Service Pack 1 64bit

動作例

マウス操作

マウスホイール操作

スライドショーを実行中の場合は停止します。 奥へ回すとカーソル位置を中心に拡大表示される。
逆へ回すとカーソル位置を中心に縮小表示される。

マウス左ボタン

スライドショーを実行中の場合は停止します。 左ボタンを押すとカーソルが手の形状になります。押したままカーソルを移動させると画像が移動します。左ボタンを離した位置で移動が終了します。
ウィンドウ内に画像が全部表示されている場合は移動しません。

スクロールバー

スクロールバーを操作すると画像が移動します。

メニュー

ファイルメニュー

読み込み 表示するjpgファイルが存在するフォルダーを選択します。
終了 プログラムを終了します。

表示メニュー

等倍 jpgファイルを等倍で表示します。
ジャスト jpgファイルをウィンドウに収まるように表示します。
戻る 画像を戻します。
次へ 画像を進めます。
スライドショー スライドショーを開始及び停止します。

キー操作

左矢印キー 画像を戻します。
右矢印キー 画像を進めます。
上矢印キー スライドショーの表示間隔を短くします。
下矢印キー スライドショーの表示間隔を長くします。
ESCキー スライドショーを停止させます。

プログラムソースの概要

jpgviewm5.cpp

_tWinMain

GdiplusStartupによりGDI+の初期化をします。
その後Windowを作成します。
Windowが閉じられたら、GdiplusShutdownにより終了処理を実行します。

WndProc

WM_CREATEメッセージ
ウィンドウの初期化時にWM_CREATEメッセージが発生するので、手形状のカーソルを読み込み、ImageWindowクラスにウィンドウのデバイスコンテキストを登録します。
WM_SIZEメッセージ
ウィンドウサイズが変化した時に呼び出されます。ウィンドウサイズをImageWindow::wm_sizeにより登録します。
WM_LBUTTONDOWNメッセージ
マウスの左ボタンが押されたときに呼び出されます。スライドショーを実行中の場合は停止させます。移動の中心座標を保存しカーソルを手形状に変更します。
WM_MOUSEMOVEメッセージ
マウスが移動中に呼び出されます。
マウスの左ボタンが押された後の場合、マウス座標を取得しImageWindow::MoveImageにより画像を移動させます。
ImageWindow::MoveImage内ではScrollWindow APIにより画像を移動させています。
WM_LBUTTONUPメッセージ
マウスの左ボタンが離されたときに呼び出されます。カーソル形状を元に戻します。
WM_MOUSEWHEELメッセージ
マウスのホイールが回転した時に呼び出されます。スライドショーを実行中の場合は停止させます。
ImageWindow::wm_mousewheelで画像サイズ・位置等の計算を行います。
InvalidateRect APIによりWM_PAINTメッセージを発生させ計算結果に基づき画像の拡大縮小を行ってウィンドウに表示します。
WM_HSCROLL・WM_VSCROLLメッセージ
スクロールバーにクリック等の操作されたときに呼び出されます。
メッセージが発生するだけでスクロールバーの表示は変更されません。
ImageWindow::wm_hscroll,ImageWindow::wm_bscrollで画像位置を計算しImageWindow::ScrollBarDrawでスクロールバーの変更を行い、ScrollWindow APIで画像をスクロールさせます。
WM_COMMANDメッセージ
メニューが選択されたときに呼び出されます。
LOWORD(wp)により選択されたメニューのIDが取得できます。
IDM_READ
スライドショーを実行中の場合は停止させます。
GetFileNameによりファイル名の取得を行います。
あらかじめ読み込まれている画像があればメモリ等を開放します。(ImageWindow::DelImage)
ImageWindow::LoadImageによりjpgファイルを読み込みメモリ上に描画します。
ImageWindow::JustSizeによりウィンドウに収まるように画像の大きさを調整します。
InvalidateRect APIによりWM_PAINTメッセージを発生させウィンドウに表示します。
IDM_END
WM_DESTORYメッセージを呼び出しプログラムを終了します。
IDM_DEF
画像を等倍でウィンドウに表示します。
ウィンドウに収まらない場合は、左上を基準に表示します。
IDM_JUST
ImageWindow::JustSizeによりウィンドウに収まるサイズを計算します。
InvalidateRect APIによりWM_PAINTメッセージを発生させウィンドウに表示します。
IDM_SHOW
スライドショーが開始されていない場合は、タイマーを起動します。
スライドショーが開始されているときは、タイマーを終了させます。
IDM_NEXT
スライドショーを実行中の場合は停止させます。
画像を進めます。
IDM_BACK
スライドショーを実行中の場合は停止させます。
画像を戻す。
WM_TIMER
SetTimer APIにより設定された時間が経過した後にWM_TIMERメッセージが発生します。
killTimer APIによりタイマーを停止させます。
SendMessage APIによりWM_COMMAND(IDM_NEXT2)メッセージを発生させ画像を進めます。
再度、SetTimer APIによりタイマーを起動します。
WM_KEYDOWN
VK_LEFT
左矢印キーが押されるとこのメッセージが発生します。
SendMessage APIによりWM_COMMAND(IDM_BACK)メッセージを発生させ画像を戻します。
VK_RIGHT
右矢印キーが押されるとこのメッセージが発生します。
SendMessage APIによりWM_COMMAND(IDM_NEXT)メッセージを発生させ画像を進めます。
VK_UP
上矢印キーが押されるとこのメッセージが発生します。
スライドショーの更新間隔を0.5秒短くします。
VK_DOWN
下矢印キーが押されるとこのメッセージが発生します。
スライドショーの更新間隔を0.6秒長くします。
VK_ESCAPE
ESCキーが押されるとこのメッセージが発生します。
スライドショーを停止します。
WM_POWERBROADCASTメッセージ
サスペンド開始と復帰を検出するために使用しています。
サスペンド開始は、wParam==PBT_APMSUSPENDの時であり、スライドショーを停止させます。
WM_SYSCOMMANDメッセージ
モニターの電源断検出に使用しています。
モニターの電源断は、wParam==SC_MONITORPOWER && lParam==2 の時であり、スライドショーを停止させます。

imagewindow.h

画像をウィンドウに表示する基本的な機能を提供するImageWindowクラスを提供します。
以下の機能をサポートしています。
画像をメモリ上に読み込む。
拡大縮小及び移動に対応しウィンドウに収まらない場合は、スクロールバーを提供します。

ImageWindowクラス主要メンバー

int dx,dy;	// ウィンドウ上の表示位置(左上)
int ix,iy;	// 読み込んだ画像サイズ
int wx,wy;	// ウィンドウサイズ
double scale;	// 表示倍率
SCROLLINFO hsi,vsi; // スクロール情報構造体
int GetCurrentPositionX(void);	//	表示位置のX座標を返す
int GetCurrentPositionY(void);	//	表示位置のY座標を返す
void ClearPos(void);	//	表示座標を0,0 倍率を1にする
void wm_size(int x,int y);	//	ウィンドウサイズを設定する
void SetHWND(HWND h);	//	デバイスコンテキストを設定する
bool LoadImage(TCHAR* fname,HDC hdc);	// ファイルより画像を読み込む
void DelImage(void);	//	ImageWindowクラスに読み込まれている画像を解放する
bool IsImage(void);	//	有効な画像がImageWindowに読み込まれている場合はtrueを返す
void ScrollBarDraw(void);	//	ImageWindow内の情報をもとにスクロールバーを設定・表示	
void JustSize(void);	//	ウィンドウに収まるサイズの位置・倍率をImageWindowクラスに設定
void wm_hscroll(WPARAM wp,LPARAM lp);	//	WM_HSCROLLメッセージより画像位置・スクロールバーを設定する
void wm_vscroll(WPARAM wp,LPARAM lp);	//	WM_VSCROLLメッセージより画像位置・スクロールバーを設定する
bool wm_mousewheel(int m_x,int m_y,int m_delta);	//	中心位置及びスクロールの回転方向よりImageWindowクラス内の位置・倍率を設定
void MoveImage(int m_x,int m_y,int cx,int cy);	//	開始位置及び現在座標より画像をスクロール・スクロールバーを設定する
void wm_paint(HDC hdc);	//	ImageWindowクラスの情報をもとにウィンドウに画像を表示

file_list.h

フォルダー内のファイルの一覧を作成するクラスです。

list.g

リストを管理するクラスです。

ソースコード

jpgviewm5.cpp

// jpgファイルをウィンドウに表示する標準的なソース
// メモリ上に描画して画面に転送
// ウィンドウサイズに応じて縦横比を変えずに最大の大きさで表示
// マウス操作により移動・拡大等をサポート
// スライドショー
//   モニターが電源offになった場合は、停止する
//   スリープ状態になる場合は、停止する
// Visual C++ 2008/2013 Express (GDIPLUSが必要)


#include <windows.h>
#include <gdiplus.h>
#include <shlobj.h>
#include <tchar.h>
#include <vector>
#include "list.hpp"
#include "filelist.h"
#include "imagewindow.h"
#include "resource.h"

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

using namespace Gdiplus;

GdiplusStartupInput gdiSI;
ULONG_PTR gdiToken;

using namespace std;


LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);
void SlideShowDisable(HWND hWnd,bool* show_f);
BOOL InitApp(HINSTANCE, WNDPROC, TCHAR*);
BOOL InitInstance(HINSTANCE, TCHAR*, int);

HINSTANCE hInst;
TCHAR dir[MAX_PATH*4];
FILE_LIST file_list;

#define ID_MYTIMER	100
int INT_TIME=1000;

TCHAR def_dir[MAX_PATH*2];

//	ディレクトリ名を取得するコモンダイアログを表示する

bool GetDir(HWND hWnd, const TCHAR* def_dir,TCHAR* path);


// 表示するjpegファイル名
TCHAR* szFile=0;

// 最初に呼び出される関数

int WINAPI _tWinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst,TCHAR* lpsCmdLine, int nCmdShow){
	MSG msg;
	BOOL b;

	TCHAR szClassName[] = TEXT("JPGVIEWM5");
	GdiplusStartup(&gdiToken, &gdiSI, NULL);



	hInst=hCurInst;
	if (!hPrevInst) {
		if (!InitApp(hCurInst,WndProc, szClassName))    //    ウィンドウクラスの登録
			return FALSE;
	}
	if (!InitInstance(hCurInst, szClassName, nCmdShow)) {    // ウィンドウの作成
		return FALSE;
	}

// 見慣れないメッセージループだがエラーの場合-1が返る場合があるのでこのように記述
// MSDNを見てください。

	while ((b=GetMessage(&msg, NULL, NULL, NULL))!=0 && b!=-1) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}
	GdiplusShutdown(gdiToken);
	return (int)msg.wParam;
}



// ウィンドウを作成/閉じる/移動等のメッセージにより起動される関数

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp){
	HDC hdc;
	PAINTSTRUCT ps;

	HPEN hPen, hOldPen;
	HBRUSH hBrush;
	static int cx=0;	//	移動元の中心
	static int cy=0;	//	移動元の中心

	static int mode=0;	//	マウス移動中のモード 1:移動中
	static bool show_f=false;	//	スライドショーOn/Off
	static HCURSOR hHandCur;	//	ハンドカーソル
	static HCURSOR hOldCur;	//	カーソル
	static int m_x,m_y;
	static unsigned num=0;
	static bool getdir_f=false;
	int m_delta;
	static ImageWindow iw;
	TCHAR buf[MAX_PATH*2];

	switch (msg) {
	case WM_CREATE:
		hHandCur=LoadCursor(NULL, IDC_HAND);
		iw.SetHWND(hWnd);
		break;
	case WM_SIZE:
		iw.wm_size(LOWORD(lp),HIWORD(lp));
		iw.ScrollBarDraw();
		break;
	case WM_LBUTTONDOWN:	//	マウス左ボタンが押された
		SlideShowDisable(hWnd,&show_f);
		mode=1;
		cx=GET_X_LPARAM(lp)-iw.GetCurrentPositionX();
		cy=GET_Y_LPARAM(lp)-iw.GetCurrentPositionY();
		hOldCur=GetCursor();
		SetCursor(hHandCur);
		break;
	case WM_LBUTTONUP:	//	マウス左ボタンが離された
		if(mode){
			SetCursor(hOldCur);
			mode=0;
		}
		break;
	case WM_HSCROLL:
		iw.wm_hscroll(wp,lp);
		break;
	case WM_VSCROLL:
		iw.wm_vscroll(wp,lp);
		break;
	case WM_MOUSEMOVE:
		m_x=GET_X_LPARAM(lp);
		m_y=GET_Y_LPARAM(lp);
		if(mode==1)	//	ドラッグ中
			iw.MoveImage(m_x,m_y,cx,cy);
		break;
	case WM_MOUSEWHEEL:	//	拡大縮小
		SlideShowDisable(hWnd,&show_f);
		m_delta=GET_WHEEL_DELTA_WPARAM(wp);
		if(iw.wm_mousewheel(m_x,m_y,m_delta)==true)
			InvalidateRect(hWnd,0,TRUE);
		break;
	case WM_PAINT:	// ウィンドウの描画が必要な場合に呼び出される。
		if(iw.IsImage()==true){
			hdc = BeginPaint(hWnd, &ps);
			hPen = CreatePen(PS_SOLID, 1, RGB(255, 255, 255));
			hOldPen =(HPEN) SelectObject(hdc, hPen);
			hBrush = (HBRUSH)GetStockObject(NULL_BRUSH);

			SetStretchBltMode(hdc, HALFTONE);
			iw.wm_paint(hdc);

			SelectObject(hdc, hOldPen);
			DeleteObject(hPen);
			EndPaint(hWnd, &ps);
		}
		if(getdir_f==true){
			return (DefWindowProc(hWnd, msg, wp, lp));
		}
		break;
	case WM_POWERBROADCAST:	//	サスペンド検出
		switch(wp){
			case PBT_APMSUSPEND:	//	サスペンド開始検出
				SlideShowDisable(hWnd,&show_f);
				break;
			case PBT_APMRESUMESUSPEND:	//	サスペンドから復帰検出
				break;
		}
		return (DefWindowProc(hWnd, msg, wp, lp));
	case WM_SYSCOMMAND:
		switch(wp){
			case SC_MONITORPOWER:	//	モニター電源状態を検出
				switch(lp){
					case 2:	// モニタ電源オフを検出
						SlideShowDisable(hWnd,&show_f);
						break;
				}
				break;
		}
		return DefWindowProc(hWnd, msg, wp, lp);
	case WM_COMMAND:
		switch(LOWORD(wp)) {
			case IDM_READ:
				SlideShowDisable(hWnd,&show_f);
				getdir_f=true;
				dir[0]=_T('\0');
				if(GetDir(hWnd,0,dir)==true){
					getdir_f=false;
					file_list.find(dir, sizeof(dir) / sizeof(TCHAR), _TEXT("*.jpg"));
					file_list.full_vec(dir,sizeof(dir) / sizeof(TCHAR),file_vec);
					num=0;
					if(file_vec.size())
						szFile=file_vec[num=0];
					hdc=GetDC(hWnd);
					if(iw.LoadImage(szFile,hdc)==false){
						MessageBox(0,szFile,_TEXT("Error"),MB_OK);
						PostQuitMessage(0);
					}
					ReleaseDC(hWnd,hdc);
					SetWindowText(hWnd,szFile);

					iw.JustSize();
					InvalidateRect(hWnd,0,TRUE);
				}
				getdir_f=false;
				break;
			case IDM_NEXT:
				SlideShowDisable(hWnd,&show_f);
			case IDM_NEXT2:
				++num;
				if(unsigned(num)>=file_vec.size())
					num=0;
				szFile=file_vec[num];
				hdc=GetDC(hWnd);
				iw.DelImage();
				if(iw.LoadImage(szFile,hdc)==false){
					MessageBox(0,szFile,_TEXT("Error"),MB_OK);
					PostQuitMessage(0);
				}
				ReleaseDC(hWnd,hdc);
				SetWindowText(hWnd,szFile);
				iw.JustSize();
				InvalidateRect(hWnd,0,TRUE);
				break;
			case IDM_BACK:
				SlideShowDisable(hWnd,&show_f);
				if (num == 0)
					num = (int)file_vec.size() - 1;
				else
					--num;
				szFile=file_vec[num];
				hdc=GetDC(hWnd);
				iw.DelImage();
				if(iw.LoadImage(szFile,hdc)==false){
					MessageBox(0,szFile,_TEXT("Error"),MB_OK);
					PostQuitMessage(0);
				}
				ReleaseDC(hWnd,hdc);
				SetWindowText(hWnd,szFile);
				iw.JustSize();
				InvalidateRect(hWnd,0,TRUE);
				break;
			case IDM_SHOW:
				if(show_f==false){
					show_f=true;
					SetTimer(hWnd, ID_MYTIMER, INT_TIME, NULL);
				}else{
					SlideShowDisable(hWnd,&show_f);
				}
				break;
			case IDM_END:
				SlideShowDisable(hWnd,&show_f);
				SendMessage(hWnd,WM_DESTROY,0,0);
				break;
			case IDM_DEF:
				SlideShowDisable(hWnd,&show_f);
				iw.ClearPos();
				iw.ScrollBarDraw();
				InvalidateRect(hWnd,0,TRUE);
				break;
			case IDM_JUST:
				SlideShowDisable(hWnd,&show_f);
				iw.JustSize();
				InvalidateRect(hWnd,0,TRUE);
				break;
		}
		break;
	case WM_TIMER:{
			KillTimer(hWnd,ID_MYTIMER);
			SendMessage(hWnd,WM_COMMAND,IDM_NEXT2,0);
			_stprintf_s(buf,sizeof(buf)/sizeof(TCHAR),_TEXT("%i/%i %s"),num,file_vec.size(),szFile);
			SetWindowText(hWnd,buf);
			SetTimer(hWnd, ID_MYTIMER, INT_TIME, NULL);
		}
		break;
	case WM_KEYDOWN:
		switch(wp){
		case VK_LEFT:
			SendMessage(hWnd,WM_COMMAND,IDM_BACK,0);
			break;
		case VK_RIGHT:
			SendMessage(hWnd,WM_COMMAND,IDM_NEXT,0);
			break;
		case VK_UP:
			INT_TIME-=500;
			if(INT_TIME<500)
				INT_TIME=500;
			break;
		case VK_DOWN:
			INT_TIME+=500;
			break;
		case VK_ESCAPE:
			SlideShowDisable(hWnd,&show_f);
			break;
		}
		break;
	case WM_DESTROY:	// ウィンドウを閉じる場合に呼び出される。
		iw.DelImage();
		PostQuitMessage(0);
		break;
	default:
		return (DefWindowProc(hWnd, msg, wp, lp));
	}
	return 0L;
}

//	スライドショーを停止させる

void SlideShowDisable(HWND hWnd,bool* show_f){
	if(*show_f==true){
		*show_f=false;
		KillTimer(hWnd,ID_MYTIMER);
	}
}

//	ウィンドウクラスの登録 1回しか呼ばれないのに関数化しているのは、ウィンドウを2回呼び出す場合に
//	この関数を再利用できるからです。

BOOL InitApp(HINSTANCE hInst,WNDPROC WndProc,TCHAR* szClassName){
	WNDCLASS wc;
	wc.style = CS_HREDRAW | CS_VREDRAW;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInst;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = (HBRUSH)GetStockObject(LTGRAY_BRUSH);
	wc.lpszMenuName = _TEXT("IDM_MENU");
	wc.lpszClassName = (TCHAR*)szClassName;
	return (RegisterClass(&wc));
}

//	ウィンドウの作成 1回しか呼ばれないのに関数化しているのは、ウィンドウを2回呼び出す場合に
//	この関数を再利用できるからです。

BOOL InitInstance(HINSTANCE hInst, TCHAR* szClassName, int nCmdShow){
	HWND hWnd;
	hWnd = CreateWindow(szClassName,
			TEXT("JPGVIEWM5"),
			WS_OVERLAPPEDWINDOW ,//| WS_VSCROLL,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			CW_USEDEFAULT,
			NULL,
			NULL,
			hInst,
			NULL);
	if (!hWnd)
		return FALSE;
	ShowWindow(hWnd, nCmdShow);
	UpdateWindow(hWnd);
	return TRUE;
}

//	ディレクトリ名を取得するコモンダイアログを表示する
//	GetDir(親ウィンドウのハンドル,初期時選択フォルダー,選択されたフォルダー名);
//	trueが返された場合有効なフォルダー名が取得できた場合

int __stdcall BrowseCallbackProc(HWND hwnd, UINT uMsg, LPARAM lParam, LPARAM lpData);


bool GetDir(HWND hWnd,const TCHAR* def_dir,TCHAR* path){
	BROWSEINFO bInfo;
	LPITEMIDLIST pIDList;

	memset(&bInfo,0,sizeof(bInfo));
	bInfo.hwndOwner = hWnd; // ダイアログの親ウインドウのハンドル 
	bInfo.pidlRoot = NULL; // ルートフォルダをデスクトップフォルダとする 
	bInfo.pszDisplayName = path; //フォルダ名を受け取るバッファへのポインタ 
	bInfo.lpszTitle = TEXT("フォルダの選択"); // ツリービューの上部に表示される文字列 
	bInfo.ulFlags = BIF_RETURNONLYFSDIRS | BIF_EDITBOX | BIF_VALIDATE | BIF_NEWDIALOGSTYLE; // 表示されるフォルダの種類を示すフラグ 
	bInfo.lpfn = BrowseCallbackProc; // BrowseCallbackProc関数のポインタ 
	bInfo.lParam = (LPARAM)def_dir;
	pIDList = ::SHBrowseForFolder(&bInfo);
	if (pIDList == NULL){
		path[0] = _TEXT('\0');
		return false; //何も選択されなかった場合 
	}
	else{
		if (!SHGetPathFromIDList(pIDList, path))
			return false;//変換に失敗 
		CoTaskMemFree(pIDList);// pIDListのメモリを開放 
		return true;
	}
}

//	上記ダイアログのコールバック関数

int __stdcall BrowseCallbackProc(HWND hWnd, UINT uMsg, LPARAM lParam, LPARAM lpData){
    TCHAR dir[MAX_PATH];
    ITEMIDLIST *lpid;
	HWND hEdit;

	switch (uMsg){
	case BFFM_INITIALIZED:	//	ダイアログボックス初期化時
		if(lpData)
			SendMessage(hWnd, BFFM_SETSELECTION, (WPARAM)TRUE, lpData);	//	コモンダイアログの初期ディレクトリ
		break;
	case BFFM_VALIDATEFAILED:	//	無効なフォルダー名が入力された
		MessageBox(hWnd,(TCHAR*)lParam,_TEXT("無効なフォルダー名が入力されました"),MB_OK);
		hEdit=FindWindowEx(hWnd,NULL,_TEXT("EDIT"),NULL);	//	エディットボックスのハンドルを取得する
		SetWindowText(hEdit,_TEXT(""));
		return 1;	//	ダイアログボックスを閉じない
		break;
	case BFFM_IUNKNOWN:
		break;
	case BFFM_SELCHANGED:	//	選択フォルダーが変化した場合
		lpid=(ITEMIDLIST *)lParam;
		SHGetPathFromIDList(lpid,dir);
		hEdit=FindWindowEx(hWnd,NULL,_TEXT("EDIT"),NULL);	//	エディットボックスのハンドルを取得する
		SetWindowText(hEdit,dir);
		break;
	}
	return 0;
}

imagewindow.h

#include <windows.h>
#include <windowsx.h>
#include <gdiplus.h>
#include <tchar.h>

using namespace Gdiplus;

class ImageWindow{
	BITMAPINFOHEADER bmih;
	BYTE *pBits;
	HBITMAP hBitmap,HOldBitmap;
	HDC hdcBMP;
	HWND hWnd;
	int dx,dy;
	int ix,iy;
	int wx,wy;
	double scale;
	SCROLLINFO hsi,vsi;
public:
	ImageWindow(){
		dx=dy=ix=iy=wx=wy=0;
		scale=1.0;
	}
	int GetCurrentPositionX(void){
		return dx;
	}
	int GetCurrentPositionY(void){
		return dy;
	}
	void ClearPos(void){
		dx=dy=0;
		scale=1.0;

	}
	void wm_size(int x,int y){
		wx=x;
		wy=y;
		if(ix*scale < wx){	//	横幅がウィンドウに収まっている場合はウィンドウの真ん中に表示する
			dx=int((double(wx) - double(ix)*scale ) /2);
		}
		if(iy*scale < wy){	//	横幅がウィンドウに収まっている場合はウィンドウの真ん中に表示する
			dy=int((double(wy) - double(iy)*scale ) /2);
		}
	}
	void SetHWND(HWND h){
		hWnd=h;
	}
	bool LoadImage(TCHAR* fname,HDC hdc){
		Image* imageP;
#ifndef UNICODE
		WCHAR wTitle[MAX_PATH];
		MultiByteToWideChar(932,0,szFile,-1,wTitle,sizeof(wTitle)/sizeof(TCHAR));
		imageP=Bitmap::FromFile(wTitle);
#else
		imageP=new Image(fname);
#endif
		if(imageP==0){
			ix=0;
			iy=0;
			hdcBMP=0;
			return false;
		}
		bmih.biSize=sizeof(bmih);
		bmih.biWidth=ix=imageP->GetWidth();
		bmih.biHeight=iy=imageP->GetHeight();
		bmih.biPlanes=1;
		bmih.biBitCount=32;
		bmih.biCompression=BI_RGB;
		bmih.biSizeImage=0;
		bmih.biXPelsPerMeter=0;
		bmih.biYPelsPerMeter=0;
		bmih.biClrUsed=0;
		bmih.biClrImportant=0;
		hBitmap=CreateDIBSection(NULL,(BITMAPINFO*)&bmih,0,(void**)&pBits,NULL,0);
		if(pBits==NULL){
			MessageBox(0,_TEXT("メモリが不足しています。"),_TEXT("Message"),MB_YESNO | MB_ICONQUESTION);
			DeleteObject(hBitmap);
			delete imageP;
			ix=0;
			iy=0;
			return false;
		}
		hdcBMP=CreateCompatibleDC(hdc);
		HOldBitmap=(HBITMAP)SelectObject(hdcBMP,hBitmap);
		Graphics* MyGraphics=new Gdiplus::Graphics(hdcBMP);
		MyGraphics->DrawImage(imageP,0,0,ix,iy);
		delete imageP;
		delete MyGraphics;
		return true;
	}

	void DelImage(void){
		if(hdcBMP){
			SelectObject(hdcBMP,HOldBitmap);
			DeleteObject(hBitmap);
			DeleteDC(hdcBMP);
		}
	}
	bool IsImage(void){
		if(hdcBMP)
			return true;
		else
			return false;
	}
	void ScrollBarDraw(void){
		hsi.cbSize=sizeof(SCROLLINFO);
		hsi.fMask= SIF_POS | SIF_RANGE | SIF_PAGE;
		hsi.nMin=0;
		hsi.nMax=int(double(ix)*scale-1);
		hsi.nPage=wx;
		hsi.nPos=-dx;
		SetScrollInfo(hWnd,SB_HORZ,&hsi,TRUE);

		vsi.cbSize=sizeof(SCROLLINFO);
		vsi.fMask= SIF_POS | SIF_RANGE | SIF_PAGE;
		vsi.nMin=0;
		vsi.nMax=int(double(iy)*scale-1);
		vsi.nPage=wy;
		vsi.nPos=-dy;
		SetScrollInfo(hWnd,SB_VERT,&vsi,TRUE);
	}
	void JustSize(void){
		int sx,sy;
		double pxy=double(ix)/double(iy);	// オリジナル横/縦
		double dxy=double(wx)/double(wy);	// 変換後横/縦
		int dx2,dy2;
		if(pxy>dxy){	//	オリジナルが横が長いので横基準に縦を縮小する
			dx2=wx;
			dy2=(int)double(wx/pxy);
		}else{
			dy2=wy;
			dx2=(int)double(wy*pxy);
		}
		sx=dx2;
		sy=dy2;
		scale=double(sx)/double(ix);
		dx=(wx-sx)/2;
		dy=(wy-sy)/2;
		ScrollBarDraw();
	}
	void wm_hscroll(WPARAM wp,LPARAM lp){
		int rdx;
		int ddx=GetCurrentPositionX();
		rdx=ddx;
		switch(LOWORD(wp)){
		case SB_LINEUP:
			++ddx;
			break;
		case SB_LINEDOWN:
			--ddx;
			break;
		case SB_PAGEUP:
			ddx+=hsi.nPage;
			break;
		case SB_PAGEDOWN:
			ddx-=hsi.nPage;
			break;
		case SB_THUMBTRACK:
			ddx=-HIWORD(wp);
			break;
		}
		if(rdx != ddx){
			if(0<ddx)
				ddx=0;
			else{
				if(ddx< ((int)hsi.nPage-hsi.nMax) )
					ddx=(int)hsi.nPage-hsi.nMax;
			}
			dx=ddx;
			hsi.nPos=-ddx;
			ScrollBarDraw();
			ScrollWindow(hWnd, ddx-rdx, 0,NULL, NULL);
		}
	}
	void wm_vscroll(WPARAM wp,LPARAM lp){
		int rdy;
		int ddy=GetCurrentPositionY();
		rdy=ddy;
		switch(LOWORD(wp)){
		case SB_LINEUP:
			++ddy;
			break;
		case SB_LINEDOWN:
			--ddy;
			break;
		case SB_PAGEUP:
			ddy+=vsi.nPage;
			break;
		case SB_PAGEDOWN:
			ddy-=vsi.nPage;
			break;
		case SB_THUMBTRACK:
			ddy=-HIWORD(wp);
			break;
		}
		if(0<ddy)
			ddy=0;
		else{
			if(ddy< ((int)vsi.nPage-vsi.nMax) )
				ddy=(int)vsi.nPage-vsi.nMax;
		}
		if(rdy != ddy){
			dy=ddy;
			vsi.nPos=-ddy;
			ScrollBarDraw();
			ScrollWindow(hWnd, 0, ddy-rdy, NULL, NULL);
		}
	}

	bool wm_mousewheel(int m_x,int m_y,int m_delta){	//	拡大縮小
		double rscale=scale;
		if(m_delta<0)
			scale/=2;
		else
			scale*=2;

		int isx;
		int isy;
		isx=int(double(ix)*scale);
		isy=int(double(iy)*scale);
		if(isx>64 && isy>64){
			int rdx=dx;
			int rdy=dy;
			dx=int(m_x-double(m_x-dx)/rscale*scale);
			dy=int(m_y-double(m_y-dy)/rscale*scale);
			ScrollBarDraw();
			return true;
		}else
			scale=rscale;
		return false;
	}
	void MoveImage(int m_x,int m_y,int cx,int cy){
		int rdx=dx;
		int rdy=dy;
		dx=m_x-cx;	//	移動量
		dy=m_y-cy;	//	移動量
		if(wx < ix*scale){	//	ウィンドウサイズより表示サイズが大きい場合
			if( ix*scale+dx < wx)	//	右端に達している場合
				dx=int(wx-double(ix)*scale);
			if(0<dx)	//	左端に達している場合
				dx=0;

		}else{	//	ウィンドウサイズに表示サイズが収まっている場合
			dx=rdx;
		}

		if(wy < iy*scale){	//	ウィンドウサイズより表示サイズが大きい場合
			if( iy*scale+dy < wy)	//	下端に達している場合
				dy=int(wy-double(iy)*scale);
			if(0<dy)	//	上端に達している場合
				dy=0;
		}else{	//	ウィンドウサイズに表示サイズが収まっている場合
			dy=rdy;
		}

		if(rdx!=dx || rdy!=dy){
			ScrollWindow(hWnd, dx-rdx,dy-rdy ,NULL, NULL);
			ScrollBarDraw();
		}
	}
	void wm_paint(HDC hdc){
		StretchBlt(hdc,dx,dy,int((double)ix*scale),int((double)iy*scale),hdcBMP,0,0,ix,iy,SRCCOPY);
	}
};

filelist.h

#include <windows.h>
#include <tchar.h>
#include <vector>
#include "list.hpp"

typedef TCHAR* TCHARP;

using namespace std;


vector<TCHARP> file_vec;

//	ファイルの情報を保存する構造体

struct FILE_LINK{
	friend struct FILE_LIST;
	TCHAR* name;			//	ファイル名
	FILE_LINK* next;	//	次のファイル名へのポインタ
	FILE_LIST* child;	//	チャイルドフォルダー
	FILE_LINK(){
		name = 0;
		next = 0;
		child = 0;
	}
};

//	ファイル名を管理する構造体

struct FILE_LIST : public LIST{
	TCHAR* name;	//	親フォルダー名
	FILE_LIST* parent;	//	親フォルダーのFILE_LIST
	FILE_LIST() : LIST() {
		name = 0;
		parent = 0;
	}
	void append(FILE_LINK* p){	//	ファイルを追加
		LIST::append((void*)p);
	}
	FILE_LINK* next(void){	//	次のファイルを検索	
		return (FILE_LINK*)LIST::next();
	}
	FILE_LINK* first(void){	//	最初のファイルを検索
		return (FILE_LINK*)LIST::first();
	}
	int find(TCHAR* pass, int sz, TCHAR* card);	// passで示されるフォルダーのcardで示されるワイルカードに合致するファイルを検索しFILE_LISTに登録する
	FILE_LINK* dupchk(TCHAR* fullpass);	// fullpassがFILE_LISTに登録されているか検索
	void short_puts(TCHAR* pass, int num);	// ファイルの一覧を標準出力に出力する。フォルダー階層ごとにインデントされる。
	void full_vec(TCHAR* pass, int sz,vector<TCHARP>& vec); // フォルダー・ファイル名を全角から半角に変換する
	FILE_LINK* find_name(TCHAR* name){	//	チャイルドフォルダーを含まず一致する名前のFILE_LINK*を返す。
		FILE_LINK* p = first();
		while (p){
			if (_tcscmp(name, p->name) == 0){
				return p;
			}
			p = next();
		}
		return 0;
	}
	void clear(void){	//	FILE_LISTに登録されているフォルダー・ファイルをクリアする。
		FILE_LINK* p = first();
		FILE_LINK* np;
		while (p){
			np = next();
			if (p->child){
				p->child->clear();
			}
			delete p->name;
			delete p;
			p = np;
		}
	}
};

//	相対パスが登録されているか確認し、登録されていればFILE_LINK*を返す。登録されていなければ0を返す。

FILE_LINK* FILE_LIST::dupchk(TCHAR* fullpass){
	FILE_LIST* flp = this;
	FILE_LINK* p;
	TCHAR* sp = fullpass;
	TCHAR* tp = fullpass;
	FILE_LINK* np;
	while (*sp){
		if (*sp == _T('\\')){
			sp[0] = _T('\0');
			p = flp->first();
			np = 0;
			while (p){
				if (_tcscmp(tp, p->name) == 0){
					np = p;
					flp = p->child;
					break;
				}
				p = flp->next();
			}
			if (np == 0)
				return 0;
			tp = sp + 1;
		}
		++sp;
	}

	p = flp->first();
	while (p){
		if (_tcscmp(tp, p->name) == 0){
			return p;
		}
		p = flp->next();
	}
	return 0;
}


void FILE_LIST::short_puts(TCHAR* pass, int num){
	FILE_LINK* p = first();
	while (p){
		for (int i = 0; i<num; i++)
			_tprintf(_TEXT("\t"));
		if (p->child){
			_tprintf(_TEXT("[%s]\n"), p->name);
			p->child->short_puts(pass, num + 1);
		}
		else
			_tprintf(_TEXT("%s\n"), p->name);
		p = next();
	}

}
#if 1
void FILE_LIST::full_vec(TCHAR* pass, int sz,vector<TCHARP>& vec){
	TCHAR dtc_file[MAX_PATH * 8];
	TCHAR* pass_end = pass + _tcslen(pass);
	TCHAR* dtc_end = dtc_file + (pass_end - pass);
	pass_end[0] = _T('\\');
	_tcscpy_s(dtc_file, sizeof(dtc_file)/sizeof(TCHAR), pass);
	dtc_end[0] = _T('\\');

	FILE_LINK* p = first();
	while (p){
		_tcscpy_s(pass_end + 1, sz - (pass_end - pass + 1), p->name);
		_tcscpy_s(dtc_end + 1, sz - (pass_end - pass + 1), p->name);
		if (p->child){
			p->child->full_vec(pass, int(sz - (pass_end - pass + 1)),vec);

		}else{
			vec.push_back(_tcsdup(pass));

		}
		p = next();
	}
	pass_end[0] = _T('\0');
}
#endif
//	再帰呼び出しによりサブディレクトリ内の指定ファイルを検索する

int FILE_LIST::find(TCHAR* pass, int sz, TCHAR* card){
	WIN32_FIND_DATA FindFileData;
	HANDLE hFind;
	FILE_LIST* child;
	int num = 0;	//	見つかったファイル数

	TCHAR* pass_end = pass + _tcslen(pass);	//	パス名の最後の位置
	TCHAR* pass_add;	//	パス名の最後の位置(\を含む)

	if (*(pass_end - 1) == _T(':')){	//	ドライブ名のみ指定された場合
		pass_add = pass_end;
	}
	else{	//	フォルダー名が含まれている場合
		pass_add = pass_end + 1;
		pass_end[0] = _T('\\');
	}
	_tcscpy_s(pass_add, sz - (pass_add - pass + 1), _TEXT("*.*"));	//	サブディレクトリの検索

	hFind = FindFirstFile(pass, &FindFileData);
	if (hFind != INVALID_HANDLE_VALUE){
		do{
			if ((FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
			if (_tcscmp(FindFileData.cFileName, _TEXT("..")) != 0 && _tcscmp(FindFileData.cFileName, _TEXT(".")) != 0){	//	親ディレクトリ及び現在のディレクトリは無視する
				_tcscpy_s(pass_add, sz - (pass_add - pass + 1), FindFileData.cFileName);
				size_t len = _tcslen(FindFileData.cFileName);
				FILE_LINK* tp = find_name(FindFileData.cFileName);
				if (tp == 0){	//	フォルダが登録されていない場合
					TCHAR* n = new TCHAR[len + 1];
					_tcscpy_s(n, len + 1, FindFileData.cFileName);
					FILE_LINK* p = new FILE_LINK;
					p->name = n;
					p->next = 0;
					append(p);
					p->child = new FILE_LIST;
					child = p->child;
					child->parent = this;	//	親フォルダーを登録
				}
				else{	//	フォルダがすでに登録されている場合
					child = tp->child;
				}
				num += child->find(pass, int(sz - (pass_add - pass + 1)), card);	//	サブディレクトリの中を検索
			}
		} while (FindNextFile(hFind, &FindFileData));
	}
	FindClose(hFind);

	_tcscpy_s(pass_add, sz - (pass_add - pass + 1), card);
	hFind = FindFirstFile(pass, &FindFileData);
	if (hFind != INVALID_HANDLE_VALUE){
		do{
			if (!(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)){
				FILE_LINK* tp = find_name(FindFileData.cFileName);
				if (tp == 0){	//ファイルが登録されていないので登録する
					++num;
					size_t len = _tcslen(FindFileData.cFileName);
					TCHAR* n = new TCHAR[len + 1];
					_tcscpy_s(n, len + 1, FindFileData.cFileName);
					FILE_LINK* p = new FILE_LINK;
					p->name = n;
					p->next = 0;
					p->child = 0;
					append(p);
				}
			}
		} while (FindNextFile(hFind, &FindFileData));
		pass_end[0] = _T('\0');
		FindClose(hFind);
		return num;
	}
	else{
		pass_end[0] = _T('\0');
		FindClose(hFind);
		return num;
	}
}

list.h

#ifndef LIST_HPP
#define LIST_HPP 1

class LINK{
	LINK* next;
	LINK* back;
	void* data;
protected:
	LINK(){
		next=0;
		back=0;
		data=0;
	}
	void set(void* d){
		data=d;
	}
	friend class LIST;
	friend int cmp(const void* d,const void* s){
		if((char*)d>(char*)s)
			return 1;
		if((char*)d<(char*)s)
			return -1;
		return 0;
		
	}
};

typedef void* LPVOID;

class LIST{
	LINK* top;	//	リストの先頭
	LINK* end;	//	リストの最後尾
	LINK* pos;	//	リストのカレントポジション
	int max;	//	リストの要素数
protected:
	LIST(){
		pos=top=end=0;
		max=0;
	}
	void append(void* d){	//	リストの最後尾にデータを追加
		LINK* p=new LINK;
		++max;
		p->data=d;
		if(top==0){
			pos=top=end=p;
		}else{
			end->next=p;
			p->back=end;
			end=p;
		}
	}
	void* next(void){	//	カレントポジジョンからデータを得た後、カレントポジションを1個進める。
		LINK* t=pos;
		if(pos==0){
			return 0;
		}else{
			pos=pos->next;
			return t->data;
		}
	}
	void* first(void){	//	カレントポジションを先頭に移動させ先頭データを得た後、カレントポジションを1個進める。
		pos=top;
		return next();
	}
	void all_del(void){	//	リスト全部を削除する
		while(top){
			LINK* t=top->next;
			delete top;
			top=t;
		}
		top=end=pos=0;
		max=0;
	}
	void repeat_del(int(*cmp )(const void* d,const void* s)){	//	ソートされたリストから重複する要素を削除
		LINK* lp=top;
		if(lp){
			LINK* bp=0;
			while(lp){
				if(bp){
					if( (*cmp)((void**)&lp->data,(void**)&bp->data) ){	//	前の要素と異なる場合
						bp=lp;
					}else{	//	前の要素と同じ場合
						LINK* t=lp->next;
						del(lp);
						--max;
						lp=t;
						continue;
					}
				}else
					bp=lp;
				lp=lp->next;
			}
		}
	}	
	void qsort(int(*cmp )(const void* d,const void* s)){
		if(max){
			int n=0;
			LPVOID* vec=new LPVOID[max];	//	リストの要素を配列にコピーする
			if(vec==0)
				return;
			void* p;
			p=first();
			while(p){
				vec[n++]=p;
				p=next();
			}
			::qsort((void*)vec,max,sizeof(LPVOID),cmp);
			LINK* lp=top;						//	配列をリストにコピー
			n=0;
			while(lp){
				lp->set(vec[n++]);
				lp=lp->next;
			}
			delete vec;
		}
	}
	void del(LINK*	p){	// リストからpを削除する
		if(p){
			if(p==top){
				LINK* t=top;
				top=top->next;
				top->back=0;
				if(pos==p){
					pos=p->next;
				}
				delete p;
				return;
			}
			if(p==end){
				LINK* t=end;
				end=end->back;
				end->next=0;
				delete p;
				return;
			}
			LINK* t=p;
			p->back->next=p->next;
			p->next->back=p->back;
			if(pos==p){
				pos=p->next;
			}
			delete t;
		}

	}
public:
	int get_max(void){
		return max;
	}
};

#endif

resource.h

#define IDM_READ	2000
#define IDM_END		2010
#define IDM_JUST	2100

#define IDM_BACK	2110
#define IDM_NEXT	2120
#define IDM_NEXT2	2125
#define IDM_DEF		2130
#define IDM_SHOW	2140

resource.rc

#include <windows.h>
#include "resource.h"


//-------------------------
// メニュー(IDM_MENU)
//-------------------------
IDM_MENU MENU DISCARDABLE
{
	POPUP "ファイル(&F)"
	{
		MENUITEM "読み込み(&R)",IDM_READ
		MENUITEM "終了(&X)", IDM_END
	}
	POPUP "表示(&V)"
	{
		MENUITEM "等倍(&D)", IDM_DEF
		MENUITEM "ジャスト(&J)", IDM_JUST
		MENUITEM "戻る(&B)", IDM_BACK
		MENUITEM "次へ(&N)", IDM_NEXT
		MENUITEM "スライドショー(&S)", IDM_SHOW
	}

}

実行ファイルとソースファイルのダウンロード