Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/jsp/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 当Visual Studio具有焦点(或任何以管理员身份运行的应用程序)时,keybd_事件以及PostMessage win32不工作_C#_Interop_Keyboard Shortcuts - Fatal编程技术网

C# 当Visual Studio具有焦点(或任何以管理员身份运行的应用程序)时,keybd_事件以及PostMessage win32不工作

C# 当Visual Studio具有焦点(或任何以管理员身份运行的应用程序)时,keybd_事件以及PostMessage win32不工作,c#,interop,keyboard-shortcuts,C#,Interop,Keyboard Shortcuts,这是一个程序,我已经使用了许多改变,从旧的xp的一天 这是一个cmd行程序,它将改变媒体应用程序(Spotify、vlc、mediaPlayer)中的曲目,就像带有下一个/上一个曲目按钮的键盘一样 目前我使用的是微软自然键盘,它没有这些按钮,但有可编程的按键来执行这个程序 除了VisualStudio2012/2013具有焦点(Windows 7)(没有尝试过其他版本)并且它在Sql management Studio中工作外,所有这些都可以工作 using System; using Syst

这是一个程序,我已经使用了许多改变,从旧的xp的一天 这是一个cmd行程序,它将改变媒体应用程序(Spotify、vlc、mediaPlayer)中的曲目,就像带有下一个/上一个曲目按钮的键盘一样

目前我使用的是微软自然键盘,它没有这些按钮,但有可编程的按键来执行这个程序

除了VisualStudio2012/2013具有焦点(Windows 7)(没有尝试过其他版本)并且它在Sql management Studio中工作外,所有这些都可以工作

using System;
using System.Runtime.InteropServices;

namespace NxtTrack
{    
class Program
{
    [DllImport("user32.dll")]
    private static extern bool PostMessage(IntPtr hWnd, uint Msg, UIntPtr wParam, IntPtr lParam);
    [DllImport("user32.dll")]
    public static extern void keybd_event(byte vkCode, byte scanCode, int flags, IntPtr extraInfo);

    enum TrackMove
    {
        Previous,Next
    }

    static void Main(string[] args)
    {

        TrackMove trackMove;

        try
        {
            if(args[0].ToLower().Contains("previous"))
                trackMove = TrackMove.Previous;
            else if(args[0].ToLower().Contains("next"))
                trackMove = TrackMove.Next;
            else
            {
                throw new Exception("wrong param");
            }
        }
        catch
        {
            Console.WriteLine("Params needed: Next or Previous");
            return;
        }
        TrackKeys(trackMove);
    }

    private static void TrackKeys(TrackMove trackMove)
    {
        //http://msdn.microsoft.com/en-us/library/dd375731%28v=VS.85%29.aspx

        byte msg = trackMove == TrackMove.Previous ? (byte)0xB1 : (byte)0xB0;
        keybd_event(msg, 0x45, 0, IntPtr.Zero);
    }
}
}

问题是VS在发送消息时有焦点,您只能将焦点放在正确的应用程序上


若要解决此问题,请在调用keybd_事件之前放置一个
线程。Sleep
,在睡眠之前放置F5,并在此时将焦点更改到正确的位置。

问题是在发送消息时VS具有焦点,您只能将其与正确应用程序上的焦点一起工作


要解决此问题,请在调用keybd_事件之前放置一个
线程。Sleep
,在睡眠之前放置F5,并在此时将焦点更改到正确的位置。

这些是VK_MEDIA_NEXT_TRACK和VK_MEDIA_PREV_TRACK虚拟键。对它们的处理非常复杂:

  • 任何拥有前台窗口的程序都将在调用GetMessage()时从其消息队列中检索他的击键
  • 该程序消息循环中的TranslateMessage()调用将击键转换为APPCOMMAND_MEDIA_NEXTTRACK或APPCOMMAND_MEDIA_PREVIOUSTRACK,并将其发送到具有焦点的子窗口
  • 子窗口将不使用它,并将消息传递给DefWindowProc()
  • 将消息传递给子窗口的父窗口。这会在嵌套子窗口时重复,最终到达顶层窗口
  • 当它调用DefWindowProc时,Windows会调用一个shell钩子来触发任何程序中的回调,该程序为WH_shell钩子HSHELL_APPCOMMAND通知调用了setWindowshookx()。像Windows Media Player这样的程序将使用该功能
  • 如果没有钩子拦截它,它最终将作为最后一个钩子出现在Explorer中,最后一个钩子将执行该操作
这里的解决方案是不发送击键,而是向上移动前面列出的处理链。如果可以直接调用shell钩子,那就太好了,但是这个特性没有公开。下一步是发送WM_APPCOMMAND消息

这需要选择一个窗口将消息发送到。这是一个困难的,因为UAC,一个叫做UIPI(用户界面特权隔离)的特性阻止了一个非提升程序向提升程序发送消息。所以你不能只使用前台的窗口,它很可能是一个具有管理员权限的应用程序。与Visual Studio一样,PostMessage和keybd_event()的失败可能是因为您尝试了它们

诀窍是将其发送到您拥有的窗口。您使用的是一个控制台模式的项目。这是可以解决的。Project+添加引用,选择System.Windows.Forms。向项目中添加一个新类并粘贴如下所示的代码。将keybd_event()调用替换为

  AppCommand.Send(AppCommands.MediaNext);  
我把你能发出的所有命令都放进去了。我提供了一个Cleanup()方法来释放资源,不需要调用它。该代码与.NET版本2.0到4.5.1兼容

