Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

514 lines
13 KiB

  1. ///////////////////////////////////////////////////////////////////////////////
  2. //
  3. // Copyright (c) 2000, Microsoft Corp. All rights reserved.
  4. //
  5. // FILE
  6. //
  7. // iasdb.cpp
  8. //
  9. // SYNOPSIS
  10. //
  11. // Defines functions for accessing OLE-DB databases.
  12. //
  13. // MODIFICATION HISTORY
  14. //
  15. // 04/13/2000 Original version.
  16. //
  17. ///////////////////////////////////////////////////////////////////////////////
  18. #include <windows.h>
  19. #include <iasdb.h>
  20. #include <oledb.h>
  21. #include <msjetoledb.h>
  22. #include <atlbase.h>
  23. #include <iastrace.h>
  24. VOID
  25. WINAPI
  26. IASCreateTmpDirectory()
  27. {
  28. // JetInit fails if the TMP directory doesn't exist, so we'll try to
  29. // create it just in case.
  30. DWORD needed = GetEnvironmentVariableW(L"TMP", NULL, 0);
  31. if (needed)
  32. {
  33. PWCHAR buf = (PWCHAR)_alloca(needed * sizeof(WCHAR));
  34. DWORD actual = GetEnvironmentVariableW(L"TMP", buf, needed);
  35. if (actual > 0 && actual < needed)
  36. {
  37. CreateDirectoryW(buf, NULL);
  38. }
  39. }
  40. }
  41. HRESULT
  42. WINAPI
  43. IASCreateJetProvider(
  44. OUT IDBInitialize** provider
  45. ) throw ()
  46. {
  47. IASCreateTmpDirectory();
  48. // Convert the ProgID to a ClsID.
  49. CLSID clsid;
  50. HRESULT hr = CLSIDFromProgID(
  51. L"Microsoft.Jet.OLEDB.4.0",
  52. &clsid
  53. );
  54. if (SUCCEEDED(hr))
  55. {
  56. // Create the OLE DB provider.
  57. hr = CoCreateInstance(
  58. clsid,
  59. NULL,
  60. CLSCTX_INPROC_SERVER,
  61. __uuidof(IDBInitialize),
  62. (PVOID*)provider
  63. );
  64. }
  65. return hr;
  66. }
  67. HRESULT
  68. WINAPI
  69. IASOpenJetDatabase(
  70. IN PCWSTR path,
  71. IN BOOL readOnly,
  72. OUT LPUNKNOWN* session
  73. )
  74. {
  75. IASTracePrintf("INFO Enter IASOpenJetDatabase. path = %S readonly = %d",
  76. path, readOnly);
  77. // Initialize the out parameter.
  78. if (session == NULL) { return E_POINTER; }
  79. *session = NULL;
  80. HRESULT hr;
  81. do
  82. {
  83. // Create the OLE DB provider.
  84. CComPtr<IDBInitialize> connection;
  85. hr = IASCreateJetProvider(&connection);
  86. if (FAILED(hr)) { break; }
  87. //////////
  88. // Set the properties for the data source.
  89. //////////
  90. CComPtr<IDBProperties> properties;
  91. hr = connection->QueryInterface(
  92. __uuidof(IDBProperties),
  93. (PVOID*)&properties
  94. );
  95. if (FAILED(hr)) { break; }
  96. CComBSTR bstrPath = SysAllocString(path);
  97. if (!bstrPath)
  98. {
  99. hr = E_OUTOFMEMORY;
  100. break;
  101. }
  102. DBPROP dbprop[2];
  103. dbprop[0].dwPropertyID = DBPROP_INIT_MODE;
  104. dbprop[0].dwOptions = DBPROPOPTIONS_REQUIRED;
  105. dbprop[0].colid = DB_NULLID;
  106. dbprop[0].vValue.vt = VT_I4;
  107. dbprop[0].vValue.lVal = readOnly ? DB_MODE_READ : DB_MODE_READWRITE;
  108. dbprop[1].dwPropertyID = DBPROP_INIT_DATASOURCE;
  109. dbprop[1].dwOptions = DBPROPOPTIONS_REQUIRED;
  110. dbprop[1].colid = DB_NULLID;
  111. dbprop[1].vValue.vt = VT_BSTR;
  112. dbprop[1].vValue.bstrVal = bstrPath;
  113. DBPROPSET dbpropSet;
  114. dbpropSet.guidPropertySet = DBPROPSET_DBINIT;
  115. dbpropSet.cProperties = 2;
  116. dbpropSet.rgProperties = dbprop;
  117. hr = properties->SetProperties(1, &dbpropSet);
  118. if (FAILED(hr))
  119. {
  120. hr = IASTraceJetError("IDBProperties::SetProperties", hr);
  121. break;
  122. }
  123. //////////
  124. // Set the Jet specific properties for the data source.
  125. //////////
  126. dbprop[0].dwPropertyID = DBPROP_JETOLEDB_DATABASELOCKMODE;
  127. dbprop[0].dwOptions = DBPROPOPTIONS_REQUIRED;
  128. dbprop[0].colid = DB_NULLID;
  129. dbprop[0].vValue.vt = VT_I4;
  130. dbprop[0].vValue.lVal = (LONG)DBPROPVAL_DL_OLDMODE;
  131. dbpropSet.guidPropertySet = DBPROPSET_JETOLEDB_DBINIT;
  132. dbpropSet.cProperties = 1;
  133. dbpropSet.rgProperties = dbprop;
  134. hr = properties->SetProperties(1, &dbpropSet);
  135. if (FAILED(hr))
  136. {
  137. hr = IASTraceJetError("IDBProperties::SetProperties", hr);
  138. break;
  139. }
  140. //////////
  141. // Initialize the connection.
  142. //////////
  143. // This is a bit of a hack. The right approach would be to either (1) always
  144. // connect to the local database and use DCOM handle the remoting or (2)
  145. // always impersonate the client. Unfortunately, both of these would require
  146. // considerable changes to the existing client code. Instead, we'll only
  147. // impersonate the client when running under the local system account AND
  148. // opening a remote database. We know this will always fail if we don't
  149. // impersonate, so we can't lose anything by attempting to impersonate.
  150. bool revert = false;
  151. if ( !IASIsInprocServer() &&
  152. path[0] == L'\\' &&
  153. path[1] == L'\\' )
  154. {
  155. HRESULT hr = CoImpersonateClient();
  156. if ( FAILED(hr) )
  157. {
  158. IASTraceFailure("CoImpersonateClient", hr);
  159. break;
  160. }
  161. else
  162. {
  163. revert = true;
  164. }
  165. }
  166. hr = connection->Initialize();
  167. if (revert) { CoRevertToSelf(); }
  168. if (FAILED(hr))
  169. {
  170. hr = IASTraceJetError("IDBInitialize::Initialize", hr);
  171. break;
  172. }
  173. //////////
  174. // Create a session.
  175. //////////
  176. CComPtr<IDBCreateSession> creator;
  177. hr = connection->QueryInterface(
  178. __uuidof(IDBCreateSession),
  179. (PVOID*)&creator
  180. );
  181. if (FAILED(hr)) { break; }
  182. hr = creator->CreateSession(
  183. NULL,
  184. __uuidof(IUnknown),
  185. session
  186. );
  187. if (FAILED(hr))
  188. {
  189. hr = IASTraceJetError("IDBCreateSession::CreateSession", hr);
  190. break;
  191. }
  192. }
  193. while(false);
  194. IASTracePrintf("INFO Leave IASOpenJetDatabase. hr = 0x%08X", hr);
  195. return hr;
  196. }
  197. HRESULT
  198. WINAPI
  199. IASExecuteSQLCommand(
  200. IN LPUNKNOWN session,
  201. IN PCWSTR commandText,
  202. OUT IRowset** result
  203. )
  204. {
  205. IASTracePrintf("INFO IASExecuteSQLCommand. Command = %S", commandText);
  206. // Initialize the out parameter.
  207. if (result) { *result = NULL; }
  208. HRESULT hr;
  209. CComPtr<IDBCreateCommand> creator;
  210. hr = session->QueryInterface(
  211. __uuidof(IDBCreateCommand),
  212. (PVOID*)&creator
  213. );
  214. if (FAILED(hr)) { return hr; }
  215. CComPtr<ICommandText> command;
  216. hr = creator->CreateCommand(
  217. NULL,
  218. __uuidof(ICommandText),
  219. (IUnknown**)&command
  220. );
  221. if (FAILED(hr))
  222. {
  223. return IASTraceJetError("IDBCreateCommand::CreateCommand", hr);
  224. }
  225. hr = command->SetCommandText(
  226. DBGUID_DBSQL,
  227. commandText
  228. );
  229. if (FAILED(hr))
  230. {
  231. return IASTraceJetError("ICommandText::SetCommandText", hr);
  232. }
  233. hr = command->Execute(
  234. NULL,
  235. (result ? __uuidof(IRowset) : IID_NULL),
  236. NULL,
  237. NULL,
  238. (IUnknown**)result
  239. );
  240. if (FAILED(hr))
  241. {
  242. return IASTraceJetError("ICommand::Execute", hr);
  243. }
  244. return S_OK;
  245. }
  246. HRESULT
  247. WINAPI
  248. IASExecuteSQLFunction(
  249. IN LPUNKNOWN session,
  250. IN PCWSTR functionText,
  251. OUT LONG* result
  252. )
  253. {
  254. // Initialize the out parameter.
  255. if (result == NULL) { return E_POINTER; }
  256. *result = 0;
  257. // Execute the function.
  258. HRESULT hr;
  259. CComPtr<IRowset> rowset;
  260. hr = IASExecuteSQLCommand(
  261. session,
  262. functionText,
  263. &rowset
  264. );
  265. if (FAILED(hr)) { return hr; }
  266. CComPtr<IAccessor> accessor;
  267. hr = rowset->QueryInterface(
  268. __uuidof(IAccessor),
  269. (PVOID*)&accessor
  270. );
  271. if (FAILED(hr)) { return hr; }
  272. // Get the result row.
  273. DBCOUNTITEM numRows;
  274. HROW hRow;
  275. HROW* pRow = &hRow;
  276. hr = rowset->GetNextRows(
  277. NULL,
  278. 0,
  279. 1,
  280. &numRows,
  281. &pRow
  282. );
  283. if (FAILED(hr))
  284. {
  285. return IASTraceJetError("IRowset::GetNextRows", hr);
  286. }
  287. if (numRows == 0) { return E_FAIL; }
  288. /////////
  289. // Create an accessor.
  290. /////////
  291. DBBINDING bind;
  292. memset(&bind, 0, sizeof(bind));
  293. bind.iOrdinal = 1;
  294. bind.dwPart = DBPART_VALUE;
  295. bind.wType = DBTYPE_I4;
  296. bind.cbMaxLen = 4;
  297. HACCESSOR hAccess;
  298. hr = accessor->CreateAccessor(
  299. DBACCESSOR_ROWDATA,
  300. 1,
  301. &bind,
  302. 4,
  303. &hAccess,
  304. NULL
  305. );
  306. if (SUCCEEDED(hr))
  307. {
  308. // Get the data.
  309. hr = rowset->GetData(
  310. hRow,
  311. hAccess,
  312. result
  313. );
  314. if (FAILED(hr))
  315. {
  316. hr = IASTraceJetError("IRowset::GetData", hr);
  317. }
  318. accessor->ReleaseAccessor(hAccess, NULL);
  319. }
  320. else
  321. {
  322. hr = IASTraceJetError("IAccessor::CreateAccessor", hr);
  323. }
  324. rowset->ReleaseRows(1, &hRow, NULL, NULL, NULL);
  325. return hr;
  326. }
  327. //////////////////////////////////////////////////////////////////////////////
  328. // IASCreateDatabase
  329. //////////////////////////////////////////////////////////////////////////////
  330. HRESULT
  331. WINAPI
  332. IASCreateJetDatabase(
  333. IN PCWSTR dataSource
  334. )
  335. {
  336. // Create the OLE DB provider.
  337. CComPtr<IDBInitialize> connection;
  338. HRESULT hr = IASCreateJetProvider(&connection);
  339. if (FAILED(hr)) { return hr; }
  340. //////////
  341. // Set the properties for the data source.
  342. //////////
  343. CComPtr<IDBProperties> properties;
  344. hr = connection->QueryInterface(
  345. __uuidof(IDBProperties),
  346. (PVOID*)&properties
  347. );
  348. if (FAILED(hr)) { return hr; }
  349. CComBSTR bstrDataSource(dataSource);
  350. if ( !bstrDataSource) { return E_OUTOFMEMORY; }
  351. DBPROP dbprop[2];
  352. dbprop[0].dwPropertyID = DBPROP_INIT_DATASOURCE;
  353. dbprop[0].dwOptions = DBPROPOPTIONS_REQUIRED;
  354. dbprop[0].colid = DB_NULLID;
  355. dbprop[0].vValue.vt = VT_BSTR;
  356. dbprop[0].vValue.bstrVal = bstrDataSource;
  357. dbprop[1].dwPropertyID = DBPROP_INIT_LOCATION;
  358. dbprop[1].dwOptions = DBPROPOPTIONS_OPTIONAL;
  359. dbprop[1].colid = DB_NULLID;
  360. dbprop[1].vValue.vt = VT_BSTR;
  361. dbprop[1].vValue.lVal = VT_NULL;
  362. DBPROPSET dbpropSet;
  363. dbpropSet.guidPropertySet = DBPROPSET_DBINIT;
  364. dbpropSet.cProperties = 2;
  365. dbpropSet.rgProperties = dbprop;
  366. hr = properties->SetProperties(1, &dbpropSet);
  367. if (FAILED(hr))
  368. {
  369. return IASTraceJetError("IDBProperties::SetProperties", hr);
  370. }
  371. // Create the Data Source
  372. CComPtr<IDBDataSourceAdmin> admin;
  373. hr = connection->QueryInterface(
  374. __uuidof(IDBDataSourceAdmin),
  375. (PVOID*)&admin
  376. );
  377. if (FAILED(hr)) { return hr; }
  378. hr = admin->CreateDataSource(
  379. 1,
  380. &dbpropSet,
  381. NULL,
  382. __uuidof(IDBCreateSession),
  383. NULL
  384. );
  385. if (FAILED(hr))
  386. {
  387. return IASTraceJetError("IDBDataSourceAdmin::CreateDataSource", hr);
  388. }
  389. return S_OK;
  390. }
  391. // Internal Jet error codes from msjeterr.h
  392. #define JET_errFileAccessDenied -1032
  393. #define JET_errKeyDuplicate -1605
  394. HRESULT
  395. WINAPI
  396. IASTraceJetError(
  397. PCSTR functionName,
  398. HRESULT errorCode
  399. )
  400. {
  401. IASTracePrintf("%s failed; return value = 0x%08X", functionName, errorCode);
  402. IErrorInfo* errInfo;
  403. if (GetErrorInfo(0, &errInfo) == S_OK)
  404. {
  405. BSTR description;
  406. if (SUCCEEDED(errInfo->GetDescription(&description)))
  407. {
  408. IASTracePrintf("\tDescription: %S", description);
  409. SysFreeString(description);
  410. }
  411. IErrorRecords* errRecords;
  412. if (SUCCEEDED(errInfo->QueryInterface(
  413. __uuidof(IErrorRecords),
  414. (PVOID*)&errRecords
  415. )))
  416. {
  417. ULONG numRecords = 0;
  418. errRecords->GetRecordCount(&numRecords);
  419. for (ULONG i = 0; i < numRecords; ++i)
  420. {
  421. ERRORINFO info;
  422. if (SUCCEEDED(errRecords->GetBasicErrorInfo(i, &info)))
  423. {
  424. IASTracePrintf(
  425. "\tRecord %lu: hrError = 0x%08X; dwMinor = 0x%08X",
  426. i, info.hrError, info.dwMinor
  427. );
  428. // Jolt does a poor job of mapping Jet error codes to HRESULTs,
  429. // so we handle a few ourselves. The Jet error code is the low
  430. // order 16 bits treated as a signed integer.
  431. switch ((WORD)info.dwMinor)
  432. {
  433. case JET_errFileAccessDenied:
  434. errorCode = E_ACCESSDENIED;
  435. break;
  436. case JET_errKeyDuplicate:
  437. errorCode = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS);
  438. break;
  439. }
  440. }
  441. }
  442. errRecords->Release();
  443. }
  444. errInfo->Release();
  445. }
  446. return errorCode;
  447. }