ウィンドウの情報を調べる(Python,Windows)

# この前
Kindle for PCのスクショを撮るPythonプログラムの記事を書きました。

https://qiita.com/dengax/items/d2f64d462715a640184b

スクショ自体はちゃんと撮れているようなのですが、本当に突貫で作ったので、幾つか気に入らない所がありました。

#### コミックなどの画像のみの電子書籍の左右に巨大な黒余白が出来る
　一番の問題はこれなんですが、これに関しては別プログラムでそのうち対応出来ればいいと思っています。というか、一応出来てはいます。

#### 白黒画像をカラーとして保存するのでサイズが大きくなる
　これも別プログラム対応したいと思っています。

#### タイトル入力時にキャンセルを押すとエラー終了する
　まあ、エラーチェックくらいしとけよって事ですね。

#### たまにフルスクリーン化に失敗する。
　この原因は恐らくマウスを動かすと起こります。Kindle for PCを最前面に持ってきた後、一度タイトルバー上で左クリックをしているので、そのときにマウスが変な所に移動しているとフルスクリーン化に失敗します。

#### 小説でたまにページ最後の行が次のページ行頭とダブる
　これってKindle for PCの問題じゃないんですかね？
　なんとかできるんでしょうか？

#### たまに別のプログラムに誤爆する。
　別のって言うか、Google chromeでタイトルバーに"Kindle"が入ってると誤爆します。探すウィンドウ文字列を'Kindle for PC'にすれば少しは減りますが、それでもタイトルバーに同じ文字があると誤爆する可能性が有ります。

# 最後のはちょっと致命的なので、
その辺りを改良する前提で、最後の動作を改良するためのプログラムを作りました。と言うより、ただのWin32APIのラッパーです。

# ソースコード
```Python:windowinfo.py
from ctypes import *
from ctypes.wintypes import *
import os.path

EnumWindows = windll.user32.EnumWindows
WNDENUMPROC = WINFUNCTYPE(c_bool, POINTER(c_int), POINTER(c_int))

ignores = ['Default IME', 'MSCTFIME UI']

ghwnd = None
wintitle = ''
pName = None
windowlist = []

def EnumWindowsProc(hwnd, lParam):
    global ghwnd, wintitle, pname
    length = windll.user32.GetWindowTextLengthW(hwnd)
    buff = create_unicode_buffer(length + 1)
    windll.user32.GetWindowTextW(hwnd, buff, length + 1)
    if buff.value.find(wintitle) != -1:
        if pname is not None:
            p = os.path.basename(GetWindowThreadProcessName(hwnd))
            if p.upper() != pname.upper():
                return True
        ghwnd = hwnd
        return False
    return True

def EnumWindowsListProc(hwnd, lParam):
    global windowlist, ignores
    length = windll.user32.GetWindowTextLengthW(hwnd)
    buff = create_unicode_buffer(length + 1)
    windll.user32.GetWindowTextW(hwnd, buff, length + 1)
    if not any([buff.value.find(x) != -1 for x in ignores]) and buff.value != '':
        windowlist.append({'Text': buff.value, 'HWND': hwnd, 
                           'Pos': GetWindowRect(ghwnd),
                           'pid': GetWindowThreadProcessId(hwnd) ,
                           'Location': GetWindowThreadProcessName(hwnd)})
    return True

def GetWindowThreadProcessId(hwnd):
    pid = c_ulong()
    windll.user32.GetWindowThreadProcessId(hwnd,pointer(pid))
    return pid.value

def GetWindowThreadProcessName(hwnd):
    pid = c_ulong()
    windll.user32.GetWindowThreadProcessId(hwnd, pointer(pid))
    handle = windll.kernel32.OpenProcess(0x0410, 0, pid)
    buffer_len = c_ulong(1024)
    buffer = create_unicode_buffer(buffer_len.value)
    windll.kernel32.QueryFullProcessImageNameW(handle, 0,pointer(buffer),pointer(buffer_len))
    buffer = buffer[:]
    buffer = buffer[:buffer.index("\0")]
    return str(buffer)

def GetWindowHandle(title):
    global ghwnd, wintitle, pname
    ghwnd = None
    wintitle = title
    pname = None
    EnumWindows(WNDENUMPROC(EnumWindowsProc), 0)
    return ghwnd

def GetWindowHandleWithName(title,name):
    global ghwnd, wintitle, pname
    ghwnd = None
    wintitle = title
    pname = name
    EnumWindows(WNDENUMPROC(EnumWindowsProc), 0)
    return ghwnd

def GetWindowList():
    global windowlist
    windowlist = []
    EnumWindows(WNDENUMPROC(EnumWindowsListProc), 0)
    return windowlist

def SetForeWindow(ghwnd):
    windll.user32.SetForegroundWindow(ghwnd)

def SetWindowPos(ghwnd,x,y,cx,cy,uflags):
    hwndinsertafter = HWND()
    windll.user32.SetWindowPos(ghwnd,hwndinsertafter,x,y,cx,cy,uflags)

def GetWindowText(hwnd):
    length = windll.user32.GetWindowTextLengthW(hwnd)
    buff = create_unicode_buffer(length + 1)
    windll.user32.GetWindowTextW(hwnd, buff, length + 1)
    return buff.value

def GetWindowRect(ghwnd):
    rect = RECT()
    windll.user32.GetWindowRect(ghwnd, pointer(rect))
    return rect.left,rect.top,rect.right,rect.bottom

if __name__ == '__main__':
    hwnd = GetWindowHandle('メモ帳')
    print('メモ帳のhwnd : ', hwnd)
    print('メモ帳のプロセスID : ', GetWindowThreadProcessId(hwnd))
    print('メモ帳の実行ファイル : ', GetWindowThreadProcessName(hwnd))
    print('メモ帳のウィンドウタイトル : ',GetWindowText(hwnd))
    print('メモ帳のウィンドウ座標 : ', GetWindowRect(hwnd))
    #SetForeWindow(ghwnd) #メモ帳をアクティブ化
    #SetWindowPos(hwnd,0,0,840,880,0)
    hwnd = GetWindowHandleWithName('.py','code.exe')
    print('".py"を含むVSCODE :',end=' ')
    if hwnd == None:
        print('なし')
    else:
        print('あり')
    l = GetWindowList()
    for i in l:
        print(i['Text'], i['Location'])
```

# 使い方
`GetWindowHandle(ウィンドウタイトルに含まれる文字) `か
`GetWindowHandleWithName(ウィンドウタイトルに含まれる文字,実行ファイル名)`
でウィンドウハンドル(hwnd)を取得して、

`GetWindowThreadProcessId(hwnd)` プロセスID
`GetWindowThreadProcessName(hwnd)` 実行ファイル(フルパス)
`GetWindowText(hwnd)` ウィンドウのタイトル
`GetWindowRect(hwnd)` ウィンドウの座標
を取得できます
`SetForeWindow(hwnd)` ウィンドウをアクティブにして前面に表示
`SetWindowPos(hwnd,x,y,cx,cy,uflags)` ウィンドウのサイズ変更
もできます。

`GetWindowList()` で一覧をリストで取得できます。
リスト内は辞書型で、'Text' , 'HWND' , 'Pos' , 'pid' , 'Location' が入っています。
ソースコード先頭の`ignores` のリストに含まれるものと空白のタイトルは無視します。