小さい mdb ですけど 10000回連続読み込んでも、メモリ消費は増えませんでした。
参考にしたところ。
- Win32OLE 製作過程の雑記
- Ruby win32ole.c
- 「るびま」連載:Win32OLE 活用法
- C++ アプリケーションではなく C アプリケーションからOLE オートメーションを使用する方法
#include <stdio.h> #include <malloc.h> #include <windows.h> /* コンパイル * gcc comtest.c -lole32 -loleaut32 -luuid -o comtest * * */ // COM は内部でBSTRを使用しています。 BSTR BSTRfromCstring(char* cstring ){ int cstringlen, out_size; BSTR wstr; cstringlen = strlen(cstring); out_size = MultiByteToWideChar(CP_ACP, 0, cstring, cstringlen, NULL, 0); wstr = SysAllocStringLen(NULL, out_size); MultiByteToWideChar(CP_ACP, 0, cstring, cstringlen, wstr, out_size); return wstr; } char* CSTRfromBSTR(BSTR bstr){ int out_size; char *cstring; out_size = WideCharToMultiByte(CP_ACP, 0, (OLECHAR*)bstr, -1, NULL, 0, NULL, NULL); cstring = (char*)malloc((out_size+1) * sizeof(char)); WideCharToMultiByte(CP_ACP, 0, (OLECHAR*)bstr, -1, cstring, out_size, NULL, NULL); return cstring; } // VARIANTは大きな共用体です。 // http://marupeke296.com/IKDADV_CPP_VARIANT.html VARIANT型を知ってみる long Variant2Long(VARIANT *pVariant){ long num; if(V_ISBYREF(pVariant)) num = (long)*V_I4REF(pVariant); else num = (long)V_I4(pVariant); return num; } // COM が新しいオブジェクトのポインタを返したとき IDispatch *Variant2Dispatch(VARIANT *pVariant){ IDispatch *pDispatch; if (V_ISBYREF(pVariant)) pDispatch = *V_DISPATCHREF(pVariant); else pDispatch = V_DISPATCH(pVariant); return pDispatch; } // 常にインタフェーステーブルへアクセスする。 // Open,Close などのコマンドからテーブルのディスパッチIDを求め // 実行する。 HRESULT ComInvoke( PVOID *p, char *ComString ,VARIANT *param, USHORT wFlags, VARIANT *result){ IDispatch *pDisp; DISPID dispID; HRESULT hr; unsigned short *ucPtr; UINT puArgErr = 0; EXCEPINFO excepinfo; // DBアクセスは引数がないか、あっても1個。デフォルトは引数なし。 // param->vt に VT_EMPTY が 設定されていれば cArgs を 1 にして // rgvarg にコマンド引数(VARIANT型)をセットします。 // 参考:invoke メソッドの引数 // http://homepage1.nifty.com/markey/ruby/win32ole/win32ole03.html#invoke-param DISPPARAMS dispParams = { NULL, NULL, 0, 0 }; if( param->vt != VT_EMPTY){ dispParams.cArgs = 1; dispParams.rgvarg = param; } memset( &excepinfo, 0, sizeof(EXCEPINFO)); pDisp = *p; // コマンド文字列からディスパッチID取得 ucPtr = BSTRfromCstring( ComString ); pDisp->lpVtbl->GetIDsOfNames((IDispatch *)pDisp, &IID_NULL, &ucPtr, 1, LOCALE_USER_DEFAULT, (DISPID*)&dispID); // ここが肝心のInvokeを実行する部分。 VariantInit(result); hr = pDisp->lpVtbl->Invoke( pDisp, // 参考: Ruby付属の「OLE View」 dispID, // arg1 - I4 dispidMember [IN] &IID_NULL, // arg2 - GUID riid [IN] LOCALE_SYSTEM_DEFAULT, // arg3 - UI4 lcid [IN] wFlags, // arg4 - UI2 wFlags [IN] &dispParams, // arg5 - DISPPARAMS pdispparams [IN] result, // arg6 - VARIANT pvarResult [OUT] &excepinfo, // arg7 - EXCEPINFO pexcepinfo [OUT] &puArgErr ); // arg8 - UINT puArgErr [OUT] // printf("Invoke %-10s dispID:%4d hr:%08x puArgErr:%d\n",ComString, dispID, hr,puArgErr); SysFreeString(ucPtr); return hr; } // ProgID("ADODB.Connection")からCLSID({00000514-0000-0010-8000-00AA006D2EA4}) // を求め、CoCreateInstance APIを呼びます。 IDispatch *InstanceNew(char *ComName,HRESULT *hr){ static IDispatch *pDisp; BSTR name; CLSID clsid; name = BSTRfromCstring( ComName ); *hr = CLSIDFromProgID(name, &clsid); // HRESULTは最上位ビットで OK ,NG を表現します。 // FAILED は hr が 0 より小さいかどうかチェックするマクロ。 if(FAILED(hr)) { *hr = CLSIDFromString(name, &clsid); } *hr = CoCreateInstance(&clsid, NULL, CLSCTX_INPROC_SERVER | CLSCTX_LOCAL_SERVER, &IID_IDispatch, (void **)&pDisp); SysFreeString(name); return pDisp; } void ADODBopen(char *connect,PVOID *pDisp,HRESULT *hr){ VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_BSTR; // 引数はBSTR param.bstrVal = BSTRfromCstring(connect); // 接続文字列をBSTRに変換して共用体に設定 *hr = ComInvoke((void **)pDisp, "Open", ¶m, DISPATCH_PROPERTYPUT | DISPATCH_METHOD, &result); } void ADODBclose(PVOID *pDisp,HRESULT *hr){ VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_EMPTY; // 引数なし *hr = ComInvoke((void **)pDisp,"Close",¶m, DISPATCH_PROPERTYPUT | DISPATCH_METHOD,&result); } IDispatch *ExecuteSql(PVOID *connDisp, char *SqlString, HRESULT *hr){ static IDispatch *resDisp; VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_BSTR; // 引数はBSTR param.bstrVal = BSTRfromCstring(SqlString); // SQL文字列をBSTRに変換して設定 *hr = ComInvoke((void **)connDisp, "Execute" , ¶m, DISPATCH_PROPERTYPUT | DISPATCH_METHOD,&result); resDisp = Variant2Dispatch(&result); return resDisp; } // rs.invoke("Fields") のように親オブジェクトから子オブジェクトを生成。 // Fields オブジェクトを取得するとカラム数、Item が取得できるようになる。 // Fields.invoke("Count") #=> 5 // カラム名を指定して Item を指定するとカラムの値が取得できるようになる。 // Fields.invoke("Item","Day").invoke("Value") #=> "1923/09/01 00:00:00" IDispatch *CreateNewObject( PVOID *parentDisp, char *ObjName, HRESULT *hr){ static IDispatch *newDisp; VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_EMPTY; *hr = ComInvoke((void **)parentDisp, ObjName, ¶m, DISPATCH_PROPERTYGET | DISPATCH_METHOD,&result); newDisp = Variant2Dispatch(&result); return newDisp; } // カラムの番号を指定して field オブジェクトを取得する。 // rs.invoke("Fields",1) #=> field // field オブジェクトからはフィールド名、タイプなどが取得出来る。 // field.invoke("Name") #=> "Day" IDispatch *CreatefieldObject( PVOID *rsDisp, long col, HRESULT *hr){ static IDispatch *field; VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_I4; param.lVal = col; *hr = ComInvoke((void **)rsDisp, "Fields", ¶m, DISPATCH_PROPERTYGET | DISPATCH_METHOD,&result); field = Variant2Dispatch(&result); return field; } long getLongProperty(PVOID *Object, char *PropertyName, HRESULT *hr){ static long num; VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_EMPTY; *hr = ComInvoke((void **)Object, PropertyName, ¶m, DISPATCH_PROPERTYGET | DISPATCH_METHOD,&result); num = Variant2Long(&result); return num; } char *getStringProperty(PVOID *Object, char *PropertyName ,HRESULT *hr){ VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_EMPTY; *hr=ComInvoke((void **)Object, PropertyName, ¶m, DISPATCH_PROPERTYGET | DISPATCH_METHOD,&result); return CSTRfromBSTR(result.bstrVal); } int getBoolProperty(PVOID *Object, char *PropertyName ,HRESULT *hr){ VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_EMPTY; *hr=ComInvoke((void **)Object, PropertyName, ¶m, DISPATCH_PROPERTYGET | DISPATCH_METHOD,&result); return (V_BOOL(&result) ? (-1) : 0); } char *Date2String(DATE date){ char *buf; SYSTEMTIME st; VariantTimeToSystemTime(date, &st); buf = (char*)malloc(20 * sizeof(char)); sprintf(buf,"%04d/%02d/%02d %02d:%02d:%02d", st.wYear,st.wMonth,st.wDay,st.wHour,st.wMinute,st.wSecond); return buf; } char *Number2String(long num){ char *buf; buf = (char*)malloc(30 * sizeof(char)); sprintf(buf,"%d",num); return buf; } char *Double2String(double num){ char *buf; buf = (char*)malloc(30 * sizeof(char)); sprintf(buf,"%f",num); return buf; } char *Variant2String(VARIANT *result){ switch(V_VT(result)){ case VT_I2: // short return Number2String((long)V_I2(result)); break; case VT_I4: // long return Number2String((long)V_I4(result)); break; case VT_R4: // float return Double2String(V_R4(result)); break; case VT_R8: // double return Double2String(V_R8(result)); break; case VT_BOOL: //(True -1,False 0) return (V_BOOL(result) ? "True" : "False"); break; case VT_BSTR: return CSTRfromBSTR(V_BSTR(result)); break; case VT_DATE: return Date2String( V_DATE(result)); break; } } // Ruby だとこんな感じ。"Name"は紛らわしいけれど"Name"というカラム名。 // puts Fields.invoke("Item","Name").invoke("Value") // #=> "関東大震災" char *readItem(PVOID *Fields, char *ColName, HRESULT *hr){ IDispatch *itemDisp; VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_BSTR; param.bstrVal = BSTRfromCstring(ColName); *hr=ComInvoke((void **)Fields, "Item" , ¶m, DISPATCH_PROPERTYGET | DISPATCH_METHOD,&result); itemDisp = Variant2Dispatch(&result); VariantClear(¶m); param.vt = VT_EMPTY; *hr=ComInvoke((void **)&itemDisp, "Value" , ¶m, DISPATCH_PROPERTYGET | DISPATCH_METHOD, &result); itemDisp ->lpVtbl->Release( (void*)itemDisp ); return Variant2String(&result); } int getEofOrBof(PVOID *rs,HRESULT *hr){ return (getBoolProperty((void **)rs, "EOF",hr) || getBoolProperty((void **)rs, "BOF",hr)); } void putProperty(PVOID *pDispatch, char *command,HRESULT *hr){ VARIANT param, result; DISPID dispID; VariantInit(¶m); param.vt = VT_EMPTY; *hr = ComInvoke((void **)pDispatch,command,¶m, DISPATCH_PROPERTYPUT | DISPATCH_METHOD,&result); } int main(int argc, char* argv[]){ IDispatch *conn, *rs, *Fields, *field, *itemDisp; DISPID dispID; VARIANT param, result; HRESULT hr=0; BSTR bstr; char *cstr, *Name, *Day, *Magnitude, *NumOfDeaths,*YesNo,*DeadOrAlive; long fCount, type; int i; //char dsn[] = {"Provider=SQLOLEDB.1;Password=hoge;User ID=user;Initial Catalog=DBname;Data Source=tcp:192.168.1.123,1433"}; char dsn[] = {"DRIVER={Microsoft Access Driver (*.mdb)};Dbq=C:\\card\\COM\\C\\sample1.mdb"}; char sqlstring[]= {"SELECT * FROM earthquake;"}; OleInitialize(NULL); conn = InstanceNew("ADODB.Connection",&hr); ADODBopen( dsn,(void **)&conn,&hr); rs = ExecuteSql( (void **)&conn, sqlstring, &hr); Fields = CreateNewObject( (void **)&rs,"Fields", &hr); fCount = getLongProperty( (void **)&Fields, "Count", &hr); // 全カラム名を表示 for(i=0;i<fCount;i++){ // rs.invoke("Fields",1) #=> fieldOBJct field = CreatefieldObject( (void **)&rs, i, &hr); // field.invoke("Name") #=> "Day" cstr = getStringProperty( (void **)&field, "Name", &hr); printf("#=> %s\n", cstr); free(cstr); field->lpVtbl->Release( (void*)field); } // #=> Name #=> Day #=> Magnitude #=> NumOfDeaths #=> YesNo #=> DeadOrAlive for(;;){ if (getEofOrBof( (void **)&rs, &hr)) break; Name = readItem( (void **)&Fields, "Name", &hr); Day = readItem( (void **)&Fields, "Day", &hr); Magnitude = readItem( (void **)&Fields, "Magnitude", &hr); NumOfDeaths = readItem( (void **)&Fields, "NumOfDeaths", &hr); YesNo = readItem( (void **)&Fields, "YesNo", &hr); DeadOrAlive = readItem( (void **)&Fields, "DeadOrAlive", &hr); //printf("%-20s %20s %10s %10s %-5s %3s\n", // Name,Day,Magnitude, NumOfDeaths,YesNo,DeadOrAlive); free(Name); free(Day); free(Magnitude); free(NumOfDeaths); free(YesNo); free(DeadOrAlive); putProperty((void **)&rs, "MoveNext", &hr); } // 関東大震災 1923/09/01 00:00:00 7.900000 142807 True -1 // 北海道東方沖地震 1994/10/04 00:00:00 8.100000 0 False 0 // 阪神淡路大震災 1995/01/17 00:00:00 7.200000 6418 False -1 // 新潟県中越地震 2004/10/23 00:00:00 6.800000 37 False -1 ADODBclose((void **)&conn,&hr); conn ->lpVtbl->Release( (void*)conn); rs ->lpVtbl->Release( (void*)rs ); Fields->lpVtbl->Release( (void*)Fields ); OleUninitialize(); return 0; }