開発環境 Microsoft Visual C++ 2010 Express (SP1)
実行環境 Microsoft Windows XP Home Edition (SP3)
プロジェクトの種類 Win32 プロジェクト
プロジェクト名 PrnAddr
アプリケーションの種類 Windows アプリケーション
追加のオプション 空のプロジェクト
文字セット マルチバイト

注意
元のファイルが失われる恐れがあるので、必要なファイルは必ずバックアップを取ってください。

PrnAddr.cpp
// マルチバイト
#include <Windows.h>
#include <CommCtrl.h>
#include <tchar.h>
#include <sqlext.h>
#include <stdio.h>
#include "resource.h"
 
#define WIDTH(rect) ((rect).right - (rect).left)
#define HEIGHT(rect) ((rect).bottom - (rect).top)
#define LTRB(rect) (rect).left, (rect).top, (rect).right, (rect).bottom
 
#define DRIVER _T("Microsoft Text Driver (*.txt; *.csv)")
#define APPNAME _T("PrnAddr")
 
// 関数プロトタイプ宣言
BOOL SplitPath(LPCTSTR pszPath);
void Trace(LPCTSTR format, ...);
void DigitOnly(LPTSTR pszStr);
 
INT_PTR CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnInitDialog(HWND hDlg);
void OnDropFiles(HWND hDlg, WPARAM wParam);
BOOL OnClose(HWND hDlg);
BOOL CheckRecMod(HWND hWnd);
void OnCommand(HWND hDlg, WPARAM wParam, LPARAM lParam);
void OnUpdate(void);
void OnDrawItem(WPARAM wParam, LPARAM lParam);
void OnDraw(HDC hdc, int nRec, BOOL bGuide);
void DrawField(HDC hdc, PRECT prc, int nRow, int nCol, BOOL bGuide, LPCTSTR pszSuffix);
void OnPrint(HWND hDlg, int nMode);
void CopyRecord(void);
void ShowSubDlg(void);
int lmpx(int lm);
int lmpy(int lm);
int lmpw(int lm);
int lmph(int lm);
 
INT_PTR CALLBACK SubDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
void OnCheck(HWND hDlg);
void OnSave(HWND hDlg);
void OnSubPrint(HWND hDlg);
void OnSubInitDialog(HWND hDlg);
void InitAddress(HWND hWnd);
void LoadTable(HWND hWnd);
BOOL SaveTable(HWND hWnd);
BOOL CreateCsvFile(HWND hWnd, LPCTSTR pszPath);
 
// 外部変数構造体
static struct {
	HINSTANCE hInstance;
	HWND hMainDlg;		// メインダイアログ
	HWND hSubDlg;		// 住所録ダイアログ
	HWND hRec;		// レコードエディットハンドル
	HWND hSpin;		// スピンコントロールハンドル
	HWND hUpdate;		// 更新ボタンハンドル
	HWND hList;		// リストビューハンドル
	HGLOBAL hDevMode;	// デバイスモード構造体ハンドル
	HGLOBAL hDevNames;	// デバイス名構造体ハンドル
	SQLHENV henv;		// 環境ハンドル
	TCHAR szDbq[MAX_PATH];	// データベースディレクトリ
	TCHAR szTable[MAX_PATH];// CSVファイル
	int nRecNum;		// レコード数
	int nRec;		// 対象レコード
	BOOL bRecMod;		// レコード変更フラグ
	BOOL bTableMod;		// テーブル変更フラグ
 
	// デバイス能力
	int nHorzRes;
	int nVertRes;
	int nPhysicalWidth;
	int nPhysicalHeight;
	int nPhysicalOffsetX;
	int nPhysicalOffsetY;
	int nLogPixelsX;
	int nLogPixelsY;
} g;
 
//==============================================================================
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE, LPSTR, int)
{
	g.hInstance = hInstance;
 
	// ODBC環境ハンドルの確保
	SQLRETURN rc;	// retcode
	rc = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &g.henv);
	rc = SQLSetEnvAttr(g.henv, SQL_ATTR_ODBC_VERSION, (SQLPOINTER)SQL_OV_ODBC3, 0);
 
	// コマンド引数の取得
	LPWSTR pszCmdLine = GetCommandLineW();
	int argc;
	LPWSTR *argv = CommandLineToArgvW(pszCmdLine, &argc);
	if (2 <= argc) {
		TCHAR szPath[MAX_PATH];
		wcstombs_s(NULL, szPath, argv[1], _TRUNCATE);
		SplitPath(szPath);
	}
	LocalFree(argv);
 
	DialogBox(hInstance, MAKEINTRESOURCE(IDD_MAIN), NULL, MainDlgProc);
 
	HGLOBAL hr;
	hr = GlobalFree(g.hDevMode);
	hr = GlobalFree(g.hDevNames);
	rc = SQLFreeHandle(SQL_HANDLE_ENV, g.henv);
	return 0;
}
 
