How can I take a window screenshot in Node.js?
Asked Answered
A

5

12

I'm in a research to find a way to take a screenshot of a window using Node.js, and I'm trying to do this with node-ffi, but I don't know how... at a time I'm stuck here:

var ffi = require('ffi');

var user32 = new ffi.Library("user32", {
      FindWindowA: [ 'uint32' , [ 'string', 'string' ]]
    , PrintWindow: [ 'int32'  , [ 'int32', 'string', 'int32' ]]
});

var IMG;
var windowHandle = user32.FindWindowA(null, "Calculator");
var printWin = user32.PrintWindow(windowHandle, IMG, 0);

console.log(printWin);
console.log(IMG);

The result:

$ node get-print.js
1
undefined

EDITED

I found the following working code in C++

Bitmap bm = new Bitmap(1024, 768);
Graphics g = Graphics.FromImage(bm);
IntPtr hdc = g.GetHdc();
Form1.PrintWindow(this.Handle, hdc, 0);
g.ReleaseHdc(hdc);
g.Flush();
g.Dispose();
this.pictureBox1.Image = bm;

now I need to do this on NodeJs,

Anyone can help me?

Asphyxiant answered 27/3, 2017 at 0:47 Comment(2)
Maybe this can help? #20589597Boeke
The project is for windows, but somwthing like ImageMagick Import can be the answerAsphyxiant
W
6

Although I don't have full code working, but in theory if you're able to do so in C++, then simply use node-gyp to compile the C++ file to a .node file, then include that in you're nodeJS file.

So some example pseudo-code, first of all make a binding.gyp file in a new directory, and put some code in it like this:

{
  "targets": [
    {
        "target_name": "addon",
        "sources": [ 
            "hi.cc"
        ]
    }
  ]
}

then in that same directory (for now) make another file called hi.cc, and put your C++ code in it, plus some more to make a node module out of it. So, based on the docs mentioned above, you could do something like this (untested):

/*don't know what includes you're using to git the Bitmap 
and  Graphics functions, but include them here */

 /*then to make a node module*/
#include <node.h>

using namespace v8;


void GetImage(const FunctionCallbackInfi<Value>& args) {
    Bitmap bm = new Bitmap(1024, 768);
    Graphics g = Graphics.FromImage(bm);
    IntPtr hdc = g.GetHdc();
    Form1.PrintWindow(this.Handle, hdc, 0);
    g.ReleaseHdc(hdc);
    g.Flush();
    /*
    this is the key part, although I'm not
    100% sure it will work since I don't 
    know exactly what type Graphics returns, 
    but basically just convert it somehow into 
    base64, or a plain old void* value
    (as in this following example), then make a new
    Local variable of it and set the return type
    (or make a function callback). So first get the 
    Graphics variable into a void* of the data, then 
    convert it to an ArrayBuffer to use in NodeJS, based on this
    answer. Anyway:
    */
    Local<
        ArrayBuffer
    > 
    v = 
    ArrayBuffer::New(i, /*some void* value*/ temp, 5000/*or some length*/);
    a.GetReturnValue().Set(v);
}


void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "hello", GetImage);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

Then make sure you actually have node-gyp and the proper build tools installed (see docs above, but its pretty much npm i -g node-gyp), then go to build -> Release -> addon.node and copy it to your main nodeJS directory, then make a new nodeJS file or include the following in an existing one:

let addon = require("./addon"),
    pictureData = Buffer.from(addon.hello()/* if you choose to return a base64 string instead, then insert: ,"base64"*/);
Wale answered 14/4, 2020 at 4:30 Comment(1)
comment to later refer back to this, very good answer on how to go about porting a simple C utility to node.Walleyed
G
9

You could use a NPM package called "desktop-screenshot". It's very simple to use.

Example on NPM:

var screenshot = require('desktop-screenshot');

screenshot("screenshot.png", function(error, complete) {
    if(error)
        console.log("Screenshot failed", error);
    else
        console.log("Screenshot succeeded");
});

https://www.npmjs.com/package/desktop-screenshot

Grab answered 27/3, 2017 at 1:16 Comment(5)
but I need a scrrenshot of a window not entire desktop.Asphyxiant
use desktop-screenshot to capture the desktop, then crop the image using node-easyimage <github.com/hacksparrow/node-easyimage>, or, since easyimage requires imagemagick, just use child_process to call imagemagick directly: it has screenshots built in.Errick
I want to create a screen capture of a region of a specific window that is not in the foreground. Any nodejs package that can do this?Hage
No. No. No. No.Tammitammie
It is, but there is no module for this, what was his question.Tammitammie
W
6

Although I don't have full code working, but in theory if you're able to do so in C++, then simply use node-gyp to compile the C++ file to a .node file, then include that in you're nodeJS file.

So some example pseudo-code, first of all make a binding.gyp file in a new directory, and put some code in it like this:

{
  "targets": [
    {
        "target_name": "addon",
        "sources": [ 
            "hi.cc"
        ]
    }
  ]
}

then in that same directory (for now) make another file called hi.cc, and put your C++ code in it, plus some more to make a node module out of it. So, based on the docs mentioned above, you could do something like this (untested):

