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.

681 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. //
  5. // Copyright (C) Microsoft Corporation, 1998 - 1998
  6. //
  7. // File: sdowrap.cpp
  8. //
  9. //--------------------------------------------------------------------------
  10. #include "stdafx.h"
  11. #include "rasdial.h"
  12. #include "sdowrap.h"
  13. #include "profsht.h"
  14. //========================================
  15. //
  16. // Open profile UI API
  17. //
  18. /*
  19. // CSdoServerWrapper class implementation
  20. HRESULT CSdoServerWrapper::ConnectServer(BSTR machineName, BSTR user, BSTR passwd, ULONG retrivetype)
  21. {
  22. HRESULT hr = S_OK;
  23. CComPtr<ISdo> spSdo;
  24. VARIANT var;
  25. VariantInit(&var);
  26. if(m_spSdoServer.p) // disconnect it if we already connected to some other
  27. {
  28. m_spDic.Release();
  29. #ifdef _USES_OLD_SDO
  30. m_spSdoServer->Disconnect();
  31. #endif
  32. m_spSdoServer.Release();
  33. }
  34. TRACE(_T("CoCreateInstance SdoServer\r\n"));
  35. CHECK_HR(hr = CoCreateInstance( CLSID_SdoServer, NULL, CLSCTX_INPROC_SERVER,
  36. IID_ISdoServer, (void**)&m_spSdoServer));
  37. ASSERT(m_spSdoServer.p);
  38. #ifdef _USES_OLD_SDO
  39. // connect
  40. TRACE(_T("SdoServer::Connect\r\n"));
  41. CHECK_HR(hr = m_spSdoServer->Connect(machineName, user, passwd, retrivetype));
  42. // get dictionary
  43. CHECK_HR(hr = m_spSdoServer->QueryInterface(IID_ISdo, (void**)&spSdo));
  44. TRACE(_T("SdoServer::GetDictionary \r\n"));
  45. VariantClear(&var);
  46. CHECK_HR(hr = spSdo->GetProperty(PROPERTY_SERVER_DICTIONARY, &var));
  47. ASSERT(V_VT(&var) & VT_DISPATCH);
  48. CHECK_HR(hr = V_DISPATCH(&var)->QueryInterface(IID_ISdoDictionary, (void**)&m_spDic));
  49. ASSERT(m_spDic.p);
  50. TRACE(_T("SdoServer::Done -- Connected \r\n"));
  51. #else
  52. // attach
  53. TRACE(_T("SdoServer::Attach\r\n"));
  54. CHECK_HR(hr = m_spSdoServer->Attach(machineName));
  55. TRACE(_T("SdoServer::Attach -- OK \r\n"));
  56. #endif
  57. m_bConnected = TRUE;
  58. L_ERR:
  59. VariantClear(&var);
  60. return hr;
  61. }
  62. HRESULT CSdoServerWrapper::GetUserSdo(IASUSERSTORE eUserStore,BSTR bstrUserName,ISdo** ppUserSdo)
  63. {
  64. HRESULT hr = S_OK;
  65. CComPtr<IUnknown> spUnk;
  66. ASSERT(ppUserSdo);
  67. if(!ppUserSdo)
  68. return E_INVALIDARG;
  69. ASSERT(m_spSdoServer.p); // connect function need to be called before this
  70. if(!m_spSdoServer.p)
  71. return E_FAIL;
  72. TRACE(_T("SdoServer::GetUserSdo\r\n"));
  73. CHECK_HR(hr = m_spSdoServer->GetUserSDO( eUserStore, bstrUserName, &spUnk));
  74. ASSERT(spUnk.p);
  75. CHECK_HR(hr = spUnk->QueryInterface(IID_ISdo, (void**)ppUserSdo));
  76. ASSERT(*ppUserSdo);
  77. TRACE(_T("SdoServer::GetUserSdo --- DONE\r\n"));
  78. L_ERR:
  79. return hr;
  80. }
  81. */
  82. //========================================
  83. //
  84. // CSdoWrapper Class Implementation
  85. //
  86. CSdoWrapper::~CSdoWrapper()
  87. {
  88. // clear the map
  89. POSITION pos = m_mapProperties.GetStartPosition();
  90. ULONG id;
  91. ISdo* pSdo = NULL;
  92. while(pos)
  93. {
  94. pSdo = NULL;
  95. m_mapProperties.GetNextAssoc(pos, id, pSdo);
  96. if(pSdo)
  97. pSdo->Release();
  98. }
  99. m_mapProperties.RemoveAll();
  100. }
  101. // Initialize the map of the attribute collection object
  102. HRESULT CSdoWrapper::Init(ULONG collectionId, ISdo* pISdo, ISdoDictionaryOld* pIDic)
  103. {
  104. HRESULT hr = S_OK;
  105. VARIANT var;
  106. VARIANT* pVar = NULL;
  107. CComPtr<IEnumVARIANT> spEnum;
  108. CComPtr<IUnknown> spIUnk;
  109. ULONG count = 0;
  110. TRACE(_T("Enter CSdoWrapper::Init\r\n"));
  111. VariantInit(&var);
  112. // it must be new
  113. ASSERT(!m_spISdoCollection.p);
  114. ASSERT(!m_spIDictionary.p);
  115. ASSERT(!m_spISdo.p);
  116. // must be valid
  117. ASSERT(pISdo && pIDic);
  118. m_spISdo = pISdo;
  119. CHECK_HR(hr = pISdo->GetProperty(collectionId, &var));
  120. ASSERT(V_VT(&var) & VT_DISPATCH);
  121. CHECK_HR(hr = V_DISPATCH(&var)->QueryInterface(IID_ISdoCollection, (void**)&m_spISdoCollection));
  122. ASSERT(m_spISdoCollection.p);
  123. m_spIDictionary = pIDic;
  124. // prepare the existing property ( in the collection) to map
  125. CHECK_HR(hr = m_spISdoCollection->get__NewEnum((IUnknown**)&spIUnk));
  126. CHECK_HR(hr = spIUnk->QueryInterface(IID_IEnumVARIANT, (void**)&spEnum));
  127. // get the list of variant
  128. CHECK_HR(hr = m_spISdoCollection->get_Count((long*)&count));
  129. if(count > 0)
  130. {
  131. try
  132. {
  133. pVar = new VARIANT[count];
  134. for(ULONG i = 0; i < count; i++)
  135. VariantInit(pVar + i);
  136. if(!pVar)
  137. {
  138. CHECK_HR(hr = E_OUTOFMEMORY);
  139. }
  140. CHECK_HR(hr = spEnum->Reset());
  141. CHECK_HR(hr = spEnum->Next(count, pVar, &count));
  142. // prepare the map
  143. {
  144. ISdo* pISdo = NULL;
  145. ULONG id;
  146. VARIANT var;
  147. VariantInit(&var);
  148. for(ULONG i = 0; i < count; i++)
  149. {
  150. CHECK_HR(hr = V_DISPATCH(pVar + i)->QueryInterface(IID_ISdo, (void**)&pISdo));
  151. CHECK_HR(hr = pISdo->GetProperty(PROPERTY_ATTRIBUTE_ID, &var));
  152. ASSERT(V_VT(&var) == VT_I4);
  153. m_mapProperties[V_I4(&var)] = pISdo;
  154. pISdo->AddRef();
  155. }
  156. }
  157. }
  158. catch(CMemoryException&)
  159. {
  160. pVar = NULL;
  161. CHECK_HR(hr = E_OUTOFMEMORY);
  162. }
  163. }
  164. L_ERR:
  165. delete[] pVar;
  166. VariantClear(&var);
  167. TRACE(_T("Leave CSdoWrapper::Init\r\n"));
  168. return hr;
  169. }
  170. // set a property based on ID
  171. HRESULT CSdoWrapper::PutProperty(ULONG id, VARIANT* pVar)
  172. {
  173. ASSERT(m_spISdoCollection.p);
  174. ASSERT(m_spIDictionary.p);
  175. ISdo* pProp = NULL;
  176. IDispatch* pDisp = NULL;
  177. HRESULT hr = S_OK;
  178. int ref = 0;
  179. TracePrintf(g_dwTraceHandle, _T("PutProperty %d"), id);
  180. if(!m_mapProperties.Lookup(id, pProp)) // no ref change to pProp
  181. {
  182. TracePrintf(g_dwTraceHandle, _T(" IDictionary::CreateAttribute %d"), id);
  183. CHECK_HR(hr = m_spIDictionary->CreateAttribute((ATTRIBUTEID)id, &pDisp));
  184. TracePrintf(g_dwTraceHandle, _T(" hr = %8x\r\n"), hr);
  185. ASSERT(pDisp);
  186. // since pDisp is both in, out parameter, we assume the Ref is added within the function call
  187. TracePrintf(g_dwTraceHandle, _T(" ISdoCollection::Add %x"), pDisp);
  188. CHECK_HR(hr = m_spISdoCollection->Add(NULL, (IDispatch**)&pDisp)); // pDisp AddRef
  189. TracePrintf(g_dwTraceHandle, _T(" hr = %8x\r\n"), hr);
  190. //
  191. ASSERT(pDisp);
  192. CHECK_HR(hr = pDisp->QueryInterface(IID_ISdo, (void**)&pProp)); // one ref add
  193. ASSERT(pProp);
  194. // after we have the pProp, the pDisp can be released
  195. pDisp->Release();
  196. // add to the wrapper's map
  197. m_mapProperties[id] = pProp; // no need to addref again, since there is one already
  198. }
  199. TracePrintf(g_dwTraceHandle, _T(" ISdo::PutProperty PROPERTY_ATTRIBUTE_VALUE %x"), pVar);
  200. CHECK_HR(hr = pProp->PutProperty(PROPERTY_ATTRIBUTE_VALUE, pVar));
  201. TracePrintf(g_dwTraceHandle, _T(" hr = %8x\r\n"), hr);
  202. // for debug, ensure each attribute can be commited
  203. #ifdef WEI_SPECIAL_DEBUG
  204. ASSERT(S_OK == Commit(TRUE));
  205. #endif
  206. L_ERR:
  207. TracePrintf(g_dwTraceHandle, _T("hr = %8x\r\n"), hr);
  208. return hr;
  209. }
  210. // get property based on ID
  211. HRESULT CSdoWrapper::GetProperty(ULONG id, VARIANT* pVar)
  212. {
  213. ISdo* pProp;
  214. HRESULT hr = S_OK;
  215. TRACE(_T("Enter CSdoWrapper::GetProperty %d\r\n"), id);
  216. if(m_mapProperties.Lookup(id, pProp)) // no ref change to pProp
  217. {
  218. ASSERT(pProp);
  219. CHECK_HR(hr = pProp->GetProperty(PROPERTY_ATTRIBUTE_VALUE, pVar));
  220. }
  221. else
  222. {
  223. V_VT(pVar) = VT_ERROR;
  224. V_ERROR(pVar) = DISP_E_PARAMNOTFOUND;
  225. }
  226. L_ERR:
  227. TRACE(_T("Leave CSdoWrapper::GetProperty %d\r\n"), id);
  228. return hr;
  229. }
  230. // remove a property based on ID
  231. HRESULT CSdoWrapper::RemoveProperty(ULONG id)
  232. {
  233. ASSERT(m_spISdoCollection.p);
  234. ISdo* pProp;
  235. HRESULT hr = S_OK;
  236. TracePrintf(g_dwTraceHandle, _T("RemoveProperty %d"), id);
  237. if(m_mapProperties.Lookup(id, pProp)) // no ref change to pProp
  238. {
  239. ASSERT(pProp);
  240. CHECK_HR(hr = m_spISdoCollection->Remove((IDispatch*)pProp));
  241. m_mapProperties.RemoveKey(id);
  242. pProp->Release();
  243. // for debug, ensure each attribute can be commited
  244. ASSERT(S_OK == Commit(TRUE));
  245. }
  246. else
  247. hr = S_FALSE;
  248. L_ERR:
  249. TracePrintf(g_dwTraceHandle, _T("hr = %8x\r\n"), hr);
  250. return hr;
  251. }
  252. // commit changes to the properties
  253. HRESULT CSdoWrapper::Commit(BOOL bCommit)
  254. {
  255. HRESULT hr = S_OK;
  256. TracePrintf(g_dwTraceHandle, _T("Commit %d"), bCommit);
  257. if(bCommit)
  258. {
  259. CHECK_HR(hr = m_spISdo->Apply());
  260. }
  261. else
  262. {
  263. CHECK_HR(hr = m_spISdo->Restore());
  264. }
  265. L_ERR:
  266. TracePrintf(g_dwTraceHandle, _T("hr = %8x\r\n"), hr);
  267. return hr;
  268. }
  269. //========================================
  270. //
  271. // CSdoUserWrapper Class Implementation
  272. //
  273. // set a property based on ID
  274. HRESULT CUserSdoWrapper::PutProperty(ULONG id, VARIANT* pVar)
  275. {
  276. ASSERT(m_spISdo.p);
  277. HRESULT hr = S_OK;
  278. TracePrintf(g_dwTraceHandle, _T("PutProperty %d"), id);
  279. hr = m_spISdo->PutProperty(id, pVar);
  280. TracePrintf(g_dwTraceHandle, _T("hr = %8x\r\n"), hr);
  281. return hr;
  282. }
  283. // get property based on ID
  284. HRESULT CUserSdoWrapper::GetProperty(ULONG id, VARIANT* pVar)
  285. {
  286. HRESULT hr = S_OK;
  287. TracePrintf(g_dwTraceHandle, _T("PutProperty %d"), id);
  288. hr = m_spISdo->GetProperty(id, pVar);
  289. TracePrintf(g_dwTraceHandle, _T("hr = %8x\r\n"), hr);
  290. return hr;
  291. }
  292. // remove a property based on ID
  293. HRESULT CUserSdoWrapper::RemoveProperty(ULONG id)
  294. {
  295. HRESULT hr = S_OK;
  296. VARIANT v;
  297. VariantInit(&v);
  298. V_VT(&v) = VT_EMPTY;
  299. TracePrintf(g_dwTraceHandle, _T("RemoveProperty %d"), id);
  300. hr = m_spISdo->PutProperty(id, &v);
  301. TracePrintf(g_dwTraceHandle, _T("hr = %8x\r\n"), hr);
  302. return hr;
  303. }
  304. // commit changes to the properties
  305. HRESULT CUserSdoWrapper::Commit(BOOL bCommit)
  306. {
  307. HRESULT hr = S_OK;
  308. TracePrintf(g_dwTraceHandle, _T("Commit %d"), bCommit);
  309. if(bCommit)
  310. {
  311. CHECK_HR(hr = m_spISdo->Apply());
  312. }
  313. else
  314. {
  315. CHECK_HR(hr = m_spISdo->Restore());
  316. }
  317. L_ERR:
  318. TracePrintf(g_dwTraceHandle, _T("hr = %8x\r\n"), hr);
  319. return hr;
  320. }
  321. /*
  322. //====================================================================
  323. // APIs to wrap SDO
  324. //
  325. DllExport HRESULT OpenSdoSrvObj(
  326. BSTR machinename,
  327. BSTR user,
  328. BSTR passwd,
  329. ULONG retrivetype,
  330. HSdoSrvObj* pHandle) // for SdoServer
  331. // usertype: IAS_USER_STORE_LOCAL_SAM or IAS_USER_STORE_ACTIVE_DIRECTORY
  332. // retriveType: RETRIEVE_SERVER_DATA_FROM_DS when the data is in the DS, otherwise, 0
  333. // returns S_OK, or error message from SDO
  334. {
  335. ASSERT(pHandle);
  336. if(!pHandle) return E_INVALIDARG;
  337. *pHandle = NULL;
  338. CSdoServerWrapper* pSdoSrv = new CSdoServerWrapper;
  339. if(!pSdoSrv) return E_OUTOFMEMORY;
  340. HRESULT hr = pSdoSrv->ConnectServer(machinename, user, passwd, retrivetype);
  341. if(S_OK == hr)
  342. {
  343. // assign value into out parameter
  344. *pHandle = (HSdoObj)pSdoSrv;
  345. }
  346. else
  347. delete pSdoSrv;
  348. return hr;
  349. }
  350. DllExport HRESULT CloseSdoSrvObj(HSdoSrvObj hSdoSrv)
  351. // release the memory of the server handle
  352. {
  353. CSdoServerWrapper* pSdoSrv = (CSdoServerWrapper*)hSdoSrv;
  354. delete pSdoSrv;
  355. return S_OK;
  356. }
  357. DllExport HRESULT OpenDailinUsrSdoObj(
  358. HSdoSrvObj hSdoSrv,
  359. BSTR username,
  360. IASUSERSTORE usertype,
  361. HSdoObj* pHandle) // for user
  362. // usertype: IAS_USER_STORE_LOCAL_SAM or IAS_USER_STORE_ACTIVE_DIRECTORY
  363. // returns S_OK, or error message from SDO
  364. {
  365. CSdoServerWrapper* pSdoSrv = (CSdoServerWrapper*)hSdoSrv;
  366. CComPtr<ISdo> spSdo;
  367. HRESULT hr = S_OK;
  368. CSdoWrapper* pSdoWrapper = NULL;
  369. ASSERT(hSdoSrv && pHandle && (ISdo*)pSdoSrv);
  370. if(!pHandle || !pSdoSrv)
  371. return E_INVALIDARG;
  372. CHECK_HR(hr = pSdoSrv->GetUserSdo(usertype, username, &spSdo));
  373. pSdoWrapper = new CSdoWrapper;
  374. if(!pSdoWrapper) return E_OUTOFMEMORY;
  375. CHECK_HR(hr = pSdoWrapper->Init(PROPERTY_USER_ATTRIBUTES_COLLECTION, spSdo, (ISdoDictionary*)(*pSdoSrv)));
  376. L_ERR:
  377. if(FAILED(hr))
  378. delete pSdoWrapper;
  379. else
  380. *pHandle = pSdoWrapper;
  381. return hr;
  382. }
  383. #if 0 /// OLD code
  384. DllExport HRESULT OpenDailinUsrSdoObj(
  385. BSTR machinename,
  386. BSTR username,
  387. ULONG usertype,
  388. HSdoObj* pHandle) // for user
  389. // usertype: IAS_USER_STORE_LOCAL_SAM or IAS_USER_STORE_ACTIVE_DIRECTORY
  390. // returns S_OK, or error message from SDO
  391. {
  392. *pHandle = NULL;
  393. CSdoWrapper* pSdoWrapper = new CSdoWrapper;
  394. if(!pSdoWrapper) return E_OUTOFMEMORY;
  395. VARIANT var;
  396. HRESULT hr = S_OK;
  397. CComPtr<ISdo> spSdo;
  398. CComPtr<ISdo> spUserSdo;
  399. CComPtr<ISdoServer> spSdoServer;
  400. CComPtr<ISdoDictionary> spDic;
  401. CComPtr<IDispatch> spDisp;
  402. VariantInit(&var);
  403. TRACE(_T("CoCreateInstance SdoServer\r\n"));
  404. CHECK_HR(hr = CoCreateInstance( CLSID_SdoServer, NULL, CLSCTX_INPROC_SERVER,
  405. IID_ISdoServer, (void**)&spSdoServer));
  406. ASSERT(spSdoServer.p);
  407. // one more function call to SDOSERver to set machine information
  408. // Get the user SDO
  409. TRACE(_T("SdoServer::Connect\r\n"));
  410. switch(usertype){
  411. case IAS_USER_STORE_ACTIVE_DIRECTORY:
  412. CHECK_HR(hr = spSdoServer->Connect(NULL, NULL, NULL, RETRIEVE_SERVER_DATA_FROM_DS));
  413. TRACE(_T("SdoServer::GetUserObject -- Active Directory\r\n"));
  414. CHECK_HR(hr = spSdoServer->GetUserObject( IAS_USER_STORE_ACTIVE_DIRECTORY, username, &spDisp));
  415. ASSERT(spDisp.p);
  416. break;
  417. case IAS_USER_STORE_LOCAL_SAM:
  418. CHECK_HR(hr = spSdoServer->Connect(machinename, NULL, NULL, 0));
  419. TRACE(_T("SdoServer::GetUserObject -- Local User\r\n"));
  420. CHECK_HR(hr = spSdoServer->GetUserObject( IAS_USER_STORE_LOCAL_SAM, username, &spDisp));
  421. ASSERT(spDisp.p);
  422. break;
  423. default:
  424. ASSERT(0); // this is not defined
  425. break;
  426. }
  427. CHECK_HR(hr = spDisp->QueryInterface(IID_ISdo, (void**)&spUserSdo));
  428. ASSERT(spUserSdo.p);
  429. TRACE(_T("SdoServer::GetUserObject --- DONE\r\n"));
  430. TRACE(_T("SdoServer::GetDictionary \r\n"));
  431. // get dictionary
  432. CHECK_HR(hr = spSdoServer->QueryInterface(IID_ISdo, (void**)&spSdo));
  433. VariantClear(&var);
  434. spSdo->GetProperty(PROPERTY_SERVER_DICTIONARY, &var);
  435. ASSERT(V_VT(&var) & VT_DISPATCH);
  436. CHECK_HR(hr = V_DISPATCH(&var)->QueryInterface(IID_ISdoDictionary, (void**)&spDic));
  437. ASSERT(spDic.p);
  438. TRACE(_T("SdoServer::GetDictionary --- DONE\r\n"));
  439. hr = pSdoWrapper->Init(PROPERTY_USER_ATTRIBUTES_COLLECTION, (ISdo*)spUserSdo, (ISdoDictionary*)spDic);
  440. if(S_OK == hr)
  441. {
  442. // assign value into out parameter
  443. *pHandle = (HSdoObj)pSdoWrapper;
  444. }
  445. else
  446. {
  447. delete pSdoWrapper;
  448. }
  449. L_ERR:
  450. VariantClear(&var);
  451. return hr;
  452. }
  453. #endif // #if 0 // OLD code
  454. DllExport HRESULT OpenSdoObj(
  455. ISdo* pSdo,
  456. ISdoDictionary* pDic,
  457. HSdoObj * pHandle) // for profile
  458. // pSDO, pUsrHandle are expected to be non-NULL,
  459. // returns S_OK, or error message from SDO
  460. {
  461. ASSERT(pSdo && pDic);
  462. // assign value into out parameter
  463. *pHandle = NULL;
  464. if(!(pSdo && pDic)) return E_INVALIDARG;
  465. CSdoWrapper* pSdoWrapper = new CSdoWrapper;
  466. if(!pSdoWrapper) return E_OUTOFMEMORY;
  467. HRESULT hr = pSdoWrapper->Init(PROPERTY_USER_ATTRIBUTES_COLLECTION, pSdo, pDic);
  468. if(S_OK == hr)
  469. {
  470. // assign value into out parameter
  471. *pHandle = (HSdoObj)pSdoWrapper;
  472. }
  473. else
  474. delete pSdoWrapper;
  475. return hr;
  476. }
  477. DllExport HRESULT CloseSdoObj(HSdoObj Handle)
  478. // UsrHandle is expected to be non-NULL
  479. {
  480. CSdoWrapper* pWrapper = (CSdoWrapper*)Handle;
  481. delete pWrapper;
  482. return S_OK;
  483. }
  484. DllExport HRESULT GetSdoAttr(
  485. HSdoObj Handle,
  486. ULONG id,
  487. VARIANT* pVar)
  488. // when attribute is absent,
  489. // V_VT(pVar) = VT_ERROR;
  490. // V_ERROR(pVar) = DISP_E_PARAMNOTFOUND;
  491. // returns S_OK or error message from SDO
  492. {
  493. CSdoWrapper* pWrapper = (CSdoWrapper*)Handle;
  494. return pWrapper->GetProperty(id, pVar);
  495. }
  496. DllExport HRESULT PutSdoAttr(
  497. HSdoObj Handle,
  498. ULONG id,
  499. VARIANT* pVar)
  500. // returns S_OK or error message from SDO
  501. {
  502. CSdoWrapper* pWrapper = (CSdoWrapper*)Handle;
  503. return pWrapper->PutProperty(id, pVar);
  504. }
  505. DllExport HRESULT RemoveSdoAttr(
  506. HSdoObj Handle,
  507. ULONG id)
  508. // returns S_OK or error message from SDO
  509. {
  510. CSdoWrapper* pWrapper = (CSdoWrapper*)Handle;
  511. return pWrapper->RemoveProperty(id);
  512. }
  513. DllExport HRESULT CommitSdoObj(
  514. HSdoObj Handle,
  515. BOOL bCommitChanges)
  516. // bCommitChanges -- TRUE, all changes are saved, FALSE restore to previous commit
  517. // returns S_OK or error message from SDO
  518. {
  519. CSdoWrapper* pWrapper = (CSdoWrapper*)Handle;
  520. return pWrapper->Commit(bCommitChanges);
  521. }
  522. */