金山卫士开源软件之旅(八) netmon下netmon工程的解析---netmon.exe的启动过程分析
来源:互联网 发布:淘宝管控交易风险保障 编辑:程序博客网 时间:2024/05/19 13:09
转载请标明是引用于 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来注册安装此midKDump::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 _DEBUGif (FALSE)#elseif (_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内存中存数据 1KOperMemFile::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 LONGm_nLock;volatile LONGm_nReadCnt; // 读的次数 ,当为0表没有人在读, // 当为 -10000表写在进行volatile LONGm_nWrite; // 写锁volatile DWORDm_nDbgLockProcessID;volatile DWORDm_nDbgLockThreadID;volatile DWORDm_nDbgLockTime;voidInitLock(){m_nLock = 0;m_nReadCnt = 0;m_nWrite = 0;}BOOLTryLockReadCntLock(){// LONG InterlockedCompareExchange// (LPLONG Destination, LONG Exchange, LONG Comperand);// 如果第三个参数与第一个参数指向的值相同// 那么用第二个参数取代第一个参数指向的值。函数返回值为原始值// ============如果m_nLock == 0,那么m_nLock = 1;且返回0。否则返回非0return (::InterlockedCompareExchange(&m_nLock, 1, 0) == 0);} // 让写锁还原 BOOLUnlockReadCntLock(){m_nLock = 0;return TRUE;}BOOLTryLockWriteLock(){return (::InterlockedCompareExchange(&m_nWrite, 1, 0) == 0);}// 让写锁还原BOOLUnlockWriteLock(){m_nWrite = 0;return TRUE;}// 请求读的锁,判断现在可否读// 改变完读次数时马上把读锁释放BOOLTryLockRead(){// 如果有修改请求,那么优先考虑修改if (m_nWrite)return FALSE;// 如果已经在修改了,那么先等等,锁之外的判断不// 准确,但是可以起到加速的作用if (m_nReadCnt < 0)return FALSE;// 开始进行if (TryLockReadCntLock()) // 获得锁成功{if (m_nReadCnt >= 0){m_nReadCnt ++; // 读的次数加1UnlockReadCntLock();// 改变完m_nReadCnt后马上把读锁打开 return TRUE;}else // 如果小于0,即表示有写请求{UnlockReadCntLock();// 马上把读锁打开 return FALSE;}}return FALSE;}// 锁住读BOOLLockRead(){// 如果已经有一个读在进行,那么就等待while (!TryLockRead())::Sleep(1);return TRUE;}voidUnLockRead(){while (!TryLockReadCntLock())// 保护m_nReadCtn::Sleep(1);m_nReadCnt --;UnlockReadCntLock(); // 保护m_nReadCtn}/// 把读锁锁上.BOOLTryLockWrite(){// 如果有人在读,那么再等等// 但是可以起到加速作用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(即写锁已经解开)// 如果成功,那么把写锁与读锁都锁上BOOLLockWrite(){// 保证没有其他人在写while (!TryLockWriteLock())::Sleep(1);// 与LockRead不一样,这样如果发现在忙时不退出而是等待,这样就有了优先级// 保证其他人没有在读,也没有在写while (!TryLockWrite())::Sleep(1);return TRUE;}// 写锁解开voidUnLockWrite(){while (!TryLockReadCntLock())::Sleep(1);m_nReadCnt = 0;UnlockReadCntLock();UnlockWriteLock();}}ShareMemLock;
eg: 示例一:在类KStasticFluxProcessList中有成员ShareMemLock m_lock;
struct KStasticFluxProcessList{DWORDm_nSize;ShareMemLockm_lock;ULONGLONGm_nTotalRecv;// 总计接受的流量ULONGLONGm_nTotalSend;// 总计发送的流量__int64m_nTimeWatch;// 监控时间__int64m_nTimeTodayStart;// 今天截止时间点__int64m_nTimeTodayLastTime;// 今天截止时间点DWORDm_nMaxCnt;DWORDm_nCurrentCnt;DWORDm_nReserved[100];KFluxStasticProcItemm_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==================// 初始化状态列表,从数据库中获得信息和状态填充列表//listboxm_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{DWORDm_nSize;ShareMemLockm_lock;KPFWFLUXm_SysFlux;DWORDm_nMaxCnt;DWORDm_nCurrentCnt;DWORDm_nProcessPopCount;DWORDm_nReserved[99];KProcFluxItemm_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();}
- 金山卫士开源软件之旅(八) netmon下netmon工程的解析---netmon.exe的启动过程分析
- 金山卫士开源软件之旅(八) netmon下netmon工程的解析---netmon.exe的启动过程分析
- 金山卫士开源软件之旅(七) netmon下netmon工程的解析---接口KTdiDriverProxy的实现
- 金山卫士开源软件之旅(三) netmon下FwProxy工程的解析----COM组件的管理模式
- 金山卫士开源软件之旅(三) netmon下FwProxy工程的解析----COM组件的管理模式
- 金山卫士开源软件之旅(四) netmon下FwProxy工程的解析---接口实现及接口使用方法
- netmon
- 金山卫士开源软件之旅(十) KSafeMain工程的分析 1
- 金山卫士开源软件之旅(二) 简单教程:如何创建一个基于金山卫士界面库的工程
- 使用netmon进行网络监控
- NNM的netmon运行小问题,No connection with master agent
- 案例研究:使用 ETW 和 Netmon 解决未知 USB 设备的问题
- c# 利用Log Parser制作简易可视化netmon抓包分析工具
- 金山卫士开源软件之旅(五) XML编码格式导致的运行出错
- 金山卫士开源软件之旅(一) VS 2005环境下编译
- 金山卫士开源软件之旅(一) VS 2005环境下编译
- 用 Netmon 和 NM Decrypt解密 SSL(TLS) 数据包
- 金山卫士开源软件之旅(六) 添加控件CBkEdit与CbkCombo
- Ext布局学习整理
- DBCP,C3P0,Tomcat_JDBC druidDatasource 性能及稳定性测试
- IIS下架设网站账户权限配置
- 算法小题2→素数
- hibernate 类生成表 的实现源码
- 金山卫士开源软件之旅(八) netmon下netmon工程的解析---netmon.exe的启动过程分析
- 数据库之搜索效率及优化
- stream 、 string 、byte[] 间的相互转换 扩展方法
- 认识“数据可视化”和“信息可视化”
- 谷歌管理员工具提示robots文件”?User-agent: *”语法有误解决办法
- strace实现原理
- win7下android开发环境搭建(win7 64位)
- 今天你“导”了吗?
- dialog,activity 屏蔽Home键详解