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.

878 lines
23 KiB

  1. //
  2. // tclient2.c
  3. //
  4. // This contains many wrappers for TCLIENT with some new features to
  5. // make the API easier to use.
  6. //
  7. // Copyright (C) 2001 Microsoft Corporation
  8. //
  9. // Author: a-devjen (Devin Jenson)
  10. //
  11. #include <windows.h>
  12. #include <protocol.h>
  13. #include <extraexp.h>
  14. #include <tclient2.h>
  15. #include "connlist.h"
  16. #include "apihandl.h"
  17. #include "idletimr.h"
  18. #include "tclnthlp.h"
  19. // T2Check
  20. //
  21. // This is a wrapper for TCLIENT's SCCheck function.
  22. //
  23. // Returns NULL on success, or a string explaining the error
  24. // on failure.
  25. TSAPI LPCSTR T2Check(HANDLE Connection, LPCSTR Command, LPCWSTR Param)
  26. {
  27. // Validate the handle
  28. if (T2IsHandle(Connection) == FALSE)
  29. return "Invalid connection handle";
  30. // Do the TCLIENT action
  31. return SCCheck(SCCONN(Connection), Command, Param);
  32. }
  33. // T2ClientTerminate
  34. //
  35. // This is a wrapper for TCLIENT's SCClientTerminate function.
  36. //
  37. // Returns NULL on success, or a string explaining the error
  38. // on failure.
  39. TSAPI LPCSTR T2ClientTerminate(HANDLE Connection)
  40. {
  41. // Validate the handle
  42. if (T2IsHandle(Connection) == FALSE)
  43. return "Invalid connection handle";
  44. // Do the TCLIENT action
  45. return SCClientTerminate(SCCONN(Connection));
  46. }
  47. // T2Clipboard
  48. //
  49. // This is a wrapper for TCLIENT's SCClipboard function.
  50. //
  51. // Returns NULL on success, or a string explaining the error
  52. // on failure.
  53. TSAPI LPCSTR T2Clipboard(HANDLE Connection, INT ClipOp, LPCSTR FileName)
  54. {
  55. // Validate the handle
  56. if (T2IsHandle(Connection) == FALSE)
  57. return "Invalid connection handle";
  58. // Do the TCLIENT action
  59. return SCClipboard(SCCONN(Connection), ClipOp, FileName);
  60. }
  61. // T2CloseClipboard
  62. //
  63. // This is a wrapper for TCLIENT's T2CloseClipboard function.
  64. //
  65. // Returns TRUE on success and FALSE on failure.
  66. TSAPI BOOL T2CloseClipboard(VOID)
  67. {
  68. return SCCloseClipboard();
  69. }
  70. // T2Connect
  71. //
  72. // This is a wrapper for TCLIENT's SCConnect function. It does some
  73. // additional stuff however - it allocates a handle local to
  74. // TCLIENT2, sets default data such as Words Per Minute, and even
  75. // attempts to get the build number immediately after the connection.
  76. //
  77. // Returns NULL on success, or a string explaining the error
  78. // on failure.
  79. TSAPI LPCSTR T2Connect(LPCWSTR Server, LPCWSTR User,
  80. LPCWSTR Pass, LPCWSTR Domain,
  81. INT xRes, INT yRes, HANDLE *Connection)
  82. {
  83. __try {
  84. LPCSTR Result = NULL;
  85. // Create a new connection handle
  86. TSAPIHANDLE *T2Handle = T2CreateHandle();
  87. if (T2Handle == NULL)
  88. return "Could not allocate an API handle";
  89. // Connect
  90. Result = SCConnect(Server, User, Pass, Domain,
  91. xRes, yRes, &(T2Handle->SCConnection));
  92. if (Result != NULL) {
  93. // Connection failed, destroy handle and return
  94. T2DestroyHandle((HANDLE)T2Handle);
  95. return Result;
  96. }
  97. // Attempt to retrieve the build number
  98. T2SetBuildNumber(T2Handle);
  99. // Set the default words per minute
  100. T2SetDefaultWPM(Connection, T2_DEFAULT_WORDS_PER_MIN);
  101. // Set the default latency
  102. T2SetLatency(Connection, T2_DEFAULT_LATENCY);
  103. // Give the user the handle
  104. *Connection = (HANDLE)T2Handle;
  105. }
  106. __except (EXCEPTION_EXECUTE_HANDLER) {
  107. _ASSERT(FALSE);
  108. return "Connection error";
  109. }
  110. return NULL;
  111. }
  112. // T2ConnectEx
  113. //
  114. // This is a wrapper for TCLIENT's SCConnectEx function. It does some
  115. // additional stuff however - it allocates a handle local to
  116. // TCLIENT2, sets default data such as Words Per Minute, and even
  117. // attempts to get the build number immediately after the connection.
  118. //
  119. // Returns NULL on success, or a string explaining the error
  120. // on failure.
  121. TSAPI LPCSTR T2ConnectEx(LPCWSTR Server, LPCWSTR User, LPCWSTR Pass,
  122. LPCWSTR Domain, LPCWSTR Shell, INT xRes, INT yRes,
  123. INT Flags, INT BPP, INT AudioFlags, HANDLE *Connection)
  124. {
  125. __try {
  126. LPCSTR Result = NULL;
  127. // Create a new connection handle
  128. TSAPIHANDLE *T2Handle = T2CreateHandle();
  129. if (T2Handle == NULL)
  130. return "Could not allocate an API handle";
  131. // Connect
  132. Result = SCConnectEx(Server, User, Pass, Domain, Shell, xRes,
  133. yRes, Flags, BPP, AudioFlags, &(T2Handle->SCConnection));
  134. if (Result != NULL) {
  135. // Connection failed, destroy handle and return
  136. T2DestroyHandle((HANDLE)T2Handle);
  137. return Result;
  138. }
  139. // Attempt to retrieve the build number
  140. T2SetBuildNumber(T2Handle);
  141. // Set the default words per minute
  142. T2SetDefaultWPM(Connection, T2_DEFAULT_WORDS_PER_MIN);
  143. // Set the default latency
  144. T2SetLatency(Connection, T2_DEFAULT_LATENCY);
  145. *Connection = (HANDLE)T2Handle;
  146. }
  147. __except (EXCEPTION_EXECUTE_HANDLER) {
  148. _ASSERT(FALSE);
  149. return "Connection error";
  150. }
  151. return NULL;
  152. }
  153. // T2Disconnect
  154. //
  155. // This is a wrapper for TCLIENT's SCDisconnect function.
  156. //
  157. // Returns NULL on success, or a string explaining the error
  158. // on failure.
  159. TSAPI LPCSTR T2Disconnect(HANDLE Connection)
  160. {
  161. LPCSTR Result = NULL;
  162. // Validate the handle
  163. if (T2IsHandle(Connection) == FALSE)
  164. return "Invalid connection handle";
  165. // Do the TCLIENT action
  166. Result = SCDisconnect(SCCONN(Connection));
  167. // If we got here (regardless if SCDisconnect failed or not)
  168. // we have an allocated object that needs to be freed.
  169. T2DestroyHandle(Connection);
  170. // Return the result of the TCLIENT action
  171. return Result;
  172. }
  173. // T2FreeMem
  174. //
  175. // This is a wrapper for TCLIENT's SCFreeMem function.
  176. //
  177. // No return value.
  178. TSAPI VOID T2FreeMem(PVOID Mem)
  179. {
  180. SCFreeMem(Mem);
  181. }
  182. // T2GetBuildNumber
  183. //
  184. // This sets the DWORD to the build number that was (if) detected
  185. // upon connection. If the build number was not detected, 0 (zero)
  186. // will be the value.
  187. //
  188. // Returns NULL on success, or a string explaining the error
  189. // on failure.
  190. TSAPI LPCSTR T2GetBuildNumber(HANDLE Connection, DWORD *BuildNumber)
  191. {
  192. // Validate the handle
  193. if (T2IsHandle(Connection) == FALSE)
  194. return "Invalid connection handle";
  195. __try {
  196. // Attempt to set a value at the specified pointer
  197. *BuildNumber = ((TSAPIHANDLE *)Connection)->BuildNumber;
  198. }
  199. __except (EXCEPTION_EXECUTE_HANDLER) {
  200. // Nope, it failed.
  201. _ASSERT(FALSE);
  202. return "Invalid BuildNumber pointer";
  203. }
  204. return NULL;
  205. }
  206. // T2GetFeedback
  207. //
  208. // This is a wrapper for TCLIENT's SCGetFeedback function.
  209. //
  210. // Returns NULL on success, or a string explaining the error
  211. // on failure.
  212. TSAPI LPCSTR T2GetFeedback(HANDLE Connection, LPWSTR *Buffers, UINT *Count, UINT *MaxStrLen)
  213. {
  214. // Validate the handle
  215. if (T2IsHandle(Connection) == FALSE)
  216. return "Invalid connection handle";
  217. // Do the TCLIENT action
  218. return SCGetFeedback(((TSAPIHANDLE *)Connection)->SCConnection,
  219. Buffers, Count, MaxStrLen);
  220. }
  221. // T2GetParam
  222. //
  223. // This will get the value pointed to by lParam
  224. // the "user-defined" value set using T2SetParam. It can
  225. // be used for callbacks and other application purposes.
  226. //
  227. // Returns NULL on success, or a string explaining the error
  228. // on failure.
  229. TSAPI LPCSTR T2GetParam(HANDLE Connection, LPARAM *lParam)
  230. {
  231. // Validate the handle
  232. if (T2IsHandle(Connection) == FALSE)
  233. return "Invalid connection handle";
  234. __try {
  235. // Attempt to set a value at the specified pointer
  236. *lParam = ((TSAPIHANDLE *)Connection)->lParam;
  237. }
  238. __except (EXCEPTION_EXECUTE_HANDLER) {
  239. // Nope, it failed.
  240. _ASSERT(FALSE);
  241. return "Invalid lParam pointer";
  242. }
  243. return NULL;
  244. }
  245. // T2GetLatency
  246. //
  247. // On multi-input commands, such as "T2KeyAlt" which presses
  248. // several keys to accomplish its goal, a latency value is used
  249. // to slow the presses down so ALT-F is not pressed so fast
  250. // it becomes unrealistic. The default value is T2_DEFAULT_LATENCY
  251. // or you can retrieve its current value using this function.
  252. // To change the value, use the T2SetLatency function.
  253. //
  254. // Returns NULL on success, or a string explaining the error
  255. // on failure.
  256. TSAPI LPCSTR T2GetLatency(HANDLE Connection, DWORD *Latency)
  257. {
  258. // Validate the handle
  259. if (T2IsHandle(Connection) == FALSE)
  260. return "Invalid connection handle";
  261. __try {
  262. // Attempt to set a value at the specified pointer
  263. *Latency = ((TSAPIHANDLE *)Connection)->Latency;
  264. }
  265. __except (EXCEPTION_EXECUTE_HANDLER) {
  266. // Nope, it failed.
  267. _ASSERT(FALSE);
  268. return "Invalid Latency pointer";
  269. }
  270. return NULL;
  271. }
  272. // T2GetSessionId
  273. //
  274. // This is a wrapper for TCLIENT's SCGetSessionId function.
  275. //
  276. // Returns the session id on success, or 0 on failure.
  277. TSAPI UINT T2GetSessionId(HANDLE Connection)
  278. {
  279. // Validate the handle
  280. if (T2IsHandle(Connection) == FALSE)
  281. return 0;
  282. // Do the TCLIENT action
  283. return SCGetSessionId(SCCONN(Connection));
  284. }
  285. // T2Init
  286. //
  287. // This is a wrapper for TCLIENT's SCInit function. However this
  288. // function does an additional internal item: Record the callback
  289. // routines.
  290. //
  291. // No return value.
  292. TSAPI VOID T2Init(SCINITDATA *InitData, PFNIDLEMESSAGE IdleCallback)
  293. {
  294. __try {
  295. // If we have a valid structure, grab its data to wrap it
  296. if (InitData != NULL)
  297. // Create the timer which will monitor idles in the script
  298. T2CreateTimerThread(InitData->pfnPrintMessage, IdleCallback);
  299. // Initialize TCLIENT
  300. SCInit(InitData);
  301. }
  302. __except (EXCEPTION_EXECUTE_HANDLER) {
  303. _ASSERT(FALSE);
  304. return;
  305. }
  306. }
  307. // T2IsDead
  308. //
  309. // This is a wrapper for TCLIENT's SCIsDead function.
  310. //
  311. // Returns TRUE if the connection is invalid, or does not
  312. // contain a valid connection. Otherwise FALSE is returned.
  313. TSAPI BOOL T2IsDead(HANDLE Connection)
  314. {
  315. // Validate the handle
  316. if (T2IsHandle(Connection) == FALSE)
  317. return TRUE;
  318. // Do the TCLIENT action
  319. return SCIsDead(SCCONN(Connection));
  320. }
  321. // T2IsHandle
  322. //
  323. // This function checks a handle for validity.
  324. //
  325. // Returns TRUE if the connection handle is a valid handle.
  326. // This differs to T2IsDead by it only verifies the memory
  327. // location, it does not check the connection status.
  328. TSAPI BOOL T2IsHandle(HANDLE Connection)
  329. {
  330. TSAPIHANDLE *APIHandle = (TSAPIHANDLE *)Connection;
  331. // Use exception handling in case memory has been freed
  332. __try
  333. {
  334. // Simply reference the first and last members for validity
  335. if (APIHandle->SCConnection == NULL &&
  336. APIHandle->Latency == 0)
  337. return FALSE;
  338. }
  339. // If we tried to reference an invalid pointer, we will get here
  340. __except (EXCEPTION_EXECUTE_HANDLER)
  341. {
  342. return FALSE;
  343. }
  344. // Everything worked ok, tell the user
  345. return TRUE;
  346. }
  347. // T2Logoff
  348. //
  349. // This is a wrapper for TCLIENT's SCLogoff function. Additionally, if
  350. // the logoff completed successfully, the connection handle is destroyed.
  351. //
  352. // Returns NULL on success, or a string explaining the error
  353. // on failure.
  354. TSAPI LPCSTR T2Logoff(HANDLE Connection)
  355. {
  356. LPCSTR Result;
  357. // Validate the handle
  358. if (T2IsHandle(Connection) == FALSE)
  359. return "Invalid connection handle";
  360. // Do the TCLIENT action
  361. Result = SCLogoff(SCCONN(Connection));
  362. // If the logoff completed, release and destroy the handle.
  363. if (Result == NULL)
  364. T2DestroyHandle(Connection);
  365. return Result;
  366. }
  367. // T2OpenClipboard
  368. //
  369. // This is a wrapper for TCLIENT's SCOpenClipboard function.
  370. //
  371. // Returns TRUE if the operation completed successfully,
  372. // otherwise FALSE is returned.
  373. TSAPI BOOL T2OpenClipboard(HWND Window)
  374. {
  375. return SCOpenClipboard(Window);
  376. }
  377. // T2PauseInput
  378. //
  379. // This routine sets or unsets an event which makes all
  380. // "input" functions pause or unpause. This can only be used
  381. // in multithreaded applications. When "Enable" is TRUE,
  382. // the function will pause all input - meaning all keyboard
  383. // messages will not return until T2PauseInput is called again
  384. // with "Enable" as FALSE. This allows the opportunity to
  385. // pause a script during execution without losing its place.
  386. //
  387. // Returns NULL on success, or a string explaining the error
  388. // on failure.
  389. TSAPI LPCSTR T2PauseInput(HANDLE Connection, BOOL Enable)
  390. {
  391. TSAPIHANDLE *Handle;
  392. // Validate the handle
  393. if (T2IsHandle(Connection) == FALSE)
  394. return "Invalid connection handle";
  395. // Cast the HANDLE over to an internal structure
  396. Handle = (TSAPIHANDLE *)Connection;
  397. // Validate the event handle
  398. if (Handle->PauseEvent == NULL) {
  399. _ASSERT(FALSE);
  400. return "Invalid pause event handle";
  401. }
  402. // Disable Pause
  403. if (Enable == FALSE)
  404. SetEvent(Handle->PauseEvent);
  405. // Enable Pause
  406. else
  407. ResetEvent(Handle->PauseEvent);
  408. // Success
  409. return NULL;
  410. }
  411. // T2SaveClipboard
  412. //
  413. // This is a wrapper for TCLIENT's SCSaveClipboard function.
  414. //
  415. // Returns NULL on success, or a string explaining the error
  416. // on failure.
  417. TSAPI LPCSTR T2SaveClipboard(HANDLE Connection,
  418. LPCSTR FormatName, LPCSTR FileName)
  419. {
  420. // Validate the handle
  421. if (T2IsHandle(Connection) == FALSE)
  422. return "Invalid connection handle";
  423. // Do the TCLIENT action
  424. return SCSaveClipboard(SCCONN(Connection), FormatName, FileName);
  425. }
  426. // T2SendData
  427. //
  428. // This is a wrapper for TCLIENT's SCSenddata function.
  429. //
  430. // Returns NULL on success, or a string explaining the error
  431. // on failure.
  432. TSAPI LPCSTR T2SendData(HANDLE Connection,
  433. UINT Message, WPARAM wParam, LPARAM lParam)
  434. {
  435. // Validate the handle
  436. if (T2IsHandle(Connection) == FALSE)
  437. return "Invalid connection handle";
  438. // This is an input call, ensure we are not paused first
  439. T2WaitForPauseInput(Connection);
  440. // Do the TCLIENT action
  441. return SCSenddata(SCCONN(Connection), Message, wParam, lParam);
  442. }
  443. // T2SendMouseClick
  444. //
  445. // This is a wrapper for TCLIENT's SCSendMouseClick function.
  446. //
  447. // Returns NULL on success, or a string explaining the error
  448. // on failure.
  449. TSAPI LPCSTR T2SendMouseClick(HANDLE Connection, UINT xPos, UINT yPos)
  450. {
  451. // Validate the handle
  452. if (T2IsHandle(Connection) == FALSE)
  453. return "Invalid connection handle";
  454. // This is an input call, ensure we are not paused first
  455. T2WaitForPauseInput(Connection);
  456. // Do the TCLIENT action
  457. return SCSendMouseClick(SCCONN(Connection), xPos, yPos);
  458. }
  459. // T2SendText
  460. //
  461. // This is a wrapper for TCLIENT's SCSendtextAsMsgs function.
  462. //
  463. // Returns NULL on success, or a string explaining the error
  464. // on failure.
  465. TSAPI LPCSTR T2SendText(HANDLE Connection, LPCWSTR String)
  466. {
  467. // Validate the handle
  468. if (T2IsHandle(Connection) == FALSE)
  469. return "Invalid connection handle";
  470. // This is an input call, ensure we are not paused first
  471. T2WaitForPauseInput(Connection);
  472. // Do the TCLIENT action
  473. return SCSendtextAsMsgs(SCCONN(Connection), String);
  474. }
  475. // T2SetClientTopmost
  476. //
  477. // This is a wrapper for TCLIENT's SCSetClientTopmost function.
  478. //
  479. // Returns NULL on success, or a string explaining the error
  480. // on failure.
  481. TSAPI LPCSTR T2SetClientTopmost(HANDLE Connection, LPCWSTR Param)
  482. {
  483. // Validate the handle
  484. if (T2IsHandle(Connection) == FALSE)
  485. return "Invalid connection handle";
  486. // Do the TCLIENT action
  487. return SCSetClientTopmost(SCCONN(Connection), Param);
  488. }
  489. // T2SetParam
  490. //
  491. // This changes the user-defined parameter for the specified
  492. // connection which can be retrieved using the T2GetParam function.
  493. //
  494. // Returns NULL on success, or a string explaining the error
  495. // on failure.
  496. TSAPI LPCSTR T2SetParam(HANDLE Connection, LPARAM lParam)
  497. {
  498. // Validate the handle
  499. if (T2IsHandle(Connection) == FALSE)
  500. return "Invalid connection handle";
  501. // Set the parameter
  502. ((TSAPIHANDLE *)Connection)->lParam = lParam;
  503. return NULL;
  504. }
  505. // T2SetLatency
  506. //
  507. // On multi-input commands, such as "T2KeyAlt" which presses
  508. // several keys to accomplish its goal, a latency value is used
  509. // to slow the presses down so ALT-F is not pressed so fast
  510. // it becomes unrealistic. The default value is T2_DEFAULT_LATENCY
  511. // and you can use this function to change its value.
  512. //
  513. // Returns NULL on success, or a string explaining the error
  514. // on failure.
  515. TSAPI LPCSTR T2SetLatency(HANDLE Connection, DWORD Latency)
  516. {
  517. // Validate the handle
  518. if (T2IsHandle(Connection) == FALSE)
  519. return "Invalid connection handle";
  520. // Set the latency
  521. ((TSAPIHANDLE *)Connection)->Latency = Latency;
  522. return NULL;
  523. }
  524. // T2Start
  525. //
  526. // This is a wrapper for TCLIENT's SCStart function.
  527. //
  528. // Returns NULL on success, or a string explaining the error
  529. // on failure.
  530. TSAPI LPCSTR T2Start(HANDLE Connection, LPCWSTR AppName)
  531. {
  532. // Validate the handle
  533. if (T2IsHandle(Connection) == FALSE)
  534. return "Invalid connection handle";
  535. // This is an input call, ensure we are not paused first
  536. T2WaitForPauseInput(Connection);
  537. // Do the TCLIENT action
  538. return SCStart(SCCONN(Connection), AppName);
  539. }
  540. // T2SwitchToProcess
  541. //
  542. // This is a wrapper for TCLIENT's SCSwitchToProcess function.
  543. // The TCLIENT2 extension to this function is that it ignores spaces,
  544. // the old version you HAD to pass in "MyComputer" instead of
  545. // "My Computer" to have it work.
  546. //
  547. // Returns NULL on success, or a string explaining the error
  548. // on failure.
  549. TSAPI LPCSTR T2SwitchToProcess(HANDLE Connection, LPCWSTR Param)
  550. {
  551. WCHAR CompatibleStr[1024] = { 0 };
  552. // Validate the handle
  553. if (T2IsHandle(Connection) == FALSE)
  554. return "Invalid connection handle";
  555. // This is an input call, ensure we are not paused first
  556. T2WaitForPauseInput(Connection);
  557. // Copy the string (without spaces) to a temporary buffer
  558. if (T2CopyStringWithoutSpaces(CompatibleStr, Param) == 0)
  559. return "Invalid process name";
  560. // Do the TCLIENT action
  561. return SCSwitchToProcess(SCCONN(Connection), CompatibleStr);
  562. }
  563. // T2WaitForText
  564. //
  565. // This is a wrapper for TCLIENT's SCSwitchToProcess function.
  566. // The TCLIENT2 extension to this function is that it ignores spaces,
  567. // the old version you HAD to pass in "MyComputer" instead of
  568. // "My Computer" to have it work.
  569. //
  570. // Note: The Timeout value is in milliseconds.
  571. //
  572. // Returns NULL on success, or a string explaining the error
  573. // on failure.
  574. TSAPI LPCSTR T2WaitForText(HANDLE Connection, LPCWSTR String, INT Timeout)
  575. {
  576. LPCSTR Result;
  577. WCHAR CompatibleStr[1024];
  578. WCHAR *CStrPtr = CompatibleStr;
  579. // Validate the handle
  580. if (T2IsHandle(Connection) == FALSE)
  581. return "Invalid connection handle";
  582. // Sanity checks always come first
  583. if (String == NULL || *String == 0)
  584. return "No text to wait for";
  585. // If timeout is infinite, convert that value over to TCLIENT terms.
  586. if (Timeout == T2INFINITE)
  587. Timeout = WAIT_STRING_TIMEOUT;
  588. // Now copy the string without spaces over to our stack buffer.
  589. CStrPtr += (T2CopyStringWithoutSpaces(CompatibleStr, String) - 1);
  590. // At the end of the new buffer, append the TCLIENT compatible version
  591. // of a timeout indicator.
  592. T2AddTimeoutToString(CStrPtr, Timeout);
  593. // Begin a timer for our callback routines to indicate idles
  594. T2StartTimer(Connection, String);
  595. // Now wait for the string
  596. Result = T2Check(Connection, "Wait4StrTimeout", CompatibleStr);
  597. // Wait4StrTimeout returned, so the text was either found or the
  598. // function failed.. in any case, stop the timer.
  599. T2StopTimer(Connection);
  600. // Return the result back to the user
  601. return Result;
  602. }
  603. // T2WaitForMultiple
  604. //
  605. // Like T2WaitForText, this function waits for a string. What is different
  606. // about this function is that it will wait for any number of strings.
  607. // For example, if you pass in the strings "My Computer" and "Recycle Bin",
  608. // the function will return when EITHER has been found - NOT BOTH. The
  609. // only way to indicate "return only when both has been found" is to call
  610. // T2WaitForText multiple times.
  611. //
  612. // The Strings parameter is an array of pointers to a string, and the last
  613. // pointer must point to a NULL value or an empty string. Example:
  614. //
  615. // WCHAR *StrArray = {
  616. // "Str1",
  617. // "Str2",
  618. // NULL
  619. // };
  620. //
  621. // Note: The Timeout value is in milliseconds.
  622. //
  623. // Returns NULL on success, or a string explaining the error
  624. // on failure.
  625. TSAPI LPCSTR T2WaitForMultiple(HANDLE Connection,
  626. LPCWSTR *Strings, INT Timeout)
  627. {
  628. LPCSTR Result;
  629. WCHAR CompatibleStr[1024] = { 0 };
  630. WCHAR *CStrPtr = CompatibleStr;
  631. // Validate the handle
  632. if (T2IsHandle(Connection) == FALSE)
  633. return "Invalid connection handle";
  634. // Sanity checks always come first
  635. if (Strings == NULL || *Strings == NULL)
  636. return "No text to wait for";
  637. // If timeout is infinite, convert that value over to TCLIENT terms.
  638. if (Timeout == T2INFINITE)
  639. Timeout = WAIT_STRING_TIMEOUT;
  640. // Next step is to convert our nice string array to a TCLIENT
  641. // version of "multiple strings".
  642. CStrPtr += (T2MakeMultipleString(CompatibleStr, Strings) - 1);
  643. // Validate our result by checking the first character
  644. if (*CompatibleStr == L'\0')
  645. return "No text to wait for";
  646. // At the end of the new buffer, append the TCLIENT compatible version
  647. // of a timeout indicator.
  648. T2AddTimeoutToString(CStrPtr, Timeout);
  649. // Begin a timer for our callback routines to indicate idles
  650. T2StartTimer(Connection, *Strings);
  651. // Now wait for the string
  652. Result = T2Check(Connection, "Wait4MultipleStrTimeout", CompatibleStr);
  653. // Wait4StrTimeout returned, so the text was either found or the
  654. // function failed.. in any case, stop the timer.
  655. T2StopTimer(Connection);
  656. // Return the result back to the user
  657. return Result;
  658. }
  659. // If the DLL is unloaded unsafely, this closes some handles.
  660. BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved)
  661. {
  662. // Only call during an "unload"
  663. if (fdwReason == DLL_PROCESS_DETACH)
  664. T2DestroyTimerThread();
  665. return TRUE;
  666. }