EternalWindows
ODBC / データベースとODBC

データベースがどのようなものであるかは、アプリケーション開発者にとってある意味で分かりやすいといえます。 たとえば、ある人物の個人情報を記録するファイルがあり、そのファイルから年齢というデータを取得したいとします。 この場合、まずそのファイルをオープンし、特定の行から年齢を取得するコードを記述することになると思われますが、 よく考えてみるとこれは非常に不思議なことではないでしょうか。 なぜなら、そのアプリケーションが究極的に必要としているのは、ある人物の年齢であり、 それがどのファイルのどの位置に格納されているかは、本来ならば問われるべきではないはずです。 極端な話、「xxさんの年齢をください」という命令を送り、 その応答として年齢を取得することができたならば、これほど便利なことはないでしょう。 データベースというのは、こうした動作を実現するための基盤であり、 データの格納場所やアドレスといったものをアプリケーションから隠蔽します。

先に述べたような命令を処理するのは、DBMSというアプリケーションです。 多くの場合、DBMSはWindowsサービスとして実装され、自動で起動するように設定されているでしょう。 DBMSがデータの要求を受け取った場合、データベースから目的のデータが検索され、 見つかった場合はそのデータがアプリケーションに返されることになります。 この流れの最大の特徴は、データの格納場所をアプリケーションが指定していないという点であり、 たとえ目的のデータが何らかのファイルに格納されていたとしても、 そのファイルがデータベースであると解釈してはいけません。 DBMSからすれば、データはファイルからでもネットワーク上からでも取得できますし、 動的に生成したデータを返すことすらも可能であることから、 データベースの実体というのは定義できるものではありません。 したがって、データの要求元であるアプリケーションは、 データベースを「データが格納されているモノ」という抽象的な解釈しかできなくなりますが、 これでよいのです。 この抽象化により、データの格納場所というものを意識する必要がなくなるからです。

多くのDBMSは、自身のデータベースへアクセスするための専用のAPIを公開しています。 たとえば、MySQLならばC API、Oracleならばoo4oというAPIがあります。 ただし、こうしたAPIを呼び出す設計にすると、 使用するDBMSを変更した場合にコードの互換性がなくなりますから、 どのようなDBMSにも共通した方法でアクセスできるAPIが欲しいものです。 これがODBC(Open DataBase Connectivity)であり、 Windowsにはodbc32.dllとして存在しています。 次に、ODBCのモデル図を示します。

odbc32.dllはドライバマネージャとも呼ばれ、その目的はODBCドライバにアプリケーションからの要求を伝えることです。 たとえば、アプリケーションがodbc32.dllのSQLExecDirectを呼び出した場合は、 その内部で今度は、ODBCドライバのSQLExecDirectが呼ばれます。 ODBCドライバの正体はDLLであり、odbc32.dllが実装する関数と同一の名前を持った関数を実装しています。 DBMSの開発元がODBCドライバを用意していれば、 アプリケーションはドライバ専用の関数ではなく、ODBC関数(SQLから始まる関数)を呼び出せますから、 コードの互換性を維持しやすくなります。

アプリケーションはデータベースにアクセスして具体的に何を行うことになるのでしょうか。 一般的には、テーブルを作成してデータを追加したり、 テーブルに記録されているデータを取得したりするのが代表的であるといえます。 ただし、ここで少し注意しなければならないのは、 こうした操作自体をODBCが提供しているわけではないという点です。 つまり、テーブルを作成する関数というものが用意されているわけではないのです。 アプリケーションがデータベースに対して何らかの操作を行うためには、 その操作内容を示す文字列をODBCドライバに送らなければならず、 多くの場合この文字列のフォーマットはSQL言語を基にしています。 つまり、DBMSとODBCドライバを用意してもSQL言語の知識がなければ、 事実上データベースは利用できないことになります。

ODBCを使用するためにはODBCドライバが必要になりますから、 まずはシステムにどのようなODBCドライバがインストールされているかを確認してみましょう。 次に示すSQLGetInstalledDriversは、インストールされているドライバの名前を返します。

BOOL SQLGetInstalledDrivers(
  LPSTR lpszBuf,
  WORD cbBufMax,
  WORD *pcbBufOut
);

