/////////////////////////////////////////////////////////////////////////////// // // Copyright (c) 2000, Microsoft Corp. All rights reserved. // // FILE // // iasdb.cpp // // SYNOPSIS // // Defines functions for accessing OLE-DB databases. // // MODIFICATION HISTORY // // 04/13/2000 Original version. // /////////////////////////////////////////////////////////////////////////////// #include #include #include #include #include #include VOID WINAPI IASCreateTmpDirectory() { // JetInit fails if the TMP directory doesn't exist, so we'll try to // create it just in case. DWORD needed = GetEnvironmentVariableW(L"TMP", NULL, 0); if (needed) { PWCHAR buf = (PWCHAR)_alloca(needed * sizeof(WCHAR)); DWORD actual = GetEnvironmentVariableW(L"TMP", buf, needed); if (actual > 0 && actual < needed) { CreateDirectoryW(buf, NULL); } } } HRESULT WINAPI IASCreateJetProvider( OUT IDBInitialize** provider ) throw () { IASCreateTmpDirectory(); // Convert the ProgID to a ClsID. CLSID clsid; HRESULT hr = CLSIDFromProgID( L"Microsoft.Jet.OLEDB.4.0", &clsid ); if (SUCCEEDED(hr)) { // Create the OLE DB provider. hr = CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, __uuidof(IDBInitialize), (PVOID*)provider ); } return hr; } HRESULT WINAPI IASOpenJetDatabase( IN PCWSTR path, IN BOOL readOnly, OUT LPUNKNOWN* session ) { IASTracePrintf("INFO Enter IASOpenJetDatabase. path = %S readonly = %d", path, readOnly); // Initialize the out parameter. if (session == NULL) { return E_POINTER; } *session = NULL; HRESULT hr; do { // Create the OLE DB provider. CComPtr connection; hr = IASCreateJetProvider(&connection); if (FAILED(hr)) { break; } ////////// // Set the properties for the data source. ////////// CComPtr properties; hr = connection->QueryInterface( __uuidof(IDBProperties), (PVOID*)&properties ); if (FAILED(hr)) { break; } CComBSTR bstrPath = SysAllocString(path); if (!bstrPath) { hr = E_OUTOFMEMORY; break; } DBPROP dbprop[2]; dbprop[0].dwPropertyID = DBPROP_INIT_MODE; dbprop[0].dwOptions = DBPROPOPTIONS_REQUIRED; dbprop[0].colid = DB_NULLID; dbprop[0].vValue.vt = VT_I4; dbprop[0].vValue.lVal = readOnly ? DB_MODE_READ : DB_MODE_READWRITE; dbprop[1].dwPropertyID = DBPROP_INIT_DATASOURCE; dbprop[1].dwOptions = DBPROPOPTIONS_REQUIRED; dbprop[1].colid = DB_NULLID; dbprop[1].vValue.vt = VT_BSTR; dbprop[1].vValue.bstrVal = bstrPath; DBPROPSET dbpropSet; dbpropSet.guidPropertySet = DBPROPSET_DBINIT; dbpropSet.cProperties = 2; dbpropSet.rgProperties = dbprop; hr = properties->SetProperties(1, &dbpropSet); if (FAILED(hr)) { hr = IASTraceJetError("IDBProperties::SetProperties", hr); break; } ////////// // Set the Jet specific properties for the data source. ////////// dbprop[0].dwPropertyID = DBPROP_JETOLEDB_DATABASELOCKMODE; dbprop[0].dwOptions = DBPROPOPTIONS_REQUIRED; dbprop[0].colid = DB_NULLID; dbprop[0].vValue.vt = VT_I4; dbprop[0].vValue.lVal = (LONG)DBPROPVAL_DL_OLDMODE; dbpropSet.guidPropertySet = DBPROPSET_JETOLEDB_DBINIT; dbpropSet.cProperties = 1; dbpropSet.rgProperties = dbprop; hr = properties->SetProperties(1, &dbpropSet); if (FAILED(hr)) { hr = IASTraceJetError("IDBProperties::SetProperties", hr); break; } ////////// // Initialize the connection. ////////// // This is a bit of a hack. The right approach would be to either (1) always // connect to the local database and use DCOM handle the remoting or (2) // always impersonate the client. Unfortunately, both of these would require // considerable changes to the existing client code. Instead, we'll only // impersonate the client when running under the local system account AND // opening a remote database. We know this will always fail if we don't // impersonate, so we can't lose anything by attempting to impersonate. bool revert = false; if ( !IASIsInprocServer() && path[0] == L'\\' && path[1] == L'\\' ) { HRESULT hr = CoImpersonateClient(); if ( FAILED(hr) ) { IASTraceFailure("CoImpersonateClient", hr); break; } else { revert = true; } } hr = connection->Initialize(); if (revert) { CoRevertToSelf(); } if (FAILED(hr)) { hr = IASTraceJetError("IDBInitialize::Initialize", hr); break; } ////////// // Create a session. ////////// CComPtr creator; hr = connection->QueryInterface( __uuidof(IDBCreateSession), (PVOID*)&creator ); if (FAILED(hr)) { break; } hr = creator->CreateSession( NULL, __uuidof(IUnknown), session ); if (FAILED(hr)) { hr = IASTraceJetError("IDBCreateSession::CreateSession", hr); break; } } while(false); IASTracePrintf("INFO Leave IASOpenJetDatabase. hr = 0x%08X", hr); return hr; } HRESULT WINAPI IASExecuteSQLCommand( IN LPUNKNOWN session, IN PCWSTR commandText, OUT IRowset** result ) { IASTracePrintf("INFO IASExecuteSQLCommand. Command = %S", commandText); // Initialize the out parameter. if (result) { *result = NULL; } HRESULT hr; CComPtr creator; hr = session->QueryInterface( __uuidof(IDBCreateCommand), (PVOID*)&creator ); if (FAILED(hr)) { return hr; } CComPtr command; hr = creator->CreateCommand( NULL, __uuidof(ICommandText), (IUnknown**)&command ); if (FAILED(hr)) { return IASTraceJetError("IDBCreateCommand::CreateCommand", hr); } hr = command->SetCommandText( DBGUID_DBSQL, commandText ); if (FAILED(hr)) { return IASTraceJetError("ICommandText::SetCommandText", hr); } hr = command->Execute( NULL, (result ? __uuidof(IRowset) : IID_NULL), NULL, NULL, (IUnknown**)result ); if (FAILED(hr)) { return IASTraceJetError("ICommand::Execute", hr); } return S_OK; } HRESULT WINAPI IASExecuteSQLFunction( IN LPUNKNOWN session, IN PCWSTR functionText, OUT LONG* result ) { // Initialize the out parameter. if (result == NULL) { return E_POINTER; } *result = 0; // Execute the function. HRESULT hr; CComPtr rowset; hr = IASExecuteSQLCommand( session, functionText, &rowset ); if (FAILED(hr)) { return hr; } CComPtr accessor; hr = rowset->QueryInterface( __uuidof(IAccessor), (PVOID*)&accessor ); if (FAILED(hr)) { return hr; } // Get the result row. DBCOUNTITEM numRows; HROW hRow; HROW* pRow = &hRow; hr = rowset->GetNextRows( NULL, 0, 1, &numRows, &pRow ); if (FAILED(hr)) { return IASTraceJetError("IRowset::GetNextRows", hr); } if (numRows == 0) { return E_FAIL; } ///////// // Create an accessor. ///////// DBBINDING bind; memset(&bind, 0, sizeof(bind)); bind.iOrdinal = 1; bind.dwPart = DBPART_VALUE; bind.wType = DBTYPE_I4; bind.cbMaxLen = 4; HACCESSOR hAccess; hr = accessor->CreateAccessor( DBACCESSOR_ROWDATA, 1, &bind, 4, &hAccess, NULL ); if (SUCCEEDED(hr)) { // Get the data. hr = rowset->GetData( hRow, hAccess, result ); if (FAILED(hr)) { hr = IASTraceJetError("IRowset::GetData", hr); } accessor->ReleaseAccessor(hAccess, NULL); } else { hr = IASTraceJetError("IAccessor::CreateAccessor", hr); } rowset->ReleaseRows(1, &hRow, NULL, NULL, NULL); return hr; } ////////////////////////////////////////////////////////////////////////////// // IASCreateDatabase ////////////////////////////////////////////////////////////////////////////// HRESULT WINAPI IASCreateJetDatabase( IN PCWSTR dataSource ) { // Create the OLE DB provider. CComPtr connection; HRESULT hr = IASCreateJetProvider(&connection); if (FAILED(hr)) { return hr; } ////////// // Set the properties for the data source. ////////// CComPtr properties; hr = connection->QueryInterface( __uuidof(IDBProperties), (PVOID*)&properties ); if (FAILED(hr)) { return hr; } CComBSTR bstrDataSource(dataSource); if ( !bstrDataSource) { return E_OUTOFMEMORY; } DBPROP dbprop[2]; dbprop[0].dwPropertyID = DBPROP_INIT_DATASOURCE; dbprop[0].dwOptions = DBPROPOPTIONS_REQUIRED; dbprop[0].colid = DB_NULLID; dbprop[0].vValue.vt = VT_BSTR; dbprop[0].vValue.bstrVal = bstrDataSource; dbprop[1].dwPropertyID = DBPROP_INIT_LOCATION; dbprop[1].dwOptions = DBPROPOPTIONS_OPTIONAL; dbprop[1].colid = DB_NULLID; dbprop[1].vValue.vt = VT_BSTR; dbprop[1].vValue.lVal = VT_NULL; DBPROPSET dbpropSet; dbpropSet.guidPropertySet = DBPROPSET_DBINIT; dbpropSet.cProperties = 2; dbpropSet.rgProperties = dbprop; hr = properties->SetProperties(1, &dbpropSet); if (FAILED(hr)) { return IASTraceJetError("IDBProperties::SetProperties", hr); } // Create the Data Source CComPtr admin; hr = connection->QueryInterface( __uuidof(IDBDataSourceAdmin), (PVOID*)&admin ); if (FAILED(hr)) { return hr; } hr = admin->CreateDataSource( 1, &dbpropSet, NULL, __uuidof(IDBCreateSession), NULL ); if (FAILED(hr)) { return IASTraceJetError("IDBDataSourceAdmin::CreateDataSource", hr); } return S_OK; } // Internal Jet error codes from msjeterr.h #define JET_errFileAccessDenied -1032 #define JET_errKeyDuplicate -1605 HRESULT WINAPI IASTraceJetError( PCSTR functionName, HRESULT errorCode ) { IASTracePrintf("%s failed; return value = 0x%08X", functionName, errorCode); IErrorInfo* errInfo; if (GetErrorInfo(0, &errInfo) == S_OK) { BSTR description; if (SUCCEEDED(errInfo->GetDescription(&description))) { IASTracePrintf("\tDescription: %S", description); SysFreeString(description); } IErrorRecords* errRecords; if (SUCCEEDED(errInfo->QueryInterface( __uuidof(IErrorRecords), (PVOID*)&errRecords ))) { ULONG numRecords = 0; errRecords->GetRecordCount(&numRecords); for (ULONG i = 0; i < numRecords; ++i) { ERRORINFO info; if (SUCCEEDED(errRecords->GetBasicErrorInfo(i, &info))) { IASTracePrintf( "\tRecord %lu: hrError = 0x%08X; dwMinor = 0x%08X", i, info.hrError, info.dwMinor ); // Jolt does a poor job of mapping Jet error codes to HRESULTs, // so we handle a few ourselves. The Jet error code is the low // order 16 bits treated as a signed integer. switch ((WORD)info.dwMinor) { case JET_errFileAccessDenied: errorCode = E_ACCESSDENIED; break; case JET_errKeyDuplicate: errorCode = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); break; } } } errRecords->Release(); } errInfo->Release(); } return errorCode; }