Windows環境にてC言語やC++でODBCを使うには2

Windows環境にてC言語やC++でODBCを使うには1
http://sato-si.at.webry.info/200503/article_11.html
の続きです。

実際のC言語/C++でのプログラムのコーディングからです。

C:\test.xls を作成し,中身に次のような簡単な表を作成し,この表を印刷範囲に設定します。


+------+------+
| 名前 | 年齢 |
+------+------+
| やま |    27|
+------+------+
| たま |     2|
+------+------+
| くろ |     4|
+------+------+


※これはコンソールアプリケーションとして作成していますが,
通常のWindowsアプリケーションでも同じです。

// ODBCで必要な include 文
#include <windows.h>
#include <sql.h>
#include <sqlext.h>

// printf 使うので,これもinclude
#include <stdio.h>

// ODBCで必要なライブラリ(インポートライブラリ)
#pragma comment(lib, "odbc32.lib")

int main()
{
  //ODBCで最低限必要なハンドル
  HENV henv;    // ODBC環境のハンドル
  HDBC hdbc;    // ODBC接続中のハンドル
  HSTMT hstmt;    // SQL文実行中のハンドル(クエリ実行や結果のテーブルの値取得など)
  RETCODE rc;    // 関数の処理結果(戻り値)を保持するための変数

  // ODBCドライバ接続用の文字列 (ODBCで開くエクセルファイルも指定できる)
  TCHAR *ucConnectStr = "Driver={Microsoft Excel Driver (*.xls)};dbq=C:\\test.xls";

  // ODBCドライバ接続用の文字列の完全版の取得用文字列 と その長さ
  TCHAR ucOutConnectStr[1024];  // 最低でも1024バイト必要です
  SQLSMALLINT pcbConOut = 0;

  // SQL文実行時に利用する文
  UCHAR select[] = "SELECT * FROM [Sheet1$Print_Area]";
   // [シート名$範囲・範囲名] (エクセルの印刷範囲をテーブルとみなす)

// カラム数取得時に使う変数
  SQLSMALLINT nresultcols = 0, i; // カラム数と,forループ変数

// カラム詳細情報取得時に使う変数
  SQLCHAR colname[128]; // カラム名
  SQLSMALLINT colnamelen = 0; // カラム名の文字数
  SQLSMALLINT coltype = 0; // データの型
  SQLUINTEGER collen; // このカラムの領域サイズ(バイト単位)
  SQLSMALLINT scale = 0; // たとえば,NUMERIC(10,3)なら, 3が入る。使われないことも多い
  SQLSMALLINT nullable = 0; // NULLが可能かどうか

  // SQL結果取得用のバッファ
  UCHAR data[30][256]; // データ格納用
  SQLINTEGER datastrlen[30]; // 帰ってきた文字列長

  // 初期化処理
  SQLAllocEnv(&henv);    // ODBC環境のためのメモリ(作業領域)確保
  SQLAllocConnect(henv, &hdbc);    // ODBC接続中状態のメモリ(作業領域)確保
  rc = SQLDriverConnect(hdbc, NULL, (SQLTCHAR*)ucConnectStr, lstrlen(ucConnectStr), (SQLTCHAR*)ucOutConnectStr, sizeof(ucOutConnectStr), &pcbConOut, SQL_DRIVER_NOPROMPT ); // パラメータを文字列で指定してODBCドライバに接続する

  // ODBC接続時に使った実際の文字列を出力
  ucOutConnectStr[pcbConOut] = '\0';
  printf("%s\n",ucOutConnectStr);

  // もし,接続に失敗したら,終了する。
  if (rc != SQL_SUCCESS && rc != SQL_SUCCESS_WITH_INFO) {
    SQLDisconnect(hdbc);  // ODBC接続を切断
    SQLFreeConnect(hdbc);  // ODBC接続状態のメモリを開放
    SQLFreeEnv(henv);  // ODBC環境のメモリを開放
    return -1;  // プログラム終了(main関数から出る)
  }

  SQLAllocStmt(hdbc, &hstmt);  // ステートメント開始

  // ここから,hstmt を使ってSQLが使えます♪

  // SQL文を実行
  if (SQLExecDirect(hstmt, select, SQL_NTS) != SQL_SUCCESS) {
    SQLFreeStmt(hstmt, SQL_DROP); // SQL文の実行の後始末,メモリの開放
    SQLDisconnect(hdbc);  // ODBC接続を切断
    SQLFreeConnect(hdbc);  // ODBC接続状態のメモリを開放
    SQLFreeEnv(henv);  // ODBC環境のメモリを開放
    return -2;
  }

  // SQL文の結果のテーブルをとりたいときは以下の文を使います。(いらない場合は使いません。)

  // SQL文の結果のテーブルのカラム(項目)数をnresultcolsに取得
  SQLNumResultCols(hstmt, &nresultcols);

  for (i = 0; i < nresultcols; i++) {
    SQLDescribeCol(hstmt, (UWORD)(i + 1), colname,
      (SWORD)sizeof(colname),
      &colnamelen, &coltype, &collen, &scale,
      &nullable);

    printf("%s\t", colname);

  // フィールドごとにと変数を関連付けする
    SQLBindCol(hstmt, (UWORD)(i + 1), SQL_C_CHAR, data[i],
      sizeof(data[0]), &datastrlen[i]);
  }
  printf("\n");

// データの取り込み(1ループ1レコード
  while (TRUE) {
    rc = SQLFetch(hstmt);
    if (rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO) {
      for (i = 0; i < nresultcols; i++) {
        if (datastrlen[i] == SQL_NULL_DATA) {
          printf("null\t");
        } else {
          data[i][datastrlen[i]] = '\0';
          printf("%s\t", data[i]);
        }
      }
    } else {
      break;
    }
    printf("\n");
  }

  // 後始末
  SQLFreeStmt(hstmt, SQL_DROP); // SQL文の実行の後始末,メモリの開放
  SQLDisconnect(hdbc);  // ODBC接続を切断
  SQLFreeConnect(hdbc);  // ODBC接続状態のメモリを開放
  SQLFreeEnv(henv);  // ODBC環境のメモリを開放

  return 0;
}


以上のプログラムを正しく実行できた場合は,


DBQ=C:\test.xls;Driver={Microsoft Excel Driver (*.xls)};DriverId=790;MaxBufferSi
ze=2048;PageTimeout=5;
名前 年齢
やま 27.0
たま 2.0
くろ 4.0



といった感じの結果が出てきます。

これで,とりあえず,ODBCがつかえると思います。

エクセルODBC応用

例1 シート全体を1つのテーブルとするなら,
UCHAR select[] = "SELECT * FROM [Sheet1$]";

例2 一部のセルを選択 Sheet1のA3からC30の範囲を選ぶなrら
UCHAR select[] = "SELECT * FROM [Sheet1$A3:C30]";

例3 セルに名前を定義していると,それをテーブル名として指定できます。
たとえば,領域を「集計表」として,定義しておくと,
UCHAR select[] = "SELECT * FROM [集計表]";
という風にもできます。
ちなみに,この名前の作り方は,Excelのメニューの「挿入(I)」 - 「名前(N)」-「定義(D)...」でできます。

実際のプログラムでは,SQL文の結果テーブルのデータ取得の方法を
もう少し工夫したほうがよいでしょう。それは,次回の
Windows環境にてC言語やC++でODBCを使うには3
http://sato-si.at.webry.info/200601/article_5.html
で説明します。

この記事へのコメント

くまぱんだ
2007年01月20日 09:35
質問してもよろしいでしょうか。

