
/****************************************************************************
                   Microsoft RPC Version 2.0
           Copyright Microsoft Corp. 1992, 1993, 1994
                       yield Example

    FILE:       yieldp.c
    
    PURPOSE:    RPC sample Windows client
                Based on Win 3.x SDK Generic template for Windows applications

    FUNCTIONS:  WinMain()         - same as Windows generic example
                InitApplication() - same as Windows generic example
                InitInstance()    - same as Windows generic example
                MainWndProc()     - processes messages

                About()     - processes messages for "About" dialog box
                BindInfo()  - processes messages for "Bind" dialog box
                WaitInfo()  - processes messages for "Wait" dialog box
                YieldInfo() - processes messages for "Yield" dialog box

                Bind()               - calls the RPC API functions
                midl_user_allocate() - memory allocation function needed by RPC
                midl_user_free()     - memory free function needed by RPC

    COMMENTS:   This sample application demonstrates the yield capability
                of the Microsoft RPC for Microsoft Windows 3.x using the
                RpcWinSetYieldInfo API function.

                By yielding, you can prevent your distributed application
                from blocking during lengthy remote procedure calls.

                This sample is based on the Win 3.x generic example. To focus
                attention on the RPC-related aspects of this application,
                many comments from the Windows-only version are removed.

****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <windows.h>
#include <windowsx.h>
#include "yield.h"     // header file generated by MIDL compiler
#include "yieldc.h"    // client-specific header file  

/* global data */

unsigned char   pszProtocolSequence[MAXPROTSEQ+1];
unsigned char   pszNetworkAddress[NETLEN+1];
unsigned char   pszEndpoint[PATHLEN+1];
unsigned char * pszUuid           = NULL;
unsigned char * pszOptions        = NULL;
unsigned char * pszStringBinding  = NULL;

int fBound = FALSE;    // flag indicates whether client is bound to server 
int fCancel = FALSE;   // flag indicates whether the user has chosen CANCEL

unsigned int cWaitSec; // parameter to remote procedure Sleep()

int   fCustomYield;    // TRUE = custom yield, FALSE = standard yield
DWORD dwOtherInfo;     // depends on the value of fCustomYield

HANDLE  hInst;         // current instance
HCURSOR hHourGlass;    // during calls to RPC API functions
HWND    hWndMain;      // main handle


/****************************************************************************

    FUNCTION: WinMain(HINSTANCE, HINSTANCE, LPSTR, int)

    PURPOSE:  Calls initialization function, processes message loop

    COMMENTS: Windows recognizes this function by name as the initial    
              entry point for the program.  This function calls the      
              application initialization routine, if no other instance   
              of the program is running, and always calls the instance   
              initialization routine.  It then executes a message        
              retrieval and dispatch loop that is the top-level control  
              structure for the remainder of execution.  The loop is     
              terminated when a WM_QUIT message is received, at which    
              time this function exits the application instance by       
              returning the value passed by PostQuitMessage().           
                                                                         
              If this function must abort before entering the message    
              loop, it returns the conventional value NULL.              

****************************************************************************/

int WINAPI WinMain(HINSTANCE hInstance,      // current instance
                   HINSTANCE hPrevInstance,  // previous instance
                   LPSTR     lpCmdLine,      // command line
                   int       nCmdShow)       // show-window type
{
    MSG msg;

    UNREFERENCED_PARAMETER(lpCmdLine);

    if (! hPrevInstance)                   // no other instances of app running
        if (! InitApplication(hInstance))  // initialize shared things      
            return(FALSE);                 // exit if unable to initialize  
                                                                            
    /* Perform initializations that apply to a specific instance */         
    if (! InitInstance(hInstance, nCmdShow))                                 
        return(FALSE);                                                     
                                                                            
    /* Acquire and dispatch messages until a WM_QUIT message is received */
    while (GetMessage(&msg,        // message structure                     
                      (HWND)NULL,  // handle of window receiving the message
                      0,           // lowest message to examine             
                      0))          // highest message to examine            
    {
        TranslateMessage(&msg);    // translate virtual key codes          
        DispatchMessage(&msg);     // dispatche message to window          
    }                                                                       
                                                                            
    return(msg.wParam);  // return the value from PostQuitMessage  
}