//------------------------------------------------------------------------------
BOOL SplitPath(LPCTSTR pszPath)
{
	TCHAR szDrive	[_MAX_DRIVE];
	TCHAR szDir	[_MAX_DIR];
	TCHAR szFName	[_MAX_FNAME];
	TCHAR szExt	[_MAX_EXT];
 
	_splitpath_s(pszPath, szDrive, szDir, szFName, szExt);
	_makepath_s(g.szDbq, szDrive, szDir, NULL, NULL);
	_makepath_s(g.szTable, NULL, NULL, szFName, szExt);
	return TRUE;
}
 
//------------------------------------------------------------------------------
void Trace(LPCTSTR format, ...)
{
	va_list arg_ptr;
	TCHAR buffer[256];
	int size;
 
	va_start(arg_ptr, format);
	size = _vsntprintf_s(buffer, _TRUNCATE, format, arg_ptr);
	va_end(arg_ptr);
	OutputDebugString(buffer);
	if (size < 0) {
		OutputDebugString(_T("...\n"));
	}
}
 
//------------------------------------------------------------------------------
void DigitOnly(LPTSTR pszStr)
{
	LPTSTR psz;
	TCHAR c;
 
	for (psz = pszStr; c = *pszStr; pszStr++) {
		if (_istdigit(c)) {
			*psz++ = c;
		}
	}
	*psz = c;
}
 
//==============================================================================
INT_PTR CALLBACK MainDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	INT_PTR nRet = TRUE;	// メッセージを処理した
 
	switch (uMsg) {
	case WM_COMMAND:
		OnCommand(hDlg, wParam, lParam);
		break;
	case WM_DRAWITEM:
		OnDrawItem(wParam, lParam);
		break;
	case WM_DROPFILES:
		OnDropFiles(hDlg, wParam);
		DragFinish((HDROP)wParam);
		break;
	case WM_INITDIALOG:
		OnInitDialog(hDlg);
		nRet = TRUE;	// SetFocusでフォーカスを設定した場合はFALSE
		break;
	case WM_CLOSE:
		if (OnClose(hDlg)) {
			EndDialog(hDlg, 0);
		}
		break;
	default:
		nRet = FALSE;	// メッセージを処理しなかった
	}
	return nRet;
}
 
//------------------------------------------------------------------------------
void OnInitDialog(HWND hDlg)
{
	g.hMainDlg = hDlg;
	g.hRec		= GetDlgItem(hDlg, IDC_RECORD);
	g.hSpin		= GetDlgItem(hDlg, IDC_SPIN);
	g.hUpdate	= GetDlgItem(hDlg, IDC_UPDATE);
	SendMessage(g.hSpin, UDM_SETBUDDY, (WPARAM)g.hRec, 0);
	g.hSubDlg = CreateDialog(g.hInstance, MAKEINTRESOURCE(IDD_SUB), hDlg, SubDlgProc);
}
 
//------------------------------------------------------------------------------
void OnDropFiles(HWND hDlg, WPARAM wParam)
{
	HDROP hDrop = (HDROP)wParam;
	TCHAR szPath[MAX_PATH];
	UINT ur = DragQueryFile(hDrop, 0, szPath, _countof(szPath));
	SplitPath(szPath);
	ListView_DeleteAllItems(g.hList);
	InitAddress(hDlg);
	InvalidateRect(GetDlgItem(hDlg, IDC_PREVIEW), NULL, FALSE);
}
 
//------------------------------------------------------------------------------
BOOL OnClose(HWND hDlg)
{
	BOOL bRet = TRUE;
 
	// レコード変更の確認
	if (CheckRecMod(hDlg) == FALSE) return FALSE;
 
	// テーブル変更の確認
	if (g.bTableMod == FALSE) return TRUE;
	int nr = MessageBox(hDlg,
		_T("住所録の内容は変更されています。\n\n保存しますか?"),
		APPNAME, MB_YESNOCANCEL | MB_ICONWARNING);
	switch (nr) {
	case IDYES:
		bRet = SaveTable(hDlg);
		break;
	case IDNO:
		break;
	default:
		bRet = FALSE;
	}
	return bRet;
}
 
