614 lines
14 KiB
C++
614 lines
14 KiB
C++
|
/*++
|
|||
|
|
|||
|
Copyright (C) Microsoft Corporation, 1996 - 1999
|
|||
|
|
|||
|
Module Name:
|
|||
|
|
|||
|
loader.cxx
|
|||
|
|
|||
|
Abstract:
|
|||
|
|
|||
|
Configuration and loading of RPC transports
|
|||
|
|
|||
|
Revision History:
|
|||
|
MarioGo 03-18-96 Cloned from parts of old common.c
|
|||
|
MarioGo 10-31-96 Async RPC
|
|||
|
|
|||
|
--*/
|
|||
|
|
|||
|
#include <precomp.hxx>
|
|||
|
#include <loader.hxx>
|
|||
|
#include <trans.hxx>
|
|||
|
#include <cotrans.hxx>
|
|||
|
#include <dgtrans.hxx>
|
|||
|
|
|||
|
// Globals - see loader.hxx
|
|||
|
|
|||
|
DWORD gdwComputerNameLength = 0;
|
|||
|
RPC_CHAR gpstrComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
|||
|
|
|||
|
UINT gPostSize = CO_MIN_RECV;
|
|||
|
|
|||
|
#ifdef _INTERNAL_RPC_BUILD_
|
|||
|
RPCLT_PDU_FILTER_FUNC gpfnFilter = NULL;
|
|||
|
#endif
|
|||
|
|
|||
|
//
|
|||
|
// Used to convert numbers to hex strings
|
|||
|
//
|
|||
|
|
|||
|
const RPC_CHAR HexDigits[] =
|
|||
|
{
|
|||
|
RPC_CONST_CHAR('0'),
|
|||
|
RPC_CONST_CHAR('1'),
|
|||
|
RPC_CONST_CHAR('2'),
|
|||
|
RPC_CONST_CHAR('3'),
|
|||
|
RPC_CONST_CHAR('4'),
|
|||
|
RPC_CONST_CHAR('5'),
|
|||
|
RPC_CONST_CHAR('6'),
|
|||
|
RPC_CONST_CHAR('7'),
|
|||
|
RPC_CONST_CHAR('8'),
|
|||
|
RPC_CONST_CHAR('9'),
|
|||
|
RPC_CONST_CHAR('A'),
|
|||
|
RPC_CONST_CHAR('B'),
|
|||
|
RPC_CONST_CHAR('C'),
|
|||
|
RPC_CONST_CHAR('D'),
|
|||
|
RPC_CONST_CHAR('E'),
|
|||
|
RPC_CONST_CHAR('F')
|
|||
|
};
|
|||
|
|
|||
|
// WARNING: The order of these protocols must be consistent with the
|
|||
|
// definition of PROTOCOL_ID.
|
|||
|
|
|||
|
const
|
|||
|
TRANSPORT_TABLE_ENTRY TransportTable[] = {
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
0
|
|||
|
},
|
|||
|
|
|||
|
// TCP/IP
|
|||
|
{
|
|||
|
TCP_TOWER_ID,
|
|||
|
IP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&TCP_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
#ifdef SPX_ON
|
|||
|
// SPX
|
|||
|
{
|
|||
|
SPX_TOWER_ID,
|
|||
|
IPX_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&SPX_TransportInterface
|
|||
|
},
|
|||
|
#else
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
},
|
|||
|
#endif
|
|||
|
|
|||
|
// Named pipes
|
|||
|
{
|
|||
|
NMP_TOWER_ID,
|
|||
|
UNC_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&NMP_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
#ifdef NETBIOS_ON
|
|||
|
// Netbeui
|
|||
|
{
|
|||
|
NB_TOWER_ID,
|
|||
|
NBF_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&NBF_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
// Netbios over TCP/IP
|
|||
|
{
|
|||
|
NB_TOWER_ID,
|
|||
|
IP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&NBT_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
// Netbios over IPX
|
|||
|
{
|
|||
|
NB_TOWER_ID,
|
|||
|
IPX_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&NBI_TransportInterface
|
|||
|
},
|
|||
|
#else
|
|||
|
// Netbeui
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
},
|
|||
|
|
|||
|
// Netbios over TCP/IP
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
},
|
|||
|
|
|||
|
// Netbios over IPX
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
},
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef APPLETALK_ON
|
|||
|
// Appletalk Datastream protocol
|
|||
|
{
|
|||
|
DSP_TOWER_ID,
|
|||
|
NBP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&DSP_TransportInterface
|
|||
|
},
|
|||
|
#else
|
|||
|
// Appletalk Datastream protocol
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
},
|
|||
|
#endif
|
|||
|
|
|||
|
// Banyan Vines SSP
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
},
|
|||
|
|
|||
|
// Hyper-Text Tranfer Protocol (HTTP)
|
|||
|
{
|
|||
|
HTTP_TOWER_ID,
|
|||
|
HTTP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&HTTP_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
// UDP/IP
|
|||
|
{
|
|||
|
UDP_TOWER_ID,
|
|||
|
IP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&UDP_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
#ifdef IPX_ON
|
|||
|
// IPX
|
|||
|
{
|
|||
|
IPX_TOWER_ID,
|
|||
|
IPX_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&IPX_TransportInterface
|
|||
|
},
|
|||
|
#else
|
|||
|
// IPX
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
0
|
|||
|
},
|
|||
|
#endif
|
|||
|
|
|||
|
// CDP/UDP/IP
|
|||
|
{
|
|||
|
CDP_TOWER_ID,
|
|||
|
IP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&CDP_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
#ifdef NCADG_MQ_ON
|
|||
|
// MSMQ (Falcon/RPC)
|
|||
|
{
|
|||
|
MQ_TOWER_ID,
|
|||
|
MQ_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&MQ_TransportInterface
|
|||
|
},
|
|||
|
#else
|
|||
|
// MSMQ (Falcon/RPC)
|
|||
|
{
|
|||
|
0,
|
|||
|
0,
|
|||
|
NULL
|
|||
|
},
|
|||
|
#endif
|
|||
|
|
|||
|
// TCP over IPv6
|
|||
|
{
|
|||
|
TCP_TOWER_ID,
|
|||
|
IP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&TCP_TransportInterface
|
|||
|
},
|
|||
|
|
|||
|
// HTTP2 - same as HTTP in terms of contents.
|
|||
|
{
|
|||
|
HTTP_TOWER_ID,
|
|||
|
HTTP_ADDRESS_ID,
|
|||
|
(RPC_TRANSPORT_INTERFACE)&HTTP_TransportInterface
|
|||
|
}
|
|||
|
};
|
|||
|
|
|||
|
const DWORD cTransportTable = sizeof(TransportTable)/sizeof(TRANSPORT_TABLE_ENTRY);
|
|||
|
|
|||
|
|
|||
|
inline
|
|||
|
BOOL CompareProtseqs(
|
|||
|
IN const CHAR *p1,
|
|||
|
IN const RPC_CHAR *p2)
|
|||
|
// Note: protseqs use only ANSI characters so this is ok.
|
|||
|
{
|
|||
|
while(*p1)
|
|||
|
{
|
|||
|
if (*p1 != *p2)
|
|||
|
{
|
|||
|
return FALSE;
|
|||
|
}
|
|||
|
p1++;
|
|||
|
p2++;
|
|||
|
}
|
|||
|
|
|||
|
return(*p2 == 0);
|
|||
|
}
|
|||
|
|
|||
|
PROTOCOL_ID
|
|||
|
MapProtseq(
|
|||
|
IN const RPC_CHAR *RpcProtocolSequence
|
|||
|
)
|
|||
|
{
|
|||
|
PROTOCOL_ID index;
|
|||
|
|
|||
|
for(index = 1; index < cTransportTable; index++)
|
|||
|
{
|
|||
|
if (TransportTable[index].pInfo != NULL)
|
|||
|
{
|
|||
|
if (RpcpStringCompare(RpcProtocolSequence,
|
|||
|
TransportTable[index].pInfo->ProtocolSequence) == 0)
|
|||
|
{
|
|||
|
return(index);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|||
|
DPFLTR_WARNING_LEVEL,
|
|||
|
RPCTRANS "Called with unknown protseq %S\n",
|
|||
|
RpcProtocolSequence));
|
|||
|
|
|||
|
ASSERT(0);
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
PROTOCOL_ID
|
|||
|
MapProtseq(
|
|||
|
IN const CHAR *RpcProtocolSequence
|
|||
|
)
|
|||
|
{
|
|||
|
PROTOCOL_ID index;
|
|||
|
|
|||
|
for(index = 1; index < cTransportTable; index++)
|
|||
|
{
|
|||
|
if (TransportTable[index].pInfo != NULL)
|
|||
|
{
|
|||
|
if (CompareProtseqs(RpcProtocolSequence,
|
|||
|
TransportTable[index].pInfo->ProtocolSequence))
|
|||
|
{
|
|||
|
return(index);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|||
|
DPFLTR_WARNING_LEVEL,
|
|||
|
RPCTRANS "Called with unknown protseq %S\n",
|
|||
|
RpcProtocolSequence));
|
|||
|
|
|||
|
ASSERT(0);
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
// NB: must be called before RpcCompletionPort is zeroed out, because it is used for comparison
|
|||
|
void FreeCompletionPortHashTable(void)
|
|||
|
{
|
|||
|
DWORD i;
|
|||
|
HANDLE hCurrentHandle;
|
|||
|
|
|||
|
// walk through the table, not closing if there is next entry, and it is the same as this
|
|||
|
for (i = 0; i < gNumberOfProcessors * 2; i ++)
|
|||
|
{
|
|||
|
hCurrentHandle = RpcCompletionPorts[i];
|
|||
|
|
|||
|
if (hCurrentHandle && (hCurrentHandle != RpcCompletionPort))
|
|||
|
{
|
|||
|
CloseHandle(hCurrentHandle);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
HANDLE GetCompletionPortHandleForThread(void)
|
|||
|
{
|
|||
|
DWORD i;
|
|||
|
DWORD nMinLoad = (DWORD) -1;
|
|||
|
int nMinLoadIndex = -1;
|
|||
|
|
|||
|
for (i = 0; i < gNumberOfProcessors * 2; i ++)
|
|||
|
{
|
|||
|
if ((DWORD)CompletionPortHandleLoads[i] < nMinLoad)
|
|||
|
{
|
|||
|
nMinLoadIndex = i;
|
|||
|
nMinLoad = CompletionPortHandleLoads[i];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ASSERT (nMinLoadIndex >= 0);
|
|||
|
InterlockedIncrement(&CompletionPortHandleLoads[nMinLoadIndex]);
|
|||
|
ASSERT(RpcCompletionPorts[nMinLoadIndex] != 0);
|
|||
|
return RpcCompletionPorts[nMinLoadIndex];
|
|||
|
}
|
|||
|
|
|||
|
void ReleaseCompletionPortHandleForThread(HANDLE h)
|
|||
|
{
|
|||
|
DWORD i;
|
|||
|
|
|||
|
for (i = 0; i < gNumberOfProcessors * 2; i ++)
|
|||
|
{
|
|||
|
if (h == RpcCompletionPorts[i])
|
|||
|
{
|
|||
|
InterlockedDecrement((long *)&CompletionPortHandleLoads[i]);
|
|||
|
ASSERT(CompletionPortHandleLoads[i] >= 0);
|
|||
|
return;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(0);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
RPC_TRANSPORT_INTERFACE
|
|||
|
TransportLoad (
|
|||
|
IN const RPC_CHAR * RpcProtocolSequence
|
|||
|
)
|
|||
|
{
|
|||
|
static fLoaded = FALSE;
|
|||
|
RPC_STATUS RpcStatus;
|
|||
|
|
|||
|
PROTOCOL_ID index;
|
|||
|
RPC_STATUS status;
|
|||
|
RPC_TRANSPORT_INTERFACE pInfo;
|
|||
|
|
|||
|
if (fLoaded == FALSE)
|
|||
|
{
|
|||
|
|
|||
|
RpcStatus = InitTransportProtocols();
|
|||
|
if (RpcStatus != RPC_S_OK)
|
|||
|
return NULL;
|
|||
|
|
|||
|
//
|
|||
|
// Query the computer name - used by most protocols.
|
|||
|
//
|
|||
|
|
|||
|
gdwComputerNameLength = sizeof(gpstrComputerName)/sizeof(RPC_CHAR);
|
|||
|
|
|||
|
if (!GetComputerName((RPC_SCHAR *)gpstrComputerName, &gdwComputerNameLength))
|
|||
|
{
|
|||
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|||
|
DPFLTR_WARNING_LEVEL,
|
|||
|
"RPCTRANS: GetComputerNameW failed: %d\n",
|
|||
|
GetLastError()));
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
gdwComputerNameLength++; // Include the null in the count.
|
|||
|
|
|||
|
// Create initial IO completion port. This saves us from a race
|
|||
|
// assigning the global io completion port.
|
|||
|
ASSERT(RpcCompletionPort == 0);
|
|||
|
RpcCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
|
|||
|
0,
|
|||
|
0,
|
|||
|
0); // PERF REVIEW
|
|||
|
|
|||
|
if (RpcCompletionPort == 0)
|
|||
|
{
|
|||
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|||
|
DPFLTR_WARNING_LEVEL, RPCTRANS "Failed to create initial completion port: %d\n",
|
|||
|
GetLastError()));
|
|||
|
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
InactiveRpcCompletionPort = CreateIoCompletionPort(INVALID_HANDLE_VALUE,
|
|||
|
0,
|
|||
|
0,
|
|||
|
MAXULONG); // PERF REVIEW
|
|||
|
|
|||
|
if (InactiveRpcCompletionPort == 0)
|
|||
|
{
|
|||
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|||
|
DPFLTR_WARNING_LEVEL, RPCTRANS "Failed to create initial completion port: %d\n",
|
|||
|
GetLastError()));
|
|||
|
|
|||
|
CloseHandle(RpcCompletionPort);
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
HANDLE hCurrentCompletionPortHandle;
|
|||
|
DWORD i;
|
|||
|
BOOL fSuccess = TRUE;
|
|||
|
HANDLE hSourceProcessHandle = GetCurrentProcess();
|
|||
|
|
|||
|
RpcCompletionPorts = new HANDLE[gNumberOfProcessors * 2];
|
|||
|
CompletionPortHandleLoads = new long[gNumberOfProcessors * 2];
|
|||
|
|
|||
|
if ((RpcCompletionPorts == NULL) || (CompletionPortHandleLoads == NULL))
|
|||
|
{
|
|||
|
CloseHandle(RpcCompletionPort);
|
|||
|
RpcCompletionPort = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
for (i = 0; i < gNumberOfProcessors * 2; i ++)
|
|||
|
{
|
|||
|
RpcCompletionPorts[i] = 0;
|
|||
|
CompletionPortHandleLoads[i] = 0;
|
|||
|
}
|
|||
|
|
|||
|
RpcCompletionPorts[0] = RpcCompletionPort;
|
|||
|
for (i = 1; i < gNumberOfProcessors * 2; i ++)
|
|||
|
{
|
|||
|
fSuccess = DuplicateHandle(hSourceProcessHandle, RpcCompletionPort,
|
|||
|
hSourceProcessHandle, &hCurrentCompletionPortHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
|||
|
if (!fSuccess)
|
|||
|
break;
|
|||
|
|
|||
|
ASSERT(hCurrentCompletionPortHandle != 0);
|
|||
|
RpcCompletionPorts[i] = hCurrentCompletionPortHandle;
|
|||
|
}
|
|||
|
|
|||
|
if (!fSuccess)
|
|||
|
{
|
|||
|
FreeCompletionPortHashTable();
|
|||
|
CloseHandle(RpcCompletionPort);
|
|||
|
RpcCompletionPort = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
//
|
|||
|
// Initalize locks, use Rtl* so we don't need to catch exception.
|
|||
|
//
|
|||
|
|
|||
|
NTSTATUS NtStatus;
|
|||
|
|
|||
|
NtStatus = RtlInitializeCriticalSectionAndSpinCount(&AddressListLock, PREALLOCATE_EVENT_MASK);
|
|||
|
if (!NT_SUCCESS(NtStatus))
|
|||
|
{
|
|||
|
FreeCompletionPortHashTable();
|
|||
|
CloseHandle(RpcCompletionPort);
|
|||
|
RpcCompletionPort = 0;
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
if (fPagedBCacheMode)
|
|||
|
{
|
|||
|
// allocate minimum post size. This guarantees that buffer
|
|||
|
// will always be at the end.
|
|||
|
gPostSize = sizeof(CONN_RPC_HEADER);
|
|||
|
}
|
|||
|
|
|||
|
fLoaded = TRUE;
|
|||
|
}
|
|||
|
|
|||
|
index = MapProtseq(RpcProtocolSequence);
|
|||
|
|
|||
|
if (!index)
|
|||
|
{
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
pInfo = 0;
|
|||
|
|
|||
|
switch (index)
|
|||
|
{
|
|||
|
case NMP:
|
|||
|
pInfo = (RPC_TRANSPORT_INTERFACE) NMP_TransportLoad();
|
|||
|
break;
|
|||
|
|
|||
|
#ifdef NETBIOS_ON
|
|||
|
case NBF:
|
|||
|
case NBT:
|
|||
|
case NBI:
|
|||
|
pInfo = (RPC_TRANSPORT_INTERFACE) NB_TransportLoad(index);
|
|||
|
break;
|
|||
|
#endif
|
|||
|
|
|||
|
case TCP:
|
|||
|
#ifdef SPX_ON
|
|||
|
case SPX:
|
|||
|
#endif
|
|||
|
|
|||
|
#ifdef APPLETALK_ON
|
|||
|
case DSP:
|
|||
|
#endif
|
|||
|
case HTTP:
|
|||
|
pInfo = (RPC_TRANSPORT_INTERFACE) WS_TransportLoad(index);
|
|||
|
break;
|
|||
|
|
|||
|
#ifdef NCADG_MQ_ON
|
|||
|
case MSMQ:
|
|||
|
#endif
|
|||
|
case CDP:
|
|||
|
case UDP:
|
|||
|
#ifdef IPX_ON
|
|||
|
case IPX:
|
|||
|
#endif
|
|||
|
pInfo = (RPC_TRANSPORT_INTERFACE) DG_TransportLoad(index);
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
if (pInfo == 0)
|
|||
|
{
|
|||
|
#ifdef UNICODE
|
|||
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|||
|
DPFLTR_WARNING_LEVEL,
|
|||
|
RPCTRANS "Load of %S failed\n",
|
|||
|
RpcProtocolSequence));
|
|||
|
#else
|
|||
|
TransDbgPrint((DPFLTR_RPCPROXY_ID,
|
|||
|
DPFLTR_WARNING_LEVEL,
|
|||
|
RPCTRANS "Load of %s failed\n",
|
|||
|
RpcProtocolSequence));
|
|||
|
#endif
|
|||
|
return(0);
|
|||
|
}
|
|||
|
|
|||
|
ASSERT(pInfo == TransportTable[index].pInfo);
|
|||
|
|
|||
|
return(pInfo);
|
|||
|
}
|
|||
|
|
|||
|
void
|
|||
|
UnjoinCompletionPort (
|
|||
|
void
|
|||
|
)
|
|||
|
{
|
|||
|
DWORD NumberOfBytes;
|
|||
|
ULONG_PTR CompletionKey;
|
|||
|
LPOVERLAPPED Overlapped;
|
|||
|
BOOL b;
|
|||
|
|
|||
|
// The kernel today doesn't have the functionality to
|
|||
|
// unjoin a thread from a completion port. Therefore
|
|||
|
// we fake unjoining by joining another completion port which has
|
|||
|
// unlimited concurrency called the inactive completion port.
|
|||
|
// Thus threads unjoined from the main completion port will not
|
|||
|
// affect its concurrency. One undesirable effect is that each
|
|||
|
// time a thread joined to the inactive completion port blocks,
|
|||
|
// it will try to wake up another thread, and there won't be any
|
|||
|
// there, which is a waste of CPU. Ideally, we should have had
|
|||
|
// a capability to set KTHREAD::Queue to NULL, but we don't
|
|||
|
b = GetQueuedCompletionStatus(InactiveRpcCompletionPort,
|
|||
|
&NumberOfBytes,
|
|||
|
&CompletionKey,
|
|||
|
&Overlapped,
|
|||
|
0
|
|||
|
);
|
|||
|
|
|||
|
// this operation should either timeout or fail - it should never
|
|||
|
// succeed. If it does, this means somebody has erroneously posted
|
|||
|
// an IO on the inactive completion port
|
|||
|
ASSERT(b == FALSE);
|
|||
|
}
|
|||
|
|
|||
|
#ifdef _INTERNAL_RPC_BUILD_
|
|||
|
void
|
|||
|
I_RpcltDebugSetPDUFilter (
|
|||
|
IN RPCLT_PDU_FILTER_FUNC pfnFilter
|
|||
|
)
|
|||
|
{
|
|||
|
gpfnFilter = pfnFilter;
|
|||
|
}
|
|||
|
#endif
|