金山卫士开源软件之旅(八) netmon下netmon工程的解析---netmon.exe的启动过程分析

 

转载请标明是引用于 http://blog.csdn.net/chenyujing1234

 

_tWInMain主要做安装,实例判断,初始化COM环境,开始界面显示.

int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPTSTR lpstrCmdLine, int nCmdShow)
{
	// 获得安装路径
	// 采用MD5方法将软id和硬id合并为mid
	// 从dump.dll中的接口KxEOpenDumpMonitorEx2来注册安装此mid
	KDump::Instance().Install();

	if (S_FALSE == _Module.Init(hInstance))
		return -1;

	KCheckInstance* _pInst = KCheckInstance::Instance();
	if (_pInst == NULL)
		return 0;
	//  检查是不是第一个实例,并处理多实例
	int nRetv = _pInst->CheckFirstInstance(lpstrCmdLine/*, NULL, _T("Kingsoft Antivirus KSG Update Mutex")*/);
	if (!nRetv)
	{
		_pInst->ClearFirstInstance();
		return 0;
	}
	
	// 启动的时候,自动起托盘
// 	WCHAR bufPath[MAX_PATH] = {0};
// 	::GetModuleFileName(NULL, bufPath, MAX_PATH);
// 	::PathRemoveFileSpecW(bufPath);
// 	::PathAppend(bufPath, TEXT("KSafeTray.exe"));
// 	::ShellExecute(NULL, NULL, bufPath, NULL, NULL, SW_HIDE);

	::CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
	// 加载皮肤、类型、串,加载RICHED20.dll, 查看KSafeSvc服务是否在运行, 创建各自的共享内存
	_Module.Run();
	::CoUninitialize();

	_Module.Uninit();

	return 0;
}


在_Module.Run主要做加载皮肤、类型、串,加载RICHED20.dll, 查看KSafeSvc服务是否在运行, 创建各自的共享内存

// 加载皮肤、类型、串,加载RICHED20.dll, 查看KSafeSvc服务是否在运行, 创建各自的共享内存
HRESULT KAppModule::Run()
{
	// ======================Small tip==================
	// 设置资源路径、加载皮肤、类型、串
	_InitUIResource();

	if ( m_hModRichEdit2 == NULL )
		m_hModRichEdit2 = ::LoadLibrary(_T("RICHED20.DLL"));
/*
#if _DEBUG
	if (FALSE)
#else
	if (_CheckIs64BitOp())
#endif
	{
		//如果是64位操作系统,就退出。
		CBkSafeMsgBox2 dlg;
		dlg.ShowMutlLineMsg(BkString::Get(DefString33), BkString::Get(DefString7), MB_OK | MB_ICONEXCLAMATION);
		return S_OK;
	}*/

	// 由于类KMainDlg里有	KFlowStat m_FlowStatLog;
	// KFlowStat的构造函数中会加载数据库、打开数据库
	KMainDlg dlgMain;
	// ======================Small tip==================
	// 通过OpenSCManager、OpenService、QueryServiceStatus来查看KSafeSvc服务是否在运行
	if (!dlgMain.CheckServericeIsOK())
	{
		netmon_log(L"CheckServiceIsOK Failed!");
	}
	// KUrlMonCfgReader类与KNetFluxCacheReaderod类的Init,创建各自的共享内存
	if (!dlgMain.CheckDriver())
	{
		netmon_log(L"CheckDriver failed.Netmon exit.");
	}
	else
	{
		netmon_log(L"Netmon dlg start ");
		int uRet = dlgMain.DoModal(NULL);// 窗口显示
	}
	
	return S_OK;
}

 

在代码的注释中讲到类KFlowStat的构造函数做了数据库的初始化.

这里先不对数据库展开说,以后会专门讲到金山中的数据库.