lpszBufは、一連のドライバ名を受け取るバッファを指定します。 cbBufMaxは、lpszBufのサイズを指定します。 pcbBufOutは、lpszBufに格納されたサイズを受け取る変数のアドレスを指定します。

今回のプログラムは、システムにインストールされているODBCドライバを列挙します。 ODBCのインストール系関数を呼び出す場合は、odbcinst.hのインクルードが必要になります。

#include <windows.h>
#include <odbcinst.h>

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	TCHAR      szAppName[] = TEXT("sample");
	HWND       hwnd;
	MSG        msg;
	WNDCLASSEX wc;

	wc.cbSize        = sizeof(WNDCLASSEX);
	wc.style         = 0;
	wc.lpfnWndProc   = WindowProc;
	wc.cbClsExtra    = 0;
	wc.cbWndExtra    = 0;
	wc.hInstance     = hinst;
	wc.hIcon         = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	wc.hCursor       = (HCURSOR)LoadImage(NULL, IDC_ARROW, IMAGE_CURSOR, 0, 0, LR_SHARED);
	wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	wc.lpszMenuName  = NULL;
	wc.lpszClassName = szAppName;
	wc.hIconSm       = (HICON)LoadImage(NULL, IDI_APPLICATION, IMAGE_ICON, 0, 0, LR_SHARED);
	
	if (RegisterClassEx(&wc) == 0)
		return 0;

	hwnd = CreateWindowEx(0, szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hinst, NULL);
	if (hwnd == NULL)
		return 0;

	ShowWindow(hwnd, nCmdShow);
	UpdateWindow(hwnd);
	
	while (GetMessage(&msg, NULL, 0, 0) > 0) {
		TranslateMessage(&msg);
		DispatchMessage(&msg);
	}

	return (int)msg.wParam;
}

LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
	static HWND hwndListBox = NULL;
	
	switch (uMsg) {

	case WM_CREATE: {
		BYTE   buf[2048];
		LPTSTR lpsz;
		
		hwndListBox = CreateWindowEx(0, TEXT("LISTBOX"), NULL, WS_CHILD | WS_VISIBLE | WS_VSCROLL, 0, 0, 0, 0, hwnd, (HMENU)1, ((LPCREATESTRUCT)lParam)->hInstance, NULL);
	
		SQLGetInstalledDrivers((LPTSTR)buf, sizeof(buf), NULL);

		lpsz = (LPTSTR)buf;
		for (;lpsz[0] != '\0';) {
			SendMessage(hwndListBox, LB_ADDSTRING, 0, (LPARAM)lpsz);
			lpsz = lpsz + lstrlen(lpsz) + 1;
		}

		return 0;
	}

	case WM_SIZE:
		MoveWindow(hwndListBox, 0, 0, LOWORD(lParam), HIWORD(lParam), TRUE);
		return 0;

	case WM_DESTROY:
		PostQuitMessage(0);
		return 0;

	default:
		break;

	}

	return DefWindowProc(hwnd, uMsg, wParam, lParam);
}

SQLGetInstalledDriversの第1引数には、 インストールされているODBCドライバの名前が'\0'文字で区切られて格納されています。 よって、次のドライバ名を参照したい場合は、lstrlenの戻り値に+1をしたものを、 現在指しているアドレスに足せばよいことになります。 最後のドライバ名には'\0'文字が2つ格納されているため、 lpsz[0]が'\0'文字である場合は、これ以上ドライバが存在しないことを意味しています。

データベースにアクセスするためには、DBMSとODBCドライバの両方が必要になりますから、 これらを事前にダウンロードしてインストールする必要があります。 ダウンロード先は、使用するDBMSによって異なりますから、 たとえばMySQLを使用するならば、「MySQL ダウンロード」という語句で検索することになるでしょう。 また、ODBCドライバについては、「MySQL ODBCドライバ ダウンロード」という語句で検索すればよいでしょう。 ちなみに、筆者はMySQLのODBCドライバをインストールしたため、 SQLGetInstalledDriversの結果に"MySQL ODBC 5.1 Driver"という名前が含まれていました。


戻る