当系统闲置时(鼠标,键盘长时间不动),用户定制的回调

本文介绍了一个系统闲置状态下的回调机制,包括如何创建和释放闲置回调对象,以及如何通过回调函数来响应系统进入闲置状态和从闲置状态恢复。

 //当系统闲置时,用户定制的回调
 /*如果下面的两个回调之一存在S循环,调用CreateIdlingProc将会不返回 */
//头文件
#ifndef FengFunlib
#define FengFunlib
#pragma  once
#include <windows.h>

typedef     BOOL (CALLBACK* PIDLINGPROC)(LPVOID lpParam);//当系统进入闲置状态的回调函数,要继续执行返回TRUE,返回FALSE退出,不再继续执行
typedef     PIDLINGPROC     PIDLINGPROC;
typedef     BOOL (CALLBACK* PTOUCHEDPROC)(LPVOID lpParam);//当系统首次从闲置状态返回到非闲置状态时的回调函数,要继续执行返回TRUE,返回FALSE退出,不再继续执行
typedef     PTOUCHEDPROC    PTOUCHEDPROC;
typedef     void*           HIDLINGOBJECT;

 /* 当系统长时间闲置时,在dwSecondDueTime(按毫秒计算)的等待之后,执行由用户定制的pfnIdlingFun回调函数
    ,如果pfnIdlingFun返回TURE,则在每次dwSecondDueTime定义的时间到达后,会继续执行pfnIdlingFun函数;
     另外:当系统从闲置时返回(如果一个新的应用程序被运行,或是用户移动鼠标、按动键盘则会触发PTOUCHEDPROC
     回调函数的调用,这个函数只被调用一次,当系统从闲置状态返回到非闲置状态时,如果已进入非闲置状态并且存在连续的活动(例如:
          鼠标、键盘的活动、应用程序的运行,都不会再次被调用.如果再次从闲置进入非闲置状态,会再一次被调用一次.
    如果pfnIdlingFun返回FALSE,则不会继续执行pfnIdlingFun函数。当不使用时,调用ReleaseIdlingProc释放
    需要特别注意的是,不允许在CreateIdlingProc的两个回调函数的实现中存在长时间的执行过程(或者是S循序),
    否则会使后台线程对系统的状态的监视失效*/


__out              
HIDLINGOBJECT WINAPI
    CreateIdlingProc(
    __in   PIDLINGPROC   pfnIdlingFun,/*当系统进入闲置状态时,用户定制的回调函数  */
          __in   LPVOID        lpIdlingFunParam,/* pfnIdlingFun函数的参数 */
    __in   DWORD         dwDueTime ,/*按毫秒为单位的计时等待,达到后执行pfnIdlingFun  */
    __in   PTOUCHEDPROC  pfnTouchedFun,/*当系统从闲置状态转到非闲置状态时,用户定制的回调函数 */
    __in   LPVOID        lpTouchedFunParam/*pfnTouchedFun的参数 */
    );
         /*释放从CreateIdlingProc返回的对象 */
         BOOL ReleaseIdlingProc(__in HIDLINGOBJECT hIdlingObj);
//-------------------------------------------------------------------------------
//cpp  这个函数只能运行在win2000或之后的版本
#define  _WIN32_WINNT 0x0500

 

#include "..FengFunlib.H"