KFlowStat::KFlowStat()
{
 /*HRESULT hr = BKDbCreateObject(__uuidof(ISQLiteComDatabase3), (void**)&m_spiDB);
 if (!SUCCEEDED(hr))
 return;

 int nCmdLen = MAX_PATH * 2;
 wchar_t szFilePath[MAX_PATH * 2] = {0};
 DWORD dwCode = ::GetModuleFileName(NULL, szFilePath, nCmdLen);
 if (dwCode == 0)a
 return;

 ::PathRemoveFileSpecW(szFilePath);
 ::PathAppend(szFilePath, DEF_SQLITE_FILE);

 hr = m_spiDB->Open(szFilePath);
 if (!SUCCEEDED(hr))
 return;
 */

 HRESULT hr = S_OK;

 // chenyujing1234@163.com 2012-3-29   14:23
 m_piWlDB = NULL;
 // ======================Small tip==================
 // 从注册表中读取LeiDian的APP路径、LeidianLog路径
 hr = CAppPath::Instance().GetLeidianAppPath( m_modpath.m_strPath );
 if ( FAILED( hr ) )
  goto Exit0;


 hr = CAppPath::Instance().GetLeidianLogPath( m_dbpath.m_strPath );
 if ( FAILED( hr ) )
  goto Exit0;

 m_dbpath.Append( DEF_SQLITE_FILE );

 m_modpath.Append( BKMOD_NAME_BKDB );
/* {
  int nCmdLen = MAX_PATH * 2;
  wchar_t szFilePath[MAX_PATH * 2] = {0};
  DWORD dwCode = ::GetModuleFileName(NULL, szFilePath, nCmdLen);
  ::PathRemoveFileSpecW(szFilePath);
  ::PathAppend(szFilePath, DEF_SQLITE_FILE);
  m_dbpath.m_strPath = szFilePath;
 }
*/ 

    // ======================Small tip==================
    // 这里会提示“没有找到MSVCR80.dll”   m_modpath为c:\program files\ksafe\ksafedb.dll,加载失败
 hr = m_dbmod.LoadLib( m_modpath );
 if ( FAILED( hr ) )
  goto Exit0;

 hr = m_dbmod.BKDbCreateObject( 
  __uuidof( Skylark::ISQLiteComDatabase3 ), 
  ( void** )&m_piWlDB 
  );
 if ( FAILED( hr ) )
  goto Exit0;

 hr = m_piWlDB->Open( m_dbpath );
 if ( FAILED( hr ) )
 {
  ::SetFileAttributes( m_dbpath, 0 );
  ::DeleteFile( m_dbpath );
  hr = m_piWlDB->Open( m_dbpath );
 }

 if ( FAILED( hr ) )
  goto Exit1;

 goto Exit0;

Exit1:
 if ( m_piWlDB )
 {
  m_piWlDB->Release();
  m_piWlDB = NULL;
 }

Exit0:
 return ;
}


既然dlgMain.DoModal(NULL);了,那么接下来就是OnInitDialog了。

它主要做

1、 KOperMemFile创建共享文件,并存入标志,设备提醒信息,日期
2、 KCheckInstance 把容器句柄等信息写到KCheckInstance映射的内存里
3、 KNetFluxCacheReader  根据映射内在中的信息获取系统和当前进程的流量信息
4、 KFlowTray 确保KSafeTray.exe已经运行
5、 本次和上次的日期信息判断是否需要显示出来
6、 创建CBkNetMonitorListBox,并从listbox_template_netmonitor.xml初始化ListBox,把进程流程信息加到列表里
7、并启动定时器
(定时器时的处理内容下节讲到)

    定时器的类别有:

     (1)更新状态信息
     (2)  更新网络流量信息(间隔:1000)
     (3)更新流量窗口状态(间隔:500)
     (4)检查是否退出了(间隔:500)

BOOL KMainDlg::OnInitDialog( CWindow /*wndFocus*/, LPARAM /*lInitParam*/ )
{
	if (KOperMemFile::Instance().Init() == S_OK) // 创建共享文件
	{
		// ======================Small tip==================
		// 在映射的MemShareFlowElem内存中存数据 1
		KOperMemFile::Instance().SetWaitMoniterOpen(1);	
		// ======================Small tip==================
		// 设置这次与上次提醒的信息:日期
		_InitFlowRemindInfo();
	}

	SetIcon(::LoadIcon((HMODULE)&__ImageBase, MAKEINTRESOURCE(IDI_BEIKESAFE)));
	SetIcon(::LoadIcon((HMODULE)&__ImageBase, MAKEINTRESOURCE(IDI_SMALL)), FALSE);

	m_enumProcessMode = enumProcessModeHasNetFlow;

	// ======================Small tip==================
	// 把容器句柄等信息写到KCheckInstance映射的内存里
	KCheckInstance::Instance()->CfgFirstInstance(NULL, this->m_hWnd, FALSE);

	KNetFluxCacheReader reader;
	if (SUCCEEDED(reader.Init()))
		//根据映射内在中的信息获取系统和当前进程的流量信息
		reader.GetProcessesFluxInfo(m_FluxSys, &m_processInfoList, m_enumProcessMode);
//	KNetFluxCacheReader::Instance().GetSystemNetFlow(m_FluxSys);
	_DisableDelayScan();

	// 	//加入任务栏右键最大化置灰
	// 	LONG lSys = GetWindowLong(GWL_STYLE);
	// 	lSys &= ~(WS_MAXIMIZEBOX);
	// 	SetWindowLong(GWL_STYLE, lSys);

	KFlowTray prot_shell(TRUE);
	// ======================Small tip==================
	// 确保KSafeTray.exe已经运行
	prot_shell.ShellTray();

	//	CListBoxData::GetDataPtr()->SetProviderFunc(NET_MONITOR_219, &KMainDlg::_UpdateNetFlowSummaryWnd);

	// ======================Small tip==================
	// 本次和上次的日期信息判断是否需要显示出来
	_ShowNetMintorRemindDlg(TRUE);

	// ======================Small tip==================
	// 创建CBkNetMonitorListBox,并从listbox_template_netmonitor.xml初始化ListBox
	// 把进程流程信息加到列表里
	_InitNetMonitorListBox();
	// 初始化状态列表,从数据库中获得信息和状态填充列表
	// 为列表中的项显示当前页面
	// 通过KNetFluxStasticCacheReader类获得进程流统计列表,
	_InitStatList();

	// 初始化基本完成,接下来就是开启定时器:
	//		更新状态信息
	//		更新网络流量信息(1000)
	//		更新流量窗口状态(500)
	//		检查是否退出了(500)
	m_uTimer = SetTimer(ID_TIMER_UPDATE_NETFlOW_MON, UPDATE_NETFLOW_MON_INTERVAL, NULL);

	SetTimer(ID_TIMER_REFRESH_FLOATWND_STATUS, 500, NULL);

	//_SetAccessNetCount(0);
	_SetDownAndUpdateSum(0.0, 0.0);
	_SetDownSpeed(0.0);
	_SetUpSpeed(0.0);

	_InitFloatWndSwitch();

	m_hEventExit = ::CreateEvent(
		NULL,
		FALSE,
		FALSE,
		EVENT_NETMON_DLG_EXIT
		);

	m_hEventChangeFlowatWndDisplayStatusText =::CreateEvent(
		NULL,
		FALSE,
		FALSE,
		EVENT_NETMON_DLG_FLOATWND_DISPLAY_STATUS_TEXT
		);


	SetTimer(ID_TIMER_CHECK_EXIT, CHECK_EXIT_INTERVAL);

	_InitNetMonSwitch();

	m_bRptThreadWorking = FALSE;
	m_hEventRptThreadExit = CreateEvent(NULL, TRUE, FALSE, NULL);

	if (m_nCurShowType == enumQueryTypeEveryMonth)
		OnClickShowStatMonths();

	return TRUE;
}


 


初始化过程中把进程信息加到列表中去,这里的列表是继续于框架CBkListBox控制,重载得到的列表.

void KMainDlg::_InitNetMonitorListBox() { m_pNetMonitorListBox = new CBkNetMonitorListBox; ..... }

BOOL KMainDlg::_InitStatList( void )
{
 m_pNetStatListBox = new CBkNetMonitorListBox;

..........}

=========================================================================================

总结:

1、 用到的互斥机制有种:

 (1)采用CLocker

          eg: 在类class KMainDlg中有成员CLocker   m_locker;

           采用关键代码段,相对简单。这里提一下,因为关键代码段采用不让执行代码的方法,当进入嵌套时很容易造成死锁。这在大型框架设备中特别重要。

(2)采用ShareMemLock方式。

 //
// 本锁的特性:
// 当有写的要求时,让旧的读取完成;而新的读取请求被挂起,直到本次写完,即写入优先
// 允许多线程读,只能有一个线程在写

typedef struct _tagShareMemLock
{
	// m_nLock用于保护m_nReadCnt变量 ,使在多线程时不互斥
	volatile LONG			m_nLock;
	volatile LONG			m_nReadCnt; // 读的次数 ,当为0表没有人在读,
	                                    //           当为 -10000表写在进行
	volatile LONG			m_nWrite;   // 写锁

	volatile DWORD			m_nDbgLockProcessID;
	volatile DWORD			m_nDbgLockThreadID;
	volatile DWORD			m_nDbgLockTime;

	void	InitLock()
	{
		m_nLock = 0;
		m_nReadCnt = 0;
		m_nWrite = 0;
	}

	BOOL	TryLockReadCntLock()
	{
		// LONG InterlockedCompareExchange
		// (LPLONG Destination, LONG Exchange, LONG Comperand);
		// 如果第三个参数与第一个参数指向的值相同
		// 那么用第二个参数取代第一个参数指向的值。函数返回值为原始值
		// ============如果m_nLock == 0,那么m_nLock = 1;且返回0。否则返回非0
		return (::InterlockedCompareExchange(&m_nLock, 1, 0) == 0);
	}
  //  让写锁还原 
	BOOL	UnlockReadCntLock()
	{
		m_nLock = 0;
		return TRUE;
	}

	BOOL	TryLockWriteLock()
	{
		return (::InterlockedCompareExchange(&m_nWrite, 1, 0) == 0);
	}

	//  让写锁还原
	BOOL	UnlockWriteLock()
	{
		m_nWrite = 0;
		return TRUE;
	}
	
	// 请求读的锁,判断现在可否读
	// 改变完读次数时马上把读锁释放
	BOOL	TryLockRead()
	{
		// 如果有修改请求,那么优先考虑修改
		if (m_nWrite)
			return FALSE;

		// 如果已经在修改了,那么先等等,锁之外的判断不
		// 准确,但是可以起到加速的作用
		if (m_nReadCnt < 0)
			return FALSE;

		// 开始进行
		if (TryLockReadCntLock())  // 获得锁成功
		{
			if (m_nReadCnt >= 0)
			{
				m_nReadCnt ++;     // 读的次数加1
				UnlockReadCntLock();// 改变完m_nReadCnt后马上把读锁打开 
				return TRUE;
			}
			else                   // 如果小于0,即表示有写请求
			{
				UnlockReadCntLock();// 马上把读锁打开 
				return FALSE;
			}
		}
		return FALSE;
	}
	// 锁住读
	BOOL	LockRead()
	{
		// 如果已经有一个读在进行,那么就等待
		while (!TryLockRead())
			::Sleep(1);
		return TRUE;
	}

	void	UnLockRead()
	{
		while (!TryLockReadCntLock())// 保护m_nReadCtn
			::Sleep(1);
		m_nReadCnt --;
		UnlockReadCntLock();          // 保护m_nReadCtn
	}

	/// 把读锁锁上.

	BOOL	TryLockWrite()
	{
		// 如果有人在读,那么再等等
		// 但是可以起到加速作用
		if (m_nReadCnt > 0)
			return FALSE;

		if (TryLockReadCntLock())
		{
			// 确保没有人读,也没有人写
			if (m_nReadCnt == 0)
			{
				m_nReadCnt = -10000;
				UnlockReadCntLock();
				return TRUE;
			}
			else  // 还有读在进行,那么
			{
				UnlockReadCntLock();
				return FALSE;
			}
		}
		return FALSE;
	}

	// 想锁住写,
	//     前提是:上一次的写已经调用了UnLockWrite(即写锁已经解开)
	// 如果成功,那么把写锁与读锁都锁上
	BOOL	LockWrite()
	{
		// 保证没有其他人在写
		while (!TryLockWriteLock())
			::Sleep(1);

		// 与LockRead不一样,这样如果发现在忙时不退出而是等待,这样就有了优先级
		// 保证其他人没有在读,也没有在写
		while (!TryLockWrite())
			::Sleep(1);
		return TRUE;
	}

	// 写锁解开
	void	UnLockWrite()
	{
		while (!TryLockReadCntLock())
			::Sleep(1);
		m_nReadCnt = 0;
		UnlockReadCntLock();

		UnlockWriteLock();
	}
}ShareMemLock;


 

 

          eg: 示例一:在类KStasticFluxProcessList中有成员ShareMemLock   m_lock;

struct KStasticFluxProcessList
{
	DWORD					m_nSize;
	ShareMemLock			m_lock;
	ULONGLONG				m_nTotalRecv;			// 总计接受的流量
	ULONGLONG				m_nTotalSend;			// 总计发送的流量
	__int64					m_nTimeWatch;			// 监控时间
	__int64					m_nTimeTodayStart;		// 今天截止时间点
	__int64					m_nTimeTodayLastTime;	// 今天截止时间点

	DWORD					m_nMaxCnt;
	DWORD					m_nCurrentCnt;
	DWORD					m_nReserved[100];
	KFluxStasticProcItem	m_Items[1];
};


在上面OnInitDialog( 讲到BOOL KMainDlg::_InitStatList( void ),里它会调用_GetAndShowProcessInfo(); 它就是 通过KNetFluxStasticCacheReader类获得进程流统计列表,
_GetAndShowProcessInfo();里就用到了此锁 m_lock来控制访问.

// 初始化状态列表,从数据库中获得信息和状态填充列表
// 为列表中的项显示当前页面
// 通过KNetFluxStasticCacheReader类获得进程流统计列表,
BOOL KMainDlg::_InitStatList( void )
{
	m_pNetStatListBox = new CBkNetMonitorListBox;
	if (NULL == m_pNetStatListBox)
		return FALSE;

	_GetCurLogInfo(enumQueryTypeEveryDay);
	
	// ======================Small tip==================
	// 初始化状态列表,从数据库中获得信息和状态填充列表
	//listbox
	m_pNetStatListBox->Create( GetViewHWND(), TAB_SHOW_STAT_WINDOW);
	m_pNetStatListBox->Load(IDR_BK_LISTBOX_STATINFO);
	m_pNetStatListBox->SetCanGetFocus(FALSE);

	// ======================Small tip==================
	// 为列表中的项显示当前页面
	_ShowPageForList();

//	m_fluxStatRead.Init();

	// ======================Small tip==================
	// 通过KNetFluxStasticCacheReader类获得进程流统计列表,
	_GetAndShowProcessInfo();
	
	_ShowRemindInfo();

	SetTimer(ID_TIMER_UPDATE_STAT_INFO, 30000, NULL);

	PostMessage(WM_TIMER, ID_TIMER_UPDATE_STAT_INFO, 0);

	return TRUE;
}
void KMainDlg::_GetAndShowProcessInfo( void )
{

pFluxStatRead->m_lock.LockRead();

....... //  处理数据

pFluxStatRead->m_lock.UnLockRead();

}



 

 

示例二:在类KProcessFluxList中有成员ShareMemLock   m_lock;

struct KProcessFluxList
{
	DWORD					m_nSize;
	ShareMemLock			m_lock;
	KPFWFLUX				m_SysFlux;
	DWORD					m_nMaxCnt;
	DWORD					m_nCurrentCnt;
	DWORD					m_nProcessPopCount;
	DWORD					m_nReserved[99];
	KProcFluxItem			m_Items[1];
};


在上面OnInitDialog( 讲到BOOL KMainDlg::_InitStatList( void ),里它会调用reader.GetProcessesFluxInfo(m_FluxSys, &m_processInfoList, m_enumProcessMode);

它就是 根据映射内在中的信息获取系统和当前进程的流量信息.

GetProcessesFluxInfo里就用到了_GetProcessesFluxInfo(sysFlux, pProcessesList, nProcessMode);

GetProcessesFluxInfo就是用到了此锁来访问信息。

 

// 获取系统和当前进程的流量信息
 BOOL _GetProcessesFluxInfo(KPFWFLUX& sysFlux, std::vector<KProcFluxItem>* pProcessesList, int nProcessMode = enumProcessModeHasAll)
 {

         // 为保证快速读取,并且释放锁,这里先使用内存拷贝的方法

       pList->m_lock.LockRead();

       .......// 处理数据

      // 解锁

      pList->m_lock.UnLockRead();

}  


 

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值