网络管理实验1——MIB Walk的运行和代码分析

2 篇文章 0 订阅
2 篇文章 0 订阅

一、实验预备

1、软件的类别、应用范畴及基本功能

        MIB Browser是由MG-SOFT公司开发的一个使用灵活、有着强大性能和友好用户界面操纵SNMP网络数据的工具集,MIBBrowser支持SNMP v1、v2和v3协议的各项操作,通过它可以有效、安全地对MIB数据进行读取、修改和监控,从而实现对网络的有效管理。相对闭源的MIB Browser而言,开源的MIB Walk实现的功能和类型也类似。

2、软件的运行环境、运行支持条件

        在本次实验中,我采用了Windows XP SP3环境下,Microsoft Visual C++ 6.0开发环境,进行开发和使用。


图   1 安装SNMP协议、服务

        在使用运行MIB Walk前,我们需要安装SNMP服务。打开“控制面板”,点击“添加/删除程序”,“添加/删除Windows组件”,在“Windows组件向导”中,点击“管理和监视工具”,选择“WMISNMP提供程序”和“简单网络管理协议”,如图1所示。点击确定后,插入WindowsXP安装盘,完成安装。

        安装完成后,还需要在Windows中开启SNMP服务。点击“开始”,“运行”,输入“services. MSc”打开服务管理,选择“SNMP Service”,并将其开启,如图2所示。


图 2 开启SNMP服务

3、软件的功能预期

        MIBWalk访问已经开启了SNMP服务的主机的MIB_Ⅱ包后,能够实现以下功能:

(1) 查:能够访问当前节点的状态信息(Get)、下一个节点的状态信息(GetNext)遍历当前节点的子树(Walk)的信息。

(2) 改:能够修改当前节点的状态信息(Set)。

(3) 导出:选中指定的OID后,能够以文本的形式保存当前OID状态信息。

4、软件源代码的开发编译环境以及环境学习

        在本次综合性实验中,我采用了Microsoft Visual C++ 6.0的开发编译环境。由于本次实验的代码是采用MFC架构下的C++语言编写的,因此我采用了《C++ Primer Plus 中文版》作为参考书籍。

二、软件的编译(安装)及运行过程

        开启服务后,就可以开始调试程序了。打开Microsoft Visual C++ 6.0,点击“文件”,“打开工作区间”,打开MIB Walk源代码下的“Monitor.dsw”文件,见图3。打开MIBWalk项目后,先编译“StdAfx.cpp”文件,编译成功后,编译“Monitor.cpp”文件,最后编译“Input.cpp”文件,如图4所示。当这三个文件都正常编译后,点击运行(Ctrl+F5),即可看见MIBWalk的运行界面,如图5所示。


图 3 打开工作区间



图 4 编译、运行MIB Walk过程示意

      由图5可以看得出,界面的左边的以树节点的形式,列出了MIB_Ⅱ包中所有可能的被管理对象的集合的数据结构。界面的右边Agent部分,填写的是需要管理的远程计算机的IP或本地计算机的IP地址。Community是指SNMP中服务的安全设置中接受团体名称,默认为public,因此在MIBWalk中填写的也应该是public否则无法后面的实验中无法获取OID信息。OID是对象标识符(Object identifier-OID)的缩写,在对象术语中,是为找到区分整体中的对象而添加的标记。在MIB Walk中,默认已直接打开了MIB_Ⅱ,所以在整个实验过程中都默认隐藏了部分OID(.1.3.6.1.2.1)。紧接就是MIB Walk常用来执行的4项操作,分别是Get、GetNext、Set、Walk。在左面是“监听窗口”的选框,用于确定是否启动监听。而监听框左边则是“执行”按钮。在操作框下面,是显示OID和OID状态信息。最下面的是被选中的OID列表,在选择上框OID的信息,可以添加OID到被选中列表;相反地,选中被选中列表中的OID,可以在被选中列表中选定的OID。点击“StartStatistics”可以保存被选择OID列表的信息。采样间隔是指监听时采样的频率。


图 5 MIB Walk运行界面

1、Get功能

        打开MIB Walk界面中,在应用的MIB_Ⅱ对象列表中,选中需要查看的对象。以MIB_Ⅱ包中system的sysName为例。双击“sysName”,选中后,在右边的操作框中,选中Get的RadioBox,在点击“执行”。如图6所示,为获取(Get)sysName的操作结果。


图 6 Get sysName的操作示意图

2、GetNext功能

        该功能用于查看当前对象的下一个对象信息。如果当前对象是父节点的最后一个节点,执行GetNext后,会显示父节点下一个兄弟节点的第一个子节点。以MIB_Ⅱ包中的system节点的子节点sysName为例,执行后会显示sysLocation的信息。如果选中system中的sysServices对象,执行后会显示interface中第一个子节点ifNumber的信息。执行结果如图7所示。


(1)选中sysName执行GetNext


(2)选中sysServices执行GetNext

图7

3、Set功能

       使用Set功能,需要与Get功能相结合。以修改MIB_Ⅱ包中ip下的ipDefaultTTL为例。首先,选中ipDefaultTTL或,选中“Get”操作,点击“执行”。在OID信息框中,可以看到ipDefaultTTL的类型为ASN_INTEGER32。选中“Set”操作,点击“执行”,输入64,点击“OK”。当我们再次执行“Get”操作时,可以发现ipDefaultTTL的值已经改为64。操作流程如图8所示。


(1)对ipDefaultTTL执行Get操作得到类型


(2)修改ipDefaultTTL的值


(3)再一次对ipDefaultTTL执行Get操作

图8

4、Walk功能

        遍历选中节点的所有子节点或当前节点的信息,并以“End of MIB sub tree”结尾。以MIB_Ⅱ中的ip节点为例,执行Walk后,会显示所有的子节点对象信息。以ip节点下的ipDefaultTTL为例,由于其已经没有子节点了,所以会显示当前节点的信息。执行结果如图9、10所示。

图9 选中ipDefaultTTL节点后执行Walk


图10 选中ip节点后执行Walk

5、导出/保存对象信息功能

        该功能可以保存在被选中列表中的对象的信息。可以同时保存一个或多个信息对象的信息。使用该功能需要与Get功能结合。以ipDefaultTTL和sysName为例,分别使用Get功能获得其OID和Type(Value)后,选定OID,点击添加OID,在被选中列表中便会显示添加的OID。选中在被选中列表的OID,点击“删除OID”,可以取消选中该OID。选择了需要导出的OID后,点击“Start Statistics”会弹出保存对话框,输入文件名后点击“保存”便可导出被选中列表中的OID信息。执行示意如图11所示。

        保存文件后,使用写字板(Word Pad)打开文件,即可查看保存的OID信息,如图12所示。


图11 保存ipDefaultTTL和sysName的信息


图12 查看保存在“MIB2_Info”的信息

6、Trap功能

       SNMP Trap(SNMP 陷阱):某种入口,到达该入口会使SNMP被管设备主动通知SNMP管理器,而不是等待SNMP管理器的再次轮询。这个功能是针对SNMP基于轮询机制的缺陷而定制的,这个既能保证SNMP是主动管理,又能及时地反映被管对象可能发生的灾难性信息。


图13 开启SNMPTrap服务

       使用Trap功能,首先要在系统的服务中开启“SNMPTrap”服务,如图13所示。由于SNMPTrap需要使用UDP的161和162端口,因此我们需要另外配置网络安全。以下是参考微软技术支持中心的文档资料:

创建筛选器列表

要创建保护 SNMP 消息的 IPSec 策略,请首先创建筛选器列表。为此,请按照下列步骤操作:

1.       单击开始,指向管理工具,然后单击本地安全策略

2.       展开安全设置,右键单击“IP 安全策略,在本地计算机上,然后单击管理 IP 筛选器表和筛选器操作

3.       单击管理 IP 筛选器列表选项卡,然后单击添加

4.        IP筛选器列表对话框的名称框中键入 SNMP 消息 (161/162),然后在描述框中键入 TCP  UDP 端口 161 筛选器

5.       单击以清除使用添加向导复选框,然后单击添加

6.       在出现的 IP筛选器属性对话框的地址选项卡上,在源地址框中单击任何 IP 地址。在目标地址框中单击我的 IP 地址。单击以选中镜像。同时与源地址和目标地址正好相反的数据包相匹配复选框。

7.       单击协议选项卡。在选择协议类型框中,单击 UDP。在设置 IP 协议端口框中,单击从此端口,然后在框中键入 161。单击到此端口,然后在框中键入 161

8.       单击确定

9.        IP筛选器列表对话框中,单击添加

10.    在出现的 IP筛选器属性对话框的地址选项卡上,在源地址框中单击任何 IP 地址。在目标地址框中单击我的 IP 地址。单击以选中镜像。同时与源地址和目标地址正好相反的数据包相匹配复选框。

11.    单击协议选项卡。在选择协议类型框中,单击 TCP。在设置 IP 协议框中,单击从此端口,然后在框中键入 161。单击到此端口,然后在框中键入 161

12.    单击确定

13.     IP筛选器列表对话框中,单击添加

14.    在出现的 IP筛选器属性对话框的地址选项卡上,在源地址框中单击任何 IP 地址。在目标地址框中单击我的 IP 地址。单击以选中镜像。同时与源地址和目标地址正好相反的数据包相匹配复选框。

15.    单击协议选项卡。在选择协议类型框中,单击 UDP。在设置 IP 协议框中,单击从此端口,然后在框中键入 162。单击到此端口,然后在框中键入 162

16.    单击确定

17.     IP筛选器列表对话框中,单击添加

18.    在出现的 IP筛选器属性对话框的地址选项卡上,在源地址框中单击任何 IP 地址。在目标地址框中单击我的 IP 地址。单击以选中镜像。同时与源地址和目标地址正好相反的数据包相匹配复选框。

19.    单击协议选项卡。在选择协议类型框中,单击 TCP。在设置 IP 协议框中,单击从此端口,然后在框中键入 162。单击到此端口,然后在框中键入 162

20.    单击确定

21.     IP筛选器列表对话框中单击确定,然后在管理 IP 筛选器列表和筛选器操作对话框中单击确定

创建 IPSec 策略

要创建 IPSec 策略来对 SNMP 通信强制实施 IPSec,请按下列步骤操作:

1.       右键单击左窗格中的“IP 安全策略,在本地计算机上,然后单击创建 IP 安全策略

IP 
安全策略向导启动。

2.       单击下一步

3.       “IP 安全策略名称页上的名称框中键入安全 SNMP。在描述框中,键入 SNMP 通信强制实施 IPSec,然后单击下一步

4.       单击以清除激活默认响应规则复选框,然后单击下一步

5.       正在完成 IP 安全策略向导页上,验证编辑属性复选框已被选中,然后单击完成

