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.

1723 lines
61 KiB

  1. /****************************************************************************
  2. *
  3. * File: testnet.cpp
  4. * Project: DxDiag (DirectX Diagnostic Tool)
  5. * Author: Jason Sandlin (jasonsa@microsoft.com)
  6. * Purpose: Test DPlay8 functionality on this machine
  7. *
  8. * (C) Copyright 2000-2001 Microsoft Corp. All rights reserved.
  9. *
  10. ****************************************************************************/
  11. #define INITGUID
  12. #include <Windows.h>
  13. #include <multimon.h>
  14. #include <dplay8.h>
  15. #include <tchar.h>
  16. #include <wchar.h>
  17. #include <dplobby.h>
  18. #include <mmsystem.h>
  19. #include "reginfo.h"
  20. #include "sysinfo.h"
  21. #include "dispinfo.h"
  22. #include "netinfo.h"
  23. #include "testnet.h"
  24. #include "resource.h"
  25. #ifndef ReleasePpo
  26. #define ReleasePpo(ppo) \
  27. if (*(ppo) != NULL) \
  28. { \
  29. (*(ppo))->Release(); \
  30. *(ppo) = NULL; \
  31. } \
  32. else (VOID)0
  33. #endif
  34. #define TIMER_WAIT_CONNECT_COMPLETE 0
  35. #define TIMER_UPDATE_SESSION_LIST 1
  36. enum TESTID
  37. {
  38. TESTID_COINITIALIZE = 1,
  39. TESTID_CREATEDPLAY,
  40. TESTID_ADDRESSING,
  41. TESTID_ENUMSESSIONS,
  42. TESTID_ENUMPLAYERS,
  43. TESTID_SENDCHATMESSAGE,
  44. TESTID_RECEIVE,
  45. TESTID_SETPEERINFO,
  46. TESTID_CREATESESSION,
  47. TESTID_JOINSESSION,
  48. };
  49. struct DPHostEnumInfo
  50. {
  51. DPN_APPLICATION_DESC* pAppDesc;
  52. IDirectPlay8Address* pHostAddr;
  53. IDirectPlay8Address* pDeviceAddr;
  54. TCHAR szSession[MAX_PATH];
  55. DWORD dwLastPollTime;
  56. BOOL bValid;
  57. DPHostEnumInfo* pNext;
  58. };
  59. #define MAX_CHAT_STRING_LENGTH 200
  60. #define MAX_PLAYER_NAME MAX_PATH
  61. #define MAX_CHAT_STRING (MAX_PLAYER_NAME + MAX_CHAT_STRING_LENGTH + 32)
  62. struct APP_PLAYER_INFO
  63. {
  64. LONG lRefCount; // Ref count so we can cleanup when all threads
  65. // are done w/ this object
  66. DPNID dpnidPlayer; // DPNID of player
  67. WCHAR strPlayerName[MAX_PLAYER_NAME]; // Player name
  68. };
  69. #define GAME_MSGID_CHAT 1
  70. // Change compiler pack alignment to be BYTE aligned, and pop the current value
  71. #pragma pack( push, 1 )
  72. UNALIGNED struct GAMEMSG_GENERIC
  73. {
  74. WORD nType;
  75. };
  76. UNALIGNED struct GAMEMSG_CHAT : public GAMEMSG_GENERIC
  77. {
  78. WCHAR strChatString[MAX_CHAT_STRING_LENGTH];
  79. };
  80. // Pop the old pack alignment
  81. #pragma pack( pop )
  82. struct APP_QUEUE_CHAT_MSG
  83. {
  84. WCHAR strChatBuffer[MAX_CHAT_STRING];
  85. };
  86. struct APP_PLAYER_MSG
  87. {
  88. WCHAR strPlayerName[MAX_PATH]; // Player name
  89. };
  90. #define WM_APP_CHAT (WM_APP + 1)
  91. #define WM_APP_LEAVE (WM_APP + 2)
  92. #define WM_APP_JOIN (WM_APP + 3)
  93. #define WM_APP_CONNECTING (WM_APP + 4)
  94. #define WM_APP_CONNECTED (WM_APP + 5)
  95. BOOL BTranslateError(HRESULT hr, TCHAR* psz, BOOL bEnglish = FALSE); // from main.cpp (yuck)
  96. static INT_PTR CALLBACK SetupDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  97. static BOOL FAR PASCAL EnumConnectionsCallback(LPCGUID lpguidSP, VOID* pvConnection,
  98. DWORD dwConnectionSize, LPCDPNAME pName, DWORD dwFlags, VOID* pvContext);
  99. static INT_PTR CALLBACK SessionsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  100. static VOID SessionsDlgInitListbox( HWND hDlg );
  101. static VOID SessionsDlgNoteEnumResponse( PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg );
  102. static VOID SessionsDlgUpdateSessionList(HWND hDlg);
  103. static VOID SessionsDlgEnumListCleanup();
  104. static HRESULT InitDirectPlay( BOOL* pbCoInitializeDone );
  105. static HRESULT InitDirectPlayAddresses();
  106. static HRESULT InitSession();
  107. static VOID LoadStringWide( int nID, WCHAR* szWide );
  108. static BOOL FAR PASCAL EnumSessionsCallback(LPCDPSESSIONDESC2 pdpsd,
  109. DWORD* pdwTimeout, DWORD dwFlags, VOID* pvContext);
  110. static INT_PTR CALLBACK ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam);
  111. static VOID ShowTextString(HWND hDlg, WCHAR* sz );
  112. static HRESULT SendChatMessage( TCHAR* szMessage );
  113. static HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext, DWORD dwMessageId, PVOID pMsgBuffer );
  114. static BOOL ConvertStringToGUID(const TCHAR* strBuffer, GUID* lpguid);
  115. static VOID ConvertGenericStringToWide( WCHAR* wstrDestination, const TCHAR* tstrSource, int cchDestChar = -1 );
  116. static VOID ConvertWideStringToGeneric( TCHAR* tstrDestination, const WCHAR* wstrSource, int cchDestChar );
  117. static VOID ConvertWideStringToAnsi( CHAR* strDestination, const WCHAR* wstrSource, int cchDestChar );
  118. static const GUID s_guidDPTest = // {61EF80DA-691B-4247-9ADD-1C7BED2BC13E}
  119. { 0x61ef80da, 0x691b, 0x4247, { 0x9a, 0xdd, 0x1c, 0x7b, 0xed, 0x2b, 0xc1, 0x3e } };
  120. static NetInfo* s_pNetInfo = NULL;
  121. static IDirectPlay8Peer* s_pDP = NULL;
  122. static TCHAR s_szPlayerName[100];
  123. static TCHAR s_szSessionName[100];
  124. static DWORD s_dwPort = 0;
  125. static NetSP* s_pNetSP = NULL;
  126. static BOOL s_bCreateSession = FALSE;
  127. static DPHostEnumInfo* s_pSelectedSession = NULL;
  128. static DPHostEnumInfo s_DPHostEnumHead;
  129. static IDirectPlay8Address* s_pDeviceAddress = NULL;
  130. static IDirectPlay8Address* s_pHostAddress = NULL;
  131. static DPNHANDLE s_hEnumAsyncOp = NULL;
  132. static DWORD s_dwEnumHostExpireInterval = 0;
  133. static BOOL s_bEnumListChanged = FALSE;
  134. static BOOL s_bConnecting = FALSE;
  135. static DPHostEnumInfo* s_pDPHostEnumSelected = NULL;
  136. static CRITICAL_SECTION s_csHostEnum;
  137. static DPNID s_dpnidLocalPlayer = 0;
  138. static LONG s_lNumberOfActivePlayers = 0;
  139. static HWND s_hDlg = NULL;
  140. static HWND s_hwndSessionDlg = NULL;
  141. static DPNHANDLE s_hConnectAsyncOp = NULL;
  142. static HRESULT s_hrConnectComplete = S_OK;
  143. static HANDLE s_hConnectCompleteEvent = NULL;
  144. static CRITICAL_SECTION s_csPlayerContext;
  145. #define PLAYER_LOCK() EnterCriticalSection( &s_csPlayerContext );
  146. #define PLAYER_ADDREF( pPlayerInfo ) if( pPlayerInfo ) pPlayerInfo->lRefCount++;
  147. #define PLAYER_RELEASE( pPlayerInfo ) if( pPlayerInfo ) { pPlayerInfo->lRefCount--; if( pPlayerInfo->lRefCount <= 0 ) delete pPlayerInfo; } pPlayerInfo = NULL;
  148. #define PLAYER_UNLOCK() LeaveCriticalSection( &s_csPlayerContext );
  149. /****************************************************************************
  150. *
  151. * TestNetwork
  152. *
  153. ****************************************************************************/
  154. VOID TestNetwork(HWND hwndMain, NetInfo* pNetInfo)
  155. {
  156. BOOL bCoInitializeDone = FALSE;
  157. TCHAR sz[300];
  158. HINSTANCE hinst = (HINSTANCE)GetWindowLongPtr(hwndMain, GWLP_HINSTANCE);
  159. s_pNetInfo = pNetInfo;
  160. // Remove info from any previous test:
  161. ZeroMemory(&s_pNetInfo->m_testResult, sizeof(TestResult));
  162. s_pNetInfo->m_testResult.m_bStarted = TRUE;
  163. // Setup the s_DPHostEnumHead circular linked list
  164. ZeroMemory( &s_DPHostEnumHead, sizeof( DPHostEnumInfo ) );
  165. s_DPHostEnumHead.pNext = &s_DPHostEnumHead;
  166. InitializeCriticalSection( &s_csHostEnum );
  167. InitializeCriticalSection( &s_csPlayerContext );
  168. s_hConnectCompleteEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
  169. // Setup s_pDP, and mark installed SP's
  170. if( FAILED( InitDirectPlay( &bCoInitializeDone ) ) )
  171. goto LEnd;
  172. // Show setup dialog. This will tell us:
  173. // - service provider
  174. // - player name
  175. // - either create or join
  176. // - game name (if creating)
  177. // - port (if SP=TCP/IP)
  178. DialogBox(hinst, MAKEINTRESOURCE(IDD_TESTNETSETUP), hwndMain, SetupDialogProc);
  179. if (s_pNetSP == NULL)
  180. {
  181. // Something weird happened...no service provider chosen
  182. goto LEnd;
  183. }
  184. // At this point s_szPlayerName, s_szSessionName, s_pNetSP, s_dwPort,
  185. // and s_bCreateSession have been initialized
  186. // Setup s_dwEnumHostExpireInterval, s_pDeviceAddress, and s_pHostAddress
  187. if( FAILED( InitDirectPlayAddresses() ) )
  188. goto LEnd;
  189. // Now s_dwEnumHostExpireInterval, s_pDeviceAddress, and s_pHostAddress
  190. // have been initialized
  191. // Session list window (if joining session)
  192. if( !s_bCreateSession )
  193. {
  194. // Open a dialog to choose which host to connect to
  195. DialogBox(hinst, MAKEINTRESOURCE(IDD_TESTNETSESSIONS), hwndMain, SessionsDialogProc);
  196. // Now s_pDPHostEnumSelected will be NULL or valid
  197. if( FAILED(s_pNetInfo->m_testResult.m_hr) || s_pDPHostEnumSelected == NULL )
  198. goto LEnd;
  199. // Now s_pDPHostEnumSelected is valid
  200. }
  201. // Launch chat window and host or join session
  202. DialogBox(hinst, MAKEINTRESOURCE(IDD_TESTNETCHAT), hwndMain, ChatDialogProc);
  203. LEnd:
  204. s_pNetSP = NULL;
  205. ReleasePpo( &s_pDeviceAddress );
  206. ReleasePpo( &s_pHostAddress );
  207. if( s_hEnumAsyncOp )
  208. s_pDP->CancelAsyncOperation( s_hEnumAsyncOp, 0 );
  209. ReleasePpo(&s_pDP);
  210. if (bCoInitializeDone)
  211. CoUninitialize(); // Release COM
  212. DeleteCriticalSection( &s_csHostEnum );
  213. DeleteCriticalSection( &s_csPlayerContext );
  214. CloseHandle( s_hConnectCompleteEvent );
  215. if (s_pNetInfo->m_testResult.m_bCancelled)
  216. {
  217. LoadString(NULL, IDS_TESTSCANCELLED, sz, 300);
  218. lstrcpy(s_pNetInfo->m_testResult.m_szDescription, sz);
  219. LoadString(NULL, IDS_TESTSCANCELLED_ENGLISH, sz, 300);
  220. lstrcpy(s_pNetInfo->m_testResult.m_szDescriptionEnglish, sz);
  221. }
  222. else if (s_pNetInfo->m_testResult.m_iStepThatFailed == 0)
  223. {
  224. LoadString(NULL, IDS_TESTSSUCCESSFUL, sz, 300);
  225. lstrcpy(s_pNetInfo->m_testResult.m_szDescription, sz);
  226. LoadString(NULL, IDS_TESTSSUCCESSFUL_ENGLISH, sz, 300);
  227. lstrcpy(s_pNetInfo->m_testResult.m_szDescriptionEnglish, sz);
  228. }
  229. else
  230. {
  231. TCHAR szDesc[300];
  232. TCHAR szError[300];
  233. if (0 == LoadString(NULL, IDS_FIRSTDPLAYTESTERROR +
  234. s_pNetInfo->m_testResult.m_iStepThatFailed - 1, szDesc, 200))
  235. {
  236. LoadString(NULL, IDS_UNKNOWNERROR, sz, 300);
  237. lstrcpy(szDesc, sz);
  238. }
  239. LoadString(NULL, IDS_FAILUREFMT, sz, 300);
  240. BTranslateError(s_pNetInfo->m_testResult.m_hr, szError);
  241. wsprintf(s_pNetInfo->m_testResult.m_szDescription, sz,
  242. s_pNetInfo->m_testResult.m_iStepThatFailed,
  243. szDesc, s_pNetInfo->m_testResult.m_hr, szError);
  244. // Nonlocalized version:
  245. if (0 == LoadString(NULL, IDS_FIRSTDPLAYTESTERROR_ENGLISH +
  246. s_pNetInfo->m_testResult.m_iStepThatFailed - 1, szDesc, 200))
  247. {
  248. LoadString(NULL, IDS_UNKNOWNERROR_ENGLISH, sz, 300);
  249. lstrcpy(szDesc, sz);
  250. }
  251. LoadString(NULL, IDS_FAILUREFMT_ENGLISH, sz, 300);
  252. BTranslateError(s_pNetInfo->m_testResult.m_hr, szError, TRUE);
  253. wsprintf(s_pNetInfo->m_testResult.m_szDescriptionEnglish, sz,
  254. s_pNetInfo->m_testResult.m_iStepThatFailed,
  255. szDesc, s_pNetInfo->m_testResult.m_hr, szError);
  256. }
  257. }
  258. /****************************************************************************
  259. *
  260. * InitDirectPlay
  261. *
  262. ****************************************************************************/
  263. HRESULT InitDirectPlay( BOOL* pbCoInitializeDone )
  264. {
  265. HRESULT hr;
  266. DWORD dwItems = 0;
  267. DWORD dwSize = 0;
  268. DPN_SERVICE_PROVIDER_INFO* pdnSPInfoEnum = NULL;
  269. DPN_SERVICE_PROVIDER_INFO* pdnSPInfo = NULL;
  270. DWORD i;
  271. // Initialize COM
  272. if (FAILED(hr = CoInitialize(NULL)))
  273. {
  274. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_COINITIALIZE;
  275. s_pNetInfo->m_testResult.m_hr = hr;
  276. return hr;
  277. }
  278. *pbCoInitializeDone = TRUE;
  279. // Create DirectPlay object
  280. if( FAILED( hr = CoCreateInstance( CLSID_DirectPlay8Peer, NULL,
  281. CLSCTX_INPROC_SERVER,
  282. IID_IDirectPlay8Peer,
  283. (LPVOID*) &s_pDP ) ) )
  284. {
  285. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_CREATEDPLAY;
  286. s_pNetInfo->m_testResult.m_hr = hr;
  287. return hr;
  288. }
  289. // Init IDirectPlay8Peer
  290. if( FAILED( hr = s_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 ) ) )
  291. {
  292. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_CREATEDPLAY;
  293. s_pNetInfo->m_testResult.m_hr = hr;
  294. return hr;
  295. }
  296. // Enumerate all DirectPlay service providers
  297. // to figure out which are installed
  298. hr = s_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo, &dwSize,
  299. &dwItems, 0 );
  300. if( hr != DPNERR_BUFFERTOOSMALL && FAILED(hr) )
  301. {
  302. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  303. s_pNetInfo->m_testResult.m_hr = hr;
  304. return hr;
  305. }
  306. pdnSPInfo = (DPN_SERVICE_PROVIDER_INFO*) new BYTE[dwSize];
  307. if( FAILED( hr = s_pDP->EnumServiceProviders( NULL, NULL, pdnSPInfo,
  308. &dwSize, &dwItems, 0 ) ) )
  309. {
  310. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  311. s_pNetInfo->m_testResult.m_hr = hr;
  312. if( pdnSPInfo )
  313. delete[] pdnSPInfo;
  314. return hr;
  315. }
  316. // Mark installed SP's as such
  317. pdnSPInfoEnum = pdnSPInfo;
  318. for ( i = 0; i < dwItems; i++ )
  319. {
  320. NetSP* pNetSP;
  321. for (pNetSP = s_pNetInfo->m_pNetSPFirst; pNetSP != NULL;
  322. pNetSP = pNetSP->m_pNetSPNext)
  323. {
  324. if( pNetSP->m_guid == pdnSPInfoEnum->guid )
  325. {
  326. pNetSP->m_bInstalled = TRUE;
  327. break;
  328. }
  329. }
  330. pdnSPInfoEnum++;
  331. }
  332. if( pdnSPInfo )
  333. delete[] pdnSPInfo;
  334. return S_OK;
  335. }
  336. /****************************************************************************
  337. *
  338. * SetupDialogProc
  339. *
  340. ****************************************************************************/
  341. INT_PTR CALLBACK SetupDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  342. {
  343. switch (uMsg)
  344. {
  345. case WM_INITDIALOG:
  346. {
  347. NetSP* pNetSP;
  348. TCHAR sz[MAX_PATH];
  349. HWND hwndList = GetDlgItem(hDlg, IDC_SPLIST);
  350. LONG iItem;
  351. LONG iSelect = LB_ERR;
  352. for (pNetSP = s_pNetInfo->m_pNetSPFirst; pNetSP != NULL;
  353. pNetSP = pNetSP->m_pNetSPNext)
  354. {
  355. if( pNetSP->m_dwDXVer == 8 && pNetSP->m_bInstalled )
  356. {
  357. iItem = (LONG)SendMessage(hwndList, LB_ADDSTRING, 0, (LPARAM)(LPCTSTR)pNetSP->m_szName);
  358. if ((LRESULT)iItem != LB_ERR)
  359. {
  360. SendMessage(hwndList, LB_SETITEMDATA, iItem, (LPARAM)pNetSP);
  361. // Try to select TCP/IP by default
  362. if( lstrcmpi(pNetSP->m_szGuid, TEXT("{EBFE7BA0-628D-11D2-AE0F-006097B01411}")) == 0)
  363. iSelect = iItem;
  364. }
  365. }
  366. }
  367. // Try to select the default preferred provider
  368. if( iSelect != LB_ERR )
  369. SendMessage( hwndList, LB_SETCURSEL, iSelect, 0 );
  370. else
  371. SendMessage( hwndList, LB_SETCURSEL, 0, 0 );
  372. SendMessage(hDlg, WM_COMMAND, IDC_SPLIST, 0);
  373. LoadString(NULL, IDS_DEFAULTUSERNAME, sz, MAX_PATH);
  374. SetWindowText(GetDlgItem(hDlg, IDC_PLAYERNAME), sz);
  375. LoadString(NULL, IDS_DEFAULTSESSIONNAME, sz, MAX_PATH);
  376. SetWindowText(GetDlgItem(hDlg, IDC_SESSIONNAME), sz);
  377. CheckRadioButton(hDlg, IDC_CREATESESSION, IDC_JOINSESSION, IDC_CREATESESSION);
  378. return TRUE;
  379. }
  380. case WM_COMMAND:
  381. {
  382. switch(LOWORD(wParam))
  383. {
  384. case IDC_CREATESESSION:
  385. {
  386. EnableWindow(GetDlgItem(hDlg, IDC_SESSIONNAME), TRUE);
  387. break;
  388. }
  389. case IDC_JOINSESSION:
  390. {
  391. EnableWindow(GetDlgItem(hDlg, IDC_SESSIONNAME), FALSE);
  392. break;
  393. }
  394. case IDC_SPLIST:
  395. {
  396. HWND hwndList;
  397. hwndList = GetDlgItem(hDlg, IDC_SPLIST);
  398. LONG iItem;
  399. iItem = (LONG)SendMessage(hwndList, LB_GETCURSEL, 0, 0);
  400. NetSP* pNetSP = (NetSP*)SendMessage(hwndList, LB_GETITEMDATA, iItem, 0);
  401. // Only enable the port if the selected SP == TCP/IP
  402. if( pNetSP && lstrcmp( pNetSP->m_szGuid, TEXT("{EBFE7BA0-628D-11D2-AE0F-006097B01411}") ) == 0 )
  403. {
  404. EnableWindow( GetDlgItem(hDlg, IDC_PORT), TRUE );
  405. EnableWindow( GetDlgItem(hDlg, IDC_PORT_TEXT), TRUE );
  406. }
  407. else
  408. {
  409. EnableWindow( GetDlgItem(hDlg, IDC_PORT), FALSE );
  410. EnableWindow( GetDlgItem(hDlg, IDC_PORT_TEXT), FALSE );
  411. }
  412. break;
  413. }
  414. case IDOK:
  415. {
  416. // Set create/join option
  417. if (IsDlgButtonChecked(hDlg, IDC_CREATESESSION))
  418. s_bCreateSession = TRUE;
  419. else
  420. s_bCreateSession = FALSE;
  421. // Get player name
  422. GetWindowText(GetDlgItem(hDlg, IDC_PLAYERNAME), s_szPlayerName, 100);
  423. if (lstrlen(s_szPlayerName) == 0)
  424. {
  425. TCHAR szMsg[MAX_PATH];
  426. TCHAR szTitle[MAX_PATH];
  427. LoadString(NULL, IDS_NEEDUSERNAME, szMsg, MAX_PATH);
  428. LoadString(NULL, IDS_APPFULLNAME, szTitle, MAX_PATH);
  429. MessageBox(hDlg, szMsg, szTitle, MB_OK);
  430. break;
  431. }
  432. // Get port
  433. TCHAR szPort[MAX_PATH];
  434. GetDlgItemText( hDlg, IDC_PORT, szPort, MAX_PATH);
  435. s_dwPort = _ttoi( szPort );
  436. // Get session name
  437. GetWindowText(GetDlgItem(hDlg, IDC_SESSIONNAME), s_szSessionName, 100);
  438. if (s_bCreateSession && lstrlen(s_szSessionName) == 0)
  439. {
  440. TCHAR szMsg[MAX_PATH];
  441. TCHAR szTitle[MAX_PATH];
  442. LoadString(NULL, IDS_NEEDSESSIONNAME, szMsg, MAX_PATH);
  443. LoadString(NULL, IDS_APPFULLNAME, szTitle, MAX_PATH);
  444. MessageBox(hDlg, szMsg, szTitle, MB_OK);
  445. break;
  446. }
  447. // Get sp
  448. HWND hwndList;
  449. hwndList = GetDlgItem(hDlg, IDC_SPLIST);
  450. LONG iItem;
  451. iItem = (LONG)SendMessage(hwndList, LB_GETCURSEL, 0, 0);
  452. if ((LPARAM)iItem == LB_ERR)
  453. {
  454. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  455. EndDialog(hDlg, 0);
  456. return FALSE;
  457. }
  458. else
  459. {
  460. s_pNetSP = (NetSP*)SendMessage(hwndList, LB_GETITEMDATA, iItem, 0);
  461. }
  462. EndDialog(hDlg, 1);
  463. break;
  464. }
  465. case IDCANCEL:
  466. {
  467. EndDialog(hDlg, 0);
  468. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  469. break;
  470. }
  471. }
  472. }
  473. }
  474. return FALSE;
  475. }
  476. /****************************************************************************
  477. *
  478. * InitDirectPlayAddresses
  479. *
  480. ****************************************************************************/
  481. HRESULT InitDirectPlayAddresses()
  482. {
  483. HRESULT hr;
  484. // Query for the enum host timeout for this SP
  485. DPN_SP_CAPS dpspCaps;
  486. ZeroMemory( &dpspCaps, sizeof(DPN_SP_CAPS) );
  487. dpspCaps.dwSize = sizeof(DPN_SP_CAPS);
  488. if( FAILED( hr = s_pDP->GetSPCaps( &s_pNetSP->m_guid, &dpspCaps, 0 ) ) )
  489. {
  490. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  491. s_pNetInfo->m_testResult.m_hr = hr;
  492. return hr;
  493. }
  494. // Set the host expire time to around 3 times
  495. // length of the dwDefaultEnumRetryInterval
  496. s_dwEnumHostExpireInterval = dpspCaps.dwDefaultEnumRetryInterval * 3;
  497. // Create a device address
  498. ReleasePpo( &s_pDeviceAddress );
  499. hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,CLSCTX_INPROC_SERVER,
  500. IID_IDirectPlay8Address, (LPVOID*) &s_pDeviceAddress );
  501. if( FAILED(hr) )
  502. {
  503. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  504. s_pNetInfo->m_testResult.m_hr = hr;
  505. return hr;
  506. }
  507. if( FAILED( hr = s_pDeviceAddress->SetSP( &s_pNetSP->m_guid ) ) )
  508. {
  509. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  510. s_pNetInfo->m_testResult.m_hr = hr;
  511. return hr;
  512. }
  513. // Create a host address
  514. ReleasePpo( &s_pHostAddress );
  515. hr = CoCreateInstance( CLSID_DirectPlay8Address, NULL,CLSCTX_INPROC_SERVER,
  516. IID_IDirectPlay8Address, (LPVOID*) &s_pHostAddress );
  517. if( FAILED(hr) )
  518. {
  519. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  520. s_pNetInfo->m_testResult.m_hr = hr;
  521. return hr;
  522. }
  523. // Set the SP
  524. if( FAILED( hr = s_pHostAddress->SetSP( &s_pNetSP->m_guid ) ) )
  525. {
  526. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  527. s_pNetInfo->m_testResult.m_hr = hr;
  528. return hr;
  529. }
  530. // If TCP/IP then set the port if its non-zero
  531. if( s_pNetSP->m_guid == CLSID_DP8SP_TCPIP )
  532. {
  533. if( s_bCreateSession )
  534. {
  535. if( s_dwPort > 0 )
  536. {
  537. // Add the port to pDeviceAddress
  538. if( FAILED( hr = s_pDeviceAddress->AddComponent( DPNA_KEY_PORT,
  539. &s_dwPort, sizeof(s_dwPort),
  540. DPNA_DATATYPE_DWORD ) ) )
  541. {
  542. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  543. s_pNetInfo->m_testResult.m_hr = hr;
  544. return hr;
  545. }
  546. }
  547. }
  548. else
  549. {
  550. if( s_dwPort > 0 )
  551. {
  552. // Add the port to pHostAddress
  553. if( FAILED( hr = s_pHostAddress->AddComponent( DPNA_KEY_PORT,
  554. &s_dwPort, sizeof(s_dwPort),
  555. DPNA_DATATYPE_DWORD ) ) )
  556. {
  557. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ADDRESSING;
  558. s_pNetInfo->m_testResult.m_hr = hr;
  559. return hr;
  560. }
  561. }
  562. }
  563. }
  564. return S_OK;
  565. }
  566. /****************************************************************************
  567. *
  568. * SessionsDialogProc
  569. *
  570. ****************************************************************************/
  571. INT_PTR CALLBACK SessionsDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  572. {
  573. switch (uMsg)
  574. {
  575. case WM_INITDIALOG:
  576. {
  577. HRESULT hr;
  578. s_hwndSessionDlg = hDlg;
  579. s_bEnumListChanged = TRUE;
  580. // Enumerate hosts
  581. DPN_APPLICATION_DESC dnAppDesc;
  582. ZeroMemory( &dnAppDesc, sizeof(DPN_APPLICATION_DESC) );
  583. dnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
  584. dnAppDesc.guidApplication = s_guidDPTest;
  585. // Enumerate all the active DirectPlay games on the selected connection
  586. hr = s_pDP->EnumHosts( &dnAppDesc, // application description
  587. s_pHostAddress, // host address
  588. s_pDeviceAddress, // device address
  589. NULL, // pointer to user data
  590. 0, // user data size
  591. INFINITE, // retry count (forever)
  592. 0, // retry interval (0=default)
  593. INFINITE, // time out (forever)
  594. NULL, // user context
  595. &s_hEnumAsyncOp, // async handle
  596. DPNENUMHOSTS_OKTOQUERYFORADDRESSING // flags
  597. );
  598. if( FAILED(hr) )
  599. {
  600. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
  601. s_pNetInfo->m_testResult.m_hr = hr;
  602. EndDialog(hDlg, 0);
  603. return TRUE;
  604. }
  605. SessionsDlgInitListbox(hDlg);
  606. SetTimer(hDlg, TIMER_UPDATE_SESSION_LIST, 250, NULL);
  607. return TRUE;
  608. }
  609. case WM_TIMER:
  610. {
  611. if( wParam == TIMER_UPDATE_SESSION_LIST )
  612. SessionsDlgUpdateSessionList(hDlg);
  613. return TRUE;
  614. }
  615. case WM_COMMAND:
  616. {
  617. switch(LOWORD(wParam))
  618. {
  619. case IDOK:
  620. {
  621. HWND hwndList = GetDlgItem(hDlg, IDC_SESSIONLIST);
  622. LONG iSelCur = (LONG)SendMessage(hwndList, LB_GETCURSEL, 0, 0);
  623. if( iSelCur != LB_ERR )
  624. {
  625. // This will prevent s_pDPHostEnumSelected from being
  626. // deleting due to SessionsDlgUpdateSessionList()
  627. EnterCriticalSection( &s_csHostEnum );
  628. s_pDPHostEnumSelected = (DPHostEnumInfo*)SendMessage( hwndList, LB_GETITEMDATA,
  629. iSelCur, 0 );
  630. if ( (LRESULT)s_pDPHostEnumSelected != LB_ERR &&
  631. s_pDPHostEnumSelected != NULL )
  632. {
  633. // We keep the CS until we are done with s_pDPHostEnumSelected,
  634. // otherwise it might change out from under us.
  635. EndDialog(hDlg, 1);
  636. break;
  637. }
  638. s_pDPHostEnumSelected = NULL;
  639. LeaveCriticalSection( &s_csHostEnum );
  640. }
  641. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  642. EndDialog(hDlg, 0);
  643. break;
  644. }
  645. case IDCANCEL:
  646. {
  647. s_pDPHostEnumSelected = NULL;
  648. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  649. EndDialog(hDlg, 0);
  650. break;
  651. }
  652. }
  653. }
  654. case WM_DESTROY:
  655. {
  656. KillTimer( hDlg, TIMER_UPDATE_SESSION_LIST );
  657. s_hwndSessionDlg = NULL;
  658. break;
  659. }
  660. }
  661. return FALSE;
  662. }
  663. /****************************************************************************
  664. *
  665. * SessionsDlgInitListbox
  666. *
  667. ****************************************************************************/
  668. VOID SessionsDlgInitListbox( HWND hDlg )
  669. {
  670. HWND hWndListBox = GetDlgItem( hDlg, IDC_SESSIONLIST );
  671. LONG numChars;
  672. TCHAR szFmt[200];
  673. LoadString(NULL, IDS_LOOKINGFORSESSIONS, szFmt, 200);
  674. numChars = (LONG)SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), WM_GETTEXTLENGTH, 0, 0);
  675. SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_SETSEL, numChars, numChars);
  676. SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_REPLACESEL,
  677. FALSE, (LPARAM)szFmt);
  678. // Clear the contents from the list box, and
  679. // display "Looking for sessions" text in listbox
  680. SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
  681. SendMessage( hWndListBox, LB_SETITEMDATA, 0, NULL );
  682. SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
  683. // Disable the join button until sessions are found
  684. EnableWindow( GetDlgItem( hDlg, IDOK ), FALSE );
  685. }
  686. /****************************************************************************
  687. *
  688. * SessionsDlgNoteEnumResponse
  689. *
  690. ****************************************************************************/
  691. VOID SessionsDlgNoteEnumResponse( PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg )
  692. {
  693. HRESULT hr = S_OK;
  694. BOOL bFound;
  695. // This function is called from the DirectPlay message handler so it could be
  696. // called simultaneously from multiple threads, so enter a critical section
  697. // to assure that it we don't get race conditions.
  698. EnterCriticalSection( &s_csHostEnum );
  699. DPHostEnumInfo* pDPHostEnum = s_DPHostEnumHead.pNext;
  700. DPHostEnumInfo* pDPHostEnumNext = NULL;
  701. const DPN_APPLICATION_DESC* pResponseMsgAppDesc =
  702. pEnumHostsResponseMsg->pApplicationDescription;
  703. // Look for a matching session instance GUID.
  704. bFound = FALSE;
  705. while ( pDPHostEnum != &s_DPHostEnumHead )
  706. {
  707. if( pResponseMsgAppDesc->guidInstance == pDPHostEnum->pAppDesc->guidInstance )
  708. {
  709. bFound = TRUE;
  710. break;
  711. }
  712. pDPHostEnumNext = pDPHostEnum;
  713. pDPHostEnum = pDPHostEnum->pNext;
  714. }
  715. if( !bFound )
  716. {
  717. s_bEnumListChanged = TRUE;
  718. // If there's no match, then look for invalid session and use it
  719. pDPHostEnum = s_DPHostEnumHead.pNext;
  720. while ( pDPHostEnum != &s_DPHostEnumHead )
  721. {
  722. if( !pDPHostEnum->bValid )
  723. break;
  724. pDPHostEnum = pDPHostEnum->pNext;
  725. }
  726. // If no invalid sessions are found then make a new one
  727. if( pDPHostEnum == &s_DPHostEnumHead )
  728. {
  729. // Found a new session, so create a new node
  730. pDPHostEnum = new DPHostEnumInfo;
  731. if( NULL == pDPHostEnum )
  732. {
  733. hr = E_OUTOFMEMORY;
  734. goto LCleanup;
  735. }
  736. ZeroMemory( pDPHostEnum, sizeof(DPHostEnumInfo) );
  737. // Add pDPHostEnum to the circular linked list, m_DPHostEnumHead
  738. pDPHostEnum->pNext = s_DPHostEnumHead.pNext;
  739. s_DPHostEnumHead.pNext = pDPHostEnum;
  740. }
  741. }
  742. // Update the pDPHostEnum with new information
  743. TCHAR strName[MAX_PATH];
  744. if( pResponseMsgAppDesc->pwszSessionName )
  745. ConvertWideStringToGeneric( strName, pResponseMsgAppDesc->pwszSessionName, MAX_PATH );
  746. else
  747. lstrcpy( strName, TEXT("???") );
  748. // Cleanup any old enum
  749. if( pDPHostEnum->pAppDesc )
  750. {
  751. delete[] pDPHostEnum->pAppDesc->pwszSessionName;
  752. delete[] pDPHostEnum->pAppDesc;
  753. }
  754. ReleasePpo( &pDPHostEnum->pHostAddr );
  755. ReleasePpo( &pDPHostEnum->pDeviceAddr );
  756. pDPHostEnum->bValid = FALSE;
  757. //
  758. // Duplicate pEnumHostsResponseMsg->pAddressSender in pDPHostEnum->pHostAddr.
  759. // Duplicate pEnumHostsResponseMsg->pAddressDevice in pDPHostEnum->pDeviceAddr.
  760. //
  761. if( FAILED( hr = pEnumHostsResponseMsg->pAddressSender->Duplicate( &pDPHostEnum->pHostAddr ) ) )
  762. {
  763. goto LCleanup;
  764. }
  765. if( FAILED( hr = pEnumHostsResponseMsg->pAddressDevice->Duplicate( &pDPHostEnum->pDeviceAddr ) ) )
  766. {
  767. goto LCleanup;
  768. }
  769. // Deep copy the DPN_APPLICATION_DESC from
  770. pDPHostEnum->pAppDesc = new DPN_APPLICATION_DESC;
  771. ZeroMemory( pDPHostEnum->pAppDesc, sizeof(DPN_APPLICATION_DESC) );
  772. memcpy( pDPHostEnum->pAppDesc, pResponseMsgAppDesc, sizeof(DPN_APPLICATION_DESC) );
  773. if( pResponseMsgAppDesc->pwszSessionName )
  774. {
  775. pDPHostEnum->pAppDesc->pwszSessionName = new WCHAR[ wcslen(pResponseMsgAppDesc->pwszSessionName)+1 ];
  776. wcscpy( pDPHostEnum->pAppDesc->pwszSessionName,
  777. pResponseMsgAppDesc->pwszSessionName );
  778. }
  779. // Update the time this was done, so that we can expire this host
  780. // if it doesn't refresh w/in a certain amount of time
  781. pDPHostEnum->dwLastPollTime = timeGetTime();
  782. // if this node was previously invalidated, or the session name is now
  783. // different the session list in the dialog needs to be updated
  784. if( ( pDPHostEnum->bValid == FALSE ) ||
  785. ( _tcscmp( pDPHostEnum->szSession, strName ) != 0 ) )
  786. {
  787. s_bEnumListChanged = TRUE;
  788. }
  789. _tcscpy( pDPHostEnum->szSession, strName );
  790. // This host is now valid
  791. pDPHostEnum->bValid = TRUE;
  792. LCleanup:
  793. LeaveCriticalSection( &s_csHostEnum );
  794. }
  795. /****************************************************************************
  796. *
  797. * SessionsDlgUpdateSessionList
  798. *
  799. ****************************************************************************/
  800. VOID SessionsDlgUpdateSessionList( HWND hDlg )
  801. {
  802. HWND hWndListBox = GetDlgItem(hDlg, IDC_SESSIONLIST);
  803. DPHostEnumInfo* pDPHostEnum = NULL;
  804. DPHostEnumInfo* pDPHostEnumSelected = NULL;
  805. GUID guidSelectedInstance;
  806. BOOL bFindSelectedGUID;
  807. BOOL bFoundSelectedGUID;
  808. int nItemSelected;
  809. DWORD dwCurrentTime = timeGetTime();
  810. // This is called from the dialog UI thread, NoteEnumResponse()
  811. // is called from the DirectPlay message handler threads so
  812. // they may also be inside it at this time, so we need to go into the
  813. // critical section first
  814. EnterCriticalSection( &s_csHostEnum );
  815. // Expire old host enums
  816. pDPHostEnum = s_DPHostEnumHead.pNext;
  817. while ( pDPHostEnum != &s_DPHostEnumHead )
  818. {
  819. // Check the poll time to expire stale entries. Also check to see if
  820. // the entry is already invalid. If so, don't note that the enum list
  821. // changed because that causes the list in the dialog to constantly redraw.
  822. if( ( pDPHostEnum->bValid != FALSE ) &&
  823. ( pDPHostEnum->dwLastPollTime < dwCurrentTime - s_dwEnumHostExpireInterval ) )
  824. {
  825. // This node has expired, so invalidate it.
  826. pDPHostEnum->bValid = FALSE;
  827. s_bEnumListChanged = TRUE;
  828. }
  829. pDPHostEnum = pDPHostEnum->pNext;
  830. }
  831. // Only update the display list if it has changed since last time
  832. if( !s_bEnumListChanged )
  833. {
  834. LeaveCriticalSection( &s_csHostEnum );
  835. return;
  836. }
  837. s_bEnumListChanged = FALSE;
  838. bFindSelectedGUID = FALSE;
  839. bFoundSelectedGUID = FALSE;
  840. // Try to keep the same session selected unless it goes away or
  841. // there is no real session currently selected
  842. nItemSelected = (int)SendMessage( hWndListBox, LB_GETCURSEL, 0, 0 );
  843. if( nItemSelected != LB_ERR )
  844. {
  845. pDPHostEnumSelected = (DPHostEnumInfo*) SendMessage( hWndListBox, LB_GETITEMDATA,
  846. nItemSelected, 0 );
  847. if( pDPHostEnumSelected != NULL && pDPHostEnumSelected->bValid )
  848. {
  849. guidSelectedInstance = pDPHostEnumSelected->pAppDesc->guidInstance;
  850. bFindSelectedGUID = TRUE;
  851. }
  852. }
  853. // Tell listbox not to redraw itself since the contents are going to change
  854. SendMessage( hWndListBox, WM_SETREDRAW, FALSE, 0 );
  855. // Test to see if any sessions exist in the linked list
  856. pDPHostEnum = s_DPHostEnumHead.pNext;
  857. while ( pDPHostEnum != &s_DPHostEnumHead )
  858. {
  859. if( pDPHostEnum->bValid )
  860. break;
  861. pDPHostEnum = pDPHostEnum->pNext;
  862. }
  863. // If there are any sessions in list,
  864. // then add them to the listbox
  865. if( pDPHostEnum != &s_DPHostEnumHead )
  866. {
  867. // Clear the contents from the list box and enable the join button
  868. SendMessage( hWndListBox, LB_RESETCONTENT, 0, 0 );
  869. EnableWindow( GetDlgItem( hDlg, IDOK ), TRUE );
  870. pDPHostEnum = s_DPHostEnumHead.pNext;
  871. while ( pDPHostEnum != &s_DPHostEnumHead )
  872. {
  873. // Add host to list box if it is valid
  874. if( pDPHostEnum->bValid )
  875. {
  876. int nIndex = (int)SendMessage( hWndListBox, LB_ADDSTRING, 0,
  877. (LPARAM)pDPHostEnum->szSession );
  878. SendMessage( hWndListBox, LB_SETITEMDATA, nIndex, (LPARAM)pDPHostEnum );
  879. if( bFindSelectedGUID )
  880. {
  881. // Look for the session the was selected before
  882. if( pDPHostEnum->pAppDesc->guidInstance == guidSelectedInstance )
  883. {
  884. SendMessage( hWndListBox, LB_SETCURSEL, nIndex, 0 );
  885. bFoundSelectedGUID = TRUE;
  886. }
  887. }
  888. }
  889. pDPHostEnum = pDPHostEnum->pNext;
  890. }
  891. if( !bFindSelectedGUID || !bFoundSelectedGUID )
  892. SendMessage( hWndListBox, LB_SETCURSEL, 0, 0 );
  893. }
  894. else
  895. {
  896. // There are no active session, so just reset the listbox
  897. SessionsDlgInitListbox( hDlg );
  898. }
  899. // Tell listbox to redraw itself now since the contents have changed
  900. SendMessage( hWndListBox, WM_SETREDRAW, TRUE, 0 );
  901. InvalidateRect( hWndListBox, NULL, FALSE );
  902. LeaveCriticalSection( &s_csHostEnum );
  903. return;
  904. }
  905. /****************************************************************************
  906. *
  907. * SessionsDlgEnumListCleanup
  908. *
  909. ****************************************************************************/
  910. VOID SessionsDlgEnumListCleanup()
  911. {
  912. DPHostEnumInfo* pDPHostEnum = s_DPHostEnumHead.pNext;
  913. DPHostEnumInfo* pDPHostEnumDelete;
  914. while ( pDPHostEnum != &s_DPHostEnumHead )
  915. {
  916. pDPHostEnumDelete = pDPHostEnum;
  917. pDPHostEnum = pDPHostEnum->pNext;
  918. if( pDPHostEnumDelete->pAppDesc )
  919. {
  920. delete[] pDPHostEnumDelete->pAppDesc->pwszSessionName;
  921. delete[] pDPHostEnumDelete->pAppDesc;
  922. }
  923. // Changed from array delete to Release
  924. ReleasePpo( &pDPHostEnumDelete->pHostAddr );
  925. ReleasePpo( &pDPHostEnumDelete->pDeviceAddr );
  926. delete pDPHostEnumDelete;
  927. }
  928. // Re-link the s_DPHostEnumHead circular linked list
  929. s_DPHostEnumHead.pNext = &s_DPHostEnumHead;
  930. }
  931. /****************************************************************************
  932. *
  933. * ChatDialogProc
  934. *
  935. ****************************************************************************/
  936. INT_PTR CALLBACK ChatDialogProc(HWND hDlg, UINT uMsg, WPARAM wParam, LPARAM lParam)
  937. {
  938. switch (uMsg)
  939. {
  940. case WM_INITDIALOG:
  941. {
  942. s_hDlg = hDlg;
  943. // Join or host the session
  944. if( FAILED( InitSession() ) )
  945. {
  946. EndDialog(hDlg, 0);
  947. }
  948. return TRUE;
  949. }
  950. case WM_TIMER:
  951. {
  952. if( wParam == TIMER_WAIT_CONNECT_COMPLETE )
  953. {
  954. // Check for connect complete
  955. if( WAIT_OBJECT_0 == WaitForSingleObject( s_hConnectCompleteEvent, 0 ) )
  956. {
  957. s_bConnecting = FALSE;
  958. if( FAILED( s_hrConnectComplete ) )
  959. {
  960. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_JOINSESSION;
  961. s_pNetInfo->m_testResult.m_hr = s_hrConnectComplete;
  962. EndDialog(hDlg, 0);
  963. }
  964. else
  965. {
  966. // DirectPlay connect successful
  967. PostMessage( s_hDlg, WM_APP_CONNECTED, 0, 0 );
  968. EnableWindow( GetDlgItem( s_hDlg, IDC_SEND), TRUE );
  969. }
  970. KillTimer( s_hDlg, TIMER_WAIT_CONNECT_COMPLETE );
  971. }
  972. }
  973. break;
  974. }
  975. case WM_APP_CONNECTING:
  976. {
  977. WCHAR sz[MAX_PATH];
  978. LoadStringWide(IDS_CONNECTING, sz);
  979. ShowTextString( hDlg, sz );
  980. break;
  981. }
  982. case WM_APP_CONNECTED:
  983. {
  984. WCHAR sz[MAX_PATH];
  985. LoadStringWide(IDS_CONNECTED, sz);
  986. ShowTextString( hDlg, sz );
  987. break;
  988. }
  989. case WM_APP_JOIN:
  990. {
  991. APP_PLAYER_MSG* pPlayerMsg = (APP_PLAYER_MSG*) lParam;
  992. WCHAR szFmt[MAX_PATH];
  993. WCHAR szSuperMessage[MAX_PATH];
  994. LoadStringWide(IDS_JOINMSGFMT, szFmt);
  995. swprintf(szSuperMessage, szFmt, pPlayerMsg->strPlayerName);
  996. ShowTextString( hDlg, szSuperMessage );
  997. delete pPlayerMsg;
  998. break;
  999. }
  1000. case WM_APP_CHAT:
  1001. {
  1002. APP_QUEUE_CHAT_MSG* pQueuedChat = (APP_QUEUE_CHAT_MSG*) lParam;
  1003. ShowTextString( hDlg, pQueuedChat->strChatBuffer );
  1004. delete pQueuedChat;
  1005. break;
  1006. }
  1007. case WM_APP_LEAVE:
  1008. {
  1009. APP_PLAYER_MSG* pPlayerMsg = (APP_PLAYER_MSG*) lParam;
  1010. WCHAR szSuperMessage[MAX_PATH];
  1011. WCHAR szFmt[MAX_PATH];
  1012. LoadStringWide(IDS_LEAVEMSGFMT, szFmt);
  1013. swprintf(szSuperMessage, szFmt, pPlayerMsg->strPlayerName );
  1014. ShowTextString( hDlg, szSuperMessage );
  1015. delete pPlayerMsg;
  1016. break;
  1017. }
  1018. case WM_COMMAND:
  1019. {
  1020. switch(LOWORD(wParam))
  1021. {
  1022. case IDC_SEND:
  1023. {
  1024. HRESULT hr;
  1025. TCHAR szMessage[MAX_PATH];
  1026. GetWindowText(GetDlgItem(hDlg, IDC_CHATINPUT), szMessage, MAX_PATH);
  1027. SendMessage(GetDlgItem(hDlg, IDC_CHATINPUT), EM_SETSEL, 0, -1);
  1028. SendMessage(GetDlgItem(hDlg, IDC_CHATINPUT), EM_REPLACESEL, FALSE, (LPARAM)"");
  1029. hr = SendChatMessage( szMessage );
  1030. if (FAILED(hr))
  1031. {
  1032. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_SENDCHATMESSAGE;
  1033. s_pNetInfo->m_testResult.m_hr = hr;
  1034. EndDialog(hDlg, 1);
  1035. }
  1036. }
  1037. break;
  1038. case IDOK:
  1039. EndDialog(hDlg, 1);
  1040. break;
  1041. case IDCANCEL:
  1042. EndDialog(hDlg, 0);
  1043. break;
  1044. }
  1045. return TRUE;
  1046. }
  1047. case WM_DESTROY:
  1048. {
  1049. s_hDlg = NULL;
  1050. break;
  1051. }
  1052. }
  1053. return FALSE;
  1054. }
  1055. /****************************************************************************
  1056. *
  1057. * InitSession
  1058. *
  1059. ****************************************************************************/
  1060. HRESULT InitSession()
  1061. {
  1062. HRESULT hr;
  1063. if( s_bCreateSession )
  1064. {
  1065. // Set peer info name
  1066. WCHAR wszPeerName[MAX_PLAYER_NAME];
  1067. ConvertGenericStringToWide( wszPeerName, s_szPlayerName, MAX_PLAYER_NAME );
  1068. DPN_PLAYER_INFO dpPlayerInfo;
  1069. ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
  1070. dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
  1071. dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
  1072. dpPlayerInfo.pwszName = wszPeerName;
  1073. // Set the peer info, and use the DPNOP_SYNC since by default this
  1074. // is an async call. If it is not DPNOP_SYNC, then the peer info may not
  1075. // be set by the time we call Host() below.
  1076. if( FAILED( hr = s_pDP->SetPeerInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
  1077. {
  1078. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_SETPEERINFO;
  1079. s_pNetInfo->m_testResult.m_hr = hr;
  1080. return hr;
  1081. }
  1082. WCHAR wszSessionName[MAX_PATH];
  1083. ConvertGenericStringToWide( wszSessionName, s_szSessionName );
  1084. // Setup the application desc
  1085. DPN_APPLICATION_DESC dnAppDesc;
  1086. ZeroMemory( &dnAppDesc, sizeof(DPN_APPLICATION_DESC) );
  1087. dnAppDesc.dwSize = sizeof(DPN_APPLICATION_DESC);
  1088. dnAppDesc.guidApplication = s_guidDPTest;
  1089. dnAppDesc.pwszSessionName = wszSessionName;
  1090. dnAppDesc.dwFlags = DPNSESSION_MIGRATE_HOST;
  1091. // Host a game on m_pDeviceAddress as described by dnAppDesc
  1092. // DPNHOST_OKTOQUERYFORADDRESSING allows DirectPlay to prompt the user
  1093. // using a dialog box for any device address information that is missing
  1094. if( FAILED( hr = s_pDP->Host( &dnAppDesc, // the application desc
  1095. &s_pDeviceAddress, // array of addresses of the local devices used to connect to the host
  1096. 1, // number in array
  1097. NULL, NULL, // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  1098. NULL, // player context
  1099. DPNHOST_OKTOQUERYFORADDRESSING ) ) ) // flags
  1100. {
  1101. if (hr == DPNERR_USERCANCEL || hr == DPNERR_INVALIDDEVICEADDRESS)
  1102. {
  1103. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  1104. return hr;
  1105. }
  1106. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_CREATESESSION;
  1107. s_pNetInfo->m_testResult.m_hr = hr;
  1108. return hr;
  1109. }
  1110. }
  1111. else
  1112. {
  1113. // Set the peer info
  1114. WCHAR wszPeerName[MAX_PLAYER_NAME];
  1115. ConvertGenericStringToWide( wszPeerName, s_szPlayerName, MAX_PLAYER_NAME );
  1116. DPN_PLAYER_INFO dpPlayerInfo;
  1117. ZeroMemory( &dpPlayerInfo, sizeof(DPN_PLAYER_INFO) );
  1118. dpPlayerInfo.dwSize = sizeof(DPN_PLAYER_INFO);
  1119. dpPlayerInfo.dwInfoFlags = DPNINFO_NAME;
  1120. dpPlayerInfo.pwszName = wszPeerName;
  1121. // Set the peer info, and use the DPNOP_SYNC since by default this
  1122. // is an async call. If it is not DPNOP_SYNC, then the peer info may not
  1123. // be set by the time we call Connect() below.
  1124. if( FAILED( hr = s_pDP->SetPeerInfo( &dpPlayerInfo, NULL, NULL, DPNOP_SYNC ) ) )
  1125. {
  1126. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_SETPEERINFO;
  1127. s_pNetInfo->m_testResult.m_hr = hr;
  1128. LeaveCriticalSection( &s_csHostEnum );
  1129. return hr;
  1130. }
  1131. ResetEvent( s_hConnectCompleteEvent );
  1132. s_bConnecting = TRUE;
  1133. // Connect to an existing session. DPNCONNECT_OKTOQUERYFORADDRESSING allows
  1134. // DirectPlay to prompt the user using a dialog box for any device address
  1135. // or host address information that is missing
  1136. // We also pass in copies of the app desc and host addr, since pDPHostEnumSelected
  1137. // might be deleted from another thread that calls SessionsDlgExpireOldHostEnums().
  1138. // This process could also be done using reference counting instead.
  1139. hr = s_pDP->Connect( s_pDPHostEnumSelected->pAppDesc, // the application desc
  1140. s_pDPHostEnumSelected->pHostAddr, // address of the host of the session
  1141. s_pDPHostEnumSelected->pDeviceAddr, // address of the local device the enum responses were received on
  1142. NULL, NULL, // DPN_SECURITY_DESC, DPN_SECURITY_CREDENTIALS
  1143. NULL, 0, // user data, user data size
  1144. NULL, // player context,
  1145. NULL, &s_hConnectAsyncOp, // async context, async handle,
  1146. DPNCONNECT_OKTOQUERYFORADDRESSING ); // flags
  1147. LeaveCriticalSection( &s_csHostEnum );
  1148. if( hr != E_PENDING && FAILED(hr) )
  1149. {
  1150. if (hr == DPNERR_USERCANCEL)
  1151. {
  1152. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  1153. return hr;
  1154. }
  1155. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_JOINSESSION;
  1156. s_pNetInfo->m_testResult.m_hr = hr;
  1157. return hr;
  1158. }
  1159. // Set a timer to wait for m_hConnectCompleteEvent to be signaled.
  1160. // This will tell us when DPN_MSGID_CONNECT_COMPLETE has been processed
  1161. // which lets us know if the connect was successful or not.
  1162. PostMessage( s_hDlg, WM_APP_CONNECTING, 0, 0 );
  1163. SetTimer( s_hDlg, TIMER_WAIT_CONNECT_COMPLETE, 100, NULL );
  1164. EnableWindow( GetDlgItem( s_hDlg, IDC_SEND), FALSE );
  1165. }
  1166. return S_OK;
  1167. }
  1168. /****************************************************************************
  1169. *
  1170. * LoadStringWide
  1171. *
  1172. ****************************************************************************/
  1173. VOID LoadStringWide( int nID, WCHAR* szWide )
  1174. {
  1175. TCHAR sz[MAX_PATH];
  1176. LoadString(NULL, nID, sz, MAX_PATH);
  1177. ConvertGenericStringToWide( szWide, sz, MAX_PATH );
  1178. }
  1179. /****************************************************************************
  1180. *
  1181. * ShowTextString
  1182. *
  1183. ****************************************************************************/
  1184. VOID ShowTextString( HWND hDlg, WCHAR* sz )
  1185. {
  1186. TCHAR szT[MAX_CHAT_STRING];
  1187. ConvertWideStringToGeneric( szT, sz, MAX_CHAT_STRING );
  1188. LONG numChars = (LONG)SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), WM_GETTEXTLENGTH, 0, 0);
  1189. SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_SETSEL, numChars, numChars);
  1190. SendMessage(GetDlgItem(hDlg, IDC_CHATOUTPUT), EM_REPLACESEL, FALSE, (LPARAM)szT);
  1191. }
  1192. /****************************************************************************
  1193. *
  1194. * SendChatMessage
  1195. *
  1196. ****************************************************************************/
  1197. HRESULT SendChatMessage( TCHAR* szMessage )
  1198. {
  1199. // Send a message to all of the players
  1200. GAMEMSG_CHAT msgChat;
  1201. msgChat.nType = GAME_MSGID_CHAT;
  1202. ConvertGenericStringToWide( msgChat.strChatString, szMessage, MAX_CHAT_STRING_LENGTH-1 );
  1203. msgChat.strChatString[MAX_CHAT_STRING_LENGTH-1] = 0;
  1204. DPN_BUFFER_DESC bufferDesc;
  1205. bufferDesc.dwBufferSize = sizeof(GAMEMSG_CHAT);
  1206. bufferDesc.pBufferData = (BYTE*) &msgChat;
  1207. DPNHANDLE hAsync;
  1208. s_pDP->SendTo( DPNID_ALL_PLAYERS_GROUP, &bufferDesc, 1,
  1209. 0, NULL, &hAsync, 0 );
  1210. return S_OK;
  1211. }
  1212. /****************************************************************************
  1213. *
  1214. * DirectPlayMessageHandler
  1215. *
  1216. ****************************************************************************/
  1217. HRESULT WINAPI DirectPlayMessageHandler( PVOID pvUserContext,
  1218. DWORD dwMessageId,
  1219. PVOID pMsgBuffer )
  1220. {
  1221. switch( dwMessageId )
  1222. {
  1223. case DPN_MSGID_CONNECT_COMPLETE:
  1224. {
  1225. PDPNMSG_CONNECT_COMPLETE pConnectCompleteMsg;
  1226. pConnectCompleteMsg = (PDPNMSG_CONNECT_COMPLETE)pMsgBuffer;
  1227. // Set m_hrConnectComplete, then set an event letting
  1228. // everyone know that the DPN_MSGID_CONNECT_COMPLETE msg
  1229. // has been handled
  1230. s_hrConnectComplete = pConnectCompleteMsg->hResultCode;
  1231. SetEvent( s_hConnectCompleteEvent );
  1232. break;
  1233. }
  1234. case DPN_MSGID_ENUM_HOSTS_RESPONSE:
  1235. {
  1236. PDPNMSG_ENUM_HOSTS_RESPONSE pEnumHostsResponseMsg;
  1237. pEnumHostsResponseMsg = (PDPNMSG_ENUM_HOSTS_RESPONSE)pMsgBuffer;
  1238. // Take note of the host response
  1239. SessionsDlgNoteEnumResponse( pEnumHostsResponseMsg );
  1240. break;
  1241. }
  1242. case DPN_MSGID_ASYNC_OP_COMPLETE:
  1243. {
  1244. PDPNMSG_ASYNC_OP_COMPLETE pAsyncOpCompleteMsg;
  1245. pAsyncOpCompleteMsg = (PDPNMSG_ASYNC_OP_COMPLETE)pMsgBuffer;
  1246. if( pAsyncOpCompleteMsg->hAsyncOp == s_hEnumAsyncOp )
  1247. {
  1248. SessionsDlgEnumListCleanup();
  1249. s_hEnumAsyncOp = NULL;
  1250. // Ignore errors if we are connecting already or something else failed
  1251. if( !s_bConnecting && s_pNetInfo->m_testResult.m_iStepThatFailed == 0 )
  1252. {
  1253. if( FAILED(pAsyncOpCompleteMsg->hResultCode) )
  1254. {
  1255. if( pAsyncOpCompleteMsg->hResultCode == DPNERR_USERCANCEL )
  1256. {
  1257. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
  1258. s_pNetInfo->m_testResult.m_hr = pAsyncOpCompleteMsg->hResultCode;
  1259. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  1260. }
  1261. else if( pAsyncOpCompleteMsg->hResultCode == DPNERR_ADDRESSING )
  1262. {
  1263. TCHAR szTitle[MAX_PATH];
  1264. TCHAR szMessage[MAX_PATH];
  1265. LoadString(NULL, IDS_APPFULLNAME, szTitle, MAX_PATH);
  1266. LoadString(NULL, IDS_SESSIONLISTERROR, szMessage, MAX_PATH);
  1267. MessageBox(s_hwndSessionDlg, szMessage, szTitle, MB_OK);
  1268. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
  1269. s_pNetInfo->m_testResult.m_hr = pAsyncOpCompleteMsg->hResultCode;
  1270. s_pNetInfo->m_testResult.m_bCancelled = TRUE;
  1271. }
  1272. else
  1273. {
  1274. s_pNetInfo->m_testResult.m_iStepThatFailed = TESTID_ENUMSESSIONS;
  1275. s_pNetInfo->m_testResult.m_hr = pAsyncOpCompleteMsg->hResultCode;
  1276. }
  1277. EndDialog(s_hwndSessionDlg, 1);
  1278. }
  1279. }
  1280. }
  1281. break;
  1282. }
  1283. case DPN_MSGID_TERMINATE_SESSION:
  1284. {
  1285. PDPNMSG_TERMINATE_SESSION pTerminateSessionMsg;
  1286. pTerminateSessionMsg = (PDPNMSG_TERMINATE_SESSION)pMsgBuffer;
  1287. EndDialog(s_hDlg,0);
  1288. break;
  1289. }
  1290. case DPN_MSGID_CREATE_PLAYER:
  1291. {
  1292. HRESULT hr;
  1293. PDPNMSG_CREATE_PLAYER pCreatePlayerMsg;
  1294. pCreatePlayerMsg = (PDPNMSG_CREATE_PLAYER)pMsgBuffer;
  1295. // Get the peer info and extract its name
  1296. DWORD dwSize = 0;
  1297. DPN_PLAYER_INFO* pdpPlayerInfo = NULL;
  1298. // Create a new and fill in a APP_PLAYER_INFO
  1299. APP_PLAYER_INFO* pPlayerInfo = new APP_PLAYER_INFO;
  1300. ZeroMemory( pPlayerInfo, sizeof(APP_PLAYER_INFO) );
  1301. pPlayerInfo->dpnidPlayer = pCreatePlayerMsg->dpnidPlayer;
  1302. wcscpy( pPlayerInfo->strPlayerName, L"???" );
  1303. pPlayerInfo->lRefCount = 1;
  1304. hr = s_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
  1305. if( SUCCEEDED(hr) || hr == DPNERR_BUFFERTOOSMALL )
  1306. {
  1307. pdpPlayerInfo = (DPN_PLAYER_INFO*) new BYTE[ dwSize ];
  1308. ZeroMemory( pdpPlayerInfo, dwSize );
  1309. pdpPlayerInfo->dwSize = sizeof(DPN_PLAYER_INFO);
  1310. hr = s_pDP->GetPeerInfo( pCreatePlayerMsg->dpnidPlayer, pdpPlayerInfo, &dwSize, 0 );
  1311. if( SUCCEEDED(hr) )
  1312. {
  1313. // This stores a extra TCHAR copy of the player name for
  1314. // easier access. This will be redundent copy since DPlay
  1315. // also keeps a copy of the player name in GetPeerInfo()
  1316. wcsncpy( pPlayerInfo->strPlayerName,
  1317. pdpPlayerInfo->pwszName, MAX_PLAYER_NAME );
  1318. pPlayerInfo->strPlayerName[MAX_PLAYER_NAME-1] = 0;
  1319. }
  1320. }
  1321. if( pdpPlayerInfo->dwPlayerFlags & DPNPLAYER_LOCAL )
  1322. s_dpnidLocalPlayer = pCreatePlayerMsg->dpnidPlayer;
  1323. delete[] pdpPlayerInfo;
  1324. pdpPlayerInfo = NULL;
  1325. if( s_hDlg )
  1326. {
  1327. // Record the buffer handle so the buffer can be returned later
  1328. APP_PLAYER_MSG* pPlayerMsg = new APP_PLAYER_MSG;
  1329. wcscpy( pPlayerMsg->strPlayerName, pPlayerInfo->strPlayerName );
  1330. // Pass the APP_PLAYER_MSG to the main dialog thread, so it can
  1331. // process it. It will also cleanup the struct
  1332. PostMessage( s_hDlg, WM_APP_JOIN, pPlayerInfo->dpnidPlayer, (LPARAM) pPlayerMsg );
  1333. }
  1334. // Tell DirectPlay to store this pPlayerInfo
  1335. // pointer in the pvPlayerContext.
  1336. pCreatePlayerMsg->pvPlayerContext = pPlayerInfo;
  1337. // Update the number of active players, and
  1338. // post a message to the dialog thread to update the
  1339. // UI. This keeps the DirectPlay message handler
  1340. // from blocking
  1341. InterlockedIncrement( &s_lNumberOfActivePlayers );
  1342. break;
  1343. }
  1344. case DPN_MSGID_DESTROY_PLAYER:
  1345. {
  1346. PDPNMSG_DESTROY_PLAYER pDestroyPlayerMsg;
  1347. pDestroyPlayerMsg = (PDPNMSG_DESTROY_PLAYER)pMsgBuffer;
  1348. APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pDestroyPlayerMsg->pvPlayerContext;
  1349. if( s_hDlg )
  1350. {
  1351. // Record the buffer handle so the buffer can be returned later
  1352. APP_PLAYER_MSG* pPlayerMsg = new APP_PLAYER_MSG;
  1353. wcscpy( pPlayerMsg->strPlayerName, pPlayerInfo->strPlayerName );
  1354. // Pass the APP_PLAYER_MSG to the main dialog thread, so it can
  1355. // process it. It will also cleanup the struct
  1356. PostMessage( s_hDlg, WM_APP_LEAVE, pPlayerInfo->dpnidPlayer, (LPARAM) pPlayerMsg );
  1357. }
  1358. PLAYER_LOCK(); // enter player context CS
  1359. PLAYER_RELEASE( pPlayerInfo ); // Release player and cleanup if needed
  1360. PLAYER_UNLOCK(); // leave player context CS
  1361. // Update the number of active players, and
  1362. // post a message to the dialog thread to update the
  1363. // UI. This keeps the DirectPlay message handler
  1364. // from blocking
  1365. InterlockedDecrement( &s_lNumberOfActivePlayers );
  1366. break;
  1367. }
  1368. case DPN_MSGID_RECEIVE:
  1369. {
  1370. PDPNMSG_RECEIVE pReceiveMsg;
  1371. pReceiveMsg = (PDPNMSG_RECEIVE)pMsgBuffer;
  1372. APP_PLAYER_INFO* pPlayerInfo = (APP_PLAYER_INFO*) pReceiveMsg->pvPlayerContext;
  1373. GAMEMSG_GENERIC* pMsg = (GAMEMSG_GENERIC*) pReceiveMsg->pReceiveData;
  1374. if( pReceiveMsg->dwReceiveDataSize == sizeof(GAMEMSG_CHAT) &&
  1375. pMsg->nType == GAME_MSGID_CHAT )
  1376. {
  1377. // This message is sent when a player has send a chat message to us, so
  1378. // post a message to the dialog thread to update the UI.
  1379. // This keeps the DirectPlay threads from blocking, and also
  1380. // serializes the recieves since DirectPlayMessageHandler can
  1381. // be called simultaneously from a pool of DirectPlay threads.
  1382. GAMEMSG_CHAT* pChatMessage = (GAMEMSG_CHAT*) pMsg;
  1383. // Record the buffer handle so the buffer can be returned later
  1384. APP_QUEUE_CHAT_MSG* pQueuedChat = new APP_QUEUE_CHAT_MSG;
  1385. _snwprintf( pQueuedChat->strChatBuffer, MAX_CHAT_STRING, L"<%s> %s\r\n",
  1386. pPlayerInfo->strPlayerName,
  1387. pChatMessage->strChatString );
  1388. pQueuedChat->strChatBuffer[MAX_CHAT_STRING-1]=0;
  1389. // Pass the APP_QUEUE_CHAT_MSG to the main dialog thread, so it can
  1390. // process it. It will also cleanup the struct
  1391. PostMessage( s_hDlg, WM_APP_CHAT, pPlayerInfo->dpnidPlayer, (LPARAM) pQueuedChat );
  1392. }
  1393. break;
  1394. }
  1395. }
  1396. return S_OK;
  1397. }
  1398. /****************************************************************************
  1399. *
  1400. * ConvertAnsiStringToWide
  1401. *
  1402. ****************************************************************************/
  1403. VOID ConvertAnsiStringToWide( WCHAR* wstrDestination, const CHAR* strSource,
  1404. int cchDestChar )
  1405. {
  1406. if( wstrDestination==NULL || strSource==NULL )
  1407. return;
  1408. if( cchDestChar == -1 )
  1409. cchDestChar = strlen(strSource)+1;
  1410. MultiByteToWideChar( CP_ACP, 0, strSource, -1,
  1411. wstrDestination, cchDestChar-1 );
  1412. wstrDestination[cchDestChar-1] = 0;
  1413. }
  1414. /****************************************************************************
  1415. *
  1416. * ConvertGenericStringToWide
  1417. *
  1418. ****************************************************************************/
  1419. VOID ConvertGenericStringToWide( WCHAR* wstrDestination, const TCHAR* tstrSource, int cchDestChar )
  1420. {
  1421. if( wstrDestination==NULL || tstrSource==NULL )
  1422. return;
  1423. #ifdef _UNICODE
  1424. if( cchDestChar == -1 )
  1425. wcscpy( wstrDestination, tstrSource );
  1426. else
  1427. {
  1428. wcsncpy( wstrDestination, tstrSource, cchDestChar );
  1429. wstrDestination[cchDestChar-1] = 0;
  1430. }
  1431. #else
  1432. ConvertAnsiStringToWide( wstrDestination, tstrSource, cchDestChar );
  1433. #endif
  1434. }
  1435. /****************************************************************************
  1436. *
  1437. * ConvertWideStringToGeneric
  1438. *
  1439. ****************************************************************************/
  1440. VOID ConvertWideStringToGeneric( TCHAR* tstrDestination, const WCHAR* wstrSource, int cchDestChar )
  1441. {
  1442. if( tstrDestination==NULL || wstrSource==NULL )
  1443. return;
  1444. #ifdef _UNICODE
  1445. if( cchDestChar == -1 )
  1446. wcscpy( tstrDestination, wstrSource );
  1447. else
  1448. {
  1449. wcsncpy( tstrDestination, wstrSource, cchDestChar );
  1450. tstrDestination[cchDestChar-1] = 0;
  1451. }
  1452. #else
  1453. ConvertWideStringToAnsi( tstrDestination, wstrSource, cchDestChar );
  1454. #endif
  1455. }
  1456. /****************************************************************************
  1457. *
  1458. * ConvertWideStringToAnsi
  1459. *
  1460. ****************************************************************************/
  1461. VOID ConvertWideStringToAnsi( CHAR* strDestination, const WCHAR* wstrSource, int cchDestChar )
  1462. {
  1463. if( strDestination==NULL || wstrSource==NULL )
  1464. return;
  1465. if( cchDestChar == -1 )
  1466. cchDestChar = wcslen(wstrSource)+1;
  1467. WideCharToMultiByte( CP_ACP, 0, wstrSource, -1, strDestination,
  1468. cchDestChar-1, NULL, NULL );
  1469. strDestination[cchDestChar-1] = 0;
  1470. }