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