EternalWindows
ODBC / データソースの作成

前節で述べたように、アプリケーションがデータベースにアクセスするためには、 アクセス先のコンピュータにDBMSがインストールされている必要があります。 さらに、アプリケーションがODBCを呼び出してデータベースにアクセスする場合は、 ローカルコンピュータ上にODBCドライバがインストールされている必要があります。 これらは、データベースへアクセスするための最低限の準備ですが、 多くの場合はこれらに加えて、データソースの作成という作業も必要になります。 データソースとは、データベースへアクセスするための情報をまとめたものであり、 1つのデータソースはDSN(Data Source Name)という名前で識別されることになります。 データソースを作成しておけば、この名前を指定するだけで、 データソースに格納される情報を間接的に指定できるという利点があります。

データソースの作成や変更などは、SQLConfigDataSourceで行うことになります。

BOOL SQLConfigDataSource(
  HWND hwndParent,
  WORD fRequest,
  LPCSTR lpszDriver,
  LPCSTR lpszAttributes
);

hwndParentは、NULLまたはウインドウハンドルを指定します。 ウインドウハンドルを指定した場合は、ODBCドライバ固有のダイアログが表示されることがあります。 fRequestは、データソースをどう扱うかを示す定数を指定します。 ODBC_ADD_DSNならばデータソースの追加、ODBC_CONFIG_DSNならばデータソースの調整、 ODBC_REMOVE_DSNならばデータソースの削除になります。 lpszDriverは、ODBCドライバの名前を指定します。 lpszAttributesは、データソースに設定する属性を含んだ文字列を指定します。

データソースにどのような属性を設定できるかを理解するためには、 属性の設定をダイアログ経由で行うと分かりやすいと思われます。 次に例を示します。

SQLConfigDataSource(hwnd, ODBC_ADD_DSN, TEXT("MySQL ODBC 5.1 Driver"), NULL);

第1引数に有効なウインドウハンドルを指定した場合は、ダイアログ経由で属性を設定することになるため、 第4引数にはNULLを指定することができます。 第3引数に指定した名前のODBCドライバが実際に存在する場合は、 そのドライバ固有のダイアログが表示されることになります。

このダイアログは、MySQLのODBCドライバによって表示されたものです。 「Data Source Name」には、データソースに設定するDSNを指定します。 このDSNを次節で取り上げるSQLConnectに指定した場合、「Database」に設定したデータベースへのアクセスに対して、 「User」で設定したユーザーが使用されることになります。 「Test」というボタンを押せば、接続が正常に行われるかをテストすることができます。 ここで失敗する場合は、ユーザーやデータベースが作成されていない場合や、 パスワードが不正である場合が考えられます。 先のSQLConfigDataSourceでは第2引数にODBC_ADD_DSNを指定していたため、 OKボタンを押すとsample_dsnという名前を持った新しいデータソースが追加されることになります。

追加されたデータソースは、管理ツールの「データソース ODBC」から確認することができます。

このダイアログを使用すれば、SQLConfigDataSourceを呼び出さなくても、 「追加」ボタンからデータソースを作成することができます。 また、目的のDSNを選択して「構成」ボタンを押せば、 そのデータソースの属性を変更することができます。 このダイアログをアプリケーションから表示したい場合は、 有効なウインドウハンドルを指定してSQLManageDataSourcesを呼び出します。

追加されたデータソースの情報は、次のレジストリキー以下に格納されています。

HKEY_CURRENT_USER\Software\ODBC\ODBC.INI

レジストリエディタで確認すると次のようになっています。

DATABASEやUIDなどの文字列が、データソースの属性を識別する名前になります。 SQLConfigDataSourceの第4引数には、この名前と値を=で結んだ文字列を指定することができます。

SQLConfigDataSource(NULL, ODBC_CONFIG_DSN, TEXT("MySQL ODBC 5.1 Driver"), TEXT("DSN=sample_dsn;PWD=pass"));

DSNという属性名には、データソースの名前を指定します。 これは必ず指定しなければなりません。 この後の;は、次の属性を指定するという意味であり、各属性は;で区切られることになります。 上記のSQLConfigDataSourceでは、第2引数にODBC_CONFIG_DSNを指定しているため、 関数が成功すればPWDの値がpassに設定されます。 第1引数にウインドウハンドルを指定した場合は、 第4引数に指定した値がダイアログの既定値になります。

今回のプログラムは、データソースを作成する例を示しています。 ODBCドライバは、MySQLを想定しています。

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

BOOL CreateDatabase(void);

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	if (!CreateDatabase()) {
		MessageBox(NULL, TEXT("データベースの作成に失敗しました。"), NULL, MB_ICONWARNING);
		return 0;
	}
	
	SQLConfigDataSource(GetDesktopWindow(), ODBC_ADD_DSN, TEXT("MySQL ODBC 5.1 Driver"), NULL);

	return 0;
}

