#include <Windows.h>
#include <CommCtrl.h>
#include <shimgdata.h>
#include <stdio.h>
#include <map>
#include <string>
#include <vector>
#include "resource.h"
using namespace std;
#define WIDTH(rect) ((rect).right - (rect).left)
#define HEIGHT(rect) ((rect).bottom - (rect).top)
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
#define APP_NAME TEXT("ImgView3")
typedef vector<wstring> VecStr;
typedef multimap<ULONGLONG, wstring> MapStr;
// 関数プロトタイプ宣言
void CreateFileList(LPCTSTR pszPath);
BOOL IsImgFile(LPCTSTR pszPath);
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
BOOL OnCommand(HWND hWnd, WPARAM wParam);
void OnDelete(HWND hWnd);
void OnDropFiles(HWND hWnd, WPARAM wParam);
void OnSizeNormal(HWND hWnd);
void OnSizeAdjust(HWND hWnd);
void OnExecute(void);
void OnPaint(HWND hWnd);
void GetImage(HWND hWnd);
BOOL MyGetFileTime(LPSYSTEMTIME pSystemTime, LPCTSTR pszFileName);
// 外部変数構造体
static struct {
TCHAR szBaseDir[MAX_PATH]; // 基準ディレクトリ
VecStr vsFileName; // ファイル名配列
VecStr::size_type idxCurr; // 現在の位置
IShellImageDataFactory *pImgDatFac; // 画像データファクトリ
IShellImageData *pImgDat; // 画像データ
SIZE sizeImg; // 画像サイズ
SIZE sizeDst; // 出力サイズ
HWND hListView; // エクスプローラのリストビュー
} g;
//==============================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int nCmdShow)
{
// プログラム引数
LPTSTR pszCmdLine = GetCommandLine();
int argc;
LPTSTR *argv = CommandLineToArgvW(pszCmdLine, &argc);
if (2 <= argc) {
CreateFileList(argv[1]);
}
// ウィンドウクラスの登録
WNDCLASSEX wcx;
ZeroMemory(&wcx, sizeof wcx);
wcx.cbSize = sizeof wcx;
wcx.style = CS_HREDRAW | CS_VREDRAW;
wcx.lpfnWndProc = WndProc;
wcx.hInstance = hInstance;
wcx.hCursor = LoadCursor(NULL, MAKEINTRESOURCE(IDC_ARROW));
wcx.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
wcx.lpszClassName = APP_NAME;
if (RegisterClassEx(&wcx) == 0) {
return 0;
}
// ウィンドウの作成
HWND hWnd = CreateWindowEx(
WS_EX_ACCEPTFILES,
APP_NAME, APP_NAME,
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, 0,
CW_USEDEFAULT, 0,
NULL, NULL, hInstance, NULL);
if (hWnd == NULL) {
return 0;
}
ShowWindow(hWnd, nCmdShow);
UpdateWindow(hWnd);
// メッセージループ
MSG msg;
HACCEL hAccTable = LoadAccelerators(hInstance, MAKEINTRESOURCE(IDR_MAIN));
while (GetMessage(&msg, NULL, 0, 0)) {
if (TranslateAccelerator(msg.hwnd, hAccTable, &msg) == 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
return msg.wParam;
}
//------------------------------------------------------------------------------
void CreateFileList(LPCTSTR pszPath)
{
TCHAR szDrive[_MAX_DRIVE];
TCHAR szDir[_MAX_DIR];
TCHAR szFName[_MAX_FNAME];
TCHAR szExt[_MAX_EXT];
TCHAR szFileName[_MAX_PATH];
TCHAR szPath[_MAX_PATH];
g.vsFileName.clear();
g.idxCurr = -1;
errno_t er = _wsplitpath_s(pszPath, szDrive, szDir, szFName, szExt);
swprintf_s(g.szBaseDir, L"%s%s", szDrive, szDir);
swprintf_s(szFileName, L"%s%s", szFName, szExt);
swprintf_s(szPath, L"%s*.*", g.szBaseDir);
// ファイル一覧を取得し更新日時の昇順でソート
WIN32_FIND_DATA wfd;
HANDLE hFindFile = FindFirstFile(szPath, &wfd);
DWORD dwMask = FILE_ATTRIBUTE_DIRECTORY | FILE_ATTRIBUTE_HIDDEN;
MapStr list;
do {
if (wfd.dwFileAttributes & dwMask) {
continue;
}
FILETIME ft = wfd.ftLastWriteTime;
ULONGLONG qw = (ULONGLONG(ft.dwHighDateTime) << 32) + ft.dwLowDateTime;
list.insert(make_pair(qw, wfd.cFileName));
} while (FindNextFile(hFindFile, &wfd));
FindClose(hFindFile);
// 配列に転記
VecStr::size_type idx = 0;
for (MapStr::iterator it = list.begin(); it != list.end(); it++) {
g.vsFileName.push_back(it->second);
if (wcscmp(it->second.c_str(), szFileName) == 0) {
g.idxCurr = idx;
}
idx++;
}
}
//------------------------------------------------------------------------------
BOOL IsImgFile(LPCTSTR pszPath)
{
static LPCTSTR ext[] = { L".jpg", L".jpeg", L".gif", L".png" };
TCHAR szExt[_MAX_EXT];
_wsplitpath_s(pszPath, NULL, 0, NULL, 0, NULL, 0, szExt, _MAX_EXT);
for (int n = 0; n < _countof(ext); n++) {
if (_wcsicmp(ext[n], szExt) == 0) {
return TRUE;
}
}
return FALSE;
}
//==============================================================================
LRESULT CALLBACK WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg) {
case WM_PAINT:
OnPaint(hWnd);
return 0;
case WM_COMMAND:
if (OnCommand(hWnd, wParam)) {
return 0;
}
break;
case WM_DROPFILES:
OnDropFiles(hWnd, wParam);
DragFinish((HDROP)wParam);
return 0;
case WM_CREATE:
CoInitialize(NULL);
CoCreateInstance(CLSID_ShellImageDataFactory, NULL, CLSCTX_INPROC_SERVER,
IID_PPV_ARGS(&g.pImgDatFac));
GetImage(hWnd);
return 0;
case WM_DESTROY:
SAFE_RELEASE(g.pImgDat);
SAFE_RELEASE(g.pImgDatFac);
CoUninitialize();
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, uMsg, wParam, lParam);
}
//------------------------------------------------------------------------------
BOOL OnCommand(HWND hWnd, WPARAM wParam)
{
switch (LOWORD(wParam)) {
case ID_RIGHT:
case ID_DOWN:
g.idxCurr++;
if (g.vsFileName.size() <= g.idxCurr) {
g.idxCurr = 0;
}
break;
case ID_LEFT:
case ID_UP:
if (g.idxCurr <= 0) {
g.idxCurr = g.vsFileName.size();
}
g.idxCurr--;
break;
case ID_ENTER:
ShowWindow(hWnd, IsZoomed(hWnd) ? SW_SHOWNOACTIVATE : SW_MAXIMIZE);
break;
case ID_SIZE_NORMAL:
OnSizeNormal(hWnd);
// あえてスルー
case ID_SIZE_ADJUST:
OnSizeAdjust(hWnd);
break;
case ID_EXECUTE:
OnExecute();
break;
case ID_DELETE:
OnDelete(hWnd);
break;
case ID_ESCAPE:
DestroyWindow(hWnd);
return TRUE;
default:
return FALSE;
}
GetImage(hWnd);
InvalidateRect(hWnd, NULL, FALSE); // ちらつき防止のため背景消去しない
return TRUE;
}
//------------------------------------------------------------------------------
void OnDelete(HWND hWnd)
{
if (g.vsFileName.empty()) return;
TCHAR szPath[MAX_PATH]; // '\0'2つで終わる要あり
swprintf_s(szPath, L"%s%s%c", g.szBaseDir, g.vsFileName[g.idxCurr].c_str(), L'\0');
SHFILEOPSTRUCT fo;
ZeroMemory(&fo, sizeof fo);
fo.hwnd = hWnd;
fo.wFunc = FO_DELETE;
fo.pFrom = szPath;
fo.fFlags = FOF_ALLOWUNDO;
SHFileOperation(&fo);
}
//------------------------------------------------------------------------------
void OnDropFiles(HWND hWnd, WPARAM wParam)
{
HDROP hDrop;
TCHAR szPath[MAX_PATH];
hDrop = (HDROP)wParam;
DragQueryFile(hDrop, 0, szPath, _countof(szPath));
CreateFileList(szPath);
GetImage(hWnd);
InvalidateRect(hWnd, NULL, FALSE); // ちらつき防止のため背景消去しない
}
//------------------------------------------------------------------------------
void OnSizeNormal(HWND hWnd)
{
if (g.pImgDat == NULL) return;
g.sizeDst = g.sizeImg;
RECT rcWorkArea;// タスクバーを除くデスクトップ領域
RECT rcWnd; // ウィンドウ領域
RECT rcClt; // クライアント領域
SIZE size; // ウィンドウ領域とクライアント領域の差分
SystemParametersInfo(SPI_GETWORKAREA, 0, &rcWorkArea, 0);
GetWindowRect(hWnd, &rcWnd);
GetClientRect(hWnd, &rcClt);
size.cx = WIDTH(rcWnd) - WIDTH(rcClt);
size.cy = HEIGHT(rcWnd) - HEIGHT(rcClt);
// デスクトップ領域からはみ出す場合、ウィンドウを左上方向に移動する
if (rcWorkArea.right < rcWnd.left + g.sizeDst.cx + size.cx) {
rcWnd.left = 0;
}
if (rcWorkArea.bottom < rcWnd.top + g.sizeDst.cy + size.cy) {
rcWnd.top = 0;
}
// 最大クライアント領域に収める
if (rcWorkArea.right - size.cx < g.sizeDst.cx) {
g.sizeDst.cx = rcWorkArea.right - size.cx;
}
if (rcWorkArea.bottom - size.cy < g.sizeDst.cy) {
g.sizeDst.cy = rcWorkArea.bottom - size.cy;
}
// アスペクト比の調整
if (g.sizeImg.cx < g.sizeImg.cy * g.sizeDst.cx / g.sizeDst.cy) {
g.sizeDst.cx = g.sizeDst.cy * g.sizeImg.cx / g.sizeImg.cy;
}
else {
g.sizeDst.cy = g.sizeDst.cx * g.sizeImg.cy / g.sizeImg.cx;
}
// ウィンドウ位置のセット
rcWnd.right = g.sizeDst.cx + size.cx;
rcWnd.bottom = g.sizeDst.cy + size.cy;
SetWindowPos(hWnd, NULL, rcWnd.left, rcWnd.top, rcWnd.right, rcWnd.bottom, SWP_NOZORDER);
}
//------------------------------------------------------------------------------
void OnSizeAdjust(HWND hWnd)
{
if (g.pImgDat == NULL) return;
RECT rc;
SetRect(&rc, 0, 0, g.sizeDst.cx, g.sizeDst.cy);
AdjustWindowRectEx(&rc, WS_OVERLAPPEDWINDOW, FALSE, 0);
SetWindowPos(hWnd, NULL, 0, 0, WIDTH(rc), HEIGHT(rc), SWP_NOZORDER | SWP_NOMOVE);
}
//------------------------------------------------------------------------------
void OnExecute(void)
{
if (g.vsFileName.empty()) return;
TCHAR szFile[MAX_PATH];
TCHAR szParams[MAX_PATH];
GetModuleFileName(NULL, szFile, MAX_PATH);
swprintf_s(szParams, L"\"%s%s\"", g.szBaseDir, g.vsFileName[g.idxCurr].c_str());
SHELLEXECUTEINFO ei;
ZeroMemory(&ei, sizeof ei);
ei.cbSize = sizeof ei;
ei.nShow = SW_SHOWNORMAL;
ei.fMask = SEE_MASK_NOCLOSEPROCESS;
ei.lpFile = szFile;
ei.lpParameters = szParams;
ShellExecuteEx(&ei);
}
//------------------------------------------------------------------------------
void OnPaint(HWND hWnd)
{
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hWnd, &ps);
if (g.pImgDat == NULL) goto Exit;
RECT rcClt;
GetClientRect(hWnd, &rcClt);
// 画像とクライアント領域のアスペクト比を比較し
// アスペクト比を維持したままクライアント領域に収める
if (g.sizeImg.cx < g.sizeImg.cy * rcClt.right / rcClt.bottom) {
g.sizeDst.cx = rcClt.bottom * g.sizeImg.cx / g.sizeImg.cy;
g.sizeDst.cy = rcClt.bottom;
}
else {
g.sizeDst.cx = rcClt.right;
g.sizeDst.cy = rcClt.right * g.sizeImg.cy / g.sizeImg.cx;
}
RECT rcDst;
rcDst.left = (rcClt.right - g.sizeDst.cx) / 2;
rcDst.top = (rcClt.bottom - g.sizeDst.cy) / 2;
rcDst.right = rcDst.left + g.sizeDst.cx;
rcDst.bottom = rcDst.top + g.sizeDst.cy;
// 画像描画
RECT rcTmp;
SetRect(&rcTmp, 0, 0, g.sizeImg.cx, g.sizeImg.cy);
g.pImgDat->Draw(hdc, &rcDst, &rcTmp);
// 余白塗り潰し
HBRUSH hbr = (HBRUSH)(COLOR_WINDOW + 1);
if (rcClt.left < rcDst.left) {
CopyRect(&rcTmp, &rcClt);
rcTmp.right = rcDst.left;
FillRect(hdc, &rcTmp, hbr);
}
if (rcClt.top < rcDst.top) {
CopyRect(&rcTmp, &rcClt);
rcTmp.bottom = rcDst.top;
FillRect(hdc, &rcTmp, hbr);
}
if (rcDst.right < rcClt.right) {
CopyRect(&rcTmp, &rcClt);
rcTmp.left = rcDst.right;
FillRect(hdc, &rcTmp, hbr);
}
if (rcDst.bottom < rcClt.bottom) {
CopyRect(&rcTmp, &rcClt);
rcTmp.top = rcDst.bottom;
FillRect(hdc, &rcTmp, hbr);
}
Exit:
EndPaint(hWnd, &ps);
}
//------------------------------------------------------------------------------
void GetImage(HWND hWnd)
{
SAFE_RELEASE(g.pImgDat);
if (g.vsFileName.empty()) {
SetWindowText(hWnd, APP_NAME);
return;
}
// 画像ファイルの読み込み
TCHAR szBuf[512];
LPCTSTR pszFileName = g.vsFileName[g.idxCurr].c_str();
swprintf_s(szBuf, L"%s%s", g.szBaseDir, pszFileName);
HRESULT hr = g.pImgDatFac->CreateImageFromFile(szBuf, &g.pImgDat);
if (SUCCEEDED(hr)) {
hr = g.pImgDat->Decode(SHIMGDEC_DEFAULT, 0, 0);
}
if (SUCCEEDED(hr)) {
hr = g.pImgDat->GetSize(&g.sizeImg);
}
if (FAILED(hr) || g.sizeImg.cx <= 0 || g.sizeImg.cy <= 0) {
SAFE_RELEASE(g.pImgDat);
g.sizeImg.cx = g.sizeImg.cy = 0;
}
// ファイル更新日時の取得
SYSTEMTIME st;
MyGetFileTime(&st, szBuf);
// タイトルバー
swprintf_s(szBuf, L"%s [%d/%d] %d/%02d/%02d %02d:%02d:%02d (%dx%d) - %s",
pszFileName, g.idxCurr + 1, g.vsFileName.size(),
st.wYear, st.wMonth, st.wDay, st.wHour, st.wMinute, st.wSecond,
g.sizeImg.cx, g.sizeImg.cy, APP_NAME);
SetWindowText(hWnd, szBuf);
}
//------------------------------------------------------------------------------
BOOL MyGetFileTime(LPSYSTEMTIME pSystemTime, LPCTSTR pszFileName)
{
HANDLE hFile = CreateFile(pszFileName, GENERIC_READ, 0, NULL,
OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hFile == INVALID_HANDLE_VALUE) {
return FALSE;
}
FILETIME ftLastWrite;
FILETIME ftLocal;
GetFileTime(hFile, NULL, NULL, &ftLastWrite);
FileTimeToLocalFileTime(&ftLastWrite, &ftLocal);
FileTimeToSystemTime(&ftLocal, pSystemTime);
CloseHandle(hFile);
return TRUE;
}
// resource script
#include <windows.h>
#include "resource.h"
// アクセラレータ
IDR_MAIN ACCELERATORS
BEGIN
VK_RETURN, ID_ENTER, VIRTKEY // 0x0D
VK_ESCAPE, ID_ESCAPE, VIRTKEY // 0x1B
VK_LEFT, ID_LEFT, VIRTKEY // 0x25
VK_UP, ID_UP, VIRTKEY // 0x26
VK_RIGHT, ID_RIGHT, VIRTKEY // 0x27
VK_DOWN, ID_DOWN, VIRTKEY // 0x28
VK_DELETE, ID_DELETE, VIRTKEY // 0x2E
"1", ID_SIZE_ADJUST, VIRTKEY // 0x31
VK_NUMPAD1, ID_SIZE_ADJUST, VIRTKEY // 0x61
"2", ID_SIZE_NORMAL, VIRTKEY // 0x32
VK_NUMPAD2, ID_SIZE_NORMAL, VIRTKEY // 0x62
"3", ID_EXECUTE, VIRTKEY // 0x33
VK_NUMPAD3, ID_EXECUTE, VIRTKEY // 0x63
END