none
[Excel 2016] 特定の Excel ウィンドウ上のユーザーフォームがアクティブだったかどうかを調べる方法はないでしょうか RRS feed

  • 質問

  • 例えば、ユーザーフォームをモードレスで表示させてそのユーザーフォーム上にあるボタンなどの
    コントロールから新規ブックを作成したり (Workbooks.Add) 開いたり (Workbooks.Open) すると
    その新しいうぶっくのウィンドウが勝手にアクティブになってしまいますよね。
    (本当はアクティブにしないで新規作成や開けたらいいんですけど仕様のようなのであきらめてますが)

    そのあと手動でタスクバーや Ctrl + Tab によって元のウィンドウをアクティブにするとユーザーフォームは
    (コントロール上のフォーカスは消えてしまうものの) アクティブなままです。

    しかし、新規作成したブックを閉じたり、ThisWorkbook.Activate などを使ってアクティブに戻すと
    ワークブックのウィンドウの方がアクティブになってしまいます。

    具体的にはこれを防止、もしくは対策する方法が知りたいです。
    (また、ユーザーフォームがアクティブでなかったときはアクティブにしないようにしたい)

    そこでひとつ案として浮かんだのがユーザーフォームが表示されているウィンドウのユーザーフォームが
    アクティブだったかどうかを調べる方法がないかと、Spy++ で Excel ウィンドウやユーザーフォームの
    メッセージを調べたり、SetWindowPos で Z オーダーを変えてみたりなどの試行錯誤したものの、
    うまくいきません。
    (SetWindowPos で Z オーダーを変更してもアクティブなウィンドウやフォーカスのあるウィンドウは
    変わらないようですね)

    なにか、いい方法はないでしょうか。

    タスクバーからアクティブにした時の動作が API で再現できればそれが一番いいとは思いますが。


    2019年4月4日 6:58

回答

  • いろいろと API を試されているようですので、試行済みかもしれませんが、

    指定したウィンドウをアクティブウィンドウにするには SetForegroundWindow 関数はつかえないでしょうか?

    また、アクティブウィンドウのハンドルを取得するには GetActiveWindow 関数はつかえないでしょうか?
    • 回答としてマーク infade 2019年4月16日 8:16
    2019年4月4日 7:27
  • Excel2013でちょっと試しただけなんですけど、適当なテキストボックス配置しておいて、ボタンを押した際に一度アクティブにした後、押したボタンをアクティブにすることで、フォームのフォーカスを維持できませんか?

    Private Sub CommandButton1_Click()
        Application.Workbooks.Add
        TextBox1.SetFocus
        CommandButton1.SetFocus
    End Sub

    • 回答としてマーク infade 2019年4月16日 8:16
    2019年4月5日 20:24
  • みなさま、お返事ありがとうございます。

    その後、またいろいろと試行錯誤したり調べたところ、ずばりそのものといえる
    GetLastActivePopup 関数でなんとかできました。

    Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long
    Private Declare PtrSafe Function GetLastActivePopup Lib "user32" (ByVal hwndOwnder As LongPtr) As LongPtr
    Private Declare PtrSafe Function WindowFromAccessibleObject Lib "oleacc" ( _
                                            ByVal pacc As Object, ByRef phwnd As LongPtr) As Long
    Private Declare PtrSafe Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hWnd As LongPtr, ByVal wFlag As Long) As LongPtr
    
    Private Const GW_OWNER              As Integer = 4
    
    Private Const HWND_TOP              As Integer = 0
    Private Const SWP_NOACTIVATE        As Integer = &H10
    Private Const SWP_NOSIZE            As Integer = &H1
    Private Const SWP_NOMOVE            As Integer = &H2
    Private Const SWP_NOSENDCHANGING    As Integer = &H400
    
    Private Sub CommandButton1_Click()
        Dim wb As Workbook
        Dim hWnd As LongPtr
        Set wb = Workbooks.Add
        hWnd = GetLastActivePopup(GetNextWindow(GetHwnd, GW_OWNER))
        SetWindowPos hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOSENDCHANGING
        SetForegroundWindow hWnd
        wb.Close
    End Sub
    
    Private Sub CommandButton2_Click()
        SetForegroundWindow GetNextWindow(GetHwnd, GW_OWNER)
        CommandButton1_Click
    End Sub
    Function GetHwnd() As LongPtr WindowFromAccessibleObject Me, GetHwnd End Function

    • 回答としてマーク infade 2019年4月16日 8:16
    2019年4月16日 8:16