/*don't know what includes you're using to git the Bitmap 
and  Graphics functions, but include them here */

 /*then to make a node module*/
#include <node.h>

using namespace v8;


void GetImage(const FunctionCallbackInfi<Value>& args) {
    Bitmap bm = new Bitmap(1024, 768);
    Graphics g = Graphics.FromImage(bm);
    IntPtr hdc = g.GetHdc();
    Form1.PrintWindow(this.Handle, hdc, 0);
    g.ReleaseHdc(hdc);
    g.Flush();
    /*
    this is the key part, although I'm not
    100% sure it will work since I don't 
    know exactly what type Graphics returns, 
    but basically just convert it somehow into 
    base64, or a plain old void* value
    (as in this following example), then make a new
    Local variable of it and set the return type
    (or make a function callback). So first get the 
    Graphics variable into a void* of the data, then 
    convert it to an ArrayBuffer to use in NodeJS, based on this
    answer. Anyway:
    */
    Local<
        ArrayBuffer
    > 
    v = 
    ArrayBuffer::New(i, /*some void* value*/ temp, 5000/*or some length*/);
    a.GetReturnValue().Set(v);
}


void Initialize(Local<Object> exports) {
  NODE_SET_METHOD(exports, "hello", GetImage);
}

NODE_MODULE(NODE_GYP_MODULE_NAME, Initialize)

Then make sure you actually have node-gyp and the proper build tools installed (see docs above, but its pretty much npm i -g node-gyp), then go to build -> Release -> addon.node and copy it to your main nodeJS directory, then make a new nodeJS file or include the following in an existing one:

let addon = require("./addon"),
    pictureData = Buffer.from(addon.hello()/* if you choose to return a base64 string instead, then insert: ,"base64"*/);
Wale answered 14/4, 2020 at 4:30 Comment(1)
comment to later refer back to this, very good answer on how to go about porting a simple C utility to node.Walleyed
A
4

There's also an alternative Node.js package that's still being worked on at the moment (last commit from 15 days ago; compare to the package mentioned above, which has last commit from 2015 or 2016). It allows to choose the screen it captures, which the other one doesn't seem to do.

https://github.com/bencevans/screenshot-desktop

const screenshot = require('screenshot-desktop');

screenshot.listDisplays().then((displays) => {
  // displays: [{ id, name }, { id, name }]
  screenshot({ screen: displays[displays.length - 1].id })
    .then((img) => {
      // img: Buffer of screenshot of the last display
    });
})
Arbiter answered 2/4, 2021 at 13:8 Comment(1)
Way better option!!! The other one has some nasty vulnerabilities reported by npm, and for windows it relies on a closed-source binary that can do a lot more to the server machine.... This solution uses a .bat, with functions native to windows. So I feel way more safe using this lib. Thanks for sharing.Shortie
S
3

If you require pixel-by-pixel access to the screenshot data from NodeJS (and don't want to have to write then read it from disk), you can use the CaptureScreenshot function of my windows-ffi package.

Usage:

import {VRect, CaptureScreenshot, GetForegroundWindowHandle} from "windows-ffi";

// First capture a screenshot of a section of the screen.
const screenshot = CaptureScreenshot({
    windowHandle: GetForegroundWindowHandle(), // comment to screenshot all windows
    rectToCapture: new VRect(0, 0, 800, 600),
});

// The image-data is now stored in the `screenshot.buffer` Buffer object.
// Access it directly (and cheaply) using the helper functions on `screenshot`.
for (let x = 0; x < 800; x++) {
    console.log(`Pixel color at [${x}, 0] is:`, screenshot.GetPixel(x, 0).ToHex_RGB());
}

Naturally, this only works on Windows, since it uses the Windows API, through ffi-napi.

Simper answered 5/9, 2021 at 12:5 Comment(2)
Works on full screen but unable to get it working on the handle returned from GetForegroundWindowHandle(). Would have been cool to have that too but preferably where I can find a window with a given name and only capture that.Russellrusset
@Russellrusset Odd, it worked for the programs I tried; which program did it fail for? An alternate approach could be to take a full-screen capture, then crop it to that program's rect (obtained using GetWindowRect in user32 dll -- definition for that func would need to be added).Simper
F
0

I tested using PhantomJS, it worked well. Install the phantomjs-prebuilt package:

npm install phantomjs-prebuilt

Run the PhantomJS binary directly by passing it a script to execute.

Here’s an example:

var page = require("webpage").create();
page.open("http://example.com/", function () {
  page.viewportSize = { width: 1280, height: 1024 };
  page.clipRect = { top: 0, left: 0, width: 1280, height: 1024 };
  page.render("website-screenshot.png");
  phantom.exit();
});

Save the script as phantom-screenshot.js, and then run it using the following command:

./node_modules/phantomjs-prebuilt/bin/phantomjs phantom-screenshot.js

This command will capture a screenshot and save it as website-screenshot.png.

Here is my source with several other scripts that might work depending on a specific case.

Floc answered 13/8 at 11:2 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.