四方山話 ( 2011/01/01 - 2011/01/31 )


このページは、私が思いついた時に(だから毎日ではない)、思 いついたことを(だからコンピュータだけの話ではない)、思いついたなりに (だから日本語むちゃくちゃ)徒然と書き綴るページです。



2011/01/29 (Sat)
うまく動かんなぁ...というわけで、前回の続き。複数のフィールドに値 を指定してレコードを追加する方法についての調査中なわけだが、MSDN を見 て AddNew メソッドに配列を渡してやればいいらしいことだけはわかってい る。で、まあそれらしきプログラムはできたのだが、どーしても納得がいかな いことがあり、こちら方面は追加調査を実施中。というわけで、少しだけ横道 にそれ、配列を渡す以外に方法がないか、ざっと調べてみた。まずは再度 MSDN で AddNew メソッドのページを読んでみると、前回の記載が不十分だという ことに気が付いた。つまり、第一引数は「(0)省略、(1)フ ィールド名、(2)フィールド名の配列、(3)位置を表す値」のいずれかを指定、 第二引数は「(0)省略、(1)フィールド値、(2)フィールド値 の配列」のいずれかを指定するらしい。ということで、レコードを追加、各フ ィールドに値を設定、更新するというプログラムを作ってみた。
サンプルプログラムNo.d04
 1: #import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
 2:         no_namespace rename( "EOF", "EndOfFile" )
 3: #define  UNICODE
 4: #define  _UNICODE
 5: #include <ole2.h>
 6: #include <Objbase.h>
 7:
 8: int wmain( int argc, wchar_t *argv[] )
 9: {
10:    if ( FAILED(::CoInitialize(NULL)) ) return 1;
11:
12:    _ConnectionPtr pCnn( L"ADODB.Connection" );
13:    _RecordsetPtr  pRst( L"ADODB.Recordset" );
14:
15:    try {
16:       pCnn->Open( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\home\\data.mdb;",
17:                   L"", L"", adConnectUnspecified );
18:       pRst->Open( L"data2", _variant_t((IDispatch *) pCnn, true),
19:                   adOpenStatic, adLockOptimistic, adCmdUnspecified );
20:       // 追加例(1)
21:       pRst->AddNew();
22:       pRst->Fields->GetItem( L"ID" )->Value = 10;
23:       pRst->Fields->GetItem( L"field1" )->Value = 20;
24:       pRst->Update();
25:       // 追加例(2)
26:       pRst->AddNew();
27:       pRst->Fields->Item[ L"ID" ]->Value = 30;
28:       pRst->Fields->Item[ L"field1" ]->Value = 40;
29:       pRst->Update();
30:       // 追加例(3)
31:       _variant_t vIndex;
32:       pRst->AddNew();
33:       vIndex = ( long )0;
34:       pRst->Fields->Item[ &vIndex ]->Value = 50;
35:       vIndex = ( long )1;
36:       pRst->Fields->Item[ &vIndex ]->Value = 60;
37:       pRst->Update();
38:       // 追加例(4)
39:       pRst->AddNew();
40:       pRst->Fields->Item[ (long)0 ]->Value = 70;
41:       pRst->Fields->Item[ (long)1 ]->Value = 80;
42:       pRst->Update();
43:    } catch( _com_error &e ) {
44:       printf( "Error: %s\n", e.ErrorMessage() );
45:    }
46:    if( pRst )
47:       if( pRst->State == adStateOpen )
48:          pRst->Close();
49:    ::CoUninitialize();
50:    return 0;
51: }

フィールドに値を設定する方法は何通りかあるみたいなので、MSDN やググ った結果を参考にして、とりあえず今回は四通りの方法で作成、したがって四 つのレコードを追加している。折角なので、実行結 果 [ PNG file, 5KB ]。例(1)と例(2)はフィールドを文字列(フィールド 名)で指定、例(3)と例(4)では数字で指定している。ちなみに例(3)で「 vIndex = 0;」とすると、実行時にエラー(0x800A0CC1)となる。同じく例(4)で 「Item[0]」としても、実行時にエラー(0x800A0CC1)となる。どちらも上記例の ように long へのキャストを明示してやれば問題はない。で、ふと思って、以 下のように改造して実験してみた。

