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.

740 lines
23 KiB

  1. /*
  2. This script is invoked by master.js to provide async
  3. communications to the slave machines running "slave.js"
  4. Communication between harness and master is exclusivly thru
  5. "messages".
  6. */
  7. /*
  8. notes:
  9. On abort we still need to be able to get to all the remote status information
  10. so that it can be inspected.
  11. Not until setmode IDLE is it OK to loose the status.
  12. May need to keep connections alive until setmode idle, maybe not.
  13. Build.log, build.wrn, build.err, sync.log, sync.err
  14. Publicdata.abuild[0].aDepot[n].aTask[x].strLog* will contain
  15. UNCs to these log files.
  16. HARNESS.js overview of how it works:
  17. mtscript
  18. starts master
  19. master
  20. starts harness
  21. master sends "start" message to harness
  22. harness
  23. for each remote machine
  24. start a slaveproxy
  25. wait for OK/FAIL
  26. sends "start" message to each slaveproxy
  27. slaveproxy
  28. When "start" message is received,
  29. connects to a remote mtscript.
  30. Exec setconfig
  31. Exec setenv
  32. Exec setmode slave
  33. Exec start
  34. slave
  35. Waits
  36. On exec("start") begin the DoBuild process
  37. When status in PublicData changes, NotifyScript("UpdateAll");
  38. After a build pass completes,
  39. set PublicData.aBuild[0].hMachine[""].strBuildPassStatus == "waitnext[012]"
  40. NotifyScript("SetBuildStatus") to slaveproxy
  41. Wait 'DoNextPass';
  42. On exec("nextpass") reset strBuildPassStatus, signal 'DoNextPass'
  43. harness sets status and syncs mtscript
  44. */
  45. Include('types.js');
  46. Include('utils.js');
  47. Include('staticstrings.js');
  48. Include('MsgQueue.js');
  49. Include('buildreport.js');
  50. var g_cDialogIndex = 0;
  51. var g_aSlaveQueues = new Array();
  52. var g_hSlaveQueues = new Object();
  53. var g_MasterQueue = null;
  54. var g_strConfigError = 'ConfigError';
  55. var g_strOK = 'ok';
  56. var g_strSlaveProxyFail = 'slaveproxy.js failed to start';
  57. var g_strNoEnvTemplate = 'must set config and environment templates first';
  58. var g_strException = "an error occurred, but I'm not telling what it was";
  59. var g_strDisconnect = "The connection to the remote machine no longer exists";
  60. var g_HarnessThreadReady = 'HarnessThreadReady';
  61. var g_HarnessThreadFailed = 'HarnessThreadFailed';
  62. var g_aHarnessWaitFor = ['HarnessThreadExit', 'RebuildWaitArray','handlebuildwaiting'];
  63. var g_aSlaveProxySignals = ['SlaveProxyThreadReady', 'SlaveProxyThreadFailed'];
  64. var g_nBuildPass = 0;
  65. var g_aStrBuildPassStatus = new Array();
  66. function harness_js::OnScriptError(strFile, nLine, nChar, strText, sCode, strSource, strDescription)
  67. {
  68. return CommonOnScriptError("harness_js", strFile, nLine, nChar, strText, sCode, strSource, strDescription);
  69. }
  70. function harness_js::ScriptMain()
  71. {
  72. LogMsg('ScriptMain()');
  73. var nEvent;
  74. var aWaitQueues;
  75. var i;
  76. g_MasterQueue = GetMsgQueue(ScriptParam);
  77. if (typeof(g_MasterQueue) == 'object')
  78. {
  79. LogMsg('ScriptMain() HarnessThreadReady');
  80. ResetSync('SlaveProxyThreadExit');
  81. SpawnScript('publicdataupdate.js', 0);
  82. SignalThreadSync(g_HarnessThreadReady);
  83. CommonVersionCheck(/* $DROPVERSION: */ "V(########) F(!!!!!!!!!!!!!!)" /* $ */);
  84. do
  85. {
  86. var name = '';
  87. aWaitQueues = new Array();
  88. aWaitQueues[0] = g_MasterQueue;
  89. aWaitQueues = aWaitQueues.concat(g_aSlaveQueues);
  90. nEvent = WaitForMultipleQueues(aWaitQueues, g_aHarnessWaitFor.toString(), HarnessMsgProc, 0);
  91. if (nEvent > 0 && nEvent <= g_aHarnessWaitFor.length)
  92. {
  93. ResetSync(g_aHarnessWaitFor[nEvent - 1]);
  94. if (nEvent == 3)
  95. {
  96. HandleBuildWaiting();
  97. }
  98. }
  99. }
  100. while (nEvent != 1); // While not HarnessThreadExit
  101. AbortRemoteMachines();
  102. }
  103. else
  104. {
  105. LogMsg('ScriptMain() ' + g_HarnessThreadFailed);
  106. SignalThreadSync(g_HarnessThreadFailed);
  107. }
  108. SignalThreadSync('publicdataupdateexit');
  109. // tell the slaveproxy thread to quit
  110. SignalThreadSync('SlaveProxyThreadExit');
  111. LogMsg('ScriptMain() exit');
  112. }
  113. function harness_js::OnEventSourceEvent(RemoteObj, DispID, cmd, params)
  114. {
  115. LogMsg('OnEventSourceEvent()');
  116. }
  117. function HarnessMsgProc(queue, msg)
  118. {
  119. var vRet = g_strOK;
  120. var params;
  121. try
  122. {
  123. params = msg.aArgs;
  124. if (queue == g_MasterQueue)
  125. {
  126. LogMsg('masterQ' + params[0]);
  127. switch (params[0])
  128. {
  129. case 'test':
  130. LogMsg('Recieved a test message. ArgCount=' + params.length + ' arg1 is: ' + params[1]);
  131. break;
  132. case 'start':
  133. vRet = StartRemoteMachines();
  134. SignalThreadSync(g_aHarnessWaitFor[1]);
  135. break;
  136. case 'restartcopyfiles':
  137. vRet = BroadCastMessage('restartcopyfiles', params[1], (params[1] ? [ params[1] ] : null ), false);
  138. break;
  139. case 'abort':
  140. AbortRemoteMachines();
  141. break;
  142. case 'remote':
  143. LogMsg("Recieved remote razzle command for machine " + params[1]);
  144. vRet = BroadCastMessage('remote', params[1], [ params[1] ], true);
  145. break;
  146. case 'ignoreerror': // Params are: ignoreerror,{mach},8,\\{mach}\BC_Build_Logs\build_logs\build_Admin.log
  147. LogMsg("ignore error, params are: " + params.join(", "));
  148. PrivateData.dateErrorMailSent = 0;
  149. vRet = BroadCastMessage('ignoreerror',
  150. params[1],
  151. [(params[1].split(','))[0]]);
  152. break;
  153. case 'restarttask': // task id: "machine.nID"
  154. break;
  155. case 'harnessexit':
  156. AbortRemoteMachines();
  157. SignalThreadSync(g_aHarnessWaitFor[0]);
  158. break;
  159. // DEBUG USE ONLY:
  160. // 'refreshpublicdata' allows you to force the
  161. // build manager to do a complete update of
  162. // its copy of PublicData from the specified
  163. // machine, or all machines.
  164. // 'getspdata' causes SlaveProxy to uneval
  165. // its view of PublicData.
  166. // The format for the param for these commands is:
  167. // " {machinename | all},data "
  168. // where "data" is specific to the command.
  169. case 'refreshpublicdata':
  170. case 'getspdata':
  171. ExecuteDebugCommand(params[0], params[1]);
  172. break;
  173. // speval -- Pass the argument to the
  174. // named remote machine to be executed by
  175. // the remote machine "mtscript.js"
  176. case 'speval':
  177. ExecuteDebugCommand('eval', params[1]);
  178. break;
  179. // spseval -- Pass the argument to the
  180. // named remote machine to be executed by
  181. // the remote machine "slave.js"
  182. case 'spseval':
  183. ExecuteDebugCommand('seval', params[1]);
  184. break;
  185. // proxeval -- Pass the argument to the
  186. // proxy for the named remote machine to be executed by
  187. // the remote machine's proxy ("slaveproxy.js");
  188. case 'proxeval':
  189. ExecuteDebugCommand('proxeval', params[1]);
  190. break;
  191. default:
  192. vRet = 'invalid command: ' + params[0];
  193. break;
  194. }
  195. }
  196. else
  197. {
  198. LogMsg('slaveQ' + params[0]);
  199. switch(params[0])
  200. {
  201. case 'displaydialog':
  202. DisplayDialog(params[1]);
  203. break;
  204. case 'abort':
  205. AbortRemoteMachines();
  206. break;
  207. }
  208. // ignore for now....
  209. }
  210. }
  211. catch(ex)
  212. {
  213. vRet = " " + ex;
  214. }
  215. LogMsg('returns :' + vRet);
  216. return vRet;
  217. }
  218. function harness_js::OnProcessEvent(pid, evt, param)
  219. {
  220. ASSERT(false, 'OnProcessEvent('+pid+', '+evt+', '+param+') received!');
  221. }
  222. function AbortRemoteMachines()
  223. {
  224. LogMsg("Aborting remote machines");
  225. var i;
  226. var aMsgs = new Object();
  227. var nEvent;
  228. for(i = 0; i < g_aSlaveQueues.length; ++i)
  229. {
  230. if (g_aSlaveQueues[i] != null)
  231. aMsgs[i] = g_aSlaveQueues[i].SendMessage('abort');
  232. }
  233. LogMsg("Aborted remote machines -- waiting for response");
  234. for(i in aMsgs)
  235. {
  236. if (aMsgs.__isPublicMember(i))
  237. {
  238. if (g_aSlaveQueues[i].WaitForMsg(aMsgs[i], 2000))
  239. delete aMsgs[i];
  240. else
  241. LogMsg("Machine " + g_aSlaveQueues.strName + " timeout");
  242. }
  243. }
  244. // try again
  245. for(i in aMsgs)
  246. {
  247. if (aMsgs.__isPublicMember(i))
  248. {
  249. if (g_aSlaveQueues[i].WaitForMsg(aMsgs[i], 2000))
  250. delete aMsgs[i];
  251. else
  252. LogMsg("Machine " + g_aSlaveQueues.strName + " timeout 2");
  253. }
  254. }
  255. for(i = 0; i < g_aSlaveQueues.length; ++i)
  256. {
  257. if (aMsgs[i] != null)
  258. {
  259. LogMsg("Machine " + g_aSlaveQueues.strName + " failed to abort");
  260. }
  261. }
  262. g_aSlaveQueues = new Array();
  263. g_hSlaveQueues = new Object();
  264. }
  265. function StartRemoteMachines()
  266. {
  267. var vRet = g_strOK;
  268. var i;
  269. var aMachinePool;
  270. var index = 0;
  271. var aIConnectedMachine = new Array();
  272. var newmach = new Object;
  273. var aSlaveQueues = new Array();
  274. var hSlaveQueues = new Object();
  275. var hStartedMachines = new Object(); // hash of machine we launched
  276. var nEvent = 0;
  277. var SlaveQueue;
  278. try
  279. {
  280. EnsureArray(PrivateData.objEnviron, 'Machine');
  281. aMachinePool = PrivateData.objEnviron.Machine;
  282. // First cleanup any leftover connections
  283. for(i in PublicData.aBuild[0].hMachine)
  284. {
  285. if (PublicData.aBuild[0].hMachine.__isPublicMember(i))
  286. {
  287. ThrowError("StartRemoteMachines called, but there are still open connections",0);
  288. }
  289. }
  290. ResetSync('SlaveProxyThreadExit');
  291. index = 0;
  292. for (i = 0; i < aMachinePool.length && nEvent != 2; ++i)
  293. {
  294. if (hStartedMachines[aMachinePool[i].Name] == null)
  295. {
  296. if (LocalMachine == aMachinePool[i].Name)
  297. {
  298. ThrowError("Warning: cannot start remote build on local machine " + aMachinePool[i].Name);
  299. }
  300. hStartedMachines[aMachinePool[i].Name] = true;
  301. SlaveQueue = new MsgQueue("slaveproxy" + index + "," + aMachinePool[i].Name);
  302. LogMsg('Start proxy for machine: ' + aMachinePool[i].Name);
  303. ResetSync(g_aSlaveProxySignals.toString());
  304. SpawnScript('slaveproxy.js', SlaveQueue);
  305. nEvent = WaitForMultipleSyncs(g_aSlaveProxySignals.toString(), false, 0);
  306. if (nEvent == 2)
  307. break;
  308. hSlaveQueues[aMachinePool[i].Name] = SlaveQueue;
  309. aSlaveQueues[index++] = SlaveQueue;
  310. }
  311. }
  312. g_aSlaveQueues = aSlaveQueues;
  313. g_hSlaveQueues = hSlaveQueues;
  314. if (nEvent == 2)
  315. {
  316. vRet = g_strSlaveProxyFail;
  317. ReportError("Starting build", "Cannot start build on machine " + aMachinePool[i].Name);
  318. AbortRemoteMachines();
  319. }
  320. else
  321. {
  322. for(i = 0; i < g_aSlaveQueues.length; ++i)
  323. {
  324. g_aSlaveQueues[i].SendMessage('start');
  325. }
  326. }
  327. }
  328. catch(ex)
  329. {
  330. vRet = " " + ex;
  331. LogMsg(vRet);
  332. }
  333. return vRet;
  334. }
  335. function ReportError(strTitle, strMsg)
  336. {
  337. dlg = new Dialog();
  338. dlg.fShowDialog = true;
  339. dlg.cDialogIndex = 0;
  340. dlg.strTitle = strTitle;
  341. dlg.strMessage = strMsg;
  342. dlg.aBtnText[0] = "OK";
  343. DisplayDialog(dlg);
  344. }
  345. function OnRemoteExecHandler(info, param)
  346. {
  347. LogMsg('Got OnRemoteExec! info='+info+', param='+param);
  348. }
  349. function ChangeFileStatus(strFrom, strTo)
  350. {
  351. try // BUGBUG remove this try/catch
  352. {
  353. var strMachineName;
  354. var strSDRoot;
  355. var PubData;
  356. var i;
  357. var nFiles = 0;
  358. // BUGBUG: This should scan "hPublishedFiles" instead of hPublisher -- its a flatter structure
  359. // and it refers to the same data anyway.
  360. for(strMachineName in PrivateData.hPublisher)
  361. {
  362. if (!PrivateData.hPublisher.__isPublicMember(strMachineName))
  363. continue;
  364. PubData = PrivateData.hPublisher[strMachineName];
  365. for (strSDRoot in PubData.hPublishEnlistment)
  366. {
  367. if (!PubData.hPublishEnlistment.__isPublicMember(strSDRoot))
  368. continue;
  369. publishEnlistment = PubData.hPublishEnlistment[strSDRoot];
  370. for (i = 0; i < publishEnlistment.aPublishedFile.length; ++i)
  371. {
  372. if (publishEnlistment.aPublishedFile[i].strPublishedStatus == strFrom)
  373. {
  374. // LogMsg("Change status to " + strTo + " of " + publishEnlistment.aPublishedFile[i].strName + " from " + strFrom);
  375. publishEnlistment.aPublishedFile[i].strPublishedStatus = strTo;
  376. nFiles++;
  377. }
  378. }
  379. }
  380. }
  381. LogMsg("Changed the status of " + nFiles + " files from " + strFrom + " to " + strTo);
  382. }
  383. catch(ex)
  384. {
  385. LogMsg("ChangeFileStatus " +
  386. strFrom +
  387. ", " +
  388. strTo +
  389. ", exception mach=" +
  390. strMachineName +
  391. ", root=" +
  392. strSDRoot +
  393. ", i is " + i +
  394. ", " + ex);
  395. throw ex;
  396. }
  397. }
  398. function HandleBuildWaiting()
  399. {
  400. try
  401. {
  402. var strMachineName;
  403. var strStatExpecting = '';
  404. var strStat;
  405. var strMatch;
  406. var nPass;
  407. var fDiff = false;
  408. var i;
  409. var fSuccess = true;
  410. i = 0;
  411. for (strMachineName in PublicData.aBuild[0].hMachine)
  412. {
  413. if (!PublicData.aBuild[0].hMachine.__isPublicMember(strMachineName))
  414. continue;
  415. strStat = strMachineName + "," + PublicData.aBuild[0].hMachine[strMachineName].strBuildPassStatus;
  416. if (g_aStrBuildPassStatus.length < i || g_aStrBuildPassStatus[i] != strStat)
  417. {
  418. g_aStrBuildPassStatus[i] = strStat;
  419. fDiff = true;
  420. }
  421. fSuccess &= PublicData.aBuild[0].hMachine[strMachineName].fSuccess;
  422. ++i;
  423. }
  424. // Set the Failure flag in the script host to allow for easy error/success queries
  425. // Oddly, normally Number(!false) == 1, but here, StatusValue(0) get set to either 0 or -1.
  426. StatusValue(0) = !fSuccess;
  427. if (!fDiff)
  428. return;
  429. LogMsg("handlebuildwaiting strBuildPassStatus changed");
  430. for (i = 0; i < g_aStrBuildPassStatus.length; ++i)
  431. {
  432. LogMsg("handlebuildwaiting " + g_aStrBuildPassStatus[i]);
  433. }
  434. for (i = 0; i < g_aStrBuildPassStatus.length; ++i)
  435. {
  436. var strStat = g_aStrBuildPassStatus[i].split(',');
  437. var nPass = strStat[2];
  438. strStat = strStat[1];
  439. if (strStatExpecting == '')
  440. strStatExpecting = strStat;
  441. if (strStat != strStatExpecting)
  442. {
  443. PublicData.strStatus = BUSY;
  444. return;
  445. }
  446. if (strStatExpecting == WAITNEXT && nPass != g_nBuildPass)
  447. {
  448. // This can happen if:
  449. // all remote machines were at NEXTPASS,0, then
  450. // one machine got to NEXTPASS,1 before at least one
  451. // of the other machines changed its strBuildPassStatus
  452. //LogMsg("Slave " + strMachineName + " is building the wrong pass!");
  453. PublicData.strStatus = BUSY;
  454. return;
  455. }
  456. }
  457. LogMsg("strStatExpecting is " + strStatExpecting);
  458. if (strStatExpecting != COMPLETED)
  459. PublicData.strStatus = BUSY;
  460. switch(strStatExpecting)
  461. {
  462. case WAITCOPYTOPOSTBUILD:
  463. HandleWaitCopyToPostBuild();
  464. break;
  465. case WAIT + BUILD:
  466. HandleWaitBuild();
  467. break;
  468. case WAITNEXT:
  469. HandleWaitNext();
  470. break;
  471. case WAIT + POSTBUILD:
  472. case COMPLETED:
  473. HandleCompleted();
  474. break;
  475. case WAITPHASE:
  476. // Change the file status of files published in phase 2.
  477. ChangeFileStatus(FS_COPYTOSLAVE, FS_ADDTOPUBLISHLOG );
  478. ChangeFileStatus(FS_COPIEDTOMASTER, FS_COPYTOSLAVE);
  479. BroadCastMessage('createmergedpublish.log', '', [PrivateData.objEnviron.BuildManager.PostBuildMachine]);
  480. BroadCastMessage('nextpass');
  481. break;
  482. case WAIT + SYNC:
  483. break;
  484. default:
  485. LogMsg("No action on strBuildPassStatus == " + strStatExpecting);
  486. break;
  487. }
  488. }
  489. catch(ex)
  490. {
  491. LogMsg("strMachineName = " + strMachineName + ", strStatExpecting=" + strStatExpecting + ", " + ex);
  492. }
  493. }
  494. function HandleCompleted()
  495. {
  496. PublicData.strStatus = COMPLETED;
  497. GenerateBuildReport();
  498. }
  499. function HandleWaitCopyToPostBuild()
  500. {
  501. LogMsg("(pass " + g_nBuildPass + ")");
  502. BroadCastMessage('copyfilestopostbuild');
  503. }
  504. function HandleWaitBuild()
  505. {
  506. // All slaves are waiting. Time to copy files back to slaves
  507. var i;
  508. LogMsg("(pass " + g_nBuildPass + ")");
  509. ChangeFileStatus(FS_COPYTOSLAVE, FS_ADDTOPUBLISHLOG );
  510. ChangeFileStatus(FS_COPIEDTOMASTER, FS_COPYTOSLAVE);
  511. BroadCastMessage('copyfilestoslaves');
  512. BroadCastMessage('nextpass');
  513. }
  514. function HandleWaitNext()
  515. {
  516. LogMsg("(pass " + g_nBuildPass + ")");
  517. ++g_nBuildPass;
  518. BroadCastMessage('nextpass');
  519. }
  520. /*
  521. Send a command to 1 or more slaveproxies.
  522. "cmd" is sent.
  523. Arg can be:
  524. empty: Send CMD to all slaveproxies
  525. name: Send CMD to just one slaveproxy, for machine "name".
  526. all: Send CMD to all slaveproxies;
  527. name,stuff: Send CMD to machine "name", with argument "stuff"
  528. all,stuff: Send CMD to all machines, with argument "stuff"
  529. */
  530. function ExecuteDebugCommand(cmd, arg)
  531. {
  532. var aMachines;
  533. var nComma;
  534. if (arg.length > 0)
  535. {
  536. aMachines = new Array();
  537. nComma = arg.indexOf(',');
  538. if (nComma > 0)
  539. {
  540. aMachines[0] = arg.slice(0, nComma).toUpperCase();
  541. arg = arg.slice(nComma + 1);
  542. }
  543. else
  544. {
  545. aMachines[0] = arg.toUpperCase();
  546. arg = "";
  547. }
  548. if (aMachines[0] == "ALL")
  549. aMachines = null;
  550. }
  551. BroadCastMessage(cmd, arg, aMachines, true);
  552. }
  553. /*
  554. BroadCastMessage
  555. Send a simple text message to all or a selected set of the slaveproxies.
  556. If aMachines is specified (as an array of machine names) then broadcast
  557. only to those machines.
  558. If fWait is true, then wait for each slaveproxy to reply to the message
  559. */
  560. function BroadCastMessage(strMsg, strArg, aMachines, fWait)
  561. {
  562. var i;
  563. var aQueues;
  564. var vRet = g_strOK;
  565. var aMsgs = new Object();
  566. LogMsg("BroadCastMessage(" + strMsg + ',' + strArg + ',' + aMachines + ',' + fWait + ")");
  567. if (!aMachines)
  568. {
  569. aQueues = g_aSlaveQueues;
  570. }
  571. else
  572. {
  573. aQueues = new Array();
  574. for (i = 0; i < aMachines.length; i++)
  575. {
  576. if (g_hSlaveQueues[aMachines[i]])
  577. {
  578. aQueues[aQueues.length] = g_hSlaveQueues[aMachines[i]];
  579. }
  580. else
  581. {
  582. vRet = g_strDisconnect;
  583. }
  584. }
  585. }
  586. for(i = 0; i < aQueues.length; ++i)
  587. {
  588. try
  589. {
  590. if (aQueues[i] != null)
  591. aMsgs[i] = aQueues[i].SendMessage(strMsg, strArg);
  592. }
  593. catch(ex)
  594. {
  595. LogMsg(" i = " +
  596. i +
  597. ", " +
  598. ex);
  599. if (aQueues[i] != null)
  600. LogMsg("Queue name is " + aQueues[i].strName);
  601. }
  602. }
  603. if (!fWait)
  604. {
  605. return vRet;
  606. }
  607. for(i in aMsgs)
  608. {
  609. if (aMsgs.__isPublicMember(i))
  610. {
  611. if (aQueues[i].WaitForMsg(aMsgs[i], 15000))
  612. {
  613. LogMsg("Machine " + aQueues[i].strName + " replied for " + strMsg);
  614. vRet = aMsgs[i].vReplyValue;
  615. delete aMsgs[i];
  616. }
  617. else
  618. LogMsg("Machine " + aQueues[i].strName + " timeout");
  619. }
  620. }
  621. return vRet;
  622. }
  623. /*
  624. DisplayDialog()
  625. Handle ErrorDialog requests from SlaveProxies as well
  626. as harness generated errors.
  627. Messages are both EMailed and displayed as a dialog.
  628. Throttle EMail messages by MAIL_RESEND_INTERVAL
  629. Throttle dialog display by PublicData.objDialog.fShowDialog.
  630. */
  631. function DisplayDialog(dlg)
  632. {
  633. var curDate = new Date().getTime();
  634. LogMsg("DisplayDialog: " + dlg.strMessage);
  635. TakeThreadLock('Dialog');
  636. try
  637. {
  638. if ((curDate - PrivateData.dateErrorMailSent) > MAIL_RESEND_INTERVAL)
  639. {
  640. PrivateData.dateErrorMailSent = curDate;
  641. SendErrorMail(dlg.strTitle, dlg.strMessage);
  642. }
  643. if (!dlg.fEMailOnly && (!PublicData.objDialog || PublicData.objDialog.fShowDialog == false))
  644. {
  645. dlg.cDialogIndex = ++g_cDialogIndex;
  646. PublicData.objDialog = dlg;
  647. }
  648. }
  649. catch(ex)
  650. {
  651. ReleaseThreadLock('Dialog');
  652. LogMsg("an error occurred in DisplayDialog, " + ex);
  653. throw ex;
  654. }
  655. ReleaseThreadLock('Dialog');
  656. }