SetForegroundWindow でアクティブにならない場合には?

解決


特攻隊長まるるう  2004-02-10 19:14:52  No: 111747

えっと…質問する側に(汗)
API の SetForegroundWindow がどうにも有効でない時の解決方法を
どなたかご存知ないでしょうか?。症状が再現する最小のコード…
とか思ったんですけど…いまいち原因が分からないので再現しなかったら
ゴメンナサイ。ボタン押下2回目でアクティブにならないと思います。
…なんかボタン押下がまずいのかなぁ…ってどうしようもないし…
下のコードではエクセル使ってますが、複数の Form を利用した
プログラムで Show しても Hide しても何も変わらないというので
困ってます。.NET ならプロパティに TopMost があるのでまだなんとか
…で API の SetWindowPos で TopMost 設定…前面には出てくるん
ですが、やっぱりアクティブにならない。苦肉の策として
Public Sub SetForegroundApp(ByVal HandleX As Long)
    SetWindowPos HandleX, conHwndTopmost, 0, 0, 0, 0, conSwpNoSize + conSwpNoMove
    SendKeys "%{TAB}", True
    SetWindowPos HandleX, conHwndNoTopmost, 0, 0, 0, 0, conSwpNoSize + conSwpNoMove
End Sub
…とかしてるんですが…よい方法がありましたら御教え下さい。

'/////////////////////////////////////////////////////////////////////////////////////////
[VB6.0(SP5) Win2000pro(SP4)]
Option Explicit

Private Declare Function SetForegroundWindow Lib "user32.dll" (ByVal hWnd As Long) As Long

Private mXLApp As Excel.Application

Private Sub Command1_Click()
    ' ウィンドウをアクティブに設定
    wResult = SetForegroundWindow(mXLApp.hWnd)
'    DoEvents
    wResult = SetForegroundWindow(Me.hWnd)
End Sub

Private Sub Form_Load()
    Set mXLApp = CreateObject("Excel.Application")

    mXLApp.Workbooks.Add
    mXLApp.Visible = True
    Command1_Click
End Sub

Private Sub Form_Unload(Cancel As Integer)
    mXLApp.Quit
    Set mXLApp = Nothing
End Sub
'/////////////////////////////////////////////////////////////////////////////////////////
'参考
'Private Declare Function SetWindowPos Lib "user32" (ByVal hWnd As Long, _
'                                            ByVal hWndInsertAfter As Long, _
'                                            ByVal x As Long, ByVal y As Long, _
'                                            ByVal cx As Long, ByVal cy As Long, _
'                                            ByVal wFlags As Long _
'                                            ) As Long
'Const conHwndTopmost = -1
'Const conHwndNoTopmost = -2
'Const conSwpNoSize = &H1
'Const conSwpNoMove = &H2


岡田 之仁  2004-02-10 19:47:09  No: 111748

やりたいことと、何が問題なのか、明記してもらわないと・・・

多分、コードから推察するに、アクティブにしたはずのVBの
フォームが、エクセルのウィンドウの後ろに隠れてしまう・・・
と言うことだと思うのですが・・・

●  まず私のWinXPでは、発生しませんでした。
    多分、ウィンドウメッセージの発生順番と、CPU等の
    性能とによるものではないかと思います・・・

解決策になるかどうか・・・

Private Const HWND_TOP = 0
Private Const HWND_TOPMOST = -1
Private Const SWP_SHOWWINDOW = &H40

Private Type RECT
        Left As Long
        Top As Long
        Right As Long
        Bottom As Long
End Type

