@@ -29,35 +29,141 @@ public static class ActionUtil
2929
3030 #endregion
3131
32+ private const int WM_APPCOMMAND = 0x0319 ;
33+ private const int APPCOMMAND_MEDIA_NEXTTRACK = 11 ;
34+ private const int APPCOMMAND_MEDIA_PREVIOUSTRACK = 12 ;
35+ private const int APPCOMMAND_MEDIA_STOP = 13 ;
36+ private const int APPCOMMAND_MEDIA_PLAY_PAUSE = 14 ;
37+
38+ private const uint INPUT_KEYBOARD = 1 ;
39+ private const uint KEYEVENTF_EXTENDEDKEY = 0x0001 ;
3240 private const uint KEYEVENTF_KEYUP = 0x0002 ;
3341
3442 [ DllImport ( "user32.dll" , SetLastError = true ) ]
3543 private static extern void keybd_event ( byte bVk , byte bScan , uint dwFlags , IntPtr dwExtraInfo ) ;
3644
45+ [ DllImport ( "user32.dll" , SetLastError = true ) ]
46+ private static extern uint SendInput ( uint nInputs , INPUT [ ] pInputs , int cbSize ) ;
47+
48+ [ DllImport ( "user32.dll" ) ]
49+ private static extern IntPtr GetForegroundWindow ( ) ;
50+
51+ [ DllImport ( "user32.dll" ) ]
52+ private static extern IntPtr SendMessage ( IntPtr hWnd , int msg , IntPtr wParam , IntPtr lParam ) ;
53+
54+ [ StructLayout ( LayoutKind . Sequential ) ]
55+ private struct INPUT
56+ {
57+ public uint type ;
58+ public InputUnion U ;
59+ }
60+
61+ [ StructLayout ( LayoutKind . Explicit ) ]
62+ private struct InputUnion
63+ {
64+ [ FieldOffset ( 0 ) ]
65+ public KEYBDINPUT ki ;
66+ }
67+
68+ [ StructLayout ( LayoutKind . Sequential ) ]
69+ private struct KEYBDINPUT
70+ {
71+ public ushort wVk ;
72+ public ushort wScan ;
73+ public uint dwFlags ;
74+ public uint time ;
75+ public IntPtr dwExtraInfo ;
76+ }
77+
3778 /// <summary>
3879 /// Simulates a key press and release.
3980 /// </summary>
4081 /// <param name="vk">The virtual key code to simulate.</param>
4182 public static void SimulateKey ( byte vk )
4283 {
43- keybd_event ( vk , 0 , 0 , IntPtr . Zero ) ;
44- keybd_event ( vk , 0 , KEYEVENTF_KEYUP , IntPtr . Zero ) ;
84+ if ( TrySendMediaAppCommand ( vk ) )
85+ return ;
86+
87+ SendKey ( vk , isKeyUp : false ) ;
88+ SendKey ( vk , isKeyUp : true ) ;
4589 }
4690
4791 /// <summary>
4892 /// Simulates pressing a key down.
4993 /// </summary>
5094 private static void KeyDown ( byte vk )
5195 {
52- keybd_event ( vk , 0 , 0 , IntPtr . Zero ) ;
96+ SendKey ( vk , isKeyUp : false ) ;
5397 }
5498
5599 /// <summary>
56100 /// Simulates releasing a key.
57101 /// </summary>
58102 private static void KeyUp ( byte vk )
59103 {
60- keybd_event ( vk , 0 , KEYEVENTF_KEYUP , IntPtr . Zero ) ;
104+ SendKey ( vk , isKeyUp : true ) ;
105+ }
106+
107+ private static void SendKey ( byte vk , bool isKeyUp )
108+ {
109+ var flags = isKeyUp ? KEYEVENTF_KEYUP : 0u ;
110+ if ( IsExtendedKey ( vk ) )
111+ flags |= KEYEVENTF_EXTENDEDKEY ;
112+
113+ var input = new INPUT
114+ {
115+ type = INPUT_KEYBOARD ,
116+ U = new InputUnion
117+ {
118+ ki = new KEYBDINPUT
119+ {
120+ wVk = vk ,
121+ wScan = 0 ,
122+ dwFlags = flags ,
123+ time = 0 ,
124+ dwExtraInfo = IntPtr . Zero
125+ }
126+ }
127+ } ;
128+
129+ if ( SendInput ( 1 , new [ ] { input } , Marshal . SizeOf < INPUT > ( ) ) == 0 )
130+ {
131+ keybd_event ( vk , 0 , flags , IntPtr . Zero ) ;
132+ }
133+ }
134+
135+ private static bool IsExtendedKey ( byte vk )
136+ {
137+ return vk is VK_MEDIA_PLAY_PAUSE
138+ or VK_MEDIA_NEXT_TRACK
139+ or VK_MEDIA_PREV_TRACK
140+ or VK_MEDIA_STOP
141+ or VK_VOLUME_MUTE
142+ or VK_VOLUME_DOWN
143+ or VK_VOLUME_UP ;
144+ }
145+
146+ private static bool TrySendMediaAppCommand ( byte vk )
147+ {
148+ var appCommand = vk switch
149+ {
150+ VK_MEDIA_PLAY_PAUSE => APPCOMMAND_MEDIA_PLAY_PAUSE ,
151+ VK_MEDIA_NEXT_TRACK => APPCOMMAND_MEDIA_NEXTTRACK ,
152+ VK_MEDIA_PREV_TRACK => APPCOMMAND_MEDIA_PREVIOUSTRACK ,
153+ VK_MEDIA_STOP => APPCOMMAND_MEDIA_STOP ,
154+ _ => - 1
155+ } ;
156+
157+ if ( appCommand < 0 )
158+ return false ;
159+
160+ var hwnd = GetForegroundWindow ( ) ;
161+ if ( hwnd == IntPtr . Zero )
162+ return false ;
163+
164+ var lParam = ( IntPtr ) ( appCommand << 16 ) ;
165+ SendMessage ( hwnd , WM_APPCOMMAND , hwnd , lParam ) ;
166+ return true ;
61167 }
62168
63169 /// <summary>
0 commit comments