金山卫士开源软件之旅(八) 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();}  


 

原创粉丝点击