Electron屏幕截图的技术方案Mac&Windows

导读:Electron比你想象的更简单也比你想象中更强大!如果你可以建一个网站,你就可以建一个桌面应用程序。Electron 是一个使用JavaScript, HTML和CSS等Web技术创建原生程序的框架。控制技术栈的复杂度,一直以来都是许多开发者和项目管理人员的追求。Electron为我们提供了一种舒适且优雅的方案,您只需要使用熟悉的开发工具、熟悉的Web开发语言和框架,就可以轻松开发桌面应用。下文将讲解Electron屏幕截图的技术方案。

不依赖第三方软件和库,单使用Electron和HTML5技术是可以实现屏幕截图的功能的,具体的思路分为如下几个步骤:

第一步:创建一个全屏的、无边框的、始终置顶的窗口,关键代码如下:

let win = new BrowserWindow({
  fullscreen:true,
  frame: false,
  resizable:false,
  enableLargerThanScreen: true,
  skipTaskbar:true,
  alwaysOnTop:true,
  show:false,
  webPreferences: {
    nodeIntegration: true,
    webSecurity:false
  }
})

第二步:在这个窗口的渲染进程中使用desktopCapturer模块的getSources方法获取到屏幕的图像(给屏幕拍照),关键代码如下所示:

desktopCapturer.getSources({
  types: ['screen'],
  thumbnailSize: {width:1920,height:1080} //实际屏幕尺寸可以通过主进程的screen模块获得
}).then(imgs=>{
yourImgDom.src = imgs[0].thumbnail.toDataURL()
ipcRenderer.invoke("showWindow") //屏幕拍照完成后,再显示这个窗口,不然它也会被拍进去
}))

第三步:在渲染进程中允许用户拖拽选择截图区域,这是纯HTML5的知识,不再提供关键代码。

第四步:很多桌面应用都有屏幕截图的功能,这些功能往往都被封装到dll动态链接库中,由应用的可执行文件调用,很多时候我们自己开发的可执行文件也可以调用这些动态链接库中的功能函数。

如果你需要使用这样的动态链接库里的功能函数,你就需要知道这些动态链接库导出的功能函数的确切的名字,微软的Visual Studio开发工具提供了一个DUMPBIN命令行工具,可以帮助开发者查看动态链接库的导出函数。

从Visual Studio开发工具的系统菜单:工具>命令行>开发者命令提示打开命令行提示符,在命令提示符输入如下指令:

dumpbin /exports a.dll

注意a.dll修改为你需要查看的dll文件的文件名,也可以是一个绝对路径。命令执行后,你将得到如下信息:

Dump of file D:\project\PC-Hik-Base\src\static\PrScrn.dll
File Type: DLL
  Section contains the following exports for PrScrn.dll
    00000000 characteristics
    5620A284 time date stamp Fri Oct 16 15:08:52 2015
        0.00 version
           1 ordinal base
           1 number of functions
           1 number of names
    ordinal hint RVA      name
          1    0 00008072 PrScrn
  Summary
        4000 .data
        B000 .rdata
        6000 .reloc
      105000 .rsrc
        1000 .shared
       1E000 .text

其中最关键的信息就是“1    0 00008072 PrScrn”这行信息了,这就是这个dll的导出函数,如果你希望了解更多有关DUMPBIN的知识,请参阅:https://docs.microsoft.com/en-us/cpp/build/reference/dumpbin-options?view=vs-2017。

接下来我们就要写一个C++程序来使用这个动态链接库导出的功能函数。这个C++程序很简单,代码如下:

#pragma comment(linker, "/subsystem:windows /entry:mainCRTStartup")
#include <stdio.h>
#include <tchar.h>
#include <windows.h>
#include <exception>
#include <iostream>
using namespace std;
typedef int (*PrScrn)();
int main(int argc, char *argv[])
{
  try
  {
    HINSTANCE hDll = LoadLibrary(_T("PrScrn.dll"));
    if (hDll == NULL)
    {
      printf("LoadLibraryError %d\n", GetLastError());
      return -1;
    }
    PrScrn prScrn = (PrScrn)GetProcAddress(hDll, "PrScrn");
    int result = prScrn();
    FreeLibrary(hDll);
    return result;
  }
  catch (exception &e)
  {
    std::cout << e.what() << std::endl;
    return -1;
  }
}

程序的第一行告诉操作系统,这是一个窗口程序不是一个控制台程序,不需要出现控制台命令行界面。

在程序的main方法中,我们使用LoadLibrary方法加载了这个第三方动态链接库,并通过GetProcAddress方法把这个动态链接库的导出函数“PrScrn”的地址映射到了一个函数指针上。接着通过这个指针调用了这个导出函数,导出函数执行完成后,通过FreeLibrary方法释放了这个动态链接库。