/****************************************************************************

    FUNCTION: InitApplication(HANDLE)

    PURPOSE:  Initializes window data and registers window class

    COMMENTS: This function is called at initialization time only if    
              no other instances of the application are running.  This  
              function performs initialization tasks that can be done   
              once for any number of running instances.                 
                                                                        
              In this case, we initialize a window class by filling out 
              a data structure of type WNDCLASS and calling the Windows 
              RegisterClass() function.  Since all instances of this    
              application use the same window class, we only need to do 
              this when the first instance is initialized.              

****************************************************************************/

BOOL InitApplication(HANDLE hInstance)    // current instance
{
    WNDCLASS wc;

    /* Fill in the window class structure with parameters that   */
    /* describe the main window                                  */
    wc.style         = 0;
    wc.lpfnWndProc   = (WNDPROC) MainWndProc;
    wc.cbClsExtra    = 0;
    wc.cbWndExtra    = 0;
    wc.hInstance     = hInstance;
    wc.hIcon         = LoadIcon(hInstance, "YieldIcon"); 
    wc.hCursor       = LoadCursor((HANDLE) NULL, IDC_ARROW);
    wc.hbrBackground = GetStockObject(WHITE_BRUSH); 
    wc.lpszMenuName  = "GenericMenu";
    wc.lpszClassName = "GenericWClass";

    /* Register the window class and return success/failure code */
    return(RegisterClass(&wc));
}


/****************************************************************************

    FUNCTION:  InitInstance(HANDLE, int)

    PURPOSE:   Saves instance handle and creates main window

    COMMENTS:  This function is called at initialization time for every     
               instance of this application.  This function performs        
               initialization tasks that cannot be shared by multiple       
               instances.                                                   
                                                                           
               In this case, we save the instance handle in a global        
               variable and create and display the main program window.     
                                                                           
****************************************************************************/

BOOL InitInstance(HANDLE  hInstance,    // current instance 
                  int     nCmdShow)     // param for first ShowWindow() call
{
    HWND hWnd;

                                                                           
    /* Save the instance handle in global variable, which will be used */
    /* in many subsequence calls from this application to Windows      */
    hInst = hInstance;
    hHourGlass = LoadCursor((HANDLE) NULL, IDC_WAIT);

    /* Create a main window for this application instance */
    hWnd = CreateWindow("GenericWClass",
                        "RPC Sample Application",  
                        WS_OVERLAPPEDWINDOW,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        CW_USEDEFAULT,
                        (HWND) NULL,
                        (HMENU) NULL,
                        hInstance,
                        (LPVOID) NULL
                        );

    /* If the window cannot be created, return "failure" */
    if (!hWnd)
        return(FALSE);

    /* Initialize RPC binding data */
    strcpy(pszProtocolSequence, DEFAULT_PROT_SEQ); 
    strcpy(pszEndpoint, DEFAULT_ENDPOINT);     
    pszNetworkAddress[0] = '\0';

    /* Bind client to server */
    fBound = FALSE;

    /* Initialize the parameter to the remote procedure Sleep() */
    cWaitSec = DEFAULT_WAIT;  

    /* Initialize the parameters to RpcWinSetYieldInfo() */
    fCustomYield = FALSE;         // FALSE = std yield 
    dwOtherInfo = (DWORD) NULL;   // NULL = RPC-supplied dialog box 

    RpcWinSetYieldInfo(hWnd,                  // handle
                       fCustomYield,          // standard or custom yield?
                       WM_RPC_YIELD_MESSAGE,  // 0 = no message is posted
                       dwOtherInfo);          // depends on fCustomYield value

    /* Make the window visible, update its client area, and return "success" */
    ShowWindow(hWnd, nCmdShow);
    UpdateWindow(hWnd);

    return(TRUE);
}


/****************************************************************************

    FUNCTION: MainWndProc(HWND, UINT, WPARAM, LPARAM)

    PURPOSE:  Processes messages

    MESSAGES: WM_COMMAND  - application menu (About dialog box)
              WM_DESTROY  - destroy window

    COMMENTS: Based on the Windows generic sample.
              Several new menu items are added to demonstrate the
              RpcWinSetYieldInfo function.

              The "Bind" menu is associated with the "GetBindInfo"
              dialog box function.  GetBindInfo allows the user to
              set the parameters for the RpcStringBindingCompose and
              RpcBindingFromStringBinding functions.  A flag, fBound,
              keeps track of whether the client application is bound
              to a remote server.  If this flag indicates that the
              client application is not bound to the server, it calls
              the "Bind" utility function to call the
              RPC API functions that establish the binding.

              The "Yield" menu is associated with the "GetYieldInfo"
              dialog box function. GetYieldInfo allows the user to
              select the yielding model:  standard yield with an
              RPC run-time library-supplied dialog box; standard
              yield with a user-supplied dialog box; or custom yield.
              These methods are described in detail in the documentation
              for the RpcWinSetYieldInfo function.

****************************************************************************/

long APIENTRY MainWndProc(HWND hWnd,       // window handle   
                          UINT message,    // type of message 
                          WPARAM wParam,   // additional information
                          LPARAM lParam    // additional information  
                          )
{
    DLGPROC lpProc;    // pointer to the dialog box function 

    /* copy the window handle for CustomYield() */
    hWndMain = hWnd;  

    switch (message) {

    case WM_CREATE:
        /* Win 3.x is client-only; force user to specify server */
        PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L);  
        break;

    case WM_COMMAND:
        switch (wParam) {

        case IDM_ABOUT:
            lpProc = MakeProcInstance(About, hInst);
            DialogBox(hInst,
                      "AboutBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_BIND:
            lpProc = MakeProcInstance(GetBindInfo, hInst);
            DialogBox(hInst,
                      "BindBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_WAIT:
            lpProc = MakeProcInstance(GetWaitInfo, hInst);
            DialogBox(hInst,
                      "WaitBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_YIELD:
            lpProc = MakeProcInstance(GetYieldInfo, hInst);
            DialogBox(hInst,
                      "YieldBox",
                      hWnd,
                      lpProc);
            FreeProcInstance(lpProc);
            break;

        case IDM_EXIT:
            DestroyWindow(hWnd);                                
            if (fBound == TRUE) {
                RpcTryExcept {
                    Shutdown();     // shut down the server 
                }
                RpcExcept(1) {
                    MessageBox(hWnd,
                               EXCEPT_MSG,
                               "Remote Procedure Call",
                               MB_ICONINFORMATION);
                }
                RpcEndExcept
            }
            break;

        default:
            return(DefWindowProc(hWnd, message, wParam, lParam));

        }
        break;

    case WM_RPC_YIELD_MESSAGE:   // signals beginning or end of yield period
        if (wParam == 0)
            SetWindowText(hWnd, YIELD_END_MSG);
        else if (wParam == 1)
            SetWindowText(hWnd, YIELD_START_MSG);
        break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    default:  // passes it on if unprocessed
        return(DefWindowProc(hWnd, message, wParam, lParam));

    }

    return(0L);
}


/****************************************************************************

    FUNCTION: About(HWND, unsigned, WORD, LONG)

    PURPOSE:  "About" dialog box

    COMMENTS: 

****************************************************************************/

BOOL APIENTRY About(HWND hDlg,
                    UINT message,
                    UINT wParam,
                    LONG lParam)
{
    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:
        return(TRUE);

    case WM_COMMAND:
        if (wParam == IDOK || wParam == IDCANCEL) {
            EndDialog(hDlg, TRUE);
            return(TRUE);
        }
        break;

    }

    return(FALSE);
}


/****************************************************************************

    FUNCTION: GetBindInfo(HWND, unsigned, WORD, LONG)

    PURPOSE:  Collect components of string binding;
              protocol sequence, network address, endpoint

    COMMENTS:

****************************************************************************/

BOOL APIENTRY GetBindInfo(HWND hDlg,
                          UINT message,
                          UINT wParam,
                          LONG lParam)
{
    HCURSOR hOld;

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:  // fill in dialog's edit boxes 
        SetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint);
        SetDlgItemText(hDlg, IDD_PROT_SEQ, pszProtocolSequence);
        SetDlgItemText(hDlg, IDD_NET_ADDR, pszNetworkAddress);
        return(TRUE);

    case WM_COMMAND:
        switch(wParam) {

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            return(TRUE);

        case IDOK:
            GetDlgItemText(hDlg, IDD_PROT_SEQ, pszProtocolSequence, MAXPROTSEQ);
            GetDlgItemText(hDlg, IDD_ENDPOINT, pszEndpoint, PATHLEN);
            GetDlgItemText(hDlg, IDD_NET_ADDR, pszNetworkAddress, NETLEN);

            hOld = SetCursor(hHourGlass);
            if (Bind(hDlg) != RPC_S_OK) {  // bind to server 
                EndDialog(hDlg, FALSE);
                return(FALSE);
            }

            SetCursor(hOld);
            EndDialog(hDlg, TRUE);
            return(TRUE);
       }
    }

    return(FALSE);
}