30:       // 追加例(3b)
31:       long LIndex;
32:       pRst->AddNew();
33:       LIndex = 0;
34:       pRst->Fields->Item[LIndex]->Value = 50;
35:       LIndex = 1;
36:       pRst->Fields->Item[LIndex]->Value = 60;
37:       pRst->Update();

例(3b)でも問題なし。で、「AddNew → フィールド値設定 → Update」する 方法だと、DB で「値要求:はい」としていると、もしかするとエラーになるの ではないかと思い試してみた。MS-Access で、フィールド「ID」の値要求を「 はい」して上記プログラムを動かしてみたが...特にエラーもなく、うまく動い ている様子。となると、特段問題もない気がして、これでいいんじゃないかと... (→配列編

(余談)MSDN の「 Visual C++ での ADO プログラミング」のページ。日本語なので英語版「 Visual C++ ADO Programming」に比べれば分かりやすい(読みやすい)... と思いきや、意味がよくわからない表現が多く、結局、英語版を読んだほうが 分かりやすい部分もある。あと、サンプルとして「Item[2]」と書いてあったの で、それを信じてかなり時間を無駄にした。で、英語版では「Item[long(2)]」 とあったので、試しに書いてみたら問題なく動く。まあ確かに日本語版もページ の下のほうの「GetItem(x) と Item[x] の使用」に書かれている例では「 Item[(long)2]」と書かれているんですが...結局のところ、慌てずよくドキュメ ントを読めということでしょうか。

2011/01/25 (Tue)
こんな雑駁なまとめであっても考えをまとめるという点では非常に役に立 つ。そんなわけで数多くある課題の中の一つを解くカギである AddNew メソッドについてもう少し詳しめに。MSDN によれば第一引数は「(1)フィ ールド名、(2)フィールド名の配列、(3)位置を表す値」のいずれかを指定、第 二引数は「(1)フィールド値、(2)フィールド値の配列」のいずれかを指定する らしい。先のプログラム sampleD3.cpp は _variant_t class のコンストラク タで値を指定して、フィールド名 ID が 62 というレコードを追加しているが、 これは以下のような書き方でもいいみたい。(というか、こっちの書き方のほ うが現実的だと思うけど...)
21:       _variant_t vField, vData;
22:       vField = L"ID";
23:       vData = 62;

これがパターン(1)の「フィールド名とフィールド値」を指定した場合。で、 パターン(2)の「フィールド名の配列、フィールド値の配列」はとりあえずとば して、パターン(3)の「位置を表す値とフィールド値」について、上記の例から 以下のようにすればいいのではないかと推定。さて、うまくいくかな?

21:       _variant_t vField, vData;
22:       vField = 0;
23:       vData = 62;
コンパイル結果および実行結果
[D:\home] cl /EHsc sampleD3.cpp
((省略))
[D:\home] sampleD3.exe
Error: Unknown error 0x800A0CC1

[D:\home] 

コンパイルはうまくいくが、実行時にエラーが発生。ちなみに ADO のエラー番号 0x800A0CC1 は「adErrItemNotFound」(要求された名 前、または序数に対応する項目がコレクションで見つかりません)ということ らしい。うーむ、MSDN に騙されたのか?と思いつつ、_variant_t は VARIANT型のカプセル化したものだから、という理由で VARIANT型につい て調べてみた。VARIANT型は様々なデータ型のデータを入れられるようにした もので、メンバ vt には以下に示すデータ型を表す定数?を指定します。折角 なので、MSDN に書いてあるものを全部書き出してみました。説明は省略。ま、 名前から大体の想像はつくからいいよね。

VT_EMPTY VT_EMPTY | VT_BYREF VT_UI1 VT_UI1 | VT_BYREF
VT_UI2 VT_UI2 | VT_BYREF VT_UI4 VT_UI4 | VT_BYREF
VT_UI8 VT_UI8 | VT_BYREF VT_UINT VT_UINT | VT_BYREF
VT_INT VT_INT | VT_BYREF VT_I1 VT_I1 | VT_BYREF
VT_I2 VT_I2 | VT_BYREF VT_I4 VT_I4 | VT_BYREF
VT_I8 VT_I8 | VT_BYREF VT_R4 VT_R4 | VT_BYREF
VT_R8 VT_R8 | VT_BYREF VT_CY VT_CY | VT_BYREF
VT_BSTR VT_BSTR | VT_BYREF VT_DECIMAL VT_DECIMAL | VT_BYREF
VT_NULL VT_NULL | VT_BYREF VT_ERROR VT_ERROR | VT_BYREF
VT_BOOL VT_BOOL | VT_BYREF VT_DATE VT_DATE | VT_BYREF
VT_DISPATCHVT_DISPATCH | VT_BYREF VT_VARIANT VT_VARIANT | VT_BYREF
VT_UNKNOWN VT_UNKNOWN | VT_BYREF VT_ARRAY | <anything>

で、とりあえず実験として、なんの根拠もなく思いつきで、変数 vField のメンバ vt に VT_I4 を指定してみた。

21:       _variant_t vField, vData;
22:       vField = 0;
23:       vField.vt = VT_I4;
24:       vData = 62;
コンパイル結果および実行結果
[D:\home] cl /EHsc sampleD3.cpp
((省略))
[D:\home] sampleD3.exe

[D:\home] 

お、なんだかよくわからんけど、うまくいったっぽい(笑) で、上記の全デ ータ型について調べたわけじゃないけど、「VT_I4」以外でうまくいったのは 「VT_I2」だけで、「VT_INT」「VT_I8」「VT_UINT」「VT_UI4」では実行時エ ラー(0x800A0CC1)が発生。うーん、どういうことなのかよくわからんけど、 ま「VT_I4」を指定しておくことにしよう。では残ったパターン(2)の「フィー ルド名の配列、フィールド値の配列」について。普通に考えたら以下のような 書きっぷりで問題ないんじゃないかと思うんだけど...

21:       _variant_t vField[2], vData[2];
22:       vField[0] = L"ID";   vField[1] = L"field1";
23:       vData[0]  = 95;      vData[1]  = 32;
24:       pRst->AddNew( vField, vData );
コンパイル結果および実行結果
[D:\home] cl /EHsc sampleD3.cpp
((省略))
[D:\home] sampleD3.exe

[D:\home] 

コンパイルも問題なし、実行時エラーも問題なし、なんだけど 実行結果 [ PNG file, 4KB ] が大いに問題あ り。うーん、なんでフィールド field1 に値が設定されないのかなぁ。ちなみ に上記例の24行目を pRst->AddNew( &vField, &vData ); にしてみ たらコンパイル時には「warning C4305: '引数' : '_variant_t (*)[2]' から 'bool' へ切り詰めます。」と言われ、実行時にはエラー(0x800A0CC1)が発 生しました。うーん、どうすればうまくいくんだ...ってなわけで次回に続く。

2011/01/23 (Sun)
先日の FormatMessage関数などを使っていろいろ調査した結果、ものすご く初歩的なミスをしていることにようやく気が付いた。あまりにも初歩的なミ スすぎて、恥ずかしくて情けなくて...がっかりorz だけどがっかりしている 時間的余裕もなく、なんとか2月中には alpha 版を release したいので、気 を取り直して開発継続中。しっかしプログラムを作成するようになってから激 しく肩がこるようになった。さらに昨日は肩こりが(多分)原因の頭痛までする ようになった。こんな状態でちゃんと動くものができるのだろうか。「俺、こ のプログラムが完成したら、長期休暇を取るんだ(死亡フラグ)」ってな雰囲 気がかなり強烈に漂っているんだけど...さてさて、ここんところ寄り道に寄り 道を重ねていたけれども、ちょこっとだけ戻って、database プログラミング。 db にレコードを 1 件追加してみようと、に作ったも のを基に試行錯誤を重ねて、ようやくうまく動くプログラムが完成。ただし正 しいかどうかは不明。しかし、この書きっぷりからは全然伝わらないけど、本 当に大変だった... 鈴井 さんと大泉さんの気持ちがよくわかる(笑) 編集されて、プログラム書き始 めて、完成して終わりなんだよ、違うんだよ〜本番は書いている時なんだよ〜 エラーが出るんだよ〜動かないんだよ〜
サンプルプログラムNo.d03
 1: #import "C:\Program Files\Common Files\System\ADO\msado15.dll" \
 2:         no_namespace rename( "EOF", "EndOfFile" )
 3: #define  UNICODE
 4: #define  _UNICODE
 5: #include <ole2.h>
 6: #include <Objbase.h>
 7:
 8: int wmain( int argc, wchar_t *argv[] )
 9: {
10:    if ( FAILED(::CoInitialize(NULL)) ) return 1;
11:
12:    _ConnectionPtr pCnn( L"ADODB.Connection" );
13:    _RecordsetPtr  pRst( L"ADODB.Recordset" );
14:
15:    try {
16:       pCnn->Open( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\home\\data.mdb;",
17:                   L"", L"", adConnectUnspecified );
18:       pRst->Open( L"data2", _variant_t((IDispatch *) pCnn, true),
19:                   adOpenStatic, adLockOptimistic, adCmdUnspecified );
20:
21:       _variant_t vField( L"ID" ), vData( 62 );
22:       pRst->AddNew( vField, vData );
23:    } catch( _com_error &e ) {
24:       printf( "Error: %s\n", e.ErrorMessage() );
25:    }
26:    if( pRst )
27:       if( pRst->State == adStateOpen )
28:          pRst->Close();
29:    ::CoUninitialize();
30:    return 0;
31: }

まず12行目と13行目。先のプログラムでは pCnn.CreateInstance( __uuidof(Connection) ); などという(自分で書いておいて申し訳ないのだが)よくわからない記載をし ていましたが、こういう風にわかりやすい書き方もできるようなので (→ 参考)変更。これで ADO を使っているということが明確になるし、 以前に作成した WSH プログラムと見た 目が似てきてわかりやすいのではと思っている。18行目でテーブル data2 用 の recordset を open。第四引数を adLockReadOnly から adLockOptimistic に変更、第五引数を -1 から adCmdUnspecified に変更(ただし意味は一緒) 。で、22行目でフィールド名「ID」値「62」というレコード 1 件を追加して いる。最後に今回使用した table 「data2」の構成を以下に示す。それと 実行結果( PNG file, 9KB )。

フィールド名ID field1field2
データ型長整数型 長整数型テキスト型
値要求いいえ いいえいいえ
(余談)AddNewメソッドではまった時、一時的に SQL の INSERT 文でレ コードを追加するものを作って、まあちゃんと動いたのですが、レコードを追 加するのにいちいち文字列を作るのも面倒なので、後々のことを考えてやめま した。しかし、複数のフィールドを指定するのはどうするのかなぁ。あと、日 付データを登録しようと思ったのですが何故かうまくいかず、新たにテーブル を作って数値データの登録に途中で方針変換しております。こちらも最終的に は日付データを登録しないといけないんだけど...またこの辺で四苦八苦するこ とになるのか...(→日付データの登録方法 ;あまり四苦八苦しなかった...)

2011/01/21 (Fri)
うーん、今、開発中のプログラムが何故かうまく動かない...ま、うまく 動かないということは、どこかで何かが間違っているんだろうな。しかし、ど こがどういう風に間違っているのかがわからない。それだとどこを直せばいい のかもわからない...困ったなぁ。そんなわけでちょっと思うところがありシリ ーズ第三弾。例えば先日の test2b.cpp で、33行目の GetAddrInfoW 関数にお いてエラーが発生しているのだが、その内容については関数の返り値を printf 関数で表示し、その返り値が意味するところを Windows Sockets Error Codes のページで調べることで分かるわけです。これだと とても面倒なので、最初は switch( ans ){ } とかして自分でごりごり書こう としたのだが、それはもっと面倒なので書く前から挫折。途方に暮れて MSDN をだらだら見ていたら、なんととても便利な関数を発見した。それが FormatMessage関数 である。というわけで、兎にも角にもテストプログラ ム。
テストプログラムNo.3a
 1: #pragma comment( lib, "Ws2_32.lib" )
 2: #pragma comment( lib, "user32.lib" )
 3: #define UNICODE
 4: #define _UNICODE
 5: #include <winsock2.h>
 6: #include <Ws2tcpip.h>
 7: #include <stdio.h>
 8:
 9: int wmain( int argc, wchar_t *argv[] )
10: {
11:    int ans;
12:    addrinfoW hints, *result;
13:    WSADATA wsa;
14:
15:    ans = WSAStartup( MAKEWORD( 2, 2 ), &wsa );
16:    if( ans != 0 ){
17:       wprintf( L"WSAStartup failed: %d\n", ans );
18:       return 1;
19:    }
20:
21:    ZeroMemory( &hints, sizeof( hints ) );
22:    hints.ai_family = AF_INET;
23:    hints.ai_socktype = SOCK_STREAM;
24:    hints.ai_protocol = IPPROTO_TCP;
25:
26:    ans = GetAddrInfoW( L"none.none.none.none", L"http", &hints, &result );
27:    if( ans != 0 ){
28:       wchar_t *ErrMsg;
29:       FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
30:                      NULL, WSAGetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
31:                      (LPTSTR)&ErrMsg, 0, NULL );
32:       wprintf( L"Error : %s\n", ErrMsg );
33:       LocalFree( ErrMsg );
34:    }
35:    FreeAddrInfoW( result );
36:    WSACleanup();
37:    return 0;
38: }
コンパイル結果および実行結果
[D:\home] cl test3a.cpp
((省略))
[D:\home] test3a.exe
Error : ??????????????


