54140


調べても子ウィンドウのハンドルを調べてそこをクリックする、という方法しか出てきません。  
ウィンドウハンドルはSpy++などで調べても1つしかないため、そのウィンドウの中の任意の箇所をx,yで指定してクリックさせたいです。  
pyautoguiなどを使えばクリックさせることは出来ますが、対象ウィンドウがアクティブになってしまいます。  
対象ウィンドウのハンドル指定でスペースキーなどを非アクティブなまま送ることは成功しているのですが、これのクリックを任意の場所にさせることは出来ないのでしょうか？

試したコードは以下のようなものです。

[code]

    l_param = win32api.MAKELONG(1732,1010)
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, 0, l_param)
    win32api.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, l_param)
    
[/code]

[code]

    l_param = win32api.MAKELONG(1732,1010)
    win32gui.PostMessage(hwnd, win32con.WM_LBUTTONDOWN, win32con.MK_LBUTTON, l_param)
    win32gui.PostMessage(hwnd, win32con.WM_LBUTTONUP, 0, l_param)
    
[/code]

[code]

    l_param = win32api.MAKELONG(1732,1010)
    win32api.PostMessage(hwnd, win32con.BM_CLICK, l_param, 0)
    
[/code]

こういったググって出てきたようなクリックさせる系は大体試しましたが、出来ませんでした。  
PostMessageをSendMessageにしても無理です。  
ちなみに  
`win32gui.SendMessage(hwnd, win32con.WM_ACTIVATE, win32con.WA_ACTIVE, 0)`  
これは効きましたし、前述したようにスペースキーは

[code]

    win32api.PostMessage(hwnd, win32con.WM_KEYDOWN, win32con.VK_SPACE, 0)
    win32api.PostMessage(hwnd, win32con.WM_KEYUP, win32con.VK_SPACE, 0)
    
[/code]

これで正常に動くのでハンドルは合っているし、PostMessageで非アクティブなままキーを送ることは出来ているかと思います。

「任意の場所をクリックさせる」という部分がネックなような気がするのですが  
l_paramもいろいろな方法で指定しましたが同じく無理でした。  
不可能なのでしょうか？  
win32apiではなくても、pythonで特定ハンドルを非アクティブなまま任意箇所左クリックする方法があれば教授お願いします。

環境:  
OS Windows7 64bit  
Python 3.7.1


CC BY-SA 4.0
2019-04-13T08:03:43.707
54136
2019-04-13T11:25:07.123
2019-04-13T08:58:00.810
31469
31469
question
python winapi
pythonでwin32api.PostMessage()を使って任意の箇所をウィンドウを非アクティブでクリックしたい


