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.

1367 lines
43 KiB

  1. //+-------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1996 - 2000.
  5. //
  6. // File: svcproxy.cxx
  7. //
  8. // Contents: Proxy to cisvc encapsulating all the context for a
  9. // running query, including the query execution context, the
  10. // cached query results, and all cursors over the results.
  11. //
  12. // Classes: CSvcQueryProxy
  13. //
  14. // History: 13 Sept 96 dlee created (mostly copied) from queryprx.cxx
  15. // 22 Aug 99 KLam Win64->Win32 support
  16. //
  17. //--------------------------------------------------------------------------
  18. #include <pch.cxx>
  19. #pragma hdrstop
  20. #include <query.hxx>
  21. #include <pickle.hxx>
  22. #include <memser.hxx>
  23. #include <sizeser.hxx>
  24. #include <propvar.h>
  25. #include <proxymsg.hxx>
  26. #include <tblvarnt.hxx>
  27. #include <tgrow.hxx>
  28. #include <pmalloc.hxx>
  29. #include "tabledbg.hxx"
  30. #include "rowseek.hxx"
  31. //+---------------------------------------------------------------------------
  32. //
  33. // Member: CSvcQueryProxy::CSvcQueryProxy, public
  34. //
  35. // Synopsis: Creates a locally accessible Query
  36. //
  37. // Arguments: [client] - Proxy for talking to remote process
  38. // [cols] - Columns that may be bound to
  39. // [rst] - Query restriction
  40. // [pso] - Sort order of the query
  41. // [pcateg] - Categorization specification
  42. // [RstProp] - Rowset properties for rowset(s) created
  43. // [pidmap] - Property ID mapper
  44. // [cCursors] - count of cursors expected to be created
  45. // [aCursors] - returns handles to cursors created
  46. //
  47. // History: 13 Sept 96 dlee created
  48. //
  49. //----------------------------------------------------------------------------
  50. CSvcQueryProxy::CSvcQueryProxy(
  51. CRequestClient & client,
  52. CColumnSet const & cols,
  53. CRestriction const & rst,
  54. CSortSet const * pso,
  55. CCategorizationSet const * pcateg,
  56. CRowsetProperties const & RstProp,
  57. CPidMapper const & pidmap ,
  58. ULONG cCursors,
  59. ULONG * aCursors )
  60. : _ref( 0 ),
  61. _client( client ),
  62. _fTrueSequential( FALSE ),
  63. _fWorkIdUnique( FALSE ),
  64. _xQuery( ),
  65. _xBindings( )
  66. {
  67. tbDebugOut(( DEB_PROXY, "CSvcQueryProxy\n" ));
  68. // DSO property IDs change with version 5
  69. if ( _client.GetServerVersion() < 5 )
  70. THROW( CException( STATUS_INVALID_PARAMETER_MIX ) );
  71. _aCursors.Init( cCursors );
  72. ULONG cbIn = PickledSize( _client.GetServerVersion(),
  73. &cols,
  74. &rst,
  75. pso,
  76. pcateg,
  77. &RstProp,
  78. &pidmap );
  79. cbIn = AlignBlock( cbIn, sizeof ULONG );
  80. XArray<BYTE> xQuery( cbIn + sizeof CPMCreateQueryIn );
  81. BYTE * pbPickledQuery = xQuery.GetPointer() + sizeof CPMCreateQueryIn;
  82. Pickle( _client.GetServerVersion(),
  83. &cols,
  84. &rst,
  85. pso,
  86. pcateg,
  87. &RstProp,
  88. &pidmap,
  89. pbPickledQuery,
  90. cbIn );
  91. CPMCreateQueryIn & request = * ( new( xQuery.Get() ) CPMCreateQueryIn );
  92. request.SetCheckSum( xQuery.SizeOf() );
  93. const unsigned cbCursors = sizeof ULONG * cCursors;
  94. const unsigned cbReply = sizeof CPMCreateQueryOut + cbCursors;
  95. XGrowable<BYTE, 200> xReply( cbReply );
  96. CPMCreateQueryOut * pReply = new( xReply.Get() ) CPMCreateQueryOut();
  97. ULONG cbRead;
  98. _client.DataWriteRead( &request,
  99. xQuery.SizeOf(),
  100. pReply,
  101. cbReply,
  102. cbRead );
  103. // DataWriteRead throws both connection problems and request problems
  104. Win4Assert( SUCCEEDED( pReply->GetStatus() ) );
  105. Win4Assert( _client.IsPipeTracingEnabled() || cbReply == cbRead );
  106. _fTrueSequential = pReply->IsTrueSequential();
  107. _fWorkIdUnique = pReply->IsWorkIdUnique();
  108. _ulServerCookie = pReply->GetServerCookie();
  109. RtlCopyMemory( aCursors, pReply->GetCursors(), cbCursors );
  110. //
  111. // Preserve xQuery for RestartPosition on sequential queries
  112. //
  113. if ( _fTrueSequential )
  114. {
  115. unsigned cElems = xQuery.Count();
  116. _xQuery.Set( cElems, xQuery.Acquire() );
  117. // The assumption here is that for a seq query the cursor does not
  118. // change. If this becomes different at a later date, the rowset
  119. // will have an old cursor after RestartPosition.
  120. RtlCopyMemory( _aCursors.Get(), aCursors, cbCursors );
  121. }
  122. #if CIDBG == 1
  123. for ( ULONG i = 0; i < cCursors; i++ )
  124. Win4Assert( 0 != aCursors[i] );
  125. #endif // CIDBG == 1
  126. AddRef();
  127. } //CSvcQueryProxy
  128. //+---------------------------------------------------------------------------
  129. //
  130. // Member: CSvcQueryProxy::~CSvcQueryProxy, public
  131. //
  132. // Synopsis: Destroy the query. Nothing to do -- all of the cursors
  133. // have been freed by now.
  134. //
  135. // History: 13 Sept 96 dlee created
  136. //
  137. //----------------------------------------------------------------------------
  138. CSvcQueryProxy::~CSvcQueryProxy()
  139. {
  140. tbDebugOut(( DEB_PROXY, "~CSvcQueryProxy\n\n" ));
  141. Win4Assert( 0 == _ref );
  142. // don't _client.Disconnect() here -- keep it open for more queries
  143. } //~CSvcQueryProxy
  144. //+-------------------------------------------------------------------------
  145. //
  146. // Member: CSvcQueryProxy::AddRef, public
  147. //
  148. // Synopsis: Reference the query.
  149. //
  150. // History: 13 Sept 96 dlee created
  151. //
  152. //--------------------------------------------------------------------------
  153. ULONG CSvcQueryProxy::AddRef()
  154. {
  155. return InterlockedIncrement( & _ref );
  156. } //AddRef
  157. //+-------------------------------------------------------------------------
  158. //
  159. // Member: CSvcQueryProxy::Release, public
  160. //
  161. // Synopsis: De-Reference the query.
  162. //
  163. // Effects: If the ref count goes to 0 then the query is deleted.
  164. //
  165. // History: 13 Sept 96 dlee created
  166. //
  167. //--------------------------------------------------------------------------
  168. ULONG CSvcQueryProxy::Release()
  169. {
  170. long l = InterlockedDecrement( & _ref );
  171. if ( l <= 0 )
  172. {
  173. tbDebugOut(( DEB_PROXY, "CSvcQueryProxy unreferenced. Deleting.\n" ));
  174. delete this;
  175. return 0;
  176. }
  177. return l;
  178. } //Release
  179. //+-------------------------------------------------------------------------
  180. //
  181. // Member: CSvcQueryProxy::FreeCursor, public
  182. //
  183. // Synopsis: Free a handle to a CTableCursor
  184. //
  185. // Arguments: [hCursor] - handle to the cursor to be freed
  186. //
  187. // Returns: # of cursors remaining
  188. //
  189. // History: 13 Sept 96 dlee created
  190. //
  191. //--------------------------------------------------------------------------
  192. unsigned CSvcQueryProxy::FreeCursor(
  193. ULONG hCursor )
  194. {
  195. tbDebugOut(( DEB_PROXY, "FreeCursor\n" ));
  196. Win4Assert( 0 != hCursor );
  197. // If FreeCursor fails (likely because the system is out of memory),
  198. // terminate the connection with cisvc so query resources are freed.
  199. TRY
  200. {
  201. CPMFreeCursorIn request( hCursor );
  202. CPMFreeCursorOut reply;
  203. ULONG cbRead;
  204. _client.DataWriteRead( &request,
  205. sizeof request,
  206. &reply,
  207. sizeof reply,
  208. cbRead );
  209. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  210. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  211. return reply.CursorsRemaining();
  212. }
  213. CATCH( CException, e )
  214. {
  215. prxDebugOut(( DEB_IWARN,
  216. "freecursor failed 0x%x, rudely terminating connection\n",
  217. e.GetErrorCode() ));
  218. _client.TerminateRudelyNoThrow();
  219. RETHROW();
  220. }
  221. END_CATCH
  222. return 0;
  223. } //FreeCursor
  224. //+-------------------------------------------------------------------------
  225. //
  226. // Member: CSvcQueryProxy::GetRows, public
  227. //
  228. // Synopsis: Retrieve row data for a table cursor
  229. //
  230. // Arguments: [hCursor] - the handle of the cursor to fetch data for
  231. // [rSeekDesc] - row seek operation to be done before fetch
  232. // [pGetRowsParams] - row fetch parameters and buffer pointers
  233. // [pSeekDescOut] - row seek description for restart
  234. //
  235. // Returns: SCODE - the status of the operation.
  236. //
  237. // History: 13 Sept 96 dlee created
  238. //
  239. //--------------------------------------------------------------------------
  240. SCODE CSvcQueryProxy::GetRows(
  241. ULONG hCursor,
  242. const CRowSeekDescription & rSeekDesc,
  243. CGetRowsParams & rGetRowsParams,
  244. XPtr<CRowSeekDescription> & pSeekDescOut)
  245. {
  246. tbDebugOut(( DEB_PROXY,
  247. "GetRows 0x%x\n",
  248. rGetRowsParams.RowsToTransfer() ));
  249. unsigned cbSeek = rSeekDesc.MarshalledSize();
  250. unsigned cbInput = sizeof CPMGetRowsIn + cbSeek;
  251. unsigned cbReserved = AlignBlock( sizeof CPMGetRowsOut + cbSeek,
  252. sizeof LONGLONG );
  253. XArray<BYTE> xIn( cbInput );
  254. CPMGetRowsIn *pRequest = new( xIn.Get() )
  255. CPMGetRowsIn( hCursor,
  256. rGetRowsParams.RowsToTransfer(),
  257. rGetRowsParams.GetFwdFetch(),
  258. rGetRowsParams.GetRowWidth(),
  259. cbSeek,
  260. cbReserved );
  261. // serialize the seek description
  262. CMemSerStream stmMem( pRequest->GetDesc(), cbSeek );
  263. rSeekDesc.Marshall( stmMem );
  264. // Make an allocator for the output. Scale the buffer size based
  265. // on the # of rows to be retrieved (just a heuristic). Large buffers
  266. // are more expensive since the entire buffer must be sent over the
  267. // pipe (since var data grows down from the end of the buffer).
  268. // For 10 rows in a typical web query, it takes about 7-10k. Any way
  269. // to squeeze it so it always fits in 8k? Eg:
  270. // filename,size,characterization,vpath,doctitle,write
  271. // filename -- 20
  272. // abstract -- 640
  273. // props+variants -- 16*6
  274. // vpath -- 100
  275. // title -- 80
  276. // total: 936 * 10 rows = 9360 bytes
  277. const unsigned cbGetRowsGranularity = 512;
  278. const unsigned cbBigRow = 1000;
  279. const unsigned cbNormalRow = 300;
  280. unsigned cbOut = rGetRowsParams.RowsToTransfer() * cbBigRow;
  281. cbOut = __max( rGetRowsParams.GetRowWidth(), cbOut );
  282. cbOut = __min( cbOut, cbMaxProxyBuffer );
  283. XArray<BYTE> xOut;
  284. // loop until at least 1 row fits in a buffer
  285. CPMGetRowsOut *pReply = 0;
  286. NTSTATUS Status = 0;
  287. DWORD cbRead;
  288. do
  289. {
  290. cbOut = AlignBlock( cbOut, cbGetRowsGranularity );
  291. Win4Assert( cbOut <= cbMaxProxyBuffer );
  292. xOut.ReSize( cbOut );
  293. pReply = (CPMGetRowsOut *) xOut.GetPointer();
  294. pRequest->SetReadBufferSize( cbOut );
  295. #ifdef _WIN64
  296. //
  297. // If a Win64 client is talking to a Win32 server set the base in the sent
  298. // buffer to 0 so that the values returned are offsets and remember what the
  299. // real pointer is.
  300. // Otherwise, be sure to set the reply base pointer to zero to indicate to the
  301. // rowset that no munging has to be done.
  302. //
  303. if ( !_client.IsServer64() )
  304. {
  305. pRequest->SetClientBase ( 0 );
  306. rGetRowsParams.SetReplyBase ( (BYTE *) pReply );
  307. }
  308. else
  309. {
  310. pRequest->SetClientBase( (ULONG_PTR) pReply );
  311. }
  312. #else
  313. pRequest->SetClientBase( (ULONG_PTR) pReply );
  314. #endif
  315. pRequest->SetCheckSum( cbInput );
  316. TRY
  317. {
  318. _client.DataWriteRead( pRequest,
  319. cbInput,
  320. pReply,
  321. cbOut,
  322. cbRead );
  323. Status = pReply->GetStatus();
  324. }
  325. CATCH( CException, e )
  326. {
  327. Status = e.GetErrorCode();
  328. }
  329. END_CATCH;
  330. } while ( ( Status == STATUS_BUFFER_TOO_SMALL ) &&
  331. ( cbOut++ < cbMaxProxyBuffer ) );
  332. prxDebugOut(( DEB_ITRACE, "Status at end of getrows: 0x%x\n", Status ));
  333. Win4Assert( pReply == (CPMGetRowsOut *) xOut.GetPointer() );
  334. SCODE scResult = 0;
  335. if ( NT_SUCCESS( Status ) )
  336. {
  337. rGetRowsParams.SetRowsTransferred( pReply->RowsReturned() );
  338. CMemDeSerStream stmDeser( pReply->GetSeekDesc(), cbReserved );
  339. UnmarshallRowSeekDescription( stmDeser,
  340. _client.GetServerVersion(),
  341. pSeekDescOut,
  342. TRUE );
  343. // Hand off the block to the allocator made in CRowset
  344. Win4Assert( pReply == (CPMGetRowsOut *) xOut.GetPointer() );
  345. PFixedVarAllocator & rAlloc = rGetRowsParams.GetFixedVarAllocator();
  346. rAlloc.ReInit( TRUE, cbReserved, xOut.Acquire(), cbOut );
  347. // we have pointers already, not offsets
  348. rAlloc.SetBase( 0 );
  349. scResult = Status; // ok, endOfRowset, blockLimitedRows, etc.
  350. }
  351. else
  352. {
  353. if ( DB_E_BADRATIO != Status && DB_E_BADSTARTPOSITION != Status) {
  354. prxDebugOut(( DEB_ERROR, "GetRows returned 0x%x\n", Status ));
  355. }
  356. if ( NT_WARNING( Status ) )
  357. scResult = Status; // presumably, status is an SCODE
  358. else
  359. scResult = E_FAIL;
  360. }
  361. return scResult;
  362. } //GetRows
  363. //+-------------------------------------------------------------------------
  364. //
  365. // Member: CSvcQueryProxy::ReExecuteSequentialQuery, private
  366. //
  367. // Synopsis: Simulates re-execution of a sequential query by shutting down
  368. // the query object on the server, recreating it and setting up
  369. // the bindings from cached values
  370. //
  371. // Arguments:
  372. //
  373. // History: 02-28-98 danleg Created
  374. //
  375. //--------------------------------------------------------------------------
  376. void CSvcQueryProxy::ReExecuteSequentialQuery()
  377. {
  378. tbDebugOut(( DEB_PROXY, "ReExecuteSequentialQuery\n" ));
  379. // SPECDEVIATION: if we have heirarchical rowsets, all current positions
  380. // will be reset.
  381. //
  382. // Shutdown the existing PQuery on the server first
  383. //
  384. for ( unsigned i=0; i<_aCursors.Count(); i++ )
  385. FreeCursor( _aCursors[i] );
  386. //
  387. // Recreate PQuery on the server
  388. //
  389. CPMCreateQueryIn * pCreateReq = (CPMCreateQueryIn *) _xQuery.Get();
  390. const unsigned cbCursors = sizeof ULONG * _aCursors.Count();
  391. const unsigned cbReply = sizeof CPMCreateQueryOut + cbCursors;
  392. XGrowable<BYTE, 200> xReply( cbReply );
  393. CPMCreateQueryOut * pReply = new( xReply.Get() ) CPMCreateQueryOut();
  394. ULONG cbRead;
  395. _client.DataWriteRead( pCreateReq,
  396. _xQuery.SizeOf(),
  397. pReply,
  398. cbReply,
  399. cbRead );
  400. // DataWriteRead throws both connection problems and request problems
  401. Win4Assert( SUCCEEDED( pReply->GetStatus() ) );
  402. Win4Assert( _client.IsPipeTracingEnabled() || cbReply == cbRead );
  403. _fTrueSequential = pReply->IsTrueSequential();
  404. _fWorkIdUnique = pReply->IsWorkIdUnique();
  405. _ulServerCookie = pReply->GetServerCookie();
  406. RtlCopyMemory( _aCursors.Get(), pReply->GetCursors(), _aCursors.Count() );
  407. //
  408. // Recreate bindings
  409. //
  410. CProxyMessage reply;
  411. CPMSetBindingsIn *pBindReq = (CPMSetBindingsIn *) _xBindings.Get();
  412. ULONG cbRequest = sizeof CPMSetBindingsIn + pBindReq->GetBindingDescLength();
  413. cbRequest = AlignBlock( cbRequest, sizeof ULONG );
  414. _client.DataWriteRead( pBindReq,
  415. cbRequest,
  416. &reply,
  417. sizeof reply,
  418. cbRead );
  419. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  420. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  421. } // ReExecuteSequentialQuery
  422. //+-------------------------------------------------------------------------
  423. //
  424. // Member: CSvcQueryProxy::RestartPosition, public
  425. //
  426. // Synopsis: Reset the fetch position for the chapter back to the start
  427. //
  428. // Arguments: [hCursor] - handle of the cursor
  429. // [chapt] - chapter
  430. //
  431. // History: 17 Apr 97 emilyb created
  432. // 02-01-98 danleg restart for seq queries
  433. //
  434. //--------------------------------------------------------------------------
  435. void CSvcQueryProxy::RestartPosition(
  436. ULONG hCursor,
  437. CI_TBL_CHAPT chapt )
  438. {
  439. tbDebugOut(( DEB_PROXY, "RestartPosition\n" ));
  440. //
  441. // If sequential, re-execute the query
  442. //
  443. if ( _fTrueSequential )
  444. ReExecuteSequentialQuery();
  445. else
  446. {
  447. CPMRestartPositionIn request( hCursor, chapt );
  448. CProxyMessage reply;
  449. DWORD cbRead;
  450. _client.DataWriteRead( &request,
  451. sizeof request,
  452. &reply,
  453. sizeof reply,
  454. cbRead );
  455. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  456. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  457. }
  458. } //RestartPosition
  459. //+-------------------------------------------------------------------------
  460. //
  461. // Member: CSvcQueryProxy::StopAsync, public
  462. //
  463. // Synopsis: Stop processing of async rowset
  464. //
  465. // Arguments: [hCursor] - handle of the cursor
  466. //
  467. // History: 17 Apr 97 emilyb created
  468. //
  469. //--------------------------------------------------------------------------
  470. void CSvcQueryProxy::StopAsynch(
  471. ULONG hCursor )
  472. {
  473. tbDebugOut(( DEB_PROXY, "Stop\n" ));
  474. CPMStopAsynchIn request( hCursor );
  475. CProxyMessage reply;
  476. DWORD cbRead;
  477. _client.DataWriteRead( &request,
  478. sizeof request,
  479. &reply,
  480. sizeof reply,
  481. cbRead );
  482. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  483. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  484. } //StopAsynch
  485. //+-------------------------------------------------------------------------
  486. //
  487. // Member: CSvcQueryProxy::StartWatching, public
  488. //
  489. // Synopsis: Start watch all behavior for rowset
  490. //
  491. // Arguments: [hCursor] - handle of the cursor
  492. //
  493. // History: 17 Apr 97 emilyb created
  494. //
  495. //--------------------------------------------------------------------------
  496. void CSvcQueryProxy::StartWatching(
  497. ULONG hCursor )
  498. {
  499. tbDebugOut(( DEB_PROXY, "StartWatching\n" ));
  500. CPMStartWatchingIn request( hCursor );
  501. CProxyMessage reply;
  502. DWORD cbRead;
  503. _client.DataWriteRead( &request,
  504. sizeof request,
  505. &reply,
  506. sizeof reply,
  507. cbRead );
  508. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  509. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  510. } //StartWatching
  511. //+-------------------------------------------------------------------------
  512. //
  513. // Member: CSvcQueryProxy::StopWatching, public
  514. //
  515. // Synopsis: Stop watch all behavior for rowset
  516. //
  517. // Arguments: [hCursor] - handle of the cursor
  518. //
  519. // History: 17 Apr 97 emilyb created
  520. //
  521. //--------------------------------------------------------------------------
  522. void CSvcQueryProxy::StopWatching(
  523. ULONG hCursor )
  524. {
  525. tbDebugOut(( DEB_PROXY, "StopWatching\n" ));
  526. CPMStopWatchingIn request( hCursor );
  527. CProxyMessage reply;
  528. DWORD cbRead;
  529. _client.DataWriteRead( &request,
  530. sizeof request,
  531. &reply,
  532. sizeof reply,
  533. cbRead );
  534. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  535. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  536. } //StopWatching
  537. //+-------------------------------------------------------------------------
  538. //
  539. // Member: CSvcQueryProxy::RatioFinished, public
  540. //
  541. // Synopsis: Return the completion status as a fraction
  542. //
  543. // Arguments: [hCursor] - handle of the cursor to check
  544. // [rulDenominator] - on return, denominator of fraction
  545. // [rulNumerator] - on return, numerator of fraction
  546. // [rcRows] - on return, number of rows in cursor
  547. // [rfNewRows] - on return, TRUE if new rows available
  548. //
  549. // History: 13 Sept 96 dlee created
  550. //
  551. //--------------------------------------------------------------------------
  552. void CSvcQueryProxy::RatioFinished(
  553. ULONG hCursor,
  554. DBCOUNTITEM & rulDenominator,
  555. DBCOUNTITEM & rulNumerator,
  556. DBCOUNTITEM & rcRows,
  557. BOOL & rfNewRows )
  558. {
  559. tbDebugOut(( DEB_PROXY, "RatioFinished\n" ));
  560. SCODE sc = S_OK;
  561. CPMRatioFinishedIn request( hCursor, TRUE );
  562. CPMRatioFinishedOut reply;
  563. DWORD cbReply;
  564. _client.DataWriteRead( &request,
  565. sizeof request,
  566. &reply,
  567. sizeof reply,
  568. cbReply );
  569. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  570. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbReply );
  571. rulDenominator = reply.Denominator();
  572. rulNumerator = reply.Numerator();
  573. rcRows = reply.RowCount();
  574. rfNewRows = reply.NewRows();
  575. // the values must be good by now or we would have thrown
  576. Win4Assert( 0 != rulDenominator );
  577. Win4Assert( rulDenominator >= rulNumerator );
  578. } //RatioFinished
  579. //+-------------------------------------------------------------------------
  580. //
  581. // Member: CSvcQueryProxy::RatioFinished, public
  582. //
  583. // Synopsis: Return the completion status as a fraction
  584. //
  585. // Arguments: [rSync] - notification synchronization info
  586. // [hCursor] - handle of the cursor to check
  587. // [rulDenominator] - on return, denominator of fraction
  588. // [rulNumerator] - on return, numerator of fraction
  589. // [rcRows] - on return, number of rows in cursor
  590. // [rfNewRows] - on return, TRUE if new rows available
  591. //
  592. // Returns: S_OK or STATUS_CANCELLED if rSync's cancel event signalled.
  593. //
  594. // History: 13 Sept 96 dlee created
  595. //
  596. //--------------------------------------------------------------------------
  597. SCODE CSvcQueryProxy::RatioFinished(
  598. CNotificationSync & rSync,
  599. ULONG hCursor,
  600. DBCOUNTITEM & rulDenominator,
  601. DBCOUNTITEM & rulNumerator,
  602. DBCOUNTITEM & rcRows,
  603. BOOL & rfNewRows )
  604. {
  605. tbDebugOut(( DEB_PROXY, "RatioFinished\n" ));
  606. SCODE sc = S_OK;
  607. CPMRatioFinishedIn request( hCursor, TRUE );
  608. CPMRatioFinishedOut reply;
  609. DWORD cbReply;
  610. if ( _client.NotifyWriteRead( rSync.GetCancelEvent(),
  611. &request,
  612. sizeof request,
  613. &reply,
  614. sizeof reply,
  615. cbReply ) )
  616. sc = STATUS_CANCELLED;
  617. else
  618. {
  619. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  620. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbReply );
  621. rulDenominator = reply.Denominator();
  622. rulNumerator = reply.Numerator();
  623. rcRows = reply.RowCount();
  624. rfNewRows = reply.NewRows();
  625. // the values must be good by now or we would have thrown
  626. Win4Assert( 0 != rulDenominator );
  627. Win4Assert( rulDenominator >= rulNumerator );
  628. }
  629. return sc;
  630. } //RatioFinished (notify version)
  631. //+-------------------------------------------------------------------------
  632. //
  633. // Member: CSvcQueryProxy::Compare, public
  634. //
  635. // Synopsis: Return the approximate current position as a fraction
  636. //
  637. // Arguments: [hCursor] - handle of the cursor used to compare
  638. // [chapt] - chapter of bookmarks
  639. // [bmkFirst] - First bookmark to compare
  640. // [bmkSecond] - Second bookmark to compare
  641. // [rdwComparison] - on return, comparison value
  642. //
  643. // History: 13 Sept 96 dlee created
  644. //
  645. //--------------------------------------------------------------------------
  646. void CSvcQueryProxy::Compare(
  647. ULONG hCursor,
  648. CI_TBL_CHAPT chapt,
  649. CI_TBL_BMK bmkFirst,
  650. CI_TBL_BMK bmkSecond,
  651. DWORD & rdwComparison )
  652. {
  653. tbDebugOut(( DEB_PROXY, "Compare\n" ));
  654. CPMCompareBmkIn request( hCursor, chapt, bmkFirst, bmkSecond );
  655. CPMCompareBmkOut reply;
  656. DWORD cbRead;
  657. _client.DataWriteRead( &request,
  658. sizeof request,
  659. &reply,
  660. sizeof reply,
  661. cbRead );
  662. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  663. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  664. rdwComparison = reply.Comparison();
  665. } //Compare
  666. //+-------------------------------------------------------------------------
  667. //
  668. // Member: CSvcQueryProxy::GetApproximatePosition, public
  669. //
  670. // Synopsis: Return the approximate current position as a fraction
  671. //
  672. // Arguments: [hCursor] - cursor handle used to retrieve info
  673. // [chapt] - chapter requested
  674. // [bmk] - table bookmark for position
  675. // [pulNumerator] - on return, numerator of fraction
  676. // [pulDenominator] - on return, denominator of fraction
  677. //
  678. // History: 13 Sept 96 dlee created
  679. //
  680. //--------------------------------------------------------------------------
  681. void CSvcQueryProxy::GetApproximatePosition(
  682. ULONG hCursor,
  683. CI_TBL_CHAPT chapt,
  684. CI_TBL_BMK bmk,
  685. DBCOUNTITEM * pulNumerator,
  686. DBCOUNTITEM * pulDenominator )
  687. {
  688. tbDebugOut(( DEB_PROXY, "GAP\n" ));
  689. CPMGetApproximatePositionIn request( hCursor, chapt, bmk );
  690. CPMGetApproximatePositionOut reply;
  691. DWORD cbRead;
  692. _client.DataWriteRead( &request,
  693. sizeof request,
  694. &reply,
  695. sizeof reply,
  696. cbRead );
  697. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  698. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  699. *pulNumerator = reply.Numerator();
  700. *pulDenominator = reply.Denominator();
  701. } //GetApproximatePosition
  702. //+-------------------------------------------------------------------------
  703. //
  704. // Member: CSvcQueryProxy::SetBindings, public
  705. //
  706. // Synopsis: Set column bindings into a cursor
  707. //
  708. // Arguments: [hCursor] - handle of the cursor to set bindings on
  709. // [cbRowLength] - the width of an output row
  710. // [cols] - a description of column bindings to be set
  711. // [pids] - a PID mapper which maps fake pids in cols to
  712. // column IDs.
  713. //
  714. // History: 13 Sept 96 dlee created
  715. // 22 Aug 99 klam Win64 client -> Win32 server
  716. // 09 Feb 2000 KLam Win64: Reset variant size when done
  717. //
  718. //--------------------------------------------------------------------------
  719. void CSvcQueryProxy::SetBindings(
  720. ULONG hCursor,
  721. ULONG cbRowLength,
  722. CTableColumnSet & cols,
  723. CPidMapper & pids )
  724. {
  725. tbDebugOut(( DEB_PROXY, "SetBindings\n" ));
  726. #ifdef _WIN64
  727. // WIN64 client and servers mark the low bit of the hi-word of the
  728. // version to indicate it is a 64 bit machine.
  729. if ( !_client.IsServer64() )
  730. {
  731. tbDebugOut(( DEB_PROXY, "64bit client querying 32bit server!\n" ));
  732. // If there is a PROPVARIANT stored, adjust its size
  733. for (unsigned i = 0; i < cols.Count(); i++)
  734. {
  735. CTableColumn *pColumn = cols.Get(i);
  736. tbDebugOut(( DEB_PROXY, "\tFound type: %d width: %d\n",pColumn->GetStoredType(), pColumn->GetValueSize()));
  737. if ( VT_VARIANT == pColumn->GetStoredType() )
  738. {
  739. pColumn->SetValueField( VT_VARIANT,
  740. pColumn->GetValueOffset(),
  741. SizeOfWin32PROPVARIANT );
  742. tbDebugOut(( DEB_PROXY, "\tReplacing variant with size %d and offset 0x%x \n",
  743. SizeOfWin32PROPVARIANT, pColumn->GetValueOffset() ));
  744. }
  745. }
  746. }
  747. #endif
  748. // determine the size of the serialized column set
  749. CSizeSerStream stmSize;
  750. cols.Marshall( stmSize, pids );
  751. ULONG cbRequest = sizeof CPMSetBindingsIn + stmSize.Size();
  752. cbRequest = AlignBlock( cbRequest, sizeof ULONG );
  753. XArray<BYTE> xIn( cbRequest );
  754. CPMSetBindingsIn *pRequest = new( xIn.Get() )
  755. CPMSetBindingsIn( hCursor,
  756. cbRowLength,
  757. stmSize.Size() );
  758. // serialize the column set
  759. CMemSerStream stmMem( pRequest->GetDescription(), stmSize.Size() );
  760. cols.Marshall( stmMem, pids );
  761. pRequest->SetCheckSum( cbRequest );
  762. CProxyMessage reply;
  763. DWORD cbRead;
  764. _client.DataWriteRead( pRequest,
  765. cbRequest,
  766. &reply,
  767. sizeof reply,
  768. cbRead );
  769. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  770. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  771. #ifdef _WIN64
  772. if ( !IsCi64(_client.GetServerVersion()) )
  773. {
  774. tbDebugOut(( DEB_PROXY, "...64bit client finished querying 32bit server.\n" ));
  775. //
  776. // If there is a PROPVARIANT stored, readjust its size back to its original size
  777. //
  778. for (unsigned i = 0; i < cols.Count(); i++)
  779. {
  780. CTableColumn *pColumn = cols.Get(i);
  781. tbDebugOut(( DEB_PROXY, "\tFound type: %d width: %d\n",pColumn->GetStoredType(), pColumn->GetValueSize()));
  782. if ( VT_VARIANT == pColumn->GetStoredType() )
  783. {
  784. pColumn->SetValueField( VT_VARIANT,
  785. pColumn->GetValueOffset(),
  786. sizeof ( PROPVARIANT ) );
  787. tbDebugOut(( DEB_PROXY, "\tReseting variant with size %d and offset 0x%x \n",
  788. sizeof ( PROPVARIANT ), pColumn->GetValueOffset() ));
  789. }
  790. }
  791. }
  792. #endif
  793. //
  794. // Preserve binding stream if sequential for RestartPosition
  795. //
  796. if ( _fTrueSequential )
  797. {
  798. unsigned cElems = xIn.Count();
  799. _xBindings.Set( cElems, xIn.Acquire() );
  800. }
  801. } //SetBindings
  802. //+-------------------------------------------------------------------------
  803. //
  804. // Member: CSvcQueryProxy::GetNotifications, public
  805. //
  806. // Synopsis: Gets notification information, when available.
  807. //
  808. // Arguments: [rSync] -- notification synchronization info
  809. // [changeType] -- returns notification data info
  810. //
  811. // Returns: S_OK or STATUS_CANCELLED if rSync's cancel event signalled.
  812. //
  813. // History: 13 Sept 96 dlee created
  814. //
  815. //--------------------------------------------------------------------------
  816. SCODE CSvcQueryProxy::GetNotifications(
  817. CNotificationSync & rSync,
  818. DBWATCHNOTIFY & changeType)
  819. {
  820. tbDebugOut(( DEB_PROXY, "GetNotifications\n" ));
  821. SCODE sc = S_OK;
  822. CProxyMessage request( pmGetNotify );
  823. CPMSendNotifyOut reply(0);
  824. DWORD cbReply;
  825. if ( _client.NotifyWriteRead( rSync.GetCancelEvent(),
  826. &request,
  827. sizeof request,
  828. &reply,
  829. sizeof reply,
  830. cbReply ) )
  831. sc = STATUS_CANCELLED;
  832. else
  833. changeType = reply.WatchNotify();
  834. tbDebugOut(( DEB_PROXY, "GetNotifications %d, sc %lx\n", changeType, sc ));
  835. return sc;
  836. } //GetNotifications
  837. //+-------------------------------------------------------------------------
  838. //
  839. // Member: CSvcQueryProxy::SetWatchMode
  840. //
  841. // Synopsis: Stub implementation
  842. //
  843. // Arguments: [phRegion] - in/out region handle
  844. // [mode] - watch mode
  845. //
  846. // History: 13 Sept 96 dlee created
  847. //
  848. //--------------------------------------------------------------------------
  849. void CSvcQueryProxy::SetWatchMode(
  850. HWATCHREGION * phRegion,
  851. ULONG mode )
  852. {
  853. tbDebugOut (( DEB_PROXY, "Calling SetWatchMode\n" ));
  854. CPMSetWatchModeIn request( *phRegion, mode );
  855. CPMSetWatchModeOut reply;
  856. DWORD cbRead;
  857. _client.DataWriteRead( &request,
  858. sizeof request,
  859. &reply,
  860. sizeof reply,
  861. cbRead );
  862. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  863. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  864. *phRegion = reply.Region();
  865. } //SetWatchMode
  866. //+-------------------------------------------------------------------------
  867. //
  868. // Member: CSvcQueryProxy::GetWatchInfo
  869. //
  870. // Synopsis: Stub implementation
  871. //
  872. // Arguments: [hRegion] -- handle to watch region
  873. // [pMode] -- watch mode
  874. // [pChapter] -- chapter
  875. // [pBookmark] -- bookmark
  876. // [pcRows] -- number of rows
  877. //
  878. // History: 13 Sept 96 dlee created
  879. //
  880. //--------------------------------------------------------------------------
  881. void CSvcQueryProxy::GetWatchInfo (
  882. HWATCHREGION hRegion,
  883. ULONG * pMode,
  884. CI_TBL_CHAPT * pChapter,
  885. CI_TBL_BMK * pBookmark,
  886. DBCOUNTITEM * pcRows)
  887. {
  888. tbDebugOut (( DEB_PROXY, "Calling GetWatchInfo\n" ));
  889. // prepare for failure
  890. *pBookmark = 0;
  891. *pChapter = 0;
  892. *pMode = 0;
  893. *pcRows = 0;
  894. CPMGetWatchInfoIn request( hRegion );
  895. CPMGetWatchInfoOut reply;
  896. DWORD cbRead;
  897. _client.DataWriteRead( &request,
  898. sizeof request,
  899. &reply,
  900. sizeof reply,
  901. cbRead );
  902. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  903. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  904. *pMode = reply.Mode();
  905. *pChapter = reply.Chapter();
  906. *pBookmark = reply.Bookmark();
  907. *pcRows = reply.RowCount();
  908. } //GetWatchInfo
  909. //+-------------------------------------------------------------------------
  910. //
  911. // Member: CSvcQueryProxy::ShrinkWatchRegion
  912. //
  913. // Synopsis: Stub implementation
  914. //
  915. // Arguments: [hRegion] -- handle to watch region
  916. // [chapter] -- chapter
  917. // [bookmark] -- size of bookmark
  918. // [cRows] -- number of rows
  919. //
  920. // History: 13 Sept 96 dlee created
  921. //
  922. //--------------------------------------------------------------------------
  923. void CSvcQueryProxy::ShrinkWatchRegion (
  924. HWATCHREGION hRegion,
  925. CI_TBL_CHAPT chapter,
  926. CI_TBL_BMK bookmark,
  927. LONG cRows )
  928. {
  929. tbDebugOut (( DEB_PROXY, " Calling ShrinkWatchRegion\n" ));
  930. CPMShrinkWatchRegionIn request( hRegion, chapter, bookmark, cRows );
  931. CProxyMessage reply;
  932. DWORD cbRead;
  933. _client.DataWriteRead( &request,
  934. sizeof request,
  935. &reply,
  936. sizeof reply,
  937. cbRead );
  938. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  939. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  940. } //ShrinkWatchRegion
  941. //+-------------------------------------------------------------------------
  942. //
  943. // Member: CSvcQueryProxy::Refresh
  944. //
  945. // Synopsis: Stub implementation
  946. //
  947. // History: 13 Sept 96 dlee created
  948. //
  949. //--------------------------------------------------------------------------
  950. void CSvcQueryProxy::Refresh()
  951. {
  952. tbDebugOut(( DEB_PROXY, "Refresh\n" ));
  953. CProxyMessage request( pmRefresh );
  954. CProxyMessage reply;
  955. DWORD cbRead;
  956. _client.DataWriteRead( &request,
  957. sizeof request,
  958. &reply,
  959. sizeof reply,
  960. cbRead );
  961. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  962. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  963. tbDebugOut(( DEB_PROXY, "Refresh (end)\n" ));
  964. } //Refresh
  965. //+-------------------------------------------------------------------------
  966. //
  967. // Member: CSvcQueryProxy::GetQueryStatus, public
  968. //
  969. // Synopsis: Return the query status
  970. //
  971. // Arguments: [hCursor] - handle of the cursor to check
  972. // [rdwStatus] - on return, the query status
  973. //
  974. // History: 13 Sept 96 dlee created
  975. //
  976. //--------------------------------------------------------------------------
  977. void CSvcQueryProxy::GetQueryStatus(
  978. ULONG hCursor,
  979. DWORD & rdwStatus )
  980. {
  981. tbDebugOut(( DEB_PROXY, "GetQueryStatus\n" ));
  982. CPMGetQueryStatusIn request( hCursor );
  983. CPMGetQueryStatusOut reply;
  984. DWORD cbRead;
  985. _client.DataWriteRead( &request,
  986. sizeof request,
  987. &reply,
  988. sizeof reply,
  989. cbRead );
  990. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  991. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  992. rdwStatus = reply.QueryStatus();
  993. } //GetQueryStatus
  994. //+-------------------------------------------------------------------------
  995. //
  996. // Member: CSvcQueryProxy::GetQueryStatusEx, public
  997. //
  998. // Synopsis: Return the query status plus bonus information. It's kind
  999. // of an odd assortment of info, but it saves net trips.
  1000. //
  1001. // Arguments: [hCursor] - handle of the cursor to check completion for
  1002. // [rdwStatus] - returns the query status
  1003. // [rcFilteredDocuments] - returns # of filtered docs
  1004. // [rcDocumentsToFilter] - returns # of docs to filter
  1005. // [rdwRatioFinishedDenominator] - ratio finished denom
  1006. // [rdwRatioFinishedNumerator] - ratio finished num
  1007. // [bmk] - bmk to find
  1008. // [riRowBmk] - index of bmk row
  1009. // [rcRowsTotal] - # of rows in table
  1010. //
  1011. // History: Nov-9-96 dlee Created
  1012. //
  1013. //--------------------------------------------------------------------------
  1014. void CSvcQueryProxy::GetQueryStatusEx(
  1015. ULONG hCursor,
  1016. DWORD & rdwStatus,
  1017. DWORD & rcFilteredDocuments,
  1018. DWORD & rcDocumentsToFilter,
  1019. DBCOUNTITEM & rdwRatioFinishedDenominator,
  1020. DBCOUNTITEM & rdwRatioFinishedNumerator,
  1021. CI_TBL_BMK bmk,
  1022. DBCOUNTITEM & riRowBmk,
  1023. DBCOUNTITEM & rcRowsTotal )
  1024. {
  1025. tbDebugOut(( DEB_PROXY, "GetQueryStatusEx\n" ));
  1026. CPMGetQueryStatusExIn request( hCursor, bmk );
  1027. CPMGetQueryStatusExOut reply;
  1028. DWORD cbRead;
  1029. _client.DataWriteRead( &request,
  1030. sizeof request,
  1031. &reply,
  1032. sizeof reply,
  1033. cbRead );
  1034. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  1035. Win4Assert( _client.IsPipeTracingEnabled() || sizeof reply == cbRead );
  1036. rdwStatus = reply.QueryStatus();
  1037. rcFilteredDocuments = reply.FilteredDocuments();
  1038. rcDocumentsToFilter = reply.DocumentsToFilter();
  1039. rdwRatioFinishedDenominator = reply.RatioFinishedDenominator();
  1040. rdwRatioFinishedNumerator = reply.RatioFinishedNumerator();
  1041. riRowBmk = reply.RowBmk();
  1042. rcRowsTotal = reply.RowsTotal();
  1043. } //GetQueryStatusEx
  1044. //+-------------------------------------------------------------------------
  1045. //
  1046. // Member: CSvcQueryProxy::FetchDeferredValue, public
  1047. //
  1048. // Synopsis: Returns a property value for a workid from the property cache
  1049. //
  1050. // Arguments: [wid] - workid for which property value is retrieved
  1051. // [ps]] - prop spec identifying value to be retrieved
  1052. // [var] - returns the value if available
  1053. //
  1054. // Returns: TRUE if a value was retrieved or FALSE otherwise
  1055. //
  1056. // History: 30 Sept 96 dlee created
  1057. //
  1058. //--------------------------------------------------------------------------
  1059. BOOL CSvcQueryProxy::FetchDeferredValue(
  1060. WORKID wid,
  1061. CFullPropSpec const & ps,
  1062. PROPVARIANT & var )
  1063. {
  1064. tbDebugOut(( DEB_PROXY, "FetchValue\n" ));
  1065. CLock lock( _mutexFetchValue );
  1066. XArray<BYTE> xValue;
  1067. XArray<BYTE> xResult( cbMaxProxyBuffer );
  1068. // cbChunk is the size of the output buffer including CPMFetchValueOut
  1069. const DWORD cbChunk = cbMaxProxyBuffer;
  1070. // Since the value might be large and each proxy buffer is limited to
  1071. // cbMaxProxyBuffer, iterate until the entire value is retrieved.
  1072. do
  1073. {
  1074. // only send the propspec once
  1075. DWORD cbPropSpec = 0;
  1076. if ( 0 == xValue.SizeOf() )
  1077. {
  1078. CSizeSerStream stmSize;
  1079. ps.Marshall( stmSize );
  1080. cbPropSpec = stmSize.Size();
  1081. }
  1082. ULONG cbRequest = AlignBlock( sizeof CPMFetchValueIn + cbPropSpec,
  1083. sizeof ULONG );
  1084. XArray<BYTE> xRequest( cbRequest );
  1085. CPMFetchValueIn *pRequest = new( xRequest.Get() )
  1086. CPMFetchValueIn( wid, xValue.SizeOf(), cbPropSpec, cbChunk );
  1087. if ( 0 == xValue.SizeOf() )
  1088. {
  1089. CMemSerStream stmMem( pRequest->GetPS(), cbPropSpec );
  1090. ps.Marshall( stmMem );
  1091. }
  1092. pRequest->SetCheckSum( xRequest.SizeOf() );
  1093. DWORD cbReply;
  1094. _client.DataWriteRead( pRequest,
  1095. xRequest.SizeOf(),
  1096. xResult.Get(),
  1097. xResult.SizeOf(),
  1098. cbReply );
  1099. CPMFetchValueOut &result = * (CPMFetchValueOut *) xResult.Get();
  1100. if ( !result.ValueExists() )
  1101. return FALSE;
  1102. // append the next portion of the value
  1103. DWORD cbOld = xValue.SizeOf();
  1104. Win4Assert( 0 != result.ValueSize() );
  1105. xValue.ReSize( cbOld + result.ValueSize() );
  1106. RtlCopyMemory( xValue.Get() + cbOld,
  1107. result.Value(),
  1108. result.ValueSize() );
  1109. // all done?
  1110. if ( !result.MoreExists() )
  1111. break;
  1112. } while ( TRUE );
  1113. CCoTaskMemAllocator tbaAlloc;
  1114. StgConvertPropertyToVariant( (SERIALIZEDPROPERTYVALUE *) xValue.Get(),
  1115. CP_WINUNICODE,
  1116. &var,
  1117. &tbaAlloc );
  1118. Win4Assert( (var.vt & 0x0fff) <= VT_CLSID );
  1119. return TRUE;
  1120. } //FetchValue
  1121. //+-------------------------------------------------------------------------
  1122. //
  1123. // Member: CSvcQueryProxy::WorkIdToPath, public
  1124. //
  1125. // Synopsis: Converts a wid to a path
  1126. //
  1127. // Arguments: [wid] -- wid to convert
  1128. // [funnyPath] -- resulting path
  1129. //
  1130. // History: 30 Sept 96 dlee created
  1131. //
  1132. //--------------------------------------------------------------------------
  1133. void CSvcQueryProxy::WorkIdToPath(
  1134. WORKID wid,
  1135. CFunnyPath & funnyPath )
  1136. {
  1137. tbDebugOut(( DEB_PROXY, "WorkIdToPath\n" ));
  1138. CPMWorkIdToPathIn request( wid );
  1139. XArray<WCHAR> xReply( cbMaxProxyBuffer );
  1140. CPMWorkIdToPathOut &reply = * (CPMWorkIdToPathOut *) xReply.Get();
  1141. DWORD cbRead;
  1142. _client.DataWriteRead( &request,
  1143. sizeof request,
  1144. &reply,
  1145. cbMaxProxyBuffer,
  1146. cbRead );
  1147. Win4Assert( SUCCEEDED( reply.GetStatus() ) );
  1148. if ( reply.Any() )
  1149. {
  1150. funnyPath.SetPath( reply.Path() );
  1151. }
  1152. } //WorkIdToPath