//------------------------------------------------------------------------------
BOOL CheckRecMod(HWND hWnd)
{
	if (g.bRecMod == FALSE) return TRUE;
	int nr = MessageBox(hWnd,
		_T("レコードの内容は変更されています。\n\n更新しますか?"),
		APPNAME, MB_YESNOCANCEL | MB_ICONWARNING);
	switch (nr) {
	case IDYES:
		OnUpdate();
		break;
	case IDNO:
		break;
	default:
		return FALSE;
	}
	return TRUE;
}
 
//------------------------------------------------------------------------------
void OnCommand(HWND hDlg, WPARAM wParam, LPARAM lParam)
{
	WORD wNotifyCode = HIWORD(wParam);
	WORD wID = LOWORD(wParam);
	HWND hwndCtl = (HWND)lParam;
 
	switch (wID) {
	case IDC_ADDRESS:	// 住所録
		ShowSubDlg();
		break;
	case IDC_PRINT:		// 単票印刷
		if (CheckRecMod(hDlg)) {
			OnPrint(hDlg, 0);
		}
		break;
	case IDC_UPDATE:	// 更新
		OnUpdate();
		break;
	case IDCANCEL:		// 終了
		SendMessage(hDlg, WM_CLOSE, 0, 0);
		break;
	}
	if (wNotifyCode == EN_CHANGE) {
		if (hwndCtl == g.hRec) {
			int nRec = max(SendMessage(g.hSpin, UDM_GETPOS, 0, 0) - 1, 0);
			if (nRec == g.nRec) return;
			if (CheckRecMod(hDlg)) {
				g.nRec = nRec;
				CopyRecord();
				InvalidateRect(GetDlgItem(hDlg, IDC_PREVIEW), NULL, FALSE);
			} else {
				SendMessage(g.hSpin, UDM_SETPOS, 0, g.nRec + 1);
			}
		} else {
			if (g.bRecMod == FALSE) {
				g.bRecMod = TRUE;
				EnableWindow(g.hUpdate, g.bRecMod);
			}
		}
	}
}
 
//------------------------------------------------------------------------------
void OnUpdate(void)
{
	TCHAR szBuf[256];
 
	if (g.nRecNum <= g.nRec) return;
	for (int n = 0; n < 6; n++) {
		GetDlgItemText(g.hMainDlg, IDC_FIELD0 + n, szBuf, _countof(szBuf));
		ListView_SetItemText(g.hList, g.nRec, n, szBuf);
	}
	g.bTableMod = TRUE;
	g.bRecMod = FALSE;
	EnableWindow(g.hUpdate, g.bRecMod);
	InvalidateRect(GetDlgItem(g.hMainDlg, IDC_PREVIEW), NULL, FALSE);
}
 
//------------------------------------------------------------------------------
void OnDrawItem(WPARAM wParam, LPARAM lParam)
{
	LPDRAWITEMSTRUCT pdi = (LPDRAWITEMSTRUCT)lParam;
 
	HDC hdc = pdi->hDC;
 
	// 背景塗り潰し
	RECT rc;
	GetClientRect(pdi->hwndItem, &rc);
	FillRect(hdc, &rc, GetSysColorBrush(COLOR_WINDOW));
	Trace(_T("OnDrawItem(%u) %d, %d, %d, %d\n"), wParam, LTRB(rc));
 
	// デバイス能力
	g.nPhysicalWidth	= WIDTH(rc);
	g.nPhysicalHeight	= HEIGHT(rc);
	g.nPhysicalOffsetX	= 0;
	g.nPhysicalOffsetY	= 0;
 
	// 背景
	SetRect(&rc, lmpx(0), lmpy(0), lmpx(300), lmpy(570));
	FillRect(hdc, &rc, GetSysColorBrush(COLOR_BTNFACE));
	SetRect(&rc, lmpx(0), lmpy(1250), lmpx(1000), lmpy(1480));
	FillRect(hdc, &rc, GetSysColorBrush(COLOR_BTNFACE));
 
	int nRec = SendMessage(g.hSpin, UDM_GETPOS, 0, 0);
	OnDraw(hdc, nRec - 1, TRUE);
}
 