6.       安全SNMP 属性对话框中,单击以清除使用添加向导复选框,然后单击添加

7.       单击 IP筛选器列表选项卡,然后单击 SNMP 消息 (161/162)

8.       单击筛选器操作选项卡,然后单击需要安全

9.       单击身份验证方法选项卡。默认的身份验证方法为 Kerberos。如果您需要备用身份验证方法,请单击添加。在新身份验证方法属性对话框中,从下面的列表中选择所需的身份验证方法,然后单击确定

o    Active Directory 默认值(KerberosV5 协议)

o    使用来自证书颁发机构(CA)的证书

o    使用此字符串(预共享密钥)

10.    新规则属性对话框中,单击应用,然后单击确定

11.     SNMP属性对话框中,验证 SNMP 消息 (161/162) 复选框已被选中,然后单击确定

12.    本地安全设置控制台的右窗格中,右键单击安全 SNMP 规则,然后单击指派


在所有运行 SNMP 服务的基于 Windows 的计算机上完成此过程。SNMP 管理站上也必须配置此 IPSec 策略。


图14 开始Trap功能

       配置完后,运行MIB Walk,开启监听功能,如图14所示。


图15 使用SNMPUtil产生监听错误信息

       为了测试Trap功能,另外使用了软件SNMPUtil用错误的团体名发送Get请求给Agent,如图15所示。右边是SNMPUtil的监听功能收到来自被管对象的错误信息。MIB Walk无任何操作下,可收到该错误信息,如图16所示。


三、源代码分析

1、模块分析


图17 MIBWalk的五大组成模块

       如图17所示,MIB Walk程序由五大部分组成。这五个模块分别是:列表单元格编辑模块(ListCellEdit)、输入模块(Input)、列表控制执行模块(ListCtrlEx)、显示对话模块(MonitorDlg)、显示模块(Monitor)。

2、流程图分析

        如图18所示,当执行MIB Walk时,程序首先会进行初始化,对Agent的地址、Community的属性,OID的初始值、执行的默认值进行初始化赋值,显示MIB_Ⅱ子树,而这些初始化语句均在显示对话模块(MonitorDlg)中。该模块在初始化时还会调用显示模块(Monitor),显示程序的窗口的图标等交互按钮。当程序在执行Get、GetNext、Walk或将OID加入被选中列表等功能时,程序会调用列表单元编辑模块(ListCellEdit)显示OID、类型和值,同时列表单元编辑模块中则调用了列表控制执行模块(ListCtrlEx)模块,使得单元格可被选中。


图18 MIBWalk运行流程图

        另外,在显示对话模块中,还包块了陷阱功能、OID可被选中、导出/保存对象信息功能。

3、代码分析

(1) 列表单元格编辑模块(ListCellEdit),该类主要用于读入和输出OID,如图19,为其主要操作的对象。其代码分析如下。

图19 列表单元格编辑模块的主要操作对象

// ListCellEdit.cpp : implementation file
//

#include "stdafx.h"
#include "ListCellEdit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CListCellEdit

/*构造ListCellEdit类*/
CListCellEdit:: CListCellEdit(int nItem, int nSubItem, CString strInitText)

/*分别将获取的变量赋值给m_nItem,m_nSubItem,m_strInitText*/
:m_bEscape (FALSE)
{
    m_nItem = nItem;
    m_nSubItem = nSubItem;
    m_strInitText = strInitText;
}

CListCellEdit::~CListCellEdit()
{
}

/*使用BEGIN_MESSAGE_MAP宏开始消息映射定义*/
/*CListCellEdit,指定消息映射所属的类的名字*/
/*CEdit,指定CListCellEdit的基类的名字*/
BEGIN_MESSAGE_MAP(CListCellEdit, CEdit)
	//{{AFX_MSG_MAP(CListCellEdit)
	/*框架在失去输入焦点之后立刻条用这个成员函数*/
ON_WM_KILLFOCUS()
/*当非用户区即将被销毁时,框架调用这个函数,*/
/*这是Windows的窗口被销毁时调用的最后一个成员函数*/
	ON_WM_NCDESTROY()
/*当击键被转换为非系统字符时,调用这个函数*/
	ON_WM_CHAR()
/*当应用程序通过调用成员函数Create或CreateEx请求创建*/
/*Windows的窗口时,框架调用这个成员函数*/
ON_WM_CREATE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/
// CListCellEdit message handlers

/*重写OnKillFocus方法,失去焦点是调用*/
void CListCellEdit::OnKillFocus(CWnd* pNewWnd) 
{
	/*调用父类OnKillFocus方法*/
CEdit::OnKillFocus(pNewWnd);
	/*获取OID*/
	SetListItemText();
	/*关闭陷阱和数据保存线程*/
    DestroyWindow();
}

/*释放该类指向的内存空间*/
void CListCellEdit::OnNcDestroy() 
{
	CEdit::OnNcDestroy();

    delete this;
}

/*信息转化*/
BOOL CListCellEdit::PreTranslateMessage(MSG* pMsg) 
{
	// TODO: Add your specialized code here and/or call the base class
	/*如果用户键入一个非系统键的值时,包括回车、删除、Esc或Ctrl时*/
	if( pMsg->message == WM_KEYDOWN )
	{
		if(pMsg->wParam == VK_RETURN
			|| pMsg->wParam == VK_DELETE
			|| pMsg->wParam == VK_ESCAPE
			|| GetKeyState( VK_CONTROL)
			)
		{
			::TranslateMessage(pMsg);/*转化为字符信息*/
			::DispatchMessage(pMsg);/*调度信息给窗口程序*/
			return TRUE;		    	// DO NOT process further
		}
	}
	
	return CEdit::PreTranslateMessage(pMsg);/*直接调用父类的方法*/
}

/*定义OnChar函数,当击键被转化为非系统字符是,框架调用这个函数*/
void CListCellEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) 
{
	// TODO: Add your message handler code here and/or call default
	/*当键入回车或Esc时,指针指向当前焦点;如果是Esc还需将*/
/*m_bEscape标记为真*/
	if( nChar == VK_ESCAPE || nChar == VK_RETURN)
	{
		if( nChar == VK_ESCAPE )
			m_bEscape = TRUE;
		GetParent()->SetFocus();

		return;
	}

	// Resize edit control if needed
	// Get text extent
	CString str;

	GetWindowText( str ); /*获得文本框文字*/
	CWindowDC dc(this); /*构造一个CWindowsDC对象*/
	CFont *pFont = GetParent()->GetFont();//创建指向当前文本框的CFont指针
	CFont *pFontDC = dc.SelectObject( pFont );//创建指向选择框的CFont指针
	CSize size = dc.GetTextExtent( str );
	dc.SelectObject( pFontDC );
	size.cx += 5;			   	// add some extra buffer

	/*获得来自客户端的rect值*/
	CRect rect, parentrect;
	GetClientRect( &rect );
	GetParent()->GetClientRect( &parentrect );

	//匹配rect的值
	ClientToScreen( &rect );
	GetParent()->ScreenToClient( &rect );

	/*检查是否需要调整大小和是否有足够的增长空间*/
if( size.cx > rect.Width() )
	{
		if( size.cx + rect.left < parentrect.right )
			rect.right = rect.left + size.cx;
		else
			rect.right = parentrect.right;
		MoveWindow( &rect );
	}
	
	CEdit::OnChar(nChar, nRepCnt, nFlags);
}

int CListCellEdit::OnCreate(LPCREATESTRUCT lpCreateStruct) 
{
	if (CEdit::OnCreate(lpCreateStruct) == -1)
		return -1;
	
	/*调整到适合的字体*/
	CFont* font = GetParent()->GetFont();
	SetFont(font);

	SetWindowText( m_strInitText );
	SetFocus();
	SetSel( 0, -1 );
	
	return 0;
}

void CListCellEdit::SetListItemText()
{
    CString Text;
    GetWindowText (Text);
	return;
	//AfxMessageBox("SetListItemText = "+ Text);
    // Send Notification to parent of ListView ctrl
    LV_DISPINFO dispinfo;
    dispinfo.hdr.hwndFrom = GetParent()->m_hWnd;
    dispinfo.hdr.idFrom = GetDlgCtrlID();
    dispinfo.hdr.code = LVN_ENDLABELEDIT;

    dispinfo.item.mask = LVIF_TEXT;
    dispinfo.item.iItem = m_nItem;
    dispinfo.item.iSubItem = m_nSubItem;
    dispinfo.item.pszText = m_bEscape ? NULL : LPTSTR ((LPCTSTR) Text);
    dispinfo.item.cchTextMax = Text.GetLength();

    GetParent()->GetParent()->SendMessage (WM_NOTIFY, GetParent()->GetDlgCtrlID(), (LPARAM) &dispinfo);
}

(2) 输入模块(Input),是实现MIB Walk的Set功能时弹出对话框的输入功能的模块。如图20所示。其代码分析如下。


图20 输入模块(Input)的主要操作对象

// Input.cpp : implementation file
//

#include "stdafx.h"
#include "Monitor.h"
#include "Input.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CInput dialog

/*初始化构造一个CDialog类型的类*/
CInput::CInput(CWnd* pParent /*=NULL*/)
	: CDialog(CInput::IDD, pParent)
{
//{{AFX_DATA_INIT(CInput)
	m_strVal = _T("");
	m_strOID = _T("");
	//}}AFX_DATA_INIT
	m_nTypeIndex = 0;
}

/*构造一个做为处理数据交换的类*/
void CInput::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CInput)

	//DDX_Text函数管理着对话框、表格或控件对象中的编辑控件与对话框
	//表格或空间对象的CString型数据成员之间int、Unit、long、DWord、
	//CString、float或double类型数据交换
	//第一个参数指向CDataExchange对象的指针。框架提供这个对象,用
//于建立数据交换的环境,包括其方向
//第二个参数是控件的ID
//第三个参数是控制对象的成员变量的引用,其类型取决于对DDX_Text
//的重载版本
	DDX_Text(pDX, IDC_Value, m_strVal);
	DDX_Text(pDX, IDC_Oid, m_strOID);
	//}}AFX_DATA_MAP
}


BEGIN_MESSAGE_MAP(CInput, CDialog)
	//{{AFX_MSG_MAP(CInput)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CInput message handlers

