Visual Studio を使うと VC++ 版の Windows GUI アプリのひな型が作成されるが、それでもアプリを構築するのは大変である。今は大半の人は C# など .net を使うと思うが、.net のない環境で GUI アプリを作成する羽目になったので、備忘録代わりにやり方を残しておこうと思う。
WinMain, RegisterClass, WndProc までは自動生成してくれるが、問題はここからである。
まず、WndProc に WM_CREATE 処理ハンドラを追加する。
LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam) {
switch (mes) {
case WM_CREATE:
onCreate(hWnd, wParam, lParam);
break;
}
...
}
onCreate() でメインウィンドウに配置する子ウィンドウを作成する。
std::vector<HWND> hChild;
void onCreate(HWND hWnd, WPARAM wParam, LPARAM lParam) {
HWND hChild[j] = CreateWindow(lpClass, lpTitle, dType, x, y, w, h, hWnd, (HMENU)j, ((LPCREATESTRUCT)lParam)->hInstance, nullptr);
}
WndProc の WM_COMMAND 処理ハンドルに子ウィンドウ処理を追加する。
LRESULT CALLBACK WndProc(HWND hWnd, UINT mes, WPARAM wParam, LPARAM lParam) {
switch(mes) {
case WM_COMMAND:
{
WORD j = LOWORD(wParam);
switch (j) {
case IDM_xxx:
onCommandXX(hWnd, hChild[j], HIWORD(wParam));
}
}
break;
}
}
子ウィンドウ処理では、例えばテキストボックスの値が変わった場合、
void onCommandXX(HWND hParent, HWND hChild, WORKD iCode) {
if (iCode == EN_CHANGE) {
GetWindowText(hChild, szEditWindow, ARRAYSIZE(szEditWindow));
}
}
とすることで、変更後の値を取得することができる。
ファイルオープン・書き込みダイアログの API は SHLWAPI に移動されたようだ。まずオープンから。
#pragma comment (lib, "shlwapi.lib")
#include <shlwapi.h>
void onBaseFileOpen(HWND hWnd, WORD iCode, const COMDLG_FILTERSPEC* pf, int fsize, LPCTSTR lpIniFile, LPCTSTR lpDefault, void (*cb)(PWSTR str)) {
if (iCode == BN_CLICKED) {
HRESULT hr = 0;
IFileDialog* pfd;
hr = CoCreateInstance(CLSID_FileOpenDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
DWORD dwFlags;
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr)) {
pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
hr = pfd->SetFileTypes(fsize, pf);
if (SUCCEEDED(hr)) {
pfd->SetFileTypeIndex(1);
pfd->SetDefaultExtension(lpDefault);
if (lpIniFile) {
pfd->SetFileName(GetFileName(lpIniFile).c_str());
}
hr = pfd->Show(hWnd);
if (SUCCEEDED(hr)) {
IShellItem* psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr)) {
PWSTR pszFilePath = nullptr;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
cb(pszFilePath);
CoTaskMemFree(pszFilePath);
psiResult->Release();
}
}
}
}
pfd->Release();
}
}
void onFileOpenCallback(PWSTR str) {
std::unordered_map<int, WndCreate>::iterator i = wndCreate.find(2);
if (i != wndCreate.end()) {
SetWindowText(i->second.hWnd, str);
_tcscpy_s(szSourceFile, ARRAYSIZE(szSourceFile), str);
}
}
void onFileOpen(HWND hWnd, HWND hChild, WORD iCode) {
static const COMDLG_FILTERSPEC c_filter[] = {
{ L"テキスト (*.txt)", L"*.txt"}
};
static LPCTSTR lpDefault = L"txt";
void (*cb)(PWSTR str) = onFileOpenCallback;
onBaseFileOpen(hWnd, iCode, c_filter, ARRAYSIZE(c_filter), szSourceFile, lpDefault, cb);
}
ファイル保存は以下の通り。
void onLogFileSaveCallback(PWSTR str) {
std::unordered_map<int, WndCreate>::iterator i = wndCreate.find(9);
if (i != wndCreate.end()) {
SetWindowText(i->second.hWnd, str);
_tcscpy_s(szLogFile, ARRAYSIZE(szLogFile), str);
}
}
void onBaseFileSave(HWND hWnd, WORD iCode, const COMDLG_FILTERSPEC* pf, int fsize, LPCTSTR lpIniFile, LPCTSTR lpDefault, void (*cb)(PWSTR str)) {
if (iCode == BN_CLICKED) {
HRESULT hr = 0;
IFileDialog* pfd;
hr = CoCreateInstance(CLSID_FileSaveDialog, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pfd));
DWORD dwFlags;
hr = pfd->GetOptions(&dwFlags);
if (SUCCEEDED(hr)) {
pfd->SetOptions(dwFlags | FOS_FORCEFILESYSTEM);
hr = pfd->SetFileTypes(fsize, pf);
if (SUCCEEDED(hr)) {
pfd->SetFileTypeIndex(1);
pfd->SetDefaultExtension(lpDefault);
if (lpIniFile) {
pfd->SetFileName(GetFileName(lpIniFile).c_str());
}
hr = pfd->Show(hWnd);
if (SUCCEEDED(hr)) {
IShellItem* psiResult;
hr = pfd->GetResult(&psiResult);
if (SUCCEEDED(hr)) {
PWSTR pszFilePath = nullptr;
hr = psiResult->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
cb(pszFilePath);
CoTaskMemFree(pszFilePath);
psiResult->Release();
}
}
}
}
pfd->Release();
}
}
void onLogFileSave(HWND hWnd, HWND hChild, WORD iCode) {
static const COMDLG_FILTERSPEC c_filter[] = {
{ L"CSVファイル (*.csv)", L"*.csv"}
};
static LPCTSTR lpDefault = L"csv";
void (*cb)(PWSTR str) = onLogFileSaveCallback;
onBaseFileSave(hWnd, iCode, c_filter, ARRAYSIZE(c_filter), szLogFile, lpDefault, cb);
}
onCommandXX の代わりに onFileOpen, OnLogFileSave を呼び出すようにすればよい。
子ウィンドウの作成及び処理は1つのクラスにまとめた方が都合が良い。
struct WndRect {
int x, y, w, h;
WndRect(int x, int y, int w, int h) : x(x), y(y), w(w), h(h) {}
};
struct WndCreate {
void (*createEx)(HWND hWnd, WndCreate &wCreate);
void (*onCommand)(HWND hWnd, HWND hChild, WORD iCode);
WndRect rect;
DWORD dType;
LPCTSTR lpClassName;
LPCTSTR lpTitle;
HWND hWnd;
std::unordered_map<int, std::wstring> vInit;
WndCreate() : hWnd(0), createEx(nullptr), onCommand(nullptr), rect(0, 0, 0, 0), dType(0LL), lpClassName(nullptr), lpTitle(nullptr), vInit() {}
};
extern std::vector<WndCreate> vCreate;
今となっては全然思い出せないが、昔はこんな苦労してウィンドウを書いていたのだった。
コメントを残す