防止系統鎖屏-python、C++實現

1、背景

做爲一個開發,個人電腦常常是一個禮拜不關機,甚至時間更久,不知道在其餘人看來這是否是一個常規操做。在平常工做中,咱們的電腦也是一直處於非鎖屏狀態,出於對我的工做成果的安全性保護,咱們公司給每一個人的電腦上下發了一個組策略(屬於強制下發,抗議無效),5min不對電腦進行操做,電腦就鎖屏,這可真是使人操蛋,出去上個廁所的功夫電腦就鎖屏啦、和別人討論問題的功夫電腦又鎖屏了,做爲一個開發,這真不能忍。html

最近一直在學習python,恰好接觸到了pythonwindows服務相關的一些東西,嘿嘿,5分鐘不操做電腦鎖屏是吧,那麼咱們在無任何操做下2分鐘給他模擬一次鍵盤或者鼠標操做可好。python

2、模擬鼠標、鍵盤事件

要寫一個windows服務也是比較簡單的,只須要繼承自win32serviceutil.ServiceFramework這個類,而後實現相關方法便可,主要的方法是SvcDoRun,服務啓動後,該方法處於激活狀態,該方法結束服務退出c++

具體的實現方式可參考Python-定時爬取指定城市天氣(二)-郵件提醒文章中的第三小節,優化定時任務。windows

一、python實現

這裏我只貼出關鍵代碼,服務的總體框架不在細說,不會的同窗請看這裏Python-定時爬取指定城市天氣(二)-郵件提醒安全

a、python服務

首先判斷鼠標2分鐘內是否有操做,咱們須要能獲取到當前鼠標位置的函數,pyautogui是一個python的自動化庫,知足咱們的需求,該庫提供了豐富的鼠標、鍵盤操做,使用該庫,首先就得使用pip進行安裝框架

pip install pyautogui函數

使用方式以下,x和y便是當前鼠標相對於屏幕左上角(0,0)的座標學習

import pyautogui as pag
x, y = pag.position() #返回鼠標的座標

模擬鼠標、鍵盤操做,無非是鼠標點擊、移動、鍵盤按下等,這些pyautogui都已經提供,看名字就知道什麼意思,這裏咱們先進行了鼠標點擊,默認是左鍵,而後移動了鼠標位置,而且在最後按下了鍵盤上的esc鍵測試

pag.click()
pag.moveTo(x + 10, y + 10, 0.1)
pag.moveTo(x, y, 0.1)
writeLog('模擬一次鼠標移動\n')#

pag.press('esc')
writeLog('模擬點擊esc\n')#

完整的SvcDoRUn函數以下優化

def SvcDoRun(self):
    #what to do#
    prev_time = datetime.datetime.now()
    oldx = 0
    oldy = 0
    while self.run:
        x, y = pag.position() #返回鼠標的座標
        now_time = datetime.datetime.now()

        if x == oldx and y == oldy:
            stay_seconds = (now_time -  prev_time).seconds
            if stay_seconds >= 60:
                prev_time = now_time
                pag.click()
                pag.moveTo(x + 10, y + 10, 0.1)
                pag.moveTo(x, y, 0.1)
                writeLog('模擬一次鼠標移動\n')#

                pag.press('esc')
                writeLog('模擬點擊esc\n')#
        else:
            #更新舊座標 最後一次移動鼠標時間
            oldx = x
            oldy = y
            prev_time = now_time
            #os.system('cls')#清楚屏幕

        stay_seconds = (now_time -  prev_time).seconds
        writeLog('鼠標{}秒未移動\n'.format(stay_seconds))#打印座標

        posStr = "Position:" + str(x).rjust(4) + ',' + str(y).rjust(4)
        writeLog(posStr + '\n')#打印座標

        time.sleep(2)

服務函數寫完了,接下來是打包服務的過程,並啓動服務

1. 打包服務成一個exe,pyinstaller -F  aaa.py
2. 安裝服務 python aaa.exe install
3. 啓動服務 python aaa.exe start 
4. 中止服務 python aaa.exe stop
5. 移除服務 python aaa.exe remove

執行上述流程的一、2和3,服務就已經被成功啓動,但不幸的是發現pag.position()返回的座標一直是0,各類測試都不對,開始懷疑是服務裏可能找不到pyautogui資源致使失敗,後來在網上找了另外一種或許鼠標位置的函數