[D:\home] 

FormatMessage関数の詳しいことはいつものとおり次回以降に無期延期なわ けだが、ちょこっとだけメモしておくと、第一引数に「 FORMAT_MESSAGE_ALLOCATE_BUFFER」を指定すると FormatMessage関数内でメッ セージ格納用のメモリ領域を確保してくれるらしい。つまりはこちら側で、事 前に malloc関数などを呼び出したりしなくてもいいということになる。あと 第三引数の WSAGetLastError関数は Winsock2 関係の最後のエラーコードを返 す。じゃあ Winsock2 関係以外のエラーの場合にはどうするのか?その場合は GetLastError関数(など)を使うようです。で wprintf関数は printf関数の Unicode 版。しかし、エラーメッセージが なんだか文字化けしているっぽい。うーん、なんでだろうと、これまたいろい ろ調べた結果、どうやら setlocale関数を呼び出せばいいらしい。ちなみに理 由とか理屈とかはよくわからない(笑) 暇ができたときに調べてみます。

テストプログラムNo.3b
 1: #pragma comment( lib, "Ws2_32.lib" )
 2: #pragma comment( lib, "user32.lib" )
 3: #define UNICODE
 4: #define _UNICODE
 5: #include <winsock2.h>
 6: #include <Ws2tcpip.h>
 7: #include <stdio.h>
 8: #include <locale.h>
 9:
10: int wmain( int argc, wchar_t *argv[] )
11: {
12:    int ans;
13:    addrinfoW hints, *result;
14:    WSADATA wsa;
15:
16:    setlocale( LC_ALL, "" );
17:    ans = WSAStartup( MAKEWORD( 2, 2 ), &wsa );
((省略))
コンパイル結果および実行結果
[D:\home] cl test3b.cpp
((省略))
[D:\home] test3b.exe
Error : そのようなホストは不明です。


[D:\home] 
2011/01/18 (Tue)
ちょっと思うところがありシリーズ第二弾。getaddrinfo 関数についてい ろいろ実験しようかと思ったんだけど、折角だから Unicode 版の GetAddrInfoW 関数についての実験に変更。
テストプログラムNo.2a
 1: #pragma comment( lib, "Ws2_32.lib" )
 2: #define UNICODE
 3: #define _UNICODE
 4: #include <winsock2.h>
 5: #include <Ws2tcpip.h>
 6: #include <stdio.h>
 7:
 8: int wmain( int argc, wchar_t *argv[] )
 9: {
10:     int ans;
11:     addrinfoW hints, *result;
12:
13:     ZeroMemory( &hints, sizeof( hints ) );
14:     hints.ai_family = AF_INET;
15:     hints.ai_socktype = SOCK_STREAM;
16:     hints.ai_protocol = IPPROTO_TCP;
17:
18:     ans = GetAddrInfoW( L"124.83.139.191", L"http", &hints, &result );
19:     if( ans != 0 ) printf( "Error %d\n", ans );
20:     ans = GetAddrInfoW( L"www.yahoo.co.jp", L"http", &hints, &result );
21:     if( ans != 0 ) printf( "Error %d\n", ans );
22:
23:     return 0;
24: }
コンパイル結果および実行結果
[D:\home] cl test2a.cpp
((省略))
[D:\home] test2a.exe
Error 10093
Error 10093

[D:\home] 

ありゃ、エラーだ。 Windows Sockets Error Codes を見てみると、10093 は「WSANOTINITIALISED」 ということで、つまりは WSAStartup 関数が呼ばれていないか失敗したときの どちらかだそうです。なるほど、 Winsock2の関数を呼び出す前には必ず WSAStartup 関数を呼び出さないと ダメなんだろうな。それをふまえて修正 and 実験継続。

テストプログラムNo.2b
 1: #pragma comment( lib, "Ws2_32.lib" )
 2: #define UNICODE
 3: #define _UNICODE
 4: #include <winsock2.h>
 5: #include <Ws2tcpip.h>
 6: #include <stdio.h>
 7:
 8: int wmain( int argc, wchar_t *argv[] )
 9: {
10:     int ans;
11:     addrinfoW hints, *result;
12:     WSADATA wsa;
13:
14:     ans = WSAStartup( MAKEWORD( 2, 2 ), &wsa );
15:     if( ans != 0 ){
16:         printf( "WSAStartup failed: %d\n", ans );
17:         return 1;
18:     }
19:     ZeroMemory( &hints, sizeof( hints ) );
20:     hints.ai_family = AF_INET;
21:     hints.ai_socktype = SOCK_STREAM;
22:     hints.ai_protocol = IPPROTO_TCP;
23:
24:     ans = GetAddrInfoW( L"124.83.139.191", NULL, &hints, &result );
25:     if( ans != 0 ) printf( "Error %d\n", ans );
26:     else           printf( "Port %d\n", ntohs( ((struct sockaddr_in *)(result->ai_addr))->sin_port ) );
27:     ans = GetAddrInfoW( L"localhost", L"", &hints, &result );
28:     if( ans != 0 ) printf( "Error %d\n", ans );
29:     else           printf( "Port %d\n", ntohs( ((struct sockaddr_in *)(result->ai_addr))->sin_port ) );
30:     ans = GetAddrInfoW( L"www.yahoo.co.jp", L"hoge", &hints, &result );
31:     if( ans != 0 ) printf( "Error %d\n", ans );
32:     else           printf( "Port %d\n", ntohs( ((struct sockaddr_in *)(result->ai_addr))->sin_port ) );
33:     ans = GetAddrInfoW( L"none.none.none.none", L"http", &hints, &result );
34:     if( ans != 0 ) printf( "Error %d\n", ans );
35:     else           printf( "Port %d\n", ntohs( ((struct sockaddr_in *)(result->ai_addr))->sin_port ) );
36:     ans = GetAddrInfoW( L"300.300.300.300", L"http", &hints, &result );
37:     if( ans != 0 ) printf( "Error %d\n", ans );
38:     else           printf( "Port %d\n", ntohs( ((struct sockaddr_in *)(result->ai_addr))->sin_port ) );
39:     FreeAddrInfoW( result );
40:     WSACleanup();
41:     return 0;
42: }
コンパイル結果および実行結果
[D:\home] cl test2b.cpp
((省略))
[D:\home] test2b.exe
Port 0
Port 0
Error 10109
Error 11001
Error 11001

[D:\home] 

その一(24行目)その二(27行目)その三(30行目)では GetAddrInfoW 関数の第二引数(サービス名またはポート番号)に関する実験。その一、その 二でもエラーにはならないんだね。エラー 10109 は「WSATYPE_NOT_FOUND」で 、第二引数がサポートされていないということ。その四(33行目)その五(36 行目)はあり得ないホスト名またはアドレスを第一引数で指定した場合。エラ ー 11001 は「WSAHOST_NOT_FOUND」で、そのまんまの意味。

2011/01/07 (Fri)
ちょっと思うところがあり、 Strsafe 関数についてちょっとした実験。
テストプログラムNo.1
 1: #include <Tchar.h>
 2: #include <Strsafe.h>
 3: #include <stdio.h>
 4:
 5: #define  MAX  100
 6:
 7: int _tmain( int argc, _TCHAR *argv[] )
 8: {
 9:    size_t  sz;
10:
11:    StringCbLength( _T( "1234567890" ), MAX, &sz );
12:    printf( "%d\n", sz );
13:    StringCbLength( _T( "あかさたな" ), MAX, &sz );
14:    printf( "%d\n", sz );
15:
16:    StringCchLength( _T( "1234567890" ), MAX, &sz );
17:    printf( "%d\n", sz );
18:    StringCchLength( _T( "あかさたな" ), MAX, &sz );
19:    printf( "%d\n", sz );
20:
21:    return 0;
22: }
コンパイル結果および実行結果
[D:\home] cl test1.cpp
((省略))
[D:\home] test1.exe
10
10
10
10

[D:\home] cl /D UNICODE /D _UNICODE test1.cpp
((省略))
[D:\home] test1.exe
20
10
10
5

[D:\home] 
2011/01/06 (Thu)
思い返してみると去年初頭から始めた Windows プログラミングは、結局 予定通りこれといった成果物もできなかったが、一年たった今も継続中。継続 しているだけでも良しとしよう。このところ慌てて network やら database やら ini ファイルやら寄り道していましたが、そろそろ成果物に向けてまと めていきたいものである。そんなわけで複雑な諸事情につき、database プロ グラムについて、先日作成した ADO.NET 使用 version ではなく、 ADO 使用 version を訳も分からず作ってみた。単純なプログラムなんだけど、ち ゃんと動くようになるまで無茶苦茶苦労した...さて、後は、これまで作成し たプログラムを適当に組み合わせて、拡張していけば、無事(?)立派な成果物に ...なるといいなぁ(笑)
サンプルプログラムNo.d02
 1: #import "C:\Program Files\Common Files\System\ADO\msado15.dll" no_namespace rename("EOF", "EndOfFile")
 2: #include <ole2.h>
 3: #include <stdio.h>
 4: #include <conio.h>
 5:
 6: int main( int argc, char *argv[] )
 7: {
 8:    if ( FAILED(::CoInitialize(NULL)) ) return 1;
 9:
10:    _ConnectionPtr pCnn = NULL;
11:    _RecordsetPtr  pRst = NULL;
12:    _CommandPtr    pCmd = NULL;
13:    _bstr_t        sCnn( "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\home\\data.mdb;" );
14:    _bstr_t        sCmd( "SELECT * FROM sample" );
15:
16:    try {
17:       HRESULT err;
18:
19:       err = pCnn.CreateInstance( __uuidof(Connection) );
20:       if( FAILED( err ) ) _com_issue_error( err );
21:       pCnn->Open( sCnn, "", "", adConnectUnspecified );
22:
23:       err = pCmd.CreateInstance( __uuidof(Command) );
24:       if( FAILED( err ) ) _com_issue_error( err );
25:       pCmd->CommandText = sCmd;
26:       pCmd->ActiveConnection = pCnn;
27:
28:       err = pRst.CreateInstance( __uuidof(Recordset) );
29:       if( FAILED( err ) ) _com_issue_error( err );
30:
31:       _variant_t vCnn;
32:       vCnn.vt = VT_ERROR;
33:       vCnn.scode = DISP_E_PARAMNOTFOUND;
34:       pRst->Open( (_variant_t((IDispatch *) pCmd)), vCnn, adOpenStatic, adLockReadOnly, -1 );
35:       pRst->MoveFirst();
36:
37:       _variant_t vDate;
38:       SYSTEMTIME stDate;
39:       while( pRst->EndOfFile == false ){
40:          vDate = pRst->GetCollect(_variant_t("date"));
41:          vDate.ChangeType(VT_DATE);
42:          VariantTimeToSystemTime( vDate.date, &stDate );
43:          printf( "%04d/%02d/%02d\n", stDate.wYear, stDate.wMonth, stDate.wDay );
44:          pRst->MoveNext();
45:       }
46:    } catch( _com_error &e ) {
47:       printf( "Errors occured." );
48:    }
49:    if( pRst )
50:       if( pRst->State == adStateOpen )
51:          pRst->Close();
52:    ::CoUninitialize();
53:    return 0;
54: }

解説はまた次回以降。が、一言二言。1 行目の「 rename("EOF", "EndOfFile")」は、 Recordset オブジェクトの EOF プロパティについて、EOF というキーワード が stdio.h で #define されていて競合するため、別名に変更しているのだと 思います。8 行目は COM を使用するときのお約束(らしい)。42行目は variant 型から SYSTEMTIME 構造体に変換。実はこの関数を発見するのが一番大変だっ た(笑)

コンパイル結果および実行結果
[D:\home] cl sampleD2.cpp
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

sampleD2.cpp
sampleD2.cpp(16) : warning C4530: C++ 例外処理を使っていますが、アンワインド セ
マンティクスは有効にはなりません。/EHsc を指定してください。
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:sampleD2.exe
sampleD2.obj

[D:\home] cl /EHsc sampleD2.cpp
Microsoft(R) 32-bit C/C++ Optimizing Compiler Version 16.00.30319.01 for 80x86
Copyright (C) Microsoft Corporation.  All rights reserved.

sampleD2.cpp
Microsoft (R) Incremental Linker Version 10.00.30319.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:sampleD2.exe
sampleD2.obj

[D:\home] sampleD2.exe
1970/03/14
1979/03/20
1983/05/27
1970/11/28
1974/11/14
1979/03/27

[D:\home]
(追記) _bstr_t とか _variant_t 、_com_error について、詳しくは Compiler COM Support Classes とかを見るといいかも...しれない。


Copyright(C) 2011 Hiroaki Fujiuchi ( fujiuchi@gmail.com )
inserted by FC2 system