< WSH よりクリップボードを使う | 非同期イベント発生コントロール >

February 23, 2005

ウインドウを一時的にアクティブにして最前面に持ってくる

後ろで動いてるアプリを手前に持ってくる関数です。いつも作るうえに、毎回調べないと作れないので載せておきます。

ちなみに、ウインドウが「常に」最前面に表示されるようにするには、SetWindowPos 関数で HWND_TOPMOST を渡します。でもこれはググればごろごろサンプル出てくるんですけどね。

Option Explicit

Private Declare Function GetWindowThreadProcessId Lib "user32" (ByVal hWnd As Long, lpdwProcessId As Long) As Long
Private Declare Function GetForegroundWindow Lib "user32" () As Long
Private Declare Function AttachThreadInput Lib "user32" (ByVal idAttach As Long, ByVal idAttachTo As Long, ByVal fAttach As Long) As Long
Private Declare Function BringWindowToTop Lib "user32.dll" (ByVal hWnd As Long) As Long
Private Declare Function SetForegroundWindow Lib "user32" (ByVal hWnd As Long) As Long

Private Const FORMAT_MESSAGE_FROM_SYSTEM     As Long = &H1000&
Private Const FORMAT_MESSAGE_IGNORE_INSERTS  As Long = &H200&
Private Declare Function FormatMessage Lib "kernel32" Alias "FormatMessageA" (ByVal dwFlags As Long, lpSource As Any, ByVal dwMessageId As Long, ByVal dwLanguageId As Long, lpBuffer As Any, ByVal nSize As Long, Arguments As Long) As Long

'******************************************************************
'プロシージャ名   :ForceSetForegroundWindow
'説明             :フォームを強制的にアクティブにする
'引数             :pfrmTarget           I   アクティブにするウインドウ
'******************************************************************
Public Sub ForceSetForegroundWindow(pfrmTarget As VB.Form)
'============= Declare ==============
Dim lngRet              As Long
Dim hWndActive          As OLE_HANDLE
Dim hThreadActive       As OLE_HANDLE

'============= Initiarize ===========
On Error GoTo ERR_HANDLER_00

'============= Main =================

    '' 現在アクティブなスレッドを取得する
    hWndActive = GetForegroundWindow()
    If (0& = hWndActive) Then
        Call DllErrRaise(Err.LastDllError, "GetForegroundWindow")
    End If
    hThreadActive = GetWindowThreadProcessId(hWndActive, ByVal 0&)
    If (0& = hThreadActive) Then
        Call DllErrRaise(Err.LastDllError, "GetWindowThreadProcessId")
        
    '' 自分自身がアクティブなら、ただ最前面にするだけ
    ElseIf App.ThreadID = hThreadActive Then
        lngRet = SetForegroundWindow(pfrmTarget.hWnd)
        If (0& = lngRet) Then
            Call DllErrRaise(Err.LastDllError, "SetForegroundWindow")
        End If
        
    '' 自分自身がアクティブでなければ、アクティブなスレッドにアタッチする
    Else
        lngRet = AttachThreadInput(App.ThreadID, hThreadActive, 1&)
        If (0& = lngRet) Then
            Call DllErrRaise(Err.LastDllError, "AttachThreadInput")
        Else
On Error GoTo ERR_HANDLER_10
            '' ウインドウをアクティブにする
            lngRet = SetForegroundWindow(pfrmTarget.hWnd)
            If (0& = lngRet) Then
                Call DllErrRaise(Err.LastDllError, "SetForegroundWindow")
            End If
            
            '' スレッドをデタッチする
            lngRet = AttachThreadInput(App.ThreadID, hThreadActive, 0&)
            If (0& = lngRet) Then
                Call DllErrRaise(Err.LastDllError, "AttachThreadInput")
            End If
On Error GoTo ERR_HANDLER_00
        End If
    End If

'============= Release ==============
RESUME_00:
    Exit Sub
'============= Error ================
ERR_HANDLER_10:
    '' スレッドをデタッチする
    Call AttachThreadInput(App.ThreadID, hThreadActive, 0&)