すべての返信

  • いろいろと API を試されているようですので、試行済みかもしれませんが、

    指定したウィンドウをアクティブウィンドウにするには SetForegroundWindow 関数はつかえないでしょうか?

    また、アクティブウィンドウのハンドルを取得するには GetActiveWindow 関数はつかえないでしょうか?
    • 回答としてマーク infade 2019年4月16日 8:16
    2019年4月4日 7:27
  • Excel2013でちょっと試しただけなんですけど、適当なテキストボックス配置しておいて、ボタンを押した際に一度アクティブにした後、押したボタンをアクティブにすることで、フォームのフォーカスを維持できませんか?

    Private Sub CommandButton1_Click()
        Application.Workbooks.Add
        TextBox1.SetFocus
        CommandButton1.SetFocus
    End Sub

    • 回答としてマーク infade 2019年4月16日 8:16
    2019年4月5日 20:24
  • みなさま、お返事ありがとうございます。

    その後、またいろいろと試行錯誤したり調べたところ、ずばりそのものといえる
    GetLastActivePopup 関数でなんとかできました。

    Private Declare PtrSafe Function SetWindowPos Lib "user32" (ByVal hWnd As LongPtr, ByVal hWndInsertAfter As LongPtr, ByVal x As Long, ByVal y As Long, ByVal cx As Long, ByVal cy As Long, ByVal wFlags As Long) As Long
    Private Declare PtrSafe Function SetForegroundWindow Lib "user32" (ByVal hWnd As LongPtr) As Long
    Private Declare PtrSafe Function GetLastActivePopup Lib "user32" (ByVal hwndOwnder As LongPtr) As LongPtr
    Private Declare PtrSafe Function WindowFromAccessibleObject Lib "oleacc" ( _
                                            ByVal pacc As Object, ByRef phwnd As LongPtr) As Long
    Private Declare PtrSafe Function GetNextWindow Lib "user32" Alias "GetWindow" (ByVal hWnd As LongPtr, ByVal wFlag As Long) As LongPtr
    
    Private Const GW_OWNER              As Integer = 4
    
    Private Const HWND_TOP              As Integer = 0
    Private Const SWP_NOACTIVATE        As Integer = &H10
    Private Const SWP_NOSIZE            As Integer = &H1
    Private Const SWP_NOMOVE            As Integer = &H2
    Private Const SWP_NOSENDCHANGING    As Integer = &H400
    
    Private Sub CommandButton1_Click()
        Dim wb As Workbook
        Dim hWnd As LongPtr
        Set wb = Workbooks.Add
        hWnd = GetLastActivePopup(GetNextWindow(GetHwnd, GW_OWNER))
        SetWindowPos hWnd, HWND_TOP, 0, 0, 0, 0, SWP_NOACTIVATE Or SWP_NOSIZE Or SWP_NOMOVE Or SWP_NOSENDCHANGING
        SetForegroundWindow hWnd
        wb.Close
    End Sub
    
    Private Sub CommandButton2_Click()
        SetForegroundWindow GetNextWindow(GetHwnd, GW_OWNER)
        CommandButton1_Click
    End Sub
    Function GetHwnd() As LongPtr WindowFromAccessibleObject Me, GetHwnd End Function

    • 回答としてマーク infade 2019年4月16日 8:16
    2019年4月16日 8:16