tagCANDY CGI VBレスキュー(花ちゃん)の Visual Basic 6.0用 掲示板 [ツリー表示へ]   [Home]
一括表示(VB6.0)
タイトルホットキーの実装
記事No16307
投稿日: 2017/05/18(Thu) 23:26
投稿者デファイアント
いつもお世話になっております。

とある業務用アプリケーションをつかっています。
あるフィールドはマウスクリックでないと指定、選択できません。

入力の省力化を検討し
キーボード、マウス動作を設定できるツールを見つけました
このツールは、ホットキーがPauseキー(変更可)に割り付けてあり
この業務アプリの操作中でもPauseキーを押すと
キーボード入力、マウス操作を自動で行えます。
ただし、現在のマウス位置からの相対設定ができないため
利用を断念し、自作に挑戦しています

findwindowや Declare Sub mouse_event Lib "user32" などで
動作部分は完成したのですが、
本来の業務アプリケーションから起動することができません

起動方法をwindowsのショートカットにキーを割り付ける方法では
業務アプリから一度離れないといけないので思った動作になりません

Pauseキー、または、その他のキーでこのプログラムを実行させるには
どうすればよいのでしょうか
自作プログラムは起動状態でもかまいません
また、vb6よりは.netのほうが簡単ということであれば
それでもいいです。
(とりあえずツールの使い方がおかしいかもしれませんが
それは別とします)

[ツリー表示へ]
タイトルRe: ホットキーの実装
記事No16308
投稿日: 2017/05/20(Sat) 14:03
投稿者魔界の仮面弁士
> とある業務用アプリケーションをつかっています。
そのアプリは自作したものでしょうか。
それとも他者が作成したもので、自身では改修できないのでしょうか。


> また、vb6よりは.netのほうが簡単ということであれば
> それでもいいです。
とりあえず、両方のパターンで回答しておきます。


> あるフィールドはマウスクリックでないと指定、選択できません。
そのフィールドは、UIAutomation で拾えますか?
https://www.ka-net.org/blog/?p=4946

上記では PowerShell エクステンション版で紹介されていますが、
UI Automation の API 仕様に従い、自作アプリに同種の機構を組み込むこともできます。

あるいは昔ながらの MSAA で拾って
IAccessible::accDoDefaultAction メソッドを呼び出すとか。


> findwindowや Declare Sub mouse_event Lib "user32" などで
> 動作部分は完成したのですが、
既に実装済みなのですね。


他のアプリに対して Pause キーの押下をシミュレートする、という意味ならば、
SendInput API を使うことができるのではないでしょうか。この API は、
共用体配列を渡すことで、「Shift + 右クリック」のような、
キーボードとマウスの組み合わせ操作にも使えるようになっています。


----- VB6 -----
http://hanatyan.sakura.ne.jp/vb6/keyboard05.htm

----- VB.NET -----
http://hanatyan.sakura.ne.jp/vb2005/vb2013keyboard03.htm
http://hanatyan.sakura.ne.jp/yybbs/read.cgi?mode=view&no=79

.NET 実装の場合は、下記の点にも留意しておください。
http://jinblog.at.webry.info/201604/article_1.html



> 本来の業務アプリケーションから起動することができません
「Pause キーが押されたとき」に何かアクションを起こしたいなら、
アクティブなフォームに対して、KeyDown イベントを仕掛けておくことができますが、
そのためには、「業務アプリ側」の改修が必要になってしまいますね。


---- VB6 ----
Option Explicit
Friend Sub NoticePausePressed()
    List1.AddItem FormatDateTime(Now), 0
    List1.NewIndex = 0
End Sub
Private Sub Form_KeyDown(KeyCode As Integer, Shift As Integer)
    If KeyCode = vbKeyPause Then
        NoticePausePressed
    End If
End Sub
Private Sub Form_Load()
    KeyPreview = True
End Sub


---- VB.NET ----
Friend Sub NoticePausePressed()
    ListBox1.Items.Insert(0, Now)
    ListBox1.SelectedIndex = 0
    Shell("notepad", AppWinStyle.NormalNoFocus, False)
End Sub
Private Sub Form1_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
    If e.KeyCode = Keys.Pause Then
        NoticePausePressed()
    End If
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    KeyPreview = True
End Sub


もしも自アプリがアクティブでない場合にもキー押下を拾いたい場合には、
DirectInput を使う方法や RegisterHotKey を使う方法などがあります。
今回は使う出番が無いかもしれませんが。