BOOL CreateDatabase(void)
{
	SQLHENV     henv;
	SQLHDBC     hdbc;
	SQLHSTMT    hstmt;
	SQLRETURN   nResult;
	SQLSMALLINT nSize;
	TCHAR       szState[6];
	TCHAR       szErrorMsg[1024];
	TCHAR       szConnectionOut[1024];
	SQLINTEGER  nErrorCode;

	nResult = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
	if (nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO)
		return FALSE;
	
	nResult = SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void *)SQL_OV_ODBC3, 0);
	if (nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) {
		SQLFreeHandle(SQL_HANDLE_ENV, henv);
		return FALSE;
	}
	
	nResult = SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
	if (nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) {
		SQLFreeHandle(SQL_HANDLE_ENV, henv);
		return FALSE;
	}
	
	nResult = SQLDriverConnect(hdbc, NULL, (SQLTCHAR *)TEXT("Driver={MySQL ODBC 5.1 Driver};UID=root"), SQL_NTS, (SQLTCHAR *)szConnectionOut, sizeof(szConnectionOut) / sizeof(TCHAR), &nSize, SQL_DRIVER_NOPROMPT);
	if (nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) {
		SQLGetDiagRec(SQL_HANDLE_DBC, hdbc, 1, (SQLTCHAR *)szState, &nErrorCode, (SQLTCHAR *)szErrorMsg, sizeof(szErrorMsg), &nSize);
		MessageBox(NULL, szErrorMsg, NULL, MB_ICONWARNING);
		SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
		SQLFreeHandle(SQL_HANDLE_ENV, henv);
		return FALSE;
	}
	
	nResult = SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
	if (nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) {
		SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
		SQLFreeHandle(SQL_HANDLE_ENV, henv);
		return FALSE;
	}
	
	nResult = SQLExecDirect(hstmt, (SQLTCHAR *)TEXT("CREATE DATABASE IF NOT EXISTS sample_db"), SQL_NTS);
	if (nResult != SQL_SUCCESS && nResult != SQL_SUCCESS_WITH_INFO) {
		SQLGetDiagRec(SQL_HANDLE_STMT, hstmt, 1, (SQLTCHAR *)szState, &nErrorCode, (SQLTCHAR *)szErrorMsg, sizeof(szErrorMsg), &nSize);
		MessageBox(NULL, szErrorMsg, NULL, MB_ICONWARNING);
		SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
		SQLDisconnect(hdbc);
		SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
		SQLFreeHandle(SQL_HANDLE_ENV, henv);
		return FALSE;
	}

	SQLFreeHandle(SQL_HANDLE_STMT, hstmt);
	SQLDisconnect(hdbc);
	SQLFreeHandle(SQL_HANDLE_DBC, hdbc);
	SQLFreeHandle(SQL_HANDLE_ENV, henv);

	return TRUE;
}

データソースを作成するためには、データベースが必要になりますから、 CreateDatabaseという自作関数で予め作成するようにしています。 ただし、既存のデータベースを使用する場合は、 CreateDatabaseの呼び出しはコメントアウトすることになります。 このプログラムはウインドウを作成していないため、 SQLConfigDataSourceの第1引数にはGetDesktopWindowの戻り値を指定します。

CreateDatabaseの中で使用されている関数については、後の節で取り上げられていますが、 いくつか重要な点を補足します。 まず、ドライバへの接続関数として、SQLConnectではなくSQLDriverConnectを呼び出しているのが重要です。 SQLConnectはDSNを要求するため、データソースを作成していない現時点では、この関数を呼び出すわけにはいきません。 rootユーザーにパスワードが設定されている場合は、第4引数の文字列にPWD=でパスワードを指定します。 データベースの作成は、SQLExecDirectで行われています。 この第2引数に指定しているsample_dbを変更すれば、任意の名前のデータベースを作成できるようになります。 データベースを削除する場合は、DROP DATABASE sample_dbを指定します。

プロファイル関数について

SQLConfigDataSourceにODBC_CONFIG_DSNを指定すれば、 データソースの属性を変更することができますが、 これはSQLWritePrivateProfileStringでも可能です。 次に例を示します。

SQLWritePrivateProfileString(TEXT("sample_dsn"), TEXT("PWD"), TEXT("pass"), TEXT("ODBC.INI"));

第1引数にはデータソースの名前を指定し、第2引数には属性名を指定します。 第3引数は、第2引数の属性に設定したい値を指定します。 第4引数は、ODBC.INIを指定します。

データソースの属性の値を取得したい場合は、SQLGetPrivateProfileStringを呼び出します。

TCHAR szBuf[256];

SQLGetPrivateProfileString(TEXT("sample_dsn"), TEXT("UID"), TEXT(""), szBuf, sizeof(szBuf) / sizeof(TCHAR), TEXT("ODBC.INI"));
MessageBox(NULL, szBuf, TEXT("UID"), MB_OK);

第1引数にはデータソースの名前を指定し、第2引数には属性名を指定します。 第3引数は、第2引数に指定した属性が存在しなかった場合に、 第4引数に格納する文字列を指定します。 第4引数は、属性の値を受け取るバッファを指定し、 第5引数はバッファのサイズ、第6引数はODBC.INIを指定します。



戻る