質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

89.11%

Python3.7 で EnumWindows の列挙を止める際に EnumWindowsProc に 0 を返すとエラーが発生する

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 84

busyoda

score 33

EnumWindows の列挙を止める際に EnumWindowsProc(コールバック関数)に 0 を返すとエラーが発生します。
エラーを発生させずに列挙を止める方法についてご教示お願いいたします。

言語:Python3.7
実行環境:Windows10 64bit / Visual Studio 2019

import win32gui

def EnumWindowsProc(hWnd, lParam):
    if lParam == 0:
        return 0

win32gui.EnumWindows(EnumWindowsProc, 0)

エラー箇所

win32gui.EnumWindows(EnumWindowsProc, 0)

エラー内容

Message=(126, 'EnumWindows', '指定されたモジュールが見つかりません。')
Source=C:\Users\sample.py
スタック トレース:
File "C:\Users\sample.py", line 9, in <module>
win32gui.EnumWindows(EnumWindowsProc, 0)

上記プログラムについて

御覧の通りですが、トップレベルウィンドウの列挙を行い、
最初の1回目で列挙を終了させています。

本来は該当の pid を持つ場合に列挙を終了させますが、
本件のエラーが EnumWindowsProc の return False に起因することが判明している為、
極力簡潔に事象を再現できる様、上記プログラムを掲載しました。

試したこと

  1. EnumWindowsProc の return False を削除する。
    エラーは発生しませんが、列挙を止めることができません。
     
  2. EnumWindowsProc で SetLastError を実行する。
    EnumWindows の Docs を確認した所、
    EnumWindowsProc が 0 を返すと列挙を終了するが、
    その場合は EnumWindowsProc が EnumWindows の呼び出し元に SetLastError で
    意味のあるエラー番号を返す必要があると記載がありました。
     
    なので、EnumWindowsProc で return 0 の直前に SetLastError を呼び出して、
    有効な(エラー内容が紐付けられた)エラー番号を渡してみました。
      
    結果、上記で発生しているエラー番号 126 が SetLastError で指定したエラー番号に変化しました。
    またエラー内容も該当のエラー番号に合わせて変化します。
     
    この場合もエラー自体は発生している為、処理が止まってしまいます。
    複数種類のエラーを試行したものの、いずれの種類のエラーでも同様にエラー自体は発生しました。
     
    また、有効でないエラー(-1 や 0 など)の場合もエラーは発生します。
    その場合、エラー内容は「エラー番号が定義されていない」といったものになります。
     
    そもそも他言語で EnumWindows を利用した際はこの様な処理をしなくとも正常に動作しておりましたので、
    SetLastError では本事象を解消できないのではないかと考えております。
     
  3. EnumWindowsProc の戻り値を return 0 でなく return False にする。
    Docs によると、EnumWindowsProc の戻り値は BOOL の様でしたので変更してみました。
    結果は変化ありませんでした。
     
  4. EnumWindows の戻り値を rc で受け取ってみる
    Docs によると、EnumWindowsProc が 0 を返した場合、
    EnumWindows 自体も 0 を返すらしく、
    以下の様に、EnumWindows の呼び出し元に明示的にそれを受け取らせてみました。
rc = win32gui.EnumWindows(EnumWindowsProc, 0)


結果は変化ありませんでした。 

例外処理による回避策について

本事象の回避策として、例外処理をすれば問題なく処理を進められるかと思います。

しかし、今後も利用頻度は少なくないであろう API についてでしたので、
利用方法に落ち度があるのであれば、どう改善すべきか、
または、win32gui 配下で EnumWindows を利用する以上、エラーを発生させない方法はないのか、
など、例外処理以外の回避策の有無やその内容について確認したくご質問させて頂きました。
 
 
もし上記事象について何かご存じの方がいらっしゃいましたら、
ご回答頂けますと幸甚です。

  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

ええと、pywin32のドキュメントによると、

callback : function
A Python function to be used as the callback. Function can return False to stop enumeration, or raise an exception.

win32gui.EnumWindows - Python for Win32 Extensions Help

ということで、「コールバック関数は列挙を止めるためにFalseを返すことができ、言い換えると例外を発生させることができる」と書いてある(よね? この", or")ので、例外が発生するのはwin32gui.EnumWindowsの仕様のようです。

なお、例外処理をせずにEnumWindowsを実行するには、ctypesを使ってuser32.dllのEnumWindowsを呼び出すコードを書けばいいのですが(Stack Overflowにいくつかコード例が見つかります。「python ctypes enumwindows」で検索)、どれかひとつ見ればわかるようにかえって長くなるので。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2020/07/01 22:41

    ご回答ありがとうございます。

    なるほど、確かに ", or" だと類義語のニュアンスですね。
    完全に「もしくは」のニュアンスで読み解いておりました。

    また、ctypes であれば例外が発生しない旨の情報もありがとうございます。

    ただ、おっしゃる通りコードが冗長になるのが難儀ですね。
    実際、私も一時期は ctypes で細やかにコーディングしていたこともありますが、
    現在では win32api は pywin32 から呼ぶ様にしています。

    つきまして、スマートに1行で納めるのは諦めて、
    例外処理を内包した def 関数を定義して使っていこうと思います。

    本当にありがとうございました。

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 89.11%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる