はじめに
前回のアーティクル「SQLiteで組み込みDB体験(2007年版)」は、
SQLiteはお手軽軽量なDBとしてさまざまなアプリケーションに使えるものと思います。このお手軽さを享受できるのがC/C++だけではもったいない。C++/CLIで薄いラッパーを被せれば、C#やVB.NETから使えるようにできるはずですが、そのお話はまたいずれ。
……という、なんとも思わせぶりな「まとめ」で締めくくってました、このまま放っておくわけにもいきませんよね。
そんなわけで続編、C++/CLIでSQLiteに薄いラッパーを被せ、C#から呼び出す試みです。基本的にどんなC/C++コードでもC++/CLIでラッパーを被せることで.NET化できるはず。ネタに用いたSQLiteはC++/CLIによるラッパーの1つのサンプルと考えてください。
対象読者
- C/C++で書かれたコードをなんとかC#/VBでも使いたい方
必要環境
- Visual C++ 2005 Express Edition もしくは Visual Studio 2005
- SQLite version 3.3.17
アフター・ケアとDLLのつくりかた 1
さて、いきなりお詫びをしなくてはなりません。前回公開直前にSQLiteのバージョンが3.3.16から3.3.17に上がってました。あまりのことに修正が間に合わず旧版のままで公開することになりました。
お詫びも兼ねて、より簡単に日本語サポートのための関数を追加する手順、およびDLLの作成手順を紹介しておきます。
- SQLite home pageのdownloadから「sqlite-source-3_3_17.zip」と「sqlitedll-3_3_17.zip」をダウンロードします。
- 次にVC8EEのIDEを起動し、ソリューション「SQLite3」に、スタティック・ライブラリ・プロジェクト「sqlite3」を作成します。
- ダウンロードしたsqlite-source-3_3_17.zipとsqlitedll-3_3_17.zipを展開し、「sqlite3.h」「sqlite3.c」「sqlite3.def」を[プロジェクト]-[ディレクトリ]-[...\SQLite3\sqlite3]に配置します。
- さらに以下の3つ、「legacy16.h」「legacy16.c」「legacy16.bat」も[...\SQLite3\sqlite3]に置いてください。
#ifndef LEGACY16_H__ #define LEGACY16_H__ #ifdef __cplusplus extern "C" #endif typedef int (*sqlite3_callback16)(void*,int,__wchar_t**, __wchar_t**); int sqlite3_exec16( sqlite3 *db, /* The database on which the SQL executes */ const __wchar_t *zSql, /* The SQL to be executed */ sqlite3_callback16 xCallback, /* Invoke this callback routine */ void *pArg, /* First argument to xCallback() */ __wchar_t **pzErrMsg /* Write error messages here */ ); #ifdef __cplusplus } #endif #endif
typedef int (*sqlite3_callback16)(void*,int,__wchar_t**, __wchar_t**); int sqlite3_exec16( sqlite3 *db, /* The database on which the SQL executes */ const __wchar_t *zSql, /* The SQL to be executed */ sqlite3_callback16 xCallback, /* Invoke this callback routine */ void *pArg, /* First argument to xCallback() */ __wchar_t **pzErrMsg /* Write error messages here */ ){ int rc = SQLITE_OK; const __wchar_t *zLeftover; sqlite3_stmt *pStmt = 0; __wchar_t **azCols = 0; int nRetry = 0; int nChange = 0; int nCallback; if( zSql==0 ) return SQLITE_OK; while( (rc==SQLITE_OK || (rc==SQLITE_SCHEMA && (++nRetry)<2)) && zSql[0] ){ int nCol; __wchar_t **azVals = 0; pStmt = 0; rc = sqlite3_prepare16(db, zSql, -1, &pStmt, &zLeftover); assert( rc==SQLITE_OK || pStmt==0 ); if( rc!=SQLITE_OK ){ continue; } if( !pStmt ){ /* this happens for a comment or white-space */ zSql = zLeftover; continue; } db->nChange += nChange; nCallback = 0; nCol = sqlite3_column_count(pStmt); azCols = sqliteMalloc(2*nCol*sizeof(const __wchar_t *) + 1); if( azCols==0 ){ goto exec_out; } while( 1 ){ int i; rc = sqlite3_step(pStmt); /* Invoke the callback function if required */ if( xCallback && (SQLITE_ROW==rc || (SQLITE_DONE==rc && !nCallback && db->flags&SQLITE_NullCallback)) ){ if( 0==nCallback ){ for(i=0; i<nCol; i++){ azCols[i] = (__wchar_t*)sqlite3_column_name16(pStmt, i); } nCallback++; } if( rc==SQLITE_ROW ){ azVals = &azCols[nCol]; for(i=0; i<nCol; i++){ azVals[i] = (__wchar_t*)sqlite3_column_text16(pStmt, i); } } if( xCallback(pArg, nCol, azVals, azCols) ){ rc = SQLITE_ABORT; goto exec_out; } } if( rc!=SQLITE_ROW ){ rc = sqlite3_finalize(pStmt); pStmt = 0; if( db->pVdbe==0 ){ nChange = db->nChange; } if( rc!=SQLITE_SCHEMA ){ nRetry = 0; zSql = zLeftover; while( iswspace((wint_t)zSql[0]) ) zSql++; } break; } } sqliteFree(azCols); azCols = 0; } exec_out: if( pStmt ) sqlite3_finalize(pStmt); if( azCols ) sqliteFree(azCols); rc = sqlite3ApiExit(0, rc); if( rc!=SQLITE_OK && rc==sqlite3_errcode(db) && pzErrMsg ){ *pzErrMsg = malloc( (wcslen((const __wchar_t*)sqlite3_errmsg16(db)) +1) *sizeof(__wchar_t)); if( *pzErrMsg ){ wcscpy(*pzErrMsg, sqlite3_errmsg16(db)); } }else if( pzErrMsg ){ *pzErrMsg = 0; } return rc; }
md original move sqlite3.h original\sqlite3.h copy original\sqlite3.h+legacy16.h sqlite3.h move sqlite3.c original\sqlite3.c copy original\sqlite3.c+legacy16.c sqlite3.c copy sqlite3.def original\sqlite3.def echo sqlite3_exec16 >> sqlite3.def