このページは、私が思いついた時に(だから毎日ではない)、思 いついたことを(だからコンピュータだけの話ではない)、思いついたなりに (だから日本語むちゃくちゃ)徒然と書き綴るページです。
Message Source | wParam (high word) | wParam (low word) | lParam |
---|---|---|---|
Menu | 0 | Menu identifier (IDM_*) | 0 |
Accelerator | 1 | Accelerator identifier (IDM_*) | 0 |
Control | Control-defined notification code | Control identifier | Handle to the control window |
で、次は、wParam および lParam で処理を振り分けないといけないわけで す。上表から wParam (high word)、つまり HIWORD( wParam ) で処理を振り分 ければいいと思ったのだが、Control-defined notification code には 0 や 1 のものもあり(多分 BN_CLICKED は 0、CBN_SELCHANGE は 1)、そうすると Menu や Accelerator と区別をするのが難しい場合がある。じゃあどうするの か、wParam (low word)、つまり LOWORD( wParam ) ですることになるのかなぁ ?と思うわけです。この LOWORD( wParam ) の Menu / Accelerator / Control identifier は(多分)プログラマが勝手に(自由に)定義するものだと思う ので、これらを unique なものにすれば問題は解決する...というのが現在の当 方の公式見解(間違っていたらごめんなさい)。そんなわけで、まずは MSDN の「 Symbols: Resource Identifiers」とか「 Symbol Name Restrictions」とかを見て、Menu / Accelerator / Control identifier の命名に関する「マイルール」を検討してみた が、なかなか納得のいくものができず。そして「もう少し勉強を進めて、幅広 い知識がついた段階で再度検討してみよう」という結論に達したのでした。
CreateObject("SAPI.SpVoice").Speak"I love you"
プログラム catn2.vbs 1: If WScript.Arguments.Count <> 1 Then 2: WScript.Echo "Usage: catn2.vbs [filename]" 3: WScript.Quit 1 4: End If 5: 6: Set oWSHShell = WScript.CreateObject( "WScript.Shell" ) 7: Set oFileSystem = CreateObject( "Scripting.FileSystemObject" ) 8: 9: If oFileSystem.FileExists( WScript.Arguments(0) ) = False Then 10: WScript.Echo "ファイル " & WScript.Arguments(0) & " が見つかりません" 11: WScript.Quit 1 12: End If 13: 14: iLine = 1 15: Set oDataFile = oFileSystem.OpenTextFile( WScript.Arguments(0) ) 16: Do While oDataFile.AtEndOfStream <> True 17: sData = Replace( oDataFile.ReadLine, "&", "&" ) 18: sData2 = Replace( sData, """", """ ) 19: sData = Replace( sData2, "<", "<" ) 20: sData2 = Replace( sData, ">", ">" ) 21: WScript.Echo iLine & ": " & sData2 22: iLine = iLine + 1 23: Loop 24: oDataFile.Close
サンプルプログラムNo.d05 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: #define FIELD_NUM 2 9: 10: int wmain( int argc, wchar_t *argv[] ) 11: { 12: if( FAILED( ::CoInitialize( NULL ) ) ) return 1; 13: 14: _ConnectionPtr pCnn( L"ADODB.Connection" ); 15: _RecordsetPtr pRst( L"ADODB.Recordset" ); 16: 17: try { 18: pCnn->Open( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=D:\\home\\data.mdb;", 19: L"", L"", adConnectUnspecified ); 20: pRst->Open( L"data2", _variant_t((IDispatch *) pCnn, true), 21: adOpenStatic, adLockOptimistic, adCmdUnspecified ); 22: 23: VARIANT *vp; 24: SAFEARRAY *saField, *saData; 25: 26: saField = SafeArrayCreateVector( VT_VARIANT, 0, FIELD_NUM ); 27: SafeArrayAccessData( saField, (void **)&vp ); 28: vp[0].vt = VT_BSTR; 29: vp[0].bstrVal = _bstr_t( L"ID" ); 30: vp[1].vt = VT_BSTR; 31: vp[1].bstrVal = _bstr_t( L"field1" ); 32: SafeArrayUnaccessData( saField ); 33: 34: saData = SafeArrayCreateVector( VT_VARIANT, 0, FIELD_NUM ); 35: SafeArrayAccessData( saData, (void **)&vp ); 36: vp[0].vt = VT_I4; 37: vp[0].lVal = 12; 38: vp[1].vt = VT_I4; 39: vp[1].lVal = 65; 40: SafeArrayUnaccessData( saData ); 41: 42: VARIANT vField, vData; 43: vField.vt = VT_ARRAY | VT_VARIANT; 44: vData.vt = VT_ARRAY | VT_VARIANT; 45: vField.parray = saField; 46: vData.parray = saData; 47: 48: pRst->AddNew( vField, vData ); 49: } catch( _com_error &e ) { 50: printf( "Error: %s\n", e.ErrorMessage() ); 51: printf( "警告:%s\n", (char*)e.Description() ); 52: } 53: if( pRst ) 54: if( pRst->State == adStateOpen ) 55: pRst->Close(); 56: pCnn->Close(); 57: ::CoUninitialize(); 58: return 0; 59: }
少しだけ解説。AddNewメソッドについては先の解説通り、配列を引数とす ることで複数フィールドを一度に指定してレコードを追加することができる。 ということだったので「_variant_t vField[2], vData[2];」としてみたが、 うまくいかなかった、というのが前回までのあらすじ。あと、VARIANT型とい うのは様々なデータ型を扱えるというのも前回までに説明済み。で、この VARIANT型は「配列(VT_ARRAY)」も扱えるということにデータ型を表す定数 の表を作っていて気が付いた。なるほど、じゃあ「vParam1.parray = vField[2]」 みたいな感じでいいのかな?とおもったんだが、そうは問屋が卸さない、この 場合 SAFEARRAY型を使うんだそうだ。また、なんか聞いたことない言葉が出て きたなぁ... 仕方がなく、いろいろ調べてみたところ、SAFEARRAY型は SafeArrayCreate関数で作成するものらしい。ただ、今回必要な配列は一 次元だから、 SafeArrayCreateVector関数で作成してみた。で、コンパイルして、実行 (実行結果 [ PNG file, 4KB ])して、特段 問題なし。よかった、よかった。しかし、見ていただいた通り、この方法でレ コードを追加するのは非常に面倒くさい。正直、「AddNew → フィールド値設 定 → Update」方法の方が、分かり易いし、多分、実行速度も速いと思う(未 実験だが、SafeArrayAccessData関数と SafeArrayUnaccessData関数を呼び出 すオーバヘッドを考えれば、上記方法の方が遅いと思われる)。ということで 、この方法を使うメリットがよくわからないので、今後、この方法を使うこと はないでしょう。ただ、SAFEARRAY型は使うかもしれないので、頭の隅には残 しておこう...
サンプルプログラムNo.d05 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: wchar_t *DatabaseName = L"D:\\home\\data.mdb"; 16: _bstr_t sCnn( L"Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" ); 17: 18: try { 19: pCnn->Open( sCnn + _bstr_t( DatabaseName ), L"", L"", adConnectUnspecified ); 20: pRst->Open( L"min", _variant_t((IDispatch *)pCnn, true), 21: adOpenStatic, adLockOptimistic, adCmdUnspecified ); 22: 23: _variant_t vDate; 24: SYSTEMTIME stDate; 25: GetLocalTime( &stDate ); 26: SystemTimeToVariantTime( &stDate, &(vDate.date) ); 27: vDate.vt = VT_DATE; 28: 29: long LIndex; 30: pRst->AddNew(); 31: LIndex = (long)0; 32: pRst->Fields->Item[LIndex]->Value = vDate; 33: pRst->Update(); 34: } catch( _com_error &e ) { 35: printf( "Error:%s\n", e.ErrorMessage() ); 36: printf( "警告:%s\n", (char*)e.Description() ); 37: } 38: if( pRst ) 39: if( pRst->State == adStateOpen ) 40: pRst->Close(); 41: pCnn->Close(); 42: ::CoUninitialize(); 43: return 0; 44: } コンパイル結果および実行結果 [D:\home] cl /EHsc sampleD5.cpp ((省略)) [D:\home] sampleD5.exe Error:IDispatch error #3092 警告:SQL ステートメントが正しくありません。 'DELETE'、'INSERT'、'PROCEDURE'、'S ELECT'、または 'UPDATE' を使用してください。 [D:\home]
改造点。本格的なプログラムを考えると DBファイル名は定数ではなく、変 数で与えられるんじゃないかと。そんなわけで DB ファイル の path をわざわ ざ wchar_t * 変数にしてみました(15〜19行)。次は、日付データを DB に保 存してみた(23〜32行目)。あと、もう少し詳しいエラーメッセージを出して くれる e.Description() を追加(36行目)。ちなみに返り値は MBCSなので、Unicode として 表示したい場合には、「 mbstowcs、_mbstowcs_l関数」とか「 mbstowcs_s、_mbstowcs_s_l関数」とか「 MultiByteToWideChar関数」なんかで変換してやる必要がある。最後に pCnn をクローズ(41行目)。で、実行結果ですが、エラーになっていたんじゃ意味 がない。で、その原因が「table 名」にあることに気が付くのにえらく時間が かかりました。min でも駄目、minute でも駄目、最後はやけくそで「分データ 」にしたらちゃんと動いた orz。そんなわけで、MS-Access で table 名を「分 データ」に直すとともに、20 行目を「pRst->Open( L"分データ" (省略)」にすることで無事動きました。ちなみに Provider を「 Microsoft.ACE.OLEDB.12.0」に変えてみたら、実行時にエラー「 Unknown error 0x800A0E7A」「プロバイダーが見つかりません。正しくインス トールされていない可能性があります。」が出た。んー、おかしいなぁ。WSH じゃうまくいったのに。なんでだろう ...といろいろ調べてみたところ、install したのは Access2010 の 64bit 版 で、となると install された ACE.OLEDB の DLL (多分 C:\Program Files\ Common Files\Microsoft Shared\OFFICE14\ACEOLEDB.DLL)も 64bit、プログラ ムは 32bit。32bit プログラムから 64bit DLL は利用できない、ということで うまく動かないらしい。じゃあ 64bit プログラムだとちゃんと動くのか?とい う疑問が生じたが、普通の VC++ 2010 Express では 64bitプログラムは作成で きないみたいなので、この件の検証はまた時間ができたら。