BOOL CInput::OnInitDialog() 
{
	CDialog::OnInitDialog();
	
	// TODO: Add extra initialization here
	/*将焦点设为下拉框被选中项*/
	((CComboBox*)GetDlgItem(IDC_ValueType))->SetCurSel(0);
	return TRUE;  // return TRUE unless you set the focus to a control
	              // EXCEPTION: OCX Property Pages should return FALSE
}
/************************************************************************/
/* Value Type
ASN_INTEGER
ASN_INTEGER32
ASN_UNSIGNED32
ASN_COUNTER64
ASN_OCTETSTRING
ASN_BITS
ASN_OBJECTIDENTIFIER
ASN_SEQUENCE
ASN_IPADDRESS
ASN_COUNTER32
ASN_GAUGE32
ASN_TIMETICKS
ASN_OPAQUE
*/
/************************************************************************/
void CInput::OnOK() 
{
	// TODO: Add extra validation here
	/*将被选中项的索引赋值给m_nTypeIndex*/
	m_nTypeIndex = ((CComboBox*)GetDlgItem(IDC_ValueType))->GetCurSel();
	
	CDialog::OnOK();
}

(3) 列表控制执行模块(ListCtrlEx),主要功能是列出对象的OID,类型和值。如图21所示。代码分析如下。


图21列表控制执行模块(ListCtrlEx)的主要操作对象

// ListCtrlEx.cpp : implementation file
//

#include "stdafx.h"
#include "Monitor.h"
#include "ListCtrlEx.h"
#include "ListCellEdit.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CListCtrlEx

CListCtrlEx::CListCtrlEx(BOOL allusr)
{
	forAllUser = allusr;
}

CListCtrlEx::~CListCtrlEx()
{
}


BEGIN_MESSAGE_MAP(CListCtrlEx, CListCtrl)
	//{{AFX_MSG_MAP(CListCtrlEx)
	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, OnCustomDraw)
	ON_WM_LBUTTONDOWN()
	ON_WM_VSCROLL()
	ON_WM_HSCROLL()
	ON_WM_SIZE()
	ON_NOTIFY_REFLECT(LVN_ENDLABELEDIT, OnEndlabeledit)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/

///
// CListCtrlEx message handlers
// OnCustomDraw
void CListCtrlEx::OnCustomDraw(NMHDR* pNMHDR, LRESULT* pResult)
{
	NMLVCUSTOMDRAW* pLVCD = reinterpret_cast<NMLVCUSTOMDRAW*>(pNMHDR);

	// Take the default processing unless we set this to something else below.
	*pResult = CDRF_DODEFAULT;

	// 第一,检查画板,如果是控件预画板,让系统传达信息给每一项。
	if (pLVCD->nmcd.dwDrawStage == CDDS_PREPAINT)
	{
		*pResult = CDRF_NOTIFYITEMDRAW;
	}
	else if (pLVCD->nmcd.dwDrawStage == CDDS_ITEMPREPAINT)
	{
		// 这是给每一项的通告,当子项预构画板时。
		*pResult = CDRF_NOTIFYSUBITEMDRAW;
	}
}


void CListCtrlEx::OnLButtonDown(UINT nFlags, CPoint point) 
{
	CListCtrl::OnLButtonDown(nFlags, point);
	// TODO: Add your message handler code here and/or call default
    int nItem;
    int nSubItem;
	if (!((CMonitorApp*)AfxGetApp())->isRoot && !forAllUser )
	{
		return;
	}
    if ((nItem = HitTestEx (point, nSubItem)) != -1)
    {
		EditSubItem(nItem, nSubItem);
	}
}

/*返回鼠标点击子项的行数*/
int CListCtrlEx::HitTestEx (CPoint& Point, int& nSubItem)
{
	nSubItem = 0;
	int ColumnNum = 0;
    int Row = HitTest (Point, NULL);
	
    // 确保整一列都在LVS_REPORT中
    if ((GetWindowLong (m_hWnd, GWL_STYLE) & LVS_TYPEMASK) != LVS_REPORT)
		return Row;
	
    //使得最顶和最底的行均可见
    Row = GetTopIndex();
    int Bottom = Row + GetCountPerPage();
    if (Bottom > GetItemCount())
		Bottom = GetItemCount();
    
    // 获得列数
    CHeaderCtrl* pHeader = (CHeaderCtrl*) GetDlgItem(0);
    int nColumnCount = pHeader->GetItemCount();
	
    // 遍历可见行
    for(; Row <= Bottom; Row++)
    {
		// 获取边缘值并判断鼠标的坐标是否在范围内
		CRect Rect;
		GetItemRect (Row, &Rect, LVIR_BOUNDS);
		if (Rect.PtInRect (Point))
		{
			// 查找鼠标点击的对应列
			for (ColumnNum = 0; ColumnNum < nColumnCount; ColumnNum++)
			{
				int ColWidth = GetColumnWidth (ColumnNum);
				if (Point.x >= Rect.left && Point.x <= (Rect.left + ColWidth))
				{
					nSubItem = ColumnNum;
					return Row;
				}
				Rect.left += ColWidth;
			}
		}
    }

    return -1;
}

/*编辑子项*/
CEdit* CListCtrlEx::EditSubItem(int nItem, int nSubItem)
{
    // 返回的指针不应被保留

    // 确保子项可见
    if (!EnsureVisible (nItem, TRUE)) return NULL;

    // 确保nCol是正确的
    CHeaderCtrl* pHeader = (CHeaderCtrl*) GetDlgItem(0);
    int nColumnCount = pHeader->GetItemCount();
    if (nSubItem >= nColumnCount || GetColumnWidth (nSubItem) < 5)
		return NULL;

    // 获取行分支
    int Offset = 0;
    for (int iColumn = 0; iColumn < nSubItem; iColumn++)
		Offset += GetColumnWidth (iColumn);

    CRect Rect;
    GetItemRect (nItem, &Rect, LVIR_BOUNDS);

    // 判断展开子项时是否需要滚动条
    CRect ClientRect;
    GetClientRect (&ClientRect);
    if (Offset + Rect.left < 0 || Offset + Rect.left > ClientRect.right)
    {
		CSize Size;
		if (Offset + Rect.left > 0)
			Size.cx = -(Offset - Rect.left);
		else
			Size.cx = Offset - Rect.left;
		Size.cy = 0;
		Scroll (Size);
		Rect.left -= Size.cx;
    }

    // 获得第n个子项的准线
    LV_COLUMN lvCol;
    lvCol.mask = LVCF_FMT;
    GetColumn (nSubItem, &lvCol);
    DWORD dwStyle;
    if ((lvCol.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_LEFT)
		dwStyle = ES_LEFT;
    else if ((lvCol.fmt & LVCFMT_JUSTIFYMASK) == LVCFMT_RIGHT)
		dwStyle = ES_RIGHT;
    else dwStyle = ES_CENTER;
	
	//wStyle |= ES_LOWERCASE;

    Rect.left += Offset+4;
    Rect.right = Rect.left + GetColumnWidth (nSubItem) - 3;
    if (Rect.right > ClientRect.right)
		Rect.right = ClientRect.right;
	dwStyle |= WS_BORDER | WS_CHILD | WS_VISIBLE | ES_AUTOHSCROLL;
	//dwStyle = GetStyle();
    CEdit *pEdit = new CListCellEdit (nItem, nSubItem, GetItemText (nItem, nSubItem));
    pEdit->Create(dwStyle, Rect, this, IDC_CtrlList);
    return pEdit;
}

void CListCtrlEx::OnVScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default
    if (GetFocus() != this) SetFocus();
	
	CListCtrl::OnVScroll(nSBCode, nPos, pScrollBar);
}

void CListCtrlEx::OnHScroll(UINT nSBCode, UINT nPos, CScrollBar* pScrollBar) 
{
	// TODO: Add your message handler code here and/or call default
    if (GetFocus() != this) SetFocus();

	CListCtrl::OnHScroll(nSBCode, nPos, pScrollBar);
}

void CListCtrlEx::OnSize(UINT nType, int cx, int cy) 
{
	CListCtrl::OnSize(nType, cx, cy);
	
	// TODO: Add your message handler code here
	
}

void CListCtrlEx::OnEndlabeledit(NMHDR* pNMHDR, LRESULT* pResult) 
{
    LV_DISPINFO *plvDispInfo = (LV_DISPINFO *)pNMHDR;
    LV_ITEM	*plvItem = &plvDispInfo->item;

    if (plvItem->pszText != NULL)
    {
		SetItemText (plvItem->iItem, plvItem->iSubItem, plvItem->pszText);
    }
    *pResult = FALSE;
}

(4) 显示对话模块(MonitorDlg),该模块是主界面的主要功能的实现模块。如图22所示,为其主要操作对象。代码分析如下。


图22监视对话模块(MonitorDlg)的主要操作对象

// MonitorDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Monitor.h"
#include "MonitorDlg.h"
#include "Input.h"
#include <mgmtapi.h>
#include <io.h>

#pragma comment(lib,"mgmtapi")
#pragma comment(lib,"snmpapi")

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

#define TIMEOUT 6000 /* milliseconds */
#define RETRIES 3

#define USER_EXEC_SNMP	(WM_USER+1)
#define USER_START_TRAP	(WM_USER+2)
#define USER_STOP_TRAP	(WM_USER+3)
#define USER_START_STAT (WM_USER+4)
#define USER_STOP_STAT	(WM_USER+5)

struct ThreadParam{
	CString		agent;
	CString		comm;
	CMonitorDlg* pDlg;
};

CWinThread * pThread = NULL;	//陷阱线程
CWinThread * pThread2 = NULL;	//数据线程
CMonitorDlg * pDlg = NULL;
/
UINT SnmpTrap(LPVOID param);
UINT SnmpStat(LPVOID param);
BOOL AsnPrint(AsnAny* any, CString& strbuf, BOOL bShowType=TRUE);

//将IP值转换成IP串
CString  ul2addr(ULONG l)
{
	unsigned char* pch = (unsigned char*)&l;
	CString str;
	str.Format("%hu.%hu.%hu.%hu",(unsigned short)*(pch),(unsigned short)*(pch+1),
		(unsigned short)*(pch+2),(unsigned short)*(pch+3));
	return str;
}

//将内存中的数据转为字符串
CString dumpOctStr(void * buf, int len)
{
	ASSERT(buf && len>0);
	CString str = _T("");
	unsigned char *psh = (unsigned char*)buf;
	char pch[4] = {'\0'};
	int ret = 0;

	while (len--) {
		ret = sprintf(pch,"%02X ",(UINT)*psh);
		pch[ret] = '\0';
		psh++;
		str = pch + str;
	}
	return str;
}

// 显示关于对话框
class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	//{{AFX_DATA(CAboutDlg)
	enum { IDD = IDD_ABOUTBOX};
	//}}AFX_DATA

	// ClassWizard generated virtual function overrides
	//{{AFX_VIRTUAL(CAboutDlg)
	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
	//}}AFX_VIRTUAL

// Implementation
protected:
	//{{AFX_MSG(CAboutDlg)
	//}}AFX_MSG
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
	//{{AFX_DATA_INIT(CAboutDlg)
	//}}AFX_DATA_INIT
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CAboutDlg)
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
	//{{AFX_MSG_MAP(CAboutDlg)
		// No message handlers
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CMonitorDlg dialog

