/****************************************************************************
                    Microsoft RPC Version 1.0
                 Copyright Microsoft Corp. 1992
                        whello Example

    FILE:       whelloc.c

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

    FUNCTIONS:  WinMain()         - call initialization function, 
                                    process message loop
                InitApplication() - initializ window data and register window
                InitInstance()    - save instance handle and create main window
                MainWndProc()     - process messages

                About()    - process messages for "About" dialog box
                Server()   - process messages for "Server" dialog box
                Endpoint() - process messages for "Endpoint" dialog box
                Send()     - process messages for "Send" dialog box; 
                             sends on OK

                midl_user_allocate() - memory allocation function needed by RPC
                midl_user_free()     - memory free function needed by RPC

    COMMENTS:   Windows version of the "Hello, world" example.

                Windows can have several copies of your application 
                running at the same time.  The variable hInst keeps 
                track of which instance the application is so that 
                processing will be to the correct window.

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

#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <windowsx.h>       // select between win16 or win32      
#include <string.h>
#include "whello.h"         // header file generated by MIDL compiler
#include "whelloc.h"        // client-specific header file

/* global data */

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

int fBound = FALSE;     // flag indicates whether client bound to server

HANDLE hInst;           // current instance  
HCURSOR hHourGlass;     // during calls to RPC API functions 


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

    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 (open/icon) 
{
    MSG msg;              
    
    UNREFERENCED_PARAMETER(lpCmdLine);

    if (!hPrevInstance)                   // Other instances of app running? 
        if (!InitApplication(hInstance))  // Initialize shared things 
            return(FALSE);               // Exits 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);    // Translates virtual key codes   
        DispatchMessage(&msg);     // Dispatches message to window   
    }

    return(msg.wParam);            // Returns 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 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, "HelloIcon");  
    wc.hCursor = LoadCursor(0, 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 static 
              variable and create and display the main program window.  
        
****************************************************************************/

BOOL InitInstance(HANDLE hInstance,    // Current instance identifier.      
                  int    nCmdShow)     // Param for first ShowWindow() call.
{
    HWND hWnd;    // Main window handle.         

    /* Save the instance handle in static variable, which will be used in  */
    /* many subsequence calls from this application to Windows.            */
    hInst = hInstance;
    hHourGlass = LoadCursor(0, IDC_WAIT);
    
    /* Create a main window for this application instance.  */
    hWnd = CreateWindow("GenericWClass",           // See RegisterClass() call.
                        "RPC Sample Application",  // Text for window title bar.
                        WS_OVERLAPPEDWINDOW,       // Window style.             
                        CW_USEDEFAULT,    // Default horizontal position.     
                        CW_USEDEFAULT,    // Default vertical position.  
                        CW_USEDEFAULT,    // Default width.           
                        CW_USEDEFAULT,    // Default height.                  
                        (HWND) NULL,      // Overlapped windows have no parent. 
                        (HMENU) NULL,     // Use the window class menu.   
                        hInstance,        // This instance owns this window.  
                        (LPVOID) NULL     // Pointer not needed.           
                        );

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

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

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

    return(TRUE);                // Return the value from PostQuitMessage 
}


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

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

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

    COMMENTS: To process the IDM_ABOUT message, call MakeProcInstance() 
              to get the current instance address of the About() function.  
              Then call Dialog box which will create the box according to 
              the information in your generic.rc file and turn control 
              over to the About() function.  When it returns, free the 
              intance address.

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

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 

    switch (message) {

    case WM_CREATE:

#ifdef WIN16
        PostMessage(hWnd, WM_COMMAND, IDM_SERVER, 0L);  // force server spec
#else
        PostMessage(hWnd, WM_COMMAND, IDM_BIND, 0L);    // bind to server
#endif
        break;

    case WM_COMMAND:    // message: command from application menu 
        switch (wParam) {

        case IDM_BIND:
            if (Bind(hWnd) != RPC_S_OK)
                PostMessage(hWnd, WM_DESTROY, 0, 0L);
            break;

        case IDM_ABOUT:
            lpProc = MakeProcInstance(About, hInst);
            DialogBox(hInst,        // current instance     
                      "AboutBox",   // resource to use      
                      hWnd,         // parent handle        
                      lpProc);      // About() instance address 
            FreeProcInstance(lpProc);
            break;

        case IDM_SERVER:
            lpProc = MakeProcInstance(Server, hInst);      
            DialogBox(hInst,        // current instance      
                      "ServerBox",  // resource to use     
                      hWnd,         // parent handle       
                      lpProc);      // Server instance address 
            FreeProcInstance(lpProc);
            break;

        case IDM_ENDPOINT:
            lpProc = MakeProcInstance(Endpoint, hInst);
            DialogBox(hInst,        // current instance   
                      "EndpointBox",// resource to use    
                      hWnd,         // parent handle      
                      lpProc);      // Server instance address
            FreeProcInstance(lpProc);
            break;

        case IDM_SEND:
            lpProc = MakeProcInstance(Send, hInst);
            DialogBox(hInst,        // current instance      
                      "SendBox",    // resource to use          
                      hWnd,         // parent handle        
                      lpProc);      // Server instance address 
            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:        // Let Windows process it      
            return(DefWindowProc(hWnd, message, wParam, lParam));

        }
        break;

    case WM_DESTROY:    // message: window being destroyed 
        PostQuitMessage(0);
        break;

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

    }

    return(0);
}


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

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

    PURPOSE:  Processes messages for "Server" dialog box

    MESSAGES: WM_INITDIALOG - initialize dialog box
              WM_COMMAND    - Input received

    COMMENTS: No initialization is needed for this particular dialog box, 
              but TRUE must be returned to Windows.

              Wait for user to click on "Ok" button, then close the dialog box.

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

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

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box 
        SetDlgItemText((HANDLE)hDlg, IDD_SERVERNAME, pszNetworkAddress);
        return(TRUE);

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

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

        case IDOK:         // "OK" box selected?      
            GetDlgItemText(hDlg, IDD_SERVERNAME, pszNetworkAddress, UNCLEN);

            /* pszNetworkAddress must start with two backslashes */
            if (pszNetworkAddress[0] != '\0' && 
                strncmp(pszNetworkAddress, "\\\\", 2)) {

                unsigned char oldNetAddr[UNCLEN+1];

                strcpy(oldNetAddr, pszNetworkAddress);
                sprintf(pszNetworkAddress, "\\\\%s", oldNetAddr);
            }

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

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

        }   

    }   

    return(FALSE);  // Didn't process a message   
}


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

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

    PURPOSE:  Processes messages for "About" dialog box

    MESSAGES: WM_INITDIALOG - initialize dialog box
              WM_COMMAND    - Input received

    COMMENTS: No initialization is needed for this particular dialog box, 
              but TRUE must be returned to Windows.

              Wait for user to click on "Ok" button, then close the dialog box.

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

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

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box 
        return(TRUE);

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

    }

    return(FALSE);  // Didn't process a message  
}


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

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

    PURPOSE:  Processes messages for "Endpoint" dialog box

    MESSAGES: WM_INITDIALOG - initialize dialog box
              WM_COMMAND    - Input received

    COMMENTS: No initialization is needed for this particular dialog box, 
              but TRUE must be returned to Windows.

              Wait for user to click on "Ok" button, then close the dialog box.

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

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

    UNREFERENCED_PARAMETER(lParam);

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box 
        SetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint);
        return(TRUE);

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

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

        case IDOK:            
            GetDlgItemText(hDlg, IDD_ENDPOINTNAME, pszEndpoint, PATHLEN);

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

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

        }  

    } 

    return(FALSE);  // Didn't process a message    
}


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

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

    PURPOSE:  Processes messages for "Send" dialog box

    MESSAGES: WM_INITDIALOG - initialize dialog box
              WM_COMMAND    - Input received

    COMMENTS: No initialization is needed for this particular dialog box, 
              but TRUE must be returned to Windows.

              Wait for user to click on "Ok" button, then close the dialog box.

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

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

    switch (message) {

    case WM_INITDIALOG:    // message: initialize dialog box 
        SetDlgItemText(hDlg, IDD_MESSAGE, pszString);
        return(TRUE);

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

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

        case IDOK:      
            GetDlgItemText(hDlg, IDD_MESSAGE, pszString, MSGLEN);

            RpcTryExcept {
                HelloProc(pszString);  // make call with user message 
            }
            RpcExcept(1) {
                char pszFail[MSGLEN];

                sprintf(pszFail, "%s (0x%x)\n", EXCEPT_MSG, RpcExceptionCode());
                MessageBox(hDlg,
                           pszFail,
                           "Remote Procedure Call",
                           MB_ICONINFORMATION);
            }
            RpcEndExcept

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

        } 

    }  

    return(FALSE);  // Didn't process a message    
}


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

    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_API 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_API 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 server name 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);
        if (status) {
            MessageBox(hWnd, "RpcStringFree failed", "RPC Error", MB_ICONSTOP);
            return(status);
        }

        status = RpcBindingFree(&hWHello);  
        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,
                                         &hWHello);
    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 whelloc.c ****/
