トップ回答者
Excelイベントからフォームをアクティブにしたい

質問
-
Excelファイルに配置してあるコマンドボタンが押された時、.NETのフォームを最前面に表示にするプログラムを作成しています。
コードは以下の通りですが、ボタンを押下した際、
フォームが最前面に表示されずタスクバーでアイコンが更新通知状態(黄色に点滅)となってしまいます。
Imports Microsoft.Office.Interop Imports Microsoft.Vbe.Interop Imports Microsoft.Office.Tools.Excel Module Module1 Private xls As New Excel.Application Private xlsWBook As Excel.Workbook Private xlsSheet As Excel.Worksheet Private cmbFrmAct As _ Microsoft.Vbe.Interop.Forms.CommandButton Declare Function SetForegroundWindow Lib "user32" _ (ByVal hwnd As Long) As Long Declare Function FindWindow Lib "user32" _ (ByVal wtext As String, _ ByVal ntext As String) As Long Declare Function GetWindowThreadProcessId _ Lib "user32"(ByVal hWnd As Long, _ ByRef lpdwProcessId As Long) As Long Declare Function GetCurrentThreadId _ Lib "Kernel32" () As Long Declare Function AttachThreadInput Lib "user32" _ (ByVal idAttach As Long, _ ByVal idAttachTo As Long, _ ByVal fAttach As Long) As Long Public Sub xlsOpen() xlsWBook = xls.Workbooks.Open("エクセルファイル名") xlsSheet = xlsWBook.Worksheets("シート名") cmbFrmAct = CType(xlsSheet.エクセルファイルのコマンドボタン名, Microsoft.Vbe.Interop.Forms.CommandButton) AddHandler cmbFrmAct.Click, AddressOf _ cmbFrmAct_Click xls.Visible = True End Sub Private Sub cmbFrmAct_Click()'エクセルファイル内のコマンドボタン押下時の処理 Dim h As Long = _ FindWindow(FormMain.Text, vbNullString) Dim tFrm As Long = _ GetWindowThreadProcessId(h, 0&) Dim tMain As Long = GetCurrentThreadId()
AttachThreadInput(tMain, tFrm, 1&) '最前面にある画面のプロセスにアタッチ AppActivate(FormMain.Text) SetForegroundWindow(FormMain.Handle) AttachThreadInput(tMain, tFrm, 0&) End Sub End Module Public Class FormMain Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click Module1.xlsOpen() End Sub End Class
不思議な事に、ボタンを連打していると4~5回に1回はフォームが最前面に表示されます。
インターネットで検索した情報から以下の処置を試してみましたがいずれも上手く行かず、泣く泣く質問させて頂いた次第です。
・APIの画面操作関数を一通り実行
・最前面になっているウィンドウのプロセスにアタッチし、更新通知状態にならないようにする方法
・FormMainのTopMostプロパティをTrueにする方法など
解決策など解ればお教えて頂ければ幸いです。
よろしくお願いいたします。
- 編集済み YamCha03 2013年12月17日 6:02
回答
-
少し古い質問でもう解決済みかもしれませんが・・・
プログラムによる最前面ウィンドウの変更操作は、Windowsの仕様により簡単に操作できないようになっています。
以下記事の「最前面ウィンドウの扱い」の項をご覧ください。この記事は XP が対象ですので、Windows 7・8 以降より仕様が厳しくなっている可能性があります。
Windows XP 環境への既存アプリケーションの移行
それでもどうしても最前面ウィンドウの操作を行いたいなら、以下 DOBONさんの記事を参考にするといいと思います。ただし記事内で述べておられますが、記事中の具体例(フォアグラウンドにするための処理をてんこ盛りにしたコード)を実行しても、最前面化できない場合があるとのことです。
外部アプリケーションのウィンドウをアクティブにする
ひらぽん http://d.hatena.ne.jp/hilapon/
すべての返信
-
少し古い質問でもう解決済みかもしれませんが・・・
プログラムによる最前面ウィンドウの変更操作は、Windowsの仕様により簡単に操作できないようになっています。
以下記事の「最前面ウィンドウの扱い」の項をご覧ください。この記事は XP が対象ですので、Windows 7・8 以降より仕様が厳しくなっている可能性があります。
Windows XP 環境への既存アプリケーションの移行
それでもどうしても最前面ウィンドウの操作を行いたいなら、以下 DOBONさんの記事を参考にするといいと思います。ただし記事内で述べておられますが、記事中の具体例(フォアグラウンドにするための処理をてんこ盛りにしたコード)を実行しても、最前面化できない場合があるとのことです。
外部アプリケーションのウィンドウをアクティブにする
ひらぽん http://d.hatena.ne.jp/hilapon/
-
お返事どうも有難うございます。両ページとも拝見させて頂きました。
当方その後色々試していましたが、Windows7のエアロなタスクバー(?)のアイコンで、2画面以上まとまっているものをアクティブにしようとした際に更新通知がされてしまうことが分かってきました。アクティブにする画面以外を非表示にした後、AppActivateをした所高確率で表示されるようになりました。
DOBONさんのソースも試させて頂きましたが動作は変わらず....、
ただ、試している中でわかってきた事がいくつかありました。
・Excelファイルのコマンドボタン押下処理(cmbFrmAct_Click)は、<別スレッド>にて実行される
・それによりメイン処理で使用するWindowsフォームのハンドルを正常に取得出来ていない
プロセスにアタッチする際に使用したハンドルが正常なものではなかったため、アタッチ自体が上手くいっていないようでした。InvokeやShellScriptなどでメイン関数に処理を戻した後アタッチをしてみた所、最前面に表示される確率は多少上がったように思います。
が、どうしても100%表示出来る方法には至っておりません。
これだけのAPI関数を試しても確実な方法...というのは難しそうでしたので、いよいよ打つ手無しです。ご紹介頂いた記事の通り、基本はWindowsの仕様を受け入れる方向で進めていきたいと思います。
- 編集済み YamCha03 2014年1月23日 8:47