CMonitorDlg::CMonitorDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CMonitorDlg::IDD, pParent)
{
	//{{AFX_DATA_INIT(CMonitorDlg)
	m_strOid = _T("");
	m_strAgentComm = _T("");
	m_nIntervalTime = 2;
	//}}AFX_DATA_INIT
	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32
	m_bEnableTrap = FALSE;
	m_nRadioIndex = WALK;
	m_strAgentComm = "public";
	m_strOid = "1.1.0";
	nItem = -1;
	nSubItem = -1;
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
}

void CMonitorDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	//{{AFX_DATA_MAP(CMonitorDlg)
	DDX_Control(pDX, IDC_SPIN1, m_CtrlSpin);
	DDX_Control(pDX, IDC_CtrlList, m_CtrlList);
	DDX_Control(pDX, IDC_Interface, m_CtrlInterface);
	DDX_Control(pDX, IDC_AgentIP, m_CtrlAgentIP);
	DDX_Control(pDX, IDC_MibTree, m_CtrlMibTree);
	DDX_Text(pDX, IDC_Oid, m_strOid);
	DDX_Text(pDX, IDC_AgentComm, m_strAgentComm);
	DDX_Text(pDX, IDC_IntervalTime, m_nIntervalTime);
	DDV_MinMaxUInt(pDX, m_nIntervalTime, 1, 60);
	//}}AFX_DATA_MAP
}

BEGIN_MESSAGE_MAP(CMonitorDlg, CDialog)
	//{{AFX_MSG_MAP(CMonitorDlg)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(IDC_Exec, OnExec)
	ON_BN_CLICKED(IDC_SnmpGet, OnSnmpGet)
	ON_BN_CLICKED(IDC_SnmpGetNext, OnSnmpGetNext)
	ON_BN_CLICKED(IDC_SnmpSet, OnSnmpSet)
	ON_BN_CLICKED(IDC_SnmpWalk, OnSnmpWalk)
	ON_BN_CLICKED(IDC_SnmpTrap, OnSnmpTrap)
	ON_NOTIFY(NM_DBLCLK, IDC_MibTree, OnDblclkMibTree)
	ON_NOTIFY(NM_CLICK, IDC_MibTree, OnClickMibTree)
	ON_BN_CLICKED(IDC_StatStart, OnStatStart)
	ON_NOTIFY(NM_RCLICK, IDC_CtrlList, OnRclickCtrlList)
	ON_BN_CLICKED(IDC_AddOid, OnAddOid)
	ON_NOTIFY(NM_CLICK, IDC_CtrlList, OnClickCtrlList)
	ON_LBN_SETFOCUS(IDC_Interface, OnSetfocusInterface)
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()

/
// CMonitorDlg message handlers

BOOL CMonitorDlg::OnInitDialog()
{
	CDialog::OnInitDialog();
	
	// Add "About..." menu item to system menu.
	HTREEITEM	current;
	int i;	
	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);
	

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon
	
	// TODO: Add extra initialization here
	/************************************************************************/
	/* 初始化MIB_Ⅱ子树			                                    */
	/************************************************************************/
	m_CtrlMibTree.InsertItem("mib-2");
	current = m_CtrlMibTree.GetRootItem();
	m_CtrlMibTree.InsertItem("1 system",current);
	m_CtrlMibTree.InsertItem("2 interface",current);
	m_CtrlMibTree.InsertItem("3 at",current);
	m_CtrlMibTree.InsertItem("4 ip",current);
	m_CtrlMibTree.InsertItem("5 icmp",current);
	m_CtrlMibTree.InsertItem("6 tcp",current);
	m_CtrlMibTree.InsertItem("7 udp",current);
	m_CtrlMibTree.InsertItem("8 egp",current);
	m_CtrlMibTree.InsertItem("9 comt",current);
	m_CtrlMibTree.InsertItem("10 transmission",current);
	m_CtrlMibTree.InsertItem("11 snmp",current);
		current = m_CtrlMibTree.GetChildItem(current);
		m_CtrlMibTree.InsertItem("1 sysDescr",current);
		m_CtrlMibTree.InsertItem("2 sysObjectID",current);
		m_CtrlMibTree.InsertItem("3 sysUpTime",current);
		m_CtrlMibTree.InsertItem("4 sysContact",current);
		m_CtrlMibTree.InsertItem("5 sysName",current);
		m_CtrlMibTree.InsertItem("6 sysLocation",current);
		m_CtrlMibTree.InsertItem("7 sysServices",current);
		/*以下省略部分MIB_Ⅱ子树的节点*/

	/*设置默认属性*/
	m_CtrlMibTree.SelectItem(m_CtrlMibTree.GetRootItem());
	m_CtrlAgentIP.SetWindowText("127.0.0.1");
	m_CtrlList.InsertColumn(0,"OID");
	m_CtrlList.InsertColumn(1,"Type(Value)");
	m_CtrlList.SetColumnWidth(0,160);
	m_CtrlList.SetColumnWidth(1,300);
	m_CtrlSpin.SetBuddy((CWnd*)GetDlgItem(IDC_IntervalTime));
	m_CtrlSpin.SetRange(1,60);
	((CButton*)GetDlgItem(IDC_SnmpWalk))->SetCheck(1);
	pDlg = this;
	return TRUE;  // return TRUE  unless you set the focus to a control
}

	/*关于窗口的特殊处理*/
void CMonitorDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

void CMonitorDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

//最小化窗口
HCURSOR CMonitorDlg::OnQueryDragIcon()
{
	return (HCURSOR) m_hIcon;
}

LRESULT CMonitorDlg::DefWindowProc(UINT message, WPARAM wParam, LPARAM lParam) 
{
	// TODO: Add your specialized code here and/or call the base class
	/*功能选择和信息提示*/
	switch(message) {
	case USER_EXEC_SNMP:
		if(!SnmpQuery()){
			AfxMessageBox("Snmp Query Failure");
		}
		SetDlgItemText(IDC_Status,"Snmp Query Success");
		break;
	case USER_START_TRAP:
		pThread = AfxBeginThread(SnmpTrap,NULL);
		break;
	case USER_STOP_TRAP:
		if(pThread){
			pThread->ExitInstance();
		}
		pThread = NULL;
		AfxMessageBox("Trap Server Was Stopped ");
		break;
	case USER_START_STAT:
		pThread2 = AfxBeginThread(SnmpStat,NULL);
		//AfxMessageBox("Stat Started");
		break;
	case USER_STOP_STAT:
		if (pThread2){
			pThread2->ExitInstance();
		}
		pThread2 = NULL;
		SetDlgItemText(IDC_Status,"Statistics Thread Was Stoped");
		break;
	default:
		break;
	}
	return CDialog::DefWindowProc(message, wParam, lParam);
}

void CMonitorDlg::OnExec() 
{
	// TODO: Add your control notification handler code here
	if (CheckParams()) {
		PostMessage(USER_EXEC_SNMP);
	}else{
		AfxMessageBox("Wrong Parameters");
	}
	
}

