C#调用windows API实现 smallpdf客户端程序进行批量压缩
一、背景
Smallpdf 网站针对PDF文件提供了非常齐全的功能:PDF 与 Word、PPT、Excel、JPG 的相互转化、PDF 的压缩、编辑、合并、分割、解密、加密等功能,用户无需注册即可免费使用。
但是不注册每小时只能处理有限的PDF,对于我们这种有大量PDF需要压缩的人来说就不太合适啦,所以购买了专业版,其在线版本提供了批量压缩的功能,需要上批量上传等待压缩后再下载(存在批量上传后,浏览器卡死或者无法压缩一直等待,以及压缩成功后无法下载,速度只有几十K),而客户端程序版本又没有批量压缩的功能,只能一篇一篇的打开压缩。
为了解解上述问题,经过考虑,准备利用其客户端的压缩功能来进行批量压缩,虽然时间花得久点,但是在本地不用考虑网络因素这些,至少非常可控,但是是不可能人工一篇一篇来压的,所以这就是这篇文章的由来。
二、先看实现后的效果:
三、实现原理
主要的原理就是调用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; } } }