+------+------+
| 名前 | 年齢 |
+------+------+
| やま | 27|
+------+------+
| たろ | 2|
+------+------+
| くろ | 4|
+------+------+
で「たろ」の代わりに、
http://www.thaismile.jp/ThaiLanguage/4/3kihon.html
にあるようなタイ語の名前を入れてみたところ、
「?????????」と出力されました。
文字化けしないようにするには
どうしたらいいでしょうか。

お願いします。

さとーし
2007年01月22日 15:51
日本語以外も使えるようにするには,UNICODE版でプログラムを作成しなければなりません。
プログラムのプロジェクトの設定をUNICODEにします。(開発環境ごとに異なります。。。)

以下のincludeを追加します
#include

char は全て wchar_t に書き換えてやる必要があります。(ちなみに,TCHAR型はcharとwchar_tが,設定に応じて自動でかわります。)

printf を wprintfへ
文字列の頭に全て Lをつける
つまり,
printf("null\t");

wprintf(L"null\t");
といった感じにします。すべてです。


さらに,コマンドブロンプトに使われているフォントが,日本語専用のShift-JISしか対応していないため,正しく出ない可能性があります。

以上を確認してください。
くまぱんだ
2007年01月26日 18:16
unicodeでファイルに出力して確かめましたが、まだ文字が化けてしまいます。

プロジェクトをunicode対応に、
_wsetlocale( LC_ALL, L"Japanese" );を付けて、
printf→wprintf、
SQLCHAR→SQLWCHAR、
%s→%S
などなどしてみたのですが。うーん。。。

ODBC接続って文字コード変換してるのでしょうか?

実行結果

DBQ=C:\test.xls;Driver={Microsoft Excel Driver (*.xls)};DriverId=790;MaxBufferSize=2048;PageTimeout=5;
名前 年齢
やま 27.0
?????????????????????? 2.0
くろ 4.0

この記事へのトラックバック

  • Windows環境にてC言語やC++でODBCを使うには1

    Excerpt: ODBCとは Open DataBase Connectのことで,データベースに接続する オープンな規格のこと。 Weblog: さとーC++ぶろぐ@WebryBlog racked: 2005-05-06 15:34
  • 「エクセル」でブログ検索してみました。

    Excerpt: 「 エクセル 」の検索結果で、「さとーC ぶろぐ .. 」さんを紹介させていただきました。つながり系サイトです。 Weblog: 日刊カタログ racked: 2005-05-07 03:54
  • 「Excel」でブログ検索してみました。

    Excerpt: 「 Excel 」の検索結果で、「さとーC ぶろぐ .. 」さんを紹介させていただきました。つながり系サイトです。 Weblog: 日刊カタログ racked: 2005-05-10 12:18
  • Excel ODBC でCREATE TABLEするときに使えるデータ型

    Excerpt: Excel ODBC でCREATE TABLEするときに使えるデータ型 Weblog: さとーC++ぶろぐ racked: 2006-01-23 15:31
  • Windows環境にてC言語やC++でODBCを使うには3

    Excerpt: Windows環境にてC言語やC++でODBCを使うには2 http://sato-si.at.webry.info/200505/article_1.html の続き。 Weblog: さとーC++ぶろぐ racked: 2006-01-23 15:56
  • ODBC

    Excerpt: ODBC C++で使うには? http://sato-si.at.webry.info/200505/article_1.html SQLite ODBC Driver http://www.ch.. Weblog: PukiWiki Plus! (PukiWiki/TrackBack 0.3) racked: 2007-06-03 17:18
  • ODBC

    Excerpt: Open DataBase Connectivity Microsoft Open Database Connectivity (ODBC) DB2 for iSeries SQL 呼び出しレベル・イ.. Weblog: assari (PukiWiki/TrackBack 0.3) racked: 2008-06-18 10:34
  • :log

    Excerpt: ODBC, 日本語 Postgres 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 .. Weblog: assari (PukiWiki/TrackBack 0.3) racked: 2008-09-24 20:47