DirectInput で取得する方法については、下記に具体例があります。
(タイトルは「マウスジェスチャ」になっていますが、キー入力についても書かれています)
http://www.geocities.co.jp/SiliconValley/7406/tips/mouse/index.html

ただ、今となっては、VB6 向けの dx8vb.dll (dx7vb.dll) は入手しにくくなっていますし、
.NET についても、Microsoft.DirectX.DirectInput.dll はサポートが終了しています。

DirectInput を使うのであれば、.NET から SharpDX (あるいは SlimDX)を
利用した方が良いかもしれません。



一方、RegisterHotKey についてはこんな感じです。
後者はホットキーのため、利用可能なキーの組み合わせに制限があります。


---- VB6 ----
http://www.geocities.co.jp/SiliconValley/4805/vbtips/vbtips092.htm
※VB6 では、WM_HOTKEY を捕らえるためにサブクラス化が必要なことに注意が必要です。
 デバッグ時に「停止ボタン」で強制終了させるとクラッシュしてしまいますので、
 必ず、Form_QueryUnload イベントの処理を通過させてから終了させるようにします。



---- VB.NET ----
Private Sub Form1_NoticeHotKey(sender As Object, e As HotKeyEventArgs) Handles Me.NoticeHotKey
    '自作イベント。今回は Pause キーが押されたときにここが実行されます。
End Sub

#Region "Pause キーをホットキーに割り当て"
Public Event NoticeHotKey As EventHandler(Of HotKeyEventArgs)
Private hotKeyAtom As String = "デファイアント"

Protected Overrides Sub OnHandleCreated(e As EventArgs)
    MyBase.OnHandleCreated(e)
    hotKeyId = GlobalAddAtom(hotKeyAtom)
    registered = RegisterHotKey(Handle, hotKeyId, ModifierFlags.None, Keys.Pause)
End Sub


Private Const WM_HOTKEY As Integer = &H312
Protected Overrides Sub WndProc(ByRef m As Message)
    If m.Msg = WM_HOTKEY Then   '
        RaiseEvent NoticeHotKey(Me, New HotKeyEventArgs(m))
    Else
        MyBase.WndProc(m)
    End If
End Sub

Private Declare Unicode Function GlobalAddAtom Lib "kernel32" Alias "GlobalAddAtomW" (ByVal lpString As String) As UShort
Private Declare Function GlobalDeleteAtom Lib "kernel32" (ByVal nAtom As UShort) As UShort
Private Declare Function RegisterHotKey Lib "user32" (hWnd As IntPtr, id As Integer, fsModifiers As ModifierFlags, vk As Keys) As Boolean
Private Declare Function UnregisterHotKey Lib "user32" (hWnd As IntPtr, id As Integer) As Boolean
Private hotKeyId As UShort
Private registered As Boolean
Protected Overrides Sub OnFormClosing(e As FormClosingEventArgs)
    MyBase.OnFormClosing(e)
    If registered AndAlso UnregisterHotKey(Handle, hotKeyId) Then
        registered = False
    End If
    hotKeyId = GlobalDeleteAtom(hotKeyId)
End Sub

<Flags> Private Enum ModifierFlags
    None = 0
    Alt = 1
    Control = 2
    Shift = 4
    Windows = 8
End Enum

Public Class HotKeyEventArgs
    Inherits KeyEventArgs
    Public Property Id As Integer
    Public Sub New(m As Message)
        MyBase.New(ToKeyData(m))
        Id = CInt(m.WParam.ToInt64() And &HFFFFFFFF)
    End Sub
    Private Shared Function ToKeyData(m As Message) As Keys
        If m.Msg <> WM_HOTKEY Then
            Throw New ArgumentOutOfRangeException()
        End If
        'KeyEventArgs.KeyData は、上位ワードが修飾キー、下位ワードがキーコードとなっているが、
        'WM_HOTKEY の LPARAM は、上位ワードがキーコード、下位ワードが修飾キーなので入れ替える
        Dim modifierKeys As ModifierFlags = CType(m.LParam.ToInt64() And Keys.KeyCode, ModifierFlags)
        Dim keyCode As Keys = CType((m.LParam.ToInt64() And Keys.Modifiers) >> 16, Keys)
        Dim keyData As Keys = keyCode
        If (modifierKeys And ModifierFlags.Alt) = ModifierFlags.Alt Then keyData = keyData Or Keys.Alt
        If (modifierKeys And ModifierFlags.Shift) = ModifierFlags.Shift Then keyData = keyData Or Keys.Shift
        If (modifierKeys And ModifierFlags.Control) = ModifierFlags.Control Then keyData = keyData Or Keys.Control
        'If (modifierKeys And ModifierFlags.Windows) = ModifierFlags.Windows Then keyData = keyData
        Return keyData
    End Function
