YIELDC.C


/****************************************************************************
Microsoft RPC Version 2.0
Copyright Microsoft Corp. 1992, 1993, 1994- 1996
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 ****/