C言語でデータベースに接続するプログラムを組んでます。
サンプルプログラムは以下に示します。

サーバ:Windows Server 2008 R2 + SQL Server 2008 R2
クライアント:Windows 7(64bit) + Visual C++ 2010 + SQL Server Native Client(ODBC Driver)

Visual Studioでプロジェクトの言語設定は「マルチバイト文字セットを使用」に設定。
データベースのテーブルは、カラムをUnicode型(nvarchar)に定義。
オンラインマニュアルだと文字化けしないようアプリケーション側で注意、と書いてあるんですが。
char型変数を用意してそこにデータを取得すると、自動でS-JISに変換されている。

…なんで?! SQL ServerのマニュアルのどこにもUnicodeとS-JISを自動変換するとは書かれてないけど。
もしかして、VC++が自動変換してる?
やはりODBCドライバが自動変換しているっぽい。
変数の型にあわせて数値や文字列を自動で変換するとは書いてあるけど、UnicodeとS-JISの変換もここに含まれるのだろうか……
#include <Windows.h>
#include <tchar.h>
#include <stdio.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>

int main(void)
{
  SQLHENV henv;
  SQLHDBC hdbc;
  HSTMT hstmt;
  RETCODE rc;
  SQLSMALLINT nresultcols = 0, i;
  UCHAR colname[128];
  SQLSMALLINT coltype = 0;
  char data[256];
  SQLLEN datastrlen;

  // ODBCドライバ接続用の文字列
  char *ucConnectStr = "DRIVER=SQL Server Native Client 10.0;SERVER=192.168.1.1;UID=sa;PWD=password;DATABASE=sampleDB;PORT=1433;";
  // SQL文
  SQLCHAR select[] = "SELECT * FROM test_table;";

  // 環境ハンドルを確保する
  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &henv);
  // バージョンを設定 (ODBC3)
  SQLSetEnvAttr(henv, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
  // 接続ハンドルの確保
  SQLAllocHandle(SQL_HANDLE_DBC, henv, &hdbc);
  // パラメータを文字列で指定してODBCドライバに接続する
  rc = SQLDriverConnect(hdbc, NULL, (SQLTCHAR FAR*)ucConnectStr, SQL_NTS, NULL, 0, NULL, SQL_DRIVER_NOPROMPT);
  // 接続に失敗なら終了する。
  if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
    SQLDisconnect(hdbc);SQLFreeConnect(hdbc);SQLFreeEnv(henv);
    return -1;
  }

  // ステートメント開始
  SQLAllocHandle(SQL_HANDLE_STMT, hdbc, &hstmt);
  // SQL文を実行
  if (SQLExecDirect(hstmt, select, SQL_NTS) != SQL_SUCCESS) {
    SQLFreeStmt(hstmt, SQL_DROP);SQLDisconnect(hdbc);SQLFreeConnect(hdbc);SQLFreeEnv(henv);
    return -2;
  }

  // SQL文実行結果のテーブルカラム数を取得
  SQLNumResultCols(hstmt, &nresultcols);
  // データの取り込み(1ループ1レコード)
  while (TRUE) {
    // 1レコード取得
    rc = SQLFetch(hstmt);
    if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
      // データの取り込み(1ループ1カラム)
      for (i = 0; i < nresultcols; i++) {
        // カラムデータの長さを取得
        // Unicode型カラムの場合、半角全角の区別なく1文字として取得される。
        // Native ClientでなくOSデフォルトのドライバ(SQLSRV32.DLL)を使うとS-JIS変換後のバイト数が取れる
        rc = SQLGetData(hstmt, i + 1, SQL_C_DEFAULT, data, 0, &datastrlen);
        if(rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
          printf("len=%d", datastrlen);
        }
        // カラムの詳細情報を取得
        SQLDescribeCol(hstmt, i + 1, colname, 0, NULL, &coltype, NULL, NULL, NULL);
        // UnicodeがS-JISに自動変換されることを考慮し、長さを2倍にする
        if(coltype == SQL_WCHAR || coltype == SQL_WVARCHAR || coltype == SQL_WLONGVARCHAR) {
          datastrlen = datastrlen * 2 + 1;
        } else {
          datastrlen += 1;  // NULLストッパーのため+1
        }
        // カラムデータを取得
        rc = SQLGetData(hstmt, i + 1, SQL_C_DEFAULT, data, datastrlen, NULL);
        if(rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
          printf("data=%s
", data);
        }
      }
    } else {
      break;
    }
  }

  // 後始末
  SQLFreeStmt(hstmt, SQL_DROP);SQLDisconnect(hdbc);SQLFreeConnect(hdbc);SQLFreeEnv(henv);
  return 0;
}

お気に入り日記の更新

この日記について

日記内を検索