おそらく、以下の記事が同じ話題を扱っています。  
解決はされていませんが、コメント欄で好評なものをピックアップして翻訳します。  
[How to keep window inactive on simulated
clicks?](https://stackoverflow.com/q/14085286/9014308)  
質問

> 私はC ++で非アクティブウィンドウのクリックをシミュレートするプログラムを作成しました。
[code]

>     PostMessage (z, WM_LBUTTONDOWN, 0,MAKELONG(t.left+x,t.top+y));
>  
[/code]

>
> しかしクリックするたびにウィンドウがアクティブになり、ウィンドウが一番上に移動します。  
>  ウィンドウを非アクティブにしておく方法、またはクリックする方法はありますか。  
>  私は`SetWindowPos(z ,
> HWND_BOTTOM,....)`でウィンドウをzオーダーリストの一番下にするようにしていましたが、それでもアクティブになります。  
>  編集：ウィンドウはゲームコンソールです

コメント

>   * これは当然のことですが、マウスを使ってウィンドウをクリックしたときにも起こります。  
>  いいえ、プログラムのソースがない場合は、その動作を変更することはできません。
>
>   *
> ターゲットウィンドウを制御する場合、WM_MOUSEACTIVATEメッセージを処理する（MA_NOACTIVATEを返す）ことによってウィンドウがアクティブになるのを防げます。
>
>   * PostMessageで入力を合成するのは間違った使い方です。  
>  代わりにSendInputを使用してください。  
>  あなたがソースコードを持っていなくても、Jonathan
> Potterがあなたが所有していないウィンドウを制御することができると提案したことを拡張します。  
>  最初にコードをターゲットプロセスに送り込む必要があります（例えばフックを使って）。  
>  次のステップは、ターゲットウィンドウをサブクラス化することです。  
>  サブクラス化したあなたのWndProcにて、単にWM_MOUSEACTIVATEメッセージを無視することで制御できます。  
>  ただし、これが機能するとは限りません。
>
>

フックやサブクラス化は(操作側ゲーム側のどちらでも)プログラムを終了する時には元に戻しておく必要があるでしょう。

* * *

他に類似の記事でこんなのがあります。  
こちらは対象ウィンドウの拡張スタイルを書き換える方法です。  
操作側プログラムが終了する際には戻しておくのが望ましいですが、それをしなくてもゲーム側プログラムを再起動すれば元に戻ります。  
(人の操作でもアクティブにならなくなる、というのはこの拡張スタイルを戻さずに操作側を終了し、ゲーム側は動作を継続する場合のことです。)  
[Avoid application activation and focus in when clicking buttons on it -
Windows API or Qt](https://stackoverflow.com/q/18662031/9014308)

> Windowsのflags（#include
> ）を使用すると、ウィンドウをクリックしたときに、そのウィンドウを無効化したりフォーカス解除したりすることができます。ウィンドウが作成されて表示された後は、次のものを使用する必要があります。
[code]

>     HWND winHandle = (HWND)winId();
>     ShowWindow(winHandle, SW_HIDE);
>     SetWindowLong(winHandle, GWL_EXSTYLE, GetWindowLong(winHandle,
> GWL_EXSTYLE) | WS_EX_NOACTIVATE | WS_EX_APPWINDOW);
>     ShowWindow(winHandle, SW_SHOW);
>  
[/code]

[常に非アクティブなウインドウを作る方法](https://culage.hatenablog.com/entry/20080612/p1)

> 最初に言うが、これは絶対にアクティブにならないウインドウを作る方法ではない。  
>  （タスクバーでこのウインドウを選択した場合などはアクティブになる）  
>
> 正確には、『ウインドウ上で何か操作をしても、アクティブ化されないウインドウ』を作る方法。例えば、このウインドウのボタンを押しても、このウインドウはアクティブにならずに、元々アクティブだったウインドウがそのままアクティブでありつづける、という動きをする。サンプルコードはc#だけれども、内容はwinapiなので言語は問わずに実装できると思う。

以下途中を抜粋

>
[code]

>     // アクティブ化されないスタイル設定
>     private void setNotActiveWindow(IntPtr hWnd)
>     {
>         // 現在のスタイルを取得
>         UInt32 unSyle = GetWindowLong(hWnd, GWL.EXSTYLE);
>  
>         // キャプションのスタイルを削除
>         unSyle = (unSyle | WS_EX_NOACTIVATE);
>  
>         // スタイルを反映
>         UInt32 unret = SetWindowLong(hWnd, GWL.EXSTYLE, unSyle);
>  
>         // ウィンドウを再描画
>         SetWindowPos(hWnd, IntPtr.Zero,
>         0, 0, 0, 0,
>         SWP.NOMOVE | SWP.NOSIZE |
>         SWP.NOZORDER | SWP.FRAMECHANGED);
>     }
>  
[/code]

ただし、ゲーム側プログラムのメインウィンドウだけではなく、ボタン等の全部の部品も対策する必要があるかもしれません。

[【C#】アクティブにならないウィンドウを作る](http://mist-
elisha.hatenablog.com/entry/2014/07/10/213808)

> このままではフォームはアクティブにならないが  
>  配置されたコントロールはアクティブになってしまい  
>  結果、メインフォームからフォーカスが離れてしまうので一工夫する。
[code]

>     class MyButton : Button
>     {
>         public MyButton()
>         {
>             base.SetStyle(ControlStyles.Selectable, false);
>         }
>     }
>  
[/code]

しかも、ゲームということは、クリックしたいオブジェクト(のウィンドウ)がどんどん変わる(作られては消えてゆく)可能性も考えられるので、対策は事実上不可能かもしれませんね。


CC BY-SA 4.0
2019-04-13T11:10:12.180
54140
2019-04-13T11:25:07.123
2019-04-13T11:25:07.123
26370
26370
54136
answer