asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search








Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Creating an SNMP Component - Part 2
By Ben Garcia
Rating: 4.5 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    Welcome to Part 2 of the SNMP component series! The first article explained the background and architecture of the Simple Network Management Protocol, and included source code for a component written in Visual C++ 6.0. Listed below is an outline of what will be discussed in this article:

    • Visual C++ 6.0 NetMgr Component
      • Restructured Get/Set methods to allow multiple OIDs to be passed in
      • Added the GetNext method
      • Demonstrates how to pass arrays between Visual Basic and Visual C++, as well as Variant type manipulation
    • Visual Basic 6.0 NetMgrVB Component
      • Ported version of NetMgr component using the wsnmp32 library instead of the SnmpAPI and MgmtAPI libraries
      • Added GetNext method

    This article has made the following assumptions:

    • The reader has a good understanding of:
      • Visual C++ 6.0
      • Visual Basic 6.0
      • Component Object Model
    • The SNMP version being used is 1.0
    • Only Get, GetNext and Set operations are being demonstrated; therefore, only code that corresponds to those operations are listed
    • The source code provided shows how to communicate with SNMP agents.

    NetMgr Component Updates

    As stated above, GetNext and passing multiple OIDs to the Get/Set methods have been added to this version of NetMgr. The entire object will be not laid out as in the last article. Instead, only the major differences between the two will be explained in detail. Also, the entire project including a pre-compiled DLL will be provided with this article.

    SNMPv1.cpp

    All major changes have occurred in the SNMPv1.cpp file. Granted that changes had to made to NetMgr.idl and SNMPv1.h, but they've been categorized as minor changes and will not be listed in detail.

    Get Method

    The previous version would only allow a single OID to be passed in the oidStr argument. Now we're allowed to choose between passing a single OID or an array of OIDs. Let's begin to dissect the new Get method.

    First we have to change the function definition to accommodate arrays being passed to and from Visual Basic and VBScript.

    
    STDMETHODIMP CSNMPv1::Get(VARIANT *varOID, VARIANT* varRetVal)
    
    
    Next the local variable declarations will change slightly to help with the SAFEARRAY type.
    
    	// Local variable declarations -- P1
    	RFC1157VarBindList  vb;		// Variable Binding List; Binds OID to value
    	AsnObjectIdentifier objID;	// The actual OID itself
    	AsnInteger	    errorStatus;	// Error type that is returned if encountered
    	AsnInteger	    errorIndex;	// Works with variable above
    
    	// Local variable declarations -- P2
    	VARIANT	  *lvarArr;		// place holder
    	BSTR	  *bstrArr;			// place holder
    	SAFEARRAY *psa;			// temporary array
    	HRESULT	  hr;
    
    	// Local variable declarations -- P3
    	char		 asciiStr[255];	// Utility variable
    	char		 lcOID[255];	// Local char reference to the oidStr parameter
    	int		 ret;			// Used for return values
    	unsigned int i;			// Used as a counter
    
    	// Initialize VariableBinding List
    	vb.list = NULL;
    	vb.len = 1;
    
    
    In this project the actual ASN parsing was moved to different function simply because both this and the GetNext functions use identical statements.

    After local variable declarations and variable binding initialization, the next step is to test the varOID argument to determine if it is an array or a string. If it is an array and it has equal to or greater than 1 element, psa, lvarArr and bstrArr are initialized for use.

    
    	// Verify what our varOID variable contains
    	if (varOID->vt == (VT_ARRAY | VT_BSTR))
    	{
    		// Validate that more than 1 element has been passed
    	if ((vb.len = varOID->parray->rgsabound[0].cElements) < 1)
    		{
    			// Return Error
    varRetVal->vt = VT_BSTR;
    varRetVal->bstrVal = SysAllocString(L"Error: Array size less than 1");
    
    			return S_OK;
    		}
    
    		// Create SafeArray element bounds by using length of 
    		// VariableBinding List
    		SAFEARRAYBOUND bounds[] = {(int)vb.len, 0};
    
    		// Create a Single-Dimensional SafeArray with a type 
    		// of Variant
    		psa = SafeArrayCreate(VT_VARIANT, 1, bounds);
    
    		// Assign pointer lvarArr to psa
    		SafeArrayAccessData(psa, (LPVOID*) &lvarArr);
    
    		// Assign varOID array elements to bstrArr
    		bstrArr = (BSTR *) (*varOID).parray->pvData;
    	}
    
    
    It's worth mentioning the purpose that psa, lvarArr and bstrArr serve here, as well as in other functions. The variable psa represents the actual array that will contain our return values from the agent that correlate to their respective OIDs. Using the SafeArrayAccessData function, lvarArr becomes the pointer to the psa SafeArray. So element manipulation occurs through lvarArr and not psa directly. To get the current values out of the varOID variable, bstrArr is used by acting as a container for the array elements.
    
    	// VariableBinding List Allocation
    if ((vb.list = (RFC1157VarBind *)SNMP_realloc(vb.list, sizeof(RFC1157VarBind) *vb.len)) == NULL)
    	{
    		// Return Error
    		varRetVal->vt = VT_BSTR;
    	varRetVal->bstrVal = SysAllocString(L"Error: Error allocating OID");
    
    		// Remove binding to lvarArr if varOID is an Array
    		if (varOID->vt == (VT_ARRAY | VT_BSTR))
    			hr = SafeArrayUnaccessData(psa);
    
    		return S_OK;
    	}
    
    	// Assign values to VariableBinding List
    	for (i = 0; i < vb.len; i++)
    	{
    		// Validate varOID type and convert 
    		// appropriate reference to char
    		if (varOID->vt == (VT_ARRAY | VT_BSTR))
    			sprintf(lcOID, "%ls", bstrArr[i]);
    		else
    			sprintf(lcOID, "%ls", (*varOID).bstrVal);
    
    		// Convert the string OID to the AsnObjectIdentifier data type
    		ret = SnmpMgrStrToOid((LPSTR)lcOID, &objID);
    
    		// Assigning OID to variable bindings list
    		vb.list[i].name = objID;
    		vb.list[i].value.asnType = ASN_NULL;
    	}
    
    
    If a string is passed, vb.len is equal to 1 and varOID->vt is equal to VT_BSTR. So only one iteration will occur and (*varOID).bstrVal is used instead of bstrArr[i].
    
    	// Make request to agent
    	if (!SnmpMgrRequest(session, ASN_RFC1157_GETREQUEST, &vb, &errorStatus, &errorIndex))
    	{
    		// Return Error
    sprintf(asciiStr,"Error: SnmpMgrRequest could not carry out desired option 
    (Error %d)", errorStatus);
    		lpVal = (LPSTR)asciiStr;
    
    		varRetVal->vt = VT_BSTR;
    		varRetVal->bstrVal = lpVal.AllocSysString();
    
    		// Remove binding to lvarArr if varOID is an Array
    		if (varOID->vt == (VT_ARRAY | VT_BSTR))
    			hr = SafeArrayUnaccessData(psa);
    	} 
    	  else
    	{
    		// Verify that no error exists
    		if (errorStatus > 0)
    		{
    			// Return Error
    varRetVal->vt = VT_BSTR;
    varRetVal->bstrVal = SysAllocString(L"Error: Error with Agent Process");
    
    			// Remove binding to lvarArr if varOID is an Array
    			if (varOID->vt == (VT_ARRAY | VT_BSTR))
    				hr = SafeArrayUnaccessData(psa);
    		}
    		  else
    		{
    			// Retrieve ASN value
    			if (varOID->vt == (VT_ARRAY | VT_BSTR))
    			{
    				// Loop through all elements in the VariableBinding
    				// List
    				for (i = 0; i < vb.len; i++)
    				{
    					// Initialize variant array element
    					VariantInit(&lvarArr[i]);
    
    					// Set VarType to BSTR
    					lvarArr[i].vt = VT_BSTR;
    
    					// Extract ASN Value from VB List Element
    					lvarArr[i].bstrVal = extractAsnValue(vb.list[i]);
    				}
    
    
    Within the For...Next loop we initialize lvarArr[i]as a variant, then set its type to VT_BSTR (in this component, all values passed back are defined as strings [BSTR]). Once that is completed, extractAsnValue(...) has its return value assigned to lvarArr[i]. Function extractAsnValue(...) contains the actual parsing of the ASN value (vb.list[i].value is defined as an AsnAny type).
    
    				// Remove binding to lvarArr
    				SafeArrayUnaccessData(psa);
    
    				// Initialize varRetVal
    				VariantInit(varRetVal);
    
    				// Set VarType to a Variant Array
    				varRetVal->vt = VT_ARRAY | VT_VARIANT;
    
    				// Assign local SafeArray psa to return variable
    				varRetVal->parray = psa;
    			}
    			  else
    			{
    				// Initialize varRetVal
    				VariantInit(varRetVal);
    
    				// Set VarType to BSTR
    				varRetVal->vt = VT_BSTR;
    
    				// Assign ASN Value to return variable
    				varRetVal->bstrVal = extractAsnValue(vb.list[0]);
    			}
    
    		}
    	}
    
    	// Release variable binding list
    	SnmpUtilVarBindListFree(&vb);
    
    	// Done
    	return S_OK;
    }
    
    
    Set Method

    As with the Get Method, we've modified this to allow manipulation of arrays in the event that multiple sets must occur in a single transmission.

    
    STDMETHODIMP CSNMPv1::Set(VARIANT *varOID, VARIANT *value, BSTR *statusCode)
    {
    	...
    
    	// Local variable declarations -- P2
    	BSTR	  *bstrArr, *bstrValue;		// Pointers to string arrays
    
    	// Local variable declarations -- P2
    	...
    	long	     *lngArr;			// Pointer to long array
    	...
    
    
    This is similar to the Get Method with the difference being that no SAFEARRAY declarations have occurred. The reason being that an array is not going to be passed back, only a status message reporting success or type of failure. The bstrArr pointer represents the array of elements from the varOID variable, while bstrValue and lngArr deal with the array of elements from the value variable (value->vt determines which pointer is used).
    
    	...
    	// Verify what our varOID variable contains
    	if (varOID->vt == (VT_ARRAY | VT_BSTR))
    	{
    		// Validate that more than 1 element has been passed
    	if ((vb.len = varOID->parray->rgsabound[0].cElements) < 1)
    		{
    			// Return Error
    		(*statusCode) = SysAllocString(L"Error: Array size less than 1");
    			return S_OK;
    		}
    
    		// Assign varOID array elements to bstrArr
    		bstrArr = (BSTR *) (*varOID).parray->pvData;
    
    		// Validate Array VarType and assign to appropriate variable pointer
    		if (value->vt == (VT_ARRAY | VT_BSTR))
    			bstrValue = (BSTR *) (*value).parray->pvData;
    		else
    			lngArr = (long *) (*value).parray->pvData;
    	}
    		// Assign values to VariableBinding List
    		for (i = 0; i < vb.len; i++)
    		{
    			// Validate varOID type and convert 
    			// appropriate reference to char
    			if (varOID->vt == (VT_ARRAY | VT_BSTR))
    				sprintf(lcOID, "%ls", bstrArr[i]);
    			else
    				sprintf(lcOID, "%ls", (*varOID).bstrVal);
    
    			// Convert the string OID to the AsnObjectIdentifier data type
    			ret = SnmpMgrStrToOid((LPSTR)lcOID, &objID);
    
    			// Assigning OID to variable bindings list
    			vb.list[i].name = objID;
    
    			// Test to see if we're dealing with an integer or a string
    			switch (value->vt)
    			{
    				case (VT_ARRAY | VT_I4):
    					// Long Array type     -- lngArr[]
    					vb.list[i].value.asnType = ASN_INTEGER32;
    					vb.list[i].value.asnValue.number = lngArr[i];
    						break;
    				case 2:
    				case 3:
    					// Long non-array type -- lVal
    					vb.list[i].value.asnType = ASN_INTEGER32;
    					vb.list[i].value.asnValue.number = (*value).lVal;
    						break;
    				case (VT_ARRAY | VT_BSTR):
    				case (VT_BSTR):
    					// String -- things are more complex here
    					// First let's define the asn type
    					vb.list[i].value.asnType = ASN_OCTETSTRING;
    
    						// wide character string format type
    						// Determine which variable to evaluate
    						if (value->vt == VT_BSTR)
    							sprintf(tcVar,"%ls",(*value).bstrVal);
    						else
    							sprintf(tcVar,"%ls",bstrValue[i]);
    
    			// Next we set the string length (remember to add 1 for \0)
    			vb.list[i].value.asnValue.string.length = strlen(tcVar) + 1;
    
    		// From here we basically allocate an empty string into the stream
    	vb.list[i].value.asnValue.string.stream = 
    (unsigned char*)malloc(vb.list[i].value.asnValue.string.length);
    
    				// then we copy the string from tcVar into stream
    				// (NOTE: All empty spaces are trim'd off)
    						strcpy((char *)vb.list[i].value.asnValue.string.stream, tcVar);
    
    			// Set dynamic to false because we're specifying a length
    					vb.list[i].value.asnValue.string.dynamic = FALSE;
    			}
    		}
    
    
    Above is a For...Next loop that converts the OID(s) from varOID, assigns it to vb.list, and goes into a switch statement to determine what type of value it is. Once the loop is complete, a request to the Agent is made and the return value is either Successful or Failure with error details. This section was shorter than the last because there was some duplication in code/methods, so to eliminate redundancy they were not shown.

    GetNext Method

    Note: varOID is defined in NetMgr.idl as [in,out]

    
    STDMETHODIMP CSNMPv1::GetNext(VARIANT *varOID, VARIANT *varRetVal)
    {
    	...
    	// Local variable declarations - P2
    	...
    	LPSTR lpOID;				// Return OID placeholder
    	...
    
    	// Initialize varRetVal
    	VariantInit(varRetVal);
    
    	// Set VarType to BSTR
    	varRetVal->vt = VT_BSTR;
    
    	// Assign ASN Value to return variable
    	varRetVal->bstrVal = extractAsnValue(vb.list[0]);
    
    	// Assign OID to out variable [varOID]
    	// == If you wanted the actual OID in "dotted numeric string format", 
    	//    you would use the SnmpOidToStr function.  Include winsnmp.h and 
    	// 	add wsnmp32.lib to Project->Settings->Link-	
    	//	>Object/library Modules
    	ret = SnmpMgrOidToStr(&vb.list[0].name, &lpOID);
    	lpVal = lpOID;
    	varOID->bstrVal = lpVal.AllocSysString();
    
    
    We've set the return value to the value of the OID that comes after varOID. Once the value has been extracted, we set varOID to the OID of the value being returned.

    NetMgrVB Component

    The NetMgrVB object uses the wsnmp32 library as its primary API. Rather than re-inventing NetMgr, it was decided that it would be more beneficial to illustrate, for example, what code is behind some of the SnmpMgr[x] functions. In addition, it demonstrates a different way of getting the same results.

    Of course with every new venture comes new challenges, and this component was no exception. As you'll see below, the third argument of the SnmpCreateSession function represents a callback function (SNMPAPI_CALLBACK). By using AddressOf [module.function] in this argument, that function becomes bound to the all SNMP Agent Responses. Because this object is not going to be sending or receiving traps, all of the communication with the agent is Get/GetNext/Set actions. One of the major issues that were encountered during development dealt with API calls within callback functions. The first API call that was executed within the callback function would automatically crash the application with an Access Violation error when it ran as a stand-alone.

    Further research into the issue uncovered that this happens when a thread that was not specifically created by Visual Basic executes the callback function. So the ultimate challenge was how to process the request from the agent without calling any API functions from within the callback function and keep CPU/memory utilization very low within the confines of COM. A work-around was found and is shown here, but will not be discussed in detail because this article is geared around SNMP component building. Another article is currently in the works that will explain in detail what the issue is and a resolution for COM objects.

    Function Process Outline

    Let's briefly discuss what major API functions are necessary in order to replicate the functionality that you get with the SnmpAPI and MgmtAPI libraries:

    • SnmpMgrOpen - Opens a session and "assigns" the agent (snmp host), community string, timeout and retry values to it
      • SnmpStartup
      • SnmpSetTranslateMode
      • SnmpSetRetransmitMode
      • SnmpCreateSession
    • SnmpMgrClose - Closes session and performs clean up
      • SnmpClose
      • SnmpCleanUp
    • SnmpMgrRequest - Initiates request and gathers response from Agent
      • SnmpCreatePdu
      • SnmpSendMsg
      • [SNMPAPI_CallBack]
      • SnmpRecvMsg
      • SnmpGetPduData
      • SnmpGetVb
      • CopyMemory (kernel32.dll)

    Even though in the code below there will be explanations on the functions listed above, it's nice to see how the functions correlate to one another.

    SnmpV1.cls / modSNMPAPI.mod

    The following procedures will alternate between the exposed method in the SnmpV1 class document and the module functions that reside in modSNMPAPI.mod.

    
    ' ==========================================================
    ' "Open" session to agent
    ' ==========================================================
    Public Function SnmpMgrOpen() As String
        ' Validate session isn't open and appropriate data has
        ' been assigned to agentSettings struct
       
        Select Case True
            ' Properties haven't been initialized
            Case (agentSettings.agtAddr = "" Or agentSettings.CommStr = "")
                SnmpMgrOpen = "[NetMgrVB.SnmpV1.SnmpMgrOpen][MV]=You must set both
     the Agent and CommStr properties before you can open a session."
            
            ' Pre-existing session encountered
            Case (lSession <> 0)
    		SnmpMgrOpen = "[NetMgrVB.SnmpV1.SnmpMgrOpen][OS]=
    You must close your existing session before opening another one."
            
            ' Good to go!
            Case Else
                SnmpMgrOpen = SnmpOpenSession(lcSession)
        End Select
    End Function
    
    
    Before SnmpOpenSession is called, checks are made to ensure that a session isn't currently open and that the necessary properties have been assigned values.
    
    ' ==========================================================
    ' Initalizes WinSNMP API and SNMP session
    ' ==========================================================
    Public Function SnmpOpenSession(ByRef lSession As Long) As String
    On Error GoTo errorHandler
        ' Local variable declarations
        ' ---------------------------
        Dim lngRetVal As Long
        Dim LPVOID As Long
    
        ' "Startup" API
        lngRetVal = SnmpStartup(LPVOID, LPVOID, LPVOID, LPVOID, LPVOID)
        
        ' Set translate mode to Translated
        lngRetVal = SnmpSetTranslateMode(0)		
        
        ' Set to On
        lngRetVal = SnmpSetRetransmitMode(1)		
        
        ' Create session
        lSession = SnmpCreateSession(0, 0, AddressOf SnmpAgentResponse, 0)
        
        ' Done!
        SnmpOpenSession = "Successful"
        Exit Function
        
    errorHandler:
       SnmpOpenSession = "[NetMgrVB.Mod.SnmpOpenSession][WAO" & Err.Number & "]=" &_
     Err.Description
    End Function
    
    
    SnmpStartup must be called before any other API calls can be used (much like WSAStartup in wsock32 library). According to MSDN, SnmpSetTranslateMode affects the interpretation and return of WinSNMP I/O string parameters, while SnmpSetRetransmitMode is responsible for setting timeout and retry attempts. Within SnmpCreateSession we're passing in the function pointer of SnmpAgentResponse.
    
    ' ==========================================================
    ' "Close" session to agent
    ' ==========================================================
    Public Function SnmpMgrClose() As String
        ' Verify if the session is already closed
        If lcSession = 0 Then
            ' Session is already closed -- no need to run through process
            SnmpMgrClose = "[NetMgrVB.SnmpV1.SnmpMgrClose][CS]=No session currently initialized to close."
        Else
            ' Session is active -- run SnmpCloseSession
            SnmpMgrClose = SnmpCloseSession(lcSession)
        End If
    End Function
    
    ' ==========================================================
    ' Closes session
    ' ==========================================================
    Public Function SnmpCloseSession(ByRef lSession As Long) As String
    On Error GoTo errorHandler
        ' Local variable declarations
        ' ---------------------------
        Dim lngRetVal As Long
        
        ' Close SNMP Session
        lngRetVal = SnmpClose(lSession)
        lSession = 0
        
        ' Initiate CleanUp mode
        lngRetVal = SnmpCleanup()
        
        ' Done!
        SnmpCloseSession = "Successful"
        Exit Function
        
    errorHandler:
        SnmpCloseSession = "[NetMgrVB.Mod.SnmpCloseSession][WA0" & Err.Number & "]=" & Err.Description
    End Function
    
    ' ==========================================================
    ' Request OID value from Agent and return response
    ' ==========================================================
    Public Function SnmpGet(ByVal strOID As String) As String
        ' Local variable declarations
        ' ---------------------------
        Dim strRetVal As String
    
        ' Make request to agent
        strRetVal = initAgentProc(lcSession, SNMP_PDU_GET, strOID, "")
        
        ' Return agent response
        SnmpGet = strRetVal
    End Function
    
    ' ==========================================================
    ' Set OID value to Agent and return response
    ' ==========================================================
    Public Function SnmpGetNext(strOID As String) As String
        ' Local variable declarations
        ' ---------------------------
        Dim strRetVal As String
    
        ' Make request to agent
        strRetVal = initAgentProc(lcSession, SNMP_PDU_GETNEXT, strOID, "")
        
        ' =Re-Assign Next OID
        ' == If preservation of the incoming oid is an issue then you can do something like:
        '    SnmpGetNext(byVal strInOID as String, strOutOID as String) as String
        strOID = strReturnOID
        
        ' Return agent response
        SnmpGetNext = strRetVal
    
    
    Actual request/response actions occur within the module-defined functions. SnmpGet, SnmpGetNext and SnmpSet simply returns what it gets from initAgentProc. SnmpGetNext does one additional step, which is assigning the next OID back to strOID.
    
    ' ==========================================================
    ' Request that OID value change and return response
    ' ==========================================================
    Public Function SnmpSet(ByVal strOID As String, ByVal varOIDValue As Variant, 
    Optional ByVal intSetTypeOverride As Integer = 99) As String
    On Error GoTo errorHandler
    
        ' Local variable declarations
        ' ---------------------------
        Dim strRetVal As String
        
        ' Make request to agent
        Select Case intSetTypeOverride
            Case 0      ' Setting oid set value to string
       strRetVal = initAgentProc(lcSession, SNMP_PDU_SET, strOID, CStr(varOIDValue))
            
            Case 1      ' Setting oid set value to numeric
       strRetVal = initAgentProc(lcSession, SNMP_PDU_SET, strOID, CLng(varOIDValue))
            
            Case 99     ' Auto oid set value detection put in place
       strRetVal = initAgentProc(lcSession, SNMP_PDU_SET, strOID, varOIDValue)
            
            Case Else   ' Invalid argument passed
    strRetVal = "[NetMgrVB.SnmpV1.SnmpSet][IST]=An invalid Set Type Override value
     has been encountered.  Valid Values are 0 for String Override and 1 for Numeric
     Override."
        End Select
        
        ' Return agent response
        SnmpSet = strRetVal
        
        ' Done!
        Exit Function
        
    errorHandler:
        SnmpSet = "[NetMgrVB.SnmpV1.SnmpSet][" & Err.Number & "]=" & Err.Description
    End Function
    
    
    Before initAgentProc is executed, data type checks are ran to ensure that the right type is getting passed through. If intSetTypeOverride is allowed to default to 99, it will allow the functions that utilize those variables to automatically determine what type variable type is. Listed below are functions that come directly from modSNMPAPI.mod.
    
    ' ==========================================================
    ' Centralized point for request/response between module and class
    ' ==========================================================
    Public Function initAgentProc(ByVal lSession As Long, ByVal lngPDUType As Long, 
    ByVal strOID As String, ByVal varOIDValue As Variant) As String
        ' Local variable declarations
        ' --------------------------------
        Dim strRetVal As String
        Dim lngRetVal As String
        
        ' Make request to agent
        strRetVal = SnmpAgentRequest(lSession, lngPDUType, strOID, varOIDValue)
       
        ' Check to see if an error was returned
        If InStr(1, strRetVal, "[NetMgrVB.Mod.", 1) > 0 Then
            ' Return error message
            initAgentProc = strRetVal
        Else
            ' Wait for SnmpAgentResponse to be executed
            Do
                DoEvents
                Sleep APP_RESPONSE_TIME
            Loop Until (boolResponse)
            
            ' Return response for request value
            initAgentProc = SnmpResponseProcess(lSession)
        End If
    End Function
    
    
    SnmpAgentRequest is responsible for creating and sending the request to the agent for processing, while SnmpResponseProcess parses the response from the agent.
    
    ' ==========================================================
    ' Generates and sends the SNMP packet to the agent
    ' ==========================================================
    Public Function SnmpAgentRequest(ByVal lSession As Long, ByVal lngPDUType As
     Long, ByVal strOID As String, ByVal varOIDValue As Variant) As String
    On Error GoTo errorHandler
        ' Local variable declarations
        ' ------------------------------------
        Dim vbListOctValue As vbOctetValue
        Dim vbListIntValue As vbIntValue
        Dim vbOID          As AsnVar
        Dim ctxOctetVal    As ContextOctetValue
        Dim lngRetVal      As Long
        Dim lngSrcEntity   As Long
        Dim lngDstEntity   As Long
        Dim lngVBL         As Long
        Dim lngPDU         As Long
        Dim lngContext     As Long
        
        ' Recycle to false
        boolResponse = False
        
        ' Set Translate mode to Translated
        lngRetVal = SnmpSetTranslateMode(0)
        
        ' Convert source (mgr) string to entity (long)
        lngSrcEntity = SnmpStrToEntity(lSession, agentSettings.mgrAddr)
            ' Verify that conversion happened appropriately
            If lngSrcEntity = SNMPAPI_FAILURE Then
                ' Translation did not occur -- set translation to
                ' Untranslated for v1
                lngRetVal = SnmpSetTranslateMode(1)
                
                ' Retry assignment
                lngSrcEntity = SnmpStrToEntity(lSession, agentSettings.mgrAddr)
                    ' Verify that assignment occured
                    If lngSrcEntity = SNMPAPI_FAILURE Then
                        ' Translation did not occur -- return error
                        SnmpAgentRequest = "[NetMgrVB.Mod.SnmpAgentRequest][US]=Unknown Source"
                        Exit Function
                    End If
            End If
        
        ' Reset translation mode to translated
        lngRetVal = SnmpSetTranslateMode(0)
        
        ' Convert target (agent) string to entity (long
        lngDstEntity = SnmpStrToEntity(lSession, agentSettings.agtAddr)
            ' Verify that conversation happened appropriately
            If lngDstEntity = SNMPAPI_FAILURE Then
                ' Translation did not occur -- set translation to
                ' Untranslated for v1
                lngRetVal = SnmpSetTranslateMode(1)
                
                ' Retry assignment
                lngDstEntity = SnmpStrToEntity(lSession, agentSettings.agtAddr)
                    ' Verify that assignment occured
                    If lngDstEntity = SNMPAPI_FAILURE Then
                        ' Free up any resources up until this point
                        lngRetVal = SnmpFreeEntity(lngSrcEntity)
                        
                        ' Translation did not occur -- return error
                        SnmpAgentRequest =
     "[NetMgrVB.Mod.SnmpAgentRequest][UT]=Unknown Target"
                        Exit Function
                    End If
            End If
        
        ' Assign community string to context type
        ctxOctetVal.len = Len(agentSettings.CommStr)
        ctxOctetVal.ptr = agentSettings.CommStr
        
        ' Reset translation mode to translated
        lngRetVal = SnmpSetTranslateMode(0)
        
        ' Convert context from string to long
        lngContext = SnmpStrToContext(lSession, ctxOctetVal)
            ' Verify that assignment occured
            If lngContext = SNMPAPI_FAILURE Then
                ' Translation did not occur -- set translation to
                ' Untranslated for v1
                lngRetVal = SnmpSetTranslateMode(1)
                
                ' Reassign context
                lngContext = SnmpStrToContext(lSession, ctxOctetVal)
                    ' Verify that assignment occured
                    If lngContext = SNMPAPI_FAILURE Then
                        ' Free any resources up until this point
                        lngRetVal = SnmpFreeEntity(lngSrcEntity)
                        lngRetVal = SnmpFreeEntity(lngDstEntity)
    
                        'Translation did not occur -- return error
                        SnmpAgentRequest =
    "[NetMgrVB.Mod.SnmpAgentRequest][CSTR]=Invalid community string"
                        Exit Function
                    End If
            End If
        
        ' Convert OID string to vbOID
        lngRetVal = SnmpStrToOid(strOID, vbOID)
            ' Check to see if any errors may have been encountered
            If lngRetVal = SNMPAPI_FAILURE Then
                ' Free any resources up until this point
                lngRetVal = SnmpFreeContext(lngContext)
    
                ' Return Error
                SnmpAgentRequest = "[NetMgrVB.Mod.SnmpAgentRequest][AP0" & _
    SnmpGetLastError(lSession) & "]=Error during String to OID conversion"
                
                ' No more processing is necessary
                Exit Function
            End If
    
        ' Create variable binding list
        ' Initialize null value
        'OutputDebugString CStr(IsNumeric(varOIDValue)) & ":" & CStr(lngPDUType) &
     ":" & CStr(SNMP_PDU_SET)
        If lngPDUType = SNMP_PDU_SET Then
            If IsNumeric(varOIDValue) Then
                vbListIntValue.vbType = ASN_INTEGER32
                CopyMemory vbListIntValue.lngVal, CLng(varOIDValue), 4
                
                lngVBL = SnmpCreateVbl(lSession, vbOID, vbListIntValue)
            Else
                vbListOctValue.vbType = ASN_OCTETSTRING
                vbListOctValue.octetRef.len = Len(varOIDValue)
                CopyMemory vbListOctValue.octetRef.ptr, CStr(varOIDValue), 
    Len(varOIDValue)
            
                lngVBL = SnmpCreateVbl(lSession, vbOID, vbListOctValue)
            End If
        Else
            vbListIntValue.vbType = ASN_NULL
            lngVBL = SnmpCreateVbl(lSession, vbOID, vbListIntValue)
        End If
        
            ' Check to see if any errors may have been encountered
            If lngVBL = SNMPAPI_FAILURE Then
                ' Return Error
                SnmpAgentRequest = "[NetMgrVB.Mod.SnmpAgentRequest][AP0" & 
    SnmpGetLastError(lSession) & "]=Error during VariableBind object creation"
                
                ' Free any resources up until this point
                lngRetVal = SnmpFreeDescVar(ASN_OBJECTIDENTIFIER, vbOID)
                lngRetVal = SnmpFreeEntity(lngSrcEntity)
                lngRetVal = SnmpFreeEntity(lngDstEntity)
                lngRetVal = SnmpFreeContext(lngContext)
                
                ' No more processing is necessary
                Exit Function
            End If
        
        ' Randomize Timer for Request ID Generation
        Randomize Timer
        
        ' Generate PDU
        lngPDU = SnmpCreatePdu(lSession, lngPDUType, 0, 0, 0, lngVBL)
            ' Check to see if any errors may have been encountered
            If lngPDU = SNMPAPI_FAILURE Then
                ' Return Error
                SnmpAgentRequest = "[NetMgrVB.Mod.SnmpAgentRequest][AP0" & 
    SnmpGetLastError(lSession) & "]=Error during PDU creation"
                
                ' Free any resources up until this point
                lngRetVal = SnmpFreeDescVar(ASN_OBJECTIDENTIFIER, vbOID)
                lngRetVal = SnmpFreeEntity(lngSrcEntity)
                lngRetVal = SnmpFreeEntity(lngDstEntity)
                lngRetVal = SnmpFreeVbl(lngVBL)
                lngRetVal = SnmpFreeContext(lngContext)
                
                ' No more processing is necessary
                Exit Function
            End If
    
        ' Send request to Agent
        lngRetVal = SnmpSendMsg(lSession, lngSrcEntity, lngDstEntity, lngContext, 
    lngPDU)
            ' Verify that no errors occured
            If lngRetVal = SNMPAPI_FAILURE Then
                ' Report error
                SnmpAgentRequest = "[NetMgrVB.Mod.SnmpAgentRequest][WAPI]=" & 
    SnmpGetLastError(lSession)
            End If
        
        ' Free local variables
        lngRetVal = SnmpFreeDescVar(ASN_OBJECTIDENTIFIER, vbOID)
        lngRetVal = SnmpFreeEntity(lngSrcEntity)
        lngRetVal = SnmpFreeEntity(lngDstEntity)
        lngRetVal = SnmpFreeVbl(lngVBL)
        lngRetVal = SnmpFreePdu(lngPDU)
        lngRetVal = SnmpFreeContext(lngContext)
        
        ' Done!
        Exit Function
        
    errorHandler:
        SnmpAgentRequest = "[NetMgrVB.Mod.SnmpAgentRequest][WA0" & Err.Number & "]=" & Err.Description
    End Function
    
    
    Once the SnmpSendMsg command is initiated and returns SNMPAPI_SUCCESS (or 1), the function that was passed as a pointer in the SnmpCreateSession function will be executed once a response has been received from the agent. This means that execution of code after SnmpSendMsg will continue and NOT wait for that response. Within the initAgentProc function, SnmpAgentRequest is executed and then, if successful, waits for variables to be set by the SNMPAPI_CallBack function (in our case SnmpAgentResponse). Once completed, it will then execute the SnmpResponseProcess listed below.
    
    ' ==========================================================
    ' Processes response from agent
    ' ==========================================================
    Public Function SnmpResponseProcess(ByVal lSession As Long) As String
    On Error GoTo errorHandler
    
        ' Local variable declarations
        ' --------------------------------
        Dim vbValue         As vbOctetValue
        Dim vbValueTmp      As vbOctetValue
        Dim vbOID           As AsnVar
        Dim strOctetValueT  As String
        Dim strRequestValue As String
        Dim lngRetVal       As Long
        Dim lngSrcEntity    As Long
        Dim lngDstEntity    As Long
        Dim lngContext      As Long
        Dim lngPDU          As Long
        Dim lngVBL          As Long
        Dim lngPDUType      As Long
        Dim lngRequestID    As Long
        Dim lngErrorStatus  As Long
        Dim lngErrorIndex   As Long
        
        ' Verify that no error is being passed
        If lngWParam = SNMP_ERRORSTATUS_NOERROR Then
            ' Assign RequestID
            lngRequestID = lngLParam
            
                ' Get the message from the agent
    	lngRetVal = SnmpRecvMsg(lSession, lngSrcEntity, lngDstEntity,
    lngContext, lngPDU)
                
                    ' Check to see if any errors may have been encountered
                    If lngRetVal = SNMPAPI_FAILURE Then
                        ' Return Error
                        SnmpResponseProcess = 
    "[NetMgrVB.Mod.SnmpResponseProcess][AP0" & SnmpGetLastError(lSession) & "]=Error 
    during message retrieval"
                        
                        ' No more processing is necessary
                        Exit Function
                    End If
                    
                ' Process data within the PDU
                lngRetVal = SnmpGetPduData(lngPDU, lngPDUType, lngRequestID, 
    lngErrorStatus, lngErrorIndex, lngVBL)
                    ' =Check to see if any errors may have been encountered
                    ' ==Check return value from SnmpRecvMsg
                    If lngRetVal = SNMPAPI_FAILURE Then
                        ' Return Error
                        SnmpResponseProcess = 
    "[NetMgrVB.Mod.SnmpResponseProcess][AP0" & SnmpGetLastError(lSession) & "]=Error 
    during PDU parsing"
                        
                        ' No more processing is necessary
                        Exit Function
                    End If
                    
                    ' ==Check ErrorStatus to get Agent Specific error
                    If lngErrorStatus > 0 Then
                        SnmpResponseProcess = 
    "[NetMgrVB.Mod.SnmpResponseProcess][AP0" & CStr(lngErrorStatus) & "C" & 
    CStr(lngErrorIndex) & "]=Error encountered during Agent Process"
                        
                        ' No more processing is necessary
                        Exit Function
                    End If
                
            ' Get the variable binding list
            lngRetVal = SnmpGetVb(lngVBL, 1, vbOID, vbValue)
                ' Check to see if any errors may have been encountered
                If lngRetVal = SNMPAPI_FAILURE Then
                    ' Return Error
                    SnmpResponseProcess = "[NetMgrVB.Mod.SnmpResponseProcess][AP0" & 
    SnmpGetLastError(lSession) & "]=Error during VariableBind parsing"
                    
                    ' No more processing is necessary
                    Exit Function
                End If
                
                ' Determine how to process response from agent
                Select Case vbValue.vbType
                    Case ASN_OCTETSTRING            ' String value
                        ' Initiliaze variable
                        strOctetValueT = String$(vbValue.octetRef.len, 0)
                        CopyMemory ByVal strOctetValueT, ByVal vbValue.octetRef.ptr, 
    vbValue.octetRef.len
                        
                            ' What kind of data are we dealing with?
                            If Asc(Mid(strOctetValueT, 1, 1)) > 0 Then
                                ' Strict string -- return value
                               strRequestValue = strOctetValueT
                            Else
                                ' loop through octet stream and apply appropriate
                                ' conversions
                                For X = 1 To vbValue.octetRef.len
                                   If Hex(Asc(Mid(strOctetValueT, X, 1))) = "0" Then
                                        strRequestValue = strRequestValue & "0"
                                    End If
                                    
                                    strRequestValue = strRequestValue & 
    Hex(Asc(Mid(strOctetValueT, X, 1)))
                                Next
                            End If
                        
                    Case ASN_OBJECTIDENTIFIER       ' OID type
                        ' Initialize variable
                        strOctetValueT = String(1408, 0)
                        CopyMemory vbValueTmp, vbValue, 12
                        
                        ' Convert OID to String
                        lngRetVal = SnmpOidToStr(vbValueTmp.octetRef, 1408, 
    strOctetValueT)
                        
                        ' Accommodate \0 character
                        If lngRetVal > 0 Then lngRetVal = lngRetVal - 1
                        
                        ' Return agent response value
                        strRequestValue = Left(strOctetValueT, lngRetVal)
                        
                    Case Else
                        ' All others
                        strRequestValue = CStr(vbValue.octetRef.len)
                End Select
            
            ' This is used primarily for GetNext operations
            strReturnOID = String$(1408, 0)
            lngRetVal = SnmpOidToStr(vbOID, 1408, strReturnOID)
                If lngRetVal = SNMPAPI_FAILURE Then
                    strReturnOID = "[NetMgrVB.Mod.SnmpResponseProcess][AP2" & 
    lngLParam & "]=Unable to retrieve OID from VariableBinding List"
                End If
            strReturnOID = Trim(Replace(strReturnOID, Chr(0), ""))
            
            ' Free local variables
            lngRetVal = SnmpFreeDescVar(ASN_OBJECTIDENTIFIER, vbOID)
            lngRetVal = SnmpFreeDescVar(vbValue.vbType, vbValue.octetRef)
            lngRetVal = SnmpFreeVbl(lngVBL)
            lngRetVal = SnmpFreePdu(lngPDU)
            lngRetVal = SnmpFreeEntity(lngSrcEntity)
            lngRetVal = SnmpFreeEntity(lngDstEntity)
            lngRetVal = SnmpFreeContext(lngContext)
            
            ' Return Value
            SnmpResponseProcess = strRequestValue
        Else
            ' Return Error Message
            SnmpResponseProcess = "[NetMgrVB.Mod.SnmpAgentResponse][AP1" & lngWParam 
    & "]=Request ID " & lngLParam & " has encountered an error"
            strReturnOID = ""
        End If
        
        ' Done!
        Exit Function
        
    errorHandler:
        strRequestValue = "[NetMgrVB.Mod.SnmpResponseProcess][WA0" & Err.Number & 
    "]=" & Err.Description
        strReturnOID = ""
    End Function
    
    
    The reason why this and SnmpAgentResponse are separated is because of limitations within VB 6.0, which is explained briefly in the comments below.
    
    ' ==========================================================
    ' Adheres to the SNMPAPI_Callback function layout but does
    ' NOT HANDLE API CALLS because of limitations of Visual
    ' Basic 6.0 -- If an API function is placed in this function
    ' an Access Violation error will occur within any
    ' stand-alone application that tries to use this component
    ' ==========================================================
    Private Function SnmpAgentResponse(ByVal lSession As Long, ByVal hWnd As Long, ByVal wMsg As Long, 
    ByVal wParam As Long, ByVal lParam As Long, ByVal lpClientData As Long) As Long
        ' Assign values to global variables
        lngWParam = wParam	' If wParam = 0 then a SNMP message is available
        lngLParam = lParam  ' Specifies the request id for the pdu being processed
        boolResponse = True ' Set global to recognize response has been received
        
        ' Return Success -- must be returned
        SnmpAgentResponse = SNMPAPI_SUCCESS
    End Function
    
    
    By doing this, it not only allows the application not to crash (a big plus), but also helps separate out functionality. So all request related functionality resides in SnmpAgentRequest, while all response parsing is primarily done in SnmpResponseProcess (triggered by SnmpAgentResponse). This model allows the ability to introduce trap functionality.

    Sample Application

    In addition to the source code for both the NetMgr (VC++) and NetMgrVB components, attached is also source code for a sample application that shows how to use both components.

    There are three different areas to choose from - each one is aimed at a specific example. Samples 1 and 3 demonstrate how to perform an SNMP Walk using their respective objects.

    Sample 1 shows how to use the NetMgr component and Sample 3 shows how to use the NetMgrVB component.

    Sample 2 shows how to do Multiple Get/Sets with the NetMgr component.

    Conclusion

    This article has shown how to create an SNMP component in both Visual C++ 6.0 and Visual Basic 6.0. Even though each component utilizes different APIs, they accomplish the same goals in the end. Developers should be able to use the source code provided and build upon it to suit their needs. Please review the source code for the components as not everything was placed within the article (i.e. API function declarations, type definitions, etc.).

    References

    MSDN, WinSNMP Functions

    Q198607 - PRB: Access Violation in VB Run-Time Using AddressOf, Microsoft Knowledge Base

    About the Author

    Ben Garcia is the President and Co-founder of Adaptive Innovations, LLC., a solution provider specializing in integration and artificial intelligence. He has more than 13 years of software development experience in many different facets of the industry. Please feel free to contact Ben at bgarcia@personify.ws.

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Other Articles
    Apr 27, 2004 - Develop and Customize Web Parts with Custom Tool Parts
    Tool Parts provide an interface for Web Part properties well beyond the capabilities of the default property pane. In this article Gayan Peiris shows how to customize Web Parts with custom Tool Parts.
    [Read This Article]  [Top]
    Apr 7, 2004 - Reusable Components in ASP.NET 2.0, Object Binding and Precompilation
    This article demonstrates how to create a reusable component in ASP.NET 2.0 and then consume it from an ASP.NET page. Also learn how the ObjectDataSource control can be used to directly bind the output of an object to the controls in an ASP.NET page and how precompilation can be used to increase the performance of the Web application and catch compilation errors.
    [Read This Article]  [Top]
    Mar 31, 2004 - Build a Managed BHO and Plug into the Browser
    Browser Helper Objects (BHOs) are COM components that communicate with Internet Explorer to enrich the browsing experience. Michele Leroux Bustamante returns to the world of COM to show you how to build a managed BHO with the help of the .NET Framework's COM interoperability features.
    [Read This Article]  [Top]
    Feb 18, 2004 - Customizing SharePoint Web Parts with Custom Properties
    In addition to creating custom Web Parts for SharePoint Portal Server, developers can actually create their own custom properties to further enhance Web Part appearance and behavior. Gayan Peiris explains the process and provides all the necessary code.
    [Read This Article]  [Top]
    Sep 26, 2003 - Accessing Shared Resources Using ASP.NET
    Accessing shared resources is a challenge for many ASP.NET developers. Tony Arslan explains how a simple serviced component can solve this infamous problem.
    [Read This Article]  [Top]
    Oct 2, 2002 - Function Pointers and COM
    Using callbacks and function pointers in VB can be risky and complicated. Ben Garcia explains his work-around for the function pointer issue he encountered while creating the VB version of his SNMP component.
    [Read This Article]  [Top]
    Jul 23, 2002 - Creating an SNMP Component
    Ben Garcia sheds some light on the Simple Network Management Protocol (SNMP). First he provides a history of SNMP, then he dives right into its architecture. Finally, he shows how to build a COM component that communicates with SNMP-enabled devices.
    [Read This Article]  [Top]
    Jun 26, 2002 - Accessing Caller ID from the Web - Part 1
    Paul Apostolos begins his series on using Web services and the MSComm32.OCX component to access caller id information from a Web page. In part 1, learn how to write the Visual Basic program that runs on the server and updates a database with incoming callers.
    [Read This Article]  [Top]
    Nov 20, 2001 - Creating a Server Component with VB - Redesigned - Part 2
    Doug Dean explains different methods of retrieving and manipulating data from a database in a VB DLL so that it is ready to be rendered in a browser.
    [Read This Article]  [Top]
    Aug 28, 2001 - Create Your Own Visual Basic Add-Ins
    In this article, S.S. Ahmed shows you how to create VB add-ins. Programmers always feel that they are short of several features while working with development tools. Since Microsoft made Visual Basic an extensible product, VB developers can create their own features in VB.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers

    Solutions
    Whitepapers and eBooks
    Microsoft Article: Will Hyper-V Make VMware This Decade's Netscape?
    Microsoft Article: 7.0, Microsoft's Lucky Version?
    Microsoft Article: Hyper-V--The Killer Feature in Windows Server 2008
    Avaya Article: How to Feed Data into the Avaya Event Processor
    Microsoft Article: Install What You Need with Windows Server 2008
    HP eBook: Putting the Green into IT
    Whitepaper: HP Integrated Citrix XenServer for HP ProLiant Servers
    Intel Go Parallel Portal: Interview with C++ Guru Herb Sutter, Part 1
    Intel Go Parallel Portal: Interview with C++ Guru Herb Sutter, Part 2--The Future of Concurrency
    Avaya Article: Setting Up a SIP A/S Development Environment
    IBM Article: How Cool Is Your Data Center?
    Microsoft Article: Managing Virtual Machines with Microsoft System Center
    HP eBook: Storage Networking , Part 1
    Microsoft Article: Solving Data Center Complexity with Microsoft System Center Configuration Manager 2007
    MORE WHITEPAPERS, EBOOKS, AND ARTICLES
    Webcasts
    Intel Video: Are Multi-core Processors Here to Stay?
    On-Demand Webcast: Five Virtualization Trends to Watch
    HP Video: Page Cost Calculator
    Intel Video: APIs for Parallel Programming
    HP Webcast: Storage Is Changing Fast - Be Ready or Be Left Behind
    Microsoft Silverlight Video: Creating Fading Controls with Expression Design and Expression Blend 2
    MORE WEBCASTS, PODCASTS, AND VIDEOS
    Downloads and eKits
    Sun Download: Solaris 8 Migration Assistant
    Sybase Download: SQL Anywhere Developer Edition
    Red Gate Download: SQL Backup Pro and free DBA Best Practices eBook
    Red Gate Download: SQL Compare Pro 6
    Iron Speed Designer Application Generator
    MORE DOWNLOADS, EKITS, AND FREE TRIALS
    Tutorials and Demos
    How-to-Article: Preparing for Hyper-Threading Technology and Dual Core Technology
    eTouch PDF: Conquering the Tyranny of E-Mail and Word Processors
    IBM Article: Collaborating in the High-Performance Workplace
    HP Demo: StorageWorks EVA4400
    Intel Featured Algorhythm: Intel Threading Building Blocks--The Pipeline Class
    Microsoft How-to Article: Get Going with Silverlight and Windows Live
    MORE TUTORIALS, DEMOS AND STEP-BY-STEP GUIDES