465 lines
15 KiB
C
465 lines
15 KiB
C
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
|
|
// FILE: AsyncRPCc.c
|
|
//
|
|
// USAGE: AsyncRPCc /?
|
|
//
|
|
// PURPOSE: Client side main program
|
|
//
|
|
// COMMENTS: The Microsoft RPC AsyncRPC sample program demonstrates
|
|
// the use of the [async] attribute and the related asynchronous
|
|
// RPC API functions. The client will first call the asynchronous
|
|
// function. The time it will take to run on the server side is
|
|
// specified by the user and is passed by the asynchronous function
|
|
// to the server as one of its parameters (asynchronous_delay).
|
|
// The non-asynchronous routine is called next while the asynchronous one
|
|
// is still running on the server side. The asynchronous routine can be
|
|
// cancelled by supplying the command line option "-c".
|
|
//
|
|
// The user can choose any of the following three types of asynchronous
|
|
// notification methods: event, iocp, or apc.
|
|
//
|
|
// For more information, see MSDN online reference.
|
|
//
|
|
//
|
|
|
|
#include <stdlib.h>
|
|
#include <stdio.h>
|
|
#include <ctype.h>
|
|
#include "AsyncRPC.h" // header file generated by MIDL compiler
|
|
#include "spn.h"
|
|
|
|
#define DEFAULT_NOTIFICATION "event"
|
|
#define DEFAULT_MESSAGE "message from client"
|
|
|
|
void Usage(char * pszProgramName)
|
|
{
|
|
fprintf_s(stderr, "USAGE: %s\n", pszProgramName);
|
|
fprintf_s(stderr, " -p protocol_sequence\n");
|
|
fprintf_s(stderr, " -e endpoint\n");
|
|
fprintf_s(stderr, " -a server principal name\n");
|
|
fprintf_s(stderr, " -n network_address\n");
|
|
fprintf_s(stderr, " -o network_options\n");
|
|
fprintf_s(stderr, " -m notification_method\n");
|
|
fprintf_s(stderr, " -d asynchronous_delay\n");
|
|
fprintf_s(stderr, " -c\n");
|
|
fprintf_s(stderr, " -s\n\n");
|
|
|
|
fprintf_s(stderr, "DESCRIPTION:\n");
|
|
fprintf_s(stderr, " -p protocol_sequence:\n"
|
|
" Specifies the protocol sequence (default is %s).\n",
|
|
DEFAULT_PROTOCOL_SEQUENCE);
|
|
fprintf_s(stderr, " -e endpoint:\n"
|
|
" Specifies the endpoint (default is %s).\n",
|
|
DEFAULT_ENDPOINT);
|
|
fprintf_s(stderr, " -a server principal name:\n"
|
|
" Specifies the server principal name (default is NULL).\n");
|
|
fprintf_s(stderr, " -n network_address:\n"
|
|
" Specifies the host name or IP address (default is localhost).\n");
|
|
fprintf_s(stderr, " -o network_options:\n"
|
|
" Specified the network options.\n");
|
|
fprintf_s(stderr, " -m notification_method:\n"
|
|
" Specifies the notification method used by the asynchronous function.\n"
|
|
" Supported method: event, iocp, or apc (default is %s).\n",
|
|
DEFAULT_NOTIFICATION);
|
|
fprintf_s(stderr, " -d asynchronous_delay:\n"
|
|
" Specifies the number of seconds how long the asynchronous function\n"
|
|
" will take ro run on the server (default is %u seconds).\n",
|
|
DEFAULT_ASYNC_DELAY);
|
|
fprintf_s(stderr, " -c:\n"
|
|
" Indicates the asynchronous call will be cancelled.\n");
|
|
fprintf_s(stderr, " -s:\n"
|
|
" Indicates the server will be shutdown.\n");
|
|
exit(1);
|
|
}
|
|
|
|
|
|
void RPC_ENTRY APCNotificationRoutine(PRPC_ASYNC_STATE pAsync,
|
|
void *Context,
|
|
RPC_ASYNC_EVENT Event);
|
|
|
|
|
|
void __cdecl main(int argc, char **argv)
|
|
{
|
|
unsigned char *pszUuid = NULL;
|
|
unsigned char *pszProtocolSequence = DEFAULT_PROTOCOL_SEQUENCE;
|
|
unsigned char *pszEndpoint = DEFAULT_ENDPOINT;
|
|
unsigned char *pszSpn = NULL;
|
|
unsigned char *pszNetworkAddress = NULL;
|
|
unsigned char *pszOptions = NULL;
|
|
unsigned char *pszStringBinding = NULL;
|
|
unsigned char *pszNotificationMethod = DEFAULT_NOTIFICATION;
|
|
unsigned char *pszMessage = DEFAULT_MESSAGE;
|
|
unsigned long nAsychDelay = DEFAULT_ASYNC_DELAY;
|
|
unsigned long ulCode;
|
|
unsigned long nAsychTimeOut;
|
|
BOOL bAsychCancel = FALSE;
|
|
BOOL bShutDownServer = FALSE;
|
|
BOOL bIsUDP = FALSE;
|
|
BOOL bCallFinished = TRUE;
|
|
BOOL bAsyncCallSuccess = TRUE;
|
|
int i, nReply;
|
|
RPC_ASYNC_STATE Async;
|
|
RPC_STATUS status;
|
|
RPC_SECURITY_QOS SecQos;
|
|
DWORD dwResult;
|
|
|
|
for (i = 1; i < argc; i++)
|
|
{
|
|
if ((*argv[i] == '-') || (*argv[i] == '/'))
|
|
{
|
|
switch (tolower(*(argv[i]+1)))
|
|
{
|
|
case 'p':
|
|
pszProtocolSequence = argv[++i];
|
|
break;
|
|
case 'e': // endpoint
|
|
pszEndpoint = argv[++i];
|
|
break;
|
|
case 'n': // network address
|
|
pszNetworkAddress = argv[++i];
|
|
break;
|
|
case 'a':
|
|
pszSpn = argv[++i];
|
|
break;
|
|
case 'o':
|
|
pszOptions = argv[++i];
|
|
break;
|
|
case 'm':
|
|
pszNotificationMethod = argv[++i];
|
|
break;
|
|
case 'd':
|
|
if (argv[i+1][0] != '-')
|
|
nAsychDelay = atol(argv[++i]);
|
|
else
|
|
Usage(argv[0]);
|
|
break;
|
|
case 'c':
|
|
bAsychCancel = TRUE;
|
|
break;
|
|
case 's':
|
|
bShutDownServer = TRUE;
|
|
break;
|
|
case 'h':
|
|
case '?':
|
|
default:
|
|
Usage(argv[0]);
|
|
}
|
|
}
|
|
else
|
|
Usage(argv[0]);
|
|
}
|
|
|
|
if ((pszProtocolSequence == NULL) || (pszEndpoint == NULL) ||
|
|
(pszNotificationMethod == NULL) || (pszMessage == NULL) ||
|
|
(nAsychDelay < 0))
|
|
{
|
|
Usage(argv[0]);
|
|
}
|
|
|
|
// Use a convenience function to concatenate the elements of
|
|
// the string binding into the proper sequence.
|
|
status = RpcStringBindingCompose(pszUuid,
|
|
pszProtocolSequence,
|
|
pszNetworkAddress,
|
|
pszEndpoint,
|
|
pszOptions,
|
|
&pszStringBinding);
|
|
printf_s("RpcStringBindingCompose returned 0x%x\n", status);
|
|
printf_s("pszStringBinding = %s\n", pszStringBinding);
|
|
|
|
if (status)
|
|
exit(status);
|
|
|
|
// Set the binding handle that will be used to bind to the server.
|
|
status = RpcBindingFromStringBinding(pszStringBinding,
|
|
&AsyncRPC_ClientIfHandle);
|
|
printf_s("RpcBindingFromStringBinding returned 0x%x\n", status);
|
|
if (status)
|
|
exit(status);
|
|
|
|
status = RpcAsyncInitializeHandle(&Async, sizeof(RPC_ASYNC_STATE));
|
|
printf_s("RpcAsyncInitializeHandle returned 0x%x\n", status);
|
|
if (status)
|
|
exit(status);
|
|
|
|
Async.UserInfo = NULL;
|
|
|
|
// user did not specify spn, construct one.
|
|
if (pszSpn == NULL) {
|
|
MakeSpn(&pszSpn);
|
|
}
|
|
|
|
// Set the quality of service on the binding handle
|
|
SecQos.Version = RPC_C_SECURITY_QOS_VERSION_1;
|
|
SecQos.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH;
|
|
SecQos.IdentityTracking = RPC_C_QOS_IDENTITY_DYNAMIC;
|
|
SecQos.ImpersonationType = RPC_C_IMP_LEVEL_IDENTIFY;
|
|
|
|
// Set the security provider on binding handle
|
|
status = RpcBindingSetAuthInfoEx(AsyncRPC_ClientIfHandle,
|
|
pszSpn,
|
|
RPC_C_AUTHN_LEVEL_PKT_PRIVACY,
|
|
RPC_C_AUTHN_GSS_NEGOTIATE,
|
|
NULL,
|
|
RPC_C_AUTHZ_NONE,
|
|
&SecQos);
|
|
|
|
printf_s("RpcBindingSetAuthInfoEx returned 0x%x\n", status);
|
|
if (status) {
|
|
exit(status);
|
|
}
|
|
|
|
// Detemine what type of notification method should be used
|
|
if (_stricmp(pszNotificationMethod, "apc") == 0)
|
|
{
|
|
Async.NotificationType = RpcNotificationTypeApc;
|
|
if (_stricmp(pszProtocolSequence, "ncadg_ip_udp") == 0)
|
|
bIsUDP = TRUE;
|
|
}
|
|
else if (_stricmp(pszNotificationMethod, "iocp") == 0)
|
|
Async.NotificationType = RpcNotificationTypeIoc;
|
|
else // "event"
|
|
Async.NotificationType = RpcNotificationTypeEvent;
|
|
|
|
// Fill in the RPC_ASYNC_STATE structure
|
|
switch (Async.NotificationType)
|
|
{
|
|
case RpcNotificationTypeApc:
|
|
Async.u.APC.NotificationRoutine = (PFN_RPCNOTIFICATION_ROUTINE)APCNotificationRoutine;
|
|
Async.u.APC.hThread = 0;
|
|
// Using APC and UDP will potentially cause SleepEx() to
|
|
// block forever. We use a user defined flag to check
|
|
// whether the call is completed or not.
|
|
if (bIsUDP)
|
|
Async.UserInfo = (void*)&bCallFinished;
|
|
break;
|
|
case RpcNotificationTypeIoc:
|
|
Async.u.IOC.hIOPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0, 0);
|
|
if (Async.u.IOC.hIOPort == NULL)
|
|
{
|
|
printf_s("CreateIoCompletionPort() failed: %d\n", GetLastError());
|
|
exit(1);
|
|
}
|
|
break;
|
|
default: // case RpcNotificationTypeEvent:
|
|
Async.u.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if (Async.u.hEvent == 0)
|
|
{
|
|
printf_s("CreateEvent() failed: %d\n", GetLastError());
|
|
|
|
exit(1);
|
|
}
|
|
break;
|
|
}
|
|
|
|
// Call an asynchronous RPC rountine here
|
|
RpcTryExcept
|
|
{
|
|
printf_s("\nCalling the remote procedure 'AsyncFunc'\n");
|
|
AsyncFunc(&Async, AsyncRPC_ClientIfHandle, nAsychDelay);
|
|
}
|
|
RpcExcept(1)
|
|
{
|
|
ulCode = RpcExceptionCode();
|
|
printf_s("AsyncFunc: Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
|
|
|
|
}
|
|
RpcEndExcept
|
|
|
|
// Call a synchronous routine while
|
|
// the asynchronous routine is still running
|
|
RpcTryExcept
|
|
{
|
|
printf_s("\nCalling the remote procedure 'NonAsyncFunc'\n");
|
|
NonAsyncFunc(AsyncRPC_ClientIfHandle, pszMessage);
|
|
fprintf_s(stderr, "You see, while 'AsyncFunc' is running asyncronously,\n"
|
|
"we still can send message to the server in the mean time.\n\n");
|
|
}
|
|
RpcExcept(( ( (RpcExceptionCode() != STATUS_ACCESS_VIOLATION) &&
|
|
(RpcExceptionCode() != STATUS_DATATYPE_MISALIGNMENT) &&
|
|
(RpcExceptionCode() != STATUS_PRIVILEGED_INSTRUCTION) &&
|
|
(RpcExceptionCode() != STATUS_BREAKPOINT) &&
|
|
(RpcExceptionCode() != STATUS_STACK_OVERFLOW) &&
|
|
(RpcExceptionCode() != STATUS_IN_PAGE_ERROR) &&
|
|
(RpcExceptionCode() != STATUS_GUARD_PAGE_VIOLATION)
|
|
)
|
|
? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH ))
|
|
{
|
|
ulCode = RpcExceptionCode();
|
|
|
|
printf_s("NonAsyncFunc: Runtime reported exception 0x%lx = %ld\n", ulCode, ulCode);
|
|
|
|
}
|
|
RpcEndExcept
|
|
|
|
if (bAsychCancel)
|
|
{
|
|
// nonabortive cancel
|
|
status = RpcAsyncCancelCall(&Async, FALSE);
|
|
printf_s("RpcAsyncCancelCall returned 0x%x\n", status);
|
|
if (status)
|
|
exit(status);
|
|
}
|
|
|
|
// should plus the synchronous call
|
|
// time if it also takes long
|
|
nAsychTimeOut = nAsychDelay * 1000 * 3;
|
|
|
|
// Wait for asynchronous notification
|
|
printf_s("Waiting for asynchronous call to finish ...\n");
|
|
switch (Async.NotificationType)
|
|
{
|
|
case RpcNotificationTypeApc:
|
|
// Over ncadg_ip_udp, the RPC calls are executed sequentially on the server side
|
|
// according to the order they are called by the client, regardless of their
|
|
// async attributes. When the client is using APC, we need to "manually" check
|
|
// whether AsyncFunc() is finished or not.
|
|
if (bIsUDP)
|
|
{
|
|
if (!bCallFinished)
|
|
{
|
|
printf_s("Asynchronous call failed\n");
|
|
bAsyncCallSuccess = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwResult = SleepEx(nAsychTimeOut, TRUE);
|
|
if (dwResult != WAIT_IO_COMPLETION)
|
|
{
|
|
if (dwResult == 0)
|
|
printf_s("SleepEx() is timed out\n");
|
|
else
|
|
printf_s("SleepEx() failed: %d\n", dwResult);
|
|
|
|
bAsyncCallSuccess = FALSE;
|
|
}
|
|
}
|
|
break;
|
|
case RpcNotificationTypeIoc:
|
|
if (!GetQueuedCompletionStatus(Async.u.IOC.hIOPort,
|
|
&Async.u.IOC.dwNumberOfBytesTransferred,
|
|
&Async.u.IOC.dwCompletionKey,
|
|
&Async.u.IOC.lpOverlapped,
|
|
nAsychTimeOut))
|
|
{
|
|
dwResult = GetLastError();
|
|
|
|
if (dwResult == WAIT_TIMEOUT)
|
|
printf_s("GetQueuedCompletionStatus() is timed out\n");
|
|
else
|
|
printf_s("GetQueuedCompletionStatus() failed: %d\n", dwResult);
|
|
|
|
bAsyncCallSuccess = FALSE;
|
|
}
|
|
break;
|
|
default: // case RpcNotificationTypeEvent:
|
|
dwResult = WaitForSingleObject(Async.u.hEvent, nAsychTimeOut);
|
|
if (dwResult == WAIT_FAILED)
|
|
{
|
|
printf_s("WaitForSingleObject() failed: %d\n", GetLastError());
|
|
bAsyncCallSuccess = FALSE;
|
|
}
|
|
else if (dwResult == WAIT_TIMEOUT)
|
|
{
|
|
printf_s("WaitForSingleObject() is timed out\n");
|
|
bAsyncCallSuccess = FALSE;
|
|
}
|
|
break;
|
|
}
|
|
|
|
if (bAsyncCallSuccess)
|
|
{
|
|
printf_s("Calling RpcAsyncCompleteCall\n");
|
|
status = RpcAsyncCompleteCall(&Async, &nReply);
|
|
printf_s("RpcAsyncCompleteCall returned 0x%x\n", status);
|
|
if (status)
|
|
exit(status);
|
|
|
|
printf_s("Remote procedure 'AsyncFunc' returned successfully\n\n");
|
|
}
|
|
|
|
// Clean up notification resources
|
|
switch (Async.NotificationType)
|
|
{
|
|
case RpcNotificationTypeApc:
|
|
break;
|
|
case RpcNotificationTypeIoc:
|
|
CloseHandle(Async.u.IOC.hIOPort);
|
|
break;
|
|
default: // case RpcNotificationTypeEvent:
|
|
CloseHandle(Async.u.hEvent);
|
|
break;
|
|
}
|
|
|
|
if (bShutDownServer)
|
|
{
|
|
printf_s("Shutting down server ...\n");
|
|
Shutdown(AsyncRPC_ClientIfHandle); // shut down the server side
|
|
}
|
|
|
|
// The calls to the remote procedures are complete.
|
|
// Free the string and the binding handle.
|
|
status = RpcStringFree(&pszStringBinding);
|
|
printf_s("RpcStringFree returned 0x%x\n", status);
|
|
if (status)
|
|
exit(status);
|
|
|
|
status = RpcBindingFree(&AsyncRPC_ClientIfHandle);
|
|
printf_s("RpcBindingFree returned 0x%x\n", status);
|
|
if (status)
|
|
exit(status);
|
|
|
|
exit(0);
|
|
}
|
|
|
|
|
|
void RPC_ENTRY APCNotificationRoutine(PRPC_ASYNC_STATE pAsync,
|
|
void *Context,
|
|
RPC_ASYNC_EVENT Event)
|
|
{
|
|
BOOL *pbCallFinished = (BOOL*)(pAsync->UserInfo);
|
|
|
|
printf_s("APCNotificationRoutine reported ");
|
|
|
|
switch (Event)
|
|
{
|
|
case RpcCallComplete:
|
|
printf_s("event RpcCallComplete\n");
|
|
if (pbCallFinished != NULL)
|
|
*pbCallFinished = TRUE; // used for ncadg_ip_udp
|
|
break;
|
|
case RpcSendComplete:
|
|
printf_s("event RpcSendComplete\n");
|
|
break;
|
|
case RpcReceiveComplete:
|
|
printf_s("event RpcReceiveComplete\n");
|
|
break;
|
|
default:
|
|
printf_s("event unknown\n");
|
|
break;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
// MIDL allocate and free
|
|
void __RPC_FAR * __RPC_USER midl_user_allocate(size_t len)
|
|
{
|
|
return(malloc(len));
|
|
}
|
|
|
|
|
|
void __RPC_USER midl_user_free(void __RPC_FAR * ptr)
|
|
{
|
|
free(ptr);
|
|
}
|