ERR_HANDLER_00:
    Call Err.Raise(Err.Number, "basCommonFunc.ForceSetForegroundWindow ← " & Err.Source, Err.Description, Err.HelpFile, Err.HelpContext)

End Sub

'******************************************************************
'プロシージャ名:  :DllErrRaise
'説明:            :Err.LastDllErrorよりエラーを発生する
'引数:            :plngLastDllError   I   Err.LastDllError
'                 :pstrErrSource      I   エラーソース文字列
'******************************************************************
Public Sub DllErrRaise(ByVal plngLastDllError As Long, pstrErrSource As String)
'============= Declare ==============
Const clngMAX_BUFFER            As Long = 1024&
Dim bytBuf(clngMAX_BUFFER - 1&) As Byte 'バッファ
Dim lngRet                      As Long 'APIの戻り値
Dim strErrDescription           As String

'============= Initiarize ===========
    If plngLastDllError = 0& Then
        Exit Sub
    End If
    Erase bytBuf
    strErrDescription = ""
'============= Main =================
    
    ' エラー値に対応するメッセージを取得する
    lngRet = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM Or FORMAT_MESSAGE_IGNORE_INSERTS, _
                            ByVal 0&, _
                            plngLastDllError, _
                            0&, _
                            bytBuf(0), _
                            clngMAX_BUFFER, _
                            ByVal 0&)
    If lngRet Then
        strErrDescription = StrConv(LeftB(bytBuf, lngRet - 2&), vbUnicode)
    End If
    
    ' エラーを発生する
    Call Err.Raise(plngLastDllError Or vbObjectError, pstrErrSource, strErrDescription)
    
End Sub

トラックバック

このエントリーにトラックバック:
http://frog.raindrop.jp/cgi-bin/mt/mt-tb.cgi/836

コメント

大変参考になりました!
ありがとうございますm(_ _)m

> kk さん
いらっしゃいませ。お役に立てましたでしょうか。

また来てくださいね (^-^)/

こんばんわ。
ExcelVBAを裏で動かし、他のアプリからテキストデータを裏のExcelシートに貼り付けています。マクロでエラーが出たときに、Excelを最前面に表示し、MsgBoxの表示を確認できるようにするために使わせてもらおうと思います。
そこで、マクロの標準モジュールにコードをコピーして、UserFormから呼び出すようにしたのですが、”ユーザー定義型は定義されていません。”というコンパイルエラーが出てしまいました。VBAそのものが初心者なので、対応がわかりません。
ご教示よろしくお願いします。

> terasan さん
Excel VBA で動かしたということは、多分 VB.Form が引っ掛かったんでしょうね。
このソースコードは VB6 時代の物で、
VB.Form は Visual Basic objects and procedures (VB6.OLB) という
ライブラリで定義されているものなので、残念ながら Excel のウインドウや UserForm とは互換性がありません。
また、App.ThreadID を参照している部分もそのままでは使用できないと思います。

生憎、今環境がなくて試してみていないんですが、
Excel マクロから Excel 自身をアクティブにするのであれば、
Call AppActivate(Application.Caption)
が利用できる気がするんですけど、いかがでしょうか?

おはようございます。
早速ご回答いただきありがとうございました。
AppActivate Application.Caption
を実行してみましたが、Excelのウインドウは他のアプリの裏になったままでした。Excelのキャプションをアクティブにするということと、モニター上の表示順位は無関係なんでしょうかね。
力不足でよくわかりません。

こんにちわ。
いろいろ探索してみた結果、以下の方のサンプルで解決できました。ありがとうございました。御礼とご報告まで。
http://naruhodo.television.co.jp/qa4876776.html?order=DESC&by=datetime

こんにちは、またブログ覗かせていただきました。また、遊びに来ま~す。よろしくお願いします

コメントする

※ コメントスパム対策のため、コメント本文はおはよう、こんにちわ、こんばんわのいずれかより始めるようにしてください。

name:
email:

※ 必要ですが、表示しません。

url:
情報を保存する ?