typedef struct {
   PIDLINGPROC       pfnIdlingFun;/*系统闲置状态下,用户定制的回调函数  */
   LPVOID            lpIdlingFunParam;/* pfnIdlingFun函数的参数 */
   DWORD             dwDueTime ;/*按毫秒为单位的计时等待,达到后执行pfnIdlingFun  */
   HANDLE            IdlingWorkingThread;/*指向IdlingWorkingThread的线程句柄  */
   PTOUCHEDPROC      pfnTouchedFun;/*从闲置进入非闲置状态的第一次通知  */
   LPVOID            lpTouchedParam;/* pfnTouchedFun函数的参数*/
   BOOL              fIdlingStatus;//是否为闲置状态,如果是为TRUE,否则为FALSE
   HANDLE            hEvt;/* 事件对象 */
  }IDLINGDATA,*PIDLINGDATA;


 DWORD CALLBACK IdlingWorkingThread(LPVOID lpParam){

 


  /*这个函数的回调都是在当前线程的,如果回调不返回,那么当前线程就被挂起 */
            if(lpParam==NULL) return (DWORD)(FALSE);
            PIDLINGDATA pIdlingData=(PIDLINGDATA)lpParam;
            DWORD    dwWaitObjStatus=0;
   DWORD    dwLastInputTime=0;
   DWORD    dwTimeCount=1;//默认值为1
   DWORD    dwFinallyCount=1;//最终次数,按每500毫秒,半秒
//如果等待的毫秒数小于,例如200毫秒的等待,在这里都为500毫秒
   BOOL     fReturnValueOfpfnIdlingFun=TRUE;//pfnIdlingFun回调的返回值
   BOOL     fReturnValueOfpfnTouchedFun=TRUE;//pfnTouchedFun回调的返回值
   if(pIdlingData->dwDueTime<500)/*小于500毫秒,立即执行  */
    dwFinallyCount=1;//dwTimeCount%dwFinallyCount总是为0
   else
    if(pIdlingData->dwDueTime==INFINITE)
     dwFinallyCount=INFINITE;/*无限等待 ,如果dwTimeCount%dwFinallyCount总是不为0,除非等到*/
   else
    if(pIdlingData->dwDueTime==0)/* 立即执行不等待 */
     dwFinallyCount=1;//dwTimeCount%dwFinallyCount总是为0
   else
    dwFinallyCount=pIdlingData->dwDueTime/500;//按每500毫秒计算,有多少个500毫秒

   LASTINPUTINFO _lastInfo={sizeof(LASTINPUTINFO)};
           
   __try{
    while(1){
              dwWaitObjStatus=WaitForSingleObject(
      pIdlingData->hEvt,
      500/*每500毫秒 */
       );
     switch(dwWaitObjStatus){
    case WAIT_TIMEOUT:
    // if(!fReturnValueOfpfnIdlingFun) __leave;//如果回调为FALSE,跳到__finally
                GetLastInputInfo(&_lastInfo);
    if(dwLastInputTime!=_lastInfo.dwTime){
                  dwLastInputTime=_lastInfo.dwTime;//赋值,上次的最后时间
                  dwTimeCount=1;//设为默认值
      if(pIdlingData->fIdlingStatus==TRUE
       &&pIdlingData->pfnTouchedFun!=NULL
       &&fReturnValueOfpfnTouchedFun==TRUE){           
        pIdlingData->fIdlingStatus=FALSE;
      
                    fReturnValueOfpfnTouchedFun=
      pIdlingData->pfnTouchedFun(pIdlingData->lpTouchedParam);
     
       }
     }else
      if(dwLastInputTime==_lastInfo.dwTime){
       if( dwTimeCount%dwFinallyCount==0){
                         
                                   if(pIdlingData->pfnIdlingFun!=NULL)
         fReturnValueOfpfnIdlingFun=
         pIdlingData->pfnIdlingFun(pIdlingData->lpIdlingFunParam);
                                  dwTimeCount=1;//清空为默认值
          pIdlingData->fIdlingStatus=TRUE;
       
        }
       else
                                dwTimeCount++;
       }
     break;
    case WAIT_OBJECT_0:
   
                   __leave;
   
    default :
     break;

     
      }//switch
     }//while
    }
      __finally{

    }
    
return 0;
  }

              
  HIDLINGOBJECT WINAPI
 CreateIdlingProc(
   PIDLINGPROC   pfnIdlingFun,/*当系统进入闲置状态时,用户定制的回调函数  */
   LPVOID        lpIdlingFunParam,/* pfnIdlingFun函数的参数 */
   DWORD         dwDueTime ,/*按毫秒为单位的计时等待,达到后执行pfnIdlingFun  */
   PTOUCHEDPROC  pfnTouchedFun,/*当系统从闲置状态转到非闲置状态时,用户定制的回调函数 */
   LPVOID        lpTouchedFunParam/*pfnTouchedFun的参数 */ 
    ){
         PIDLINGDATA pIdlingData=(PIDLINGDATA)GlobalAlloc(GPTR,sizeof(IDLINGDATA));
            if(pIdlingData==NULL) return NULL;
   __try{
    pIdlingData->hEvt= CreateEvent(NULL,FALSE,FALSE,NULL);
    if(pIdlingData->hEvt==NULL)__leave;
                pIdlingData->dwDueTime= dwDueTime;
                pIdlingData->fIdlingStatus=FALSE;
                pIdlingData->lpIdlingFunParam=lpIdlingFunParam;
    pIdlingData->pfnIdlingFun=pfnIdlingFun;
    pIdlingData->pfnTouchedFun=pfnTouchedFun;
    pIdlingData->lpTouchedParam=lpTouchedFunParam;
    
    pIdlingData->IdlingWorkingThread=CreateThread(
     NULL,0,
     (LPTHREAD_START_ROUTINE)IdlingWorkingThread,
     pIdlingData,0,NULL);
                    if(pIdlingData->IdlingWorkingThread==NULL) __leave;
     SetThreadPriority(pIdlingData->IdlingWorkingThread,THREAD_PRIORITY_IDLE);
     return pIdlingData;
    }__finally{
    }
   if(NULL!=pIdlingData->IdlingWorkingThread){
                  DWORD dwExitCode=0;
      GetExitCodeThread(pIdlingData->IdlingWorkingThread,&dwExitCode);
                if(dwExitCode!=0)
     SetEvent(pIdlingData->hEvt);
                WaitForSingleObject(pIdlingData->IdlingWorkingThread,INFINITE);
    }

        if(NULL!=pIdlingData->hEvt)CloseHandle(pIdlingData->hEvt);
            pIdlingData->dwDueTime=0;
   pIdlingData->lpIdlingFunParam=NULL;
   pIdlingData->lpTouchedParam=NULL;
   pIdlingData->pfnIdlingFun=NULL;
   pIdlingData->pfnTouchedFun=NULL;
      if(NULL!=pIdlingData->IdlingWorkingThread)
    CloseHandle(pIdlingData->IdlingWorkingThread);
GlobalFree( pIdlingData);
pIdlingData=NULL;
    
      return NULL;
   }
  BOOL ReleaseIdlingProc(__in HIDLINGOBJECT hIdlingObj){
         PIDLINGDATA   pIdlingData=(PIDLINGDATA)hIdlingObj; 

   __try{
   if(pIdlingData!=NULL){
    if(pIdlingData->IdlingWorkingThread==NULL)
     __leave;
     else{
     DWORD ThdExitCode=0;
     GetExitCodeThread(pIdlingData->IdlingWorkingThread,&ThdExitCode);
     if(ThdExitCode!=0)
      SetEvent(pIdlingData->hEvt);
                       WaitForSingleObjectEx(pIdlingData->IdlingWorkingThread,
         INFINITE,TRUE);//等待线程结束
     }

    pIdlingData->dwDueTime=0;
    pIdlingData->lpIdlingFunParam=NULL;
    pIdlingData->lpTouchedParam=NULL;
    pIdlingData->pfnIdlingFun=NULL;
    pIdlingData->pfnTouchedFun=NULL;
if(pIdlingData->hEvt!=NULL) CloseHandle(pIdlingData->hEvt);
if(pIdlingData->IdlingWorkingThread!=NULL)CloseHandle(pIdlingData->IdlingWorkingThread);

    GlobalFree( pIdlingData);
    pIdlingData=NULL;
    return TRUE;
    }
    }
   __finally{}
   return FALSE;
   }

