1319 lines
39 KiB
C++
1319 lines
39 KiB
C++
/*+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
Copyright (c) 1998-2000 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
netmon.cxx
|
|
|
|
Abstract:
|
|
|
|
Generate C code for the stub DLL's used for Netmon debugging
|
|
|
|
Notes:
|
|
|
|
Two files are generated by this file:
|
|
xxx_netmon_stub.c
|
|
xxx_netmob_stub_obj.c
|
|
|
|
|
|
History:
|
|
|
|
- Created 7/28/98 by Max Attar Feingold
|
|
----------------------------------------------------------------------------*/
|
|
|
|
#include "becls.hxx"
|
|
|
|
// Stub version
|
|
#define NETMON_STUB_VERSION "(float) 1.0"
|
|
|
|
#pragma hdrstop
|
|
|
|
CG_STATUS
|
|
CG_NETMONSTUB_FILE::GenCode(
|
|
CCB * pCCB)
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate the Netmon stub file for classic and object interfaces
|
|
|
|
Arguments:
|
|
|
|
pCCB - The code gen controller block.
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
{
|
|
|
|
ISTREAM Stream( GetFileName(), 4 );
|
|
ISTREAM * pStream = pCCB->SetStream( &Stream, this );
|
|
|
|
m_pCCB = pCCB;
|
|
m_pStream = pStream;
|
|
|
|
// Scan for interfaces of the appropriate type
|
|
ScanInterfaces();
|
|
if (!m_bDoObject && !m_bClassic ||
|
|
m_bDoObject && !m_bObject) {
|
|
m_pStream->Close();
|
|
return CG_OK;
|
|
}
|
|
|
|
// If m_bDoObject is TRUE, then we're producing a stub file for object interfaces;
|
|
// otherwise, we're doing classic interfaces
|
|
if (m_bDoObject) {
|
|
EmitFileHeadingBlock (pCCB, "code for an object interface Netmon stub DLL",
|
|
"This file should be compiled as source for a DLL, linked with rpcrt4.lib");
|
|
} else {
|
|
EmitFileHeadingBlock (pCCB, "code for a classic interface Netmon stub DLL",
|
|
"This file should be compiled as source for a DLL, linked with rpcrt4.lib");
|
|
}
|
|
|
|
// Write standard include files
|
|
EmitStandardIncludes();
|
|
|
|
// Write local include files
|
|
EmitLocalIncludes();
|
|
|
|
// Write definitions (#defines and variables needed by the code)
|
|
EmitDefinitions();
|
|
|
|
if (m_bDoObject) {
|
|
|
|
// We seem to need this to solve a link error
|
|
OpenComment();
|
|
EmitComment ("This implementation is needed to solve a link error");
|
|
CloseComment();
|
|
pStream->NewLine();
|
|
pStream->Write ("ULONG STDMETHODCALLTYPE CStdStubBuffer_Release "\
|
|
"(IRpcStubBuffer *This) { ULONG u = 0; return u; }");
|
|
|
|
// Write the special data for object interfaces
|
|
EmitObjectInterfaceData();
|
|
}
|
|
|
|
// Write the server and client debug procedures for each interface
|
|
EmitDebugProcedures();
|
|
|
|
// Write the data tables
|
|
EmitDataTables();
|
|
|
|
// Close the header block
|
|
EmitFileClosingBlock( pCCB );
|
|
|
|
// Close the stream
|
|
pStream->Close();
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Descriptions:
|
|
|
|
Determine which types of interfaces (object or classic) we have and how many,
|
|
adding the names to the interface table
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
TRUE if yes
|
|
FALSE if no
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::ScanInterfaces() {
|
|
|
|
CG_ITERATOR I;
|
|
CG_NDR * pCG;
|
|
|
|
GetMembers( I );
|
|
while( ITERATOR_GETNEXT( I, pCG ) )
|
|
{
|
|
// We check GetChild()'s non-nullness here and elsewhere in the code because
|
|
// there are object interfaces without children that cause problems, such as IUnknown
|
|
if (pCG->GetChild()) {
|
|
switch(pCG->GetCGID())
|
|
{
|
|
case ID_CG_INTERFACE:
|
|
m_bClassic = TRUE;
|
|
m_lNumClassicInterfaces ++;
|
|
m_itInterfaceTable.AddInterface (((CG_INTERFACE*)pCG)->GetInterfaceName());
|
|
break;
|
|
|
|
case ID_CG_OBJECT_INTERFACE:
|
|
m_bObject = TRUE;
|
|
m_lNumObjectInterfaces ++;
|
|
m_itInterfaceTable.AddInterface (((CG_INTERFACE*)pCG)->GetInterfaceName());
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Descriptions:
|
|
|
|
Respectively,
|
|
-Open a comment block
|
|
-Write comments
|
|
-Close a comment block
|
|
|
|
Arguments:
|
|
|
|
pszString is the comment that should be emitted
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::OpenComment() {
|
|
|
|
m_pStream->NewLine();
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("/*");
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitComment (char* pszString) {
|
|
|
|
m_pStream->NewLine();
|
|
m_pStream->Write (" * ");
|
|
m_pStream->Write (pszString);
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
CG_STATUS CG_NETMONSTUB_FILE::CloseComment() {
|
|
|
|
m_pStream->NewLine();
|
|
m_pStream->Write (" */");
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate the standard includes for the netmon stub dll
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitStandardIncludes() {
|
|
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("#include <stdio.h>");
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate the local includes for the netmon stub dll.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
Local includes are the proxy and iid files for object interfaces
|
|
and the stub file for classic interfaces.
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitLocalIncludes() {
|
|
|
|
m_pStream->NewLine(2);
|
|
|
|
// This is defined to avoid target errors
|
|
m_pStream->Write ("#define _WIN32_DCOM");
|
|
|
|
// Write includes
|
|
m_pStream->NewLine();
|
|
if (m_bDoObject) {
|
|
m_pStream->Write ("#include \"");
|
|
m_pStream->Write (pCommand->GetProxyFName());
|
|
m_pStream->Write ("\"");
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("#include \"");
|
|
m_pStream->Write (pCommand->GetIIDFName());
|
|
m_pStream->Write ("\"");
|
|
m_pStream->NewLine();
|
|
} else {
|
|
m_pStream->Write ("#include \"");
|
|
m_pStream->Write (pCommand->GetSstubFName());
|
|
m_pStream->Write ("\"");
|
|
m_pStream->NewLine();
|
|
}
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate any definitions needed by the netmon stub dll.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
const char* ppszIncludeBlock[] = {
|
|
"#define DLL_EXPORT_PROC(x) __declspec(dllexport) GLOBAL_INTERFACE_DATA* GetGlobalInterfaceData__##x () { return &GlobalInterfaceData__##x ; }",
|
|
"#define NetmonStubAllocate(x) malloc(x)",
|
|
"#define Debug() *pNull = 0",
|
|
"static DWORD* pNull = NULL;",
|
|
NULL
|
|
};
|
|
|
|
const char* ppszGlobalInterfaceDataStructure[] = {
|
|
"float Version;",
|
|
"char* InterfaceName;",
|
|
"DWORD ProcFormatStringSize;",
|
|
"DWORD NumProcedures;",
|
|
"char** ProcedureNames;",
|
|
"SERVER_ROUTINE* ServerRoutineTable;",
|
|
"SERVER_ROUTINE* ClientRoutineTable;",
|
|
"RPC_SERVER_INTERFACE* RpcServerInterface;",
|
|
"void* DebugArgumentBuffer;",
|
|
NULL
|
|
};
|
|
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitDefinitions() {
|
|
|
|
if (!m_bDoObject) {
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("#pragma warning (disable : 4700) /* No warnings from the "\
|
|
"use of uninitialized variables */");
|
|
m_pStream->NewLine();
|
|
}
|
|
m_pStream->WriteBlock (ppszIncludeBlock);
|
|
|
|
OpenComment();
|
|
EmitComment ("Structure used to encapsulate all relevant interface information");
|
|
CloseComment();
|
|
|
|
m_pStream->NewLine(2);
|
|
m_pStream->Write ("typedef struct _GLOBAL_INTERFACE_DATA {");
|
|
Out_IndentInc( m_pCCB );
|
|
m_pStream->WriteBlock (ppszGlobalInterfaceDataStructure);
|
|
Out_IndentDec( m_pCCB );
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("} GLOBAL_INTERFACE_DATA;");
|
|
|
|
OpenComment();
|
|
EmitComment ("Function used to view unmarshalled argument buffers");
|
|
CloseComment();
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("static void DebugArgumentBuffer (BYTE* ArgumentBuffer, DWORD BufferSize) "\
|
|
"{ Debug(); }");
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate the data used specifically by object interfaces.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitObjectInterfaceData() {
|
|
|
|
CG_ITERATOR I;
|
|
CG_NDR * pCG;
|
|
|
|
OpenComment();
|
|
EmitComment ("Data used specifically by object interfaces");
|
|
CloseComment();
|
|
|
|
// Loop through all interfaces
|
|
GetMembers( I );
|
|
while( ITERATOR_GETNEXT( I, pCG ) ) {
|
|
|
|
if (pCG->GetChild()) {
|
|
|
|
switch(pCG->GetCGID()) {
|
|
|
|
case ID_CG_OBJECT_INTERFACE:
|
|
|
|
// Emit the object interface RPC_SERVER_INTERFACE structures
|
|
OpenComment();
|
|
EmitComment ("RPC_SERVER_INTERFACE structure for object interface ");
|
|
m_pStream->Write (pCG->GetSymName());
|
|
CloseComment();
|
|
|
|
EmitRPCServerInterface ((CG_INTERFACE*) pCG);
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate an RPC_SERVER_INTERFACE structure for the given interface
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
Most of the code was taken from misccls.cxx -> CG_INTERFACE::GenServerStub
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitRPCServerInterface (CG_INTERFACE* pCG) {
|
|
|
|
GUID_STRS TransferSyntaxGuid( TRANSFER_SYNTAX_GUID_STR_1,
|
|
TRANSFER_SYNTAX_GUID_STR_2,
|
|
TRANSFER_SYNTAX_GUID_STR_3,
|
|
TRANSFER_SYNTAX_GUID_STR_4,
|
|
TRANSFER_SYNTAX_GUID_STR_5);
|
|
GUID_STRS GuidStrs;
|
|
unsigned short M, m;
|
|
char Buffer[ _MAX_DRIVE + _MAX_DIR + _MAX_FNAME + _MAX_EXT + 1 ];
|
|
long ProtSeqEPCount = 0;
|
|
ITERATOR * pProtSeqIterator;
|
|
|
|
GuidStrs = pCG->GetGuidStrs();
|
|
|
|
m_pCCB->SetInterfaceCG (pCG);
|
|
m_pCCB->SetInterfaceName (pCG->GetInterfaceName());
|
|
m_pCCB->GetVersion( &M,&m );
|
|
|
|
sprintf( Buffer,
|
|
"&%s_%s_DispatchTable",
|
|
pCG->GetInterfaceName(),
|
|
m_pCCB->GenMangledName()
|
|
);
|
|
|
|
if ( ( pProtSeqIterator = pCG->GetProtSeqEps() ) != 0 )
|
|
{
|
|
ProtSeqEPCount = ITERATOR_GETCOUNT( *pProtSeqIterator );
|
|
Out_EP_Info( m_pCCB, pProtSeqIterator );
|
|
}
|
|
|
|
Out_IFInfo( m_pCCB, // controller block.
|
|
RPC_S_INT_INFO_TYPE_NAME, // interface info type name.
|
|
RPC_S_INT_INFO_STRUCT_NAME, // variable name.
|
|
SIZEOF_RPC_SERVER_INTERFACE, // string speicifying size.
|
|
GuidStrs, // Guid specified in idl
|
|
M, // user specified major version
|
|
m, // user specified minor version
|
|
// TransferSyntaxGuid, // ndr identifying guid.
|
|
// NDR_UUID_MAJOR_VERSION, // ndr's version
|
|
// NDR_UUID_MINOR_VERSION,
|
|
NULL, //Buffer,
|
|
ProtSeqEPCount, // if this is 0, then the next
|
|
// 2 fields are ignored by the call.
|
|
PROTSEQ_EP_TYPE_NAME, // RPC_PROTSEQ_ENDPOINT
|
|
PROTSEQ_EP_VAR_NAME, // ___RpcProtSeqEndpoint
|
|
m_pCCB->IsNoDefaultEpv(),
|
|
1,
|
|
pCG->HasPipes()
|
|
);
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate the debug procedures for all interfaces
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitDebugProcedures() {
|
|
|
|
CG_ITERATOR I;
|
|
CG_NDR * pCG;
|
|
|
|
CG_PROC * pProc;
|
|
CG_INTERFACE * pInt;
|
|
|
|
ID_CG idCg;
|
|
|
|
ITERATOR IProc;
|
|
|
|
char* pszInterfaceName, * pszLastInterfaceName, * pszOurInterfaceName;
|
|
|
|
// Write midl malloc and free if we're writing a classic interface stub
|
|
if (!m_bDoObject) {
|
|
OpenComment();
|
|
EmitComment ("Procedures used by the runtime to allocate and free memory");
|
|
CloseComment();
|
|
|
|
m_pStream->NewLine(2);
|
|
m_pStream->Write (
|
|
"static void * __RPC_API midl_user_allocate(size_t len) { return NetmonStubAllocate(len); }");
|
|
m_pStream->NewLine();
|
|
m_pStream->Write (
|
|
"static void __RPC_API midl_user_free(void * ptr) { free(ptr); }");
|
|
}
|
|
|
|
OpenComment();
|
|
EmitComment ("Implementation of debug server and client functions for all ");
|
|
if (m_bDoObject) {
|
|
m_pStream->Write ("object");
|
|
} else {
|
|
m_pStream->Write ("classic");
|
|
}
|
|
m_pStream->Write (" interfaces");
|
|
CloseComment();
|
|
|
|
// Loop through all interfaces
|
|
GetMembers( I );
|
|
while( ITERATOR_GETNEXT( I, pCG ) )
|
|
{
|
|
if (pCG->GetChild()) {
|
|
|
|
pInt = (CG_INTERFACE*) pCG;
|
|
idCg = pCG->GetCGID();
|
|
|
|
switch(idCg) {
|
|
|
|
case ID_CG_INTERFACE:
|
|
|
|
if (!m_bDoObject) {
|
|
|
|
// Loop through all member functions
|
|
pInt->GetAllMemberFunctions( IProc );
|
|
while( ITERATOR_GETNEXT( IProc, pProc ) ) {
|
|
|
|
EmitServerClientDebugProcedure (pProc, FALSE);
|
|
|
|
// Emit the procedure as is (to avoid link errors)
|
|
m_pStream->NewLine(2);
|
|
m_pStream->Write ("static");
|
|
Out_ClientProcedureProlog( m_pCCB, pProc->GetType() );
|
|
Out_IndentInc( m_pCCB );
|
|
|
|
// Return a variable of the appropriate return value
|
|
if (((node_proc*) pProc->GetType())->HasReturn()) {
|
|
m_pStream->Spaces( STANDARD_STUB_TAB );
|
|
m_pStream->Write( pProc->GetReturnType()->GetSymName() );
|
|
m_pStream->Write(" RetVal;");
|
|
m_pStream->NewLine();
|
|
m_pStream->Write( "return RetVal;" );
|
|
}
|
|
|
|
// Close the procedure
|
|
Out_IndentDec( m_pCCB );
|
|
Out_ProcClosingBrace( m_pCCB );
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
case ID_CG_OBJECT_INTERFACE:
|
|
|
|
if (m_bDoObject) {
|
|
|
|
pszOurInterfaceName = pInt->GetInterfaceName();
|
|
pszLastInterfaceName = NULL;
|
|
|
|
// Loop through all member functions
|
|
pInt->GetAllMemberFunctions ( IProc);
|
|
while( ITERATOR_GETNEXT( IProc, pProc ) ) {
|
|
|
|
// Get the procedure's real interface name
|
|
pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
|
|
|
|
// Write the function if:
|
|
// a) We're its real interface.
|
|
// b) The interface hasn't been used yet and it's not IUnknown
|
|
// c) The interface name is the same as the last one we used
|
|
// (so it's OK to use it again)
|
|
if (strcmp (pszInterfaceName, pszOurInterfaceName) == 0 ||
|
|
(!m_itOutputInterfaceTable.FindInterface (pszInterfaceName) &&
|
|
strcmp (pszInterfaceName, "IUnknown") != 0) ||
|
|
(pszLastInterfaceName != NULL &&
|
|
strcmp (pszInterfaceName, pszLastInterfaceName) == 0)
|
|
) {
|
|
|
|
// Write the server and client debug procedures
|
|
EmitServerClientDebugProcedure (pProc, TRUE);
|
|
|
|
// Add the interface name to the table of used interfaces
|
|
m_itOutputInterfaceTable.AddInterface (pszInterfaceName);
|
|
|
|
// Record the last interface name used
|
|
pszLastInterfaceName = pszInterfaceName;
|
|
}
|
|
}
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate server, client and link stub procedures for a given procedure
|
|
|
|
Arguments:
|
|
|
|
pProc -> The procedure to be processed
|
|
bIsObject -> TRUE if the procedure belongs to an object interface
|
|
FALSE otherwise
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
This function is long and ugly
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitServerClientDebugProcedure (CG_PROC* pProc, BOOL bIsObject) {
|
|
|
|
PNAME pszProcName, pszNewProcName;
|
|
|
|
node_proc * pNodeProc = ((node_proc*) pProc->GetType());
|
|
node_param * pRetValParam, * pThisParam, * pParam;
|
|
node_skl * pOldRetType;
|
|
node_base_type * pVoidBaseType;
|
|
type_node_list ITypeNodeList;
|
|
|
|
short i, siNumArgs;
|
|
PNAME* ppszOldArgName;
|
|
PNAME* ppszNewArgName;
|
|
node_base_attr * pNodeAttribIn, * pNodeAttribOut;
|
|
|
|
BOOL bHasReturn = pNodeProc->HasReturn();
|
|
|
|
// Get the procedure name
|
|
pszProcName = pProc->GetSymName();
|
|
|
|
// Rename the procedure to Server__InterfaceName__ProcName
|
|
pszNewProcName = new char [strlen (pProc->GetInterfaceName()) +
|
|
strlen (pszProcName) + 11];
|
|
sprintf ( pszNewProcName, "Server__%s__%s", pProc->GetInterfaceName(), pszProcName );
|
|
pNodeProc->SetSymName ( pszNewProcName );
|
|
|
|
// Set void type
|
|
pVoidBaseType = new node_base_type( NODE_VOID, ATTR_BASE );
|
|
pVoidBaseType->SetSymName( "void" );
|
|
pOldRetType = pNodeProc->GetChild();
|
|
pNodeProc->SetChild( pVoidBaseType );
|
|
|
|
// Rename the arguments to IN__Name, OUT__Name or IN_OUT__name
|
|
i = 0;
|
|
siNumArgs = pNodeProc->GetNumberOfArguments();
|
|
|
|
ppszOldArgName = new PNAME [siNumArgs];
|
|
ppszNewArgName = new PNAME [siNumArgs];
|
|
|
|
pNodeProc->GetParameterList (&ITypeNodeList);
|
|
while( ITERATOR_GETNEXT( ITypeNodeList, pParam ) ) {
|
|
|
|
MIDL_ASSERT (i < siNumArgs);
|
|
|
|
ppszOldArgName[i] = pParam->GetSymName();
|
|
ppszNewArgName[i] = new char [strlen (ppszOldArgName[i]) + 10];
|
|
|
|
if ((pNodeAttribIn = pParam->GetAttribute (ATTR_IN)) &&
|
|
(pNodeAttribOut = pParam->GetAttribute (ATTR_OUT))) {
|
|
sprintf (ppszNewArgName[i], "IN_OUT__%s", ppszOldArgName[i]);
|
|
} else {
|
|
|
|
if (pNodeAttribIn) {
|
|
sprintf (ppszNewArgName[i], "IN__%s", ppszOldArgName[i]);
|
|
} else {
|
|
sprintf (ppszNewArgName[i], "OUT__%s", ppszOldArgName[i]);
|
|
}
|
|
}
|
|
pParam->SetSymName (ppszNewArgName[i]);
|
|
i ++;
|
|
}
|
|
|
|
// If proc belongs to an object interface, add the 'this' pointer
|
|
if (bIsObject) {
|
|
pThisParam = new node_param();
|
|
pThisParam->SetAttribute (ATTR_IN);
|
|
pThisParam->SetSymName ("this");
|
|
pThisParam->SetBasicType( (node_skl *) new node_def( "void*" ));
|
|
pThisParam->SetEdgeType( EDGE_USE );
|
|
|
|
pNodeProc->AddFirstMember (pThisParam);
|
|
}
|
|
|
|
// Emit the server procedure
|
|
m_pStream->NewLine(2);
|
|
m_pStream->Write ("static");
|
|
Out_ClientProcedureProlog( m_pCCB, pNodeProc );
|
|
Out_IndentInc( m_pCCB );
|
|
m_pStream->Spaces( STANDARD_STUB_TAB );
|
|
m_pStream->Write( "Debug();" );
|
|
Out_IndentDec( m_pCCB );
|
|
Out_ProcClosingBrace( m_pCCB );
|
|
|
|
// Rename the procedure to Client__InterfaceName__ProcName
|
|
sprintf( pszNewProcName, "Client__%s__%s", pProc->GetInterfaceName(), pszProcName );
|
|
|
|
// Add a RetVal parameter to the param list if the function isn't void
|
|
if (bHasReturn) {
|
|
|
|
pRetValParam = new node_param();
|
|
pRetValParam->SetAttribute (ATTR_IN);
|
|
pRetValParam->SetSymName ("RetVal");
|
|
pRetValParam->SetBasicType( (node_skl *) new node_def( pProc->GetReturnType()->GetSymName() ));
|
|
pRetValParam->SetEdgeType( EDGE_USE );
|
|
|
|
pNodeProc->AddLastMember (pRetValParam);
|
|
}
|
|
|
|
// Emit the client procedure
|
|
m_pStream->NewLine(2);
|
|
m_pStream->Write ("static");
|
|
Out_ClientProcedureProlog( m_pCCB, pNodeProc );
|
|
Out_IndentInc( m_pCCB );
|
|
m_pStream->Spaces( STANDARD_STUB_TAB );
|
|
m_pStream->Write( "Debug();" );
|
|
Out_IndentDec( m_pCCB );
|
|
Out_ProcClosingBrace( m_pCCB );
|
|
|
|
// Delete the this parameter we created
|
|
if (bIsObject) {
|
|
pNodeProc->RemoveFirstMember();
|
|
delete pThisParam;
|
|
}
|
|
|
|
// Delete the RetVal from the param List
|
|
if (bHasReturn) {
|
|
pNodeProc->RemoveLastMember();
|
|
delete pRetValParam;
|
|
}
|
|
|
|
// Restore the procedure's name
|
|
pNodeProc->SetSymName( pszProcName );
|
|
|
|
// Restore the procedure's parameter names
|
|
if (siNumArgs > 0) {
|
|
i = 0;
|
|
pNodeProc->GetParameterList (&ITypeNodeList);
|
|
while( ITERATOR_GETNEXT( ITypeNodeList, pParam ) ) {
|
|
pParam->SetSymName (ppszOldArgName[i]);
|
|
delete [] ppszNewArgName[i];
|
|
i ++;
|
|
}
|
|
delete [] ppszNewArgName;
|
|
delete [] ppszOldArgName;
|
|
}
|
|
|
|
// Restore the node's return type
|
|
pNodeProc->SetChild( pOldRetType );
|
|
|
|
// Delete the fake void type node we created
|
|
delete pVoidBaseType;
|
|
|
|
// Delete the new name we created
|
|
delete [] pszNewProcName;
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate data tables for each interface
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitDataTables() {
|
|
|
|
// Write procedure name tables for each interface
|
|
EmitProcNameTables();
|
|
|
|
// Write the server and client lookup tables for each interface
|
|
EmitServerClientTables();
|
|
|
|
// Write the global_interface_data structures and exports for each interface
|
|
EmitGlobalInterfaceData();
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate tables with each interface's procedure names and the number of
|
|
procedures in each interface.
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitProcNameTables() {
|
|
|
|
CG_ITERATOR I;
|
|
CG_NDR * pCG;
|
|
CG_INTERFACE* pInt;
|
|
|
|
ITERATOR IProc;
|
|
CG_PROC * pProc;
|
|
|
|
long lNumProcs;
|
|
BOOL bBeenHereBefore;
|
|
|
|
char* pszInterfaceName;
|
|
char pszTemp [1024];
|
|
|
|
GetMembers( I );
|
|
while( ITERATOR_GETNEXT( I, pCG ) ) {
|
|
|
|
if (pCG->GetChild()) {
|
|
|
|
pInt = (CG_INTERFACE*) pCG;
|
|
|
|
if (m_bDoObject && pCG->GetCGID() == ID_CG_OBJECT_INTERFACE ||
|
|
!m_bDoObject && pCG->GetCGID() == ID_CG_INTERFACE) {
|
|
|
|
lNumProcs = 0;
|
|
bBeenHereBefore = FALSE;
|
|
|
|
pszInterfaceName = pInt->GetInterfaceName();
|
|
|
|
OpenComment();
|
|
EmitComment ("Procedure tables for interface ");
|
|
m_pStream->Write (pszInterfaceName);
|
|
CloseComment();
|
|
|
|
m_pStream->NewLine();
|
|
sprintf (pszTemp, "static char* %s__ProcedureNames[] = {", pszInterfaceName);
|
|
m_pStream->Write (pszTemp);
|
|
|
|
Out_IndentInc( m_pCCB );
|
|
|
|
pInt->GetAllMemberFunctions ( IProc);
|
|
while( ITERATOR_GETNEXT( IProc, pProc ) ) {
|
|
|
|
pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
|
|
if (strcmp (pszInterfaceName, "IUnknown") != 0) {
|
|
|
|
// Write a comma if we're not the first function in the list
|
|
if (bBeenHereBefore) {
|
|
m_pStream->Write (",");
|
|
} else {
|
|
bBeenHereBefore = TRUE;
|
|
}
|
|
|
|
// Write procedure name enclosed in quotes
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("\"");
|
|
m_pStream->Write (pProc->GetSymName());
|
|
m_pStream->Write ("\"");
|
|
|
|
// Increment procedure count
|
|
lNumProcs ++;
|
|
}
|
|
}
|
|
|
|
Out_IndentDec( m_pCCB );
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("};");
|
|
|
|
// Set number of procedures in interface table
|
|
m_itInterfaceTable.SetNumProcedures (pszInterfaceName, lNumProcs);
|
|
}
|
|
} // if
|
|
} // while
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate the server and client lookup tables for each interface
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitServerClientTables() {
|
|
|
|
CG_ITERATOR I;
|
|
CG_NDR * pCG;
|
|
CG_INTERFACE* pInt;
|
|
|
|
ITERATOR IProc;
|
|
CG_PROC * pProc;
|
|
|
|
char* pszInterfaceName;
|
|
|
|
GetMembers( I );
|
|
while( ITERATOR_GETNEXT( I, pCG ) ) {
|
|
|
|
if (pCG->GetChild()) {
|
|
|
|
if (m_bDoObject && pCG->GetCGID() == ID_CG_OBJECT_INTERFACE ||
|
|
!m_bDoObject && pCG->GetCGID() == ID_CG_INTERFACE) {
|
|
|
|
pInt = (CG_INTERFACE*) pCG;
|
|
pszInterfaceName = pInt->GetInterfaceName();
|
|
|
|
// Write the server table
|
|
OpenComment();
|
|
EmitComment ("Debug server procedures for interface ");
|
|
m_pStream->Write (pszInterfaceName);
|
|
CloseComment();
|
|
m_pStream->NewLine();
|
|
|
|
m_pStream->Write ("static SERVER_ROUTINE ");
|
|
m_pStream->Write (pszInterfaceName);
|
|
m_pStream->Write ("__ServerRoutineTable[] = {");
|
|
Out_IndentInc( m_pCCB );
|
|
|
|
// If we're processing an object interface,
|
|
// we have to make room for the 3 IUnknown procedures
|
|
if (m_bDoObject) {
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("NULL,");
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("NULL,");
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("NULL,");
|
|
}
|
|
|
|
pInt->GetAllMemberFunctions ( IProc);
|
|
while( ITERATOR_GETNEXT( IProc, pProc ) ) {
|
|
|
|
pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
|
|
if (strcmp (pszInterfaceName, "IUnknown") != 0) {
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("(SERVER_ROUTINE)Server__");
|
|
m_pStream->Write (pProc->GetInterfaceNode()->GetSymName());
|
|
m_pStream->Write ("__");
|
|
m_pStream->Write (pProc->GetSymName());
|
|
m_pStream->Write (",");
|
|
}
|
|
}
|
|
Out_IndentDec( m_pCCB );
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("};");
|
|
|
|
// Write client table
|
|
OpenComment();
|
|
EmitComment ("Debug client procedures for interface ");
|
|
m_pStream->Write (pszInterfaceName);
|
|
CloseComment();
|
|
m_pStream->NewLine();
|
|
|
|
m_pStream->Write ("static SERVER_ROUTINE ");
|
|
m_pStream->Write (pszInterfaceName);
|
|
m_pStream->Write ("__ClientRoutineTable[] = {");
|
|
Out_IndentInc( m_pCCB );
|
|
|
|
// If we're processing an object interface,
|
|
// we have to make room for the 3 IUnknown procedures
|
|
if (m_bDoObject) {
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("NULL,");
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("NULL,");
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("NULL,");
|
|
}
|
|
|
|
pInt->GetAllMemberFunctions ( IProc);
|
|
while( ITERATOR_GETNEXT( IProc, pProc ) ) {
|
|
|
|
pszInterfaceName = pProc->GetInterfaceNode()->GetInterfaceName();
|
|
if (strcmp (pszInterfaceName, "IUnknown") != 0) {
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("(SERVER_ROUTINE)Client__");
|
|
m_pStream->Write (pProc->GetInterfaceNode()->GetSymName());
|
|
m_pStream->Write ("__");
|
|
m_pStream->Write (pProc->GetSymName());
|
|
m_pStream->Write (",");
|
|
}
|
|
}
|
|
Out_IndentDec( m_pCCB );
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("};");
|
|
}
|
|
}
|
|
}
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
|
|
|
|
/*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
|
|
|
|
Routine Description:
|
|
|
|
Generate the global interface data structures for each interface, as well
|
|
as the export functions that return them
|
|
|
|
Arguments:
|
|
|
|
Return Value:
|
|
|
|
CG_OK if all is well.
|
|
|
|
Notes:
|
|
|
|
----------------------------------------------------------------------------*/
|
|
CG_STATUS CG_NETMONSTUB_FILE::EmitGlobalInterfaceData() {
|
|
|
|
CG_ITERATOR I;
|
|
CG_NDR * pCG;
|
|
ID_CG idCG;
|
|
|
|
char pszTemp [100], * pszInterfaceName, * pszFixedUuid;
|
|
char * p1, * p2, * p3, * p4, * p5;
|
|
long lNumProcs;
|
|
size_t lLength;
|
|
|
|
GetMembers( I );
|
|
while( ITERATOR_GETNEXT( I, pCG ) ) {
|
|
|
|
idCG = pCG->GetCGID();
|
|
|
|
if (((idCG == ID_CG_OBJECT_INTERFACE && m_bDoObject) ||
|
|
(idCG == ID_CG_INTERFACE && !m_bDoObject)) &&
|
|
pCG->GetChild()) {
|
|
|
|
// Get the underscored uuid
|
|
((CG_INTERFACE*) pCG)->GetGuidStrs().GetStrs (p1, p2, p3, p4, p5);
|
|
lLength = (long) strlen (p1) + strlen (p2) + strlen (p3) + strlen (p4) + strlen (p5);
|
|
pszFixedUuid = new char [lLength + 5];
|
|
sprintf (pszFixedUuid, "%s_%s_%s_%s_%s", p1, p2, p3, p4, p5);
|
|
pszFixedUuid = _strlwr (pszFixedUuid);
|
|
|
|
// Get the interface name
|
|
pszInterfaceName = pCG->GetSymName();
|
|
|
|
// Write a comment
|
|
OpenComment();
|
|
EmitComment ("GLOBAL_INTERFACE_DATA structure for interface ");
|
|
m_pStream->Write (pszInterfaceName);
|
|
CloseComment();
|
|
|
|
// Header
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("static GLOBAL_INTERFACE_DATA GlobalInterfaceData__");
|
|
m_pStream->Write (pszFixedUuid);
|
|
m_pStream->Write (" = {");
|
|
Out_IndentInc( m_pCCB );
|
|
|
|
// Version
|
|
m_pStream->NewLine();
|
|
m_pStream->Write (NETMON_STUB_VERSION);
|
|
m_pStream->Write (",");
|
|
|
|
// Name
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("\"");
|
|
m_pStream->Write (pszInterfaceName);
|
|
m_pStream->Write ("\",");
|
|
|
|
// Proc format string size
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("PROC_FORMAT_STRING_SIZE,");
|
|
|
|
// NumProcs
|
|
m_pStream->NewLine();
|
|
if (m_itInterfaceTable.GetNumProcedures (pszInterfaceName, &lNumProcs)) {
|
|
m_pStream->Write (_itoa (lNumProcs, pszTemp, 10));
|
|
} else {
|
|
MIDL_ASSERT (FALSE);
|
|
}
|
|
m_pStream->Write (",");
|
|
|
|
// Proc name table
|
|
m_pStream->NewLine();
|
|
m_pStream->Write (pszInterfaceName);
|
|
m_pStream->Write ("__ProcedureNames,");
|
|
|
|
// Server routine table
|
|
m_pStream->NewLine();
|
|
m_pStream->Write (pszInterfaceName);
|
|
m_pStream->Write ("__ServerRoutineTable,");
|
|
|
|
// Client routine table
|
|
m_pStream->NewLine();
|
|
m_pStream->Write (pszInterfaceName);
|
|
m_pStream->Write ("__ClientRoutineTable,");
|
|
|
|
// Rpc server interface pointer
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("(RPC_SERVER_INTERFACE*) &");
|
|
m_pStream->Write (pszInterfaceName);
|
|
m_pStream->Write ("___RpcServerInterface,");
|
|
|
|
// DebugArgumentBuffer
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("(void*) DebugArgumentBuffer");
|
|
|
|
Out_IndentDec( m_pCCB );
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("};");
|
|
|
|
// Export function
|
|
OpenComment();
|
|
EmitComment ("Export function for interface ");
|
|
m_pStream->Write (pszInterfaceName);
|
|
CloseComment();
|
|
|
|
m_pStream->NewLine();
|
|
m_pStream->Write ("DLL_EXPORT_PROC (");
|
|
m_pStream->Write (pszFixedUuid);
|
|
m_pStream->Write (")");
|
|
|
|
delete [] pszFixedUuid;
|
|
}
|
|
}
|
|
|
|
return CG_OK;
|
|
}
|
|
|
|
/****************************************************
|
|
* InterfaceTable implementations
|
|
****************************************************/
|
|
#define NUM_BUCKETS 100
|
|
NetmonStubFileInterfaceTable::NetmonStubFileInterfaceTable() {
|
|
m_pTable = new NetmonStubFileInterfaceList [NUM_BUCKETS];
|
|
}
|
|
|
|
NetmonStubFileInterfaceTable::~NetmonStubFileInterfaceTable() {
|
|
delete [] m_pTable;
|
|
}
|
|
|
|
void NetmonStubFileInterfaceTable::AddInterface (char* pszInterface) {
|
|
m_pTable[GetHashValue (pszInterface)].AddInterface (pszInterface);
|
|
}
|
|
|
|
// Return true if the interface name was found
|
|
BOOL NetmonStubFileInterfaceTable::FindInterface (char* pszInterface) {
|
|
return m_pTable[GetHashValue (pszInterface)].FindInterface (pszInterface);
|
|
}
|
|
|
|
// Set the number of procedures in the interface
|
|
BOOL NetmonStubFileInterfaceTable::SetNumProcedures (char* pszInterface, long lNumProcs) {
|
|
return m_pTable[GetHashValue (pszInterface)].SetNumProcedures (pszInterface, lNumProcs);
|
|
}
|
|
|
|
// Get the number of procedures in the interface
|
|
BOOL NetmonStubFileInterfaceTable::GetNumProcedures (char* pszInterface, long* plNumProcs) {
|
|
return m_pTable[GetHashValue (pszInterface)].GetNumProcedures (pszInterface, plNumProcs);
|
|
}
|
|
|
|
// The hash value is just the sum of the characters in the interface name
|
|
// mod the number of buckets in the table
|
|
long NetmonStubFileInterfaceTable::GetHashValue (char* pszInterface) {
|
|
|
|
long i, lSum = 0, lLen = (long) strlen (pszInterface);
|
|
for (i = 0; i < lLen; i ++) {
|
|
lSum += (long) pszInterface[i];
|
|
}
|
|
return lSum % NUM_BUCKETS;
|
|
}
|
|
|
|
|
|
/* InterfaceNode */
|
|
NetmonStubFileInterfaceNode::NetmonStubFileInterfaceNode (char* pszInterface) {
|
|
m_pszInterface = new char [strlen (pszInterface) + 1];
|
|
strcpy (m_pszInterface, pszInterface);
|
|
m_pNext = NULL;
|
|
m_lNumProcs = 0;
|
|
}
|
|
|
|
NetmonStubFileInterfaceNode::~NetmonStubFileInterfaceNode() {
|
|
delete [] m_pszInterface;
|
|
}
|
|
|
|
void NetmonStubFileInterfaceNode::SetNext (NetmonStubFileInterfaceNode* pNext) {
|
|
m_pNext = pNext;
|
|
}
|
|
|
|
NetmonStubFileInterfaceNode* NetmonStubFileInterfaceNode::GetNext() {
|
|
return m_pNext;
|
|
}
|
|
|
|
char* NetmonStubFileInterfaceNode::GetInterface() {
|
|
return m_pszInterface;
|
|
}
|
|
|
|
void NetmonStubFileInterfaceNode::SetNumProcedures (long lNumProcs) {
|
|
m_lNumProcs = lNumProcs;
|
|
}
|
|
|
|
long NetmonStubFileInterfaceNode::GetNumProcedures() {
|
|
return m_lNumProcs;
|
|
}
|
|
|
|
/* InterfaceList */
|
|
NetmonStubFileInterfaceList::NetmonStubFileInterfaceList() {
|
|
m_pHead = NULL;
|
|
m_pTail = NULL;
|
|
}
|
|
|
|
NetmonStubFileInterfaceList::~NetmonStubFileInterfaceList() {
|
|
NetmonStubFileInterfaceNode* pNode = m_pHead, *pDeleteNode;
|
|
while (pNode != NULL) {
|
|
pDeleteNode = pNode;
|
|
pNode = pNode->GetNext();
|
|
delete pDeleteNode;
|
|
}
|
|
}
|
|
|
|
void NetmonStubFileInterfaceList::AddInterface (char* pszInterface) {
|
|
|
|
NetmonStubFileInterfaceNode* pNode = new NetmonStubFileInterfaceNode (pszInterface);
|
|
|
|
if (m_pHead == NULL) {
|
|
m_pHead = m_pTail = pNode;
|
|
} else {
|
|
m_pTail->SetNext (pNode);
|
|
m_pTail = pNode;
|
|
}
|
|
}
|
|
|
|
BOOL NetmonStubFileInterfaceList::FindInterface (char* pszInterface) {
|
|
|
|
NetmonStubFileInterfaceNode* pNode = m_pHead;
|
|
while (pNode != NULL) {
|
|
if (strcmp (pszInterface, pNode->GetInterface()) == 0) {
|
|
return TRUE;
|
|
}
|
|
pNode = pNode->GetNext();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL NetmonStubFileInterfaceList::SetNumProcedures (char* pszInterface, long lNumProcs) {
|
|
|
|
NetmonStubFileInterfaceNode* pNode = m_pHead;
|
|
while (pNode != NULL) {
|
|
if (strcmp (pszInterface, pNode->GetInterface()) == 0) {
|
|
pNode->SetNumProcedures (lNumProcs);
|
|
return TRUE;
|
|
}
|
|
pNode = pNode->GetNext();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
BOOL NetmonStubFileInterfaceList::GetNumProcedures (char* pszInterface, long* plNumProcs) {
|
|
|
|
NetmonStubFileInterfaceNode* pNode = m_pHead;
|
|
while (pNode != NULL) {
|
|
if (strcmp (pszInterface, pNode->GetInterface()) == 0) {
|
|
*plNumProcs = pNode->GetNumProcedures();
|
|
return TRUE;
|
|
}
|
|
pNode = pNode->GetNext();
|
|
}
|
|
|
|
return FALSE;
|
|
}
|