程序编写完成需要确定目标dll是32位的还是64位的,如果目标是32位的,我们生成的exe可执行文件也应该是32位的,如何查看一个dll是32位还是64位的呢?这同样需要使用dumpbin工具来查看:

dumpbin /headers a.dll

执行上面命令后,得到的关键信息是:

FILE HEADER VALUES
             14C machine (x86)
               6 number of ps
        5620A285 time date stamp Fri Oct 16 15:08:53 2015
               0 file pointer to symbol table
               0 number of symbols
              E0 size of optional header
            2102 characteristics
                   Executable
                   32 bit word machine
                   DLL

显然这是一个32位的动态链接库,如果你用这个命令测试一个64位的动态链接库的话,得到的FILE HEADER VALUES第一行应为8664 machine (x64)。

可执行文件编译成功后,可以通过Node内置的child_process模块唤起这个可执行程序,代码如下:

import { execFile } from 'child_process'
new Promise((resolve, reject) => {
      try {
        let exeProcess = execFile(yourExePath, (error) => {
          if (error) resolve(false)
        })
        exeProcess.on('exit', (code) => {
          if (code === 1) {
            resolve(true)
          } else {
            resolve(false)
          }
        })
      } catch (ex) {
        resolve(false)
      }
    })

在上面的代码中,我们使用child_process模块的execFile方法启动了屏幕截图的可执行程序,可执行程序exe文件的路径保存在yourExePath变量中,可执行程序执行成功后会返回1(这就是我们C++程序main函数的返回值),执行失败会返回-1,相应的Node.js程序中Promise对象的resolve结果也会跟着变化。

聪明的读者可能会注意到不单单是截图的动态链接库,其他动态链接库也可以考虑使用这种方法访问其导出函数的功能。

由于Mac操作系统自带功能强大的截图工具,所以我们可以直接唤起Mac内置的截图工具供用户使用,代码如下:

import { spawn } from 'child_process'
new Promise((resolve, reject) => {
    let instance = spawn(`screencapture`, ["-c","-i"])
    instance.on(`error`, err => reject(err.toString()))
    instance.stderr.on(`data`, err => reject(err.toString()))
    instance.on(`close`, code => {
      (code === 0)
      ? resolve(true)
      : reject(false)
    })
})

在上面的代码中,我们使用child_process模块的spawn方法,唤起了Mac系统中内置的截图工具,screencapture是Mac系统中一个全局指令,"-c","-i"是给这个指令附加的两个参数,"-c"参数意为截图内容写入到剪切板,"-i"参数意为启用区域选择工具,由用户自由选择屏幕区域进行截图,你还可以使用"-w"参数使截图工具可以自动识别窗口区域,"-x"参数禁用截图时的声音,screencapture指令的更多参数参见:http://www.osdata.com/programming/shell/screencapture.html。

另外spawn与execFile都是Node.js控制子进程的API,除它们两个外,还有exec和fork,作用大同小异,详细使用参数参见:http://nodejs.cn/api/child_process.html#child_process_child_process_spawn_command_args_options。

读者当然也可以不选择Mac自带的screencapture库,使用第三方截图App,通过Electron唤起这些App的方法与前面讲的方式一样,并无二致。

 

 

《Electron实战:入门、进阶与性能优化》

《Electron实战:入门、进阶与性能优化》一书,以实战为导向,讲解了如何用Electron结合现代前端技术来开发桌面应用。不仅全面介绍了Electron入门需要掌握的功能和原理,而且还针对Electron开发中的重点和难点进行了重点讲解,旨在帮助读者实现快速进阶。作者是Electron领域的早期实践者,项目经验非常丰富,本书内容得到了来自阿里等大企业的一线专家的高度评价。本书遵循渐进式的原则逐步传递知识给读者,书中以Electron知识为主线并对现代前端知识进行了有序的整合,对易发问题从深层原理的角度进行讲解,对普适需求以实践的方式进行讲解,同时还介绍了Electron生态内的大量优秀组件和项目。

推荐阅读:跨平台桌面开发王器Electron:安装过程深入解析

更多精彩回顾

书讯 |11月书讯(下)| 这些好书必须“买买买”!

书讯 |11月书讯(上)| 这些好书必须“买买买”!

资讯 |DB-Engines 10月数据库排名:“三大王”无人能敌,PostgreSQL紧随其后

上新 | 百度官方出品 | 全面解读PaddlePaddle,零基础快速入门深度学习
书单 | 开学季——计算机专业学生必读的10本畅销经典

干货 | 数据分析必读干货:简单而实用的3大分析方法

收藏 | (万字长文)Spring的核心知识尽揽其中

点击阅读全文购买

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值