//------------------------------------------------------------------------------
void OnDraw(HDC hdc, int nRec, BOOL bGuide)
{
	TCHAR szBuf[256];
	HFONT hFont[3];
	RECT rc;
	SetBkMode(hdc, TRANSPARENT);	// 背景透過モード
 
	Trace(_T("OnDraw(%d)\n"), nRec);
 
	// 補助線
	HPEN hPen = CreatePen(PS_SOLID, 0, RGB(0xff,0x00,0x00));
	HGDIOBJ hPenOld = SelectObject(hdc, hPen);
 
	// 郵便番号
	LOGFONT lf;
	ZeroMemory(&lf, sizeof lf);
	lf.lfCharSet	= DEFAULT_CHARSET;
	lf.lfHeight	= -lmph(60);
	_tcscpy_s(lf.lfFaceName, _T("MS ゴシック"));
	hFont[0] = CreateFontIndirect(&lf);
	HGDIOBJ hFontOld = SelectObject(hdc, hFont[0]);
 
	SetRect(&rc, lmpx(440), lmpy(120), lmpx(930), lmpy(200));
	if (bGuide) {
		Rectangle(hdc, LTRB(rc));
	}
	if (nRec < g.nRecNum) {
		ListView_GetItemText(g.hList, nRec, 2, szBuf, _countof(szBuf));
		DigitOnly(szBuf);
		// 440, 510, 580, 650, 720, 790, 860
		for (size_t n = 0; n < _tcslen(szBuf); n++) {
			rc.right = rc.left + lmpw(70);
			DrawText(hdc, szBuf + n, 1, &rc, DT_SINGLELINE | DT_CENTER | DT_VCENTER);
			rc.left = rc.right;
		}
	}
 
	// 住所
	lf.lfEscapement	= 2700;
	lf.lfOrientation= 2700;
	lf.lfHeight	= -lmph(60);
	_tcscpy_s(lf.lfFaceName, _T("@MS 明朝"));
	hFont[1] = CreateFontIndirect(&lf);
	SelectObject(hdc, hFont[1]);
 
	SetRect(&rc, lmpx(850-70), lmpy(300), lmpx(850), lmpy(1230));
	DrawField(hdc, &rc, nRec, 3, bGuide, NULL);
	SetRect(&rc, lmpx(780-70), lmpy(400), lmpx(780), lmpy(1230));
	DrawField(hdc, &rc, nRec, 4, bGuide, NULL);
	SetRect(&rc, lmpx(710-70), lmpy(400), lmpx(710), lmpy(1230));
	DrawField(hdc, &rc, nRec, 5, bGuide, NULL);
 
	// 氏名・連名
	lf.lfHeight	= -lmph(90);
	hFont[2] = CreateFontIndirect(&lf);
	SelectObject(hdc, hFont[2]);
 
	SetRect(&rc, lmpx(520-100), lmpy(400), lmpx(520), lmpy(1230));
	DrawField(hdc, &rc, nRec, 0, bGuide, _T("様"));
	SetRect(&rc, lmpx(420-100), lmpy(400), lmpx(420), lmpy(1230));
	DrawField(hdc, &rc, nRec, 1, bGuide, _T("様"));
 
	// 終了処理
	SelectObject(hdc, hFontOld);
	for (int n = 0; n < _countof(hFont); n++) {
		DeleteObject(hFont[n]);
	}
	SelectObject(hdc, hPenOld);
	DeleteObject(hPen);
}
 
//------------------------------------------------------------------------------
void DrawField(HDC hdc, PRECT prc, int nRow, int nCol, BOOL bGuide, LPCTSTR pszSuffix)
{
	TCHAR szText[64];
 
	if (bGuide) {
		Rectangle(hdc, LTRB(*prc));
	}
	if (g.nRecNum <= nRow) return;
 
	ListView_GetItemText(g.hList, nRow, nCol, szText, _countof(szText));
	if (szText[0]) {
		if (pszSuffix) {
			_tcscat_s(szText, pszSuffix);
		}
		TextOut(hdc, prc->right, prc->top, szText, _tcslen(szText));
	}
}
 