BOOL CMonitorDlg::SnmpQuery()
{
	CString              agent;
	CString              community;
	RFC1157VarBindList variableBindings;
	LPSNMP_MGR_SESSION	session;
	AsnObjectIdentifier reqObject;
	INT        timeout = TIMEOUT;
	INT        retries = RETRIES;

	BYTE       requestType;
	AsnInteger errorStatus;
	AsnInteger errorIndex;
	char        *chkPtr = NULL;
	CString		errMsg;
	int			index;

	UpdateData(TRUE);
	
	//获取MIB数据
	switch(m_nRadioIndex) {
	case GET:
		if(m_strOid.Find(".0")==-1){
			m_strOid += ".0";
		}
		requestType = ASN_RFC1157_GETREQUEST;
		break;
	case WALK:
		index = m_strOid.Find(".0");
		if(index!=-1){
			m_strOid.Delete(index,2);
		}
		requestType = ASN_RFC1157_GETNEXTREQUEST;
		break;
	case GETNEXT:
		if(m_strOid.Find(".0")==-1){
			m_strOid += ".0";
		}
		requestType = ASN_RFC1157_GETNEXTREQUEST;
		break;
	case SET:
		requestType = ASN_RFC1157_SETREQUEST;
		break;
	default:
		AfxMessageBox("wrong index of radio buttons");
		break;
	}
	UpdateData(FALSE);

	//重置ListCellEdit
	m_CtrlList.DeleteAllItems();
	m_CtrlList.SetColumnWidth(0,160);
	m_CtrlList.SetColumnWidth(1,300);
	m_CtrlAgentIP.GetWindowText(agent);
	community = m_strAgentComm;
	variableBindings.len = 0;
	variableBindings.list = NULL;
	SetDlgItemText(IDC_Status,"");
	if (!SnmpMgrStrToOid(m_strOid.GetBuffer(0), &reqObject)){
		AfxMessageBox("Unknown Object Identifier");
		return FALSE;
	}else{
		// 一旦成功,将数据加载到列表当中。
		variableBindings.len++;
		if ((variableBindings.list = (RFC1157VarBind *)SNMP_realloc(variableBindings.list,
			sizeof(RFC1157VarBind))) == NULL){
			AfxMessageBox("Error: Error allocating oid "+m_strOid);
			return FALSE;
		}
	variableBindings.list[variableBindings.len - 1].name = reqObject;		variableBindings.list[variableBindings.len - 1].value.asnType = ASN_NULL;		
	}

	//开启SNMP会话
	if((session=SnmpMgrOpen(agent.GetBuffer(0),community.GetBuffer(0),timeout,retries))==NULL){
		errMsg.Format("Error: On SnmpMgrOpen [%d]",GetLastError());
		AfxMessageBox(errMsg);
		return FALSE;
	}
	
	
	//发送SNMP请求和处理返回信息S
	if (m_nRadioIndex==GET || m_nRadioIndex==GETNEXT) {
if (!SnmpMgrRequest(session, requestType, &variableBindings,&errorStatus, &errorIndex))	{
		// The API is indicating an error.
			errMsg.Format("error on SnmpMgrRequest %d\n", GetLastError());
			AfxMessageBox(errMsg);
			return FALSE;
		}else{
		// The API succeeded, errors may be indicated from the remote agent.
			if (errorStatus > 0){
errMsg.Format("Error: errorStatus=%d, errorIndex=%d\n",errorStatus, errorIndex);
				AfxMessageBox(errMsg);
				//return FALSE;
			}else{
				// Display the resulting variable bindings.
				char *string = NULL;
				
				for(unsigned int i=0; i < variableBindings.len; i++){
					m_CtrlList.InsertItem(0,NULL);
					SnmpMgrOidToStr(&variableBindings.list[i].name, &string);
					errMsg.Format("%s", string);
					m_CtrlList.SetItemText(0,0,errMsg);
					
					if (string){
						SNMP_free(string);
					}
					//SnmpUtilPrintAsnAny(&variableBindings.list[i].value);
					AsnPrint(&variableBindings.list[i].value, errMsg);
					m_CtrlList.SetItemText(0,1,errMsg);
				} // end for()				
			}
		}
		// Free the variable bindings that have been allocated.
		SnmpUtilVarBindListFree(&variableBindings);
	}else if (m_nRadioIndex == WALK) {
        // Walk is a common term used to indicate that all MIB variables
        // under a given OID are to be traversed and displayed.  This is
        // a more complex operation requiring tests and looping in addition
        // to the steps for get/getnext above.
        AsnObjectIdentifier root;
        AsnObjectIdentifier tempOid;

		SnmpUtilOidCpy(&root, &variableBindings.list[0].name);
        		
        while(TRUE){
            if (!SnmpMgrRequest(session, requestType, &variableBindings,&errorStatus, &errorIndex)) {
                // The API is indicating an error.
                errMsg.Format("error on SnmpMgrRequest %d", GetLastError());
				AfxMessageBox(errMsg);
                break;
            }else {
                // The API succeeded, errors may be indicated from the remote
                // agent.
                // Test for end of subtree or end of MIB.
				
                if (errorStatus == SNMP_ERRORSTATUS_NOSUCHNAME ||
                    SnmpUtilOidNCmp(&variableBindings.list[0].name,&root, root.idLength)) {
						errMsg.Format("End of MIB subtree.");
						m_CtrlList.InsertItem(0,NULL);
						m_CtrlList.SetItemText(0,0,errMsg);
						break;
                }
                // Test for general error conditions or sucesss.
                if (errorStatus > 0) {
                    errMsg.Format("Error: errorStatus=%d, errorIndex=%d \n",errorStatus, errorIndex);
					AfxMessageBox(errMsg);
                    break;
                }else{
                    // Display resulting variable binding for this iteration.
                    char *string = NULL;
					m_CtrlList.InsertItem(0,NULL);
                    SnmpMgrOidToStr(&variableBindings.list[0].name, &string);
                    errMsg.Format("%s", string);
					m_CtrlList.SetItemText(0,0,errMsg);
					
                    if (string){
						SNMP_free(string);
					}
                    //SnmpUtilPrintAsnAny(&variableBindings.list[0].value);
					AsnPrint(&variableBindings.list[0].value,errMsg);
					m_CtrlList.SetItemText(0,1,errMsg);
                }
           } // end if()
            // Prepare for the next iteration.  Make sure returned oid is
            // preserved and the returned value is freed.
            SnmpUtilOidCpy(&tempOid, &variableBindings.list[0].name);
            SnmpUtilVarBindFree(&variableBindings.list[0]);
            SnmpUtilOidCpy(&variableBindings.list[0].name, &tempOid);
            variableBindings.list[0].value.asnType = ASN_NULL;
            SnmpUtilOidFree(&tempOid);
		} // end while()
        // Free the variable bindings that have been allocated.
        SnmpUtilVarBindListFree(&variableBindings);
        SnmpUtilOidFree(&root);
    }else if(m_nRadioIndex == SET){
		
	/************************************************************************/
	/* get the oid and value here                                        */
		CInput m_DlgInput;
		char * stopstring;
		AsnOctetString asnstr;
		m_DlgInput.m_strOID = m_strOid;
		if(m_DlgInput.DoModal()==IDCANCEL){
			return TRUE;
		}
		/*更改代码中的类型与Index之间错位*/
		switch (m_DlgInput.m_nTypeIndex)	{
		case 0: //ASN_BITS
			variableBindings.list[0].value.asnType = ASN_BITS;
			AfxMessageBox("ASN_BITS not supported yet");
			return FALSE;
			break;
		case 1:	//ASN_COUNTER32
			variableBindings.list[0].value.asnType = ASN_COUNTER32;
variableBindings.list[0].value.asnValue.counter = 
strtoul(m_DlgInput.m_strVal.GetBuffer(0),&stopstring, 10);
			break;
		case 2:	//ASN_COUNTER64
			variableBindings.list[0].value.asnType = ASN_COUNTER64;
			//maybe use bcmath lib is better
		variableBindings.list[0].value.asnValue.counter64.QuadPart = 
				_atoi64(m_DlgInput.m_strVal.GetBuffer(0));
			break;
		case 3://ASN_GAUGE32
			variableBindings.list[0].value.asnType = ASN_GAUGE32;
			variableBindings.list[0].value.asnValue.gauge = 
				strtoul(m_DlgInput.m_strVal.GetBuffer(0),&stopstring, 10);
			break;
		case 4:	//ASN_INTEGER
			variableBindings.list[0].value.asnType = ASN_INTEGER;
			variableBindings.list[0].value.asnValue.number = 
							atoi(m_DlgInput.m_strVal.GetBuffer(0));
			break;
		case 5: //ASN_INTEGER32
			variableBindings.list[0].value.asnType = ASN_INTEGER32;
			variableBindings.list[0].value.asnValue.number = 
				atoi(m_DlgInput.m_strVal.GetBuffer(0));
			break;
		case 6:	//ASN_IPADDRESS
			variableBindings.list[0].value.asnType = ASN_IPADDRESS;
			AfxMessageBox("ASN_IPADDRESS not supported yet");
			return FALSE;
			break;
		case 7:	//ASN_OBJECTIDENTIFIER
			variableBindings.list[0].value.asnType = 
ASN_OBJECTIDENTIFIER;
			AfxMessageBox("ASN_OBJECTIDENTIFIER not supported yet");
			return FALSE;
			break;
		case 8:	//ASN_OCTETSTRING			
			asnstr.dynamic = TRUE;
			asnstr.length = m_DlgInput.m_strVal.GetLength();
			asnstr.stream = (BYTE*) SNMP_malloc(asnstr.length);
			memcpy(&asnstr, m_DlgInput.m_strVal.GetBuffer(0), 
						m_DlgInput.m_strVal.GetLength());
			variableBindings.list[0].value.asnType = ASN_OCTETSTRING;
			SnmpUtilOctetsCpy(
&variableBindings.list[0].value.asnValue.string, &asnstr);
			SnmpUtilOctetsFree(&asnstr);
			break;
		case 9://ASN_OPAQUE
			variableBindings.list[0].value.asnType = ASN_OPAQUE;
			asnstr.dynamic = TRUE;
			asnstr.length = m_DlgInput.m_strVal.GetLength();
			asnstr.stream = (BYTE*) SNMP_malloc(asnstr.length);
			memcpy(&asnstr, m_DlgInput.m_strVal.GetBuffer(0), 
m_DlgInput.m_strVal.GetLength());
			SnmpUtilOctetsCpy(
&variableBindings.list[0].value.asnValue.arbitrary, &asnstr);
			SnmpUtilOctetsFree(&asnstr);
			break;
		case 10:	//ASN_SEQUENCE
			variableBindings.list[0].value.asnType = ASN_SEQUENCE;
			AfxMessageBox("ASN_SEQUENCE not supported yet");
			return FALSE;
			break;
		case 11://ASN_TIMETICKS
			variableBindings.list[0].value.asnType = ASN_TIMETICKS;
			variableBindings.list[0].value.asnValue.ticks = 
strtoul(m_DlgInput.m_strVal.GetBuffer(0),	&stopstring, 10);
			break;
		case 12:	//ASN_UNSIGNED32
			variableBindings.list[0].value.asnType = ASN_UNSIGNED32;
			variableBindings.list[0].value.asnValue.unsigned32 = 
strtoul(m_DlgInput.m_strVal.GetBuffer(0), &stopstring, 10);
			break;
		default:
			AfxMessageBox("unknown ASN value type");
			return FALSE;
		}
		/*更改代码中的类型与Index之间错位*/
		
if (!SnmpMgrRequest(session, requestType, 
		&variableBindings,&errorStatus, &errorIndex)) {
                // The API is indicating an error.
			errMsg.Format("error on SnmpMgrRequest %d", GetLastError());
			AfxMessageBox(errMsg); 
        }else{
			if (errorStatus > 0){
				errMsg.Format("Error: errorStatus=%d, 
						errorIndex=%d\n",errorStatus, errorIndex);
				AfxMessageBox(errMsg);
				return FALSE;
			}else{
				SetDlgItemText(IDC_Status,"SNMP Set-Request OK");
			}			
        }
    }
	if (!SnmpMgrClose(session)) {
		errMsg.Format("Close SNMP Session Failed. Error : %d",GetLastError());
		AfxMessageBox(errMsg);
	}

	return TRUE;
}

void CMonitorDlg::OnSnmpGet()
{
	// TODO: Add your control notification handler code here
	m_nRadioIndex = GET;
}

void CMonitorDlg::OnSnmpGetNext() 
{
	// TODO: Add your control notification handler code here
	m_nRadioIndex = GETNEXT;
}

void CMonitorDlg::OnSnmpSet() 
{
	// TODO: Add your control notification handler code here
	m_nRadioIndex = SET;
}

void CMonitorDlg::OnSnmpWalk()
{
	// TODO: Add your control notification handler code here
	m_nRadioIndex = WALK;	
}

void CMonitorDlg::OnSnmpTrap() 
{
	// TODO: Add your control notification handler code here
	if(m_bEnableTrap){
		if(AfxMessageBox("Are you sure to stop trap server?",MB_OKCANCEL)==IDCANCEL){
			((CButton*)GetDlgItem(IDC_SnmpTrap))->SetCheck(1);
			return;
		}
		m_bEnableTrap = FALSE;
		PostMessage(USER_STOP_TRAP);
	}else{
		m_bEnableTrap = TRUE;
		PostMessage(USER_START_TRAP);
	}
}

BOOL CMonitorDlg::CheckParams()
{
	CString Agent;
	
	UpdateData(TRUE);
	m_CtrlAgentIP.GetWindowText(Agent);
	if (Agent.IsEmpty()) {
		AfxMessageBox("No Agent IP Address");
		return FALSE;
	}
	if (m_strAgentComm.IsEmpty()) {
		AfxMessageBox("No Agent Community, try 'public' as default");
		return FALSE;
	}
	if(m_strOid.IsEmpty()){
		AfxMessageBox("No Object Identifier Specified");
	}
	return TRUE;
}

/************************************************************************/
/*
typedef struct {
    BYTE asnType;
     union {
       AsnInteger32         number;      // ASN_INTEGER
                                         // ASN_INTEGER32
       AsnUnsigned32        unsigned32;  // ASN_UNSIGNED32
       AsnCounter64         counter64;   // ASN_COUNTER64
       AsnOctetString       string;      // ASN_OCTETSTRING
       AsnBits              bits;        // ASN_BITS
       AsnObjectIdentifier  object;      // ASN_OBJECTIDENTIFIER
       AsnSequence          sequence;    // ASN_SEQUENCE
       AsnIPAddress         address;     // ASN_IPADDRESS
       AsnCounter32         counter;     // ASN_COUNTER32
       AsnGauge32           gauge;       // ASN_GAUGE32
       AsnTimeticks         ticks;       // ASN_TIMETICKS
       AsnOpaque            arbitrary;   // ASN_OPAQUE
} asnValue;
} AsnAny;

asnType 
Indicates the variable's type. This member must be only one of the following values. Value Meaning 
ASN_INTEGER				Indicates a 32-bit signed integer variable.  
ASN_INTEGER32			Indicates a 32-bit signed integer variable.  
ASN_UNSIGNED32			Indicates a 32-bit unsigned integer variable.  
ASN_COUNTER64			Indicates a counter variable that increases until it reaches a maximum value of (2^64) – 1.  
ASN_OCTETSTRING			Indicates an octet string variable. 
ASN_BITS				Indicates a variable that is an enumeration of named bits.  
ASN_OBJECTIDENTIFIER	Indicates an object identifier variable.  
ASN_SEQUENCE			Indicates an ASN sequence variable. 
ASN_IPADDRESS			Indicates an IP address variable. 
ASN_COUNTER32			Indicates a counter variable. 
ASN_GAUGE32				Indicates a gauge variable. 
ASN_TIMETICKS			Indicates a timeticks variable. 
ASN_OPAQUE				Indicates an opaque variable. 

asnValue 
Contains the variable's value. This member can be only one of the following values. Value Meaning 
number			Accesses a 32-bit signed integer variable.  
unsigned32		Accesses a 32-bit unsigned integer variable.  
counter64		Accesses a counter variable that increases until it reaches a maximum value of (2^64) – 1.  
string			Accesses an octet string variable.  
bits			Accesses a variable that is an enumeration of named bits with non-negative, contiguous values, starting at zero.  
object			Accesses an object identifier variable.  
sequence		Accesses an ASN sequence variable.  
address			Accesses an IP address variable.  
counter			Accesses a counter variable that increases until it reaches a maximum value of (2^32) – 1.  
gauge			Accesses a gauge variable.  
ticks			Accesses a timeticks counter variable that is relative to a specific timer event.  
arbitrary		Accesses an opaque variable.  

*/
/************************************************************************/


BOOL AsnPrint(AsnAny* any, CString& strbuf, BOOL bShowType)
{
	ASSERT(any);
	char * string = NULL;
	if (bShowType) {
		switch(any->asnType) {
			case ASN_INTEGER:	//Indicates a 32-bit signed integer variable.  	
				strbuf.Format("ASN_INTEGER32( %d )", any->asnValue.number);
				break;
			case ASN_UNSIGNED32:	
//Indicates a 32-bit unsigned integer variable.  
				strbuf.Format("ASN_UNSIGNED32( %u )", 
									any->asnValue.unsigned32);
				break;
			case ASN_COUNTER64:	//Indicates a counter variable that increases 
								//until it reaches a maximum value of (2^64) – 1.  
				strbuf.Format(
"ASN_COUNTER64( %I64u )",any->asnValue.counter64);
				break;
			case ASN_OCTETSTRING:		//Indicates an octet string variable. 
				strbuf.Format(
"ASN_OCTETSTRING( %s )",any->asnValue.string.stream);
				break;
			case ASN_BITS:	
//Indicates a variable that is an enumeration of named bits.  
				strbuf = "ASN_BITS( " + 	
dumpOctStr((void*)any->asnValue.bits.stream,
any->asnValue.bits.length)+" )";
				break;
			case ASN_OBJECTIDENTIFIER:
//Indicates an object identifier variable.  
				SnmpMgrOidToStr(&(any->asnValue.object), &string);
				strbuf.Format("ASN_OBJECTIDENTIFIER( %s )",string);
				if(string){
					SNMP_free(string);
				}
				break;
			case ASN_SEQUENCE://Indicates an ASN sequence variable.
strbuf.Format("ASN_SEQUENCE( %s )",
any->asnValue.sequence.stream);
				break;
			case ASN_IPADDRESS:			//Indicates an IP address variable. 
				strbuf = "ASN_IPADDRESS( " + 
					ul2addr(*((ULONG*)(any->asnValue.address.stream))) + " )";
				break;
			case ASN_COUNTER32:			//Indicates a counter variable. 
				strbuf.Format("ASN_COUNTER32( %u )",any->asnValue.counter);
				break;
			case ASN_GAUGE32:			//Indicates a gauge variable. 
				strbuf.Format("ASN_GAUGE32( %u )",any->asnValue.gauge);
				break;
			case ASN_TIMETICKS:			//Indicates a timeticks variable. 
				strbuf.Format("ASN_TIMETICKS( %u )",any->asnValue.ticks);
				break;
			case ASN_OPAQUE:			//Indicates an opaque variable. 
				//treated as octstring
				strbuf = "ASN_OPAQUE( " + 
					dumpOctStr((void*)any->asnValue.arbitrary.stream,
					any->asnValue.arbitrary.length) + " )";
				break;
			default:
				strbuf = _T("unknown value type");
				return FALSE;
		}
	}else{
		switch(any->asnType) {
			case ASN_INTEGER:	//Indicates a 32-bit signed integer variable.  					strbuf.Format("%d", any->asnValue.number);
				break;
			case ASN_UNSIGNED32:	
//Indicates a 32-bit unsigned integer variable.  
				strbuf.Format("%u", any->asnValue.unsigned32);
				break;
			case ASN_COUNTER64:	//Indicates a counter variable that increases 
								//until it reaches a maximum value of (2^64) – 1.  
				strbuf.Format("%I64u",any->asnValue.counter64);
				break;
			case ASN_OCTETSTRING://Indicates an octet string variable. 
				strbuf.Format("%s",any->asnValue.string.stream);			
				break;
			case ASN_BITS:	
//Indicates a variable that is an enumeration of named bits.  
				strbuf = dumpOctStr((void*)any->asnValue.bits.stream,
					any->asnValue.bits.length);
				break;
			case ASN_OBJECTIDENTIFIER:	
//Indicates an object identifier variable.  
				SnmpMgrOidToStr(&(any->asnValue.object), &string);
				strbuf.Format("%s",string);
				if(string){
					SNMP_free(string);
				}
				break;
			case ASN_SEQUENCE://Indicates an ASN sequence variable.
				strbuf.Format("%s",any->asnValue.sequence.stream);
				break;
			case ASN_IPADDRESS:	//Indicates an IP address variable. 
				strbuf = ul2addr(*((ULONG*)(any->asnValue.address.stream)));
				break;
			case ASN_COUNTER32:	//Indicates a counter variable. 
				strbuf.Format("%u",any->asnValue.counter);
				break;
			case ASN_GAUGE32:	//Indicates a gauge variable. 
				strbuf.Format("%u",any->asnValue.gauge);
				break;
			case ASN_TIMETICKS:	//Indicates a timeticks variable. 
				strbuf.Format("%u",any->asnValue.ticks);
				break;
			case ASN_OPAQUE:		//Indicates an opaque variable. 
				//treated as octstring
				strbuf = dumpOctStr((void*)any->asnValue.arbitrary.stream,
					any->asnValue.arbitrary.length);
				break;
			default:
				strbuf = _T("unknown value type");
				return FALSE;
		}
	}
	
	return TRUE;
}

UINT SnmpTrap(LPVOID param)
{
    // Trap handling can be done two different ways: event driven or
    // polled.  The following code illustrates the steps to use event
    // driven trap reception in a management application.
	CString errMsg;
    HANDLE hNewTraps = NULL;

    if (!SnmpMgrTrapListen(&hNewTraps)){
        errMsg.Format("error on SnmpMgrTrapListen %d\n", GetLastError());
	}else{
        errMsg.Format("TrapServer Started");
	}
	AfxMessageBox(errMsg);

    while(TRUE){
        DWORD dwResult;
        if ((dwResult = WaitForSingleObject(hNewTraps, 0xffffffff))== 0xffffffff){
            errMsg.Format("error on WaitForSingleObject %d\n",GetLastError());
			AfxMessageBox(errMsg);
			return 0;
		}else if (!ResetEvent(hNewTraps)){
            errMsg.Format("error on ResetEvent %d\n", GetLastError());
			AfxMessageBox(errMsg);
			return 0;
		}else{
            AsnObjectIdentifier enterprise;
            AsnNetworkAddress   IPAddress;
            AsnInteger          genericTrap;
            AsnInteger          specificTrap;
            AsnTimeticks        timeStamp;
            RFC1157VarBindList  variableBindings;

            UINT i;
            char *string = NULL;

            while(SnmpMgrGetTrap(&enterprise, &IPAddress, &genericTrap,
                  &specificTrap, &timeStamp, &variableBindings)){                
                errMsg.Format("Trap Server : generic=%d specific=%d\n",
                       genericTrap, specificTrap);
				AfxMessageBox(errMsg);
                if (IPAddress.length == 4) {
                    errMsg.Format("  from -> %d.%d.%d.%d\n",
                         (int)IPAddress.stream[0], (int)IPAddress.stream[1],
                         (int)IPAddress.stream[2], (int)IPAddress.stream[3]);
					AfxMessageBox(errMsg);

                }
                if (IPAddress.dynamic) {
                    SNMP_free(IPAddress.stream);
                }
                for(i=0; i < variableBindings.len; i++){
                    SnmpMgrOidToStr(&variableBindings.list[i].name, &string);
                    errMsg.Format("Variable = %s\n", string);
					AfxMessageBox(errMsg);
                    if (string){
						SNMP_free(string);
					}
	                //SnmpUtilPrintAsnAny(&variableBindings.list[i].value);					
					AsnPrint(&variableBindings.list[i].value,errMsg);
					AfxMessageBox("Value = "+errMsg);
                } // end for()
                SnmpUtilOidFree(&enterprise);
                SnmpUtilVarBindListFree(&variableBindings);
            }
        }
    } // end while()
	return 1;
}

BOOL CMonitorDlg::DestroyWindow() 
{
	// TODO: Add your specialized code here and/or call the base class
	if (pThread) {
		pThread->ExitInstance();
	}
	if (pThread2) {
		pThread2->ExitInstance();
	}
	return CDialog::DestroyWindow();
}

void CMonitorDlg::OnDblclkMibTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	// TODO: Add your control notification handler code here
	CString		text, oid;
	HTREEITEM selected,root;
	UINT		ID;
	BOOL		Dot = FALSE;
	
	root = m_CtrlMibTree.GetRootItem();
	selected = m_CtrlMibTree.GetSelectedItem();
	m_strOid = _T("");
	while (root!=selected) {
		text = m_CtrlMibTree.GetItemText(selected);
		sscanf(text.GetBuffer(0),"%u",&ID);
		oid.Format("%u.",ID);
		m_strOid = oid + m_strOid;
		selected = m_CtrlMibTree.GetParentItem(selected);
	}
	m_strOid += "0";
	UpdateData(FALSE);
	*pResult = 0;
}

