Leaked source code of windows server 2003
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.

1479 lines
37 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. Protocol.c
  5. Abstract:
  6. Another Reliable Protocol (on DirectPlay)
  7. Author:
  8. Aaron Ogus (aarono)
  9. Environment:
  10. Win32
  11. Revision History:
  12. Date Author Description
  13. ====== ====== ============================================================
  14. 12/10/96 aarono Original
  15. 05/11/97 aarono convert from C++ COM object to 'C' library
  16. 2/03/98 aarono fixed ProtocolGetCaps for RAW
  17. 2/18/98 aarono changed InitProtocol to work later in connect process
  18. added new API handlers SendEx, GetMessageQueue, stub Cancel
  19. 2/18/98 aarono added Cancel support
  20. 2/19/98 aarono don't hook Shutdown anymore, dplay calls us
  21. explicitly on DP_OPEN (InitProtocol) DP_CLOSE (FiniProtocol)
  22. 2/20/98 aarono B#18827 not pulling cancelled sends from Q properly
  23. 3/5/98 aarono B#18962 allow non-reliable enumsessions reply when using protocol
  24. this avoids a bug where a remote on an invalid IPX net enums us
  25. and we get bogged down with RIPing in the response path. Actually hangs
  26. the machine and sometimes crashes IPX.
  27. 6/6/98 aarono Turn on throttling and windowing
  28. 10/8/99 aarono Improve shutdown handling, avoid 1min hang with pending sends.
  29. Notes:
  30. All direct calls from DPLAY to the PROTOCOL occur in this file.
  31. --*/
  32. #include <windows.h>
  33. #include <mmsystem.h>
  34. #include <dplay.h>
  35. #include <dplaysp.h>
  36. #include <dplaypr.h>
  37. #include "mydebug.h"
  38. #include "handles.h"
  39. #include "arpd.h"
  40. #include "arpdint.h"
  41. #include "macros.h"
  42. #include "mytimer.h"
  43. /*
  44. Protocol Object Life:
  45. =====================
  46. The protocol object is allocated on the DPLAY interface immediately after
  47. the call to SPInit. The protocol block is allocated and tacked onto the
  48. DPLAY interface. If the object is not allocated, the protocol pointer
  49. will be NULL.
  50. When the SP shutdown handler is called, the protocol object is released,
  51. first making sure that all other structures off of the protocol have
  52. been freed and all memory pools have been freed.
  53. SESSION Life:
  54. =============
  55. Sessions are the structures that support the connection of a pair of
  56. PLAYERS. For each target playerid there is a SESSION structure.
  57. SESSIONS are accessed by converting playerids into indices into a
  58. session array, valid sessions are filled in, invalid or not yet seen
  59. ones are NULL. A session is allocated for every call to the SP
  60. CreatePlayer routine. When a DeletePlayer is received, the session
  61. is freed. There are races to create players and delete players so
  62. the session state is tracked. If the session is not in the OPEN
  63. state, mesages for the session are ABORTED/IGNORED(?). When the
  64. player is being removed, there may be stragling receives, these
  65. are rejected. Any packet received for a non-existent session is
  66. dropped. When a session is being closed, all pending sends are
  67. first completed.
  68. SEND Life:
  69. ==========
  70. STATISTICS Life:
  71. ================
  72. RECEIVE Life:
  73. =============
  74. How we hook in:
  75. ===============
  76. Receive:
  77. --------
  78. HandlePacket in the ISP table has been replaced by the protocol's
  79. ProtocolHandlePacket routine. Each call to HandlePacket comes along
  80. with a pISP, from which we derive the pProtocol. If no pProtocol exits
  81. on the object, then we just call the old HandlePacket routine, otherwise
  82. we examine the packet and do our thing depending on what type of message
  83. it is and/or negotiated session parameters.
  84. Send/CreatePlayer/DeletePlayer/Shutdown:
  85. ----------------------------------------
  86. If we install:
  87. We replace the interface pointers to these SP callbacks with our own and
  88. remember the existing ones. When we are called we do our processing and
  89. then call the handler in the SP. In the case of Send, we may not even
  90. call because we need to packetize the message.
  91. We also replace the packet size information in the SPData structure so that
  92. directplay's packetize and send code won't try to break up messages before
  93. we get them. System messages that we don't handle hopefully don't exceed
  94. the actual maximum frame size, else they will fail on a non-reliable
  95. transport.
  96. */
  97. #ifdef DEBUG
  98. extern VOID My_GlobalAllocInit();
  99. extern VOID My_GlobalAllocDeInit();
  100. #endif
  101. //
  102. // Global pools should only be inited once, this counts opens.
  103. // No lock req'd since calls to spinit serialized in DirectPlay itself.
  104. //
  105. UINT nInitCount = 0;
  106. /*=============================================================================
  107. InitProtocol - initialize the protocol block and hook into the send path.
  108. Description:
  109. After each SP is initialized (in SPInit) this routine is called to
  110. hook the SP callbacks for the protocol. Also the protocol information
  111. for this instance of the protocol is allocated and initialized.
  112. Parameters:
  113. LPSPINITDATA pInitData - initialization block that was passed to the
  114. SP. We use it to hook in.
  115. Return Values:
  116. DP_OK - successfully hooked in.
  117. pProtocol on DIRECTPLAY object points to protocol obj.
  118. DPERR_GENERIC - didn't hook in. Also pProtocol in the DIRECTPLAY
  119. object will be NULL.
  120. -----------------------------------------------------------------------------*/
  121. HRESULT WINAPI InitProtocol(DPLAYI_DPLAY *lpDPlay)
  122. {
  123. PPROTOCOL pProtocol;
  124. HRESULT hr;
  125. #define TABLE_INIT_SIZE 16
  126. #define TABLE_GROW_SIZE 16
  127. #ifdef DEBUG
  128. My_GlobalAllocInit();
  129. #endif
  130. // Allocate the protocol block;
  131. pProtocol=My_GlobalAlloc(GMEM_FIXED|GMEM_ZEROINIT,sizeof(PROTOCOL));
  132. if(!pProtocol){
  133. hr=DPERR_NOMEMORY;
  134. goto exit;
  135. }
  136. //
  137. // Initialize protocol variables.
  138. //
  139. pProtocol->m_lpDPlay=lpDPlay;
  140. pProtocol->m_lpISP=lpDPlay->pISP;
  141. pProtocol->m_dwSPHeaderSize=lpDPlay->dwSPHeaderSize;
  142. pProtocol->m_nSendThreads=0; // we support any number of send threads!
  143. pProtocol->m_eState=Initializing; // we are initing
  144. InitializeCriticalSection(&pProtocol->m_ObjLock);
  145. InitializeCriticalSection(&pProtocol->m_SPLock);
  146. // Session lookup by ID list.
  147. InitializeCriticalSection(&pProtocol->m_SessionLock);
  148. pProtocol->m_SessionListSize=0;
  149. pProtocol->m_pSessions=NULL;
  150. // GLOBAL SENDQ
  151. InitializeCriticalSection(&pProtocol->m_SendQLock);
  152. InitBilink(&pProtocol->m_GSendQ);
  153. //
  154. // Get Multi-Media Timer Information.
  155. //
  156. if( timeGetDevCaps(&pProtocol->m_timecaps,sizeof(TIMECAPS)) != TIMERR_NOERROR ){
  157. // make em up
  158. ASSERT(0);
  159. pProtocol->m_timecaps.wPeriodMin=5;
  160. pProtocol->m_timecaps.wPeriodMax=10000000;
  161. }
  162. // Send Thread Triggers - waits for Sends, DataGram IDs or Reliable IDs.
  163. pProtocol->m_hSendEvent=CreateEventA(NULL, FALSE, FALSE, NULL);
  164. if(!pProtocol->m_hSendEvent){
  165. ASSERT(0); //TRACE all paths.
  166. hr=DPERR_NOMEMORY;
  167. goto exit1;
  168. }
  169. // Various descriptor pools.
  170. // These can't fail.
  171. if(!nInitCount){
  172. InitializeCriticalSection(&g_SendTimeoutListLock);
  173. InitBilink(&g_BilinkSendTimeoutList);
  174. // only allocated once per process.
  175. InitSendDescs();
  176. InitSendStats();
  177. InitFrameBuffers();
  178. InitBufferManager();
  179. InitBufferPool();
  180. }
  181. InitRcvDescs(pProtocol);
  182. nInitCount++;
  183. //
  184. // Get the datagram frame size from the SP
  185. //
  186. {
  187. DPCAPS Caps;
  188. DPSP_GETCAPSDATA GetCapsData;
  189. memset(&Caps,0,sizeof(DPCAPS));
  190. Caps.dwMaxBufferSize = 0;
  191. Caps.dwSize = sizeof(DPCAPS);
  192. GetCapsData.dwFlags = 0;
  193. GetCapsData.lpCaps = &Caps;
  194. GetCapsData.idPlayer = 0;
  195. GetCapsData.lpISP = lpDPlay->pISP;
  196. CALLSP(lpDPlay->pcbSPCallbacks->GetCaps, &GetCapsData);
  197. pProtocol->m_dwSPMaxFrame=GetCapsData.lpCaps->dwMaxBufferSize;
  198. if(pProtocol->m_dwSPMaxFrame > 1400){
  199. // Necessary since UDP reports huge capacity even though no receiver can
  200. // successfully receive a datagram of that size without throttle.
  201. pProtocol->m_dwSPMaxFrame = 1400;
  202. }
  203. GetCapsData.dwFlags = DPCAPS_GUARANTEED;
  204. hr=CALLSP(lpDPlay->pcbSPCallbacks->GetCaps, &GetCapsData);
  205. if(hr==DP_OK){
  206. pProtocol->m_dwSPMaxGuaranteed=GetCapsData.lpCaps->dwMaxBufferSize;
  207. }
  208. if(!pProtocol->m_dwSPMaxGuaranteed){
  209. pProtocol->m_dwSPMaxGuaranteed=pProtocol->m_dwSPMaxFrame;
  210. }
  211. }
  212. Lock(&pProtocol->m_ObjLock);
  213. //
  214. // Spin up the send thread
  215. //
  216. pProtocol->m_nSendThreads++;
  217. // Need for serialization starts here...
  218. pProtocol->m_hSendThread[0]=CreateThread( NULL,
  219. 4000,
  220. SendThread,
  221. (LPVOID)pProtocol,
  222. 0,
  223. &pProtocol->m_dwSendThreadId[0]);
  224. if(!pProtocol->m_hSendThread[0]){
  225. ASSERT(0); //TRACE all paths.
  226. hr=DPERR_NOMEMORY;
  227. goto exit4;
  228. }
  229. pProtocol->lpHandleTable=InitHandleTable(TABLE_INIT_SIZE,&pProtocol->csHandleTable,TABLE_GROW_SIZE);
  230. if(!pProtocol->lpHandleTable){
  231. goto exit5;
  232. }
  233. pProtocol->m_eState=Running;
  234. Unlock(&pProtocol->m_ObjLock);
  235. hr=DP_OK;
  236. exit:
  237. if(hr==DP_OK){
  238. lpDPlay->pProtocol=(LPPROTOCOL_PART)pProtocol;
  239. } else {
  240. lpDPlay->pProtocol=NULL;
  241. }
  242. return hr;
  243. //exit6: if more init written, may need this.
  244. // FiniHandleTable(pProtocol->lpHandleTable, &pProtocol->csHandleTable);
  245. exit5:
  246. pProtocol->m_eState=ShuttingDown;
  247. SetEvent(pProtocol->m_hSendEvent);
  248. Unlock(&pProtocol->m_ObjLock);
  249. while(pProtocol->m_nSendThreads){
  250. // wait for the send thread to shut off.
  251. Sleep(0);
  252. }
  253. CloseHandle(pProtocol->m_hSendThread[0]);
  254. Lock(&pProtocol->m_ObjLock);
  255. exit4:
  256. Unlock(&pProtocol->m_ObjLock);
  257. //exit3:
  258. FiniRcvDescs(pProtocol);
  259. nInitCount--;
  260. if(!nInitCount){
  261. DeleteCriticalSection(&g_SendTimeoutListLock);
  262. FiniBufferPool();
  263. FiniBufferManager();
  264. FiniFrameBuffers();
  265. FiniSendStats();
  266. FiniSendDescs();
  267. }
  268. //exit2:
  269. CloseHandle(pProtocol->m_hSendEvent);
  270. exit1:
  271. DeleteCriticalSection(&pProtocol->m_SPLock);
  272. DeleteCriticalSection(&pProtocol->m_ObjLock);
  273. DeleteCriticalSection(&pProtocol->m_SessionLock);
  274. DeleteCriticalSection(&pProtocol->m_SendQLock);
  275. My_GlobalFree(pProtocol);
  276. goto exit;
  277. #undef TABLE_INIT_SIZE
  278. #undef TABLE_GROW_SIZE
  279. }
  280. /*=============================================================================
  281. FiniProtocol -
  282. Description:
  283. Parameters:
  284. Return Values:
  285. -----------------------------------------------------------------------------*/
  286. VOID WINAPI FiniProtocol(PPROTOCOL pProtocol)
  287. {
  288. DWORD tmKill;
  289. //
  290. // Kill the send thread.
  291. //
  292. DPF(1,"==>ProtShutdown\n");
  293. Lock(&pProtocol->m_ObjLock);
  294. pProtocol->m_eState=ShuttingDown;
  295. SetEvent(pProtocol->m_hSendEvent);
  296. while(pProtocol->m_nSendThreads){
  297. // wait for the send thread to shut off.
  298. Unlock(&pProtocol->m_ObjLock);
  299. Sleep(0);
  300. Lock(&pProtocol->m_ObjLock);
  301. }
  302. Unlock(&pProtocol->m_ObjLock);
  303. CloseHandle(pProtocol->m_hSendThread[0]);
  304. DPF(1,"SHUTDOWN: Protocol Send Thread ShutDown, waiting for sessions\n");
  305. tmKill=timeGetTime()+60000;
  306. Lock(&pProtocol->m_SessionLock);
  307. while(pProtocol->m_nSessions && (((INT)(tmKill-timeGetTime())) > 0)){
  308. UINT SendRc;
  309. Unlock(&pProtocol->m_SessionLock);
  310. //NOTE: race. when m_nSessions dereffed, there
  311. // is a race for the protocol to be freed.
  312. Sleep(55);
  313. do {
  314. SendRc=SendHandler(pProtocol);
  315. } while (SendRc!=DPERR_NOMESSAGES);
  316. Lock(&pProtocol->m_SessionLock);
  317. }
  318. DPF(1,"SHUTDOWN: Sessions All Gone Freeing other objects.\n");
  319. //
  320. // Free the SESSION table
  321. //
  322. if(pProtocol->m_pSessions){
  323. My_GlobalFree(pProtocol->m_pSessions);
  324. pProtocol->m_pSessions=0;
  325. }
  326. Unlock(&pProtocol->m_SessionLock);
  327. DeleteCriticalSection(&pProtocol->m_SendQLock);
  328. DeleteCriticalSection(&pProtocol->m_SessionLock);
  329. DeleteCriticalSection(&pProtocol->m_SPLock);
  330. DeleteCriticalSection(&pProtocol->m_ObjLock);
  331. CloseHandle(pProtocol->m_hSendEvent);
  332. FiniRcvDescs(pProtocol);
  333. nInitCount--;
  334. if(!nInitCount){
  335. // Last one out, turn off the lights...
  336. DeleteCriticalSection(&g_SendTimeoutListLock);
  337. FiniBufferPool();
  338. FiniBufferManager();
  339. FiniFrameBuffers();
  340. FiniSendStats();
  341. FiniSendDescs();
  342. }
  343. FiniHandleTable(pProtocol->lpHandleTable, &pProtocol->csHandleTable);
  344. My_GlobalFree(pProtocol);
  345. #ifdef DEBUG
  346. My_GlobalAllocDeInit();
  347. #endif
  348. }
  349. /*=============================================================================
  350. ProtocolCreatePlayer - Called by DPlay when SP needs to be notified of new
  351. player creation.
  352. Description:
  353. Creates a session for the id. OPTIMIZATION: if local, don't need this?
  354. Also notifies the SP.
  355. Parameters:
  356. Return Values:
  357. -----------------------------------------------------------------------------*/
  358. HRESULT WINAPI ProtocolCreatePlayer(LPDPSP_CREATEPLAYERDATA pCreatePlayerData)
  359. {
  360. DPLAYI_DPLAY *lpDPlay;
  361. PPROTOCOL pProtocol;
  362. HRESULT hr=DP_OK;
  363. lpDPlay=((DPLAYI_DPLAY_INT *)pCreatePlayerData->lpISP)->lpDPlay;
  364. ASSERT(lpDPlay);
  365. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  366. ASSERT(pProtocol);
  367. pProtocol->m_dwIDKey=(DWORD)lpDPlay->lpsdDesc->dwReserved1;
  368. // Creates the session and gets one refcount.
  369. hr=CreateNewSession(pProtocol, pCreatePlayerData->idPlayer);
  370. if(hr==DP_OK){
  371. // Chain the call to the real provider.
  372. Lock(&pProtocol->m_SPLock);
  373. if(lpDPlay->pcbSPCallbacks->CreatePlayer){
  374. hr=CALLSP(lpDPlay->pcbSPCallbacks->CreatePlayer,pCreatePlayerData);
  375. }
  376. Unlock(&pProtocol->m_SPLock);
  377. if(hr!=DP_OK){
  378. PSESSION pSession;
  379. pSession=GetSession(pProtocol,pCreatePlayerData->idPlayer); //adds a ref
  380. if(pSession){
  381. DecSessionRef(pSession); // unGetSession
  382. DecSessionRef(pSession); // blow it away, noone could access yet.
  383. }
  384. }
  385. }
  386. return hr;
  387. }
  388. /*=============================================================================
  389. ProtocolPreNotifyDeletePlayer
  390. Called to tell us a DELETEPLAYER message was enqueued for a particular
  391. player. We need to drop the player NOW!
  392. We don't notify the SP, that will happen when we are called in
  393. ProtocolDeletePlayer later when the pending queue is processed.
  394. Description:
  395. Dereference the session for the player.
  396. Parameters:
  397. Return Values:
  398. -----------------------------------------------------------------------------*/
  399. HRESULT WINAPI ProtocolPreNotifyDeletePlayer(LPDPLAYI_DPLAY this, DPID idPlayer)
  400. {
  401. PPROTOCOL pProtocol;
  402. PSESSION pSession;
  403. HRESULT hr=DP_OK;
  404. pProtocol=(PPROTOCOL)this->pProtocol;
  405. ASSERT(pProtocol);
  406. pSession=GetSession(pProtocol,idPlayer);
  407. DPF(9,"==>Protocol Prenotify Delete Player %x, pSession %x\n",idPlayer, pSession);
  408. if(pSession){
  409. pSession->hClosingEvent=0;
  410. #if 0
  411. //NOTE: if you even think about putting this back, also do it in ProtocolDeletePlayer
  412. hClosingEvent=pSession->hClosingEvent=CreateEventA(NULL,FALSE,FALSE,NULL);
  413. if(hClosingEvent){
  414. ResetEvent(hClosingEvent);
  415. }
  416. #endif
  417. Lock(&pProtocol->m_SendQLock);
  418. Lock(&pSession->SessionLock);
  419. switch(pSession->eState)
  420. {
  421. case Open:
  422. TimeOutSession(pSession);
  423. Unlock(&pSession->SessionLock);
  424. Unlock(&pProtocol->m_SendQLock);
  425. DecSessionRef(pSession); // balance GetSession
  426. DecSessionRef(pSession); // balance Creation - may destroy session, and signal event
  427. break;
  428. case Closing:
  429. case Closed:
  430. Unlock(&pSession->SessionLock);
  431. Unlock(&pProtocol->m_SendQLock);
  432. DecSessionRef(pSession); // balance GetSession
  433. break;
  434. }
  435. #if 0
  436. if(hClosingEvent){
  437. // Wait(hClosingEvent);
  438. CloseHandle(hClosingEvent);
  439. } else {
  440. DPF(0,"ProtocolPreNotifyDeletePlayer: couldn't get close event handle--not waiting...\n");
  441. ASSERT(0);
  442. }
  443. #endif
  444. } else {
  445. DPF(0,"ProtocolPreNotifyDeletePlayer: couldn't find session for playerid %x\n",idPlayer);
  446. ASSERT(0);
  447. }
  448. DPF(9,"<==Protocol Prenotify DeletePlayer, hr=%x\n",hr);
  449. return hr;
  450. }
  451. /*=============================================================================
  452. ProtocolDeletePlayer - Called by DPlay when SP needs to be notified of
  453. player deletion.
  454. Description:
  455. Dereference the session for the player. Then notifies the SP.
  456. Parameters:
  457. Return Values:
  458. -----------------------------------------------------------------------------*/
  459. HRESULT WINAPI ProtocolDeletePlayer(LPDPSP_DELETEPLAYERDATA pDeletePlayerData)
  460. {
  461. DPLAYI_DPLAY *lpDPlay;
  462. PPROTOCOL pProtocol;
  463. PSESSION pSession;
  464. HRESULT hr=DP_OK;
  465. //HANDLE hClosingEvent;
  466. lpDPlay=((DPLAYI_DPLAY_INT *)pDeletePlayerData->lpISP)->lpDPlay;
  467. ASSERT(lpDPlay);
  468. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  469. ASSERT(pProtocol);
  470. pSession=GetSession(pProtocol,pDeletePlayerData->idPlayer);
  471. DPF(9,"==>Protocol Delete Player %x, pSession %x\n",pDeletePlayerData->idPlayer, pSession);
  472. if(pSession){
  473. pSession->hClosingEvent=0;
  474. #if 0
  475. //NOTE: if you even think about putting this back, also do it in ProtocolPreNotifyDeletePlayer
  476. hClosingEvent=pSession->hClosingEvent=CreateEventA(NULL,FALSE,FALSE,NULL);
  477. if(hClosingEvent){
  478. ResetEvent(hClosingEvent);
  479. }
  480. #endif
  481. Lock(&pProtocol->m_SendQLock);
  482. Lock(&pSession->SessionLock);
  483. switch(pSession->eState)
  484. {
  485. case Open:
  486. TimeOutSession(pSession);
  487. case Closing:
  488. Unlock(&pSession->SessionLock);
  489. Unlock(&pProtocol->m_SendQLock);
  490. DecSessionRef(pSession); // balance GetSession
  491. DecSessionRef(pSession); // balance Creation - may destroy session, and signal event
  492. break;
  493. case Closed:
  494. Unlock(&pSession->SessionLock);
  495. Unlock(&pProtocol->m_SendQLock);
  496. DecSessionRef(pSession); // balance GetSession
  497. break;
  498. }
  499. #if 0
  500. if(hClosingEvent){
  501. // Wait(hClosingEvent);
  502. CloseHandle(hClosingEvent);
  503. } else {
  504. DPF(0,"ProtocolDeletePlayer: couldn't get close event handle--not waiting...\n");
  505. ASSERT(0);
  506. }
  507. #endif
  508. } else {
  509. DPF(0,"ProtocolDeletePlayer: couldn't find session for playerid %x, ok if ProtocolPreNotifyDeletPlayer ran.\n",pDeletePlayerData->idPlayer);
  510. }
  511. DPF(9,"Protocol, deleted player id %x\n",pDeletePlayerData->idPlayer);
  512. DPF(9,"<==ProtocolDeletePlayer, hr=%x\n",hr);
  513. return hr;
  514. }
  515. /*=============================================================================
  516. ProtocolSendEx -
  517. Description:
  518. Parameters:
  519. Return Values:
  520. -----------------------------------------------------------------------------*/
  521. HRESULT WINAPI ProtocolSendEx(LPDPSP_SENDEXDATA pSendData)
  522. {
  523. DPSP_SENDDATA sd;
  524. DPLAYI_DPLAY *lpDPlay;
  525. PPROTOCOL pProtocol;
  526. HRESULT hr=DP_OK;
  527. DWORD dwCommand;
  528. PUCHAR pBuffer;
  529. lpDPlay=((DPLAYI_DPLAY_INT *)pSendData->lpISP)->lpDPlay;
  530. ASSERT(lpDPlay);
  531. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  532. ASSERT(pProtocol);
  533. ASSERT(lpDPlay->dwFlags & DPLAYI_PROTOCOL);
  534. if(pSendData->lpSendBuffers->len >= 8){
  535. pBuffer=pSendData->lpSendBuffers->pData;
  536. if((*((DWORD *)pBuffer)) == SIGNATURE('p','l','a','y')){
  537. dwCommand=GET_MESSAGE_COMMAND((LPMSG_SYSMESSAGE)pBuffer);
  538. switch(dwCommand){
  539. case DPSP_MSG_PACKET2_DATA:
  540. case DPSP_MSG_PACKET2_ACK:
  541. case DPSP_MSG_PACKET:
  542. goto send_non_protocol_message;
  543. break;
  544. default:
  545. break;
  546. }
  547. }
  548. }
  549. // OPTIMIZATION:, make Send take the SENDEXDATA struct only.
  550. hr=Send(pProtocol,
  551. pSendData->idPlayerFrom,
  552. pSendData->idPlayerTo,
  553. pSendData->dwFlags,
  554. pSendData->lpSendBuffers,
  555. pSendData->cBuffers,
  556. pSendData->dwPriority,
  557. pSendData->dwTimeout,
  558. pSendData->lpDPContext,
  559. pSendData->lpdwSPMsgID,
  560. TRUE,
  561. NULL); // forces us to be called back in InternalSendComplete, if Send is ASYNC.
  562. return hr;
  563. send_non_protocol_message:
  564. ENTER_DPLAY();
  565. Lock(&pProtocol->m_SPLock);
  566. if(lpDPlay->pcbSPCallbacks->SendEx){
  567. hr=CALLSP(lpDPlay->pcbSPCallbacks->SendEx,pSendData);
  568. } else {
  569. hr=ConvertSendExDataToSendData(lpDPlay, pSendData, &sd);
  570. if(hr==DP_OK){
  571. hr=CALLSP(lpDPlay->pcbSPCallbacks->Send, &sd);
  572. MsgFree(NULL, sd.lpMessage);
  573. }
  574. }
  575. Unlock(&pProtocol->m_SPLock);
  576. LEAVE_DPLAY();
  577. return hr;
  578. }
  579. /*=============================================================================
  580. ProtocolGetMessageQueue -
  581. Description:
  582. Parameters:
  583. Return Values:
  584. -----------------------------------------------------------------------------*/
  585. HRESULT WINAPI ProtocolGetMessageQueue(LPDPSP_GETMESSAGEQUEUEDATA pGetMessageQueueData)
  586. {
  587. #define pData pGetMessageQueueData
  588. DPLAYI_DPLAY *lpDPlay;
  589. PPROTOCOL pProtocol;
  590. PSESSION pSession;
  591. HRESULT hr=DP_OK;
  592. BILINK *pBilink;
  593. PSEND pSend;
  594. DWORD dwNumMsgs;
  595. DWORD dwNumBytes;
  596. lpDPlay=((DPLAYI_DPLAY_INT *)pData->lpISP)->lpDPlay;
  597. ASSERT(lpDPlay);
  598. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  599. ASSERT(pProtocol);
  600. dwNumMsgs=0;
  601. dwNumBytes=0;
  602. if(!pData->idTo && !pData->idFrom){
  603. // just wants totals, I know that!
  604. EnterCriticalSection(&pProtocol->m_SendQLock);
  605. dwNumMsgs = pProtocol->m_dwMessagesPending;
  606. dwNumBytes = pProtocol->m_dwBytesPending;
  607. LeaveCriticalSection(&pProtocol->m_SendQLock);
  608. } else if(pData->idTo){
  609. // Given idTo, walk that target's sendQ
  610. pSession=GetSysSession(pProtocol,pData->idTo);
  611. if(!pSession) {
  612. DPF(0,"GetMessageQueue: NO SESSION for idTo %x, returning INVALIDPLAYER\n",pData->idTo);
  613. hr=DPERR_INVALIDPLAYER;
  614. goto exit;
  615. }
  616. EnterCriticalSection(&pSession->SessionLock);
  617. pBilink=pSession->SendQ.next;
  618. while(pBilink != &pSession->SendQ){
  619. pSend=CONTAINING_RECORD(pBilink, SEND, SendQ);
  620. pBilink=pBilink->next;
  621. if((pSend->idTo==pData->idTo) && (!pData->idFrom || (pSend->idFrom == pData->idFrom))){
  622. dwNumBytes += pSend->MessageSize;
  623. dwNumMsgs += 1;
  624. }
  625. }
  626. LeaveCriticalSection(&pSession->SessionLock);
  627. DecSessionRef(pSession);
  628. } else {
  629. ASSERT(pData->idFrom);
  630. // Geting Queue for a from id, this is most costly
  631. EnterCriticalSection(&pProtocol->m_SendQLock);
  632. pBilink=pProtocol->m_GSendQ.next;
  633. while(pBilink != &pProtocol->m_GSendQ){
  634. pSend=CONTAINING_RECORD(pBilink, SEND, m_GSendQ);
  635. pBilink=pBilink->next;
  636. if(pData->idFrom == pSend->idFrom){
  637. if(!pData->idTo || pData->idTo==pSend->idTo){
  638. dwNumBytes += pSend->MessageSize;
  639. dwNumMsgs += 1;
  640. }
  641. }
  642. }
  643. LeaveCriticalSection(&pProtocol->m_SendQLock);
  644. }
  645. if(pData->lpdwNumMsgs){
  646. *pData->lpdwNumMsgs=dwNumMsgs;
  647. }
  648. if(pData->lpdwNumBytes){
  649. *pData->lpdwNumBytes=dwNumBytes;
  650. }
  651. exit:
  652. return hr;
  653. #undef pData
  654. }
  655. /*=============================================================================
  656. ProtocolCancel -
  657. Description:
  658. Parameters:
  659. Return Values:
  660. -----------------------------------------------------------------------------*/
  661. HRESULT WINAPI ProtocolCancel(LPDPSP_CANCELDATA pCancelData)
  662. {
  663. #define pData pCancelData
  664. DPLAYI_DPLAY *lpDPlay;
  665. PPROTOCOL pProtocol;
  666. HRESULT hr=DP_OK;
  667. DWORD nCancelled=0;
  668. BILINK *pBilink;
  669. BOOL bCancel;
  670. UINT i;
  671. UINT j;
  672. DWORD dwContext;
  673. PSEND pSend;
  674. lpDPlay=((DPLAYI_DPLAY_INT *)pData->lpISP)->lpDPlay;
  675. ASSERT(lpDPlay);
  676. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  677. ASSERT(pProtocol);
  678. EnterCriticalSection(&pProtocol->m_SendQLock);
  679. if(pData->dwFlags) {
  680. // either cancelpriority or cancel all, either way we
  681. // need to scan...
  682. pBilink=pProtocol->m_GSendQ.next;
  683. while(pBilink!=&pProtocol->m_GSendQ){
  684. pSend=CONTAINING_RECORD(pBilink, SEND, m_GSendQ);
  685. pBilink=pBilink->next;
  686. bCancel=FALSE;
  687. Lock(&pSend->SendLock);
  688. switch(pSend->SendState){
  689. case Start:
  690. case WaitingForId:
  691. if(pData->dwFlags & DPCANCELSEND_PRIORITY) {
  692. // Cancel sends in priority range.
  693. if((pSend->Priority <= pData->dwMaxPriority) &&
  694. (pSend->Priority >= pData->dwMinPriority)){
  695. bCancel=TRUE;
  696. }
  697. } else if(pData->dwFlags & DPCANCELSEND_ALL) {
  698. // Cancel all sends that can be.
  699. bCancel=TRUE;
  700. } else {
  701. ASSERT(0); // Invalid flags, should never happen
  702. }
  703. if(bCancel){
  704. if(pSend->SendState == WaitingForId){
  705. if(pSend->dwFlags & DPSEND_GUARANTEED){
  706. InterlockedDecrement(&pSend->pSession->nWaitingForMessageid);
  707. } else {
  708. InterlockedDecrement(&pSend->pSession->nWaitingForDGMessageid);
  709. }
  710. }
  711. nCancelled+=1;
  712. pSend->SendState=Cancelled;
  713. }
  714. break;
  715. default:
  716. DPF(5,"Couldn't cancel send %x in State %d, already sending...\n",pSend,pSend->SendState);
  717. }
  718. Unlock(&pSend->SendLock);
  719. }
  720. } else {
  721. // No flags, therefore we have a list to cancel so lookup
  722. // each send and cancel rather than scanning as above.
  723. // Run through the list, find the sends and lock em 1st, if we find one that doesn't lookup,
  724. // or one not in the start state, then we bail. We then unlock them all.
  725. for(i=0;i<pData->cSPMsgID;i++){
  726. dwContext=(DWORD)((DWORD_PTR)((*pData->lprglpvSPMsgID)[i]));
  727. pSend=(PSEND)ReadHandleTableEntry(&pProtocol->lpHandleTable, &pProtocol->csHandleTable, dwContext);
  728. if(pSend){
  729. Lock(&pSend->SendLock);
  730. if(pSend->SendState != Start && pSend->SendState != WaitingForId){
  731. Unlock(&pSend->SendLock);
  732. hr=DPERR_CANCELFAILED;
  733. break;
  734. }
  735. } else {
  736. hr=DPERR_CANCELFAILED;
  737. break;
  738. }
  739. }
  740. if(hr==DPERR_CANCELFAILED) {
  741. // release all the locks.
  742. for(j=0;j<i;j++){
  743. dwContext=(DWORD)((DWORD_PTR)((*pData->lprglpvSPMsgID)[j]));
  744. pSend=(PSEND)ReadHandleTableEntry(&pProtocol->lpHandleTable, &pProtocol->csHandleTable, dwContext);
  745. ASSERT(pSend);
  746. Unlock(&pSend->SendLock);
  747. }
  748. } else {
  749. // mark the sends cancelled and release all the locks.
  750. for(i=0;i<pData->cSPMsgID;i++){
  751. dwContext=(DWORD)((DWORD_PTR)((*pData->lprglpvSPMsgID)[i]));
  752. pSend=(PSEND)ReadHandleTableEntry(&pProtocol->lpHandleTable, &pProtocol->csHandleTable, dwContext);
  753. ASSERT(pSend);
  754. if(pSend->SendState == WaitingForId){
  755. if(pSend->dwFlags & DPSEND_GUARANTEED){
  756. InterlockedDecrement(&pSend->pSession->nWaitingForMessageid);
  757. } else {
  758. InterlockedDecrement(&pSend->pSession->nWaitingForDGMessageid);
  759. }
  760. }
  761. pSend->SendState=Cancelled;
  762. nCancelled+=1;
  763. Unlock(&pSend->SendLock);
  764. }
  765. }
  766. }
  767. LeaveCriticalSection(&pProtocol->m_SendQLock);
  768. SetEvent(pProtocol->m_hSendEvent);
  769. return hr;
  770. #undef pData
  771. }
  772. /*=============================================================================
  773. ProtocolSend - Send A message synchronously.
  774. Description:
  775. Parameters:
  776. Return Values:
  777. -----------------------------------------------------------------------------*/
  778. DWORD bForceDGAsync=FALSE;
  779. HRESULT WINAPI ProtocolSend(LPDPSP_SENDDATA pSendData)
  780. {
  781. DPLAYI_DPLAY *lpDPlay;
  782. PPROTOCOL pProtocol;
  783. HRESULT hr=DP_OK;
  784. DWORD dwCommand;
  785. DWORD dwPriority;
  786. DWORD dwFlags;
  787. PUCHAR pBuffer;
  788. MEMDESC memdesc;
  789. lpDPlay=((DPLAYI_DPLAY_INT *)pSendData->lpISP)->lpDPlay;
  790. ASSERT(lpDPlay);
  791. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  792. ASSERT(pProtocol);
  793. pBuffer=&(((PUCHAR)(pSendData->lpMessage))[pProtocol->m_dwSPHeaderSize]);
  794. if((*((DWORD *)pBuffer)) == SIGNATURE('p','l','a','y')){
  795. dwCommand=GET_MESSAGE_COMMAND((LPMSG_SYSMESSAGE)pBuffer);
  796. switch(dwCommand){
  797. case DPSP_MSG_PACKET2_DATA:
  798. case DPSP_MSG_PACKET2_ACK:
  799. case DPSP_MSG_ENUMSESSIONSREPLY:
  800. case DPSP_MSG_PACKET:
  801. goto send_non_protocol_message;
  802. break;
  803. default:
  804. break;
  805. }
  806. }
  807. memdesc.pData=((PUCHAR)pSendData->lpMessage)+pProtocol->m_dwSPHeaderSize;
  808. memdesc.len =pSendData->dwMessageSize-pProtocol->m_dwSPHeaderSize;
  809. if(pSendData->dwFlags & DPSEND_HIGHPRIORITY){
  810. pSendData->dwFlags &= ~(DPSEND_HIGHPRIORITY);
  811. dwPriority=0xFFFFFFFE;
  812. } else {
  813. dwPriority=1000;
  814. }
  815. dwFlags = pSendData->dwFlags;
  816. if(bForceDGAsync && !(dwFlags&DPSEND_GUARANTEE)){
  817. // for testing old apps with protocol make datagram sends
  818. // async so that the application doesn't block.
  819. dwFlags |= DPSEND_ASYNC;
  820. }
  821. hr=Send(pProtocol,
  822. pSendData->idPlayerFrom,
  823. pSendData->idPlayerTo,
  824. dwFlags,
  825. &memdesc,
  826. 1,
  827. dwPriority,
  828. 0,
  829. NULL,
  830. NULL,
  831. FALSE,
  832. NULL);
  833. return hr;
  834. send_non_protocol_message:
  835. if((*((DWORD *)pBuffer)) == SIGNATURE('p','l','a','y')){
  836. DPF(9,"Send Message %d Ver %d\n", pBuffer[4]+(pBuffer[5]<<8),pBuffer[6]+(pBuffer[7]<<8));
  837. }
  838. ENTER_DPLAY();
  839. Lock(&pProtocol->m_SPLock);
  840. hr=CALLSP(lpDPlay->pcbSPCallbacks->Send,pSendData);
  841. Unlock(&pProtocol->m_SPLock);
  842. LEAVE_DPLAY();
  843. return hr;
  844. }
  845. /*=============================================================================
  846. GetPlayerLatency - Get Latency for a player
  847. Description:
  848. Parameters:
  849. Return Values:
  850. -----------------------------------------------------------------------------*/
  851. DWORD GetPlayerLatency(LPDPLAYI_DPLAY lpDPlay, DPID idPlayer)
  852. {
  853. PPROTOCOL pProtocol;
  854. PSESSION pSession;
  855. DWORD dwLatency=0; // default, means I don't know latency
  856. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  857. ASSERT(pProtocol);
  858. pSession=GetSession(pProtocol,idPlayer);
  859. DPF(9,"==>Protocol GetPlayer Latency %x, pSession %x\n",idPlayer, pSession);
  860. if(pSession){
  861. Lock(&pSession->SessionLock);
  862. // Protocol Latency is round trip in 24.8 fixed point,
  863. // we net round trip latency divided by 2, so shift right 9.
  864. dwLatency=(pSession->FpLocalAverageLatency)>>(9);
  865. Unlock(&pSession->SessionLock);
  866. DecSessionRef(pSession); // balance GetSession
  867. }
  868. DPF(9,"<==Protocol GetPlayerLatency, returning dwLat=%x\n",dwLatency);
  869. return dwLatency;
  870. }
  871. /*=============================================================================
  872. ProtocolGetCaps - Get Service Provider Capabilities
  873. Description:
  874. Parameters:
  875. Return Values:
  876. -----------------------------------------------------------------------------*/
  877. HRESULT WINAPI ProtocolGetCaps(LPDPSP_GETCAPSDATA pGetCapsData)
  878. {
  879. #define ALL_PROTOCOLCAPS (DPCAPS_SENDPRIORITYSUPPORTED | \
  880. DPCAPS_ASYNCSUPPORTED | \
  881. DPCAPS_SENDTIMEOUTSUPPORTED | \
  882. DPCAPS_ASYNCCANCELSUPPORTED )
  883. DPLAYI_DPLAY *lpDPlay;
  884. PPROTOCOL pProtocol;
  885. HRESULT hr=DP_OK;
  886. lpDPlay=((DPLAYI_DPLAY_INT *)pGetCapsData->lpISP)->lpDPlay;
  887. ASSERT(lpDPlay);
  888. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  889. ASSERT(pProtocol);
  890. // Chain the call to the real provider.
  891. Lock(&pProtocol->m_SPLock);
  892. if(lpDPlay->pcbSPCallbacks->GetCaps){
  893. hr=CALLSP(lpDPlay->pcbSPCallbacks->GetCaps,pGetCapsData);
  894. }
  895. Unlock(&pProtocol->m_SPLock);
  896. // if it fails, this doesn't hurt
  897. if(lpDPlay->dwFlags & DPLAYI_DPLAY_PROTOCOL)
  898. {
  899. // 1 megabyte is lots (says Jamie Osborne)
  900. pGetCapsData->lpCaps->dwMaxBufferSize=0x100000;
  901. pGetCapsData->lpCaps->dwFlags |= ALL_PROTOCOLCAPS;
  902. }
  903. if(pGetCapsData->idPlayer && !pGetCapsData->lpCaps->dwLatency){
  904. // SP refused to guess at latency, so use ours.
  905. pGetCapsData->lpCaps->dwLatency=GetPlayerLatency(lpDPlay, pGetCapsData->idPlayer);
  906. }
  907. return hr;
  908. #undef ALL_PROTOCOLCAPS
  909. }
  910. DWORD ExtractProtocolIds(PUCHAR pInBuffer, DWORD cbBuffer, PUINT pdwIdFrom, PUINT pdwIdTo)
  911. {
  912. PCHAR pBuffer=pInBuffer;
  913. DWORD dwIdFrom=0;
  914. DWORD dwIdTo=0;
  915. DWORD cbLeft=cbBuffer;
  916. dwIdFrom=*pBuffer&0x7F;
  917. if(*pBuffer&0x80){
  918. pBuffer++;
  919. cbLeft--;
  920. if(0==cbLeft)goto error_exit;
  921. dwIdFrom=dwIdFrom+((*pBuffer&0x7F)<<7);
  922. if(*pBuffer&0x80){
  923. pBuffer++;
  924. cbLeft--;
  925. if(0==cbLeft)goto error_exit;
  926. dwIdFrom=dwIdFrom+((*pBuffer&0x7F)<<14);
  927. if(dwIdFrom > 0xFFFF || *pBuffer&0x80){
  928. DPF(0,"INVALID FROM ID %x IN MESSAGE, REJECTING PACKET\n",dwIdFrom);
  929. return 0;
  930. }
  931. }
  932. }
  933. if(dwIdFrom==0xFFFF){
  934. dwIdFrom=0x70;
  935. }
  936. pBuffer++;
  937. cbLeft--;
  938. if(0==cbLeft)goto error_exit;
  939. dwIdTo=*pBuffer&0x7F;
  940. if(*pBuffer&0x80){
  941. pBuffer++;
  942. cbLeft--;
  943. if(0==cbLeft)goto error_exit;
  944. dwIdTo=dwIdTo+((*pBuffer&0x7F)<<7);
  945. if(*pBuffer&0x80){
  946. pBuffer++;
  947. cbLeft--;
  948. if(0==cbLeft)goto error_exit;
  949. dwIdTo=dwIdTo+((*pBuffer&0x7F)<<14);
  950. if(dwIdTo > 0xFFFF || *pBuffer&0x80){
  951. DPF(0,"INVALID TO ID %x IN MESSAGE, REJECTING PACKET\n",dwIdTo);
  952. return 0;
  953. }
  954. }
  955. }
  956. *pdwIdFrom=dwIdFrom;
  957. *pdwIdTo=dwIdTo;
  958. pBuffer++;
  959. cbLeft--;
  960. if(0==cbLeft)goto error_exit;
  961. // DPF(9, "In ExtractProtocolIds: from %x became %x\n", *(DWORD *)pInBuffer, dwIdFrom);
  962. return (DWORD)(pBuffer-pInBuffer);
  963. error_exit:
  964. DPF(1,"SECURITY WARN: Invalid To/From in Protoocl ID fields");
  965. return 0;
  966. }
  967. /*=============================================================================
  968. DP_SP_ProtocolHandleMessage - Packet handler for Dplay protocol
  969. Description:
  970. All messages go through here when the protocol is active. If the
  971. message is not a protocol message, this routine doesn't process
  972. it and returns DPERR_NOTHANDLED to let other layers (probably
  973. PacketizeAndSend) process it.
  974. Parameters:
  975. IDirectPlaySP * pISP - pointer to pISP interface
  976. LPBYTE pReceiveBuffer - a single buffer of data
  977. DWORD dwMessageSize - length of the buffer
  978. LPVOID pvSPHeader - pointer to SP's header used in Reply
  979. Return Values:
  980. Notes:
  981. We don't worry about re-entering DP_SP_HandleMessage since
  982. we are calling only when a receive has completed and we are in the
  983. callback from the SP to directplay, so effectively the SP is
  984. serializing receives for us.
  985. The receive code is actually written to be re-entrant, so if we
  986. ever decide to allow concurrent receive processing the protocol
  987. can handle it.
  988. Protocol messages start with 'P','L','A','Y','0xFF' when not RAW.
  989. DPLAY gets handleMessage first, and hands off to protocol if active.
  990. -----------------------------------------------------------------------------*/
  991. HRESULT DPAPI DP_SP_ProtocolHandleMessage(
  992. IDirectPlaySP * pISP,
  993. LPBYTE pReceiveBuffer,
  994. DWORD dwMessageSize,
  995. LPVOID pvSPHeader)
  996. {
  997. DPLAYI_DPLAY *lpDPlay;
  998. DWORD dwIdFrom, dwIdTo;
  999. PBUFFER pRcvBuffer;
  1000. PPROTOCOL pProtocol;
  1001. lpDPlay=DPLAY_FROM_INT(pISP);
  1002. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  1003. if(!pProtocol){
  1004. goto handle_non_protocol_message;
  1005. }
  1006. if(pProtocol->m_lpDPlay->dwFlags & DPLAYI_DPLAY_PROTOCOL){
  1007. // Running in RAW mode there is no dplay header on protocol
  1008. // messages. If we see one with a header or we don't receive
  1009. // a message large enough to be a protocol message we punt it.
  1010. if(dwMessageSize >= 4 &&
  1011. (*((DWORD *)pReceiveBuffer)) == SIGNATURE('p','l','a','y'))
  1012. {
  1013. // Got a system message.
  1014. goto handle_non_protocol_message;
  1015. }
  1016. if( dwMessageSize < 6 ){
  1017. goto handle_non_protocol_message;
  1018. }
  1019. } else {
  1020. // this can happen when shutting down.
  1021. DPF(0,"Protocol still up, but no bits set, not handling receive (must be shutting down?)");
  1022. goto handle_non_protocol_message;
  1023. }
  1024. // Hey, this must be ours...
  1025. Lock(&pProtocol->m_ObjLock);
  1026. if(pProtocol->m_eState==Running){ // just a sanity check, we don't depend on it after dropping lock.
  1027. DWORD idLen;
  1028. Unlock(&pProtocol->m_ObjLock);
  1029. idLen = ExtractProtocolIds(pReceiveBuffer,dwMessageSize,&dwIdFrom,&dwIdTo);
  1030. if(!idLen){
  1031. goto handle_non_protocol_message;
  1032. }
  1033. if(dwMessageSize <= idLen){
  1034. DPF(1,"SECURITY WARN: protocol header contains Ids but no content, illegal");
  1035. return DP_OK;
  1036. }
  1037. pRcvBuffer=GetFrameBuffer(dwMessageSize-idLen);
  1038. if(!pRcvBuffer){
  1039. // couldn't allocate a buffer, but we are allowed to drop frames, so drop it.
  1040. return DP_OK;
  1041. }
  1042. pRcvBuffer->len=dwMessageSize-idLen;
  1043. memcpy(pRcvBuffer->pData, pReceiveBuffer+idLen,pRcvBuffer->len);
  1044. DPF(9,"DP_SP_ProtocolHandleMessage From %x To %x\n",dwIdFrom,dwIdTo);
  1045. ENTER_DPLAY();
  1046. ProtocolReceive((PPROTOCOL)lpDPlay->pProtocol, (WORD)dwIdFrom, (WORD)dwIdTo, pRcvBuffer,pvSPHeader);
  1047. LEAVE_DPLAY();
  1048. } else {
  1049. Unlock(&pProtocol->m_ObjLock);
  1050. }
  1051. return DP_OK;
  1052. handle_non_protocol_message:
  1053. return DPERR_NOTHANDLED;
  1054. }
  1055. // DP_SP_ProtocolSendComplete is the callback handler for all completions since there is no other
  1056. // way to wrap the completion. When the protocol is not present, this just calls the DPLAY handler
  1057. // immediately.
  1058. VOID DPAPI DP_SP_ProtocolSendComplete(
  1059. IDirectPlaySP * pISP,
  1060. LPVOID lpvContext,
  1061. HRESULT CompletionStatus)
  1062. {
  1063. DPLAYI_DPLAY *lpDPlay;
  1064. PPROTOCOL pProtocol;
  1065. lpDPlay=DPLAY_FROM_INT(pISP);
  1066. if(lpDPlay->pProtocol){
  1067. // NOTE: when SP SendEx is used, we have to patch and xlate here.
  1068. // for now, this should never happen.
  1069. DEBUG_BREAK(); // Shouldn't get here yet.
  1070. pProtocol=(PPROTOCOL)lpDPlay->pProtocol;
  1071. DP_SP_SendComplete(pISP, lpvContext, CompletionStatus);
  1072. } else {
  1073. DP_SP_SendComplete(pISP, lpvContext, CompletionStatus);
  1074. }
  1075. }