using System;
using System.Threading;
using System.Windows.Forms;
using System.Runtime.InteropServices;

public enum AppCommands {
    BrowserBack = 1,
    BrowserForward = 2,
    BrowserRefresh = 3,
    BrowserStop = 4,
    BrowserSearch = 5,
    BrowserFavorite = 6,
    BrowserHome = 7,
    VolumeMute = 8,
    VolumeDown = 9,
    VolumeUp = 10,
    MediaNext = 11,
    MediaPrevious = 12,
    MediaStop = 13,
    MediaPlayPause = 14,
    LaunchMail = 15,
    LaunchMediaSelect = 16,
    LaunchApp1 = 17,
    LaunchApp2 = 18,
    BassDown = 19,
    BassBoost = 20,
    BassUp = 21,
    TrebleUp = 22,
    TrebleDown = 23,
    MicrophoneMute = 24,
    MicrophoneVolumeUp = 25,
    MicrophoneVolumeDown = 26,
    Help = 27,
    Find = 28,
    New = 29,
    Open = 30,
    Close = 31,
    Save = 32,
    Print = 33,
    Undo = 34,
    Redo = 35,
    Copy = 36,
    Cut = 37,
    Paste = 38,
    ReplyToMail = 39,
    ForwardMail = 40,
    SendMail = 41,
    SpellCheck = 42,
    Dictate = 43,
    MicrophoneOnOff = 44,
    CorrectionList = 45,
    MediaPlay = 46,
    MediaPause = 47,
    MediaRecord = 48,
    MediaFastForward = 49,
    MediaRewind = 50,
    MediaChannelUp = 51,
    MediaChannelDown = 52,
    Delete = 53,
    Flip3D = 54
}

public static class AppCommand {
    public static void Send(AppCommands cmd) {
        if (frm == null) Initialize();
        frm.Invoke(new MethodInvoker(() => SendMessage(frm.Handle, WM_APPCOMMAND, frm.Handle, (IntPtr)((int)cmd << 16))));
    }

    private static void Initialize() {
        // Run the message loop on another thread so we're compatible with a console mode app
        var t = new Thread(() => {
            frm = new Form();
            var dummy = frm.Handle; 
            frm.BeginInvoke(new MethodInvoker(() => mre.Set()));
            Application.Run();
        });
        t.SetApartmentState(ApartmentState.STA);
        t.IsBackground = true;
        t.Start();
        mre.WaitOne();
    }
    public static void Cleanup() { 
        if (frm != null) {
            frm.BeginInvoke(new MethodInvoker(() => { 
                frm.Close();
                Application.ExitThread();
                mre.Set(); 
            }));
            mre.WaitOne();
            frm = null;
        }
    }

    private static ManualResetEvent mre = new ManualResetEvent(false);
    private static Form frm;

    // Pinvoke
    private const int WM_APPCOMMAND = 0x319;
    [DllImport("user32.dll")]
    private static extern IntPtr SendMessage(IntPtr hWnd, int msg, IntPtr wp, IntPtr lp);
}
使用系统;
使用系统线程;
使用System.Windows.Forms;
使用System.Runtime.InteropServices;
公共枚举AppCommands{
BrowserBack=1,
BrowserForward=2,
BrowserRefresh=3,
BrowserStop=4,
BrowserSearch=5,
BrowserFavorite=6,
BrowserHome=7,
体积=8,
体积向下=9,
体积p=10,
MediaNext=11,
MediaPrevious=12,
MediaStop=13,
MediaPlayPause=14,
LaunchMail=15,
LaunchMediaSelect=16,
LaunchApp1=17,
LaunchApp2=18,
BassDown=19,
BassBoost=20,
BassUp=21,
TrebleUp=22,
三倍降=23,
麦克风静音=24,
麦克风音量p=25,
麦克风音量向下=26,
帮助=27,
Find=28,
新=29,
开=30,
关闭=31,
Save=32,
打印=33,
撤销=34,
重做=35,
复制=36,
切割=37,
粘贴=38,
ReplyToMail=39,
ForwardMail=40,
SendMail=41,
拼写检查=42,
口述=43,
麦克风断开=44,
更正列表=45,
MediaPlay=46,
MediaPause=47,
MediaRecord=48,
MediaFastForward=49,
MediaRewind=50,
MediaChannelUp=51,
MediaChannelDown=52,
删除=53,
Flip3D=54
}
公共静态类AppCommand{
公共静态无效发送(AppCommands cmd){
如果(frm==null)初始化();

frm.Invoke(new MethodInvoker)(()=>SendMessage(frm.Handle,WM_APPCOMMAND,frm.Handle,(IntPtr)((int)cmd这些是VK_MEDIA_NEXT_TRACK和VK_MEDIA_PREV_TRACK虚拟键。对它们的处理非常复杂:

  • 任何拥有前台窗口的程序都将在调用GetMessage()时从其消息队列中检索他的击键
  • 该程序消息循环中的TranslateMessage()调用将击键转换为APPCOMMAND_MEDIA_NEXTTRACK或APPCOMMAND_MEDIA_PREVIOUSTRACK,并将其发送到具有焦点的子窗口
  • 子窗口将不使用它,并将消息传递给DefWindowProc()
  • 它将消息传递给子窗口的父窗口。当子窗口嵌套时,它会重复多次,最终到达顶层窗口
  • 当它调用DefWindowProc时,Windows会调用一个shell钩子,以在任何为WH_shell钩子HSHELL_APPCOMMAND调用setWindowshookx()的程序中触发回调