void CMonitorDlg::OnClickMibTree(NMHDR* pNMHDR, LRESULT* pResult) 
{
	// TODO: Add your control notification handler code here
	CString		text, oid;
	HTREEITEM	selected,root;
	UINT		ID;
	BOOL		Dot = FALSE;
	
	root = m_CtrlMibTree.GetRootItem();
	selected = m_CtrlMibTree.GetSelectedItem();
	m_strOid = _T("");
	while (root!=selected) {
		text = m_CtrlMibTree.GetItemText(selected);
		sscanf(text.GetBuffer(0),"%u",&ID);
		oid.Format("%u.",ID);		
		m_strOid = oid + m_strOid;
		
		selected = m_CtrlMibTree.GetParentItem(selected);
	}
	m_strOid += "0";
	UpdateData(FALSE);

	*pResult = 0;
}

void CMonitorDlg::OnStatStart() 
{
	// TODO: Add your control notification handler code here
	CButton* pb = (CButton*)GetDlgItem(IDC_StatStart);
	ASSERT(pb);

	if (pb->GetCheck()) {
		if(!m_CtrlInterface.GetCount()){
			m_strOid = _T("2");
			UpdateData(FALSE);
			((CButton*)GetDlgItem(IDC_SnmpWalk))->SetCheck(1);
			m_nRadioIndex = WALK;
			((CButton*)GetDlgItem(IDC_AddOid))->EnableWindow(TRUE);
			OnExec();
			AfxMessageBox("请先在上面的列表中选择要统计的OID");
			pb->SetCheck(0);
			return;
		}
		pb->SetCheck(0);/*修复了点击保存后,选项仍是选中的Bug*/
		//m_CtrlInterface.InsertString(0,"system.sysUpTime.0");
		PostMessage(USER_START_STAT);
	}else{
		PostMessage(USER_STOP_STAT);
		m_CtrlInterface.ResetContent();
		((CButton*)GetDlgItem(IDC_AddOid))->EnableWindow(FALSE);
	}
}

