mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
720 lines
23 KiB
720 lines
23 KiB
/******************************Module*Header*******************************\
|
|
* Module Name: usersrv.cxx
|
|
*
|
|
* This module provides all the routines necessary for calling user
|
|
* side servers. The original user mode server is the font driver.
|
|
*
|
|
* Created: 28-May-1996
|
|
* Author: Kirk Olynyk [kirko]
|
|
*
|
|
* Copyright (c) 1996 Microsoft Corporation
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hxx"
|
|
|
|
BOOL GreGetMessage( GDIMSG* );
|
|
|
|
// A GDIMSG_INFO structure is associated with each server thread
|
|
// The pointer to this struture is stored in the TEB.
|
|
|
|
typedef struct _GDIMSG_INFO {
|
|
GDIMSG ClientMessage; // client message (kernel mode)
|
|
GDIMSG ServerMessage; // server message (user mode)
|
|
KEVENT ClientEvent; // client waits on this
|
|
KEVENT ServerEvent; // server waits on this
|
|
} GDIMSG_INFO;
|
|
|
|
// for accessing the pointer to the GDIMSG_INFO in the TEB
|
|
|
|
#define PPMSG(x) ((GDIMSG_INFO**)(&((x)->Spare2)))
|
|
|
|
#define MESSAGE_BLOCK_SIZE (PAGE_SIZE - 64)
|
|
|
|
|
|
#if DBG
|
|
int giUSrv = 0;
|
|
#define TRACE_USRV(n,str) { if (giUSrv >= (n)) { KdPrint(str); } }
|
|
#else
|
|
#define TRACE_USRV(n,str)
|
|
#endif
|
|
|
|
|
|
/**********************************************************************
|
|
* *
|
|
* gapfnServerInit =: array of pointers to functions returning BOOL *
|
|
* where each of these functions is a server *
|
|
* initialization routine *
|
|
* *
|
|
**********************************************************************/
|
|
|
|
BOOL (**gapfnUserServerInit)(GDIMSG*,void*) = 0;
|
|
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Routine Name:
|
|
*
|
|
* NtGdiGetMessage
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is the routine called by the user mode server to unload the
|
|
* returned data from the previous call and to load the data associated
|
|
* with this call.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pMsg a pointer to a GDIMSG structure residing in the user
|
|
* mode space of the server thread.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE if successfull otherwist FALSE.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
extern "C" BOOL APIENTRY NtGdiGetMessage( GDIMSG *pMsg )
|
|
{
|
|
GDIMSG Msg; // Kernel Side copy of GDIMSG structure
|
|
BOOL bRet; // success indicator
|
|
|
|
ASSERTGDI(
|
|
MM_LOWEST_USER_ADDRESS <= pMsg &&
|
|
pMsg <= MM_HIGHEST_USER_ADDRESS, "Invalid pMsg\n");
|
|
|
|
TRACE_USRV(1,(
|
|
"NtGdiGetMessage:\n pMsg = %x",
|
|
pMsg));
|
|
|
|
// Make a temporary kernel size copy of the server message
|
|
|
|
__try
|
|
{
|
|
ProbeForRead( pMsg, sizeof(*pMsg), sizeof(double) );
|
|
Msg = *pMsg; // make a copy of the user mode GDIMSG structure
|
|
bRet = TRUE; // indicate that the read was successful
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
KdPrint(("NtGdiGetMessage: Exception #1\n"));
|
|
bRet = FALSE;
|
|
}
|
|
|
|
if ( bRet ) // if the read of the user mode GDIMSG structure
|
|
{ // was successful then we can pass it on to the
|
|
// client thread to process it
|
|
if ( bRet = GreGetMessage( &Msg ))
|
|
{
|
|
// Now it is time to go to take the next message
|
|
// to the user mode server. We can not be sure
|
|
// that the pointer is valid so we surround
|
|
// the write with a try/except pair
|
|
__try
|
|
{
|
|
ProbeForWrite( pMsg, sizeof(*pMsg), sizeof(double) );
|
|
*pMsg = Msg;
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
KdPrint(("NtGdiGetMessage: Exception #2\n"));
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
}
|
|
return( bRet );
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Routine Name:
|
|
*
|
|
* pMsgInfoInit
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is the work horse routine called by NtGdiGetMessage.
|
|
*
|
|
* This is the routine that is called upon the very first call from
|
|
* a user mode server to get its first message. All initialization
|
|
* is done at this point.
|
|
*
|
|
* There initialization process can be partitioned into two classes:
|
|
*
|
|
* 1. Service Identification and Initialization
|
|
* 2. Client-Server memory allocation for message transport.
|
|
*
|
|
* Arguments:
|
|
*
|
|
*
|
|
* pMsg a pointer to the GDIMSG structure that was filled in
|
|
* by the user mode driver before calling to this routine.
|
|
* In this special case of initialization this GDIMSG
|
|
* structure contains a pointer to user mode memory
|
|
* allocated by the user mode server. As such, this memory
|
|
* cannot be trusted and must be accessed only when
|
|
* surrounded by try excepts.
|
|
*
|
|
* The memory provided by the user mode server contains
|
|
* information used to identify the type of service
|
|
* provided by the server.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* a pointer to the GDIMSG_INFO structure that is to
|
|
* be associated with this server thread by placing
|
|
* a pointer to it in the TEB.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GDIMSG_INFO *pMsgInfoInit( GDIMSG *pMsg )
|
|
{
|
|
extern BOOL bInitializeUserModeServer( GDIMSG* );
|
|
|
|
GDIMSG_INFO *pInfo = 0;
|
|
|
|
TRACE_USRV(1,(
|
|
"pMsgInfoInit:\n"
|
|
"\tpMsg %x\n",
|
|
pMsg));
|
|
|
|
void *pvInfo = 0; // ptr to GDIMSG_INFO structure
|
|
|
|
if ( !bInitializeUserModeServer( pMsg ))
|
|
{
|
|
TRACE_USRV(1,(
|
|
"pMsgInfoInit:\n"
|
|
"\tbInitializeUserModeServer returned FALSE\n"));
|
|
}
|
|
else if (!( pvInfo = PALLOCMEM( sizeof(GDIMSG_INFO), 'igmG' )))
|
|
{
|
|
TRACE_USRV(1,(
|
|
"pMsgInfoInit:\n"
|
|
"\tFailed to allocated GDIMSG_INFO\n"));
|
|
}
|
|
else
|
|
{
|
|
TRACE_USRV(1,(
|
|
"pMsgInfoInit:\n"
|
|
"\tpvInfo %x\n",
|
|
pvInfo));
|
|
|
|
pInfo = (GDIMSG_INFO*) pvInfo;
|
|
|
|
// initialize server semaphore
|
|
|
|
KeInitializeEvent(
|
|
&( pInfo->ServerEvent ), // address of KEVENT structure
|
|
SynchronizationEvent, // auto-clearing
|
|
FALSE ); // initialize as Not-Signaled
|
|
|
|
// initialize client semaphore
|
|
|
|
KeInitializeEvent(
|
|
&( pInfo->ClientEvent ), // address of KEVENT structure
|
|
NotificationEvent, // requires explicit KeClearEvent
|
|
FALSE ); // initialize as Not-Signaled
|
|
}
|
|
|
|
if ( pInfo == 0 && pvInfo != 0 )
|
|
{
|
|
VFREEMEM( pvInfo );
|
|
}
|
|
|
|
return( pInfo );
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Routine Name:
|
|
*
|
|
* GreGetMessage
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is the workhorse routine for NtGdiGetMessage. A server will
|
|
* call this routine to place data in the kernel buffer, go to sleep
|
|
* for the next message, and then when wakened, copy data from the
|
|
* kernel buffer to user memory.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pMsg a pointer to a GDIMSG structure residing in
|
|
* kernel mode memory ( may contain pointers
|
|
* to user mode memory ).
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE upon success, FALSE otherwise.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL GreGetMessage( GDIMSG *pMsg )
|
|
{
|
|
BOOL bRet;
|
|
NTSTATUS NtStatus;
|
|
TEB *pTeb; // a pointer to the TEB of this thread
|
|
|
|
GDIMSG_INFO *pInfo; // a pointer to the GDIMSG_INFO structure associated
|
|
// with this server thread. If this is the first
|
|
// call to get a message then no GDIMSG_INFO structure
|
|
// will be allocated yet. We must then allocate
|
|
// a GDIMSG_INFO structure and initialize it.
|
|
// Whether the structure existed or we had to allocate
|
|
// it, this is a pointer to that structure.
|
|
|
|
GDIMSG_INFO **ppInfo; // address in TEB that is to receive the
|
|
// GDIMSG_INFO pointer.
|
|
|
|
bRet = TRUE;
|
|
pTeb = NtCurrentTeb();
|
|
pInfo = 0;
|
|
|
|
// pMsg should point to a kernel address
|
|
|
|
ASSERTGDI( pMsg >= MM_LOWEST_SYSTEM_ADDRESS, "Invalid pMsg\n" );
|
|
|
|
TRACE_USRV(1,(
|
|
"GreGetMessage:\n"
|
|
"\tpMsg %x\n"
|
|
"\tpTeb %x\n",
|
|
pMsg, pTeb));
|
|
|
|
if ( !pTeb )
|
|
{
|
|
KdPrint(("GreGetMessage error: unable to get pTeb\n"));
|
|
return( FALSE );
|
|
}
|
|
|
|
ppInfo = PPMSG( pTeb ); // location of pointer in TEB
|
|
pInfo = *ppInfo; // value of pointer in TEB
|
|
|
|
if ( pInfo == 0 ) // First message?
|
|
{
|
|
*ppInfo = pInfo = pMsgInfoInit( pMsg );
|
|
}
|
|
|
|
TRACE_USRV(2,(
|
|
"GreGetMessage:\n"
|
|
"\tppInfo %x\n"
|
|
"\tpInfo %x\n",
|
|
ppInfo, pInfo));
|
|
|
|
pInfo->ServerMessage = *pMsg; // pMsg points to safe
|
|
|
|
TRACE_USRV(2,(
|
|
"GreGetMessage:\n"
|
|
"\tServerMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n"
|
|
"\tClientMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n",
|
|
pInfo->ServerMessage.pjIn, pInfo->ServerMessage.cjIn,
|
|
pInfo->ServerMessage.pjOut, pInfo->ServerMessage.pjOut,
|
|
pInfo->ClientMessage.pjIn, pInfo->ClientMessage.cjIn,
|
|
pInfo->ClientMessage.pjOut, pInfo->ClientMessage.pjOut
|
|
));
|
|
|
|
if (
|
|
pMsg->pjOut &&
|
|
pInfo->ClientMessage.pjIn &&
|
|
pMsg->cjOut &&
|
|
pMsg->cjOut <= pInfo->ClientMessage.cjIn
|
|
)
|
|
{ // Copy new server message
|
|
// kernel mode structure
|
|
__try
|
|
{
|
|
ProbeForRead( pMsg->pjOut, pMsg->cjOut, sizeof(double) );
|
|
RtlMoveMemory(
|
|
pInfo->ClientMessage.pjIn,
|
|
pMsg->pjOut,
|
|
pMsg->cjOut
|
|
);
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
KdPrint(("GreGetMessage exception #1\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RIP("GreGetMessage:Inconsistent Server Message\n");
|
|
bRet = FALSE;
|
|
}
|
|
|
|
// Wake up the client thread so that it can process the message
|
|
// This has to be done even though the pInfo may be zero
|
|
|
|
TRACE_USRV(2,(
|
|
"GreGetMessage:\n"
|
|
"\tWake up client\n"
|
|
));
|
|
|
|
NtStatus =
|
|
KeSetEvent(
|
|
&pInfo->ClientEvent,// pointer to initialized event object
|
|
0, // set priority increment to zero
|
|
FALSE ); // don't append implicit KeWaitXxx
|
|
|
|
TRACE_USRV(2,(
|
|
"GreGetMessage:\n"
|
|
"\tNtStatus = %d\n",
|
|
NtStatus));
|
|
|
|
ASSERTGDI( NtStatus == 0,
|
|
"Client Event was in Signaled state\n");
|
|
|
|
// put the server thread to sleep and wait for the next call
|
|
// from the client
|
|
|
|
TRACE_USRV(2,(
|
|
"GreGetMessage:\n"
|
|
"\tSleep until next call from client\n"
|
|
));
|
|
|
|
NtStatus =
|
|
KeWaitForSingleObject(
|
|
&pInfo->ServerEvent,// address of initialized event
|
|
|
|
UserRequest, // this driver is doing work on behalf
|
|
// of a user and is running in the context
|
|
// of a User server thread
|
|
|
|
KernelMode, // we do not expect this to be asleep
|
|
// too long in the usual case
|
|
|
|
FALSE, // not alertable
|
|
|
|
0 ); // no time out limitation since we are
|
|
// waiting for the next call from an
|
|
// application and there is no telling
|
|
// when that will happen
|
|
|
|
TRACE_USRV(2,(
|
|
"GreGetMessage:\n"
|
|
"\tSleeping beauty awakes\n"
|
|
"\tNtStatus %d\n",
|
|
NtStatus
|
|
));
|
|
|
|
ASSERTGDI(NtStatus == STATUS_SUCCESS,
|
|
"Server Thread awoke unexpectedly\n");
|
|
|
|
// The server thread has been wakened by the client thread
|
|
// We must copy the data left by the client thread in
|
|
// the kernel buffer to the user mode buffer. No need
|
|
// for try-except's since the user mode memory is guranateed
|
|
// to be safe.
|
|
|
|
TRACE_USRV(2,(
|
|
"GreGetMessage:\n"
|
|
"\tServerMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n"
|
|
"\tClientMessage: pjIn = %x, cjIn = %x, pjOut = %x, cjOut = %x\n",
|
|
pInfo->ServerMessage.pjIn, pInfo->ServerMessage.cjIn,
|
|
pInfo->ServerMessage.pjOut, pInfo->ServerMessage.pjOut,
|
|
pInfo->ClientMessage.pjIn, pInfo->ClientMessage.cjIn,
|
|
pInfo->ClientMessage.pjOut, pInfo->ClientMessage.pjOut
|
|
));
|
|
|
|
if (
|
|
pInfo->ServerMessage.pjIn &&
|
|
pInfo->ClientMessage.pjOut &&
|
|
pInfo->ClientMessage.cjOut &&
|
|
pInfo->ClientMessage.cjOut <= pInfo->ServerMessage.cjOut
|
|
)
|
|
{
|
|
__try
|
|
{
|
|
ProbeForWrite(
|
|
pInfo->ServerMessage.pjIn,
|
|
pInfo->ClientMessage.cjOut,
|
|
sizeof(double) );
|
|
RtlMoveMemory(
|
|
pInfo->ServerMessage.pjIn,
|
|
pInfo->ClientMessage.pjOut,
|
|
pInfo->ClientMessage.cjOut );
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
KdPrint(("GreGetMessage exception #2\n"));
|
|
bRet = FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bRet = FALSE;
|
|
}
|
|
|
|
return( bRet );
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Routine Name:
|
|
*
|
|
* bIntiializeUserModeServer
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This routine does the work to set up whatever is necessary to make
|
|
* use of the user mode server.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pMsg This is a pointer to a kenrel mode copy of the original
|
|
* GDIMSG structure that contains
|
|
* pointers to user mode memory allocated by the user
|
|
* mode server. This contains all the information necessary
|
|
* to initialize this user mode server. Since this memory
|
|
* is not guaranteed to be safe, we must surround all
|
|
* access to this user mode memory with __try __except
|
|
* pairs.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* The allowed return values are TRUE upon successful initialization
|
|
* and FALSE upon failure.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bInitializeUserModeServer( GDIMSG *pMsg )
|
|
{
|
|
extern BOOL bInitServerWorkhorse( GDIMSG*, void* );
|
|
|
|
void *pv;
|
|
BOOL bRet = TRUE;
|
|
|
|
TRACE_USRV(2,(
|
|
"bInitializeUserModeServer:\n"
|
|
"\tpMsg %x",
|
|
pMsg
|
|
));
|
|
|
|
if ( pMsg == 0 )
|
|
{
|
|
KdPrint(("bInitializeUserModeServer Error: pMsg == 0\n"));
|
|
return( FALSE );
|
|
}
|
|
|
|
if ( ( pv = PALLOCMEM( pMsg->cjOut, 'pmtG' )) == 0 )
|
|
{
|
|
WARNING("bInitializeUserModeServer: PALLOCMEM failed\n");
|
|
return( FALSE );
|
|
}
|
|
|
|
__try
|
|
{
|
|
ProbeForRead( pMsg->pjOut, pMsg->cjOut, sizeof(double) );
|
|
RtlCopyMemory( pv, pMsg->pjOut, pMsg->cjOut );
|
|
}
|
|
__except( EXCEPTION_EXECUTE_HANDLER )
|
|
{
|
|
KdPrint(("bInitializeUserModeServer exception #1\n"));
|
|
bRet = FALSE;
|
|
}
|
|
|
|
bRet = bRet && bInitServerWorkhorse( pMsg, pv );
|
|
VFREEMEM( pv );
|
|
|
|
return( bRet );
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Routine Name:
|
|
*
|
|
* bInitServerWorkhorse
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is the workhorse routine for server initialization. There
|
|
* are only a finite number of servers that Gdi recognizes. An argument
|
|
* to this routine is a pointer to a kernel mode copy of the
|
|
* original user mode buffer allocated and initialized by the user mode
|
|
* server. This pointer is passed to all of the routines that claim
|
|
* to know about user mode servers. The first routine that recognizes
|
|
* the buffer will process and intialize it.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pMsg a pointer to a kernel mode copy of the original server
|
|
* message. This may contain pointers to user mode data
|
|
*
|
|
* pv a pointer to a kernel mode copy of the input buffer
|
|
* of the original server message
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE if successful otherwise FALSE.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bInitServerWorkhorse( GDIMSG *pMsg, void *pv )
|
|
{
|
|
BOOL bRet = FALSE;
|
|
|
|
TRACE_USRV(1,(
|
|
"bInitServerWorkhorse:\n"
|
|
"\tpMsg %x\n"
|
|
"\tpv %x\n",
|
|
pMsg,
|
|
pv
|
|
));
|
|
|
|
SEMOBJ so(gpsemDriverMgmt); // Enter a critical section to
|
|
// protect the dispatch table
|
|
if ( gapfnUserServerInit )
|
|
{
|
|
BOOL (**ppfn)(GDIMSG*,void*);
|
|
|
|
for (ppfn = gapfnUserServerInit; *ppfn; ppfn++)
|
|
{
|
|
TRACE_USRV(2,(
|
|
"bInitServerWorkhorse:\n"
|
|
"\tCalling function at %x ...\n",
|
|
*ppfn
|
|
));
|
|
|
|
if ( bRet = (**ppfn)( pMsg, pv ))
|
|
{
|
|
TRACE_USRV(2,(
|
|
"bInitServerWorkhorse:\n"
|
|
"\tInitialization function returend TRUE!\n"
|
|
));
|
|
|
|
break;
|
|
}
|
|
TRACE_USRV(2,(
|
|
"bInitServerWorkhorse:\n"
|
|
"\tInitialization function returned FALSE.\n"
|
|
));
|
|
}
|
|
}
|
|
return( bRet );
|
|
}
|
|
|
|
/******************************Public*Routine******************************\
|
|
*
|
|
* Routine Name:
|
|
*
|
|
* bRegisterClient
|
|
*
|
|
* Routine Description:
|
|
*
|
|
* This is the way that the engine will register a client for a server.
|
|
* GDI registers a function that is used to instatiate a user mode
|
|
* server process the first time that it calls into the kernel.
|
|
*
|
|
* I anticipate that this will be called once to register the function
|
|
* that initializes font drivers. There may be other services that
|
|
* need to be placed in user mode, each of these servcice that GDI
|
|
* anticpates using, must be registered this way in order to use
|
|
* this client server message passing mechanism.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* pfn A pointer to a boolean function that takes a GDIMSG pointer
|
|
* and a pointer to a buffer as an argument. Using this
|
|
* information, GDI will set up the data structures needed
|
|
* to take advantage of this user mode service.
|
|
*
|
|
* The reader is referred to the comments in bInitServerWorkhorse
|
|
* for a description of the arguments to these functions.
|
|
*
|
|
* Return Value:
|
|
*
|
|
* TRUE if successful otherwise FASLE.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
BOOL bRegisterClient( BOOL (*pfn)(GDIMSG*,void*) )
|
|
{
|
|
static int iCurrent = 0; // index of current slot in gapfnUserServerInit
|
|
|
|
static int iOneTooMany = 0; // first invalid index in gapfnUserServerInit,
|
|
// also equal to the number of available
|
|
// elements in the the array pointed to by
|
|
// gapfnUserServerInit
|
|
|
|
static int iIncrement = 4; // number of entries that table should grow
|
|
// upon reallocation
|
|
|
|
BOOL bRet = FALSE; // return value for this routine
|
|
|
|
TRACE_USRV(2,(
|
|
"bRegisterClient:\n"
|
|
"\tpfn %x\n"
|
|
"\tgapfnUserServerInit %x\n"
|
|
"\tiOneTooMany %x\n"
|
|
"\tiIncrement %x\n",
|
|
pfn,
|
|
gapfnUserServerInit,
|
|
iOneTooMany,
|
|
iIncrement
|
|
));
|
|
|
|
if ( pfn == 0 ) // Did the caller passin a funcion pointer?
|
|
{ // No!
|
|
KdPrint(("bRegisterClient: pfn == 0\n"));
|
|
}
|
|
else
|
|
{
|
|
SEMOBJ so(gpsemDriverMgmt); // Enter a critical section to
|
|
// protect the dispatch table
|
|
|
|
// Q: Is there room in the table
|
|
if ( iCurrent < iOneTooMany ) // for one more entry?
|
|
{ // A: Yes. There is no no need
|
|
bRet = TRUE; // need to allocate a new table
|
|
}
|
|
else // A: No. The dispatch table
|
|
{ // is full and it is necessary
|
|
// to allocate a bigger table
|
|
// allocate iIncrement more slots
|
|
// than before (Note: PALLOCMEM
|
|
// zero's allocated memory)
|
|
void *pv =
|
|
PALLOCMEM(
|
|
sizeof( *gapfnUserServerInit ) * (iOneTooMany + iIncrement),
|
|
'isuG' );
|
|
if ( pv == 0 ) // Q: Was the allocation successful?
|
|
{ // A: No. We have a problem
|
|
KdPrint(("bRegisterClient: pv == 0\n"));
|
|
}
|
|
else
|
|
{ // A: Yes, successful allocation
|
|
TRACE_USRV(2,(
|
|
"bRegisterClient:\n"
|
|
"\tAllocated new dispatch table at %x\n",
|
|
pv
|
|
));
|
|
|
|
if ( gapfnUserServerInit ) // Q: Do we already have a table?
|
|
{ // A: Yes, copy the old dispatch table
|
|
TRACE_USRV(2,( // to the new and larger memory
|
|
"bRegisterClient:\n"
|
|
"\tCopying old dispatch table from %x\n",
|
|
gapfnUserServerInit
|
|
));
|
|
|
|
RtlCopyMemory( // Copy old table to the new table
|
|
pv,
|
|
gapfnUserServerInit,
|
|
iOneTooMany * sizeof( *gapfnUserServerInit ));
|
|
VFREEMEM( gapfnUserServerInit ); // free old table
|
|
}
|
|
// reset global pointer to dispatch
|
|
// table
|
|
gapfnUserServerInit = (BOOL (**) (GDIMSG*, void*)) pv;
|
|
iOneTooMany += iIncrement; // update out of bounds index
|
|
bRet = TRUE;
|
|
}
|
|
}
|
|
if ( bRet )
|
|
{
|
|
gapfnUserServerInit[iCurrent] = pfn; // set the lowest free entry
|
|
// in the dispatch table
|
|
iCurrent += 1; // increment pointer to next entry
|
|
}
|
|
// ~SEMOBJ is called here causing you to leave the critical section
|
|
}
|
|
return( bRet );
|
|
}
|