def get_mouse_point():
   po = POINT()
   windll.user32.GetCursorPos(byref(po))
   return int(po.x), int(po.y)

通過測試,該函數單獨運行時沒有問題,放在服務裏拿到的座標仍是(0, 0),寫服務的路子算是泡湯啦

爲了更好的查找服務的運行狀態,咱們這裏把服務的運行時狀態卸載了一個文件裏,寫日誌代碼以下

#寫日誌
def writeLog(msg):
    try:
        f = open('./prevent_lock_screen.log', 'a', encoding = 'utf-8')
        f.write(datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + ':' + msg)
        f.close()
    except BaseException:
        pass
b、python函數

python服務的方式暫時算是中斷了,可是咱們仍是不能放棄啊,通過嘗試,把運行在服務裏的代碼拿出來放在正常python文件裏執行仍是好使的。不明因此啊,++哪位大神若是知道服務裏的代碼爲何運行失敗,還請評論區支出,不勝感激。。。++

爲了防止電腦自動鎖屏,要一直運行一個dos窗口看起來確實挺扯的,初學python可能好多東西仍是不懂,所以爲了讓這個需求更優雅一些,我拿起了C++,咱們仍是先來寫一個服務吧

二、C++實現

爲了實現這個需求,我也真是拼了

a、C++服務

不得不說,C++寫服務仍是挺費勁的,在網上扒了一個服務的模子,我便開始寫了,其實最主要的仍是要實現服務中的死循環函數,代碼邏輯和上述python的思路一塊兒,區別就是咱們須要使用C++的語法實現一遍而已。

POINT p;
    GetCursorPos(&p);//獲取鼠標座標
    int x = p.x, y = p.y;//返回鼠標的座標
    time_t now_time = time(NULL);

    if (x == oldx && y == oldy)
    {
        int stay_seconds = int(now_time - prev_time);
        if (stay_seconds >= 6)
        {
            prev_time = now_time;
            SetCursorPos(x + 10, y + 10);
            SetCursorPos(x, y);
            WriteToLog("模擬一次鼠標移動");

            mouse_event(MOUSEEVENTF_LEFTDOWN | MOUSEEVENTF_LEFTUP, x, y, 0, 0);
            WriteToLog("模擬鼠標單擊");

            keybd_event('esc', 0, 0, 0);
            keybd_event('a', 0, 0, 0);
            WriteToLog("模擬點擊esc");
        }
    }
    else
    {
        //更新舊座標 最後一次移動鼠標時間
        oldx = x;
        oldy = y;
        prev_time = now_time;
    }

接下來的操做就是咱們須要把寫好的服務安裝並啓動

1. sc create test binPath= 可執行文件的路徑
2. net start test
3. net stop test
4. net delete test

執行上述步驟1和2便可啓動服務

通過測試,太不幸了,GetCursorPos(&p)返回的座標也爲(0, 0),這下真是鬱悶了,服務這條路難道真的走不通了嗎?看到的大神有解決思路的還請在評論區支出,不勝感激。。。

b、C++可執行程序

照搬照抄上述python服務轉可執行程序的邏輯,咱們把C++服務裏的代碼拿出來,放到 C++可執行程序中,咱們也能夠實現一個C++可執行程序

進過測試,以上python程序和C++程序都還有一些問題,在一些特定的窗口上模擬鼠標、鍵盤操做很差使,好比notepad ++、windows任務管理器等,系統在5分鐘後仍是鎖屏,思前想後,覺着這個可能和程序權限有關係,隨即把C++工程的屬性進行了調整,生成的exe須要帶有管理員權限,在次進行測試,結果是完美的,咱們終於能夠防止系統自動鎖屏了,執行上述python的程序這裏就不作權限升級研究了,有興趣的同窗自行研究

c++程序咱們能夠經過設置來吧程序設置成後臺運行的,沒有任何界面,這樣顯得更優雅一些,首先咱們建立的是一個dos程序,設置設置兩個地方便可

  1. 鏈接器->系統:子系統設置成窗口 (/SUBSYSTEM:WINDOWS)
  2. 鏈接器->高級:入口點設置成mainCRTStartup

3、優化

最開始的模擬用戶操做,咱們使用的是點擊鼠標左鍵、移動鼠標、和模擬點擊esc按鍵,可是上述操做都會不懂程度的帶來一些影響。