UINT SnmpStat(LPVOID param)
{
	int nitem = 0;
	UINT				interval = 5;
	CString             agent;
	CString             community;
	CString				strOid;
	RFC1157VarBindList variableBindings;
	LPSNMP_MGR_SESSION	session;
	AsnObjectIdentifier reqObject;
	INT        timeout = TIMEOUT;
	INT        retries = RETRIES;
	BYTE       requestType;
	AsnInteger errorStatus;
	AsnInteger errorIndex;
	char        *chkPtr = NULL;
	CString		errMsg;
	CString		strRel = _T("");
	CFile		file;
	CFileDialog fdlg(FALSE);
	
	if (fdlg.DoModal()!=IDOK ) {
		AfxMessageBox("operation canceled");
		pThread2 = NULL;
		((CButton*)pDlg->GetDlgItem(IDC_StatStart))->SetCheck(0);
		AfxEndThread(EXIT_SUCCESS);
	}
	
	pDlg->m_CtrlAgentIP.GetWindowText(agent);
	community = pDlg->m_strAgentComm;
	requestType = ASN_RFC1157_GETREQUEST;
	
	pDlg->SetDlgItemText(IDC_Status,"Statistics Thread Started");
	while (TRUE) {
		//pDlg->m_CtrlInterface.ResetContent();
		strRel = _T("");
		//start snmp session
		if((session=SnmpMgrOpen(agent.GetBuffer(0),
community.GetBuffer(0),timeout,retries))==NULL){
			errMsg.Format("Error: On SnmpMgrOpen [%d] in SnmpStat()",
GetLastError());
			AfxMessageBox(errMsg);
			pThread2 = NULL;
			AfxEndThread(EXIT_SUCCESS);
		}
		
		nitem = pDlg->m_CtrlInterface.GetCount();
		if (nitem<1) {
			errMsg.Format("nitem = %d",nitem);
			AfxMessageBox(errMsg);
		}
		for (int i=0; i<nitem; i++) {
			pDlg->m_CtrlInterface.GetText(i,strOid);
			strRel += strOid + "\n";
			if(!SnmpMgrStrToOid(strOid.GetBuffer(0), &reqObject)){
				AfxMessageBox("Unknown Object Identifier : SnmpStat()");
				break;
			}else{
			// Since sucessfull, add to the variable bindings list.
				variableBindings.len = 1;
				variableBindings.list = NULL;
				if ((variableBindings.list = (RFC1157VarBind *)SNMP_realloc(
					variableBindings.list,sizeof(RFC1157VarBind))) == NULL){
					AfxMessageBox("Error: Error allocating oid "+strOid);
					break;
				}
				variableBindings.list[0].name = reqObject; 
				variableBindings.list[0].value.asnType = ASN_NULL;
			}
			
			if (!SnmpMgrRequest(session, requestType, 
				&variableBindings,&errorStatus, &errorIndex)){
				// The API is indicating an error.
				errMsg.Format("error on SnmpMgrRequest %d in SnmpStat()", 
								GetLastError());
				AfxMessageBox(errMsg);
				break;
			}else{
			// The API succeeded, errors may be indicated from the remote agent.
				if (errorStatus > 0){
					errMsg.Format("Error: errorStatus=%d, errorIndex=%d\n",
errorStatus, errorIndex);
					AfxMessageBox(errMsg);
				}else{
					// Display the resulting variable bindings.
					AsnPrint(&variableBindings.list[0].value,errMsg,FALSE);
					if 
				(variableBindings.list[0].value.asnType==ASN_OCTETSTRING ||
					variableBindings.list[0].value.asnType==ASN_SEQUENCE) {
						errMsg.Replace('\n','_');
						errMsg.Replace('\t','_');
						errMsg.Replace(' ', '_');
					}					
					strRel += errMsg + "\n";
				}
			}
			SnmpUtilVarBindListFree(&variableBindings);
		}//end of for		
		SnmpMgrClose(session);		
		file.Open(fdlg.GetPathName(),
CFile::shareDenyWrite | CFile::modeCreate | CFile::modeWrite);
		file.Write(strRel.GetBuffer(0),(UINT)strRel.GetLength());
		file.Close();
		//AfxMessageBox(strRel);
		interval = pDlg->GetDlgItemInt(IDC_IntervalTime);
		interval = (interval>0 && interval<=60)?interval:5;
		Sleep(60000*interval);
	}
	
	return 0;
}

void CMonitorDlg::OnRclickCtrlList(NMHDR* pNMHDR, LRESULT* pResult) 
{
	// TODO: Add your control notification handler code here
	
	*pResult = 0;
}

/*选中列表的操作*/
void CMonitorDlg::OnAddOid() 
{
	// TODO: Add your control notification handler code here
	CString		str;
	UINT	index = m_CtrlInterface.GetCurSel();

	GetDlgItemText(IDC_AddOid,str);
	if (str=="添加OID") {
		str = m_CtrlList.GetItemText(nItem,0);
		if (m_CtrlInterface.FindString(-1,str)==LB_ERR) {
			if(index<0){
				m_CtrlInterface.AddString(str);
			}else{
				m_CtrlInterface.InsertString(index+1,str);
			}
			
		}else{
			AfxMessageBox("已经添加过了");
		}
	}else{
		if (m_CtrlInterface.GetCurSel()<0) {
			AfxMess Box("请在下面表中先选中要删除的OID");
		}else{
age
			m_CtrlInterface.DeleteString(m_CtrlInterface.GetCurSel());
		}
	}
}

/*被选择列表操作*/
void CMonitorDlg::OnClickCtrlList(NMHDR* pNMHDR, LRESULT* pResult) 
{
	// TODO: Add your control notification handler code here
	NMLISTVIEW* plv = (NMLISTVIEW*)pNMHDR;
	CString errMsg;

	nItem = plv->iItem;
	nSubItem = plv->iSubItem;
	if (nItem<0) {
		((CButton*)GetDlgItem(IDC_AddOid))->EnableWindow(FALSE);
		return;
	}
	((CButton*)GetDlgItem(IDC_AddOid))->EnableWindow(TRUE);
	SetDlgItemText(IDC_AddOid,"添加OID");
	*pResult = 0;
}

/*当选择被选择列表中的OID时,更改Button显示的标题*/
void CMonitorDlg::OnSetfocusInterface() 
{
	// TODO: Add your control notification handler code here
	SetDlgItemText(IDC_AddOid,"删除OID");
}

(5) 显示模块(Monitor),主要作用于程序图标的显示,如图23所示。代码分析如下。


图23显示模块(Monitor)的主要操作对象

// Monitor.cpp : Defines the class behaviors for the application.
//

#include "stdafx.h"
#include "Monitor.h"
#include "MonitorDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif

/
// CMonitorApp

BEGIN_MESSAGE_MAP(CMonitorApp, CWinApp)
	//{{AFX_MSG_MAP(CMonitorApp)
		// NOTE - the ClassWizard will add and remove mapping macros here.
		// DO NOT EDIT what you see in these blocks of generated code!
	//}}AFX_MSG
	ON_COMMAND(ID_HELP, CWinApp::OnHelp)
END_MESSAGE_MAP()

/
// CMonitorApp construction

CMonitorApp::CMonitorApp()
{
	// TODO: add construction code here,
	isRoot = TRUE;
	// Place all significant initialization in InitInstance
}

/
// The one and only CMonitorApp object

CMonitorApp theApp;

/
// CMonitorApp initialization

BOOL CMonitorApp::InitInstance()
{
	AfxEnableControlContainer();

	// Standard initialization
	// If you are not using these features and wish to reduce the size
	//  of your final executable, you should remove from the following
	//  the specific initialization routines you do not need.

#ifdef _AFXDLL
	Enable3dControls();			// Call this when using MFC in a shared DLL
#else
	Enable3dControlsStatic();	// Call this when linking to MFC statically
#endif

	CMonitorDlg dlg;
	m_pMainWnd = &dlg;
	int nResponse = dlg.DoModal();
	if (nResponse == IDOK)
	{
		// TODO: Place code here to handle when the dialog is
		//弹出确定对话框
	}
	else if (nResponse == IDCANCEL)
	{
		// TODO: Place code here to handle when the dialog is
		//弹出取消对话框
	}
	// Since the dialog has been closed, return FALSE so that we exit the
	//  application, rather than start the application's message pump.
	return FALSE;
}