//------------------------------------------------------------------------------
void OnPrint(HWND hDlg, int nMode)
{
	PRINTDLGEX pd;
	LPDEVMODE pdm;
	LPDEVNAMES pdn;
	HRESULT hr;
	BOOL br;
	HDC hdc = NULL;
 
	// 印刷ダイアログ
	ZeroMemory(&pd, sizeof pd);
	pd.lStructSize	= sizeof pd;
	pd.hwndOwner	= hDlg;
	pd.hDevMode	= g.hDevMode;
	pd.hDevNames	= g.hDevNames;
	pd.Flags	= PD_NOPAGENUMS;
	pd.nStartPage	= START_PAGE_GENERAL;
	hr = PrintDlgEx(&pd);
	if (FAILED(hr)) {
		goto Exit;
	}
	g.hDevMode = pd.hDevMode;
	g.hDevNames = pd.hDevNames;
	if (pd.dwResultAction != PD_RESULT_PRINT) {
		goto Exit;
	}
 
	// プリンタ準備
	pdm = (LPDEVMODE)GlobalLock(g.hDevMode);
	pdn = (LPDEVNAMES)GlobalLock(g.hDevNames);
	pdm->dmOrientation = DMORIENT_PORTRAIT;
	hdc = CreateDC(NULL, (LPCTSTR)pdn + pdn->wDeviceOffset, NULL, pdm);
	br = GlobalUnlock(g.hDevNames);
	br = GlobalUnlock(g.hDevMode);
 
	// デバイス能力の取得
	g.nHorzRes		= GetDeviceCaps(hdc, HORZRES);
	g.nVertRes		= GetDeviceCaps(hdc, VERTRES);
	g.nPhysicalWidth	= GetDeviceCaps(hdc, PHYSICALWIDTH);
	g.nPhysicalHeight	= GetDeviceCaps(hdc, PHYSICALHEIGHT);
	g.nPhysicalOffsetX	= GetDeviceCaps(hdc, PHYSICALOFFSETX);
	g.nPhysicalOffsetY	= GetDeviceCaps(hdc, PHYSICALOFFSETY);
	g.nLogPixelsX		= GetDeviceCaps(hdc, LOGPIXELSX);
	g.nLogPixelsY		= GetDeviceCaps(hdc, LOGPIXELSY);
 
	// 用紙サイズを確認
	SIZE size;
	size.cx = g.nPhysicalWidth  * 254 / g.nLogPixelsX;
	size.cy = g.nPhysicalHeight * 254 / g.nLogPixelsY;
	Trace(_T("OnPrint %d %d\n"), size.cx, size.cy);
	if (100 < abs(size.cx - 1000) || 100 < abs(size.cy - 1480)) {
		MessageBox(hDlg, _T("用紙サイズを確認してください。"), NULL, MB_OK);
		goto Exit;
	}
 
	// 印刷開始
	DOCINFO di;
	ZeroMemory(&di, sizeof di);
	di.cbSize	= sizeof di;
	di.lpszDocName	= APPNAME;
	int nr = StartDoc(hdc, &di);
 
	switch (nMode) {
	case 0:	// 単票
		nr = StartPage(hdc);
		int nRec;
		nRec = SendMessage(g.hSpin, UDM_GETPOS, 0, 0);
		OnDraw(hdc, nRec - 1, TRUE);
		nr = EndPage(hdc);
		break;
	case 1:	// 連続
		for (int n = 0; n < g.nRecNum; n++) {
			br = ListView_GetCheckState(g.hList, n);
			if (br) {
				nr = StartPage(hdc);
				OnDraw(hdc, n, TRUE);
				nr = EndPage(hdc);
			}
		}
		break;
	}
 
	// 印刷終了
	nr = EndDoc(hdc);
Exit:
	if (hdc) {
		DeleteDC(hdc);
	}
}
 
//------------------------------------------------------------------------------
void CopyRecord(void)
{
	TCHAR szBuf[256];
 
	Trace(_T("CopyRecord %d %d\n"), g.nRec, g.nRecNum);
	if (g.nRecNum <= g.nRec) return;
	for (int n = 0; n < 6; n++) {
		ListView_GetItemText(g.hList, g.nRec, n, szBuf, _countof(szBuf));
		SetDlgItemText(g.hMainDlg, IDC_FIELD0 + n, szBuf);
	}
	g.bRecMod = FALSE;
	EnableWindow(g.hUpdate, g.bRecMod);
}
 
//------------------------------------------------------------------------------
void ShowSubDlg(void)
{
	ShowWindow(g.hSubDlg, IsWindowVisible(g.hSubDlg) ? SW_HIDE : SW_SHOW);
}
 
//------------------------------------------------------------------------------
// 0.1mm単位をピクセルに変換
// LoMetric to Pixel X
int lmpx(int lm)
{
	return lmpw(lm) - g.nPhysicalOffsetX;
}
// LoMetric to Pixel Y
int lmpy(int lm)
{
	return lmph(lm) - g.nPhysicalOffsetY;
}
// LoMetric to Pixel Width
int lmpw(int lm)
{
	return lm * g.nPhysicalWidth / 1000;
}
// LoMetric to Pixel Height
int lmph(int lm)
{
	return lm * g.nPhysicalHeight / 1480;
}
 
