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.

883 lines
21 KiB

  1. /*++
  2. Copyright (C) Microsoft Corporation, 1994 - 1999
  3. Module Name:
  4. Client.c
  5. Abstract:
  6. Client side of basic RPC performance test.
  7. Author:
  8. Mario Goertzel (mariogo) 31-Mar-1994
  9. Revision History:
  10. --*/
  11. #include <rpcperf.h>
  12. #include <rpcrt.h>
  13. #ifdef MAC
  14. extern void _cdecl PrintToConsole(const char *lpszFormat, ...) ;
  15. extern unsigned long ulSecurityPackage ;
  16. #else
  17. #define PrintToConsole printf
  18. unsigned long ulSecurityPackage = RPC_C_AUTHN_WINNT ;
  19. #endif
  20. // Usage
  21. const char *USAGE = "-n <threads> -a <authnlevel> -s <server> -t <protseq> -w <wait_method>\n"
  22. "Server controls iterations, test cases, and compiles the results.\n"
  23. "AuthnLevel: none, connect, call, pkt, integrity, privacy.\n"
  24. "Default threads=1, authnlevel=none\n";
  25. #define CHECK_RET(status, string) if (status)\
  26. { PrintToConsole("%s failed -- %lu (0x%08X)\n", string,\
  27. (unsigned long)status, (unsigned long)status);\
  28. return (status); \
  29. }
  30. #ifdef WIN98
  31. RPC_DISPATCH_TABLE DummyDispatchTable =
  32. {
  33. 1, NULL
  34. };
  35. RPC_SERVER_INTERFACE DummyInterfaceInformation =
  36. {
  37. sizeof(RPC_SERVER_INTERFACE),
  38. {{1,2,2,{3,3,3,3,3,3,3,3}},
  39. {1,1}},
  40. {{1,2,2,{3,3,3,3,3,3,3,3}},
  41. {0,0}},
  42. &DummyDispatchTable,
  43. 0,
  44. NULL,
  45. NULL,
  46. NULL,
  47. 0
  48. };
  49. #endif
  50. RPC_STATUS DoRpcBindingSetAuthInfo(handle_t Binding)
  51. {
  52. if (AuthnLevel != RPC_C_AUTHN_LEVEL_NONE)
  53. return RpcBindingSetAuthInfo(Binding,
  54. NULL,
  55. AuthnLevel,
  56. ulSecurityPackage,
  57. NULL,
  58. RPC_C_AUTHZ_NONE);
  59. else
  60. return(RPC_S_OK);
  61. }
  62. //
  63. // Test wrappers
  64. //
  65. unsigned long DoNullCall(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  66. {
  67. StartTime();
  68. while(i--)
  69. NullCall(*b);
  70. return (FinishTiming());
  71. }
  72. unsigned long DoNICall(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  73. {
  74. StartTime();
  75. while(i--)
  76. NICall(*b);
  77. return (FinishTiming());
  78. }
  79. unsigned long DoWrite1K(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  80. {
  81. StartTime();
  82. while(i--)
  83. Write1K(*b,p);
  84. return (FinishTiming());
  85. }
  86. unsigned long DoRead1K(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  87. {
  88. StartTime();
  89. while(i--)
  90. Read1K(*b,p);
  91. return (FinishTiming());
  92. }
  93. unsigned long DoWrite4K(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  94. {
  95. StartTime();
  96. while(i--)
  97. Write4K(*b,p);
  98. return (FinishTiming());
  99. }
  100. unsigned long DoRead4K(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  101. {
  102. StartTime();
  103. while(i--)
  104. Read4K(*b,p);
  105. return (FinishTiming());
  106. }
  107. unsigned long DoWrite32K(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  108. {
  109. StartTime();
  110. while(i--)
  111. Write32K(*b,p);
  112. return (FinishTiming());
  113. }
  114. unsigned long DoRead32K(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  115. {
  116. StartTime();
  117. while(i--)
  118. Read32K(*b,p);
  119. return (FinishTiming());
  120. }
  121. unsigned long DoContextNullCall(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  122. {
  123. unsigned long Time;
  124. PERF_CONTEXT pContext = OpenContext(*b);
  125. StartTime();
  126. while(i--)
  127. ContextNullCall(pContext);
  128. Time = FinishTiming();
  129. CloseContext(&pContext);
  130. return (Time);
  131. }
  132. unsigned long DoFixedBinding(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  133. {
  134. unsigned long status;
  135. unsigned long Time;
  136. char *stringBinding;
  137. char *ep = GetFixedEp(*b);
  138. handle_t binding;
  139. RpcBindingFree(b);
  140. RpcStringBindingCompose(0,
  141. Protseq,
  142. NetworkAddr,
  143. ep,
  144. 0,
  145. &stringBinding);
  146. MIDL_user_free(ep);
  147. StartTime();
  148. while(i--)
  149. {
  150. RpcBindingFromStringBinding(stringBinding, &binding);
  151. status = DoRpcBindingSetAuthInfo(binding);
  152. CHECK_RET(status, "RpcBindingSetAuthInfo");
  153. NullCall(binding);
  154. RpcBindingFree(&binding);
  155. }
  156. Time = FinishTiming();
  157. //
  158. // Restore binding for the rest of the test.
  159. //
  160. RpcBindingFromStringBinding(stringBinding, b);
  161. NullCall(*b);
  162. NullCall(*b);
  163. RpcStringFree(&stringBinding);
  164. return (Time);
  165. }
  166. unsigned long DoReBinding(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  167. {
  168. unsigned long status;
  169. unsigned long Time;
  170. char *stringBinding;
  171. char *ep = GetFixedEp(*b);
  172. handle_t binding;
  173. RpcStringBindingCompose(0,
  174. Protseq,
  175. NetworkAddr,
  176. ep,
  177. 0,
  178. &stringBinding);
  179. MIDL_user_free(ep);
  180. StartTime();
  181. while(i--)
  182. {
  183. RpcBindingFromStringBinding(stringBinding, &binding);
  184. status = DoRpcBindingSetAuthInfo(binding);
  185. CHECK_RET(status, "RpcBindingSetAuthInfo");
  186. NullCall(binding);
  187. RpcBindingFree(&binding);
  188. }
  189. Time = FinishTiming();
  190. RpcStringFree(&stringBinding);
  191. return (Time);
  192. }
  193. unsigned long DoDynamicBinding(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  194. {
  195. unsigned long status;
  196. unsigned long Time;
  197. char *stringBinding;
  198. handle_t binding;
  199. RpcBindingFree(b);
  200. RpcStringBindingCompose(0,
  201. Protseq,
  202. NetworkAddr,
  203. 0,
  204. 0,
  205. &stringBinding);
  206. StartTime();
  207. while(i--)
  208. {
  209. RpcBindingFromStringBinding(stringBinding, &binding);
  210. status = DoRpcBindingSetAuthInfo(binding);
  211. CHECK_RET(status, "RpcBindingSetAuthInfo");
  212. NullCall(binding);
  213. RpcBindingFree(&binding);
  214. }
  215. Time = FinishTiming();
  216. //
  217. // Restore binding for test to use.
  218. //
  219. RpcBindingFromStringBinding(stringBinding, b);
  220. NullCall(*b);
  221. NullCall(*b);
  222. RpcStringFree(&stringBinding);
  223. return (Time);
  224. }
  225. void AsyncProc(IN PRPC_ASYNC_STATE pAsync, IN void *context, IN RPC_ASYNC_EVENT asyncEvent)
  226. {
  227. // no-op
  228. }
  229. void AsyncCallbackProc(IN PRPC_ASYNC_STATE pAsync, IN void *context, IN RPC_ASYNC_EVENT asyncEvent)
  230. {
  231. // wake up our thread
  232. // in hThread we actually keep an event
  233. HANDLE hEvent = pAsync->u.APC.hThread;
  234. SetEvent(hEvent);
  235. }
  236. void NotifyProc(IN PRPC_ASYNC_STATE pAsync, IN void *context, IN RPC_ASYNC_EVENT asyncEvent)
  237. {
  238. // no-op
  239. }
  240. unsigned long DoAsyncNullCallWithEvent(IN PRPC_ASYNC_STATE pAsync, handle_t __RPC_FAR * b,
  241. long i, char __RPC_FAR *p)
  242. {
  243. HANDLE hEvent = pAsync->u.hEvent;
  244. RPC_STATUS RpcStatus;
  245. StartTime();
  246. while(i--)
  247. {
  248. RpcStatus = RpcAsyncInitializeHandle(pAsync, RPC_ASYNC_VERSION_1_0);
  249. if (RpcStatus != RPC_S_OK)
  250. {
  251. printf("RpcAsyncInitializeHandle failed: %ld\n", GetLastError());
  252. return 0;
  253. }
  254. pAsync->NotificationType = RpcNotificationTypeEvent;
  255. pAsync->u.hEvent = hEvent;
  256. AsyncNullCall(pAsync, *b);
  257. WaitForSingleObject(hEvent, INFINITE);
  258. RpcAsyncCompleteCall(pAsync, NULL);
  259. }
  260. return (FinishTiming());
  261. }
  262. unsigned long DoAsyncNullCallWithApc(IN PRPC_ASYNC_STATE pAsync, handle_t __RPC_FAR * b,
  263. long i, char __RPC_FAR *p)
  264. {
  265. RPC_STATUS RpcStatus;
  266. StartTime();
  267. while(i--)
  268. {
  269. RpcStatus = RpcAsyncInitializeHandle(pAsync, RPC_ASYNC_VERSION_1_0);
  270. if (RpcStatus != RPC_S_OK)
  271. {
  272. printf("RpcAsyncInitializeHandle failed: %ld\n", GetLastError());
  273. return 0;
  274. }
  275. pAsync->NotificationType = RpcNotificationTypeApc;
  276. pAsync->u.APC.NotificationRoutine = AsyncProc;
  277. pAsync->u.APC.hThread = 0;
  278. AsyncNullCall(pAsync, *b);
  279. SleepEx(INFINITE, TRUE);
  280. RpcAsyncCompleteCall(pAsync, NULL);
  281. }
  282. return (FinishTiming());
  283. }
  284. unsigned long DoAsyncNullCallWithCallback(IN PRPC_ASYNC_STATE pAsync, handle_t __RPC_FAR * b,
  285. long i, char __RPC_FAR *p)
  286. {
  287. RPC_STATUS RpcStatus;
  288. HANDLE hEvent;
  289. unsigned long nTiming;
  290. hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  291. if (!hEvent)
  292. {
  293. printf("CreateEvent failed: %ld\n", GetLastError());
  294. return 0;
  295. }
  296. #ifdef WIN98
  297. // under Win9x, the client needs a running server to use callback
  298. // notifications. This assumption is Ok, since only OLE uses
  299. // callbacks, and in OLE every process is both client and server
  300. RpcStatus = RpcServerUseProtseqEp("ncalrpc", 1, "dummy", NULL);
  301. if (RpcStatus != RPC_S_OK)
  302. return 0;
  303. RpcStatus = RpcServerRegisterIf(&DummyInterfaceInformation, NULL, NULL);
  304. if (RpcStatus != RPC_S_OK)
  305. return 0;
  306. RpcStatus = RpcServerListen(1, 2, TRUE);
  307. if (RpcStatus != RPC_S_OK)
  308. return 0;
  309. #endif
  310. StartTime();
  311. while(i--)
  312. {
  313. RpcStatus = RpcAsyncInitializeHandle(pAsync, RPC_ASYNC_VERSION_1_0);
  314. if (RpcStatus != RPC_S_OK)
  315. {
  316. printf("RpcAsyncInitializeHandle failed: %ld\n", GetLastError());
  317. return 0;
  318. }
  319. pAsync->NotificationType = RpcNotificationTypeCallback;
  320. pAsync->u.NotificationRoutine = AsyncCallbackProc;
  321. // just a little bit of a hack. We know that the we cannot use
  322. // the hEvent data member of u as we wished, because it occupies
  323. // the same memory location as the callback routine, so we
  324. // use the APC.hThread member which occupies the same DWORD
  325. pAsync->u.APC.hThread = hEvent;
  326. AsyncNullCall(pAsync, *b);
  327. WaitForSingleObject(hEvent, INFINITE);
  328. RpcAsyncCompleteCall(pAsync, NULL);
  329. }
  330. CloseHandle(hEvent);
  331. nTiming = FinishTiming();
  332. #ifdef WIN98
  333. RpcStatus = RpcMgmtStopServerListening(NULL);
  334. if (RpcStatus == RPC_S_OK)
  335. {
  336. RpcMgmtWaitServerListen();
  337. }
  338. #endif
  339. return nTiming;
  340. }
  341. unsigned long DoAsyncNullCallWithNone(IN PRPC_ASYNC_STATE pAsync, handle_t __RPC_FAR * b,
  342. long i, char __RPC_FAR *p)
  343. {
  344. RPC_STATUS RpcStatus;
  345. StartTime();
  346. while(i--)
  347. {
  348. RpcStatus = RpcAsyncInitializeHandle(pAsync, RPC_ASYNC_VERSION_1_0);
  349. if (RpcStatus != RPC_S_OK)
  350. {
  351. printf("RpcAsyncInitializeHandle failed: %ld\n", GetLastError());
  352. return 0;
  353. }
  354. pAsync->NotificationType = RpcNotificationTypeNone;
  355. AsyncNullCall(pAsync, *b);
  356. // make sure we catch even the slightest variations in performance. Loop all the time ...
  357. while (RpcAsyncGetCallStatus(pAsync) == RPC_S_ASYNC_CALL_PENDING)
  358. ;
  359. RpcAsyncCompleteCall(pAsync, NULL);
  360. }
  361. return (FinishTiming());
  362. }
  363. unsigned long DoAsyncNullCallWithHwnd(IN PRPC_ASYNC_STATE pAsync, handle_t __RPC_FAR * b,
  364. long i, char __RPC_FAR *p)
  365. {
  366. RPC_STATUS RpcStatus;
  367. HWND hWnd = pAsync->u.HWND.hWnd;
  368. StartTime();
  369. while(i--)
  370. {
  371. RpcStatus = RpcAsyncInitializeHandle(pAsync, RPC_ASYNC_VERSION_1_0);
  372. if (RpcStatus != RPC_S_OK)
  373. {
  374. printf("RpcAsyncInitializeHandle failed: %ld\n", GetLastError());
  375. return 0;
  376. }
  377. pAsync->NotificationType = RpcNotificationTypeHwnd;
  378. pAsync->u.HWND.hWnd = hWnd;
  379. pAsync->u.HWND.Msg = PERF_TEST_NOTIFY;
  380. AsyncNullCall(pAsync, *b);
  381. PumpMessage();
  382. RpcAsyncCompleteCall(pAsync, NULL);
  383. }
  384. return (FinishTiming());
  385. }
  386. unsigned long DoAsyncNullCall(handle_t __RPC_FAR * b, long i, char __RPC_FAR *p)
  387. {
  388. RPC_ASYNC_STATE asyncState;
  389. RPC_STATUS RpcStatus;
  390. BOOL fCallComplete = FALSE;
  391. RpcStatus = RpcAsyncInitializeHandle(&asyncState, RPC_ASYNC_VERSION_1_0);
  392. if (RpcStatus != RPC_S_OK)
  393. {
  394. printf("RpcAsyncInitializeHandle failed: %ld\n", GetLastError());
  395. return 0;
  396. }
  397. asyncState.NotificationType = NotificationType;
  398. switch (NotificationType)
  399. {
  400. case RpcNotificationTypeEvent:
  401. asyncState.u.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
  402. if (asyncState.u.hEvent == NULL)
  403. {
  404. printf("CreateEvent failed: %ld\n", GetLastError());
  405. return 0;
  406. }
  407. return DoAsyncNullCallWithEvent(&asyncState, b, i, p);
  408. case RpcNotificationTypeApc:
  409. return DoAsyncNullCallWithApc(&asyncState, b, i, p);
  410. case RpcNotificationTypeNone:
  411. return DoAsyncNullCallWithNone(&asyncState, b, i, p);
  412. case RpcNotificationTypeHwnd:
  413. asyncState.u.HWND.hWnd = CreateSTAWindow("Noname");
  414. if (asyncState.u.HWND.hWnd == NULL)
  415. {
  416. printf("CreateEvent failed: %ld\n", GetLastError());
  417. return 0;
  418. }
  419. asyncState.u.HWND.Msg = PERF_TEST_NOTIFY;
  420. return DoAsyncNullCallWithHwnd(&asyncState, b, i, p);
  421. case RpcNotificationTypeCallback:
  422. return DoAsyncNullCallWithCallback(&asyncState, b, i, p);
  423. default:
  424. printf("Invalid Notification option\n");
  425. return FALSE;
  426. }
  427. }
  428. unsigned long DoAsyncSTANullCall(handle_t __RPC_FAR * b, HWND hWnd, long i, char __RPC_FAR *p)
  429. {
  430. RPC_STATUS RpcStatus;
  431. RPC_ASYNC_STATE asyncState;
  432. StartTime();
  433. while(i--)
  434. {
  435. RpcStatus = RpcAsyncInitializeHandle(&asyncState, RPC_ASYNC_VERSION_1_0);
  436. if (RpcStatus != RPC_S_OK)
  437. {
  438. printf("RpcAsyncInitializeHandle failed: %ld\n", GetLastError());
  439. return 0;
  440. }
  441. asyncState.NotificationType = RpcNotificationTypeCallback;
  442. asyncState.u.NotificationRoutine = NotifyProc;
  443. AsyncSTANullCall(&asyncState, *b);
  444. PumpMessage();
  445. RpcAsyncCompleteCall(&asyncState, NULL);
  446. }
  447. return (FinishTiming());
  448. }
  449. static const unsigned long (*TestTable[TEST_MAX])(handle_t __RPC_FAR *, long, char __RPC_FAR *) =
  450. {
  451. DoNullCall,
  452. DoNICall,
  453. DoWrite1K,
  454. DoRead1K,
  455. DoWrite4K,
  456. DoRead4K,
  457. DoWrite32K,
  458. DoRead32K,
  459. DoContextNullCall,
  460. DoFixedBinding,
  461. DoReBinding,
  462. DoDynamicBinding,
  463. DoAsyncNullCall
  464. #ifdef WIN98
  465. , DoAsyncSTANullCall
  466. #endif
  467. };
  468. //
  469. // Worker calls the correct tests. Maybe multithreaded on NT
  470. //
  471. unsigned long Worker(unsigned long l)
  472. {
  473. unsigned long status;
  474. unsigned long lTest;
  475. long lIterations, lClientId;
  476. unsigned long lTime;
  477. char __RPC_FAR *pBuffer;
  478. char __RPC_FAR *stringBinding;
  479. handle_t binding;
  480. #ifdef WIN98
  481. BOOL fIsPortseqWmsg;
  482. HWND hWnd;
  483. HWND hServerWnd;
  484. DWORD dwServerTid;
  485. #endif
  486. #ifdef WIN98
  487. if (strcmp(Protseq, "mswmsg") == 0)
  488. {
  489. fIsPortseqWmsg = TRUE;
  490. hWnd = CreateSTAWindow("Perf Client");
  491. if (hWnd == NULL)
  492. {
  493. printf("Couldn't create STA window: %ld\n", GetLastError());
  494. return 0;
  495. }
  496. status = I_RpcServerStartListening(hWnd);
  497. if (status != RPC_S_OK)
  498. {
  499. printf("Failed to I_RpcServerStartListening: %ld\n", status);
  500. return 0;
  501. }
  502. }
  503. else
  504. fIsPortseqWmsg = FALSE;
  505. #endif
  506. pBuffer = MIDL_user_allocate(32*1024L);
  507. if (pBuffer == 0)
  508. {
  509. PrintToConsole("Out of memory!");
  510. return 1;
  511. }
  512. status =
  513. RpcStringBindingCompose(0,
  514. Protseq,
  515. NetworkAddr,
  516. Endpoint,
  517. 0,
  518. &stringBinding);
  519. CHECK_RET(status, "RpcStringBindingCompose");
  520. status =
  521. RpcBindingFromStringBinding(stringBinding, &binding);
  522. CHECK_RET(status, "RpcBindingFromStringBinding");
  523. status =
  524. DoRpcBindingSetAuthInfo(binding);
  525. CHECK_RET(status, "RpcBindingSetAuthInfo");
  526. RpcStringFree(&stringBinding);
  527. #ifdef WIN98
  528. if (fIsPortseqWmsg == TRUE)
  529. {
  530. while (TRUE)
  531. {
  532. hServerWnd = FindWindow(NULL, "Perf Server");
  533. if (hServerWnd)
  534. {
  535. dwServerTid = (DWORD)GetWindowLong(hServerWnd, GWL_USERDATA);
  536. break;
  537. }
  538. printf(".");
  539. Sleep(100);
  540. }
  541. status = I_RpcBindingSetAsync(binding, NULL, dwServerTid);
  542. if (status != RPC_S_OK)
  543. {
  544. printf("Failed to I_RpcBindingSetAsync: %ld\n", status);
  545. return 0;
  546. }
  547. PrintToConsole("(%ld iterations of case %ld: ", 10000, 13);
  548. lTime = DoAsyncSTANullCall(&binding, hWnd, 10000, pBuffer);
  549. PrintToConsole("%ld mseconds)\n",
  550. lTime
  551. );
  552. return RPC_S_OK;
  553. }
  554. #endif
  555. RpcTryExcept
  556. {
  557. status =
  558. BeginTest(binding, &lClientId);
  559. }
  560. RpcExcept(1)
  561. {
  562. PrintToConsole("First call failed %ld (%08lx)\n",
  563. (unsigned long)RpcExceptionCode(),
  564. (unsigned long)RpcExceptionCode());
  565. goto Cleanup;
  566. }
  567. RpcEndExcept
  568. if (status == PERF_TOO_MANY_CLIENTS)
  569. {
  570. PrintToConsole("Too many clients, I'm exiting\n");
  571. goto Cleanup ;
  572. }
  573. CHECK_RET(status, "ClientConnect");
  574. PrintToConsole("Client %ld connected\n", lClientId);
  575. do
  576. {
  577. status = NextTest(binding, (TEST_TYPE *)&lTest, &lIterations);
  578. if (status == PERF_TESTS_DONE)
  579. {
  580. goto Cleanup;
  581. }
  582. CHECK_RET(status, "NextTest");
  583. PrintToConsole("(%ld iterations of case %ld: ", lIterations, lTest);
  584. RpcTryExcept
  585. {
  586. lTime = ( (TestTable[lTest])(&binding, lIterations, pBuffer));
  587. PrintToConsole("%ld mseconds)\n",
  588. lTime
  589. );
  590. status =
  591. EndTest(binding, lTime);
  592. CHECK_RET(status, "EndTest");
  593. }
  594. RpcExcept(1)
  595. {
  596. PrintToConsole("\nTest case %ld raised exception %lu (0x%08lX)\n",
  597. lTest,
  598. (unsigned long)RpcExceptionCode(),
  599. (unsigned long)RpcExceptionCode());
  600. status = RpcExceptionCode();
  601. }
  602. RpcEndExcept
  603. }
  604. while(status == 0);
  605. Cleanup:
  606. RpcBindingFree(&binding) ;
  607. return status;
  608. }
  609. //
  610. // The Win32 main starts worker threads, otherwise we just call the worker.
  611. //
  612. #ifdef WIN32
  613. int __cdecl
  614. main (int argc, char **argv)
  615. {
  616. char option;
  617. unsigned long status, i;
  618. HANDLE *pClientThreads;
  619. #ifdef WIN98
  620. BOOL fIsPortseqWmsg;
  621. #endif
  622. ParseArgv(argc, argv);
  623. PrintToConsole("Authentication Level is: %s\n", AuthnLevelStr);
  624. if (Options[0] < 0)
  625. Options[0] = 1;
  626. InitAllocator();
  627. #ifdef WIN98
  628. if (strcmp(Protseq, "mswmsg") == 0)
  629. {
  630. fIsPortseqWmsg = TRUE;
  631. status = RpcServerUseProtseqEp(Protseq, 1, "Perf Client", NULL);
  632. if (status != RPC_S_OK)
  633. {
  634. printf("Failed to use protseq: %ld\n", status);
  635. return 3;
  636. }
  637. }
  638. else
  639. fIsPortseqWmsg = FALSE;
  640. #endif
  641. pClientThreads = MIDL_user_allocate(sizeof(HANDLE) * Options[0]);
  642. for(i = 0; i < (unsigned long)Options[0]; i++)
  643. {
  644. pClientThreads[i] = CreateThread(0,
  645. 0,
  646. (LPTHREAD_START_ROUTINE)Worker,
  647. 0,
  648. 0,
  649. &status);
  650. if (pClientThreads[i] == 0)
  651. ApiError("CreateThread", GetLastError());
  652. }
  653. status = WaitForMultipleObjects(Options[0],
  654. pClientThreads,
  655. TRUE, // Wait for all client threads
  656. INFINITE);
  657. if (status == WAIT_FAILED)
  658. {
  659. ApiError("WaitForMultipleObjects", GetLastError());
  660. }
  661. PrintToConsole("TEST DONE\n");
  662. return(0);
  663. }
  664. #else // !WIN32
  665. #ifdef WIN
  666. #define main c_main
  667. // We need the following to force the linker to load WinMain from the
  668. // Windows STDIO library
  669. extern int PASCAL WinMain(HANDLE, HANDLE, LPSTR, int);
  670. static int (PASCAL *wm_ptr)(HANDLE, HANDLE, LPSTR, int) = WinMain;
  671. #endif
  672. #ifndef MAC
  673. #ifndef FAR
  674. #define FAR __far
  675. #endif
  676. #else
  677. #define FAR
  678. #define main c_main
  679. #endif
  680. int main (int argc, char FAR * FAR * argv)
  681. {
  682. #ifndef MAC
  683. ParseArgv(argc, argv);
  684. #endif
  685. Worker(0);
  686. PrintToConsole("TEST DONE\n");
  687. return(0);
  688. }
  689. #endif // NTENV