四、软件应用

1、对软件应用的环境进行假设和分析

         SNMP提供了一种从运行网络管理软件的中央计算器来管理网络主机(如支持SNMP协议工作站或服务器计算机、路由器、网桥和集线器,甚至是现在的智能手机)的方法。所以,只要被管对象支持并开启了SNMP服务,就可以通过网络管理工作站(NMS)获得被管对象的信息,甚至是设置被管对象的某些信息。

         因为网络管理对于审核和资源管理来说都非常重要,所以SNMP可以用于:配置远程设备、监视网络性能、检测网络故障或不适当的访问以及审核网络使用。

2、对应用过程进行图示说明

(1) 配置远程设备。可以将配置信息从管理系统发送到每台网络主机。

        实现该功能,需要用到MIB Waklk中的Get功能和Set功能。以修改MIB_Ⅱ包中ip下的ipDefaultTTL为例。首先,选中ipDefaultTTL或,选中“Get”操作,点击“执行”。在OID信息框中,可以看到ipDefaultTTL的类型为ASN_INTEGER32。选中“Set”操作,点击“执行”,输入64,点击“OK”。当我们再次执行“Get”操作时,可以发现ipDefaultTTL的值已经改为64。操作流程如图8所示。

(2) 检测网络性能。可以跟踪处理速度和网络吞吐量,并收集关于数据传输成功的信息。

        实现该功能,主要使用到了MIB Walk中的Get、GetNext或Walk功能。查看速度和网络吞吐量主要需要查看一下节点的信息:

interface.ifTable.ifEntity
	ifInOctes:接收字节数
	ifOutOctes:输出字节数
	ifInUcastPkts:接收单播数据包数
	ifOutUcastPkts:输出单播数据包数
	ifInNUcastPkts:接收非单播数据包数
	ifOutNUcastPkts:输出非单播数据包数
ifInDiscards:接收时丢弃的数据包数
ifOutDiscards:输出时丢弃的数据包数
ifInErrors:接收时错误的数据包数
ifOutErrors:输入时错误的数据包数
ifInUnknownProtos:接收的未知协议数据包数
	ifOutQLen:输出队列中的所有包数
ip
	ipForwarding:设备是否转发数据包1是, 2否
	ipInreceives:接收的IP包数量
	ipInHdrErrors:接收头出错的包数量
	ipInAddrErrors:接收地址出错的包数量
	ipForwDatagrams:转发的IP包数量
	ipInUnknownProtos:未知协议
	ipInDiscards:丢弃的包数量
	ipInDelivers:交给设备高层处理的包数量
	ipOutRequests:设备高层发出的包数量
	ipOutNoRoutes:无法找到路由丢弃的包

总接收数据包 = ifInUcastPkts + ifInNNucastPkts
输入错误百分比= ifInErrors/TotalInputPackts
输出丢弃百分比= ifInDiscards/TotalInputPackts
IP输入错误百分比=(Disards+HdrErrors+AddrErros)/Inreceives
吞吐量=ifInOctets+ifOutOctets

         如图24所示,打开左边MIB_Ⅱ子树,依次点击“2interface”、“ifTable”、“ifEntity”,双击“ifInOctes”,点击“Walk”操作,最后点击“执行”,获得接收的字节数。这样,如果再查看“ifOutOctes”,获得输入的字节数,将两者数据相加,就可以获得被管对象的吞吐量。


图24 查看被管对象接收字节数

(3) 检测网络故障或不适当的访问。可以配置当某些时间发生时出发网络设备上的警报。在触发警报是,设备将把事件消息转发给管理系统。

         要实现该功能,需要在开始系统的SNMP服务的基础上开启SNMPTrap服务,同时还需要开通UDP协议下的161和162端口。打开MIBWalk开始监听功能,这样被管理设备中的代理就可以在任何时候向网络管理工作站(这里是MIBWalk)报告错误情况,例如预制定阈值越界程度等等。代理并不需要等到管理工作站为获得这些错误情况而轮询他的时候才会报告,详细使用过程请见第二部分“软件的编译(安装)及运行过程”中第6小节的“Trap功能”。

(4) 审核网络使用。可以监视网络的总体使用情况以便识别用户或组的访问,以及网络设备和服务的使用类型。

        要实现该功能,仍需用到SNMP协议中的Trap功能。只要开启了MIBWalk的监听功能,就能够收到来自代理(Agent)发送来的陷阱信息。陷阱的类型有很多种,其中有一种特殊陷阱(SpecificTrap),由代理端的逻辑决定,也就是可以通过管理者编写代码决定。例如,可以在代理端编写一段代码,当有非法用户试图登录被管设备时,就向网络管理工作站发送一条陷阱信息。通过自定义编码,就可以实现审核网络使用的功能。

        为了模拟该功能,我使用了Net-SNMP软件下的SNMPTrap.exe模拟代理发送一条有非法用户登录的陷阱信息给网络管理工作站。代码如下:

Snmptrap -v1 -c public 
10.0.2.15 .1.3.6.1.4.1.311.1.1.3.1.1 
127.0.0.1 6 10 100 1.3.6.1.4.1.311.1.1.3.1.2 i 12 
.1.3.6.1.4.1.311.1.1.3.1.2 
s 非法用户“BankOffChina”登录

        产生的结果如下图25和图26所示。


(1) 陷阱服务器提示,通用ID和专用ID


(2) 代理的地址:127.0.0.1


(3)通用ID和专用ID的陷阱信息类型的OID


(4) 通用ID的陷阱信息提示


(5) 专用ID的陷阱信息提示

图25  测试审核网络使用功能

3、对应用结果的成功或是失败进行深入分析

        本次的实验的应用结果,基本上是成功的。不过,还是有很多的地方与网上的参考资料不相同。之所以结果不同,原因有很多,我个人觉得,这与实验使用的平台不无关系。网上的参考资料有些是基于Linux甚至是路由器操作演示的。而基于MFC架构的MIBWalk显然只能基于Windows平台使用。最明显的区别在于使用Set功能时,很多可以读写的对象都不能修改。在MIB_Ⅱ子树中26个可读的节点中,至今,发现能够的修改的对象只有IP下的IPDefaultTTL。

        而且,在MIB Walk中的MIB_Ⅱ中的节点并不完全正确,例如Interface下的ifInOctes使用两个节点的,但MIBWalk中确没有显示。另外一方面,IP下的ipForwardTable虽然显示了节点,但实际上是没有那些节点的。这可能跟电脑的SNMP版本有关。

五、实验过程中遇到的问题及解决方法

(1) Set功能中,对应的数据类型错位,如图27所示的下拉框。

        导致这个问题出现的原因是程序中的一个Switch语句的错位。解决方案是在MonitorDlg.cpp中修改一段代码,如下所示,具体代码请见代码分析的(4)部分。

		switch (m_DlgInput.m_nTypeIndex)	{
		case 0: //ASN_BITS
			variableBindings.list[0].value.asnType = ASN_BITS;
			AfxMessageBox("ASN_BITS not supported yet");
			return FALSE;
			break;
……
default:
			AfxMessageBox("unknown ASN value type");
			return FALSE;
		}

(2)保存数据后,选中框仍然被选中。

        导致这个原因是在源代码中,缺少了执行保存后,没有取消选中状态。修改MonitorDlg.cpp中的代码,如下所示。

	……
pb->SetCheck(0);
			return;
		}
		pb->SetCheck(0);/*修复了点击保存后,选项仍是选中的Bug*/
		//m_CtrlInterface.InsertString(0,"system.sysUpTime.0");
		PostMessage(USER_START_STAT);
	}else{
……

(3) MIB Walk没有主动发送Trap的功能。

        由于MIB Walk是第三方的SNMP管理软件,对网络中的设备进行管理。因此不能模拟代理主动发送陷阱信息,因此需要另外的SNMP软件来实现这以功能。

        可以实现该功能的软件有SNMPUtil和NET-SNMP,安装在被管对象中,在命令提示符界面键入指令,即可实现主动发送陷阱信息的功能。具体实现过程请见,“四、软件应用”,“2、对应用过程进行图示说明”中的第(4)部分。

六、实验的体会及评价

        通过本次的实验,是一个非常痛苦并快乐着的过程,完成这一过程足足用了我8天的时间。个人觉得,还是了解到了一些SNMP的基本知识和基于SNMP功能的实现和使用。

        修改代码和测试代码的确是一个非常漫长和痛苦的过程,另外一方面,也是我学习MFC架构下的C++一个好机会,通过这次的实验,让我对C++有了进一步的认识,学习到了很多关于图形界面的空间和关于SNMP的属性和方法。

        修改了程序中几个比较明显的错误之后,剩下的时间主要就是功能的实现和报告的撰写了。对于功能的实现上,的确花费了不少的时间,主要的原因在于网上给出基于Windows下的SNMP功能实现、使用的介绍过于重复,精品太少了。而自己捣弄却不知如何做起。幸好,最后还是在长时间的搜索下找到了答案。

        总体来说,感谢这次实验,让我有了对网络管理的概念由抽象到具象的转变。原先模糊的概念,终于变成了一些可以看得见,甚至是可以操纵的数据。

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
SNMP的入门程序MIBWalk运行代码分析 一、实验目的 1. 通过对MibWalk代码的编译和运行,了解使用软件进行网络运作监控的实际表现,从而获得相对应理论的感性认识。 2. 通过对软件的应用(进行SNMP的Get、GetNext、Set等简单操作),进一步理解基于SNMP协议的网络运作监控软件的工作原理及系统组成架构。 3. 通过对MIBWalk软件的源代码分析,深入理解基于SNMP协议的软件程序编写。 二、实验内容及要求 1. 实验内容  编译并成功运行代码MIBWalk,观察、测试软件并与课堂演示的软件MIBBrowser对比;  使用预先准备的数据进行输入输出的测试,并对测试数据结果进行总结;  对MIBWalk进行源代码分析;  将综合结果写出实验报告。 2. 实验要求  设立实验环境、对软件进行编译、运行;  以截图形式表现实验过程;  对截图表现的各项结果进行分析总结;  对软件进行源代码分析;  提交实验报告。 三、提示与讲解 1. 实验支持条件 首先,是运行的系统平台,其次是编译运行环境,这些都是软件运行实验的必要条件;其次,软件要得到正确运行、并得到预期的结果,必须要得到软件的运作对象等方面的支持,由于是监控软件,因此监控对象的允许和支持极为重要。 2.源代码分析环境 可以从界面分析入手,找到对应的代码,再循相应的途径进一步深入分析
YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明 YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明YOLO高分设计资源源码,详情请查看资源内容中使用说明

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值