例如:

  1. 若是用戶正在看視頻,沒有進行鍵鼠操做,這個時候若是點擊鼠標左鍵,可能會致使視頻暫停,不是用戶指望的操做
  2. 若是用戶打開了一個彈框,例如頂層窗口是一個esc快捷鍵能夠關閉的程序,這個時候若是用戶2分鐘沒有操做電腦,那麼模擬的esc按鍵將會把用戶的原始狀態打亂

模擬操做優化過程

一、win+d

切換到桌面,隨即在開切換回來,這個操做相對來講比較靠譜,可是若是有一個窗口上有模態窗口存在,也會對打亂原始的窗口順序

二、win鍵

點擊windows鍵,隨即在點擊一次,恢復到點擊以前的狀態,這個操做相對第一種仍是比較友好的

三、切換大小寫

點擊CapsLk按鍵,進行大小寫切換,因爲這個時候用戶沒有操做電腦,所以切換大小寫不會對用戶操做進行干擾,並且動靜更小、更優雅

上述3中模擬操做行爲基本思路都是同樣的,只是模擬的方式全部不一樣,下邊咱們就以第三種方式講解實現過程

點擊CapsLk的操做分兩部分,第一次主要是爲了模擬用戶點擊,第二次是爲了恢復第一次操做留下的痕跡,爲了讓程序更優雅的運行,咱們這裏須要啓動子線程來恢復主線程留下的痕跡
main函數代碼以下

hMutex = CreateMutex(NULL, FALSE, (LPCWSTR)"PreventLockScreenApp");
WaitForSingleObject(hMutex, INFINITE);

HANDLE hThread = CreateThread(NULL, 0, RestoreWinState, NULL, 0, NULL);     //建立線程01
CloseHandle(hThread); //關閉句柄

remove(LOGFILE);
time_t prev_time = time(NULL);//
int oldx = 0;
int oldy = 0;
char positionText[100] = { 0 };
sprintf(positionText, "防鎖屏進程已啓動,程序將在無鼠標移動狀況下每隔%d秒模擬一次鍵盤操做", Job_TIME);
WriteToLog(positionText);
while (1)
{
    POINT p;
    GetCursorPos(&p);//獲取鼠標座標
    int x = p.x, y = p.y;//返回鼠標的座標
    time_t now_time = time(NULL);

    if (x == oldx && y == oldy)
    {
        int stay_seconds = int(now_time - prev_time);
        if (stay_seconds >= Job_TIME)
        {
            prev_time = now_time;

            keybd_event(VK_CAPITAL, (BYTE)0, 0, 0);
            keybd_event(VK_CAPITAL, (BYTE)0, KEYEVENTF_KEYUP, 0);
            WriteToLog("模擬點擊CapsLk,切換大小寫");

            //釋放鎖 讓子線程去恢復win鍵狀態
            ReleaseMutex(hMutex);
            Sleep(Restore_TIME / 2);
            WaitForSingleObject(hMutex, INFINITE);
        }
    }
    else
    {
        //更新舊座標 最後一次移動鼠標時間
        oldx = x;
        oldy = y;
        prev_time = now_time;
    }

    int stay_seconds = int(now_time - prev_time);

    char mousemoved[100] = { 0 };
    sprintf(mousemoved, "鼠標%d秒未移動", stay_seconds);
    WriteToLog(mousemoved);//打印座標

    char positionText[100] = { 0 };
    sprintf(positionText, "當前鼠標位置(%d,%d)", x, y);
    WriteToLog(positionText);//打印座標

    Sleep(SLEEP_TIME);
}

子線程代碼以下

void SimulationBehavior()
{
    keybd_event(VK_CAPITAL, (BYTE)0, 0, 0);
    keybd_event(VK_CAPITAL, (BYTE)0, KEYEVENTF_KEYUP, 0);
}

DWORD WINAPI RestoreWinState(LPVOID lvParamter)
{
    while (true)
    {
        WaitForSingleObject(hMutex, INFINITE);

        Sleep(Restore_TIME);
        SimulationBehavior();

        ReleaseMutex(hMutex);
    }

    return 0;
}

主子線程使用一個全局的信號量來進行同步

4、demo下載

須要C++python代碼的同窗到csdn下載:C++實現的防鎖屏後臺進程-內含python實現代碼

博客園地址:防止系統鎖屏-python、C++實現
簡書地址:防止系統鎖屏-python、C++實現


轉載聲明:本站文章無特別說明,皆爲原創,版權全部,轉載請註明:朝十晚八 or Twowords

相關文章
相關標籤/搜索