PythonでWindowsAPIを使ってPC業務を自動記録

# はじめに
Pythonプログラムで即業務に役立つサンプルプログラムとして、WindowsAPIを使ってパソコンの操作を自動記録しエクセルに保存するプログラムを作成しましたので、共有します。

# 動作環境
Visual Studio Code
Python3.8.8
# 各種利用ライブラリー
ctypes
win32process
win32api
win32con
# 処理概要:
パソコン操作で現在のアクティブウィンドウのタイトル、EXE名、コンピュータ名、ログオンユーザー名、アイドル時間を記録し、エクセルに書き出すことで、業務記録を自動化出来ます。
記録した結果を集計し、グラフなどで分析することで、業務改善に活用出来ます。
また、プロジェクト管理における工数管理や、使った工数による原価管理の自動化にも役立ちます。
また、最近では、在宅勤務における評価やモニタリングにも使えるのではないかと思います。

![Pythonで業務自動記録_121.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1361368/189bfbca-843f-f925-e691-3474e2698186.png)


# YouTubeでの解説：
プログラムの詳細や各種APIによる情報の取得方法などはYoutubeで詳しく解説していますので、ぜひ、ご覧ください。
<iframe width="560" height="400" src="https://www.youtube.com/embed/1MN6Jh-jU3o" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>

# サンプルソース
YouTubeで紹介している処理のプログラムソースです。
# WindowsAPIで各種情報取得方法：
```Python:Step1.py
import ctypes
import time
import win32process 
import win32api
import win32con
import os

time.sleep(5)
# フォアグラウンドのウィンドウハンドル取得
hwnd = ctypes.windll.user32.GetForegroundWindow()
length = ctypes.windll.user32.GetWindowTextLengthW(hwnd)
buff = ctypes.create_unicode_buffer(length + 1)
# ウィンドウタイトル取得
ctypes.windll.user32.GetWindowTextW(hwnd, buff, length + 1)
print('Active Window:',buff.value)
_, pid = win32process.GetWindowThreadProcessId(hwnd)
if hwnd != 0 or pid != 0:
    hndl = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, 0, pid)
    exe_NAME = os.path.split(win32process.GetModuleFileNameEx(hndl, 0))[1]
    print(exe_NAME)

#コンピュータ名
pc_NAME = win32api.GetComputerName()
print(pc_NAME)
#ログオンユーザー名
login_id = os.getlogin()
print(login_id)
#アイドル時間
idl_time = (win32api.GetTickCount() - win32api.GetLastInputInfo()) / 1000.0
print(idl_time)
```

# 最終イメージ：
10秒毎にパソコンの状態を取得し、エクセルファイルに書き込みます。
最終イメージは、ぜひ、YouTube動画もご覧いただきたいのですが・・・ソースも公開します！
でも、YouTube動画も見てね！！
https://youtu.be/1MN6Jh-jU3o

```Python:Step2.py
import ctypes
import time
import win32process,win32api,win32con
import os
import datetime
import openpyxl
import pywintypes

def get_active_window_info():    
    dt_now = datetime.datetime.now()    # 現在日付時刻
    # フォアグラウンドのウィンドウハンドル取得
    hwnd = ctypes.windll.user32.GetForegroundWindow()
    length = ctypes.windll.user32.GetWindowTextLengthW(hwnd)
    buff = ctypes.create_unicode_buffer(length + 1)
    # ウィンドウタイトル取得
    ctypes.windll.user32.GetWindowTextW(hwnd, buff, length + 1)
    print('Active Window:',buff.value)
    _, pid = win32process.GetWindowThreadProcessId(hwnd)
    if hwnd != 0 or pid != 0:
        try:
            hndl = win32api.OpenProcess(win32con.PROCESS_QUERY_INFORMATION | win32con.PROCESS_VM_READ, 0, pid)
            exe_NAME = os.path.split(win32process.GetModuleFileNameEx(hndl, 0))[1]
        except pywintypes.error:
            print("error")
            exe_NAME = ""
    #アイドル時間
    idl_time = (win32api.GetTickCount() - win32api.GetLastInputInfo()) / 1000.0
    if idl_time < pitch_time:
        #短時間の場合 0 とする
        idl_time = 0
    return hwnd,dt_now,buff.value,exe_NAME,idl_time

#初期設定
pitch_time = 10 # 時間間隔
last_hwnd = 0
pc_NAME = win32api.GetComputerName()    #コンピュータ名
login_id = os.getlogin()    #ログオンユーザー名
yyyymmdd = datetime.datetime.now().strftime('%Y%m%d')   #日付
out_file = './' + pc_NAME + '_' + yyyymmdd + '.xlsx'    #ファイル名
sheet_name = 'PC_Moniter'   #シート名
# 出力ファイルが無ければ新規作成
if os.path.isfile(out_file) == False:
    wb=openpyxl.Workbook()
    ws=wb.active
    ws.title=sheet_name
    ws.cell(row=1,column=1).value = "開始時間"
    ws.cell(row=1,column=2).value = "終了時間"
    ws.cell(row=1,column=3).value = "経過時間"
    ws.cell(row=1,column=4).value = "Windowタイトル"
    ws.cell(row=1,column=5).value = "exe名"
    ws.cell(row=1,column=6).value = "アイドル時間"
    ws.cell(row=1,column=7).value = "ログインid"
    ws.cell(row=1,column=8).value = "コンピュータ名"
    wb.save(out_file)    

try:
    while True:
        new_hwnd,new_time,new_title,new_exe,new_idl = get_active_window_info()
        wb = openpyxl.load_workbook(out_file)
        ws = wb[sheet_name]
        if last_hwnd != 0:  #2回目以降
            # 最終行を更新
            max_row = ws.max_row
            dt_deff = new_time - last_time
            ws.cell ( row = max_row ,column = 2 ).value = new_time  #終了時間
            ws.cell ( row = max_row ,column = 3 ).value = dt_deff   #経過時間
            if new_idl > 0:
                ws.cell ( row = max_row ,column = 6 ).value = \
                    ws.cell ( row = max_row ,column = 6 ).value + (new_idl - last_idl)  #アイドル時間            
        if new_hwnd != last_hwnd:
            # 新たな行を追加
            max_row = ws.max_row + 1
            ws.cell ( row = max_row ,column = 1 ).value = new_time  #開始時間
            ws.cell ( row = max_row ,column = 2 ).value = new_time  #終了時間
            ws.cell ( row = max_row ,column = 3 ).value = 0         #経過時間
            ws.cell ( row = max_row ,column = 4 ).value = new_title #ウィンドウタイトル
            ws.cell ( row = max_row ,column = 5 ).value = new_exe   #exe名
            ws.cell ( row = max_row ,column = 6 ).value = 0         #アイドル時間
            ws.cell ( row = max_row ,column = 7 ).value = login_id  #ログオン名
            ws.cell ( row = max_row ,column = 8 ).value = pc_NAME   #コンピュータ名
            # 今回の値を保存
            last_hwnd , last_time = new_hwnd , new_time #ウィンドウハンドル、開始時間を保存
        wb.save(out_file)
        last_idl = new_idl  #アイドル時間を保存
        time.sleep(pitch_time)

except KeyboardInterrupt:
    print('FINISH!')
```

# 最後に：
今後も、業務に役立ちそうなプログラムを作成して掲載していきたいと思います。