データベースの種類について

データベースを利用する手法はいくつかある。Windows環境であれは下記の手法が主流となっていると思います。

  • SQLiteを使う方法
  • Access DB(MDB)を使う方法
  • MySQLを使う方法
  • Microsoft SQL Serverを使う方法

クライアント単独のアプリであれば、単独でデータベースを操作するためのライブラリが存在しているため、 上記の上2つがよいと思います。今回は、SQLiteを使った方法を紹介します。

SQLiteの導入

SQLiteは下記のURLから必要なソースコードをダウンロードして、プロジェクトに組込めばそのまますぐに使えるようになります。

SQLite Download Page

上記のSource Codeからsqlite-amalgamation-XXXXXXX.zip(Xは数字)をダウンロード・解凍して、 sqlite3.csqlite3.hをプロジェクトに追加します。

後は、基本的な使い方は、以下のようになります。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// sqliteのインクルード
#include "sqlite3.h"

...

// データベースファイルを開く
sqlite3* db = NULL;
auto status = sqlite3_open_v2("ejdict.sqlite3", &db, SQLITE_OPEN_READONLY, nullptr);

// SQLの実行
char* errmsg;
status = sqlite3_exec(db, "select column1, column2 from items where word like 'abc%'", callback, nullptr, &errmsg);

// データベースを閉じる
sqlite3_close_v2(db);

sqlite3_exec関数の第三引数では、SQLの結果を処理するためのコールバック関数を指定します。例えば下記のような関数の形式となります。

1
2
3
4
5
6
7
8
static int callback(void* hEdit2, int argc, char** argv, char** columnName)
{
  if (argc == 2 && argv[0] && argv[1])
  {
    printf("column1 = %s, column2 = %s", argv[0], argv[1]);
  }
  return SQLITE_OK;
}

SQLiteを使ったサンプルプログラム

下記のサイトでSQLiteのデータベースが公開されているので、それを使って英和辞書を作成します。

英和辞書データ ダウンロード

ダウントードしてプログラムから参照しようとしたとき、そのままではテーブル名や列名が分からないため、sqliteのビューア等でデータベース内部の構成を確認します。 (sqliteのビューアとして、お勧めは、DB Browser for SQLiteです。)

テーブル名がitems、英単語列がword、日本語訳がmeanとなっていることが確認できます。

これに基づいて、入力された英単語をデータベースから検索し、日本語訳を表示するサンプルプログラムを書いてみました。

プロジェクトのダウンロード

[C++コード]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
#pragma comment(linker,"\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")

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

#define DEFAULT_DPI 96
#define SCALEX(X) MulDiv(X, uDpiX, DEFAULT_DPI)
#define SCALEY(Y) MulDiv(Y, uDpiY, DEFAULT_DPI)
#define POINT2PIXEL(PT) MulDiv(PT, uDpiY, 72)

TCHAR szClassName[] = TEXT("Window");

BOOL GetScaling(HWND hWnd, UINT* pnX, UINT* pnY)
{
  BOOL bSetScaling = FALSE;
  const HMONITOR hMonitor = MonitorFromWindow(hWnd, MONITOR_DEFAULTTONEAREST);
  if (hMonitor)
  {
    HMODULE hShcore = LoadLibrary(TEXT("SHCORE"));
    if (hShcore)
    {
      typedef HRESULT __stdcall GetDpiForMonitor(HMONITOR, int, UINT*, UINT*);
      GetDpiForMonitor* fnGetDpiForMonitor = reinterpret_cast<GetDpiForMonitor*>(GetProcAddress(hShcore, "GetDpiForMonitor"));
      if (fnGetDpiForMonitor)
      {
        UINT uDpiX, uDpiY;
        if (SUCCEEDED(fnGetDpiForMonitor(hMonitor, 0, &uDpiX, &uDpiY)) && uDpiX > 0 && uDpiY > 0)
        {
          *pnX = uDpiX;
          *pnY = uDpiY;
          bSetScaling = TRUE;
        }
      }
      FreeLibrary(hShcore);
    }
  }
  if (!bSetScaling)
  {
    HDC hdc = GetDC(NULL);
    if (hdc)
    {
      *pnX = GetDeviceCaps(hdc, LOGPIXELSX);
      *pnY = GetDeviceCaps(hdc, LOGPIXELSY);
      ReleaseDC(NULL, hdc);
      bSetScaling = TRUE;
    }
  }
  if (!bSetScaling)
  {
    *pnX = DEFAULT_DPI;
    *pnY = DEFAULT_DPI;
    bSetScaling = TRUE;
  }
  return bSetScaling;
}

