C#调用windows API实现 smallpdf客户端程序进行批量压缩
一、背景
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客户端程序进行批量压缩的更多相关文章
- C#调用Windows API(示例:显示任务管理器里的程序名称)
作为初学者来说,在C#中使用API确是一件令人头疼的问题. 在使用API之前你必须知道如何在C#中使用结构.类型转换.安全/不安全代码,可控/不可控代码等许多知识. 在.Net Framework S ...
- C#调用windows API的一些方法
使用C#调用windows API(从其它地方总结来的,以备查询) C#调用windows API也可以叫做C#如何直接调用非托管代码,通常有2种方法: 1. 直接调用从 DLL 导出的函数. 2. ...
- C#中调用Windows API的要点 .
介绍 API(Application Programming Interface),我想大家不会陌生,它是我们Windows编程的常客,虽然基于.Net平台的C#有了强大的类库,但是,我们还是不能否认 ...
- c# 判断窗体是否永在最前(TopMost),调用windows API
许多程序都可以把自身的窗体设为最前显示状态,这个可以参考博客c#让窗体永在最前 调用windows api 将窗体设为topmost.那么如何判断桌面上的一个窗体是否为最前显示状态呢,不光是自己的程序 ...
- c#让窗体永在最前 调用windows api 将窗体设为topmost
有时候应用程序需要将一个窗体始终位于屏幕的最前面,即使切换到其它窗体也能看到该窗体,这样的窗体就叫做TopMost窗体. 用C#制作TopMost窗体之前,首先要了解如何声明SetWindowPos函 ...
- 善于 调用Windows API
前一段时间看见别人做的一个自动填写信息并且点击登录的程序,觉得很有意思. 其实就是在程序中调用Windows的API,那么如何调用,下面就做个简单的介绍. 写的简单粗暴, 不喜轻喷. 0.首先引入名称 ...
- VBS调用Windows API函数
Demon's Blog 忘记了,喜欢一个人的感觉 Demon's Blog » 程序设计 » VBS调用Windows API函数 « 用VBS修改Windows用户密码 在VB中创建和使用 ...
- C#调用Windows API函数截图
界面如下: 下面放了一个PictureBox 首先是声明函数: //这里是调用 Windows API函数来进行截图 //首先导入库文件 [System.Runtime.InteropServices ...
- 【转】用C#调用Windows API向指定窗口发送
一.调用Windows API. C#下调用Windows API方法如下: 1.引入命名空间:using System.Runtime.InteropServices; 2.引用需要使用的方法,格式 ...
随机推荐
- uoj218_火车管理
题意 \(n\)个位置,每个位置一个栈,三种操作,询问区间栈顶的和,区间入栈某个数,单点出栈某个数. 分析 用一个线段树来维护栈顶的和,区间(单点)更新和区间询问. 用一个主席树来维护每个位置最新一次 ...
- sqlserver2008 必知必会技巧-- 快速索引对象
对象资源管理器里面 -- 数据库 -- 表目录 ,然后按 f7 弹出 对象资源管理详细信息 , 里面有搜索栏 , 可以 使用 % 进行模糊查询 例如我们查包含 student的表 %student% ...
- sql:union 与union的使用和区别
SQL UNION 操作符 UNION 操作符用于合并两个或多个 SELECT 语句的结果集. 请注意,UNION 内部的 SELECT 语句必须拥有相同数量的列.列也必须拥有相似的数据类型.同时,每 ...
- flutter-常见的基础组件(一)
flutter 1.Text 文本组件 Text( 'czklove', //文本上的文字 style: TextStyle( // style 代表对文字的描述 fontSize: 18, //文字 ...
- ES6拷贝方法
ES6 中对象拷贝方法: 方法一: Object.assign() // 对象浅拷贝, 复制所有可枚举属性 const obj1 = {a: 1}; const obj2 = {b: 2}; // c ...
- js实现简单进度条
主要用到的 offsetWidth 属性,定时器. <!DOCTYPE html> <html> 3 <head> <meta http-equiv=&quo ...
- GDAL支持中文路径和Shp文件中文属性写入
在使用GDAL的过程中,为了支持中文,比需手动进行中文路径的设置,同时特别是在对Shp的属性进行中文输入的时候,都必须进行必要的设定. 为了支持中文路径,在注册了驱动之后,加上第三句就可以了.必须设置 ...
- python自动化测试—配置文件的使用
一.什么是配置文件? 配置文件示例 [mysql] default-character-set = utf8 [mysqld] port = 3306 basedir = c:\mysql-5.7.2 ...
- Linux20期学习笔记 Day4
环境变量.常用系统变量:vim编辑器使用方法及实验:shell脚本两个层次 内置参数及相关实验
- 【转】Linux iptables 详解
转自:https://www.cnblogs.com/qwertwwwe/p/9452370.html 最近搭一个框架需要用到iptables做映射,学习了下iptables的原理,总结下方便以后查~ ...