一、背景


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. P1224 [NOI2013]向量内积

    传送门 发现这个内积和矩乘有点像,考虑构造一个 $n$ 行 $m$ 列的矩阵 $A$,每一行都是一个题目给定的 $m$ 维向量 设 $B=AA^T$ ,其中 $A^T$ 为 $A$ 的转置矩阵,那么对 ...

  2. JavaScript应懂的概念

    目录 垃圾回收 函数作用域, 块级作用域和词法作用域 调用堆栈 原始类型 值类型和引用类型 隐式, 显式, 名义和鸭子类型 == 与 ===, typeof 与 instanceof this, ca ...

  3. 如何使用前端分页框架bootstrap paginator

    前端分页框架bootstrap paginator用于web前端页面快速实现美观大方的翻页功能.在实现交互良好的页面翻页功能时,往往还需要配合使用后端分页框架pagehelper.pagehelper ...

  4. git 查看对比的方法log diff

    git shortlog 默认情况下,git shortlog 把输出按作者名字排序,但你可以传入 -n 选项来按每个作者提交数量排序. 1.有冲突时可以用 git status查看 2.通过git ...

  5. ios / % 四舍五入 向上取整(ceil()) 向下取整(floor())

    1. / //Test "/"    cout << "Test \"/\"!" << endl;    cout ...

  6. Python Requests库 Get和Post的区别和Http常见状态码

    (1)   在客户端,Get方式在通过URL提交数据,数据在URL中可以看到:POST方式,数据放置在HTML HEADER内提交. (2)   GET方式提交的数据最多只能有1024 Byte,而P ...

  7. 苹果预热 WWDC 2019

    Erika Hairston 是一位来自旧金山的 23 岁耶鲁大学毕业生,她的首款 app Zimela 刚刚上线.这款应用主要为了宣传科技行业的多样性.Hairston 设计的 Zimela 通过建 ...

  8. mysql排错小指南

    mysql排错小指南 查询运行很慢时,可以执行mysql> show processlist\G mysql> show processlist\G ******************* ...

  9. mapper映射文件配置之select、resultMap(转载)

    原文地址:http://www.cnblogs.com/dongying/p/4073259.html 先看select的配置吧: <select         <!-- 1. id ( ...

  10. enums应用详解

    枚举类: 获取枚举相关值: