转载请标明是引用于 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; // [email protected] 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(); }