windows-nt/Source/XPSP1/NT/net/ias/iasjet/dll/iasdb.cpp
2020-09-26 16:20:57 +08:00

515 lines
13 KiB
C++

///////////////////////////////////////////////////////////////////////////////
//
// 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 <windows.h>
#include <iasdb.h>
#include <oledb.h>
#include <msjetoledb.h>
#include <atlbase.h>
#include <iastrace.h>
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<IDBInitialize> connection;
hr = IASCreateJetProvider(&connection);
if (FAILED(hr)) { break; }
//////////
// Set the properties for the data source.
//////////
CComPtr<IDBProperties> 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<IDBCreateSession> 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<IDBCreateCommand> creator;
hr = session->QueryInterface(
__uuidof(IDBCreateCommand),
(PVOID*)&creator
);
if (FAILED(hr)) { return hr; }
CComPtr<ICommandText> 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<IRowset> rowset;
hr = IASExecuteSQLCommand(
session,
functionText,
&rowset
);
if (FAILED(hr)) { return hr; }
CComPtr<IAccessor> 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<IDBInitialize> connection;
HRESULT hr = IASCreateJetProvider(&connection);
if (FAILED(hr)) { return hr; }
//////////
// Set the properties for the data source.
//////////
CComPtr<IDBProperties> 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<IDBDataSourceAdmin> 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;
}