/****************************************************************************

    FUNCTION: GetWaitInfo(HWND, unsigned, WORD, LONG)

    PURPOSE:  Prompt user for the parameter to the remote call,
              then make the remote procedure call.

    COMMENTS:

****************************************************************************/

BOOL APIENTRY GetWaitInfo(HWND hDlg,     // window handle of the dialog box 
                          UINT message,  // type of message                 
                          UINT wParam,   // message-specific information  
                          LONG lParam)
{
    int fError;

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:
        SetDlgItemInt(hDlg, IDD_WAITTIME, cWaitSec, FALSE);
        return(TRUE);

    case WM_COMMAND:
        switch(wParam) {

        case IDCANCEL:
            EndDialog(hDlg, FALSE);
            fCancel = TRUE;         
            return(TRUE);

        case IDOK:
            cWaitSec = GetDlgItemInt(hDlg, IDD_WAITTIME, &fError, FALSE);
            if (cWaitSec <= 0)            // check for valid entry 
                cWaitSec = DEFAULT_WAIT;  // set an appropriate value 

            RpcTryExcept {
                YieldProc(cWaitSec);      // make the remote procedure call 
            }
            RpcExcept(1) {
                unsigned long ulCode;
                char pszFail[MSGLEN];
                
                ulCode = RpcExceptionCode();           
                if (ulCode != RPC_S_CALL_FAILED) {
                    sprintf(pszFail, "%s (0x%x)\n", EXCEPT_MSG, ulCode);
                    MessageBox(hDlg,
                               pszFail,
                               "Remote Procedure Call",
                               MB_ICONINFORMATION);
                }
            }
            RpcEndExcept

            EndDialog(hDlg, TRUE);
            return(TRUE);
        }
    }

    return(FALSE);
}


/****************************************************************************

    FUNCTION: CustomYield(void)

    PURPOSE:  Message handler during custom yield

    MESSAGES: WM_RPC_YIELD_MESSAGE - end of yield message
              WM_COMMAND           - Input received

    COMMENTS: The callback function must retrieve messages from the
              message queue. 

              The function must return TRUE when the RPC operation 
              has completed. The function must return FALSE when the 
              user cancels the RPC operation.

****************************************************************************/

BOOL FAR PASCAL __export CustomYield(void)
{
    MSG msg;

    fCancel = FALSE;

    while (TRUE) {  // message processing
        GetMessage(&msg, (HWND)NULL, 0, 0);

        if (msg.message == WM_RPC_YIELD_MESSAGE)
            return(TRUE);  // RPC operation is complete 

        if (fCancel == TRUE) 
            return(FALSE);

        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    return(TRUE);
}


/****************************************************************************

    FUNCTION: GetYieldInfo(HWND, unsigned, WORD, LONG)

    PURPOSE:  Check radio buttons to see which yield method
              was selected by the user, then set the parameters
              to RpcWinSetYieldInfo and call RpcWinSetYieldInfo.

    COMMENTS:

****************************************************************************/

BOOL APIENTRY GetYieldInfo(HWND hDlg,     // window handle of the dialog box 
                           UINT message,  // type of message                 
                           UINT wParam,   // message-specific information   
                           LONG lParam)
{
    int fCheck;   

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:  // message: initialize dialog box 
        if (fCustomYield)
            CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_CUSTOM);
        else if (dwOtherInfo != (DWORD) NULL)
            CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_STD_USER);
        else
            CheckRadioButton(hDlg, IDD_STD_RPC, IDD_CUSTOM, IDD_STD_RPC);
        return(TRUE);

    case WM_COMMAND:     // message: received a command 
        switch(wParam) {

            case IDCANCEL:   // System menu close command? 
                EndDialog(hDlg, FALSE);
                return(TRUE);

            case IDOK: 
                /* which radio button is checked: Custom yield? */
                fCheck = (int) SendDlgItemMessage(hDlg, 
                                                  IDD_CUSTOM, 
                                                  BM_GETCHECK, 
                                                  0,
                                                  0);
                if (fCheck == TRUE) {
                    fCustomYield = TRUE;
                    dwOtherInfo = (DWORD) MakeProcInstance(CustomYield, hInst);
                }
                else { 
                    fCustomYield = FALSE;

                    /* Standard yield, user-supplied dialog? */
                    fCheck = (int) SendDlgItemMessage(hDlg, 
                                                      IDD_STD_USER, 
                                                      BM_GETCHECK,
                                                      0, 
                                                      0);  
                    if (fCheck == TRUE) {
                        HRSRC hrsrc;
    
                        hrsrc = FindResource(hInst,
                                             "USERYIELDBOX",
                                             RT_DIALOG);
                        dwOtherInfo = LoadResource(hInst, hrsrc);
                    }
                    else {
                        /* Assume standard-yield, rpc-supplied dialog box */
                        dwOtherInfo = (DWORD) NULL; 
                    }
                }

                RpcWinSetYieldInfo(hWndMain,  
                                   fCustomYield,   
                                   WM_RPC_YIELD_MESSAGE, 
                                   dwOtherInfo);

                EndDialog(hDlg, TRUE);
                return(TRUE);
        }  
    } 

    return(FALSE);
}