//==============================================================================
INT_PTR CALLBACK SubDlgProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	INT_PTR nRet = TRUE;	// メッセージを処理した
 
	switch (uMsg) {
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDC_CHECK:	// 全てチェック/解除
			OnCheck(hDlg);
			break;
		case IDC_SAVE:	// 保存
			OnSave(hDlg);
			break;
		case IDC_PRINT:	// 連続印刷
			OnSubPrint(hDlg);
			break;
		case IDCANCEL:	// 閉じる
			ShowSubDlg();
			break;
		}
		break;
	case WM_INITDIALOG:
		OnSubInitDialog(hDlg);
		nRet = TRUE;	// SetFocusでフォーカスを設定した場合はFALSE
		break;
	case WM_CLOSE:
		ShowSubDlg();
		break;
	default:
		nRet = FALSE;	// メッセージを処理しなかった
	}
	return nRet;
}
 
//------------------------------------------------------------------------------
void OnCheck(HWND hDlg)
{
	UINT ur = IsDlgButtonChecked(hDlg, IDC_CHECK);
	BOOL bCheck = (ur == BST_CHECKED);
	for (int n = 0; n < g.nRecNum; n++) {
		ListView_SetCheckState(g.hList, n, bCheck);
	}
}
 
//------------------------------------------------------------------------------
void OnSave(HWND hDlg)
{
	if (g.bTableMod == FALSE) return;
	int nr = MessageBox(hDlg, _T("住所録を保存しますか?"), APPNAME, MB_YESNO | MB_ICONQUESTION);
	if (nr == IDYES) {
		SaveTable(hDlg);
	}
}
 
//------------------------------------------------------------------------------
void OnSubPrint(HWND hDlg)
{
	BOOL br = FALSE;
	for (int n = 0; n < g.nRecNum; n++) {
		br = ListView_GetCheckState(g.hList, n);
		if (br) break;
	}
	if (br == FALSE) {
		MessageBox(hDlg, _T("印刷するレコードにチェックを付けてください。"), APPNAME, MB_OK);
		return;
	}
	OnPrint(hDlg, 1);
}
 
//------------------------------------------------------------------------------
void OnSubInitDialog(HWND hDlg)
{
	RECT rc;
	GetClientRect(hDlg, &rc);
	Trace(_T("OnInitDialog %d, %d, %d, %d\n"), LTRB(rc));
 
	// リストビュー拡張スタイル
	g.hList = GetDlgItem(hDlg, IDC_LIST);
	DWORD dwExStyle = ListView_GetExtendedListViewStyle(g.hList);
	dwExStyle |= LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT | LVS_EX_GRIDLINES;
	ListView_SetExtendedListViewStyle(g.hList, dwExStyle);
 
	InitAddress(hDlg);
}
 
//------------------------------------------------------------------------------
void InitAddress(HWND hWnd)
{
	// テーブルの読み込み
	LoadTable(hWnd);
 
	g.nRecNum = ListView_GetItemCount(g.hList);
	g.nRec = 0;
	CopyRecord();
	SendMessage(g.hSpin, UDM_SETRANGE32, 1, g.nRecNum);
	SendMessage(g.hSpin, UDM_SETPOS, 0, MAKELONG(1, 0));
}
 
