Rust调用WPS转换Word为PDF

前言

https://crates.io/crates

COM接口名

MS控件名 name
WPS文字 KWPS.Aplication
WPS的Excel KET.Application
WPS的演示文档 KWPP.Application
Word Word.Application
Excel Excel.Application
Powerpoint Powerpoint.Application

概念

ActiveX、OLE、COM 之间的关系到底是什么样的?

COM是一套语言无关的二进制接口规范,它定义了一套实现面向对象的组件的规则。

GUID,IID,IUnknow,IDL,都是COM规范定义的概念,它规定了对象接口如何声明,对象如何创建销毁,对象生命周期如何管理,接口如何继承和聚合。

OLE 2(OLE1已经死了)是基于COM接口的一套应用程序直接实现数据交换和协作的规范,主要用于Office系列。

ActiveX是基于COM接口的UI 组件规范,主要用来实现语言无关的可视控件,多用于浏览器和快速应用开发领域比如 VB。

此外还有几个基于COM的框架,比如 DirectX,Media Foundation等。

OLE是一个通讯规范。OLE 1.0的时候COM还不存在,通讯的时候用的是DDE,所以OLE曾经不是基于COM的。

OLE 2.0就是基于COM了。考虑到OLE 1.0现在已经基本没人用了,说OLE是基于COM也没有什么问题。微软目前提到OLE的时候一般是特指复合文档(比如在WordPad里面插入MSPaint这样),除非加一个2.0后缀。一些人继续用OLE这个名称称呼所有OLE旗下的技术(ActiveX,剪贴板对象,拖放支持等等)。

ActiveX控件是OLE 2.0的简化版本,但是微软市场部又用ActiveX这个名字推广了Active Scripting和Active Document等等其他浏览器扩展技术,所以造成歧义。

现在提到ActiveX,一般是特指面向Internet Explorer网页开发者而开发,和java小程序有竞争的控件。广义的ActiveX就是所有COM对象了。

midl.exe

midl.exe是Microsoft的接口定义语言(Interface Definition Language,IDL)编译器,用于从IDL文件生成C/C++头文件和导入库,以供COM客户端和服务器使用。
midl.exe通常随Visual Studio和Windows SDK一起发布。

主要有以下三种获取方式:

(1) Visual Studio安装目录下。一般路径为:

C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.24.28314\bin\HostX86\x86\midl.exe

(2) Windows SDK安装目录。路径为:

C:\Program Files (x86)\Windows Kits\10\bin\<arch>\midl.exe
<arch>为x86、x64、arm或arm64,对应不同平台。

(3) 在Visual Studio的“开发人员命令提示”工具中直接运行midl命令。这会自动调用Visual Studio安装的midl.exe。
所以通常使用Visual Studio的此命令提示工具是使用midl.exe的最简单方式。

简单调用COM示例

https://kennykerr.ca/rust-getting-started/windows-or-windows-sys.html

https://github.com/microsoft/windows-rs

添加依赖

1
2
3
4
5
[dependencies.windows]
version = "0.48"
features = [
"Win32_System_Com",
]

示例代码

1
2
3
4
5
6
7
8
9
10
11
12
use windows::{core::*, Win32::System::Com::*};

fn main() -> Result<()> {
unsafe {
let uri = CreateUri(w!("https://www.psvmc.cn"), Uri_CREATE_CANONICALIZE, 0)?;
let domain = uri.GetDomain()?;
let port = uri.GetPort()?;

println!("{domain} ({port})");
Ok(())
}
}

调用WPS的COM接口

生成IDL文件

以WPS文字为例,我们在类厂中可以找到kwps.Application

image-20230423010907582

找到{000209FF-0000-4b30-A977-D214852036FF}

打开

1
HKEY_CLASSES_ROOT\WOW6432Node\CLSID\{000209FF-0000-4b30-A977-D214852036FF}\TypeLib

找到对应的值{00020905-0000-4b30-A977-D214852036FF}

我们通过TypeLib的CLSID,找到了WPS文字的idl生成的接口二进制文件的存放位置:

1
HKEY_CLASSES_ROOT\WOW6432Node\TypeLib\{00020905-0000-4B30-A977-D214852036FF}

可以看到WPS文字的API接口信息,保存在wpsapi.dll

wpsapi.dll不仅包含了代码信息,也将tlb嵌入了它的资源中。我们通过oleview.exe -> File ->View TypeLib,可以查看到WPS文字所有的API接口,对于微软Office亦是如此。不仅我们可以看到它的接口,甚至还可以看到其反编译生成的idl代码:

D:\Program Files\WPS Office\WPS Office\11.1.0.14036\office6\wpsapi.dll

获取IDL文件并生成Rust接口定义。

使用oleview生成idl文件

oleview的位置,双击打开

D:\Windows Kits\10\bin\10.0.17763.0\x64\oleview.exe

找到WPS目录下的wpsapi.dll

D:\Program Files\WPS Office\WPS Office\11.1.0.14036\office6\wpsapi.dll

使用oleview.exe打开后另存文件wpsapi.IDL

添加依赖

1
2
3
4
5
6
[dependencies.windows]
version = "0.48"
features = [
"Win32_System_Com",
"Win32_System_Ole"
]

代码

尚未实现。

文档转PDF程序

WPS转PDF命令行

https://github.com/lm3515/WPSToPDF

微软Office转PDF

https://github.com/cognidox/OfficeToPDF

这个只支持微软的Office。

下载后执行

1
OfficeToPDF.exe "D:\Tools\DocTest\01.docx" "D:\Tools\DocTest\01.pdf"

调用本地EXE

1
2
3
4
5
6
7
8
9
10
11
12
13
use std::process::Command;

fn main() {
let output = Command::new("cmd.exe")
.arg("/C chcp 65001 & ipconfig | findstr /i ipv4")
.output()
.expect("failed to execute process");
let stdout = String::from_utf8_lossy(&output.stdout)
.to_string()
.replace("\r\n", ";");

println!("{:?}", stdout);
}