LPWSTR a2w(LPCSTR lpszText)
{
  const DWORD dwSize = MultiByteToWideChar(CP_UTF8, 0, lpszText, -1, 0, 0);
  LPWSTR lpszReturnText = (LPWSTR)GlobalAlloc(0, dwSize * sizeof(WCHAR));
  MultiByteToWideChar(CP_UTF8, 0, lpszText, -1, lpszReturnText, dwSize);
  return lpszReturnText;
}

LPSTR w2a(LPCWSTR lpszText)
{
  const DWORD dwSize = WideCharToMultiByte(CP_UTF8, 0, lpszText, -1, 0, 0, 0, 0);
  LPSTR lpszReturnText = (LPSTR)GlobalAlloc(0, dwSize * sizeof(char));
  WideCharToMultiByte(CP_UTF8, 0, lpszText, -1, lpszReturnText, dwSize, 0, 0);
  return lpszReturnText;
}

static int callback(void* hEdit2, int argc, char** argv, char** columnName)
{
  if (argc == 2)
  {
    if (argv[0] && argv[1])
    {
      LPWSTR lpszText1 = a2w(argv[0]);
      SendMessageW((HWND)hEdit2, EM_REPLACESEL, 0, (LPARAM)lpszText1);
      SendMessageW((HWND)hEdit2, EM_REPLACESEL, 0, (LPARAM)L" = ");
      GlobalFree(lpszText1);
      LPWSTR lpszText2 = a2w(argv[1]);
      SendMessageW((HWND)hEdit2, EM_REPLACESEL, 0, (LPARAM)lpszText2);
      SendMessageW((HWND)hEdit2, EM_REPLACESEL, 0, (LPARAM)L"\r\n");
      GlobalFree(lpszText2);
    }
  }
  return SQLITE_OK;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
  static HWND hButton;
  static HWND hEdit1;
  static HWND hEdit2;
  static HFONT hFont;
  static UINT uDpiX = DEFAULT_DPI, uDpiY = DEFAULT_DPI;
  switch (msg)
  {
  case WM_CREATE:
    hEdit1 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), 0, WS_VISIBLE | WS_CHILD | WS_TABSTOP | ES_AUTOHSCROLL, 0, 0, 0, 0, hWnd, 0, ((LPCREATESTRUCT)lParam)->hInstance, 0);
    hButton = CreateWindow(TEXT("BUTTON"), TEXT("検索"), WS_VISIBLE | WS_CHILD | WS_TABSTOP, 0, 0, 0, 0, hWnd, (HMENU)IDOK, ((LPCREATESTRUCT)lParam)->hInstance, 0);
    hEdit2 = CreateWindowEx(WS_EX_CLIENTEDGE, TEXT("EDIT"), 0, WS_VISIBLE | WS_CHILD | WS_HSCROLL | WS_VSCROLL | WS_TABSTOP | ES_READONLY | ES_MULTILINE | ES_AUTOHSCROLL | ES_AUTOVSCROLL, 0, 0, 0, 0, hWnd, 0, ((LPCREATESTRUCT)lParam)->hInstance, 0);
    SendMessage(hWnd, WM_APP, 0, 0);
    break;
  case WM_SIZE:
    MoveWindow(hEdit1, POINT2PIXEL(10), POINT2PIXEL(10), POINT2PIXEL(256), POINT2PIXEL(20), TRUE);
    MoveWindow(hButton, POINT2PIXEL(256 + 20), POINT2PIXEL(10), POINT2PIXEL(100), POINT2PIXEL(20), TRUE);
    MoveWindow(hEdit2, POINT2PIXEL(10), POINT2PIXEL(40), LOWORD(lParam) - POINT2PIXEL(20), HIWORD(lParam) - POINT2PIXEL(50), TRUE);
    break;
  case WM_SETFOCUS:
    SetFocus(hEdit1);
    break;
  case WM_COMMAND:
    if (LOWORD(wParam) == IDOK)
    {
      sqlite3* db = NULL;
      auto status = sqlite3_open_v2("ejdict.sqlite3", &db, SQLITE_OPEN_READONLY, nullptr);
      if (status == SQLITE_OK)
      {
        const int nTextLength = GetWindowTextLength(hEdit1);
        LPWSTR lpszText = (LPWSTR)GlobalAlloc(0, sizeof(WCHAR)*(nTextLength + 1));
        GetWindowText(hEdit1, lpszText, nTextLength + 1);
        LPSTR lpszTextA = w2a(lpszText);
        GlobalFree(lpszText);
        const int nTextLengthA = (int)GlobalSize(lpszTextA);
        LPSTR lpszSQL = (LPSTR)GlobalAlloc(0, nTextLengthA + 100);
        lstrcpyA(lpszSQL, "select word, mean from items where word like '");
        lstrcatA(lpszSQL, lpszTextA);
        lstrcatA(lpszSQL, "%' limit 100");
        SetWindowText((HWND)hEdit2, 0);
        char* errmsg;
        status = sqlite3_exec(db, lpszSQL, callback, (void*)hEdit2, &errmsg);
        if (errmsg)
        {
          LPWSTR lpszText = a2w(errmsg);
          SendMessageW((HWND)hEdit2, EM_REPLACESEL, 0, (LPARAM)lpszText);
          GlobalFree(lpszText);
        }
        GlobalFree(lpszSQL);
        GlobalFree(lpszTextA);
        sqlite3_close_v2(db);
      }
    }
    break;
  case WM_NCCREATE:
    {
      const HMODULE hModUser32 = GetModuleHandle(TEXT("user32.dll"));
      if (hModUser32)
      {
        typedef BOOL(WINAPI*fnTypeEnableNCScaling)(HWND);
        const fnTypeEnableNCScaling fnEnableNCScaling = (fnTypeEnableNCScaling)GetProcAddress(hModUser32, "EnableNonClientDpiScaling");
        if (fnEnableNCScaling)
        {
          fnEnableNCScaling(hWnd);
        }
      }
    }
    return DefWindowProc(hWnd, msg, wParam, lParam);
  case WM_DPICHANGED:
    SendMessage(hWnd, WM_APP, 0, 0);
    break;
  case WM_APP:
    GetScaling(hWnd, &uDpiX, &uDpiY);
    DeleteObject(hFont);
    hFont = CreateFontW(-POINT2PIXEL(10), 0, 0, 0, FW_NORMAL, 0, 0, 0, SHIFTJIS_CHARSET, 0, 0, 0, 0, L"MS Shell Dlg");
    SendMessage(hButton, WM_SETFONT, (WPARAM)hFont, 0);
    SendMessage(hEdit1, WM_SETFONT, (WPARAM)hFont, 0);
    SendMessage(hEdit2, WM_SETFONT, (WPARAM)hFont, 0);
    break;
  case WM_CLOSE:
    DestroyWindow(hWnd);
    break;
  case WM_DESTROY:
    DeleteObject(hFont);
    PostQuitMessage(0);
    break;
  default:
    return DefDlgProc(hWnd, msg, wParam, lParam);
  }
  return 0;
}

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPreInst, LPSTR pCmdLine, int nCmdShow)
{
  MSG msg;
  WNDCLASS wndclass = {
    CS_HREDRAW | CS_VREDRAW,
    WndProc,
    0,
    DLGWINDOWEXTRA,
    hInstance,
    0,
    LoadCursor(0,IDC_ARROW),
    0,
    0,
    szClassName
  };
  RegisterClass(&wndclass);
  HWND hWnd = CreateWindow(
    szClassName,
    TEXT("英和辞書"),
    WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN,
    CW_USEDEFAULT,
    0,
    CW_USEDEFAULT,
    0,
    0,
    0,
    hInstance,
    0
  );
  ShowWindow(hWnd, SW_SHOWDEFAULT);
  UpdateWindow(hWnd);
  while (GetMessage(&msg, 0, 0, 0))
  {
    if (!IsDialogMessage(hWnd, &msg))
    {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
    }
  }
  return (int)msg.wParam;
}

参考