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.

521 lines
15 KiB

  1. /*==========================================================================
  2. *
  3. * Copyright (C) 2000 Microsoft Corporation. All Rights Reserved.
  4. *
  5. * File: DPLApp.cpp
  6. * Content: DirectPlay Lobbied Application Functions
  7. *@@BEGIN_MSINTERNAL
  8. * History:
  9. * Date By Reason
  10. * ==== == ======
  11. * 02/21/00 mjn Created
  12. * 03/22/2000 jtk Changed interface names
  13. * 04/18/2000 rmt Added additional parameter validation
  14. * 04/25/2000 rmt Bug #s 33138, 33145, 33150
  15. * 04/26/00 mjn Removed dwTimeOut from Send() API call
  16. * 05/03/00 rmt DPL_UnRegister was not implemented!!
  17. * 05/08/00 rmt Bug #34301 - Add flag to SetAppAvail to allow for multiple connects
  18. * 06/15/00 rmt Bug #33617 - Must provide method for providing automatic launch of DirectPlay instances
  19. * 07/08/2000 rmt Bug #38725 - Need to provide method to detect if app was lobby launched
  20. * rmt Bug #38757 - Callback messages for connections may return AFTER WaitForConnection returns
  21. * rmt Bug #38755 - No way to specify player name in Connection Settings
  22. * rmt Bug #38758 - DPLOBBY8.H has incorrect comments
  23. * rmt Bug #38783 - pvUserApplicationContext is only partially implemented
  24. * rmt Added DPLHANDLE_ALLCONNECTIONS and dwFlags (reserved field to couple of funcs).
  25. * 07/14/2000 rmt Bug #39257 - LobbyClient::ReleaseApp returns E_OUTOFMEMORY when called when no one connected
  26. * rmt Bug #39487 - Remove WaitForConnect
  27. * 08/05/2000 RichGr IA64: Use %p format specifier in DPFs for 32/64-bit pointers and handles.
  28. * 08/15/2000 rmt Bug #42273 - DPLAY8: Samples sometimes get a DPNERR_ALREADYREGISTERED error. (Double connections)
  29. * 08/18/2000 rmt Bug #42751 - DPLOBBY8: Prohibit more than one lobby client or lobby app per process
  30. *@@END_MSINTERNAL
  31. *
  32. ***************************************************************************/
  33. #include "dnlobbyi.h"
  34. //**********************************************************************
  35. // Macro definitions
  36. //**********************************************************************
  37. //**********************************************************************
  38. // Structure definitions
  39. //**********************************************************************
  40. //**********************************************************************
  41. // Variable definitions
  42. //**********************************************************************
  43. typedef STDMETHODIMP AppQueryInterface(IDirectPlay8LobbiedApplication *pInterface,REFIID ridd,PVOID *ppvObj);
  44. typedef STDMETHODIMP_(ULONG) AppAddRef(IDirectPlay8LobbiedApplication *pInterface);
  45. typedef STDMETHODIMP_(ULONG) AppRelease(IDirectPlay8LobbiedApplication *pInterface);
  46. typedef STDMETHODIMP AppRegisterMessageHandler(IDirectPlay8LobbiedApplication *pInterface,const PVOID pvUserContext,const PFNDPNMESSAGEHANDLER pfn, DPNHANDLE * const pdpnhConnection, const DWORD dwFlags);
  47. typedef STDMETHODIMP AppSend(IDirectPlay8LobbiedApplication *pInterface,const DPNHANDLE hTarget,BYTE *const pBuffer,const DWORD pBufferSize,const DWORD dwFlags);
  48. typedef STDMETHODIMP AppClose(IDirectPlay8LobbiedApplication *pInterface, const DWORD dwFlags);
  49. typedef STDMETHODIMP AppGetConnectionSettings(IDirectPlay8LobbiedApplication *pInterface, const DPNHANDLE hLobbyClient, DPL_CONNECTION_SETTINGS * const pdplSessionInfo, DWORD *pdwInfoSize, const DWORD dwFlags );
  50. typedef STDMETHODIMP AppSetConnectionSettings(IDirectPlay8LobbiedApplication *pInterface, const DPNHANDLE hTarget, const DPL_CONNECTION_SETTINGS * const pdplSessionInfo, const DWORD dwFlags );
  51. IDirectPlay8LobbiedApplicationVtbl DPL_8LobbiedApplicationVtbl =
  52. {
  53. (AppQueryInterface*) DPL_QueryInterface,
  54. (AppAddRef*) DPL_AddRef,
  55. (AppRelease*) DPL_Release,
  56. (AppRegisterMessageHandler*) DPL_RegisterMessageHandler,
  57. DPL_RegisterProgram,
  58. DPL_UnRegisterProgram,
  59. (AppSend*) DPL_Send,
  60. DPL_SetAppAvailable,
  61. DPL_UpdateStatus,
  62. (AppClose*) DPL_Close,
  63. (AppGetConnectionSettings*) DPL_GetConnectionSettings,
  64. (AppSetConnectionSettings*) DPL_SetConnectionSettings
  65. };
  66. //**********************************************************************
  67. // Function prototypes
  68. //**********************************************************************
  69. //**********************************************************************
  70. // Function definitions
  71. //**********************************************************************
  72. #undef DPF_MODNAME
  73. #define DPF_MODNAME "DPL_RegisterProgram"
  74. STDMETHODIMP DPL_RegisterProgram(IDirectPlay8LobbiedApplication *pInterface,
  75. DPL_PROGRAM_DESC *const pdplProgramDesc,
  76. const DWORD dwFlags)
  77. {
  78. HRESULT hResultCode;
  79. DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
  80. DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], pdplProgramDesc [0x%p], dwFlags [0x%lx]",
  81. pInterface,pdplProgramDesc,dwFlags);
  82. TRY
  83. {
  84. pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pInterface));
  85. if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
  86. {
  87. if( FAILED( hResultCode = DPL_ValidateRegisterProgram( pInterface , pdplProgramDesc, dwFlags ) ) )
  88. {
  89. DPFX(DPFPREP, 0, "Error validating register params hr=[0x%lx]", hResultCode );
  90. DPF_RETURN( hResultCode );
  91. }
  92. }
  93. }
  94. EXCEPT(EXCEPTION_EXECUTE_HANDLER)
  95. {
  96. DPFERR("Invalid object" );
  97. DPF_RETURN(DPNERR_INVALIDOBJECT);
  98. }
  99. hResultCode = DPLWriteProgramDesc(pdplProgramDesc);
  100. DPF_RETURN(hResultCode);
  101. }
  102. #undef DPF_MODNAME
  103. #define DPF_MODNAME "DPL_UnRegisterProgram"
  104. STDMETHODIMP DPL_UnRegisterProgram(IDirectPlay8LobbiedApplication *pInterface,
  105. GUID *const pGuidApplication,
  106. const DWORD dwFlags)
  107. {
  108. HRESULT hResultCode;
  109. DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
  110. DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], pGuidApplication [0x%p], dwFlags [0x%lx]",
  111. pInterface,pGuidApplication,dwFlags);
  112. TRY
  113. {
  114. pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pInterface));
  115. if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
  116. {
  117. if( FAILED( hResultCode = DPL_ValidateUnRegisterProgram( pInterface , pGuidApplication, dwFlags ) ) )
  118. {
  119. DPFX(DPFPREP, 0, "Error validating unregister params hr=[0x%lx]", hResultCode );
  120. DPF_RETURN( hResultCode );
  121. }
  122. }
  123. }
  124. EXCEPT(EXCEPTION_EXECUTE_HANDLER)
  125. {
  126. DPFERR("Invalid object" );
  127. DPF_RETURN(DPNERR_INVALIDOBJECT);
  128. }
  129. hResultCode = DPLDeleteProgramDesc( pGuidApplication );
  130. DPF_RETURN(hResultCode);
  131. }
  132. #undef DPF_MODNAME
  133. #define DPF_MODNAME "DPL_SetAppAvailable"
  134. STDMETHODIMP DPL_SetAppAvailable(IDirectPlay8LobbiedApplication *pInterface, const BOOL fAvailable, const DWORD dwFlags )
  135. {
  136. DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
  137. HRESULT hResultCode;
  138. DPFX(DPFPREP, 3,"Parameters: (none)");
  139. TRY
  140. {
  141. pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pInterface));
  142. if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
  143. {
  144. if( FAILED( hResultCode = DPL_ValidateSetAppAvailable( pInterface, fAvailable, dwFlags ) ) )
  145. {
  146. DPFX(DPFPREP, 0, "Error validating makeappavail params hr=[0x%lx]", hResultCode );
  147. DPF_RETURN( hResultCode );
  148. }
  149. }
  150. // Ensure we've been initialized
  151. if (pdpLobbyObject->pReceiveQueue == NULL)
  152. {
  153. DPFERR("Not initialized");
  154. DPF_RETURN(DPNERR_UNINITIALIZED);
  155. }
  156. }
  157. EXCEPT(EXCEPTION_EXECUTE_HANDLER)
  158. {
  159. DPFERR("Invalid object" );
  160. DPF_RETURN(DPNERR_INVALIDOBJECT);
  161. }
  162. if( fAvailable )
  163. {
  164. // Indicate that we are waiting
  165. pdpLobbyObject->pReceiveQueue->MakeAvailable();
  166. if( dwFlags & DPLAVAILABLE_ALLOWMULTIPLECONNECT )
  167. {
  168. pdpLobbyObject->dwFlags |= DPL_OBJECT_FLAG_MULTICONNECT;
  169. }
  170. else
  171. {
  172. pdpLobbyObject->dwFlags &= ~(DPL_OBJECT_FLAG_MULTICONNECT);
  173. }
  174. }
  175. else
  176. {
  177. pdpLobbyObject->pReceiveQueue->MakeUnavailable();
  178. }
  179. hResultCode = DPN_OK;
  180. DPF_RETURN(hResultCode);
  181. }
  182. // DPL_UpdateStatus
  183. //
  184. // Send session status information to the lobby client. This should be called whenever
  185. // the lobbied application connects to the game, fails to connect, disconnects, or is
  186. // terminated (booted).
  187. #undef DPF_MODNAME
  188. #define DPF_MODNAME "DPL_UpdateStatus"
  189. STDMETHODIMP DPL_UpdateStatus(IDirectPlay8LobbiedApplication *pInterface,
  190. const DPNHANDLE hLobbyClient,
  191. const DWORD dwStatus, const DWORD dwFlags )
  192. {
  193. HRESULT hResultCode;
  194. DIRECTPLAYLOBBYOBJECT *pdpLobbyObject;
  195. DPL_CONNECTION *pdplConnection;
  196. DPL_INTERNAL_MESSAGE_UPDATE_STATUS Msg;
  197. DPNHANDLE *hTargets = NULL;
  198. DWORD dwNumTargets = 0;
  199. DWORD dwTargetIndex = 0;
  200. DPFX(DPFPREP, 3,"Parameters: pInterface [0x%p], hLobbyClient [0x%lx], dwStatus [0x%lx]",
  201. pInterface,hLobbyClient,dwStatus);
  202. TRY
  203. {
  204. pdpLobbyObject = static_cast<DIRECTPLAYLOBBYOBJECT*>(GET_OBJECT_FROM_INTERFACE(pInterface));
  205. if( pdpLobbyObject->dwFlags & DPL_OBJECT_FLAG_PARAMVALIDATION )
  206. {
  207. if( FAILED( hResultCode = DPL_ValidateUpdateStatus( pInterface, hLobbyClient, dwStatus, dwFlags ) ) )
  208. {
  209. DPFX(DPFPREP, 0, "Error validating updatestatus params hr=[0x%lx]", hResultCode );
  210. DPF_RETURN( hResultCode );
  211. }
  212. }
  213. // Ensure we've been initialized
  214. if (pdpLobbyObject->pReceiveQueue == NULL)
  215. {
  216. DPFERR("Not initialized");
  217. DPF_RETURN(DPNERR_UNINITIALIZED);
  218. }
  219. }
  220. EXCEPT(EXCEPTION_EXECUTE_HANDLER)
  221. {
  222. DPFERR("Invalid object" );
  223. DPF_RETURN(DPNERR_INVALIDOBJECT);
  224. }
  225. Msg.dwMsgId = DPL_MSGID_INTERNAL_UPDATE_STATUS;
  226. Msg.dwStatus = dwStatus;
  227. if( hLobbyClient == DPLHANDLE_ALLCONNECTIONS )
  228. {
  229. dwNumTargets = 0;
  230. // We need loop so if someone adds a connection during our run
  231. // it gets added to our list
  232. //
  233. while( 1 )
  234. {
  235. hResultCode = DPLConnectionEnum( pdpLobbyObject, hTargets, &dwNumTargets );
  236. if( hResultCode == DPNERR_BUFFERTOOSMALL )
  237. {
  238. if( hTargets )
  239. {
  240. delete [] hTargets;
  241. }
  242. hTargets = new DPNHANDLE[dwNumTargets];
  243. if( hTargets == NULL )
  244. {
  245. DPFERR("Error allocating memory" );
  246. hResultCode = DPNERR_OUTOFMEMORY;
  247. dwNumTargets = 0;
  248. goto EXIT_AND_CLEANUP;
  249. }
  250. memset( hTargets, 0x00, sizeof(DPNHANDLE)*dwNumTargets);
  251. continue;
  252. }
  253. else if( FAILED( hResultCode ) )
  254. {
  255. DPFX(DPFPREP, 0, "Error getting list of connections hr=0x%x", hResultCode );
  256. break;
  257. }
  258. else
  259. {
  260. break;
  261. }
  262. }
  263. // Failed getting connection information
  264. if( FAILED( hResultCode ) )
  265. {
  266. if( hTargets )
  267. {
  268. delete [] hTargets;
  269. hTargets = NULL;
  270. }
  271. dwNumTargets = 0;
  272. goto EXIT_AND_CLEANUP;
  273. }
  274. }
  275. else
  276. {
  277. hTargets = new DPNHANDLE[1]; // We use array delete below so we need array new
  278. if( hTargets == NULL )
  279. {
  280. DPFERR("Error allocating memory" );
  281. hResultCode = DPNERR_OUTOFMEMORY;
  282. dwNumTargets = 0;
  283. goto EXIT_AND_CLEANUP;
  284. }
  285. dwNumTargets = 1;
  286. hTargets[0] = hLobbyClient;
  287. }
  288. for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
  289. {
  290. if ((hResultCode = DPLConnectionFind(pdpLobbyObject,hTargets[dwTargetIndex],&pdplConnection,TRUE)) != DPN_OK)
  291. {
  292. DPFERR("Invalid send target");
  293. DisplayDNError(0,hResultCode);
  294. hResultCode = DPNERR_INVALIDHANDLE;
  295. goto EXIT_AND_CLEANUP;
  296. }
  297. DNASSERT(pdplConnection->pSendQueue != NULL);
  298. if (!pdplConnection->pSendQueue->IsReceiving())
  299. {
  300. DPFERR("Other side is not listening");
  301. hResultCode = DPNERR_INVALIDHANDLE;
  302. goto EXIT_AND_CLEANUP;
  303. }
  304. hResultCode = pdplConnection->pSendQueue->Send(reinterpret_cast<BYTE*>(&Msg),
  305. sizeof(DPL_INTERNAL_MESSAGE_UPDATE_STATUS),
  306. INFINITE,
  307. DPL_MSGQ_MSGFLAGS_USER1,
  308. 0);
  309. if( FAILED( hResultCode ) )
  310. {
  311. DPFX(DPFPREP, 0, "Error sending to connection 0x%x hr=0x%x", hTargets[dwTargetIndex], hResultCode );
  312. }
  313. }
  314. EXIT_AND_CLEANUP:
  315. for( dwTargetIndex = 0; dwTargetIndex < dwNumTargets; dwTargetIndex++ )
  316. {
  317. if( hTargets[dwTargetIndex] )
  318. DPLConnectionRelease(pdpLobbyObject,hTargets[dwTargetIndex]);
  319. }
  320. if( hTargets )
  321. delete [] hTargets;
  322. DPF_RETURN(hResultCode);
  323. }
  324. #undef DPF_MODNAME
  325. #define DPF_MODNAME "DPLAttemptLobbyConnection"
  326. HRESULT DPLAttemptLobbyConnection(DIRECTPLAYLOBBYOBJECT *const pdpLobbyObject)
  327. {
  328. PSTR pszCommandLine;
  329. char *c;
  330. DWORD dwCommandLineSize;
  331. CHAR pszObjectName[(sizeof(DWORD)*2)*2 + 1 + 1];
  332. HANDLE hSyncEvent;
  333. HRESULT hResultCode;
  334. HANDLE hFileMap;
  335. DPL_SHARED_CONNECT_BLOCK *pSharedBlock;
  336. DWORD dwError;
  337. DWORD dwReturnValue;
  338. DPFX(DPFPREP, 3,"Parameters: (none)");
  339. // Need a copy of the command line
  340. dwCommandLineSize = strlen(GetCommandLineA()) + 1;
  341. if ((pszCommandLine = static_cast<PSTR>(DNMalloc(dwCommandLineSize))) == NULL)
  342. {
  343. return(DPNERR_NORESPONSE);
  344. }
  345. strcpy(pszCommandLine,GetCommandLineA());
  346. DPFX(DPFPREP, 5,"Got command line [%s]",pszCommandLine);
  347. // Try to find Lobby Launch ID string
  348. c = strstr(pszCommandLine,DPL_ID_STR_A);
  349. if (c == NULL)
  350. {
  351. DNFree(pszCommandLine);
  352. return(DPNERR_NORESPONSE);
  353. }
  354. c += strlen(DPL_ID_STR_A);
  355. c--;
  356. strncpy(pszObjectName,c,(sizeof(DWORD)*2)*2 + 1);
  357. pszObjectName[(sizeof(DWORD)*2)*2 + 1] = '\0'; // Ensure null terminated
  358. DPFX(DPFPREP, 5,"Got object name [%s]",pszObjectName);
  359. DNFree(pszCommandLine);
  360. // Try to open shared memory
  361. *pszObjectName = DPL_MSGQ_OBJECT_IDCHAR_FILEMAP;
  362. hFileMap = CreateFileMappingA(INVALID_HANDLE_VALUE,(LPSECURITY_ATTRIBUTES) NULL,
  363. PAGE_READWRITE,(DWORD)0,sizeof(DPL_SHARED_CONNECT_BLOCK),pszObjectName);
  364. if (hFileMap == NULL)
  365. {
  366. DPFERR("CreateFileMapping() failed");
  367. dwError = GetLastError();
  368. DNASSERT(FALSE);
  369. return(DPNERR_NORESPONSE);
  370. }
  371. // Ensure it existed already
  372. dwError = GetLastError();
  373. if (dwError != ERROR_ALREADY_EXISTS)
  374. {
  375. DPFERR("File mapping did not already exist");
  376. DNASSERT(FALSE);
  377. CloseHandle(hFileMap);
  378. return(DPNERR_NORESPONSE);
  379. }
  380. // Map file
  381. pSharedBlock = reinterpret_cast<DPL_SHARED_CONNECT_BLOCK*>(MapViewOfFile(hFileMap,FILE_MAP_ALL_ACCESS,0,0,0));
  382. if (pSharedBlock == NULL)
  383. {
  384. DPFERR("MapViewOfFile() failed");
  385. dwError = GetLastError();
  386. DNASSERT(FALSE);
  387. CloseHandle(hFileMap);
  388. return(DPNERR_NORESPONSE);
  389. }
  390. // Try to open connection event
  391. *pszObjectName = DPL_MSGQ_OBJECT_IDCHAR_EVENT;
  392. hSyncEvent = OpenEventA(EVENT_MODIFY_STATE,FALSE,pszObjectName);
  393. if (hSyncEvent == NULL)
  394. {
  395. DPFERR("OpenEvent() failed");
  396. dwError = GetLastError();
  397. DNASSERT(FALSE);
  398. UnmapViewOfFile(pSharedBlock);
  399. CloseHandle(hFileMap);
  400. return(DPNERR_NORESPONSE);
  401. }
  402. DPFX(DPFPREP, 5,"Opened sync event");
  403. ResetEvent(pdpLobbyObject->hConnectEvent);
  404. // Look for lobby launch -- set lobby launch value if connection is received
  405. pdpLobbyObject->dwFlags |= DPL_OBJECT_FLAG_LOOKINGFORLOBBYLAUNCH;
  406. // Make application available for connection by lobby client
  407. DNASSERT(pdpLobbyObject->pReceiveQueue != NULL);
  408. // Signal lobby client
  409. pSharedBlock->dwPID = pdpLobbyObject->dwPID;
  410. SetEvent(hSyncEvent);
  411. dwReturnValue = WaitForSingleObject(pdpLobbyObject->hConnectEvent,DPL_LOBBYLAUNCHED_CONNECT_TIMEOUT);
  412. // Turn off the looking for lobby launch flag
  413. pdpLobbyObject->dwFlags &= ~(DPL_OBJECT_FLAG_LOOKINGFORLOBBYLAUNCH);
  414. if (dwReturnValue == WAIT_OBJECT_0)
  415. hResultCode = DPN_OK;
  416. else
  417. hResultCode = DPNERR_TIMEDOUT;
  418. // Clean up
  419. CloseHandle(hSyncEvent);
  420. UnmapViewOfFile(pSharedBlock);
  421. CloseHandle(hFileMap);
  422. DPFX(DPFPREP, 3,"Returning: [0x%lx]",hResultCode);
  423. return(hResultCode);
  424. }
  425. //------------------------------------------------------------------------