Private Declare Function SetWindowPos Lib "user32" (ByVal hwnd As Long, ByVal hWndInsertAfter As Long, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
Private Declare Function GetWindowRect Lib "user32" (ByVal hwnd As Long, lpRect As RECT) As Long

Private mXLApp As Excel.Application

Private rt As RECT

Private Sub Command1_Click()
    Dim wResult As Long
    
    wResult = GetWindowRect(mXLApp.hwnd, rt)
    wResult = SetWindowPos(mXLApp.hwnd, HWND_TOPMOST, rt.Left, rt.Top, rt.Right, rt.Bottom, SWP_SHOWWINDOW)
    'DoEvents
    wResult = GetWindowRect(Me.hwnd, rt)
    wResult = SetWindowPos(Me.hwnd, HWND_TOPMOST, rt.Left, rt.Top, (rt.Right - rt.Left), (rt.Bottom - rt.Top), SWP_SHOWWINDOW)

End Sub

これではダメですか?

以上。


岡田 之仁  2004-02-10 19:48:41  No: 111749

すいません・・・タイトルに書いてましたネ!
あしからず・・・
以上。


魔界の仮面弁士  2004-02-10 19:58:21  No: 111750

まず、SDKでSetForegroundWindowについて調べてみてください。
フォアグラウンドウィンドウを設定可能な条件が書かれています。

Win95/NT4までは、SetForegroundWindowだけでフォアグラウンドウィンドウを
切り替えられましたが、Win98/2000移行では、切り替え可能なプロセスは、
システムにより制限されるようになっています。

# メニュー選択や文字の入力中に、勝手にウィンドウが切り替えられてしまい、
# 作業中の処理が中断してしまうことを防ぐための仕様変更です。

で。処理の流れとしては、おそらくこんな感じになると思います。
手元にVB6が無いので、デバッグしておらず、処理のイメージだけでしか
書けませんが、特攻隊長まるるうさんなら、感じは掴めるかと思います。

'=========================
'現在ユーザーが作業しているウィンドウを取得
h = GetForegroundWindow()

'フォアグラウンドウィンドウを作成したスレッドのIDを取得
ThreadID1 = GetWindowThreadProcessId(h, ByVal 0&)

'目的のウィンドウを作成したスレッドのIDを取得
ThreadID2 = GetCurrentThreadId()

'現在の入力状態を目的のスレッドにアタッチ
AttachThreadInput ThreadID2, ThreadID1, 1

'現在の[フォアグラウンド ロック タイムアウト]の設定を取得
SystemParametersInfo SPI_GETFOREGROUNDLOCKTIMEOUT, 0, VarPtr(buf), 0

'設定を 0ms に変更
SystemParametersInfo SPI_SETFOREGROUNDLOCKTIMEOUT, 0, ByVal 0&, 0

'ようやく、本命の処理のお出まし
SetForegroundWindow hWnd

'設定を元に戻して…
SystemParametersInfo SPI_SETFOREGROUNDLOCKTIMEOUT, 0, VarPtr(buf), 0

'デタッチしておしまい
AttachThreadInput ThreadID2, ThreadID1, 0


魔界の仮面弁士  2004-02-10 20:06:09  No: 111751

GetCurrentThreadId APIの部分は、App.ThreadID で十分かも。


魔界の仮面弁士  2004-02-10 20:45:06  No: 111752

》特攻隊長まるるうさん
>>>>> ボタン押下2回目でアクティブにならないと思います。
当方のXPでは、アクティブになりました。(^^;

》自分
>> 手元にVB6が無いので、デバッグしておらず
VB6版のソースを書いてみました。
http://www.ocv.ne.jp/~oratorio/junk/Sample/30/SetForegroundWindow.txt

》岡田 之仁さん
>>>> 多分、コードから推察するに、アクティブにしたはずのVBの
>>>> フォームが、エクセルのウィンドウの後ろに隠れてしまう・・・
>>>> と言うことだと思うのですが・・・

あぁ……なるほど。
もし、そういう話だとしたら、私の回答は的外れだったかも知れません。m(_ _)m


特攻隊長まるるう  2004-02-17 23:46:21  No: 111753

自分で回答してる時は『説明が分かりにくいなぁ』…とか思ってるくせに
質問側に回るとやっぱり意図を伝えきれていなかったという…申し訳ないです。
ご指摘の通り
>多分、コードから推察するに、アクティブにしたはずのVBの
>フォームが、エクセルのウィンドウの後ろに隠れてしまう・・・
現象でした。ボタン押下1回目は問題なく、2回目でエクセルが
前面に来た後、フォームが前面に出てこない…よく見ると
タスクバーでこのアプリケーションが点滅しておりました。
魔界の仮面弁士さん指摘の SetForegroundWindow の仕様ですね。

http://support.microsoft.com/default.aspx?scid=kb;ja;227043

ざっとは見てたんですが、アプリケーションがフォアグラウンドウィンドウ
になってればいいものと思い込んでいて詳しく調べてませんでした。

余談ですが、CPU は .NET も動かすので 1.60GHz です。
最初のコードでもコード内で Command1_Click を繰り返して呼んだ場合は
問題なかったりします(汗)。2回目で失敗してタスクバーでフォームを
選択し前面に表示。…で1(3)回目は成功2(4)回目で失敗...(  _)_
ボタン押下か SetForegroundWindow の1回目の呼び出しで『ユーザーが
作業している』と判断されてアクティブにならないのでしょうね。

岡田 之仁さんのウィンドウの寸法を取得して SetWindowPos する方法。
問題なく動作します。最初にエクセルを表示した時にエクセルがアクティブ
ウィンドウになりますが、フォームが自分に SetFocus すれば問題ありません。
フォアグラウンドウィンドウが変更されないので(?)質問とは少しずれる
かもしれませんが解決です。

魔界の仮面弁士さんのフォアグラウンドロックタイムアウトの設定でも
うまく動作しました。

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


※返信する前に利用規約をご確認ください。








  このエントリーをはてなブックマークに追加