//------------------------------------------------------------------------------
void LoadTable(HWND hWnd)
{
	TCHAR buf[256];
	SQLHDBC hdbc;	// 接続ハンドル
	SQLHSTMT hstmt;	// 命令ハンドル
	SQLSMALLINT col;
	SQLSMALLINT n;
	SQLSMALLINT datatype;
	SQLLEN len;
	SQLRETURN rc;	// retcode
 
	// データベースへの接続
	_stprintf_s(buf, _T("driver={%s}; dbq=%s"), DRIVER, g.szDbq);
	rc = SQLAllocHandle(SQL_HANDLE_DBC, g.henv, &hdbc);
	rc = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *)buf,
		SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
	if (!SQL_SUCCEEDED(rc)) {
		goto Exit;
	}
 
	// 検索
	_stprintf_s(buf, _T("select * from %s"), g.szTable);
	rc = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	rc = SQLExecDirect(hstmt, (SQLTCHAR *)buf, SQL_NTS);
	if (!SQL_SUCCEEDED(rc)) {
		goto Exit;
	}
	// 列情報
	LV_COLUMN lvCol;
	lvCol.mask	= LVCF_FMT | LVCF_WIDTH | LVCF_TEXT | LVCF_SUBITEM;
	lvCol.fmt	= LVCFMT_LEFT;
	lvCol.cx	= 100;
	rc = SQLNumResultCols(hstmt, &col);
	for (n = 0; n < col; n++) {
		rc = SQLDescribeCol(hstmt, n+1, (SQLTCHAR *)buf, _countof(buf), NULL,
			&datatype, NULL, NULL, NULL);
		lvCol.pszText	= buf;
		lvCol.iSubItem	= n;
		ListView_InsertColumn(g.hList, n, &lvCol);
		// Text 12 SQL_VARCHAR Char
		// Byte -6 SQL_TINYINT
	}
	// 行情報
	LV_ITEM lvItem;
	lvItem.mask	= LVIF_TEXT;
	lvItem.iItem	= 0;
	while (1) {
		rc = SQLFetch(hstmt);
		if (rc == SQL_NO_DATA) break;
		if (rc == SQL_ERROR) break;
		for (n = 0; n < col; n++) {
			SQLGetData(hstmt, n+1, SQL_C_TCHAR, buf, _countof(buf), &len);
			if (len == SQL_NULL_DATA) {
				buf[0] = _T('\0');
			}
			lvItem.pszText	= buf;
			lvItem.iSubItem	= n;
			if (n == 0) {
				ListView_InsertItem(g.hList, &lvItem);
			} else {
				ListView_SetItem(g.hList, &lvItem);
			}
		}
		lvItem.iItem++;
	}
	SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
Exit:
	SQLDisconnect(hdbc);
	SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
}
 
//------------------------------------------------------------------------------
BOOL SaveTable(HWND hWnd)
{
	TCHAR szNew[MAX_PATH];
	_stprintf_s(szNew, _T("%s%s_"), g.szDbq, g.szTable);
	if (CreateCsvFile(hWnd, szNew) == FALSE) {
		return FALSE;
	}
 
	// CSVファイルの削除とリネーム
	TCHAR szOld[MAX_PATH];
	_stprintf_s(szOld, _T("%s%s"),  g.szDbq, g.szTable);
	BOOL br;
	br = DeleteFile(szOld);
	br = MoveFile(szNew, szOld);
	if (br == FALSE) {
		MessageBox(hWnd, _T("CSVファイルのリネームに失敗しました。"), NULL, MB_OK);
		return FALSE;
	}
	g.bTableMod = FALSE;
	return TRUE;
}
 
//------------------------------------------------------------------------------
BOOL CreateCsvFile(HWND hWnd, LPCTSTR pszPath)
{
	TCHAR szBuf[MAX_PATH];
	int nColNum = 6;
 
	// ファイルのオープン
	FILE *pFile;
	errno_t er = _tfopen_s(&pFile, pszPath, _T("wt"));
	if (er) {
		MessageBox(hWnd, _T("ファイルのオープンに失敗しました。"), NULL, MB_OK);
		return FALSE;
	}
 
	// ヘッダ行
	_stprintf_s(szBuf, _T("%s%s"), g.szDbq, _T("schema.ini"));
	DWORD dw = GetPrivateProfileString(g.szTable, _T("ColNameHeader"), NULL,
		szBuf, _countof(szBuf), szBuf);
	if (_tcsicmp(szBuf, _T("true")) == 0) {
		LVCOLUMN lvcol;
		lvcol.mask	= LVCF_TEXT;
		lvcol.pszText	= szBuf;
		lvcol.cchTextMax= _countof(szBuf);
		for (int nCol = 0; ; ) {
			ListView_GetColumn(g.hList, nCol, &lvcol);
			_ftprintf_s(pFile, _T("%s"), lvcol.pszText);
			if (nColNum <= ++nCol) {
				break;
			}
			_ftprintf_s(pFile, _T(","));
		}
		_ftprintf_s(pFile, _T("\n"));
	}
 
	// 書き込み
	int nRowNum = ListView_GetItemCount(g.hList);
	for (int nRow = 0; nRow < nRowNum; nRow++) {
		for (int nCol = 0; ; ) {
			ListView_GetItemText(g.hList, nRow, nCol, szBuf, _countof(szBuf));
			_ftprintf_s(pFile, _T("%s"), szBuf);
			if (nColNum <= ++nCol) {
				break;
			}
			_ftprintf_s(pFile, _T(","));
		}
		_ftprintf_s(pFile, _T("\n"));
	}
 
	fclose(pFile);
	return TRUE;
}
 

