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.

2010 lines
61 KiB

  1. //+--------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 1997
  5. //
  6. // File: pipes.cxx
  7. //
  8. // History:
  9. // RichN 10/30/97 Created.
  10. //
  11. //---------------------------------------------------------------------------
  12. #include <ole2int.h>
  13. #include "pipes.hxx"
  14. /////////////////////////////////////////////////////////////////////////////
  15. // Externals
  16. EXTERN_C HRESULT PrxDllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv);
  17. extern HRESULT RemUnkPSGetClassObject(REFIID riid, PVOID* ppv);
  18. //////////////////////////////////////////////////////////////////////////////
  19. // Constants
  20. const DWORD WAIT_INFINITE = DWORD(-1);
  21. const ULONG FRAGMENT_SIZE = 1300;
  22. typedef struct
  23. {
  24. const char *key;
  25. const char *value;
  26. } RegistryKeyValue;
  27. const RegistryKeyValue REG_CONST_KEY[] =
  28. {
  29. "CLSID\\{0000032E-0000-0000-C000-000000000046}", "PipePSFactory",
  30. "CLSID\\{0000032E-0000-0000-C000-000000000046}\\InprocServer32", "ole32.dll",
  31. "Interface\\{DB2F3ACA-2F86-11d1-8E04-00C04FB9989A}", "IPipeByte",
  32. "Interface\\{DB2F3ACA-2F86-11d1-8E04-00C04FB9989A}\\ProxyStubClsid32", "{0000032E-0000-0000-C000-000000000046}",
  33. "Interface\\{DB2F3ACC-2F86-11d1-8E04-00C04FB9989A}", "IPipeLong",
  34. "Interface\\{DB2F3ACC-2F86-11d1-8E04-00C04FB9989A}\\ProxyStubClsid32", "{0000032E-0000-0000-C000-000000000046}",
  35. "Interface\\{DB2F3ACE-2F86-11d1-8E04-00C04FB9989A}", "IPipeDouble",
  36. "Interface\\{DB2F3ACE-2F86-11d1-8E04-00C04FB9989A}\\ProxyStubClsid32", "{0000032E-0000-0000-C000-000000000046}",
  37. // Indicates end of list.
  38. "", ""
  39. };
  40. /////////////////////////////////////////////////////////////////////////////
  41. // Macros
  42. inline ULONG MIN( ULONG a, ULONG b )
  43. {
  44. return a < b ? a : b;
  45. }
  46. inline ULONG MAX( ULONG a, ULONG b )
  47. {
  48. return a < b ? b : a;
  49. }
  50. inline HRESULT MAKE_WIN32( HRESULT status )
  51. {
  52. return MAKE_SCODE( SEVERITY_ERROR, FACILITY_WIN32, status );
  53. }
  54. inline HRESULT FIX_WIN32( HRESULT result )
  55. {
  56. if ((ULONG) result > 0xfffffff7 || (ULONG) result < 0x2000)
  57. return MAKE_WIN32( result );
  58. else
  59. return result;
  60. }
  61. /////////////////////////////////////////////////////////////////////////////
  62. // Globals
  63. #define DISABLEASYNC 0
  64. /////////////////////////////////////////////////////////////////////////////
  65. // Prototypes
  66. //+**************************************************************************
  67. // FixUpPipeRegistry(void)
  68. //
  69. // Description: Modifies the registry to have the pipe interface point
  70. // to a different class ID for the PSFactory. Adds to the
  71. // registry the new ID for the PipePSFactory.
  72. //
  73. // History:
  74. // Date: Time: Developer: Action:
  75. // 12/3/97 10:14:48 AM RichN Created.
  76. //
  77. // Notes: We do not change the async interfaces. They should still
  78. // be handled by ole32 directly. Only modify the synchronous varity.
  79. //
  80. //-**************************************************************************
  81. HRESULT FixUpPipeRegistry(void)
  82. {
  83. HRESULT result = ERROR_SUCCESS;
  84. // Create the Pipe interfaces and add the clsid for the PipePSFactory.
  85. for (int i = 0; (REG_CONST_KEY[i].key[0] != '\0') && result == ERROR_SUCCESS; i++)
  86. {
  87. // Use Ascii so it works on Win95.
  88. result = RegSetValueA(
  89. HKEY_CLASSES_ROOT,
  90. REG_CONST_KEY[i].key,
  91. REG_SZ,
  92. REG_CONST_KEY[i].value,
  93. strlen(REG_CONST_KEY[i].value) );
  94. }
  95. if( result != ERROR_SUCCESS )
  96. return FIX_WIN32( result );
  97. return S_OK;
  98. }
  99. //+**************************************************************************
  100. // ProxyDllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv)
  101. //
  102. // Description: Creates a proxy. Trys PrxDllGetClassObject first since that
  103. // is the most likely. If not, then sees if it is the pipe
  104. // proxy being created.
  105. //
  106. // History:
  107. // Date: Time: Developer: Action:
  108. // 10/30/97 11:43:55 AM RichN Created.
  109. //
  110. // Notes:
  111. //
  112. //-**************************************************************************
  113. EXTERN_C HRESULT ProxyDllGetClassObject(REFCLSID clsid, REFIID riid, void **ppv)
  114. {
  115. ComDebOut(( DEB_MARSHAL, "ProxyDllGetClassObject, clsid: %l, riid: %l \n",
  116. clsid, riid));
  117. HRESULT hr = PrxDllGetClassObject(clsid, riid, ppv);
  118. if( FAILED(hr) )
  119. {
  120. // Not a well known one, maybe it is the pipe factory.
  121. if( clsid == CLSID_PipePSFactory )
  122. {
  123. // Create the pipe proxy/stub class factory
  124. CPipePSFactory *pPipePSFactory = new CPipePSFactory();
  125. if( NULL != pPipePSFactory )
  126. {
  127. // Get the interface Requested.
  128. hr = pPipePSFactory->QueryInterface(riid, ppv);
  129. pPipePSFactory->Release();
  130. }
  131. else
  132. {
  133. *ppv = NULL;
  134. hr = E_OUTOFMEMORY;
  135. }
  136. }
  137. else if (clsid == CLSID_RemoteUnknownPSFactory)
  138. {
  139. hr = RemUnkPSGetClassObject(riid, ppv);
  140. }
  141. }
  142. return hr;
  143. }
  144. //+**************************************************************************
  145. // CPipePSFactory()
  146. //
  147. // Description: CTOR
  148. //
  149. // History:
  150. // Date: Time: Developer: Action:
  151. // 10/30/97 12:55:55 PM RichN Created.
  152. //
  153. // Notes:
  154. //
  155. //-**************************************************************************
  156. CPipePSFactory::CPipePSFactory() :
  157. m_cRef(1)
  158. {
  159. ComDebOut(( DEB_MARSHAL, "CPipePSFactory - ctor, this:%x \n", this));
  160. }
  161. //+**************************************************************************
  162. // CPipePSFactory()
  163. //
  164. // Description: DTOR
  165. //
  166. // History:
  167. // Date: Time: Developer: Action:
  168. // 10/30/97 12:56:19 PM RichN Created.
  169. //
  170. // Notes:
  171. //
  172. //-**************************************************************************
  173. CPipePSFactory::~CPipePSFactory()
  174. {
  175. ComDebOut(( DEB_MARSHAL, "CPipePSFactory - dtor, this:%x \n"));
  176. }
  177. //+**************************************************************************
  178. // QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  179. //
  180. // Description: Standard QI
  181. //
  182. // History:
  183. // Date: Time: Developer: Action:
  184. // 10/30/97 11:10:58 AM RichN Created.
  185. //
  186. // Notes:
  187. //
  188. //-**************************************************************************
  189. STDMETHODIMP CPipePSFactory::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  190. {
  191. ComDebOut(( DEB_MARSHAL, "CPipePSFactory::QueryInterface, this:%x, riid:%i \n",
  192. this, riid));
  193. if (IsEqualIID(riid, IID_IUnknown) ||
  194. IsEqualIID(riid, IID_IPSFactoryBuffer) )
  195. {
  196. *ppvObj = (IPSFactoryBuffer *) this;
  197. }
  198. else
  199. {
  200. *ppvObj = NULL;
  201. return E_NOINTERFACE;
  202. }
  203. ((IUnknown *)*ppvObj)->AddRef();
  204. return S_OK;
  205. }
  206. //+**************************************************************************
  207. // CPipePSFactory::AddRef()
  208. //
  209. // Description: Standard AddRef
  210. //
  211. // History:
  212. // Date: Time: Developer: Action:
  213. // 10/30/97 11:11:16 AM RichN Created.
  214. //
  215. // Notes:
  216. //
  217. //-**************************************************************************
  218. STDMETHODIMP_(ULONG) CPipePSFactory::AddRef()
  219. {
  220. ComDebOut(( DEB_MARSHAL, "CPipePSFactory::AddRef, this:%x, m_cRef:%d \n",
  221. this, m_cRef + 1));
  222. return InterlockedIncrement( &m_cRef );
  223. }
  224. //+**************************************************************************
  225. // CPipePSFactory::Release()
  226. //
  227. // Description: Standard Release
  228. //
  229. // History:
  230. // Date: Time: Developer: Action:
  231. // 10/30/97 11:11:31 AM RichN Created.
  232. //
  233. // Notes:
  234. //
  235. //-**************************************************************************
  236. STDMETHODIMP_(ULONG) CPipePSFactory::Release()
  237. {
  238. ComDebOut(( DEB_MARSHAL, "CPipePSFactory::Release, this:%x, m_cRef:%d \n",
  239. this, m_cRef - 1));
  240. ULONG lRef;
  241. if( (lRef = InterlockedDecrement( &m_cRef )) == 0)
  242. {
  243. delete this;
  244. return 0;
  245. }
  246. return lRef;
  247. }
  248. //+**************************************************************************
  249. // CreateProxy
  250. //
  251. // Description: Creates a pipe proxy.
  252. //
  253. // History:
  254. // Date: Time: Developer: Action:
  255. // 10/30/97 11:11:45 AM RichN Created.
  256. //
  257. // Notes: We will pass back to the call a ptr to our object and we will
  258. // hold a pointer to the real proxy. When the client calls us, we
  259. // can do whatever we want/need to do and then call the real proxy.
  260. //
  261. //-**************************************************************************
  262. STDMETHODIMP CPipePSFactory::CreateProxy( IUnknown *pUnkOuter,
  263. REFIID riid,
  264. IRpcProxyBuffer **ppProxy,
  265. void **ppv )
  266. {
  267. ComDebOut(( DEB_MARSHAL, "CreateProxy, pUnkOuter:%x riid:%I \n",
  268. pUnkOuter, riid));
  269. if( NULL == ppv || NULL == ppProxy )
  270. return E_INVALIDARG;
  271. *ppProxy = NULL;
  272. *ppv = NULL;
  273. IPSFactoryBuffer *pPSFactory;
  274. // Create the real PSFactory for the pipe interface.
  275. HRESULT hr = PrxDllGetClassObject( riid,
  276. IID_IPSFactoryBuffer,
  277. (void **) &pPSFactory);
  278. IUnknown *pNDRPipeProxy = NULL;
  279. IRpcProxyBuffer *pInternalProxyBuffer = NULL;
  280. if( SUCCEEDED(hr) )
  281. {
  282. // Create the real proxy.
  283. hr = pPSFactory->CreateProxy( pUnkOuter,
  284. riid,
  285. &pInternalProxyBuffer,
  286. (void **)&pNDRPipeProxy);
  287. pPSFactory->Release();
  288. }
  289. if( FAILED(hr) )
  290. return hr;
  291. if( IID_IPipeByte == riid )
  292. {
  293. *ppv = new CPipeProxy<BYTE,
  294. &IID_IPipeByte,
  295. &IID_AsyncIPipeByte,
  296. IPipeByte,
  297. AsyncIPipeByte>
  298. (pUnkOuter, pNDRPipeProxy);
  299. }
  300. else if( IID_IPipeLong == riid )
  301. {
  302. *ppv = new CPipeProxy<LONG,
  303. &IID_IPipeLong,
  304. &IID_AsyncIPipeLong,
  305. IPipeLong,
  306. AsyncIPipeLong>
  307. (pUnkOuter, pNDRPipeProxy);
  308. }
  309. else if( IID_IPipeDouble == riid )
  310. {
  311. *ppv = new CPipeProxy<DOUBLE,
  312. &IID_IPipeDouble,
  313. &IID_AsyncIPipeDouble,
  314. IPipeDouble,
  315. AsyncIPipeDouble>
  316. (pUnkOuter, pNDRPipeProxy);
  317. }
  318. else
  319. {
  320. hr = E_NOINTERFACE;
  321. }
  322. if( SUCCEEDED(hr) && NULL == *ppv)
  323. hr = E_OUTOFMEMORY;
  324. // Create the object that contains the IRpcProxyBuffer
  325. // and the pipe interface. Created with refcount of 1.
  326. if( SUCCEEDED(hr) )
  327. {
  328. CPipeProxyImp *pProxyImp = new CPipeProxyImp(pUnkOuter,
  329. pInternalProxyBuffer,
  330. pNDRPipeProxy,
  331. (IUnknown*) *ppv,
  332. riid);
  333. if( NULL == pProxyImp )
  334. {
  335. hr = E_OUTOFMEMORY;
  336. }
  337. else
  338. *ppProxy = (IRpcProxyBuffer *) pProxyImp;
  339. }
  340. // Clean up failure.
  341. if( FAILED(hr) )
  342. {
  343. if( NULL != *ppv )
  344. {
  345. delete *ppv;
  346. *ppv = NULL;
  347. }
  348. pNDRPipeProxy->Release();
  349. pInternalProxyBuffer->Release();
  350. }
  351. return hr;
  352. }
  353. //+**************************************************************************
  354. // CreateStub
  355. //
  356. // Description: Creates a pipe stub.
  357. //
  358. // History:
  359. // Date: Time: Developer: Action:
  360. // 10/30/97 11:12:46 AM RichN Created.
  361. //
  362. // Notes:
  363. //
  364. //-**************************************************************************
  365. STDMETHODIMP CPipePSFactory::CreateStub( REFIID riid,
  366. IUnknown *pUnkServer,
  367. IRpcStubBuffer **ppStub )
  368. {
  369. ComDebOut(( DEB_MARSHAL, "CreateStub, riid:%x pUnkServer:%x \n",
  370. riid, pUnkServer ));
  371. HRESULT hr = S_OK;
  372. IPSFactoryBuffer *pPSFactory;
  373. // Create the real PSFactory for the pipe interface.
  374. hr = PrxDllGetClassObject( riid,
  375. IID_IPSFactoryBuffer,
  376. (void **) &pPSFactory);
  377. // Call real factory to get stub.
  378. if( SUCCEEDED(hr) )
  379. {
  380. hr = pPSFactory->CreateStub(riid, pUnkServer, ppStub);
  381. pPSFactory->Release();
  382. }
  383. return hr;
  384. }
  385. //+**************************************************************************
  386. // CPipePoxyImp(IRpcProxyBuffer *pInternalPB, IUnknown *pPipe)
  387. //
  388. // Description: CTOR
  389. //
  390. // History:
  391. // Date: Time: Developer: Action:
  392. // 11/11/97 11:31:43 AM RichN Created.
  393. //
  394. // Notes:
  395. //
  396. //-**************************************************************************
  397. CPipeProxyImp::CPipeProxyImp(IUnknown *pUnkOuter,
  398. IRpcProxyBuffer *pInternalPB,
  399. IUnknown *pRealPipeProxy,
  400. IUnknown *pInternalPipeProxy,
  401. IID iid) :
  402. m_cRef (1),
  403. m_pInternalPipeProxy(pInternalPipeProxy),
  404. m_pInternalPB (pInternalPB),
  405. m_pRealPipeProxy (pRealPipeProxy),
  406. m_pUnkOuter (pUnkOuter),
  407. m_IidOfPipe (iid)
  408. {
  409. ComDebOut(( DEB_MARSHAL, "CPipeProxyImp ctor, this:%x \n"));
  410. Win4Assert(NULL != m_pInternalPB);
  411. Win4Assert(NULL != m_pRealPipeProxy);
  412. Win4Assert(NULL != m_pInternalPipeProxy);
  413. }
  414. //+**************************************************************************
  415. // CPipeProxyImp()
  416. //
  417. // Description: DTOR
  418. //
  419. // History:
  420. // Date: Time: Developer: Action:
  421. // 11/11/97 11:32:02 AM RichN Created.
  422. //
  423. // Notes:
  424. //
  425. //-**************************************************************************
  426. CPipeProxyImp::~CPipeProxyImp()
  427. {
  428. ComDebOut(( DEB_MARSHAL, "~CPipeProxyImp, this:%x \n", this));
  429. // AddRef the outer because we are aggregated.
  430. m_pUnkOuter->AddRef();
  431. // Delete the internal proxy.
  432. if( NULL != m_pInternalPipeProxy )
  433. {
  434. delete m_pInternalPipeProxy;
  435. m_pInternalPipeProxy = NULL;
  436. }
  437. // Release the real proxy.
  438. if( NULL != m_pRealPipeProxy )
  439. {
  440. m_pRealPipeProxy->Release();
  441. m_pRealPipeProxy = NULL;
  442. }
  443. // Release the pointer to the IRpcProxyBuffer
  444. if( NULL != m_pInternalPB )
  445. m_pInternalPB->Release();
  446. }
  447. //+**************************************************************************
  448. // QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  449. //
  450. // Description: QI
  451. //
  452. // History:
  453. // Date: Time: Developer: Action:
  454. // 11/11/97 11:36:15 AM RichN Created.
  455. //
  456. // Notes:
  457. //
  458. //-**************************************************************************
  459. STDMETHODIMP CPipeProxyImp::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  460. {
  461. ComDebOut(( DEB_MARSHAL, "QueryInterface, this:%x \n", this));
  462. HRESULT hr = S_OK;
  463. if( NULL == ppvObj )
  464. return E_INVALIDARG;
  465. if (IsEqualIID(riid, IID_IUnknown) ||
  466. IsEqualIID(riid, IID_IRpcProxyBuffer) )
  467. {
  468. *ppvObj = (IUnknown *) this;
  469. }
  470. else if( IsEqualIID(riid, m_IidOfPipe) )
  471. {
  472. *ppvObj = m_pInternalPipeProxy;
  473. }
  474. else
  475. {
  476. return m_pInternalPB->QueryInterface(riid, ppvObj);
  477. }
  478. ((IUnknown *)(*ppvObj))->AddRef();
  479. return hr;
  480. }
  481. //+**************************************************************************
  482. // CPipeProxyImp::AddRef()
  483. //
  484. // Description: AddRef
  485. //
  486. // History:
  487. // Date: Time: Developer: Action:
  488. // 11/11/97 11:36:34 AM RichN Created.
  489. //
  490. // Notes:
  491. //
  492. //-**************************************************************************
  493. STDMETHODIMP_(ULONG) CPipeProxyImp::AddRef()
  494. {
  495. return InterlockedIncrement( &m_cRef );
  496. }
  497. //+**************************************************************************
  498. // CPipeProxyImp::Release()
  499. //
  500. // Description:
  501. //
  502. // History:
  503. // Date: Time: Developer: Action:
  504. // 11/11/97 11:36:48 AM RichN Created.
  505. //
  506. // Notes:
  507. //
  508. //-**************************************************************************
  509. STDMETHODIMP_(ULONG) CPipeProxyImp::Release()
  510. {
  511. ULONG lRef;
  512. if( (lRef = InterlockedDecrement( &m_cRef )) == 0)
  513. {
  514. delete this;
  515. return 0;
  516. }
  517. return lRef;
  518. }
  519. //+**************************************************************************
  520. // Connect(IRpcChannelBuffer *pRpcChannelBuffer)
  521. //
  522. // Description: Simple pass through.
  523. //
  524. // History:
  525. // Date: Time: Developer: Action:
  526. // 11/11/97 11:36:59 AM RichN Created.
  527. //
  528. // Notes:
  529. //
  530. //-**************************************************************************
  531. STDMETHODIMP CPipeProxyImp::Connect(IRpcChannelBuffer *pRpcChannelBuffer)
  532. {
  533. return m_pInternalPB->Connect(pRpcChannelBuffer);
  534. }
  535. //+**************************************************************************
  536. // Disconnect( void )
  537. //
  538. // Description: Simple pass through.
  539. //
  540. // History:
  541. // Date: Time: Developer: Action:
  542. // 11/11/97 11:37:25 AM RichN Created.
  543. //
  544. // Notes:
  545. //
  546. //-**************************************************************************
  547. STDMETHODIMP_(void) CPipeProxyImp::Disconnect( void )
  548. {
  549. m_pInternalPB->Disconnect();
  550. }
  551. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  552. //+**************************************************************************
  553. // CPipeProxy( void * pProxy ):
  554. //
  555. // Description:CTOR
  556. //
  557. // History:
  558. // Date: Time: Developer: Action:
  559. // 11/11/97 11:43:00 AM RichN Created.
  560. //
  561. // Notes:
  562. //
  563. //-**************************************************************************
  564. CPipeProxy<T, ID, AsyncID, I, AsyncI>
  565. ::CPipeProxy(IUnknown *pUnkOuter, void * pProxy ):
  566. m_cFreeSpace (0), m_cKeepBufferSize (0),
  567. m_cKeepDataSize (0), m_cLastRead (0),
  568. m_cPushBufferSize (0), m_cReadAhead (0),
  569. m_cRef (0), m_pAsyncPullPipe (NULL),
  570. m_pAsyncPushPipe (NULL), m_pFreeSpace (NULL),
  571. m_pISyncPull (NULL), m_pISyncPush (NULL),
  572. m_pKeepBuffer (NULL), m_pKeepData (NULL),
  573. m_pRealProxy ((I *)pProxy), m_pUnkOuter (pUnkOuter),
  574. m_pPushBuffer (NULL), m_PullState (PULLSTATE0_ENTRY),
  575. m_PushState (PUSHSTATE0_ENTRY)
  576. {
  577. ComDebOut(( DEB_MARSHAL, "CPipeProxy, pUnkOuter:%x pProxy:%x p \n",
  578. pUnkOuter, pProxy));
  579. Win4Assert(NULL != m_pUnkOuter);
  580. Win4Assert(NULL != m_pRealProxy);
  581. // Fill in the array of functions for the pull states.
  582. PullStateFunc[0] = NULL; // Should never execute in state zero.
  583. PullStateFunc[1] = NbNaRgtRA1; // No Buffer, No async outstanding, Request > Read ahead
  584. PullStateFunc[2] = NbaRltRA2; // No Buffer, async call outstanding, Request < Read Ahead
  585. PullStateFunc[3] = NbaRgtRA3; // No Buffer, async, Req >= Read ahead
  586. PullStateFunc[4] = baRltB4; // Buffer, async, Request < Buffer size
  587. PullStateFunc[5] = baRgtB5; // Buffer, async, Request >= Buffer size
  588. PullStateFunc[6] = PullDone6; // done.
  589. // Fill in the array of functions for the push states.
  590. PushStateFunc[0] = NULL; // Should never execute in state zero.
  591. PushStateFunc[1] = NbNf1; // No Buffer, No free buffer space
  592. PushStateFunc[2] = bfPgtF2; // Buffer, free space in buffer, push size >= free size
  593. PushStateFunc[3] = bfPltF3; // Buffer, free, push < free
  594. PushStateFunc[4] = bPSz4; // Buffer, push size zero
  595. PushStateFunc[5] = PushDone5; // Done
  596. #if DBG==1
  597. for(int i = 1; i < MAX_PULL_STATES; i++)
  598. Win4Assert(PullStateFunc[i] != NULL);
  599. for(i = 1; i < MAX_PUSH_STATES; i++)
  600. Win4Assert(PushStateFunc[i] != NULL);
  601. #endif
  602. return;
  603. }
  604. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  605. //+**************************************************************************
  606. // CPipeProxy( void )
  607. //
  608. // Description: DTOR
  609. //
  610. // History:
  611. // Date: Time: Developer: Action:
  612. // 11/11/97 11:43:20 AM RichN Created.
  613. //
  614. // Notes: Addref the outer unknown and then release the pointer to the
  615. // real proxy.
  616. //
  617. //-**************************************************************************
  618. CPipeProxy<T, ID, AsyncID, I, AsyncI>
  619. ::~CPipeProxy( void )
  620. {
  621. Win4Assert(NULL != m_pUnkOuter);
  622. Win4Assert(NULL != m_pRealProxy);
  623. }
  624. //+**************************************************************************
  625. // QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  626. //
  627. // Description: IUnknown implementation. All delegate to outer unknown.
  628. //
  629. // History:
  630. // Date: Time: Developer: Action:
  631. // 11/11/97 11:48:42 AM RichN Created.
  632. //
  633. // Notes:
  634. //
  635. //-**************************************************************************
  636. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  637. STDMETHODIMP CPipeProxy<T, ID, AsyncID, I, AsyncI>
  638. ::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
  639. {
  640. return m_pUnkOuter->QueryInterface(riid, ppvObj);
  641. }
  642. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  643. STDMETHODIMP_(ULONG) CPipeProxy<T, ID, AsyncID, I, AsyncI>
  644. ::AddRef()
  645. {
  646. m_cRef++;
  647. return m_pUnkOuter->AddRef();
  648. }
  649. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  650. STDMETHODIMP_(ULONG) CPipeProxy<T, ID, AsyncID, I, AsyncI>
  651. ::Release()
  652. {
  653. m_cRef--;
  654. return m_pUnkOuter->Release();
  655. }
  656. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  657. //+**************************************************************************
  658. // Pull( T *Buf, ULONG Request, ULONG *Received)
  659. //
  660. // Description: Pull the data from the server.
  661. //
  662. // History:
  663. // Date: Time: Developer: Action:
  664. // 11/11/97 11:49:18 AM RichN Created.
  665. //
  666. // Notes: We read data ahead by using async calls. The size of the
  667. // read ahead can be controled by the user by implementing
  668. // the IPipeHueristic and setting it on the interface.
  669. //
  670. //-**************************************************************************
  671. STDMETHODIMP CPipeProxy<T, ID, AsyncID, I, AsyncI>
  672. ::Pull( T *Buf, ULONG Request, ULONG *Received)
  673. {
  674. ComDebOut(( DEB_MARSHAL, "Pull, this:%x, Buf:%x, Request:%d, Received:%x \n",
  675. this, Buf, Request, Received));
  676. if( 0 == Request )
  677. return E_UNEXPECTED;
  678. HRESULT hr;
  679. // For debugging it is sometimes useful to disable
  680. // the async read ahead.
  681. #if DISABLEASYNC==1
  682. hr = m_pRealProxy->Pull(Buf, Request, Received);
  683. return hr;
  684. #endif
  685. *Received = 0;
  686. // Transition to the next state.
  687. hr = PullStateTransition( Request );
  688. // Should never see state 0.
  689. Win4Assert(0 != m_PullState);
  690. // Call the function for the new state.
  691. if( SUCCEEDED(hr) )
  692. {
  693. hr = (this->*(PullStateFunc[m_PullState]))( Buf, Request, Received );
  694. }
  695. return hr;
  696. }
  697. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  698. //+**************************************************************************
  699. // Push( T *Buf, ULONG count)
  700. //
  701. // Description: Pushes data to the server.
  702. //
  703. // History:
  704. // Date: Time: Developer: Action:
  705. // 11/11/97 11:49:39 AM RichN Created.
  706. //
  707. // Notes:
  708. //
  709. //-**************************************************************************
  710. STDMETHODIMP CPipeProxy<T, ID, AsyncID, I, AsyncI>
  711. ::Push( T *Buf, ULONG Count)
  712. {
  713. ComDebOut(( DEB_MARSHAL, "Push, this:%x, Buf:%x, Count:%u \n",
  714. this, Buf, Count));
  715. HRESULT hr;
  716. // For debugging it is sometimes useful to disable
  717. // write behind.
  718. #if DISABLEASYNC==1
  719. hr = m_pRealProxy->Push(Buf, Count);
  720. return hr;
  721. #endif
  722. // Transition to the next state.
  723. hr = PushStateTransition( Count );
  724. // Should never see state 0.
  725. Win4Assert(0 != m_PushState);
  726. // Call the function for the new state.
  727. if( SUCCEEDED(hr) )
  728. {
  729. hr = (this->*(PushStateFunc[m_PushState]))( Buf, Count );
  730. }
  731. return hr;
  732. }
  733. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  734. //+**************************************************************************
  735. // InitAsync(void)
  736. //
  737. // Description: Initializes, gets, the pointers to the async parts.
  738. //
  739. // History:
  740. // Date: Time: Developer: Action:
  741. // 12/8/97 4:45:22 PM RichN Created.
  742. //
  743. // Notes:
  744. //
  745. //-**************************************************************************
  746. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  747. ::InitAsync(IUnknown** ppCallObj,
  748. AsyncI** ppAsyncPipe,
  749. ISynchronize** ppISync)
  750. {
  751. ComDebOut(( DEB_MARSHAL, "InitAsync, this:%x \n", this));
  752. Win4Assert(NULL != m_pRealProxy);
  753. Win4Assert(NULL == (*ppAsyncPipe));
  754. Win4Assert(NULL == (*ppISync));
  755. HRESULT hr;
  756. ICallFactory *pCF = NULL;
  757. hr = m_pRealProxy->QueryInterface(IID_ICallFactory, (void **) &pCF);
  758. if( FAILED(hr) )
  759. return hr;
  760. hr = pCF->CreateCall(*AsyncID, NULL, IID_IUnknown, ppCallObj);
  761. pCF->Release();
  762. if( FAILED(hr) )
  763. return hr;
  764. hr = (*ppCallObj)->QueryInterface(*AsyncID, (void **) ppAsyncPipe);
  765. if( FAILED(hr) )
  766. goto ErrorCallObj;
  767. hr = (*ppCallObj)->QueryInterface(IID_ISynchronize, (void **) ppISync);
  768. if( FAILED(hr) )
  769. goto ErrorAsyncPipe;
  770. return S_OK;
  771. ErrorAsyncPipe:
  772. (*ppAsyncPipe)->Release();
  773. (*ppAsyncPipe) = NULL;
  774. ErrorCallObj:
  775. (*ppCallObj)->Release();
  776. (*ppCallObj) = NULL;
  777. return hr;
  778. }
  779. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  780. //+**************************************************************************
  781. // CleanupProxy(IUnknown* pCallObj, IUnknown* pAsyncPipe, ISynchronize* pISync)
  782. //
  783. // Description: Cleans up all the async interfaces acquired.
  784. //
  785. // History:
  786. // Date: Time: Developer: Action:
  787. // 12/16/97 11:42:42 AM RichN Created.
  788. //
  789. // Notes:
  790. //
  791. //-**************************************************************************
  792. void CPipeProxy<T, ID, AsyncID, I, AsyncI>
  793. ::CleanupProxy(T ** ppBuffer,
  794. IUnknown** ppCallObj,
  795. AsyncI** ppAsyncPipe,
  796. ISynchronize** ppISync)
  797. {
  798. if( *ppBuffer )
  799. {
  800. delete (*ppBuffer);
  801. (*ppBuffer) = NULL;
  802. }
  803. if( NULL != (*ppISync) )
  804. {
  805. (*ppISync)->Release();
  806. *ppISync = NULL;
  807. }
  808. if( NULL != (*ppAsyncPipe) )
  809. {
  810. (*ppAsyncPipe)->Release();
  811. *ppAsyncPipe = NULL;
  812. }
  813. if( NULL != (*ppCallObj) )
  814. {
  815. (*ppCallObj)->Release();
  816. *ppCallObj = NULL;
  817. }
  818. }
  819. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  820. //+**************************************************************************
  821. // CancelTheCall(DWORD delay)
  822. //
  823. // Description: Cancel the currently outstanding call.
  824. //
  825. // History:
  826. // Date: Time: Developer: Action:
  827. // 12/9/97 10:59:54 AM RichN Created.
  828. //
  829. // Notes:
  830. //
  831. //-**************************************************************************
  832. void CPipeProxy<T, ID, AsyncID, I, AsyncI>
  833. ::CancelTheCall(IUnknown *pCallObj, DWORD delay)
  834. {
  835. ComDebOut(( DEB_MARSHAL, "CancelTheCall, this:%x \n", this));
  836. ICancelMethodCalls *pICancel;
  837. HRESULT hr = pCallObj->QueryInterface(IID_ICancelMethodCalls,
  838. (void **) &pICancel);
  839. if( FAILED(hr) )
  840. return;
  841. pICancel->Cancel(delay);
  842. pICancel->Release();
  843. return;
  844. }
  845. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  846. //+**************************************************************************
  847. // SetReadAhead(ULONG Request)
  848. //
  849. // Description: Determine the size of the read ahead.
  850. //
  851. // History:
  852. // Date: Time: Developer: Action:
  853. // 11/20/97 10:01:10 AM RichN Created.
  854. //
  855. // Notes:
  856. //
  857. //-**************************************************************************
  858. void CPipeProxy<T, ID, AsyncID, I, AsyncI>
  859. ::SetReadAhead(ULONG Request)
  860. {
  861. ComDebOut(( DEB_MARSHAL, "SetReadAhead, this:%x Request:%u \n", this, Request));
  862. Win4Assert(Request != 0);
  863. switch(m_PullState)
  864. {
  865. case PULLSTATE1_FIRST_CALL :
  866. // On the first call just set the read ahead to the request.
  867. // This assumes that the request will be constant and
  868. // we will be one call ahead all the time.
  869. m_cReadAhead = Request;
  870. break;
  871. case PULLSTATE2_NS_RQlsRA :
  872. // We haven't had a zero read or we wouldn't be here
  873. Win4Assert(m_cLastRead != 0);
  874. // Set the read ahead to the lesser of the request and the
  875. // amount last read. We are trying to match the read ahead with
  876. // the request by assuming a constant request, but the server
  877. // may only return a given amount regardless of what we
  878. // request.
  879. m_cReadAhead = MIN(Request, m_cLastRead);
  880. break;
  881. case PULLSTATE3_NS_RQgeRA :
  882. case PULLSTATE5_S_RQgeBS :
  883. // No zero read
  884. Win4Assert(m_cLastRead != 0);
  885. // The request is greater than what was asked for last time. So
  886. // we increase the read ahead to the max of the request or what
  887. // was actually read last time.
  888. m_cReadAhead = MAX(Request, m_cLastRead);
  889. break;
  890. default :
  891. // For all other states we should not be making read ahead calls.
  892. // Mostly because we read zero elements last time which indicates
  893. // the end of the data. The PULLSTATE4_S_RQlsBS doesn't do a read
  894. // ahead so we shouldn't be here while in that state.
  895. Win4Assert(FALSE && "Request read ahead in wrong state.");
  896. }
  897. Win4Assert( 0 != m_cReadAhead );
  898. }
  899. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  900. //+**************************************************************************
  901. // CheckAndSetKeepBuffer()
  902. //
  903. // Description: Check to see if the Buffer is the correct size and if not
  904. // make it the correct size.
  905. //
  906. // History:
  907. // Date: Time: Developer: Action:
  908. // 11/19/97 3:27:25 PM RichN Created.
  909. //
  910. // Notes: The buffer will never get smaller.
  911. //
  912. //-**************************************************************************
  913. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  914. ::CheckAndSetKeepBuffer(void)
  915. {
  916. Win4Assert(0 != m_cReadAhead);
  917. // Create a keep buffer the size of the read ahead.
  918. // We assume here that the user will not change the
  919. // request size and that the amount of data on hand
  920. // will never be larger. When it is we will re-allocate.
  921. // The buffer will never get smaller. Something to look at.
  922. if( m_cKeepBufferSize >= m_cReadAhead )
  923. return S_OK;
  924. T *temp = new T[m_cReadAhead];
  925. if( NULL == temp )
  926. {
  927. delete[] m_pKeepBuffer;
  928. m_pKeepBuffer = NULL;
  929. return E_OUTOFMEMORY;
  930. }
  931. if( m_pKeepBuffer != NULL )
  932. {
  933. // Copy the data into the new buffer
  934. memcpy(temp, m_pKeepBuffer, m_cKeepDataSize * sizeof(T));
  935. // Delete the old buffer and reset the bookkeeping.
  936. delete[] m_pKeepBuffer;
  937. }
  938. m_pKeepBuffer = temp;
  939. m_cKeepBufferSize = m_cReadAhead;
  940. m_pKeepData = m_pKeepBuffer + m_cKeepDataSize;
  941. return S_OK;
  942. }
  943. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  944. //+**************************************************************************
  945. // PullStateTransition(ULONG Request)
  946. //
  947. // Description: Transition from one state to the next. See pipes document
  948. // for a description of the state machine.
  949. //
  950. // History:
  951. // Date: Time: Developer: Action:
  952. // 11/11/97 4:20:19 PM RichN Created.
  953. //
  954. // Notes:
  955. //
  956. //-**************************************************************************
  957. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  958. ::PullStateTransition(ULONG Request)
  959. {
  960. ComDebOut(( DEB_MARSHAL, "PullStateTransition \n"));
  961. Win4Assert(Request > 0);
  962. switch( m_PullState )
  963. {
  964. case PULLSTATE0_ENTRY:
  965. // Transition to the first call state.
  966. m_PullState = PULLSTATE1_FIRST_CALL;
  967. break;
  968. case PULLSTATE1_FIRST_CALL:
  969. // If the last read was zero we are done. We have no stored
  970. // data so go to the state that handles request
  971. // that are either less than or greater than or
  972. // equal to the last read ahead. Realize the amount of
  973. // data returned from the last read ahead may
  974. // not equal the requeat and could be greater than or less than.
  975. if( 0 == m_cLastRead )
  976. m_PullState = PULLSTATE6_DONE;
  977. else
  978. m_PullState = (Request < m_cReadAhead) ?
  979. PULLSTATE2_NS_RQlsRA : PULLSTATE3_NS_RQgeRA;
  980. break;
  981. case PULLSTATE2_NS_RQlsRA:
  982. // In this state, either the last read is not zero or
  983. // the amount of data remaining is zero. Whatever state
  984. // we get to this one from does not go here on a last read
  985. // of zero nor if there is any data in the buffer (kept data).
  986. // Possible we where in this state and got a last
  987. // read of zero, in which case the amount of held data will
  988. // be = zero. We are in a state with no held data and got
  989. // a zero read of data, there can't be any kept data.
  990. Win4Assert(!( m_cLastRead == 0 && m_cKeepDataSize > 0));
  991. // If we had a zero read, we cleaned up in
  992. // this state and go to the state that just returns zero.
  993. if( 0 == m_cLastRead)
  994. {
  995. Win4Assert( 0 == m_cKeepDataSize );
  996. m_PullState = PULLSTATE6_DONE;
  997. }
  998. else
  999. {
  1000. // If the kept data is zero, then we need to go to a state
  1001. // that understands an empty buffer. Determine which one
  1002. // by the request and the read ahead.
  1003. // If there is kept data then go to a state that understands
  1004. // a non empty buffer. This time the correct one depends on
  1005. // the amount of data in the buffer.
  1006. if( 0 == m_cKeepDataSize )
  1007. m_PullState = ( Request < m_cReadAhead ) ?
  1008. PULLSTATE2_NS_RQlsRA : PULLSTATE3_NS_RQgeRA;
  1009. else
  1010. m_PullState = ( Request < m_cKeepDataSize) ?
  1011. PULLSTATE4_S_RQlsBS : PULLSTATE5_S_RQgeBS;
  1012. }
  1013. break;
  1014. case PULLSTATE3_NS_RQgeRA:
  1015. // We can never leave this state with data in the buffer. The
  1016. // request is greater than the read ahead and the returned amount
  1017. // of data can never be greater than the requested data, but it
  1018. // could be less.
  1019. Win4Assert(m_cKeepDataSize == 0);
  1020. // If the last read was zero go to the done state.
  1021. // else go to the state that understands empty buffers depending
  1022. // on the request and the read ahead.
  1023. if( 0 == m_cLastRead )
  1024. m_PullState = PULLSTATE6_DONE;
  1025. else
  1026. m_PullState = ( Request < m_cReadAhead ) ?
  1027. PULLSTATE2_NS_RQlsRA : PULLSTATE3_NS_RQgeRA;
  1028. break;
  1029. case PULLSTATE4_S_RQlsBS:
  1030. // When this state was entered there was data in the buffer. The
  1031. // request was for less than the buffer size so when we returned
  1032. // there should have still been data in the buffer. No read is done.
  1033. Win4Assert(m_cKeepDataSize > 0);
  1034. Win4Assert(m_cLastRead > 0);
  1035. // Go to the state that handles data in the buffer
  1036. // depending on the request and the amount of data in the buffer.
  1037. m_PullState = ( Request < m_cKeepDataSize ) ?
  1038. PULLSTATE4_S_RQlsBS : PULLSTATE5_S_RQgeBS;
  1039. break;
  1040. case PULLSTATE5_S_RQgeBS:
  1041. // Because we can fulfill at aleast part of the request from
  1042. // the buffer, we don't wait on the async call to finish. If it
  1043. // did finish (wait 0 tells us that) then there is data in the
  1044. // buffer (assuming it returned data) otherwise it is empty.
  1045. // So when the call finished last time the buffer
  1046. // could be empty or not. If the read was zero the buffer is empty.
  1047. Win4Assert( (m_cLastRead == 0 && m_cKeepDataSize == 0) ||
  1048. m_cLastRead != 0 );
  1049. // If the buffer is empty then on a zero last read go to done.
  1050. // Otherwise go to a state that understands empty buffers depending
  1051. // on the request size and the read ahead.
  1052. if( 0 == m_cKeepDataSize )
  1053. if( 0 == m_cLastRead )
  1054. m_PullState = PULLSTATE6_DONE;
  1055. else
  1056. m_PullState = ( Request < m_cReadAhead ) ?
  1057. PULLSTATE2_NS_RQlsRA : PULLSTATE3_NS_RQgeRA;
  1058. else
  1059. // Otherwise go to one that understands having data in the
  1060. // buffer depending on the request and the amount of data in
  1061. // the buffer.
  1062. m_PullState = ( Request < m_cKeepDataSize ) ?
  1063. PULLSTATE4_S_RQlsBS : PULLSTATE5_S_RQgeBS;
  1064. break;
  1065. case PULLSTATE6_DONE:
  1066. // When in this state there better not be any data left and
  1067. // the last read must be zero.
  1068. Win4Assert(m_cKeepDataSize == 0);
  1069. Win4Assert(m_cLastRead == 0);
  1070. m_PullState = PULLSTATE1_FIRST_CALL;
  1071. break;
  1072. default:
  1073. return E_UNEXPECTED;
  1074. }
  1075. return S_OK;
  1076. }
  1077. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1078. //+**************************************************************************
  1079. // NbNaRgtRA1
  1080. //
  1081. // Description: Pull, No data in buffer, no async call outstanding and
  1082. // request is >= read ahead. State 1.
  1083. //
  1084. // History:
  1085. // Date: Time: Developer: Action:
  1086. // 11/19/97 2:30:11 PM RichN Created.
  1087. //
  1088. // Notes: Make a sync call to get some data and then make an async call to
  1089. // read ahead.
  1090. //
  1091. //-**************************************************************************
  1092. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1093. ::NbNaRgtRA1(T *Buf,
  1094. ULONG Request,
  1095. ULONG *Received)
  1096. {
  1097. ComDebOut(( DEB_MARSHAL, "NbNaRgtRA1 Request:%d\n", Request));
  1098. Win4Assert(1 == m_PullState);
  1099. Win4Assert(NULL != Buf);
  1100. // State conditions
  1101. Win4Assert(0 == m_cLastRead );
  1102. Win4Assert(0 == m_cKeepDataSize);
  1103. HRESULT hr = S_OK;
  1104. // We are only in this state one time. There will always be
  1105. // an out standing async call, so we init async here.
  1106. hr = InitAsync(&m_pPullCallObj, &m_pAsyncPullPipe, &m_pISyncPull);
  1107. if( FAILED(hr) )
  1108. return hr;
  1109. // make a sync call to get started.
  1110. hr = m_pRealProxy->Pull(Buf, Request, Received);
  1111. m_cLastRead = *Received;
  1112. if( m_cLastRead > 0 && SUCCEEDED(hr))
  1113. {
  1114. SetReadAhead(Request);
  1115. // Make the async call.
  1116. hr = m_pAsyncPullPipe->Begin_Pull(m_cReadAhead);
  1117. }
  1118. else
  1119. {
  1120. CleanupProxy(&m_pKeepBuffer,
  1121. &m_pPullCallObj,
  1122. &m_pAsyncPullPipe,
  1123. &m_pISyncPull);
  1124. }
  1125. // Post condition
  1126. // Wouldn't expect the last read to be zero here, but no reason
  1127. // it couldn't be.
  1128. Win4Assert( 0 == m_cKeepDataSize );
  1129. return hr;
  1130. }
  1131. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1132. //+**************************************************************************
  1133. // NbaRltRA2 (T *Buf, ULONG Request, ULONG *Received)
  1134. //
  1135. // Description: Pull, No Buffer, read ahead call outstanding.
  1136. // State 2. We have to be prepared
  1137. // for the amount returned to be greater than, less than or equal
  1138. // to the amount requested. This works for both states 2 and 3.
  1139. //
  1140. // History:
  1141. // Date: Time: Developer: Action:
  1142. // 11/19/97 2:32:13 PM RichN Created.
  1143. //
  1144. // Notes: wait on the sync object,
  1145. // Finish the async call,
  1146. // Copy the data into the user Buffer
  1147. // make another async call
  1148. //
  1149. //-**************************************************************************
  1150. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1151. ::NbaRltRA2 (T *Buf, ULONG Request, ULONG *Received)
  1152. {
  1153. ComDebOut(( DEB_MARSHAL, "NbaRltRA2 Request:%d\n", Request));
  1154. Win4Assert(2 == m_PullState);
  1155. Win4Assert(NULL != Buf);
  1156. // State conditions.
  1157. Win4Assert(0 == m_cKeepDataSize); // No data in keep Buffer.
  1158. Win4Assert(0 < m_cLastRead);
  1159. Win4Assert(Request < m_cReadAhead);
  1160. HRESULT hr = S_OK;
  1161. bool bDoCleanup = false;
  1162. // There might be a Bug here. Bug!
  1163. // We should just be able to call the finish method, but the
  1164. // bug requires us to wait first.
  1165. hr = m_pISyncPull->Wait(0, WAIT_INFINITE);
  1166. if( SUCCEEDED(hr) )
  1167. {
  1168. hr = CheckAndSetKeepBuffer();
  1169. if( SUCCEEDED(hr) )
  1170. {
  1171. // Get the data requested last time. Remember the amount returned
  1172. // could be less than we requested.
  1173. hr = m_pAsyncPullPipe->Finish_Pull(m_pKeepBuffer, &m_cLastRead);
  1174. // We can't return more than requested, the buffer may not be
  1175. // large enough.
  1176. *Received = MIN(Request, m_cLastRead);
  1177. if( SUCCEEDED(hr) && m_cLastRead > 0 )
  1178. {
  1179. // Copy the data to the users Buffer and updata bookkeeping.
  1180. memcpy(Buf, m_pKeepBuffer, (*Received) * sizeof(T));
  1181. m_pKeepData = m_pKeepBuffer + *Received;
  1182. m_cKeepDataSize = m_cLastRead - *Received;
  1183. SetReadAhead(Request);
  1184. // Make another read ahead
  1185. hr = m_pAsyncPullPipe->Begin_Pull(m_cReadAhead);
  1186. }
  1187. else
  1188. {
  1189. // If the call failed or we received no data, we
  1190. // need to clean up since we won't be called again.
  1191. bDoCleanup = true;
  1192. }
  1193. }
  1194. else
  1195. {
  1196. //Cancel the call here.
  1197. CancelTheCall(m_pPullCallObj, 0);
  1198. }
  1199. }
  1200. if( FAILED(hr) || bDoCleanup )
  1201. CleanupProxy(&m_pKeepBuffer,
  1202. &m_pPullCallObj,
  1203. &m_pAsyncPullPipe,
  1204. &m_pISyncPull);
  1205. // Post condition
  1206. Win4Assert(!(m_cLastRead == 0 && m_cKeepDataSize > 0));
  1207. return hr;
  1208. }
  1209. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1210. //+**************************************************************************
  1211. // NbaRgtRA3 (T *Buf, ULONG Request, ULONG *Received)
  1212. //
  1213. // Description: Pull, No Buffered data, async call outstanding and request
  1214. // is greater than read ahead.
  1215. //
  1216. // History:
  1217. // Date: Time: Developer: Action:
  1218. // 11/20/97 10:50:22 AM RichN Created.
  1219. //
  1220. // Notes: Difference between this and the previous state: we know
  1221. // we don't need a keep Buffer here.
  1222. // wait on the sync object,
  1223. // Finish the async call,
  1224. // Copy the data into the user Buffer
  1225. // make another async call
  1226. //
  1227. //-**************************************************************************
  1228. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1229. ::NbaRgtRA3 (T *Buf, ULONG Request, ULONG *Received)
  1230. {
  1231. ComDebOut(( DEB_MARSHAL, "NbaRgtRA3, Request:%d \n", Request));
  1232. Win4Assert(3 == m_PullState);
  1233. Win4Assert(NULL != Buf);
  1234. // State conditions.
  1235. Win4Assert(0 == m_cKeepDataSize); // No data in keep Buffer.
  1236. Win4Assert(0 < m_cLastRead);
  1237. Win4Assert(Request >= m_cReadAhead);
  1238. bool bDoCleanup = false;
  1239. HRESULT hr = m_pISyncPull->Wait(0, WAIT_INFINITE);
  1240. if( SUCCEEDED(hr))
  1241. {
  1242. // Get the data requested last time.
  1243. hr = m_pAsyncPullPipe->Finish_Pull(Buf, &m_cLastRead);
  1244. *Received = m_cLastRead;
  1245. if( SUCCEEDED(hr) && m_cLastRead > 0 )
  1246. {
  1247. // Reset the amount of data remaining.
  1248. m_cKeepDataSize = 0;
  1249. m_pKeepData = NULL;
  1250. SetReadAhead(Request);
  1251. // Make another read ahead
  1252. hr = m_pAsyncPullPipe->Begin_Pull(m_cReadAhead);
  1253. }
  1254. else
  1255. {
  1256. // If the call failed or we received no data, we
  1257. // need to clean up since we won't be called again.
  1258. bDoCleanup = TRUE;
  1259. }
  1260. }
  1261. if( FAILED(hr) || bDoCleanup )
  1262. CleanupProxy(&m_pKeepBuffer,
  1263. &m_pPullCallObj,
  1264. &m_pAsyncPullPipe,
  1265. &m_pISyncPull);
  1266. // Post condition
  1267. Win4Assert( 0 < m_cReadAhead);
  1268. Win4Assert(0 == m_cKeepDataSize);
  1269. return hr;
  1270. }
  1271. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1272. //+**************************************************************************
  1273. // baRltB4 (T *Buf, ULONG Request, ULONG *Received)
  1274. //
  1275. // Description: Pull, Data in Buffer, async call outstanding and Request is
  1276. // less than the data in the keep Buffer.
  1277. //
  1278. // History:
  1279. // Date: Time: Developer: Action:
  1280. // 11/20/97 1:22:41 PM RichN Created.
  1281. //
  1282. // Notes: Copy data from the keep Buffer to the users Buffer.
  1283. // Update state variables.
  1284. //
  1285. //-**************************************************************************
  1286. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1287. ::baRltB4 (T *Buf, ULONG Request, ULONG *Received)
  1288. {
  1289. ComDebOut(( DEB_MARSHAL, "baRltB4 \n"));
  1290. Win4Assert(NULL != Buf);
  1291. Win4Assert(4 == m_PullState);
  1292. // State conditions.
  1293. Win4Assert(0 < m_cKeepDataSize); // Data in keep Buffer.
  1294. Win4Assert(0 < m_cLastRead);
  1295. Win4Assert(Request < m_cKeepDataSize);
  1296. memcpy(Buf, m_pKeepData, Request * (sizeof(T)));
  1297. m_cKeepDataSize -= Request;
  1298. m_pKeepData += Request;
  1299. // Post condition
  1300. Win4Assert(m_cKeepDataSize > 0);
  1301. Win4Assert(m_cLastRead > 0);
  1302. // Post condition
  1303. Win4Assert(0 < m_cKeepDataSize); // Data in keep Buffer.
  1304. Win4Assert(0 < m_cLastRead);
  1305. return S_OK;
  1306. }
  1307. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1308. //+**************************************************************************
  1309. // baRgtB5 (T *Buf, ULONG Request, ULONG *Received)
  1310. //
  1311. // Description: Pull, Data in Buffer, async call outstanding and Request is
  1312. // greater than or equal the data in the keep Buffer.
  1313. //
  1314. // History:
  1315. // Date: Time: Developer: Action:
  1316. // 11/20/97 1:24:25 PM RichN Created.
  1317. //
  1318. // Notes: Copy the keep Buffer data to the users Buffer.
  1319. // Wait 0
  1320. // if the call has completed.
  1321. // Finish the async call (keep Buffer, RA)
  1322. // Copy data into users Buffer to fill request
  1323. // Update keep data size.
  1324. // if we didn't read zero
  1325. // set read ahead
  1326. // Begin async call(RA)
  1327. //
  1328. //-**************************************************************************
  1329. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1330. ::baRgtB5 (T *Buf, ULONG Request, ULONG *Received)
  1331. {
  1332. ComDebOut(( DEB_MARSHAL, "baRgtB5 \n"));
  1333. Win4Assert(NULL != Buf);
  1334. Win4Assert(5 == m_PullState);
  1335. // State conditions.
  1336. Win4Assert(0 < m_cKeepDataSize); // Data in keep buffer.
  1337. Win4Assert(0 < m_cLastRead);
  1338. Win4Assert(Request >= m_cKeepDataSize);
  1339. HRESULT hr = S_OK;
  1340. // Give whatever data we already have.
  1341. T *tempBuf = Buf;
  1342. memcpy(tempBuf, m_pKeepData, m_cKeepDataSize * (sizeof(T)) );
  1343. // Remainder of the request.
  1344. ULONG Remainder = Request - m_cKeepDataSize;
  1345. tempBuf += m_cKeepDataSize;
  1346. m_cKeepDataSize = 0;
  1347. m_pKeepData = NULL;
  1348. hr = m_pISyncPull->Wait(0, 0);
  1349. // If the call is finished get the data and
  1350. // copy up to the total request or as much as
  1351. // we have into the buffer.
  1352. if( SUCCEEDED(hr) && RPC_S_CALLPENDING != hr)
  1353. {
  1354. hr = CheckAndSetKeepBuffer();
  1355. if( SUCCEEDED(hr) )
  1356. {
  1357. hr = m_pAsyncPullPipe->Finish_Pull(m_pKeepBuffer, &m_cLastRead);
  1358. if( SUCCEEDED(hr) )
  1359. {
  1360. // Copy the smaller of the remainder of the
  1361. // request or what was actually received.
  1362. ULONG CopySize = MIN(Remainder, m_cLastRead);
  1363. memcpy(tempBuf, m_pKeepBuffer, CopySize * sizeof(T));
  1364. m_cKeepDataSize = m_cLastRead - CopySize;
  1365. m_pKeepData = m_pKeepBuffer + CopySize;
  1366. if( m_cLastRead > 0 )
  1367. {
  1368. SetReadAhead(Request);
  1369. hr = m_pAsyncPullPipe->Begin_Pull(m_cReadAhead);
  1370. }
  1371. }
  1372. }
  1373. else
  1374. CancelTheCall(m_pPullCallObj, 0);
  1375. }
  1376. else
  1377. {
  1378. if( RPC_S_CALLPENDING == hr )
  1379. hr = S_OK;
  1380. else
  1381. CancelTheCall(m_pPullCallObj, 0);
  1382. }
  1383. *Received = (ULONG) (tempBuf - Buf);
  1384. if( FAILED(hr) )
  1385. CleanupProxy(&m_pKeepBuffer,
  1386. &m_pPullCallObj,
  1387. &m_pAsyncPullPipe,
  1388. &m_pISyncPull);
  1389. // Post condition
  1390. Win4Assert( (m_cLastRead == 0 && m_cKeepDataSize == 0) ||
  1391. m_cLastRead != 0 );
  1392. return hr;
  1393. }
  1394. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1395. //+**************************************************************************
  1396. // PullDone6 (T *Buf, ULONG Request, ULONG *Received)
  1397. //
  1398. // Description: Pull Done, no data in the Buffer and no outstanding calls.
  1399. //
  1400. // History:
  1401. // Date: Time: Developer: Action:
  1402. // 11/20/97 3:30:14 PM RichN Created.
  1403. //
  1404. // Notes:
  1405. //
  1406. //-**************************************************************************
  1407. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1408. ::PullDone6 (T *Buf, ULONG Request, ULONG *Received)
  1409. {
  1410. ComDebOut(( DEB_MARSHAL, "PullDone6 \n"));
  1411. Win4Assert(6 == m_PullState);
  1412. CleanupProxy(&m_pKeepBuffer,
  1413. &m_pPullCallObj,
  1414. &m_pAsyncPullPipe,
  1415. &m_pISyncPull);
  1416. HRESULT hr = S_OK;
  1417. if (Request > 0)
  1418. {
  1419. m_PullState = PULLSTATE1_FIRST_CALL;
  1420. hr = (this->*(PullStateFunc[m_PullState]))( Buf, Request, Received );
  1421. }
  1422. else
  1423. {
  1424. *Received = 0;
  1425. // Post condition
  1426. Win4Assert( 0 == m_cKeepDataSize );
  1427. Win4Assert( 0 == m_cLastRead );
  1428. }
  1429. return hr;
  1430. }
  1431. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1432. //+**************************************************************************
  1433. // SetPushBuffer(ULONG PushSize)
  1434. //
  1435. // Description: Allocates a buffer for push, or reallocates if it
  1436. // needs to grow.
  1437. //
  1438. // History:
  1439. // Date: Time: Developer: Action:
  1440. // 11/21/97 10:10:50 AM RichN Created.
  1441. //
  1442. // Notes: The buffer will never get smaller. We might want
  1443. // to reduce it by some algorithm, but not this time.
  1444. //
  1445. //-**************************************************************************
  1446. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1447. ::SetPushBuffer(ULONG PushSize)
  1448. {
  1449. ComDebOut(( DEB_MARSHAL, "SetPushBuffer, PushSize:%l \n"));
  1450. // If it is already big enough just return.
  1451. if( m_cPushBufferSize >= PushSize )
  1452. return S_OK;
  1453. ULONG NewSize = MAX(PushSize, (FRAGMENT_SIZE / sizeof(T)) + 1);
  1454. T *pTtemp = new T[NewSize];
  1455. if( NULL == pTtemp )
  1456. {
  1457. delete[] m_pPushBuffer;
  1458. m_pPushBuffer = NULL;
  1459. return E_OUTOFMEMORY;
  1460. }
  1461. ULONG BufferedDataSize = m_cPushBufferSize - m_cFreeSpace;
  1462. if( m_pPushBuffer != NULL )
  1463. {
  1464. // Copy data over and reset bookkeeping.
  1465. memcpy(pTtemp, m_pPushBuffer, BufferedDataSize * sizeof(T));
  1466. delete[] m_pPushBuffer;
  1467. }
  1468. m_pPushBuffer = pTtemp;
  1469. m_cPushBufferSize = NewSize;
  1470. m_pFreeSpace = m_pPushBuffer + BufferedDataSize;
  1471. m_cFreeSpace = m_cPushBufferSize - BufferedDataSize;
  1472. return S_OK;
  1473. }
  1474. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1475. //+**************************************************************************
  1476. // PushStateTransition(ULONG Request)
  1477. //
  1478. // Description: Implements the transition table for push. See the pipes
  1479. // document for a description of the state machine.
  1480. //
  1481. // History:
  1482. // Date: Time: Developer: Action:
  1483. // 11/13/97 4:36:43 PM RichN Created.
  1484. //
  1485. // 02/05/99 JohnStra Modified Push state machine to
  1486. // allow multiple Push operations
  1487. // on a pipe.
  1488. // Notes:
  1489. //
  1490. //-**************************************************************************
  1491. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1492. ::PushStateTransition(ULONG PushSize)
  1493. {
  1494. ComDebOut(( DEB_MARSHAL, "PushStateTransition, PushSize:%l \n"));
  1495. switch( m_PushState )
  1496. {
  1497. case PUSHSTATE0_ENTRY:
  1498. // From the entry state we always go to the first call state.
  1499. m_PushState = PUSHSTATE1_FIRSTCALL;
  1500. break;
  1501. case PUSHSTATE1_FIRSTCALL:
  1502. case PUSHSTATE2_FS_PSgeFS:
  1503. case PUSHSTATE3_FS_PSltFS:
  1504. // If the push size is zero transition to state that
  1505. // does a zero send.
  1506. if( 0 == PushSize )
  1507. m_PushState = PUSHSTATE4_FS_PSZERO;
  1508. else
  1509. // Go to state that either puts the data in free space
  1510. // or one that handles a push greater than the free space.
  1511. m_PushState = (PushSize < m_cFreeSpace) ?
  1512. PUSHSTATE3_FS_PSltFS : PUSHSTATE2_FS_PSgeFS;
  1513. break;
  1514. case PUSHSTATE4_FS_PSZERO:
  1515. // If we are in the state that handles a zero push we may
  1516. // be called again with a positive buffer size to execute
  1517. // another push. If we are called with any other
  1518. // buffer size, go to the state that returns an error.
  1519. if( 0 < PushSize )
  1520. m_PushState = PUSHSTATE1_FIRSTCALL;
  1521. else
  1522. m_PushState = PUSHSTATE5_DONE_ERROR;
  1523. break;
  1524. case PUSHSTATE5_DONE_ERROR:
  1525. // Stay in state PUSHSTATE_DONE_ERROR.
  1526. break;
  1527. default:
  1528. return E_UNEXPECTED;
  1529. }
  1530. return S_OK;
  1531. }
  1532. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1533. //+**************************************************************************
  1534. // NbNf1(T *Buf, ULONG PushSize)
  1535. //
  1536. // Description: Push, No buffer, no free, state 1.
  1537. //
  1538. // History:
  1539. // Date: Time: Developer: Action:
  1540. // 11/21/97 9:42:57 AM RichN Created.
  1541. //
  1542. // Notes:
  1543. //
  1544. //-**************************************************************************
  1545. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1546. ::NbNf1(T *Buf, ULONG PushSize)
  1547. {
  1548. ComDebOut(( DEB_MARSHAL, "NbNf1, PushSize:%l \n", PushSize));
  1549. Win4Assert(1 == m_PushState);
  1550. // This is the first call to push so PushSize shouldn't be zero.
  1551. if( PushSize == 0 || NULL == Buf)
  1552. return E_INVALIDARG;
  1553. HRESULT hr;
  1554. // We are only in this state one time so init the async stuff.
  1555. hr = InitAsync(&m_pPushCallObj, &m_pAsyncPushPipe, &m_pISyncPush);
  1556. if( FAILED(hr) )
  1557. return hr;
  1558. hr = m_pAsyncPushPipe->Begin_Push(Buf, PushSize);
  1559. if( FAILED(hr) )
  1560. CleanupProxy(&m_pPushBuffer,
  1561. &m_pPushCallObj,
  1562. &m_pAsyncPushPipe,
  1563. &m_pISyncPush);
  1564. return hr;
  1565. }
  1566. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1567. //+**************************************************************************
  1568. // bfPgtF2(T *Buf, ULONG PushSize)
  1569. //
  1570. // Description: Push, Have a buffer with free space and the push size
  1571. // is greater than or equal to the free space. State 2.
  1572. //
  1573. // History:
  1574. // Date: Time: Developer: Action:
  1575. // 11/21/97 10:52:41 AM RichN Created.
  1576. //
  1577. // Notes: This may grow the buffer, look at reducing it in the next method.
  1578. //
  1579. //-**************************************************************************
  1580. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1581. ::bfPgtF2(T *Buf, ULONG PushSize)
  1582. {
  1583. ComDebOut(( DEB_MARSHAL, "bfPgtF2, PushSize:%l \n", PushSize));
  1584. Win4Assert(2 == m_PushState);
  1585. Win4Assert( PushSize >= m_cFreeSpace );
  1586. Win4Assert( PushSize > 0 );
  1587. Win4Assert( (LONG) m_cFreeSpace >= 0 );
  1588. if( PushSize == 0 || NULL == Buf)
  1589. return E_INVALIDARG;
  1590. // There might be a BUG here. BUG! Shouldn't have to wait.
  1591. HRESULT hr = m_pISyncPush->Wait(0, WAIT_INFINITE);
  1592. if( SUCCEEDED(hr) )
  1593. hr = m_pAsyncPushPipe->Finish_Push();
  1594. if( SUCCEEDED(hr) )
  1595. {
  1596. ULONG TotalData = PushSize + (m_cPushBufferSize - m_cFreeSpace);
  1597. hr = SetPushBuffer( TotalData );
  1598. if( SUCCEEDED(hr) )
  1599. {
  1600. // Append the data to the buffer.
  1601. memcpy(m_pFreeSpace, Buf, PushSize * sizeof(T));
  1602. hr = m_pAsyncPushPipe->Begin_Push(m_pPushBuffer, TotalData);
  1603. m_pFreeSpace = m_pPushBuffer;
  1604. m_cFreeSpace = m_cPushBufferSize;
  1605. }
  1606. }
  1607. if( FAILED(hr) )
  1608. CleanupProxy(&m_pPushBuffer,
  1609. &m_pPushCallObj,
  1610. &m_pAsyncPushPipe,
  1611. &m_pISyncPush);
  1612. return hr;
  1613. }
  1614. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1615. //+**************************************************************************
  1616. // bfPltF3(T *Buf, ULONG PushSize)
  1617. //
  1618. // Description: Push, Have buffer and pushed data is less than free space.
  1619. //
  1620. // History:
  1621. // Date: Time: Developer: Action:
  1622. // 11/21/97 11:03:19 AM RichN Created.
  1623. //
  1624. // Notes:
  1625. //
  1626. //-**************************************************************************
  1627. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1628. ::bfPltF3(T *Buf, ULONG PushSize)
  1629. {
  1630. ComDebOut(( DEB_MARSHAL, "bfPltF3, PushSize:%l \n", PushSize));
  1631. Win4Assert(3 == m_PushState);
  1632. Win4Assert( m_cFreeSpace > PushSize );
  1633. Win4Assert( PushSize > 0 );
  1634. Win4Assert( m_cFreeSpace > 0 );
  1635. if( NULL == Buf)
  1636. return E_INVALIDARG;
  1637. // Copy the data into the buffer.
  1638. memcpy(m_pFreeSpace, Buf, PushSize * sizeof(T));
  1639. m_cFreeSpace -= PushSize;
  1640. m_pFreeSpace += PushSize;
  1641. return S_OK;
  1642. }
  1643. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1644. //+**************************************************************************
  1645. // bPSz4(T *Buf, ULONG PushSize)
  1646. //
  1647. // Description: Push, Have buffer and push size is zero.
  1648. //
  1649. // History:
  1650. // Date: Time: Developer: Action:
  1651. // 11/21/97 11:33:32 AM RichN Created.
  1652. //
  1653. // Notes:
  1654. //
  1655. //-**************************************************************************
  1656. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1657. ::bPSz4(T *Buf, ULONG PushSize)
  1658. {
  1659. ComDebOut(( DEB_MARSHAL, "bPSz4, PushSize:$l \n", PushSize));
  1660. Win4Assert(4 == m_PushState);
  1661. Win4Assert( PushSize == 0 );
  1662. Win4Assert( (LONG) m_cFreeSpace >= 0 );
  1663. // There might be a BUG here. BUG! Shouldn't have to wait.
  1664. HRESULT hr = m_pISyncPush->Wait(0, WAIT_INFINITE);
  1665. if( SUCCEEDED(hr) )
  1666. hr = m_pAsyncPushPipe->Finish_Push();
  1667. if( SUCCEEDED(hr) )
  1668. {
  1669. if( (m_cPushBufferSize - m_cFreeSpace) > 0 )
  1670. {
  1671. // Data in buffer so send it.
  1672. hr = m_pAsyncPushPipe->Begin_Push(m_pPushBuffer,
  1673. m_cPushBufferSize - m_cFreeSpace);
  1674. if( FAILED(hr) )
  1675. goto asyncFailed;
  1676. hr = m_pISyncPush->Wait(0, WAIT_INFINITE);
  1677. if( FAILED(hr) )
  1678. goto asyncFailed;
  1679. hr = m_pAsyncPushPipe->Finish_Push();
  1680. }
  1681. if( SUCCEEDED(hr) )
  1682. {
  1683. // Push a zero size buffer to signal end of data.
  1684. hr = m_pAsyncPushPipe->Begin_Push(Buf, PushSize);
  1685. if( FAILED(hr) )
  1686. goto asyncFailed;
  1687. hr = m_pISyncPush->Wait(0, WAIT_INFINITE);
  1688. if( FAILED(hr) )
  1689. goto asyncFailed;
  1690. hr = m_pAsyncPushPipe->Finish_Push();
  1691. }
  1692. }
  1693. asyncFailed:
  1694. // Last call regardless of success or failure so clean async up.
  1695. CleanupProxy(&m_pPushBuffer,
  1696. &m_pPushCallObj,
  1697. &m_pAsyncPushPipe,
  1698. &m_pISyncPush);
  1699. return hr;
  1700. }
  1701. template<class T, const IID* ID, const IID *AsyncID, class I, class AsyncI>
  1702. //+**************************************************************************
  1703. // PushDone5(T *Buf, ULONG PushSize)
  1704. //
  1705. // Description: Push Done, so this should never be called.
  1706. //
  1707. // History:
  1708. // Date: Time: Developer: Action:
  1709. // 11/21/97 11:42:08 AM RichN Created.
  1710. //
  1711. // Notes:
  1712. //
  1713. //-**************************************************************************
  1714. HRESULT CPipeProxy<T, ID, AsyncID, I, AsyncI>
  1715. ::PushDone5(T *Buf, ULONG PushSize)
  1716. {
  1717. ComDebOut(( DEB_MARSHAL, "PushDone5, PushSize:%u \n"));
  1718. Win4Assert(FALSE && "Push call after completion.");
  1719. Win4Assert(5 == m_PushState);
  1720. return E_UNEXPECTED;
  1721. }