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.

604 lines
16 KiB

  1. /***************************************************************************/
  2. /** Microsoft Windows **/
  3. /** Copyright(c) Microsoft Corp., 1991, 1992 **/
  4. /***************************************************************************/
  5. /****************************************************************************
  6. welcome.cpp
  7. Aug 92, JimH
  8. May 93, JimH chico port
  9. more CMainWindow member functions
  10. CTheApp::InitInstance() posts a IDM_WELCOME message as soon as it has
  11. constructed and shown the main window. This file includes that message's
  12. handler (OnWelcome) and some support routines.
  13. ****************************************************************************/
  14. #include "hearts.h"
  15. #include <nddeapi.h>
  16. #include "main.h"
  17. #include "resource.h"
  18. #include "debug.h"
  19. extern "C" {
  20. HINSTANCE FAR PASCAL WNetGetCaps(WORD); // winnet.h equivalent prototype
  21. }
  22. static const TCHAR *szServerName = TEXT("MSHearts"); // don't translate
  23. static const TCHAR *szTopicName = TEXT("Hearts");
  24. // Typedefs for NDdeShareGetInfo (SGIPROC) and NDdeShareAdd (SAPROC)
  25. typedef UINT (WINAPI *SGIPROC)(LPSTR, LPCSTR, UINT, LPBYTE, DWORD, LPDWORD, LPWORD);
  26. typedef UINT (WINAPI *SAPROC)(LPSTR, UINT, LPBYTE, DWORD );
  27. /****************************************************************************
  28. CMainWindow::OnWelcome()
  29. Pop up the Welcome dialog. If we're gamemeister, set up dde handles and
  30. name. If we're a player, try to do the dde connection, set up advise loops,
  31. and create other player objects as remote humans. (They all look like
  32. remote humans to a PLAYER regardless of whether or not there is any flesh
  33. and blood at the other end.)
  34. ****************************************************************************/
  35. void CMainWindow::OnWelcome()
  36. {
  37. // bugbug -- what should "hearts" string really be?
  38. BOOL bCmdLine = (*m_lpCmdLine != '\0');
  39. bAutostarted = (lstrcmpi(m_lpCmdLine, TEXT("hearts")) == 0);
  40. if (bAutostarted)
  41. HeartsPlaySound(SND_QUEEN); // tell new dealer someone wants to play
  42. else
  43. CheckNddeShare();
  44. CWelcomeDlg welcome(this);
  45. again: // cancel from Browse returns here
  46. if (!bAutostarted && !bCmdLine)
  47. {
  48. if (IDCANCEL == welcome.DoModal()) // display Welcome dialog
  49. {
  50. PostMessage(WM_CLOSE);
  51. return;
  52. }
  53. }
  54. bNetDdeActive = welcome.IsNetDdeActive();
  55. if (bAutostarted || welcome.IsGameMeister()) // if Gamemeister
  56. {
  57. CClientDC dc(this);
  58. #ifdef USE_MIRRORING
  59. SetLayout(dc.m_hDC, 0);
  60. SetLayout(dc.m_hAttribDC, 0);
  61. #endif
  62. role = GAMEMEISTER;
  63. m_myid = 0;
  64. ddeServer = new DDEServer(szServerName, szTopicName,
  65. (DDECALLBACK)DdeServerCallBack);
  66. if (!ddeServer || !ddeServer->GetResult())
  67. {
  68. FatalError(IDS_SERVERFAIL);
  69. return;
  70. }
  71. dde = ddeServer;
  72. CreateStrHandles();
  73. // Don't show message if netdde not found
  74. if (bNetDdeActive)
  75. p[0]->UpdateStatus(IDS_GMWAIT); // wait for others to connect
  76. else
  77. p[0]->SetStatus(IDS_GMWAIT);
  78. CString name = welcome.GetMyName();
  79. if (name.IsEmpty())
  80. name.LoadString(IDS_DEALER);
  81. p[0]->SetName(name, dc);
  82. p[0]->DisplayName(dc);
  83. // If no netdde, no point waiting around for others to join
  84. if (!bNetDdeActive)
  85. PostMessage(WM_COMMAND, IDM_NEWGAME);
  86. return;
  87. }
  88. // At this point, we know we're not a gamemeister
  89. role = PLAYER;
  90. CString server, name;
  91. if (!bCmdLine)
  92. {
  93. // char buf[MAXCOMPUTERNAME+10]; // handle slashes, etc.
  94. // BROWSEPROC m_pWNetServerBrowseDialog;
  95. // HINSTANCE hmodNetDriver = WNetGetCaps(0xFFFF);
  96. // m_pWNetServerBrowseDialog =
  97. // (BROWSEPROC)GetProcAddress(hmodNetDriver, MAKEINTRESOURCE(146));
  98. // WORD res = (*m_pWNetServerBrowseDialog)( m_hWnd,
  99. // "MRU_MSHearts",
  100. // buf,
  101. // MAXCOMPUTERNAME+10,
  102. // 0L );
  103. // if (res != WN_SUCCESS)
  104. // goto again;
  105. // server = buf;
  106. CLocateDlg locate(this);
  107. if (IDCANCEL == locate.DoModal()) // display locate dialog
  108. goto again;
  109. server = locate.GetServer();
  110. }
  111. else
  112. server = m_lpCmdLine;
  113. if (server[0] != '\\')
  114. {
  115. CString sSlashes("\\\\");
  116. server = sSlashes + server;
  117. }
  118. name = welcome.GetMyName();
  119. if (name.IsEmpty())
  120. name.LoadString(IDS_UNKNOWN);
  121. ClientConnect(server, name);
  122. }
  123. void CMainWindow::ClientConnect(CString& server, CString& myname)
  124. {
  125. GAMESTATUS gs;
  126. // A blank server name is legal. It means try to do a local DDE
  127. // connection to a server running on the same machine. A non-blank
  128. // server name means we must construct the app name \\server\NDDE$
  129. // and the topic name has to end with $.
  130. if (server.IsEmpty()) // local connection
  131. {
  132. ddeClient =
  133. new DDEClient(szServerName, szTopicName,
  134. (DDECALLBACK)DdeClientCallBack);
  135. }
  136. else
  137. {
  138. CString prefix;
  139. if (server.GetAt(0) == '\\') // did string come back with \\s
  140. prefix = "";
  141. else
  142. prefix = "\\\\"; // if not, got to add them
  143. CString postfix = "\\NDDE$";
  144. CString nettopic = szTopicName;
  145. nettopic += "$";
  146. ddeClient = new DDEClient(prefix+server+postfix, nettopic,
  147. (DDECALLBACK)DdeClientCallBack);
  148. }
  149. // For invalid local dde session attempts, the following GetResult()
  150. // will fail. Since netdde connections always succeed, you can't tell if
  151. // a server is really there until after the Poke() later on.
  152. if (!ddeClient->GetResult())
  153. {
  154. TRACE1("GetResult last error is %x\n", ddeClient->GetLastError());
  155. FatalError(IDS_NOSERVER);
  156. return;
  157. }
  158. dde = ddeClient;
  159. CreateStrHandles();
  160. CMenu *pMenu = GetMenu();
  161. pMenu->EnableMenuItem(IDM_NEWGAME, MF_GRAYED);
  162. p[0]->UpdateStatus(IDS_CONNECTING);
  163. // Tell the server your name. This is done as a synchronous Poke()
  164. // (most others in hearts are asynch) with a 5 second timeout. If
  165. // that fails, we finally can be sure the server is not there.
  166. tryagain:
  167. if (!(ddeClient->Poke(hszJoin, myname, 5000)))
  168. {
  169. UINT err = ddeClient->GetLastError();
  170. if (err == DMLERR_BUSY) // game in progress
  171. {
  172. CString caption, text;
  173. text.LoadString(IDS_NOTREADY);
  174. caption.LoadString(IDS_APPNAME);
  175. if (bSoundOn)
  176. MessageBeep(MB_ICONINFORMATION);
  177. #if defined (WINDOWS_ME) && ! defined (USE_MIRRORING)
  178. if (meSystem)
  179. {
  180. if (IDRETRY == ::MessageBoxEx(GetSafeHwnd(), text, caption,
  181. MB_ICONINFORMATION | MB_RETRYCANCEL |
  182. meMsgBox, 0))
  183. goto tryagain;
  184. }
  185. else
  186. {
  187. if (IDRETRY == MessageBox(text, caption,
  188. MB_ICONINFORMATION | MB_RETRYCANCEL))
  189. goto tryagain;
  190. }
  191. #else
  192. if (IDRETRY == MessageBox(text, caption,
  193. MB_ICONINFORMATION | MB_RETRYCANCEL))
  194. goto tryagain;
  195. #endif
  196. FatalError();
  197. }
  198. else
  199. FatalError(IDS_NOSERVER);
  200. return;
  201. }
  202. // Ask the server for the list of current player names
  203. HDDEDATA hData = ddeClient->RequestData(hszStatus, 5000);
  204. if (!hData)
  205. {
  206. FatalError(IDS_UNKNOWNERR);
  207. return;
  208. }
  209. else
  210. {
  211. ddeClient->GetData(hData, (PBYTE)&gs, sizeof(gs));
  212. UINT err = ddeClient->GetLastError();
  213. if (err != DMLERR_NO_ERROR)
  214. {
  215. TRACE1("Get Data last error is %x\n", err);
  216. FatalError(IDS_UNKNOWNERR);
  217. return;
  218. }
  219. m_myid = gs.id;
  220. }
  221. // When p[0] got created, it was assumed to be a gamemeister, i.e.
  222. // its id was assumed to be 0. Now we know it's not true, so fix
  223. // it with a call to SetPlayerId().
  224. ((local_human *)p[0])->SetPlayerId(m_myid);
  225. p[0]->UpdateStatus(IDS_PWAIT);
  226. CClientDC dc(this);
  227. #ifdef USE_MIRRORING
  228. SetLayout(dc.m_hDC, 0);
  229. SetLayout(dc.m_hAttribDC, 0);
  230. #endif
  231. p[0]->SetName(myname, dc);
  232. p[0]->DisplayName(dc);
  233. // Create the other player objects as remote humans. A white lie,
  234. // but they look like remote humans to us.
  235. for (int pos = 1; pos < MAXPLAYER; pos++)
  236. {
  237. int remoteid = Pos2Id(pos);
  238. p[pos] = new remote_human(remoteid, pos, 0);
  239. if (p[pos] == NULL)
  240. {
  241. FatalError(IDS_MEMORY);
  242. return;
  243. }
  244. }
  245. UpdateStatusNames(gs);
  246. ddeClient->StartAdviseLoop(hszStatus); // player names and ids
  247. ddeClient->StartAdviseLoop(hszPass); // cards passed
  248. ddeClient->StartAdviseLoop(hszMove); // cards played
  249. ddeClient->StartAdviseLoop(hszGameNumber); // should be last in list
  250. }
  251. /****************************************************************************
  252. CMainWindow::CreateStrHandles()
  253. CMainWindow::DestroyStrHandles()
  254. All the dde string handles are created and cleaned up here except for the
  255. server name and topic name which the DDE class handles.
  256. ****************************************************************************/
  257. BOOL CMainWindow::CreateStrHandles()
  258. {
  259. hszJoin = dde->CreateStrHandle(TEXT("Join"));
  260. hszPass = dde->CreateStrHandle(TEXT("Pass"));
  261. hszMove = dde->CreateStrHandle(TEXT("Move"));
  262. hszStatus = dde->CreateStrHandle(TEXT("Status"));
  263. hszGameNumber = dde->CreateStrHandle(TEXT("GameNumber"));
  264. hszPassUpdate = dde->CreateStrHandle(TEXT("PassUpdate"));
  265. return (hszJoin && hszPass && hszMove && hszStatus && hszGameNumber
  266. && hszPassUpdate);
  267. }
  268. void CMainWindow::DestroyStrHandles()
  269. {
  270. if (!dde)
  271. return;
  272. dde->DestroyStrHandle(hszJoin);
  273. dde->DestroyStrHandle(hszPass);
  274. dde->DestroyStrHandle(hszMove);
  275. dde->DestroyStrHandle(hszStatus);
  276. dde->DestroyStrHandle(hszGameNumber);
  277. dde->DestroyStrHandle(hszPassUpdate);
  278. }
  279. /****************************************************************************
  280. CMainWindow::FatalError()
  281. A static BOOL prevents this function from being called reentrantly. One is
  282. enough, and more than one leaves things in bad states. The parameter is
  283. the IDS_X constant that identifies the string to display.
  284. There is also a check that we don't try to shut down while the score dialog
  285. is displayed. This avoids some nasty debug traps when the score dialog
  286. doesn't shut down properly. The same problems can happen if, say, a dealer
  287. quits when a client is looking at the quote. Oh well.
  288. ****************************************************************************/
  289. void CMainWindow::FatalError(int errorno)
  290. {
  291. if (p[0]->GetMode() == SCORING)
  292. {
  293. m_FatalErrno = errorno;
  294. return;
  295. }
  296. static BOOL bClosing = FALSE;
  297. if (bClosing)
  298. return;
  299. bClosing = TRUE;
  300. if (errno != -1) // if not default
  301. {
  302. CString s1, s2;
  303. s1.LoadString(errno);
  304. s2.LoadString(IDS_APPNAME);
  305. if (bSoundOn)
  306. MessageBeep(MB_ICONSTOP);
  307. #if defined (WINDOWS_ME) && ! defined (USE_MIRRORING)
  308. if (meSystem)
  309. ::MessageBoxEx(GetSafeHwnd(), s1, s2, MB_ICONSTOP | meMsgBox, 0); // potential reentrancy problem
  310. else
  311. #endif
  312. MessageBox(s1, s2, MB_ICONSTOP); // potential reentrancy problem
  313. }
  314. PostMessage(WM_CLOSE);
  315. }
  316. /****************************************************************************
  317. CMainWindow::UpdateStatusNames() client only
  318. Called after server advises that status has changed.
  319. ****************************************************************************/
  320. void CMainWindow::UpdateStatusNames(GAMESTATUS& gs)
  321. {
  322. ASSERT(role == PLAYER);
  323. if (gs.id != m_myid)
  324. {
  325. TRACE1("gs.id is %d ", gs.id);
  326. TRACE1("and m_myid is %d\n", m_myid);
  327. return;
  328. }
  329. CClientDC dc(this);
  330. #ifdef USE_MIRRORING
  331. SetLayout(dc.m_hDC, 0);
  332. SetLayout(dc.m_hAttribDC, 0);
  333. #endif
  334. for (int pos = 0; pos < MAXPLAYER; pos++)
  335. {
  336. int id = Pos2Id(pos);
  337. if (gs.name[id][0])
  338. {
  339. CString s;
  340. s = gs.name[id];
  341. p[pos]->SetName(s, dc);
  342. p[pos]->DisplayName(dc);
  343. }
  344. }
  345. }
  346. /****************************************************************************
  347. CMainWindow::GameOver
  348. ****************************************************************************/
  349. void CMainWindow::GameOver()
  350. {
  351. CClientDC dc(this);
  352. #ifdef USE_MIRRORING
  353. SetLayout(dc.m_hDC, 0);
  354. SetLayout(dc.m_hAttribDC, 0);
  355. #endif
  356. InvalidateRect(NULL, TRUE);
  357. p[0]->SetMode(STARTING);
  358. p[0]->SetScore(0);
  359. for (int i = 1; i < MAXPLAYER; i++)
  360. {
  361. delete p[i];
  362. p[i] = NULL;
  363. }
  364. if (role == GAMEMEISTER)
  365. {
  366. if (bNetDdeActive)
  367. p[0]->UpdateStatus(IDS_GMWAIT);
  368. else
  369. p[0]->SetStatus(IDS_GMWAIT);
  370. p[0]->DisplayName(dc);
  371. CMenu *pMenu = GetMenu();
  372. pMenu->EnableMenuItem(IDM_NEWGAME, MF_ENABLED);
  373. if (!bNetDdeActive)
  374. PostMessage(WM_COMMAND, IDM_NEWGAME);
  375. return;
  376. }
  377. CString myname = p[0]->GetName();
  378. delete ddeClient;
  379. ddeClient = NULL;
  380. dde = NULL;
  381. CString text, caption;
  382. text.LoadString(IDS_AGAIN); // wanna play again?
  383. caption.LoadString(IDS_APPNAME);
  384. if (bSoundOn)
  385. MessageBeep(MB_ICONQUESTION);
  386. #if defined (WINDOWS_ME) && ! defined (USE_MIRRORING)
  387. if (meSystem)
  388. {
  389. if (IDNO == ::MessageBoxEx(GetSafeHwnd(), text, caption,
  390. MB_ICONQUESTION | MB_YESNO | meMsgBox, 0))
  391. {
  392. FatalError();
  393. return;
  394. }
  395. }
  396. else
  397. #endif
  398. if (IDNO == MessageBox(text, caption, MB_ICONQUESTION | MB_YESNO))
  399. {
  400. FatalError();
  401. return;
  402. }
  403. CString server;
  404. RegEntry Reg(szRegPath);
  405. TCHAR *pserver = server.GetBuffer(MAXCOMPUTERNAME+1);
  406. Reg.GetString(regvalServer, pserver, MAXCOMPUTERNAME+1);
  407. server.ReleaseBuffer();
  408. ClientConnect(server, myname);
  409. }
  410. /****************************************************************************
  411. CMainWindow::CheckNddeShare
  412. Check that NDDE share exists, and add it if not.
  413. ****************************************************************************/
  414. void CMainWindow::CheckNddeShare()
  415. {
  416. /*
  417. DWORD dwAvail;
  418. WORD wItems;
  419. BYTE buffer[200];
  420. SetErrorMode(SEM_NOOPENFILEERRORBOX);
  421. HINSTANCE hinstNDDEAPI = LoadLibrary("NDDEAPI.DLL");
  422. if (hinstNDDEAPI <= (HINSTANCE)HINSTANCE_ERROR)
  423. return;
  424. SGIPROC lpfnNDdeShareGetInfo =
  425. (SGIPROC) GetProcAddress(hinstNDDEAPI, "NDdeShareGetInfo");
  426. if (lpfnNDdeShareGetInfo == NULL)
  427. {
  428. FreeLibrary(hinstNDDEAPI);
  429. return;
  430. }
  431. UINT res = (*lpfnNDdeShareGetInfo)(NULL, szShareName, 2,
  432. buffer, sizeof(buffer), &dwAvail, &wItems);
  433. if (res != NDDE_SHARE_NOT_EXIST)
  434. return;
  435. NDDESHAREINFO *pnddeInfo = (NDDESHAREINFO *)buffer;
  436. SAPROC lpfnNDdeShareAdd =
  437. (SAPROC) GetProcAddress(hinstNDDEAPI, "NDdeShareAdd");
  438. if (lpfnNDdeShareAdd == NULL)
  439. {
  440. FreeLibrary(hinstNDDEAPI);
  441. return;
  442. }
  443. lstrcpy(pnddeInfo->szShareName, szShareName);
  444. pnddeInfo->lpszTargetApp = "mshearts"; // non-const szServerName
  445. pnddeInfo->lpszTargetTopic = "Hearts"; // non-const szTopicName
  446. pnddeInfo->lpbPassword1 = (LPBYTE) "";
  447. pnddeInfo->cbPassword1 = 0;
  448. pnddeInfo->dwPermissions1 = 15;
  449. pnddeInfo->lpbPassword2 = (LPBYTE) "";
  450. pnddeInfo->cbPassword2 = 0;
  451. pnddeInfo->dwPermissions2 = 0;
  452. pnddeInfo->lpszItem = "";
  453. pnddeInfo->cAddItems = 0;
  454. pnddeInfo->lpNDdeShareItemInfo = NULL;
  455. res = (*lpfnNDdeShareAdd)(NULL, 2, buffer, sizeof(buffer));
  456. TRACE("NDdeShareAdd returns %u\n", res);
  457. FreeLibrary(hinstNDDEAPI);
  458. */
  459. }