/****************************************************************************

    FUNCTION: midl_user_allocate(size_t)

    PURPOSE:  Allocate memory as needed by the RPC runtime library

    COMMENTS: The stubs or runtime libraries may need to allocate memory.
              By convention, they call a user-specified function named
              midl_user_allocate.  In this application, no memory
              management is needed, so a dummy function is provided.

****************************************************************************/

void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
{
    UNREFERENCED_PARAMETER(len);
    return(NULL);  // no memory management required
}

/****************************************************************************

    FUNCTION: midl_user_free(void *)

    PURPOSE:  Free memory as needed by the RPC runtime library

    COMMENTS: The stubs or runtime libraries may need to free memory.
              By convention, they call a user-specified function named
              midl_user_free.  In this application, no memory allocation
              is needed so a dummy function is provided.

****************************************************************************/

void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
{
    UNREFERENCED_PARAMETER(ptr);
    return;    // no memory management required
}


/****************************************************************************

    FUNCTION: Bind(HWND)

    PURPOSE:  Make RPC API calls to bind to the server application

    COMMENTS: The binding calls are made from InitInstance() and whenever
              the user changes the protocol sequence, network address, or
              endpoint. If the bind operation is successful, the global
              flag fBound is set to TRUE.

              The global flag fBound is used to determine whether to call
              the RPC API function RpcBindingFree.

****************************************************************************/

RPC_STATUS Bind(HWND hWnd)
{
    RPC_STATUS status;
    char pszFail[MSGLEN];  

    if (fBound == TRUE) {  // unbind only if bound   
        status = RpcStringFree(&pszStringBinding);  // remote calls done; unbind
        if (status) {
            MessageBox(hWnd, "RpcStringFree failed", "RPC Error", MB_ICONSTOP);
            return(status);
        }

        status = RpcBindingFree(&hYield);  // remote calls done; unbind
        if (status) {
            MessageBox(hWnd, "RpcBindingFree failed", "RPC Error", MB_ICONSTOP);
            return(status);
        }
        
        fBound = FALSE;  // unbind successful; reset flag 
    }

    status = RpcStringBindingCompose(pszUuid,
                                     pszProtocolSequence,
                                     pszNetworkAddress,
                                     pszEndpoint,
                                     pszOptions,
                                     &pszStringBinding);
    if (status) {
        sprintf(pszFail, "RpcStringBindingCompose failed: (0x%x)\nNetwork Address = %s\n",
                status, pszNetworkAddress);
        MessageBox(hWnd,
                   pszFail, 
                   "RPC Runtime Error", 
                   MB_ICONEXCLAMATION);
        return(status);
    }

    status = RpcBindingFromStringBinding(pszStringBinding,
                                         &hYield);
    if (status) {
        sprintf(pszFail, "RpcBindingFromStringBinding failed: (0x%x)\nString = %s\n",
                status, pszStringBinding);
        MessageBox(hWnd,
                   pszFail, 
                   "RPC Runtime Error", 
                   MB_ICONEXCLAMATION);
        return(status);
    }

    fBound = TRUE;  // bind successful; reset flag 

    return(status);
}


/**** end yieldc.c ****/
