一、背景


Smallpdf 网站针对PDF文件提供了非常齐全的功能:PDF 与 Word、PPT、Excel、JPG 的相互转化、PDF 的压缩、编辑、合并、分割、解密、加密等功能,用户无需注册即可免费使用。

但是不注册每小时只能处理有限的PDF,对于我们这种有大量PDF需要压缩的人来说就不太合适啦,所以购买了专业版,其在线版本提供了批量压缩的功能,需要上批量上传等待压缩后再下载(存在批量上传后,浏览器卡死或者无法压缩一直等待,以及压缩成功后无法下载,速度只有几十K),而客户端程序版本又没有批量压缩的功能,只能一篇一篇的打开压缩。

为了解解上述问题,经过考虑,准备利用其客户端的压缩功能来进行批量压缩,虽然时间花得久点,但是在本地不用考虑网络因素这些,至少非常可控,但是是不可能人工一篇一篇来压的,所以这就是这篇文章的由来。

Smallpdf website provides a very complete function for PDF files: PDF and Word, PPT, Excel, JPG mutual conversion, PDF compression, editing, merging, segmentation, decryption, encryption and other functions, users can use it for free without registration.
But if you don't register for a limited number of PDFs per hour, it's not suitable for us who have a large number of PDFs to compress, so we bought a professional version. The online version provides batch compression, which requires a batch upload waiting for compression before downloading (there are batch uploads, browsers are stuck or no browsers). The method compression has been waiting, and the compression can not be downloaded after success, the speed is only a few dozen K), and the client version of the program does not have the function of mass compression, can only open and compress one article at a time.
In order to understand the above problems, after consideration, ready to use its client compression function for batch compression, although it takes a long time, but do not consider the local network factors, at least very controllable, but it is impossible to manually press each article, so this is the origin of this article.


二、先看实现后的效果(录了gif图,居然显示不了):


三、实现原理

  主要的原理就是调用WIN API模拟点击,辅助其它的操作完成整个功能,主要的实现就贴主要的代码了,看注释。

需要的API

        [DllImport("User32.dll", EntryPoint = "FindWindow")]
public extern static IntPtr FindWindow(string lpClassName, string lpWindowName); [DllImport("User32.dll", EntryPoint = "FindWindowEx")]
public static extern IntPtr FindWindowEx(IntPtr hwndParent, IntPtr hwndChildAfter, string lpClassName, string lpWindowName); [DllImport("User32.dll", EntryPoint = "SendMessage")]
public static extern int SendMessage(IntPtr hWnd, int Msg, IntPtr wParam, string lParam); [DllImport("user32.dll")]
public static extern void SwitchToThisWindow(IntPtr hWnd, bool fAltTab); [DllImport("user32.dll")]
[return: MarshalAs(UnmanagedType.Bool)]
static extern bool GetWindowRect(IntPtr hWnd, ref RECT lpRect); [StructLayout(LayoutKind.Sequential)]
public struct RECT
{
public int Left;
public int Top;
public int Right;
public int Bottom;
}
const int WM_CLOSE = 0x0010;
const int WM_CLICK = 0x00F5; enum MouseEventFlag : uint
{
Move = 0x0001,
LeftDown = 0x0002,
LeftUp = 0x0004,
RightDown = 0x0008,
RightUp = 0x0010,
MiddleDown = 0x0020,
MiddleUp = 0x0040,
XDown = 0x0080,
XUp = 0x0100,
Wheel = 0x0800,
VirtualDesk = 0x4000,
Absolute = 0x8000
} [DllImport("user32.dll")]
static extern void mouse_event(MouseEventFlag flags, int dx, int dy, uint data, UIntPtr extraInfo); [DllImport("user32.dll")]
public static extern int SetCursorPos(int x, int y); public static void MouseLefClickEvent(int dx, int dy, uint data)
{
SetCursorPos(dx, dy);
mouse_event(MouseEventFlag.LeftDown, dx, dy, data, UIntPtr.Zero);
mouse_event(MouseEventFlag.LeftUp, dx, dy, data, UIntPtr.Zero);
}

         核心实现:

           var pdfFile = PdfFiles[Index];
var pdfName = Path.GetFileName(pdfFile); //用smallpdf打开指定PDF
System.Diagnostics.Process.Start(SMALLPDFEXE, $"\"{pdfFile}\""); while (true)
{
//加入等待时间,打开PDF需要一定时间
Thread.Sleep(5000); //smallpdf窗体句柄
IntPtr parentWnd = new IntPtr(0);
parentWnd = FindWindow(null, pdfName); if (!parentWnd.Equals(IntPtr.Zero))
{
//保存窗口句柄
IntPtr saveWnd = new IntPtr(0);
//保存按钮句柄
IntPtr btnSave = new IntPtr(0); while (true)
{
//将指定的pdf居最上面显示
SwitchToThisWindow(parentWnd, true); //获取窗口位置信息,用于计算压缩按钮所在位置
RECT rc = new RECT();
GetWindowRect(parentWnd, ref rc); //点击压缩按钮
MouseFlag.MouseLefClickEvent(rc.Right - 50, rc.Top + 100, 0); Thread.Sleep(2000); saveWnd = FindWindow(null, "Save Document");
//当找到保存窗口时点击保存按钮,否则一直循环等待
if (!saveWnd.Equals(IntPtr.Zero))
{
btnSave = FindWindowEx(saveWnd, IntPtr.Zero, "Button", "保存(&S)");
if (!btnSave.Equals(IntPtr.Zero))
{
break;
}
}
}
//点击保存按钮
SendMessage(btnSave, WM_CLICK, IntPtr.Zero, "0"); //保存过程等待
bool isCompressed = false;
int j = 0, checkCount = 60;
while (j < checkCount)
{
//0001-compressed.pdf
string compressPdfName = $"{ Path.GetFileNameWithoutExtension(pdfName)}{COMPRESSED}{Path.GetExtension(pdfFile)}";
string compressPdfPath = Path.Combine(PDFDIR, compressPdfName);
if (File.Exists(compressPdfPath))
{
isCompressed = true;
break;
}
Thread.Sleep(1000);
j++;
} //关闭窗体
SendMessage(parentWnd, WM_CLOSE, IntPtr.Zero, "0"); //如果超时都还没有压缩文件生成,认为压缩不成功,记录。
if (!isCompressed)
{
///记录
}
break;
}
}
}

