floAr
floAr

Reputation: 829

No image in Clipboard after PrintScreen

I am implementing different screen grabber to compare them. One of the should use the 'printscreen' key and the clipboard.

I send the keystroke with keybd_event:

[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);
}

In my IEnumerable I call this method and try to grab the image afterwards:

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

The image returned is always null and Clipboards.ContainsImage() is false all the time. I´ve tried waiting a moment after sending the keys but it doesn´t work either. Am I missing some kind of setup, or is there a fundamental error?

PS: I am able to paste the correct image into paint or gimp after running the program.

Upvotes: 1

Views: 1525

Answers (4)

CodeMode
CodeMode

Reputation: 1

I know this is an older question but I thought I would share my findings since it is related to this. The problem I was seeing was that the above posted methods worked as long as I had a breakpoint in the code. With no breakpoint the events would fire, but they would fire only after exiting the method call it was in.

This means something like

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

wont work because the Clipboard wont get populated until it leaves this method. The work around to this is an old VB trick using DoEvents(). This will allow our application to process all windows messages in the queue. So the revised code should work.

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

Upvotes: 0

kwingho
kwingho

Reputation: 422

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();
}

Upvotes: 0

Hans Passant
Hans Passant

Reputation: 941675

It is a console program

That's the most relevant detail, you should put it in your question. The Clipboard is a system object whose underlying api is COM based. Which makes it sensitive to the apartment state of the thread that uses the api. The .NET Clipboard class fumbles this a bit, it should really throw an exception if the thread's state is wrong. And it is wrong in the case of console mode app, its main thread is MTA by default and you need STA to use the api.

The fix is simple, you can just put an attribute on the Main() method to ask for STA:

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

Technically an STA thread should also pump a message loop, like a Winforms or WPF app does. But you'll get away with it as long as you only make the method calls from your main thread.

Upvotes: 4

Mayazcherquoi
Mayazcherquoi

Reputation: 474

Have you tried using the SendKeys class instead?

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

Source of code sample

Upvotes: 0

Related Questions