このページは、私が思いついた時に(だから毎日ではない)、思 いついたことを(だからコンピュータだけの話ではない)、思いついたなりに (だから日本語むちゃくちゃ)徒然と書き綴るページです。
サンプルプログラム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」の値要求を「 はい」して上記プログラムを動かしてみたが...特にエラーもなく、うまく動い ている様子。となると、特段問題もない気がして、これでいいんじゃないかと... (→配列編)
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_DISPATCH | VT_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)が発 生しました。うーん、どうすればうまくいくんだ...ってなわけで次回に続く。
サンプルプログラム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 | field1 | field2 |
---|---|---|---|
データ型 | 長整数型 | 長整数型 | テキスト型 |
値要求 | いいえ | いいえ | いいえ |
テストプログラム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]
テストプログラム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」で、そのまんまの意味。
テストプログラム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]
サンプルプログラム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]