resource.h
#define IDD_MAIN	100
#define IDD_SUB		101
 
#define IDC_STATIC	-1
#define IDC_PREVIEW	1000
#define IDC_ADDRESS	1001
#define IDC_PRINT	1002
#define IDC_RECORD	1003
#define IDC_SPIN	1004
#define IDC_UPDATE	1005
#define IDC_FIELD0	1010
#define IDC_FIELD1	1011
#define IDC_FIELD2	1012
#define IDC_FIELD3	1013
#define IDC_FIELD4	1014
#define IDC_FIELD5	1015
 
#define IDC_LIST	1100
#define IDC_CHECK	1101
#define IDC_SAVE	1102
 

PrnAddr.rc
// resource script
#include <windows.h>
#include "resource.h"
 
//------------------------------------------------------------------------------
IDD_MAIN DIALOGEX 100, 50, 400, 304
STYLE WS_POPUPWINDOW | WS_MINIMIZEBOX
EXSTYLE WS_EX_APPWINDOW | WS_EX_ACCEPTFILES
CAPTION "PrnAddr"
FONT 10, "MS ゴシック"
BEGIN
	LTEXT		"",IDC_PREVIEW,4,4,200,296,WS_BORDER | SS_OWNERDRAW
	LTEXT		"氏名",IDC_STATIC,212,8,32,12
	EDITTEXT	IDC_FIELD0,248,8,144,12,ES_AUTOHSCROLL
	LTEXT		"連名",IDC_STATIC,212,28,32,12
	EDITTEXT	IDC_FIELD1,248,28,144,12,ES_AUTOHSCROLL
	LTEXT		"郵便番号",IDC_STATIC,212,48,32,12
	EDITTEXT	IDC_FIELD2,248,48,144,12,ES_AUTOHSCROLL
	LTEXT		"住所1",IDC_STATIC,212,68,32,12
	EDITTEXT	IDC_FIELD3,248,68,144,12,ES_AUTOHSCROLL
	LTEXT		"住所2",IDC_STATIC,212,88,32,12
	EDITTEXT	IDC_FIELD4,248,88,144,12,ES_AUTOHSCROLL
	LTEXT		"住所3",IDC_STATIC,212,108,32,12
	EDITTEXT	IDC_FIELD5,248,108,144,12,ES_AUTOHSCROLL
	EDITTEXT	IDC_RECORD,248,128,48,16
	CONTROL		"",IDC_SPIN,"msctls_updown32",
			UDS_ARROWKEYS | UDS_SETBUDDYINT | UDS_ALIGNRIGHT,
			280,128,32,16
	PUSHBUTTON	"更新(&U)",IDC_UPDATE,336,128,56,24
	PUSHBUTTON	"住所録(&A)",IDC_ADDRESS,212,276,56,24
	PUSHBUTTON	"単票印刷(&P)",IDC_PRINT,276,276,56,24
	PUSHBUTTON	"終了(&X)",IDCANCEL,340,276,56,24
END
 
//------------------------------------------------------------------------------
IDD_SUB DIALOG 0, 350, 384, 200
STYLE WS_POPUPWINDOW
CAPTION "住所録"
FONT 9, "MS Pゴシック"
BEGIN
	CONTROL		"",IDC_LIST,"SysListView32",
			WS_BORDER | WS_TABSTOP | LVS_REPORT,8,8,368,160
	CONTROL		"全てチェック/解除(&A)",IDC_CHECK,"button",
			BS_AUTOCHECKBOX | WS_TABSTOP,8,176,80,16
	PUSHBUTTON	"保存(&S)",IDC_SAVE,216,176,48,16
	PUSHBUTTON	"連続印刷(&P)",IDC_PRINT,272,176,48,16
	PUSHBUTTON	"閉じる(&X)",IDCANCEL,328,176,48,16
END
 

schema.ini
[address.csv]
ColNameHeader=True
CharacterSet=932
Format=CSVDelimited
Col1=氏名 Char
Col2=連名 Char
Col3=郵便番号 Char Width 8
Col4=住所1 Char
Col5=住所2 Char
Col6=住所3 Char

address.csv
氏名,連名,郵便番号,住所1,住所2,住所3
国会 議事堂,   花子 ,100-0014,東京都千代田区,永田町一丁目七番一号,
東京 タワー,,105-0011,東京都港区,芝公園四丁目二―八,
最終更新:2012年10月31日 18:06