End Class
#End Region

[ツリー表示へ]
タイトルRe^2: ホットキーの実装
記事No16309
投稿日: 2017/05/26(Fri) 15:01
投稿者デファイアント
魔界の仮面弁士様 ご回答ありがとうございました。
主となるアプリは、パッケージされた業務アプリで、
ほとんどのフィールドがタブ移動できるのですが、なぜかある項目だけ
マウスでないと変更できない仕様です。バージョンアップさせるための
謀略としか思えません(結構有名なアプリなのでばれるかも)

愚痴はさておき、結論としてうまい行きました。
>そのフィールドは、UIAutomation で拾えますか?
 windowハンドルが取れたようなので、拾えてるのかな。。

>一方、RegisterHotKey についてはこんな感じです。
>後者はホットキーのため、利用可能なキーの組み合わせに制限があります。
キー配置にはこだわっていないので、今回はこちらのやり方にしました
とりあえず Pause/Breakキー(直)は、
Call RegisterHotKey(Me.hwnd, gintAtom1, 0, VK_PAUSE)
PriScr/SysReqは
Call RegisterHotKey(Me.hwnd, gintAtom2, 0, VK_SNAPSHOT)
にて動作します。

もし、お時間があれば向学のためお願いします
(1)ご紹介いただいたページの説明にある[アトム]とは何でしょうか
(2)このプログラムですが、F-8でステップ実行すると、wndProcで、ステップ以外の
停止、メニュー操作、終了が一切できなくなりますが、どうしてでしょうか

プログラム自身は動作しますので解決とさせていただきます
このページは保存して、そのうち.netでもやってみます
(コールバック関数が出てくると鼻水がとまらなくなりますが)

[ツリー表示へ]
タイトルRe^3: ホットキーの実装
記事No16310
投稿日: 2017/05/26(Fri) 16:06
投稿者魔界の仮面弁士
> >そのフィールドは、UIAutomation で拾えますか?
>  windowハンドルが取れたようなので、拾えてるのかな。。

UI Automation は、Window ハンドル(HWND) とは無関係です。
https://blogs.msdn.microsoft.com/japan_platform_sdkwindows_sdk_support_team_blog/2011/05/26/ui-automation/

UI Automation あるいは MSAA では、フォームのタイトルバーにある
最小化ボタン・最大化ボタン・閉じるボタンさえも個別に拾えますが、
ウィンドウハンドルとしてはこれらは同一のものですよね。


> もし、お時間があれば向学のためお願いします
> (1)ご紹介いただいたページの説明にある[アトム]とは何でしょうか
この場合は、『一意な値 (unique value)』のことです。

追加の説明が必要であれば下記を参照してみてください。
http://eternalwindows.jp/ipc/globalatom/globalatom01.html
https://msdn.microsoft.com/ja-jp/library/cc429867.aspx
https://msdn.microsoft.com/en-us/library/windows/desktop/ms649053.aspx


> (2)このプログラムですが、F-8でステップ実行すると、wndProcで、ステップ以外の
> 停止、メニュー操作、終了が一切できなくなりますが、どうしてでしょうか
「サブクラス化」のためにメッセージループを横取りしているにも関わらず、
一時停止によって、メッセージ応答が阻害されてしまうためです。

大前提として、AddressOf WndProc でサブクラス化している最中は、
一時停止や終了などを行ってはいけません。止める前には、
サブクラス化を解除(先の例では UnHook の呼びだし)せねばなりません。


サブクラス化中に一時停止すると処理が阻害される問題を軽減するため、
大昔は、Microsoft のサイト(Visual Basic Owner's Area)において、
"Debug Object for AddressOf Subclassing" (DbgWProc.Dll) という
ActiveX DLL が公開されていました。

同サイトは現在閉鎖していますが、同 DLL は今でも下記から入手できます。
使用方法については割愛。
http://www.mvps.org/vbvision/_other_files/DbgWProc.zip
http://www.mvps.org/vbvision/dbugproc_dll.htm
http://vb.mvps.org/tools/ControlsAndComponents.asp


> このページは保存して、そのうち.netでもやってみます
> (コールバック関数が出てくると鼻水がとまらなくなりますが)
VB.NET なら WndProc を直接扱えるので、VB6 よりも楽ですよ。

[ツリー表示へ]