2.实验室打卡精灵2.0-单实例化、开机启动、托盘操作、程序启动时隐藏主窗口
来源:互联网 发布:文件存入阿里云oss 编辑:程序博客网 时间:2024/05/14 12:53
老规矩,上一张图片
最近对实验室打卡精灵做了一些优化,基本上达到了最初设想的功能。
现在的功能包括:
1.自定义早上、中午、下午、晚上的打卡时间
2.软件单实例化,即如果已经运行了一次再次运行会弹出“应用程序已经在运行”的提示
3.软件可选择开机自动启动
4.软件开始的时候提醒打卡,如果选择了开机自启动可以实现开机时候提示打卡
5.关机的时候提示打卡
在这里记下一些主要和通用功能的实现
1.程序单实例化
所谓程序单实例化就是说一个程序如果已经打开了,那么再次打开时会提醒该程序已经打开或显示已经打开的程序窗口
要实现这样的功能,我们自然想到给程序设置一个标记,第一次打开时设置状态打开,再次打开检测这个标志状态就可以判断当前是否有这个程序的实例在运行。但是这样有两个问题是需要我们解决的:
1.不同的程序的这个标记不能混乱
2.相同程序进程间是共享这个标记的
对于第一点,在VC6中我们采用微软提供的GUIDGEN.EXE工具(在VC6安装目录Microsoft Visual Studio\Common\Tools中)来生成全局唯一的标志,在VS2008中也可采用CRegKey::QueryGUIDValue函数来生成唯一的标志
对于第二点,我们可以使用全局内核对象或全局原子表(Atom)或进程共享单元(Shared SECTION)来实现
这里在VC6 MFC中演示使用全局内核对象,使用GUIDGEN.EXE工具来产生全局标志
代码如下
//定义全局标识号#define GUID_FLAG TEXT("0x70d56d85, 0xbb97, 0x435d, 0x9d, 0x51, 0x85, 0x89, 0x5e, 0x51, 0x3d, 0x1a")CMy20131221_VC6_MFC_App::CMy20131221_VC6_MFC_App(){//初始化内核对象指针m_OneHandle = NULL;}CMy20131221_VC6_MFC_App theApp;BOOL CMy20131221_VC6_MFC_App::InitInstance(){//创建内核对象,标记程序已经打开m_OneHandle = ::CreateMutex(NULL, FALSE, GUID_FLAG);if (ERROR_ALREADY_EXISTS == GetLastError()){::MessageBox(NULL, TEXT("应用程序已经在运行"), TEXT("警告"), MB_OK | MB_ICONWARNING);return FALSE;}#ifdef _AFXDLLEnable3dControls();#elseEnable3dControlsStatic();#endifCMy20131221_VC6_MFC_Dlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){}else if (nResponse == IDCANCEL){}return FALSE;}int CMy20131221_VC6_MFC_App::ExitInstance() {//释放内核对象if (NULL != m_OneHandle){CloseHandle(m_OneHandle);}return CWinApp::ExitInstance();}可以看到单实例化流程为:定义一个全局标志字符串GUID_FLAG,在初始化实例时创建和判断全局内核对象,如果全局对象存在则提示应用程序已经打开,记得在程序退出实例时释放内核对象。
这里在判断程序实例已经打开后弹出一个提示对话框,如果我们想实现判断程序实例已经打开后显示原来窗口怎么办呢。首先我们得想办法在获得判断出实例已经打开后要显示的窗口的句柄,显然以上介绍的几种方法都做不到这一点。以上的方法都是在窗口外设置一个全区标志,这些标志和窗口扯不上关系,当然我们也别想通过他们获得窗口句柄。
下面,我们换一种办法,以窗口自身作为全局标志,这个可能相对上面几种方法难理解一些,代码如下:
1.在Dlg的OnInitDialog函数中加入
SetProp(m_hWnd, g_key, g_value);2.在App中代码如下
//定义全局对象CString g_key = TEXT("0x70d56d85, 0xbb97, 0x435d, 0x9d, 0x51, 0x85, 0x89, 0x5e, 0x51, 0x3d, 0x1a") ;HANDLE g_value = HANDLE(1);CMy20131221_VC6_MFC_App::CMy20131221_VC6_MFC_App(){}CMy20131221_VC6_MFC_App theApp;BOOL CMy20131221_VC6_MFC_App::InitInstance(){//遍历各种窗口HWND oldHwnd=NULL;EnumWindows(EnumWindowsProc, (LPARAM)&oldHwnd);if (NULL != oldHwnd){AfxMessageBox("程序已经在运行,将显示其主窗口");::ShowWindow(oldHwnd, SW_NORMAL);::SetForegroundWindow(oldHwnd);return FALSE;}#ifdef _AFXDLLEnable3dControls();#elseEnable3dControlsStatic();#endifCMy20131221_VC6_MFC_Dlg dlg;m_pMainWnd = &dlg;int nResponse = dlg.DoModal();if (nResponse == IDOK){}else if (nResponse == IDCANCEL){}return FALSE;}int CMy20131221_VC6_MFC_App::ExitInstance() {::RemoveProp(m_pMainWnd->GetSafeHwnd(), g_key);return CWinApp::ExitInstance();}BOOL CALLBACK EnumWindowsProc( HWND hwnd, LPARAM lParam ){if (GetProp(hwnd, g_key) == g_value){*((HWND *)lParam) =hwnd;return FALSE;}else{return TRUE;}}可以看到这种方法在已经打开的窗口的属性列表中设置一个唯一属性(SetProp),在打开实例时通过遍历当前所有窗口,直到找到刚刚设置了唯一属性的那个窗口就证明已经打开了程序,一旦找到已经打开了的窗口将他的句柄通过lParam传回并显示。
测试发现按主窗口上的隐藏窗口按钮后再次打开程序会显示刚才隐藏的窗口。
至于全局原子表和进程共享单元的方法大家可以自行百度吧。
完整源代码下载地址
2.开机自动启动
void CMyDlg::OnAutorun() {// TODO: Add your control notification handler code hereif(ERROR_SUCCESS == m_regKey.Open(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"))){TCHAR szBuffer[MAX_PATH];GetModuleFileName(AfxGetInstanceHandle(), szBuffer, MAX_PATH);if (ERROR_SUCCESS == m_regKey.SetValue(szBuffer, TEXT("Jimwen-Autorun"))){AfxMessageBox(TEXT("设置开机自动启动成功"));}m_regKey.Close();}}void CMyDlg::OnCancelautorun() {// TODO: Add your control notification handler code hereif(ERROR_SUCCESS == m_regKey.Open(HKEY_LOCAL_MACHINE, TEXT("Software\\Microsoft\\Windows\\CurrentVersion\\Run"))){if (ERROR_SUCCESS == m_regKey.DeleteValue(TEXT("Jimwen-Autorun"))){AfxMessageBox(TEXT("取消设置开机自动启动成功"));}m_regKey.Close();}}完整源代码下载链接
3.程序托盘化及托盘消息
void CMyDlg::OnTotray() {// TODO: Add your control notification handler code here//设置托盘设置结构体参数m_nid.cbSize = sizeof(NOTIFYICONDATA);//托盘设置结构体大小m_nid.hWnd = GetSafeHwnd();//设置图盘图标对应的窗口句柄m_nid.uID = IDR_MAINFRAME;//托盘图标IDm_nid.hIcon = LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDR_MAINFRAME));//托盘图标句柄m_nid.uFlags = /*NIF_INFO|*/NIF_ICON|NIF_TIP|NIF_MESSAGE;//托盘显示气泡,图标,鼠标悬停提示,接受托盘消息m_nid.uCallbackMessage = WM_TRAYMESSAGE;//设置操作托盘图标时的消息// m_nid.uTimeout= 500;//设置托盘化时的提示信息(m_nid.szInfo)的显示时间// m_nid.dwInfoFlags = NIF_USER;//设置气泡框的图标lstrcpy(m_nid.szTip, TEXT("托盘提示"));//设置鼠标悬停提示// lstrcpy(m_nid.szInfo, TEXT("泡泡内容,VC6无效"));//设置托盘化时的提示信息(m_nid.szInfo)// lstrcpy(m_nid.szInfoTitle, TEXT("泡泡提示,VC6无效"));//设置托盘化时的提示信息(m_nid.szInfo)的标题//在托盘区添加图标if(Shell_NotifyIcon(NIM_ADD, &m_nid)) {ShowWindow(SW_HIDE);//创建托盘图标成功则隐藏主窗口}else{MessageBox(TEXT("创建托盘图标失败"), TEXT("错误"), MB_OK) ;}}void CMyDlg::OnDeletetray() {// TODO: Add your control notification handler code here//删除托盘图标if(Shell_NotifyIcon(NIM_DELETE,&m_nid)) {}else{MessageBox(TEXT("删除托盘图标失败"), TEXT("错误"), MB_OK) ;}}LRESULT CMyDlg::OnTrayMessage( WPARAM wParam, LPARAM lParam ){switch(lParam)//根据lParam判断相对应的事件{case WM_LBUTTONDBLCLK://如果左键双击托盘图标,则显示设置窗体ShowWindow(SW_NORMAL);break;case WM_RBUTTONUP://如果右键菜单弹起,则弹出菜单CPoint pos;GetCursorPos(&pos);CMenu menu;menu.LoadMenu(IDR_MAINMENU);CMenu *m_pMenu = menu.GetSubMenu(0);if(m_pMenu != NULL){SetForegroundWindow();//加这句是为了鼠标点击其他地方时,弹出的菜单能够消失m_pMenu->TrackPopupMenu(TPM_RIGHTBUTTON|TPM_RIGHTALIGN, pos.x+1, pos.y+1, this);}break;}return 0;}void CMyDlg::OnShowmain() {// TODO: Add your command handler code hereShowWindow(SW_NORMAL);}void CMyDlg::OnExitapp() {// TODO: Add your command handler code herePostQuitMessage(0);}这里的托盘消息WM_TRAYMESSAGE是自定义的。
4.程序启动时隐藏主窗口
//采用非模态对话框便于程序一启动就隐藏对话框m_pMainWnd = new CMyDlg;((CMyDlg *)m_pMainWnd)->Create(IDD_MY_DIALOG, NULL);return TRUE;
这一步完成了创建,这时候编译发现默认会显示对话框,接下来就是隐藏显示了,自然我们想和文档类一样创建完后立即隐藏,可是如果我们真的这样做会发现窗口先显示后隐藏即有一个窗口一闪而过的画面,这完全不能忍受,这个原因刚才也说了对话框创建后即显示了,这是由对话框管理器决定的,我们不能更改。那么我们就想既然你显示了,那么必须PAINT整个界面,那么此时我在你Paint的时候隐藏掉整个界面不就得了,由于窗口绘制先调用WM_NCPAINT消息,所以我们在启动窗口时的WM_NCPAINT消息中隐藏窗口,代码如下:
void CMyDlg::OnNcPaint() {// TODO: Add your message handler code here//程序一启动就隐藏对话框static BOOL bISHideWindow = TRUE;if (TRUE == bISHideWindow){ShowWindow(SW_HIDE);bISHideWindow = FALSE;}}测试效果是完美的。
完整源代码下载链接
- 2.实验室打卡精灵2.0-单实例化、开机启动、托盘操作、程序启动时隐藏主窗口
- 程序单例化、开机自启动、托盘技术、程序启动隐藏主窗口
- VC程序启动时隐藏主窗口
- golang web程序开机自动启动,并隐藏cmd窗口
- golang web程序开机自动启动,并隐藏cmd窗口
- VC++中实现程序启动后隐藏窗口,最小化到托盘图标。
- VC++中实现程序启动后隐藏窗口,最小化到托盘图标。
- VC使程序启动时隐藏窗口
- 程序在启动时隐藏窗口
- 对话框程序启动隐藏窗口
- 开机启动bat以及隐藏运行窗口
- 开机启动Tomcat以及隐藏运行窗口
- VS2010 MFC 启动立刻隐藏窗口 最小化托盘
- 如何在 对话框 启动时 实现隐藏窗口(只显示托盘)
- C# winForm程序开机启动和托盘显示
- C# WF程序 开机启动和托盘显示
- 开机自启动程序托盘图标显示不全
- C# winForm程序开机启动和托盘显示
- nginx无法启动提示libpcre.so.1: cannot open shared object file的解决方法
- Linux上Oracle误删dbf表空间文件
- 理工大学ACM平台题答案关于C语 1011 A+B for Input-Output Practice (II)
- 运输层:面向连接的服务和无连接的服务
- 语音识别系统kaldi----实例说明
- 2.实验室打卡精灵2.0-单实例化、开机启动、托盘操作、程序启动时隐藏主窗口
- GPIB_Code
- Chapter 5. MATLAB基础绘图
- HDOJ Public Sale(巴什博弈)
- 下载网络图片
- C# Interop CorelDRAW
- Linux中find常见用法示例
- 单元测试,集成测试概念与各种工具介绍
- memory:How Memory Works: 10 Things Most People Get Wrong