// Unicode
#include <Windows.h>
#include <CommCtrl.h>
#include <shimgdata.h>
#include <stdio.h>
#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("ImgView2")
typedef vector<wstring> VecStr;
// 関数プロトタイプ宣言
void CreateFileList(LPCTSTR pszPath);
BOOL GetFileList(HWND hExplorer, LPCTSTR pszFileName);
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam);
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];
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);
HWND hExplorer = NULL;
while (hExplorer = FindWindowEx(NULL, hExplorer, L"ExploreWClass", NULL)) {
GetFileList(hExplorer, szFileName);
if (0 <= g.idxCurr) {
break;
}
}
if (g.idxCurr == -1) {
g.vsFileName.clear();
g.vsFileName.push_back(pszPath);
g.idxCurr = 0;
}
}
//------------------------------------------------------------------------------
BOOL GetFileList(HWND hExplorer, LPCTSTR pszFileName)
{
// リストビュー
g.hListView = NULL;
BOOL br = EnumChildWindows(hExplorer, EnumWindowsProc, 0);
if (g.hListView == NULL) return FALSE;
// 対象となるリストビューのプロセスハンドルを得る
DWORD dwProcId = 0;
GetWindowThreadProcessId(g.hListView, &dwProcId);
HANDLE hProc = OpenProcess(PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE,
FALSE, dwProcId);
if (hProc == NULL) return FALSE;
// 他プロセス空間にメモリを確保
LPTSTR pbuf = (LPTSTR)VirtualAllocEx(hProc, NULL, MAX_PATH,
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
LPLVITEM plvi = (LPLVITEM)VirtualAllocEx(hProc, NULL, sizeof (LVITEM),
MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE);
// 行数
int nRowNum = ListView_GetItemCount(g.hListView);
VecStr::size_type idx = 0;
// ListView_GetItemText
LVITEM lvi;
ZeroMemory(&lvi, sizeof (LVITEM));
lvi.iSubItem = 0;
lvi.mask = LVIF_TEXT;
lvi.pszText = pbuf;
lvi.cchTextMax = MAX_PATH;
for (int nRow = 0; nRow < nRowNum; nRow++) {
lvi.iItem = nRow;
WriteProcessMemory(hProc, plvi, &lvi, sizeof (LVITEM), NULL);
BOOL br = SendMessage(g.hListView, LVM_GETITEM, 0, (LPARAM)plvi);
if (br) {
TCHAR szText[MAX_PATH];
ReadProcessMemory(hProc, pbuf, szText, MAX_PATH, NULL);
if (IsImgFile(szText)) {
g.vsFileName.push_back(szText);
if (wcscmp(szText, pszFileName) == 0) {
g.idxCurr = idx;
}
idx++;
}
}
}
VirtualFreeEx(hProc, plvi, 0, MEM_RELEASE);
VirtualFreeEx(hProc, pbuf, 0, MEM_RELEASE);
CloseHandle(hProc);
return TRUE;
}
//------------------------------------------------------------------------------
BOOL CALLBACK EnumWindowsProc(HWND hWnd, LPARAM lParam)
{
TCHAR szBuf[256];
int nr = GetClassName(hWnd, szBuf, _countof(szBuf));
if (wcscmp(szBuf, WC_LISTVIEW) == 0) { // L"SysListView32"
g.hListView = hWnd;
return FALSE; // 打ち切り
}
BOOL br = EnumChildWindows(hWnd, EnumWindowsProc, lParam);
return TRUE; // 続行
}
//------------------------------------------------------------------------------
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 ftCreation;
FILETIME ftLocal;
GetFileTime(hFile, &ftCreation, NULL, NULL);
FileTimeToLocalFileTime(&ftCreation, &ftLocal);
FileTimeToSystemTime(&ftLocal, pSystemTime);
CloseHandle(hFile);
return TRUE;
}