|
|
1.1 root 1:
2: /****************************************************************************
3: Microsoft RPC Version 1.0
4: Copyright Microsoft Corp. 1992
5: yield Example
6:
7: FILE: yieldp.c
8:
9: PURPOSE: RPC sample Windows client
10: Based on Win 3.x SDK Generic template for Windows applications
11:
12: FUNCTIONS: WinMain() - same as Windows generic example
13: InitApplication() - same as Windows generic example
14: InitInstance() - same as Windows generic example
15: MainWndProc() - processes messages
16:
17: About() - processes messages for "About" dialog box
18: BindInfo() - processes messages for "Bind" dialog box
19: WaitInfo() - processes messages for "Wait" dialog box
20: YieldInfo() - processes messages for "Yield" dialog box
21:
22: Bind() - calls the RPC API functions
23: midl_user_allocate() - memory allocation function needed by RPC
24: midl_user_free() - memory free function needed by RPC
25:
26: COMMENTS: This sample application demonstrates the yield capability
27: of the Microsoft RPC for Microsoft Windows 3.x using the
28: RpcWinSetYieldInfo API function.
29:
30: By yielding, you can prevent your distributed application
31: from blocking during lengthy remote procedure calls.
32:
33: This sample is based on the Win 3.x generic example. To focus
34: attention on the RPC-related aspects of this application,
35: many comments from the Windows-only version are removed.
36:
37: ****************************************************************************/
38:
39: #include <stdio.h>
40: #include <stdlib.h>
41: #include <string.h>
42: #include <windows.h>
43: #include <windowsx.h>
44: #include "yield.h" // header file generated by MIDL compiler
45: #include "yieldc.h" // client-specific header file
46:
47: /* global data */
48:
49: unsigned char pszProtocolSequence[MAXPROTSEQ+1];
50: unsigned char pszNetworkAddress[UNCLEN+1];
51: unsigned char pszEndpoint[PATHLEN+1];
52: unsigned char * pszUuid = NULL;
53: unsigned char * pszOptions = NULL;
54: unsigned char * pszStringBinding = NULL;
55:
56: int fBound = FALSE; // flag indicates whether client is bound to server
57: int fCancel = FALSE; // flag indicates whether the user has chosen CANCEL
58:
59: unsigned int cWaitSec; // parameter to remote procedure Sleep()
60:
61: int fCustomYield; // TRUE = custom yield, FALSE = standard yield
62: DWORD dwOtherInfo; // depends on the value of fCustomYield
63:
64: HANDLE hInst; // current instance
65: HCURSOR hHourGlass; // during calls to RPC API functions
66: HWND hWndMain; // main handle
67:
68:
69: /****************************************************************************
70:
71: FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)
72:
73: PURPOSE: Calls initialization function, processes message loop
74:
75: COMMENTS: Windows recognizes this function by name as the initial
76: entry point for the program. This function calls the
77: application initialization routine, if no other instance
78: of the program is running, and always calls the instance
79: initialization routine. It then executes a message
80: retrieval and dispatch loop that is the top-level control
81: structure for the remainder of execution. The loop is
82: terminated when a WM_QUIT message is received, at which
83: time this function exits the application instance by
84: returning the value passed by PostQuitMessage().
85:
86: If this function must abort before entering the message
87: loop, it returns the conventional value NULL.
88:
89: ****************************************************************************/
90:
91: int WINAPI WinMain(HINSTANCE hInstance, // current instance
92: HINSTANCE hPrevInstance, // previous instance
93: LPSTR lpCmdLine, // command line
94: int nCmdShow) // show-window type
95: {
96: MSG msg;
97:
98: UNREFERENCED_PARAMETER(lpCmdLine);
99:
100: if (! hPrevInstance) // no other instances of app running
101: if (! InitApplication(hInstance)) // initialize shared things
102: return(FALSE); // exit if unable to initialize
103:
104: /* Perform initializations that apply to a specific instance */
105: if (! InitInstance(hInstance, nCmdShow))
106: return(FALSE);
107:
108: /* Acquire and dispatch messages until a WM_QUIT message is received */
109: while (GetMessage(&msg, // message structure
110: (HWND)NULL, // handle of window receiving the message
111: 0, // lowest message to examine
112: 0)) // highest message to examine
113: {
114: TranslateMessage(&msg); // translate virtual key codes
115: DispatchMessage(&msg); // dispatche message to window
116: }
117:
118: return(msg.wParam); // return the value from PostQuitMessage
119: }
120:
121:
122: /****************************************************************************
123:
124: FUNCTION: InitApplication(HANDLE)
125:
126: PURPOSE: Initializes window data and registers window class
127:
128: COMMENTS: This function is called at initialization time only if
129: no other instances of the application are running. This
130: function performs initialization tasks that can be done
131: once for any number of running instances.
132:
133: In this case, we initialize a window class by filling out
134: a data structure of type WNDCLASS and calling the Windows
135: RegisterClass() function. Since all instances of this
136: application use the same window class, we only need to do
137: this when the first instance is initialized.
138:
139: ****************************************************************************/
140:
141: BOOL InitApplication(HANDLE hInstance) // current instance
142: {
143: WNDCLASS wc;
144:
145: /* Fill in the window class structure with parameters that */
146: /* describe the main window */
147: wc.style = 0;
148: wc.lpfnWndProc = (WNDPROC) MainWndProc;
149: wc.cbClsExtra = 0;
150: wc.cbWndExtra = 0;
151: wc.hInstance = hInstance;
152: wc.hIcon = LoadIcon(hInstance, "YieldIcon");
153: wc.hCursor = LoadCursor((HANDLE) NULL, IDC_ARROW);
154: wc.hbrBackground = GetStockObject(WHITE_BRUSH);
155: wc.lpszMenuName = "GenericMenu";
156: wc.lpszClassName = "GenericWClass";
157:
158: /* Register the window class and return success/failure code */
159: return(RegisterClass(&wc));
160: }
161:
162:
163: /****************************************************************************
164:
165: FUNCTION: InitInstance(HANDLE, int)
166:
167: PURPOSE: Saves instance handle and creates main window
168:
169: COMMENTS: This function is called at initialization time for every
170: instance of this application. This function performs
171: initialization tasks that cannot be shared by multiple
172: instances.
173:
174: In this case, we save the instance handle in a global
175: variable and create and display the main program window.
176:
177: ****************************************************************************/
178:
179: BOOL InitInstance(HANDLE hInstance, // current instance
180: int nCmdShow) // param for first ShowWindow() call
181: {
182: HWND hWnd;
183:
184:
185: /* Save the instance handle in global variable, which will be used */
186: /* in many subsequence calls from this application to Windows */
187: hInst = hInstance;
188: hHourGlass = LoadCursor((HANDLE) NULL, IDC_WAIT);
189:
190: /* Create a main window for this application instance */
191: hWnd = CreateWindow("GenericWClass",
192: "RPC Sample Application",
193: WS_OVERLAPPEDWINDOW,
194: CW_USEDEFAULT,
195: CW_USEDEFAULT,
196: CW_USEDEFAULT,
197: CW_USEDEFAULT,
198: (HWND) NULL,
199: (HMENU) NULL,
200: hInstance,
201: (LPVOID) NULL
202: );
203:
204: /* If the window cannot be created, return "failure" */
205: if (!hWnd)
206: return(FALSE);
207:
208: /* Initialize RPC binding data */
209: strcpy(pszProtocolSequence, DEFAULT_PROT_SEQ);
210: strcpy(pszEndpoint, DEFAULT_ENDPOINT);
211: pszNetworkAddress[0] = '\0';
212:
213: /* Bind client to server */
214: fBound = FALSE;
215:
216: /* Initialize the parameter to the remote procedure Sleep() */
217: cWaitSec = DEFAULT_WAIT;
218:
219: /* Initialize the parameters to RpcWinSetYieldInfo() */
220: fCustomYield = FALSE; // FALSE = std yield
221: dwOtherInfo = (DWORD) NULL; // NULL = RPC-supplied dialog box
222:
223: RpcWinSetYieldInfo(hWnd, // handle
224: fCustomYield, // standard or custom yield?
225: WM_RPC_YIELD_MESSAGE, // 0 = no message is posted
226: dwOtherInfo); // depends on fCustomYield value
227:
228: /* Make the window visible, update its client area, and return "success" */
229: ShowWindow(hWnd, nCmdShow);
230: UpdateWindow(hWnd);
231:
232: return(TRUE);
233: }
234:
235:
236: /****************************************************************************
237:
238: FUNCTION: MainWndProc(HWND, UINT, WPARAM, LPARAM)
239:
240: PURPOSE: Processes messages
241:
242: MESSAGES: WM_COMMAND - application menu (About dialog box)
243: WM_DESTROY - destroy window
244:
245: COMMENTS: Based on the Windows generic sample.
246: Several new menu items are added to demonstrate the
247: RpcWinSetYieldInfo function.
248:
249: The "Bind" menu is associated with the "GetBindInfo"
250: dialog box function. GetBindInfo allows the user to
251: set the parameters for the RpcStringBindingCompose and
252: RpcBindingFromStringBinding functions. A flag, fBound,
253: keeps track of whether the client application is bound
254: to a remote server. If this flag indicates that the
255: client application is not bound to the server, it calls
256: the "Bind" utility function to call the
257: RPC API functions that establish the binding.
258:
259: The "Yield" menu is associated with the "GetYieldInfo"
260: dialog box function. GetYieldInfo allows the user to
261: select the yielding model: standard yield with an
262: RPC run-time library-supplied dialog box; standard
263: yield with a user-supplied dialog box; or custom yield.
264: These methods are described in detail in the documentation
265: for the RpcWinSetYieldInfo function.
266:
267: ****************************************************************************/
268:
269: long APIENTRY MainWndProc(HWND hWnd, // window handle
270: UINT message, // type of message
271: WPARAM wParam, // additional information
272: LPARAM lParam // additional information
273: )
274: {
275: DLGPROC lpProc; // pointer to the dialog box function
276:
277: /* copy the window handle for CustomYield() */
278: hWndMain = hWnd;
279:
280: switch (message) {
281:
282: case WM_CREATE:
283: /* Win 3.x is client-only; force user to specify server */
284: PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L);
285: break;
286:
287: case WM_COMMAND:
288: switch (wParam) {
289:
290: case IDM_ABOUT:
291: lpProc = MakeProcInstance(About, hInst);
292: DialogBox(hInst,
293: "AboutBox",
294: hWnd,
295: lpProc);
296: FreeProcInstance(lpProc);
297: break;
298:
299: case IDM_BIND:
300: lpProc = MakeProcInstance(GetBindInfo, hInst);
301: DialogBox(hInst,
302: "BindBox",
303: hWnd,
304: lpProc);
305: FreeProcInstance(lpProc);
306: break;
307:
308: case IDM_WAIT:
309: lpProc = MakeProcInstance(GetWaitInfo, hInst);
310: DialogBox(hInst,
311: "WaitBox",
312: hWnd,
313: lpProc);
314: FreeProcInstance(lpProc);
315: break;
316:
317: case IDM_YIELD:
318: lpProc = MakeProcInstance(GetYieldInfo, hInst);
319: DialogBox(hInst,
320: "YieldBox",
321: hWnd,
322: lpProc);
323: FreeProcInstance(lpProc);
324: break;
325:
326: case IDM_EXIT:
327: DestroyWindow(hWnd);
328: if (fBound == TRUE) {
329: RpcTryExcept {
330: Shutdown(); // shut down the server
331: }
332: RpcExcept(1) {
333: MessageBox(hWnd,
334: EXCEPT_MSG,
335: "Remote Procedure Call",
336: MB_ICONINFORMATION);
337: }
338: RpcEndExcept
339: }
340: break;
341:
342: default:
343: return(DefWindowProc(hWnd, message, wParam, lParam));
344:
345: }
346: break;
347:
348: case WM_RPC_YIELD_MESSAGE: // signals beginning or end of yield period
349: if (wParam == 0)
350: SetWindowText(hWnd, YIELD_END_MSG);
351: else if (wParam == 1)
352: SetWindowText(hWnd, YIELD_START_MSG);
353: break;
354:
355: case WM_DESTROY:
356: PostQuitMessage(0);
357: break;
358:
359: default: // passes it on if unprocessed
360: return(DefWindowProc(hWnd, message, wParam, lParam));
361:
362: }
363:
364: return(0L);
365: }
366:
367:
368: /****************************************************************************
369:
370: FUNCTION: About(HWND, unsigned, WORD, LONG)
371:
372: PURPOSE: "About" dialog box
373:
374: COMMENTS:
375:
376: ****************************************************************************/
377:
378: BOOL APIENTRY About(HWND hDlg,
379: UINT message,
380: UINT wParam,
381: LONG lParam)
382: {
383: UNREFERENCED_PARAMETER(lParam);
384:
385: switch (message) {
386:
387: case WM_INITDIALOG:
388: return(TRUE);
389:
390: case WM_COMMAND:
391: if (wParam == IDOK || wParam == IDCANCEL) {
392: EndDialog(hDlg, TRUE);
393: return(TRUE);
394: }
395: break;
396:
397: }
398:
399: return(FALSE);
400: }
401:
402:
403: /****************************************************************************
404:
405: FUNCTION: GetBindInfo(HWND, unsigned, WORD, LONG)
406:
407: PURPOSE: Collect components of string binding;
408: protocol sequence, network address, endpoint
409:
410: COMMENTS:
411:
412: ****************************************************************************/
413:
414: BOOL APIENTRY GetBindInfo(HWND hDlg,
415: UINT message,
416: UINT wParam,
417: LONG lParam)
418: {
419: HCURSOR hOld;
420:
421: UNREFERENCED_PARAMETER(lParam);
422:
423: switch (message) {
424:
425: case WM_INITDIALOG: // fill in dialog's edit boxes
426: SetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint);
427: SetDlgItemText(hDlg, IDD_PROT_SEQ, pszProtocolSequence);
428: SetDlgItemText(hDlg, IDD_NET_ADDR, pszNetworkAddress);
429: return(TRUE);
430:
431: case WM_COMMAND:
432: switch(wParam) {
433:
434: case IDCANCEL:
435: EndDialog(hDlg, FALSE);
436: return(TRUE);
437:
438: case IDOK:
439: GetDlgItemText(hDlg, IDD_PROT_SEQ, pszProtocolSequence, MAXPROTSEQ);
440: GetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint, PATHLEN);
441: GetDlgItemText(hDlg, IDD_NET_ADDR, pszNetworkAddress, UNCLEN);
442:
443: /* pszNetworkAddress must start with two backslashes */
444: if (pszNetworkAddress[0] != '\0' &&
445: strncmp(pszNetworkAddress, "\\\\", 2)) {
446:
447: unsigned char oldNetAddr[UNCLEN+1];
448:
449: strcpy(oldNetAddr, pszNetworkAddress);
450: sprintf(pszNetworkAddress, "\\\\%s", oldNetAddr);
451: }
452:
453: hOld = SetCursor(hHourGlass);
454: if (Bind(hDlg) != RPC_S_OK) { // bind to server
455: EndDialog(hDlg, FALSE);
456: return(FALSE);
457: }
458:
459: SetCursor(hOld);
460: EndDialog(hDlg, TRUE);
461: return(TRUE);
462: }
463: }
464:
465: return(FALSE);
466: }
467:
468:
469: /****************************************************************************
470:
471: FUNCTION: GetWaitInfo(HWND, unsigned, WORD, LONG)
472:
473: PURPOSE: Prompt user for the parameter to the remote call,
474: then make the remote procedure call.
475:
476: COMMENTS:
477:
478: ****************************************************************************/
479:
480: BOOL APIENTRY GetWaitInfo(HWND hDlg, // window handle of the dialog box
481: UINT message, // type of message
482: UINT wParam, // message-specific information
483: LONG lParam)
484: {
485: int fError;
486:
487: UNREFERENCED_PARAMETER(lParam);
488:
489: switch (message) {
490:
491: case WM_INITDIALOG:
492: SetDlgItemInt(hDlg, IDD_WAITTIME, cWaitSec, FALSE);
493: return(TRUE);
494:
495: case WM_COMMAND:
496: switch(wParam) {
497:
498: case IDCANCEL:
499: EndDialog(hDlg, FALSE);
500: fCancel = TRUE;
501: return(TRUE);
502:
503: case IDOK:
504: cWaitSec = GetDlgItemInt(hDlg, IDD_WAITTIME, &fError, FALSE);
505: if (cWaitSec <= 0) // check for valid entry
506: cWaitSec = DEFAULT_WAIT; // set an appropriate value
507:
508: RpcTryExcept {
509: YieldProc(cWaitSec); // make the remote procedure call
510: }
511: RpcExcept(1) {
512: unsigned long ulCode;
513: char pszFail[MSGLEN];
514:
515: ulCode = RpcExceptionCode();
516: if (ulCode != RPC_S_CALL_FAILED) {
517: sprintf(pszFail, "%s (0x%x)\n", EXCEPT_MSG, ulCode);
518: MessageBox(hDlg,
519: pszFail,
520: "Remote Procedure Call",
521: MB_ICONINFORMATION);
522: }
523: }
524: RpcEndExcept
525:
526: EndDialog(hDlg, TRUE);
527: return(TRUE);
528: }
529: }
530:
531: return(FALSE);
532: }
533:
534:
535: /****************************************************************************
536:
537: FUNCTION: CustomYield(void)
538:
539: PURPOSE: Message handler during custom yield
540:
541: MESSAGES: WM_RPC_YIELD_MESSAGE - end of yield message
542: WM_COMMAND - Input received
543:
544: COMMENTS: The callback function must retrieve messages from the
545: message queue.
546:
547: The function must return TRUE when the RPC operation
548: has completed. The function must return FALSE when the
549: user cancels the RPC operation.
550:
551: ****************************************************************************/
552:
553: BOOL FAR PASCAL __export CustomYield(void)
554: {
555: MSG msg;
556:
557: fCancel = FALSE;
558:
559: while (TRUE) { // message processing
560: GetMessage(&msg, (HWND)NULL, 0, 0);
561:
562: if (msg.message == WM_RPC_YIELD_MESSAGE)
563: return(TRUE); // RPC operation is complete
564:
565: if (fCancel == TRUE)
566: return(FALSE);
567:
568: TranslateMessage(&msg);
569: DispatchMessage(&msg);
570: }
571:
572: return(TRUE);
573: }
574:
575:
576: /****************************************************************************
577:
578: FUNCTION: GetYieldInfo(HWND, unsigned, WORD, LONG)
579:
580: PURPOSE: Check radio buttons to see which yield method
581: was selected by the user, then set the parameters
582: to RpcWinSetYieldInfo and call RpcWinSetYieldInfo.
583:
584: COMMENTS:
585:
586: ****************************************************************************/
587:
588: BOOL APIENTRY GetYieldInfo(HWND hDlg, // window handle of the dialog box
589: UINT message, // type of message
590: UINT wParam, // message-specific information
591: LONG lParam)
592: {
593: int fCheck;
594:
595: UNREFERENCED_PARAMETER(lParam);
596:
597: switch (message) {
598:
599: case WM_INITDIALOG: // message: initialize dialog box
600: if (fCustomYield)
601: CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_CUSTOM);
602: else if (dwOtherInfo != (DWORD) NULL)
603: CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_STD_USER);
604: else
605: CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_STD_RPC);
606: return(TRUE);
607:
608: case WM_COMMAND: // message: received a command
609: switch(wParam) {
610:
611: case IDCANCEL: // System menu close command?
612: EndDialog(hDlg, FALSE);
613: return(TRUE);
614:
615: case IDOK:
616: /* which radio button is checked: Custom yield? */
617: fCheck = (int) SendDlgItemMessage(hDlg,
618: IDD_CUSTOM,
619: BM_GETCHECK,
620: 0,
621: 0);
622: if (fCheck == TRUE) {
623: fCustomYield = TRUE;
624: dwOtherInfo = (DWORD) MakeProcInstance(CustomYield, hInst);
625: }
626: else {
627: fCustomYield = FALSE;
628:
629: /* Standard yield, user-supplied dialog? */
630: fCheck = (int) SendDlgItemMessage(hDlg,
631: IDD_STD_USER,
632: BM_GETCHECK,
633: 0,
634: 0);
635: if (fCheck == TRUE) {
636: HRSRC hrsrc;
637:
638: hrsrc = FindResource(hInst,
639: "USERYIELDBOX",
640: RT_DIALOG);
641: dwOtherInfo = LoadResource(hInst, hrsrc);
642: }
643: else {
644: /* Assume standard-yield, rpc-supplied dialog box */
645: dwOtherInfo = (DWORD) NULL;
646: }
647: }
648:
649: RpcWinSetYieldInfo(hWndMain,
650: fCustomYield,
651: WM_RPC_YIELD_MESSAGE,
652: dwOtherInfo);
653:
654: EndDialog(hDlg, TRUE);
655: return(TRUE);
656: }
657: }
658:
659: return(FALSE);
660: }
661:
662:
663: /****************************************************************************
664:
665: FUNCTION: midl_user_allocate(size_t)
666:
667: PURPOSE: Allocate memory as needed by the RPC runtime library
668:
669: COMMENTS: The stubs or runtime libraries may need to allocate memory.
670: By convention, they call a user-specified function named
671: midl_user_allocate. In this application, no memory
672: management is needed, so a dummy function is provided.
673:
674: ****************************************************************************/
675:
676: void __RPC_FAR * __RPC_API midl_user_allocate(size_t len)
677: {
678: UNREFERENCED_PARAMETER(len);
679: return(NULL); // no memory management required
680: }
681:
682: /****************************************************************************
683:
684: FUNCTION: midl_user_free(void *)
685:
686: PURPOSE: Free memory as needed by the RPC runtime library
687:
688: COMMENTS: The stubs or runtime libraries may need to free memory.
689: By convention, they call a user-specified function named
690: midl_user_free. In this application, no memory allocation
691: is needed so a dummy function is provided.
692:
693: ****************************************************************************/
694:
695: void __RPC_API midl_user_free(void __RPC_FAR * ptr)
696: {
697: UNREFERENCED_PARAMETER(ptr);
698: return; // no memory management required
699: }
700:
701:
702: /****************************************************************************
703:
704: FUNCTION: Bind(HWND)
705:
706: PURPOSE: Make RPC API calls to bind to the server application
707:
708: COMMENTS: The binding calls are made from InitInstance() and whenever
709: the user changes the protocol sequence, network address, or
710: endpoint. If the bind operation is successful, the global
711: flag fBound is set to TRUE.
712:
713: The global flag fBound is used to determine whether to call
714: the RPC API function RpcBindingFree.
715:
716: ****************************************************************************/
717:
718: RPC_STATUS Bind(HWND hWnd)
719: {
720: RPC_STATUS status;
721: char pszFail[MSGLEN];
722:
723: if (fBound == TRUE) { // unbind only if bound
724: status = RpcStringFree(&pszStringBinding); // remote calls done; unbind
725: if (status) {
726: MessageBox(hWnd, "RpcStringFree failed", "RPC Error", MB_ICONSTOP);
727: return(status);
728: }
729:
730: status = RpcBindingFree(&hYield); // remote calls done; unbind
731: if (status) {
732: MessageBox(hWnd, "RpcBindingFree failed", "RPC Error", MB_ICONSTOP);
733: return(status);
734: }
735:
736: fBound = FALSE; // unbind successful; reset flag
737: }
738:
739: status = RpcStringBindingCompose(pszUuid,
740: pszProtocolSequence,
741: pszNetworkAddress,
742: pszEndpoint,
743: pszOptions,
744: &pszStringBinding);
745: if (status) {
746: sprintf(pszFail, "RpcStringBindingCompose failed: (0x%x)\nNetwork Address = %s\n",
747: status, pszNetworkAddress);
748: MessageBox(hWnd,
749: pszFail,
750: "RPC Runtime Error",
751: MB_ICONEXCLAMATION);
752: return(status);
753: }
754:
755: status = RpcBindingFromStringBinding(pszStringBinding,
756: &hYield);
757: if (status) {
758: sprintf(pszFail, "RpcBindingFromStringBinding failed: (0x%x)\nString = %s\n",
759: status, pszStringBinding);
760: MessageBox(hWnd,
761: pszFail,
762: "RPC Runtime Error",
763: MB_ICONEXCLAMATION);
764: return(status);
765: }
766:
767: fBound = TRUE; // bind successful; reset flag
768:
769: return(status);
770: }
771:
772:
773: /**** end yieldc.c ****/
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.