C#调用windows API实现 smallpdf客户端程序进行批量压缩的更多相关文章

  1. C#调用Windows API(示例:显示任务管理器里的程序名称)

    作为初学者来说,在C#中使用API确是一件令人头疼的问题. 在使用API之前你必须知道如何在C#中使用结构.类型转换.安全/不安全代码,可控/不可控代码等许多知识. 在.Net Framework S ...

  2. C#调用windows API的一些方法

    使用C#调用windows API(从其它地方总结来的,以备查询) C#调用windows API也可以叫做C#如何直接调用非托管代码,通常有2种方法: 1.  直接调用从 DLL 导出的函数. 2. ...

  3. C#中调用Windows API的要点 .

    介绍 API(Application Programming Interface),我想大家不会陌生,它是我们Windows编程的常客,虽然基于.Net平台的C#有了强大的类库,但是,我们还是不能否认 ...

  4. c# 判断窗体是否永在最前(TopMost),调用windows API

    许多程序都可以把自身的窗体设为最前显示状态,这个可以参考博客c#让窗体永在最前 调用windows api 将窗体设为topmost.那么如何判断桌面上的一个窗体是否为最前显示状态呢,不光是自己的程序 ...

  5. c#让窗体永在最前 调用windows api 将窗体设为topmost

    有时候应用程序需要将一个窗体始终位于屏幕的最前面,即使切换到其它窗体也能看到该窗体,这样的窗体就叫做TopMost窗体. 用C#制作TopMost窗体之前,首先要了解如何声明SetWindowPos函 ...

  6. 善于 调用Windows API

    前一段时间看见别人做的一个自动填写信息并且点击登录的程序,觉得很有意思. 其实就是在程序中调用Windows的API,那么如何调用,下面就做个简单的介绍. 写的简单粗暴, 不喜轻喷. 0.首先引入名称 ...

  7. VBS调用Windows API函数

    Demon's Blog 忘记了,喜欢一个人的感觉 Demon's Blog  »  程序设计  »  VBS调用Windows API函数 « 用VBS修改Windows用户密码 在VB中创建和使用 ...

  8. C#调用Windows API函数截图

    界面如下: 下面放了一个PictureBox 首先是声明函数: //这里是调用 Windows API函数来进行截图 //首先导入库文件 [System.Runtime.InteropServices ...

  9. 【转】用C#调用Windows API向指定窗口发送

    一.调用Windows API. C#下调用Windows API方法如下: 1.引入命名空间:using System.Runtime.InteropServices; 2.引用需要使用的方法,格式 ...

随机推荐

  1. .Net Core 认证系统源码解析

    不知不觉.Net Core已经推出到3.1了,大多数以.Net为技术栈的公司也开始逐步的切换到了Core,从业也快3年多了,一直坚持着.不管环境怎么变,坚持自己的当初的选择,坚持信仰 .Net Cor ...

  2. 096、运行第一个Service (Swarm03)

    参考https://www.cnblogs.com/CloudMan6/p/7874609.html   上一节我们部署好了 Swarm 集群,下面部署一个运行httpd镜像的service进行演示 ...

  3. 初探CSS -3 语法

    CSS 语法 实例 <!DOCTYPE html> <html> <head> <meta charset="utf-8"> < ...

  4. nodejs express 上传文件自定义文件名和上传路径

    1.客户端 <form action="http://localhost:3000/profile" method="post" enctype=&quo ...

  5. Big Data(三)伪分布式和完全分布式的搭建

    关于伪分布式的配置全程 伪分布式图示 1.安装VMWare WorkStation,直接下一步,输入激活码即可安装 2.安装Linux(需要100GB) 引导分区Boot200MB 交换分区Swap2 ...

  6. 写一个函数,对于一个给定的整数,如果它的二进制模式从正向看和反向看是一样的,那么返回true;

    写一个函数,对于一个给定的整数,如果它的二进制模式从正向看和反向看是一样的,那么返回true:也就是实现这样一个函数boolean isPalindrome(int x); 分析一下,该题目主要是通过 ...

  7. Mysql中event事件的入门

    Mysql中event事件的入门 主要涉及的知识点:mysql的存储过程.mysql的event事件调度. 参考资料: Qiao_Zhi的博客:[周期性执行事件]MySQL事件(Event)& ...

  8. 用Maven搭建简单的SpringMVC框架

    本文会详细阐述如何用Maven搭建一个简单的SpringMVC框架 这里就不介绍SpringMVC框架了,咱们直接来搭建 第一步 创建一个Maven的web项目  这里有一个简单的方法 new一个Ma ...

  9. json与string与map的理解

    json是一种特殊格式的string字符串,也就是json也是string类型,只是这种string是有格式的,那么他的格式就是类似map的格式[key:value] 举例子: Map map = r ...

  10. 502 bad gateway nginx 的错误的解决方案

    总结 一般是php问题居多,也需要调整相应的nginx参数,最后也可能是mysql假死 nginx问题 查看日志中的报错error.log一般设置路径/usr/local/nginx/logs/ngi ...