一、背景和实现原理
最近公司项目要求通过java控制本地刻录机,在网上查了很多资料都没有说的很详细的,之后参考了一篇博客https://stackoverflow.com/questions/8556291/reading-and-writing-to-a-dvd-cd-java,最终通过dom4j调用本机imapi实现了刻录功能,在此记录一下。
该实现方式就是通过com4j类库进行桥接,com4j是一个类型安全的java/com桥接器,作为一个java类库,允许java与Microsoft组件对象模型无缝的互操作,在此不做过多的赘述,
详细请参考:【笔记】Java 调用 COM 组件之 com4j 使用说明 - 子兮子兮
主要原理:通过com4j将对应的imapi2.dll,imapi2fs.dll接口生成java可以调用的类。
二、实现步骤
步骤1:将jdk版本切换为32位(重要),原因是imapi2.dll,imapi2fs.dll是32位的应用程序,无法与64位jdk进行匹配,否则生成的类无法使用,会报以下错误
Exception in thread "main" com4j.ExecutionException: com4j.ComException: 80040154 CoCreateInstance failed : Class not registered : .\com4j.cpp:153
步骤2:com4j包下载(两个都需下载)
1)com4j对应jar包(用于桥接应用程序)
下载地址:https://github.com/kohsuke/com4j/downloads
2)com4j对应maven包
<dependency>
<groupId>org.jvnet.com4j</groupId>
<artifactId>com4j</artifactId>
<version>2.1</version>
</dependency>
步骤3:解压下载后的文件,将文件中jar包(tlbimp.jar、com4j.jar、args4j-2.0.1.jar)复制到某一文件夹下,如(D:\bb)
步骤4:cmd到D:\bb目录,执行
java -jar tlbimp.jar -o imapi2fs -p com.yxm.burn.imapi2 "C:\Windows\System32\imapi2fs.dll"
java -jar tlbimp.jar -o imapi2 -p com.yxm.burn.imapi2 "C:\Windows\System32\imapi2.dll"
步骤5: 将imapi2和imapi2fs下的类进行整合
其中ClassFactoryExt、IDiscFormat2DataExt、StreamClassFactory需要自己补充
package com.yxm.imapi2;
import com4j.COM4J;
/**
* Defines methods to create COM objects
*/
public abstract class ClassFactoryExt {
private ClassFactoryExt() {} // instanciation is not allowed
/**
* Microsoft IMAPIv2 Data Writer
*/
public static com.yxm.imapi2.IDiscFormat2DataExt createMsftDiscFormat2Data() {
return COM4J.createInstance( com.yxm.imapi2.IDiscFormat2DataExt.class, "{2735412A-7F64-5B0F-8F00-5D77AFBE261E}" );
}
}
package com.yxm.imapi2;
import com4j.IID;
import com4j.VTID;
/**
* Data Writer
*/
@IID("{27354153-9F64-5B0F-8F00-5D77AFBE261E}")
public interface IDiscFormat2DataExt extends com.yxm.imapi2.IDiscFormat2Data {
/**
* The state (usability) of the current media
*/
@VTID(18)
//com.ms.imapi2.IMAPI_FORMAT2_DATA_MEDIA_STATE currentMediaStatus();
int currentMediaStatusExt();
}
package com.yxm.imapi2;
import com4j.COM4J;
/**
* Defines methods to create COM objects
*/
public abstract class StreamClassFactory {
private StreamClassFactory() {} // instanciation is not allowed
/**
* Boot options
*/
public static com.yxm.imapi2.IBootOptions createBootOptions() {
return COM4J.createInstance( com.yxm.imapi2.IBootOptions.class, "{2C941FCE-975B-59BE-A960-9A2A262853A5}" );
}
/**
* File system image
*/
public static com.yxm.imapi2.IFileSystemImage3 createMsftFileSystemImage() {
return COM4J.createInstance( com.yxm.imapi2.IFileSystemImage3.class, "{2C941FC5-975B-59BE-A960-9A2A262853A5}" );
}
/**
* Microsoft IMAPIv2 Iso Image Manager
*/
public static com.yxm.imapi2.IIsoImageManager createMsftIsoImageManager() {
return COM4J.createInstance( com.yxm.imapi2.IIsoImageManager.class, "{CEEE3B62-8F56-4056-869B-EF16917E3EFC}" );
}
}
步骤6:编写调用本地刻录光驱代码(对应的iso文件可自己去创建生成)
package com.yxm.burn;
import com.yxm.imapi2.*;
import java.io.File;
public class TestUtils {
public static void main(String[] args) {
testBurn();
}
public static void testBurn(){
File isoFile = new File("D:\\tmp\\4e1c1bf3-1195-4513-847a-c55d11500d60.iso");
//创建IMAPIv2光盘驱动
IDiscMaster2 dm = ClassFactory.createMsftDiscMaster2();
//获取可用光盘驱动的个数
int count = dm.count();
//获取其中一个光盘驱动(建议只链接一个光盘驱动)
String recorderUniqueId = null;
for (int i = 0; i < count; i++)
{
String cur = dm.item(i);
recorderUniqueId = cur;
}
//创建刻录机
IDiscRecorder2 recorder = ClassFactory.createMsftDiscRecorder2();
//初始化刻录机
recorder.initializeDiscRecorder(recorderUniqueId);
// log.info("正在使用的刻录机: " + recorder.vendorId() + " " + recorder.productId());
//创建镜像管理器
IIsoImageManager imageManager = StreamClassFactory.createMsftIsoImageManager();
imageManager.setPath(isoFile.getAbsolutePath());
imageManager.validate();
// log.info("ISO验证成功: " + isoFile.getAbsolutePath());
//创建数据写入程序
IDiscFormat2DataExt discData = ClassFactoryExt.createMsftDiscFormat2Data();
//数据写入程序链接刻录机
discData.recorder(recorder);
//客户端名称(用于确定刻录机保留冲突)
discData.clientName(TestUtils.class.getSimpleName());
//获取当前刻录文件状态(确保文件是iso文件)
int mediaStatus = discData.currentMediaStatusExt();
// log.info("刻录文件的状态: " + mediaStatus);
if ((mediaStatus & IMAPI_FORMAT2_DATA_MEDIA_STATE.IMAPI_FORMAT2_DATA_MEDIA_STATE_WRITE_PROTECTED.comEnumValue()) != 0)
throw new RuntimeException("Media is write protected / not empty.");
//检查写入程序的写入内容是否为空
int addr = discData.nextWritableAddress();
if (addr != 0)
throw new RuntimeException("Disc is not empty, not writing.");
//获取iso文件流
IStream isoStream = imageManager.stream();
//开始刻录
// log.info("Writing CD");
discData.write(isoStream);
}
}
三、总结
通过以上步骤即可实现java调用本地刻录光驱进行刻录,再配合自动生成iso文件即可实现将对应的文件或文件夹自动刻录到光盘,iso文件的生成可以使用UltraISO软件,通过命令进行调用,
Process exec = Runtime.getRuntime().exec("cmd.exe /c c: && cd c:/UltraISO && ultraiso -lowercase -imax -file " + filePath + " -output " + trgPath);
以上就可以实现刻录机的调用控制。
注意:
1、imapi2包中的类一定要自己手动生成,因为每个电脑的应用程序的对应注册表中的值是不一样的,直接复用可能会无法实现接口调用。
2、如果将此功能作为插件部署(即通过java -jar xxxx.jar运行项目)需要将步骤4对应生成的com4j-amd64.dll或com4j-x86.dll放到C:\Windows\System32文件夹中,否则无法使用。