1

我正在实施不同的屏幕抓取器来比较它们。其中之一应该使用“printscreen”键和剪贴板。

我用 keybd_事件发送击键:

[System.Runtime.InteropServices.DllImport("user32.dll")]
public static extern void keybd_event(byte vVK, byte bScan, int dwFlags,int dwExtraInfo);

public const int KEYEVENTF_EXTENDEDKEY=0x0001; //key down
public const int KEYEVENTF_KEYUP=0x0002; //key up

public const int VK_SNAPSHOT=0x2C; //VirtualKey code for print key

public static void PrintScreen(){
keybd_event(VK_SNAPSHOT,0,KEYEVENTF_EXTENDEDKEY,0);
keybd_event(VK_SNAPSHOT,0,KEYEVENTF_KEYUP,0);
}

在我的 IEnumerable 中,我调用了这个方法,然后尝试抓取图像:

...
InputController.PrintScreen();
var img=Clipboard.GetImage();
...

返回的图像始终为 null,并且 Clipboards.ContainsImage() 始终为 false。我尝试在发送密钥后等待片刻,但它也不起作用。我是否缺少某种设置,或者是否存在基本错误?

PS:运行程序后,我可以将正确的图像粘贴到paint或gimp中。

4

4 回答 4

4

这是一个控制台程序

这是最相关的细节,你应该把它放在你的问题中。剪贴板是一个系统对象,其底层 api 是基于 COM 的。这使得它对使用 api 的线程的单元状态很敏感。.NET Clipboard 类对此有点摸不着头脑,如果线程的状态错误,它应该真的抛出异常。而console模式的app是错误的,它的主线程默认是MTA,需要STA才能使用api。

修复很简单,您可以在 Main() 方法上添加一个属性来请求 STA:

    [STAThread]
    static void Main(string[] args) {
        // etc...
    }

从技术上讲,STA 线程也应该像 Winforms 或 WPF 应用程序那样泵送消息循环。但是,只要您只从主线程进行方法调用,您就可以摆脱它。

于 2013-09-20T12:21:23.550 回答
0

我知道这是一个较老的问题,但我想我会分享我的发现,因为它与此有关。我看到的问题是,只要我在代码中有断点,上面发布的方法就可以工作。如果没有断点,事件会触发,但它们只有在退出它所在的方法调用后才会触发。

这意味着类似

    InputController.PrintScreen();
    var img=Clipboard.GetImage();

不会工作,因为剪贴板在离开此方法之前不会被填充。解决此问题的方法是使用 DoEvents() 的旧 VB 技巧。这将允许我们的应用程序处理队列中的所有 Windows 消息。所以修改后的代码应该可以工作。

    InputController.PrintScreen();
    Application.DoEvents();
    var img=Clipboard.GetImage();
于 2014-07-28T17:14:16.863 回答
0

您是否尝试过使用SendKeys类?

public static Image TakeScreenSnapshot(bool activeWindowOnly)
{
    // PrtSc = Print Screen Key
    string keys = "{PrtSc}";
    if (activeWindowOnly)
        keys = "%" + keys; // % = Alt
    SendKeys.SendWait(keys);
    return Clipboard.GetImage();
}

代码示例的来源

于 2013-09-20T11:17:59.440 回答
0
public const int KEYEVENTF_EXTENDEDKEY=0x0001; //key down
public const int KEYEVENTF_KEYUP=0x0002; //key up

but you are using:

keybd_event(VK_SNAPSHOT,0,KEYEVENT_EXTENDEDKEY,0);
keybd_event(VK_SNAPSHOT,0,KEYEVENT_KEYUP,0);

use KEYEVENTF_EXTENDEDKEY and KEYEVENTF_KEYUP it works


re: whole thing runs in a worker thread on the threadpool I can't find a way to POST PrintScreen() To 'Main-SynchronizationContext', because it is a console program

you can try this:

class Program
{        
    [System.Runtime.InteropServices.DllImport("user32.dll")]
    public static extern void keybd_event(byte vVK, byte bScan, int dwFlags, int dwExtraInfo);

    public const int KEYEVENTF_EXTENDEDKEY = 0x0001; //key down
    public const int KEYEVENTF_KEYUP = 0x0002; //key up

    public const int VK_SNAPSHOT = 0x2C; //VirtualKey code for print key

    public static void PrintScreen()
    {
        keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_EXTENDEDKEY, 0);
        keybd_event(VK_SNAPSHOT, 0, KEYEVENTF_KEYUP, 0);
    }

    public static void test(Action<Image> action)
    {
        PrintScreen();
        var image = Clipboard.GetImage();
        action.BeginInvoke(image, ar => action.EndInvoke(ar), null);
    }

    [STAThread]
    static void Main(string[] args)
    {
        var processAction = new Action<Image>(img =>
        {
            if (img == null)
                Console.WriteLine("none");
            else
                Console.WriteLine(img.PixelFormat);
        });
        test(processAction);
        System.Console.ReadLine();
}
于 2013-09-20T11:20:37.420 回答