基于以下代码,将check_keyboard_activity的代码完善以能检测键盘活动,即:使用键盘也表明在使用中,使用键盘也可以推出倒计。 import time import threading # 运行监听器和检查器 import ctypes import tkinter as tk from tkinter import messagebox # 显示倒计窗口 from datetime import datetime, timedelta import pyautogui # 用于控制鼠标键盘的python库,检查鼠标键盘活动状态 import winsound class ComputerUsageMonitor: def __init__(self): self.start_time = time.time() self.last_activity_time = time.time() # 初始化一个变量为当前间--最后活动间 self.total_usage_time = 0 # 记录总的活动间 self.idle_threshold = 5 # 3分钟闲置阈值 self.countdown_duration = 5 # 10秒倒计 self.is_countdown_active = False # 是否在倒计锁屏 self.countdown_window = None self.cancel_countdown = False # 新增:取消倒计标志 # 创建每日使用间日志文件 self.log_file = r"C:\Users\x00708\PycharmProjects\mission\practice_from_work_data\lock_monitor\result" with open(self.log_file, "a") as f: f.write(f"{datetime.now().strftime('%Y-%m-%d %H:%M:%S')} - 监控程序启动\n") # strftime将间对象格式化为字符串 def check_idle_time(self): """检查鼠标键盘活动状态,更新最后活动间""" while True: current_pos = pyautogui.position() # 获取当前鼠标位置 time.sleep(0.5) # 缩短检测间隔提高响应速度 # 检查鼠标是否移动 new_pos = pyautogui.position() if current_pos != new_pos: # 如果鼠标位置移动 self.last_activity_time = time.time() # 记录现在的间作为最后活动间 # 如果倒计正在进行,设置取消标志 if self.is_countdown_active: self.cancel_countdown = True continue # 检查键盘活动(简化版) if self.check_keyboard_activity(): self.last_activity_time = time.time() # 如果倒计正在进行,设置取消标志 if self.is_countdown_active: self.cancel_countdown = True def check_keyboard_activity(self): # 没有检测键盘活动的❗ """检测键盘活动""" # 实际应用中可替换为更精确的键盘检测 return False def calculate_idle_time(self): """计算闲置间""" return time.time() - self.last_activity_time # 现在的间-最后一次活动间 def monitor_usage(self): """监控使用间并触发倒计""" while True: idle_time = self.calculate_idle_time() # 记录使用间(非闲置间) if idle_time < 1: self.total_usage_time += 1 # 检查是否需要启动倒计 if idle_time > self.idle_threshold and not self.is_countdown_active: active_duration = timedelta(seconds=self.total_usage_time) self.log_usage(active_duration) self.start_countdown() time.sleep(1) def start_countdown(self): """启动锁屏倒计""" self.is_countdown_active = True self.cancel_countdown = False # 重置取消标志 countdown_thread = threading.Thread(target=self.show_countdown) countdown_thread.daemon = True countdown_thread.start() def show_countdown(self): """显示锁屏倒计窗口""" self.countdown_window = tk.Tk() self.countdown_window.attributes("-fullscreen", True) self.countdown_window.configure(bg="black") self.countdown_window.attributes("-topmost", True) self.countdown_window.protocol("WM_DELETE_WINDOW", self.cancel_countdown_action) # 处理窗口关闭事件 countdown_label = tk.Label( self.countdown_window, text=str(self.countdown_duration), font=("Arial", 100), fg="white", bg="black" ) countdown_label.pack(expand=True) info_label = tk.Label( self.countdown_window, text="系统将在倒计结束后锁定\n移动鼠标或按键取消锁定", font=("Arial", 24), fg="white", bg="black" ) info_label.pack(pady=50) # 开始倒计并添加取消检测 self.countdown_window.after(100, self.check_cancel_countdown, countdown_label) self.update_countdown(countdown_label, self.countdown_duration) self.countdown_window.mainloop() def check_cancel_countdown(self, label): """定期检查是否应该取消倒计""" if self.cancel_countdown: # 取消倒计,关闭窗口 self.countdown_window.destroy() self.countdown_window = None self.is_countdown_active = False self.last_activity_time = time.time() # 重置活动间 else: # 继续检查 self.countdown_window.after(100, self.check_cancel_countdown, label) def cancel_countdown_action(self): """手动取消倒计回调函数""" self.cancel_countdown = True def update_countdown(self, label, remaining): """更新倒计显示""" if remaining >= 0 and not self.cancel_countdown: label.config(text=str(remaining)) if remaining <= 3: winsound.Beep(1000, 300) self.countdown_window.after(1000, self.update_countdown, label, remaining - 1) elif not self.cancel_countdown: # 倒计结束未被取消 self.countdown_window.destroy() self.countdown_window = None self.lock_computer() def lock_computer(self): """锁定计算机""" try: self.is_countdown_active = False ctypes.windll.user32.LockWorkStation() self.last_activity_time = time.time() except Exception as e: messagebox.showerror("错误", f"无法锁定计算机: {str(e)}") def log_usage(self, active_duration): """记录使用间到日志文件""" timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S") log_entry = f"{timestamp} - 使用间: {str(active_duration)}" with open(self.log_file, "a") as f: f.write(log_entry + "\n") def run(self): """启动监控程序""" idle_thread = threading.Thread(target=self.check_idle_time) monitor_thread = threading.Thread(target=self.monitor_usage) idle_thread.daemon = True monitor_thread.daemon = True idle_thread.start() monitor_thread.start() try: while True: time.sleep(60) print(f"当前使用间: {timedelta(seconds=self.total_usage_time)}") except KeyboardInterrupt: active_duration = timedelta(seconds=self.total_usage_time) self.log_usage(active_duration) print(f"程序结束。总使用间: {active_duration}") # 启动监控程序 if __name__ == "__main__": monitor = ComputerUsageMonitor() monitor.run()
最新发布
10-24
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值