【Activiti学习之三】Activiti API(二)
环境
JDK 1.8
MySQL 5.6
Tomcat 7
Eclipse-Luna
activiti 6.0
一、流程定义
1、中止与激活流程定义
package com.wjy.pd; import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition; public class ActiveTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 存储服务
RepositoryService rs = engine.getRepositoryService(); DeploymentBuilder builder = rs.createDeployment();
builder.addClasspathResource("test3.bpmn");
Deployment dep = builder.deploy(); ProcessDefinition def = rs.createProcessDefinitionQuery().deploymentId(dep.getId()).singleResult(); System.out.println("id: " + def.getId());
//根据 ACt_RE_PROCDEF中的key中止流程定义
rs.suspendProcessDefinitionByKey(def.getKey());
//根据 ACt_RE_PROCDEF中的key激活流程定义
rs.activateProcessDefinitionByKey(def.getKey());
//根据流程id中止流程定义
rs.suspendProcessDefinitionById(def.getId());
//根据流程id激活流程定义
rs.activateProcessDefinitionById(def.getId()); //如果流程被中止将会抛出异常 启动实例会报错
RuntimeService runService = engine.getRuntimeService();
runService.startProcessInstanceByKey(def.getKey());
}
}
2、流程定义权限
package com.wjy.pd; import java.util.List;
import java.util.UUID; import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.identity.Group;
import org.activiti.engine.identity.User;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.DeploymentBuilder;
import org.activiti.engine.repository.ProcessDefinition; public class AuthTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 存储服务
RepositoryService rs = engine.getRepositoryService();
IdentityService is = engine.getIdentityService(); User user = is.newUser(UUID.randomUUID().toString());
user.setFirstName("Angus");
is.saveUser(user); DeploymentBuilder builder = rs.createDeployment();
builder.addClasspathResource("test3.bpmn");
Deployment dep = builder.deploy();
ProcessDefinition def = rs.createProcessDefinitionQuery().deploymentId(dep.getId()).singleResult();
//设定流程定义和用户的关系 记录到表act_ru_identitylink
rs.addCandidateStarterUser(def.getId(), user.getId());
//设定流程定义和用户组的关系 记录到表act_ru_identitylink
rs.addCandidateStarterGroup(def.getId(), "group1");
//查询某个用户可以启动的流程清单
List<ProcessDefinition> defs = rs.createProcessDefinitionQuery().startableByUser(user.getId()).list();
for(ProcessDefinition de : defs) {
System.out.println(de.getId());
}
//查询某个用户可以启动的流程清单
//List<Group> list= is.createGroupQuery().potentialStarter(def.getId()).list();
}
}
二、任务操作
1、设置任务候选人(组)
package com.wjy.task; import java.util.List;
import java.util.UUID; import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.Task; public class CandidateTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService ts = engine.getTaskService();
IdentityService is = engine.getIdentityService();
// 创建任务
String taskId = UUID.randomUUID().toString();
Task task = ts.newTask(taskId);
task.setName("测试任务");
ts.saveTask(task);
// 创建用户
String userId = UUID.randomUUID().toString();
User user = is.newUser(userId);
user.setFirstName("angus");
is.saveUser(user);
//设置任务的候选用户 保存到act_ru_identitylink
ts.addCandidateUser(taskId, userId);
//设置任务的候选用户组 保存到act_ru_identitylink
//ts.addCandidateGroup(taskId, "groupid"); //查询用户组任务清单
//ts.createTaskQuery().taskCandidateGroup("groupid").list();
//查询候选用户任务清单
List<Task> tasks = ts.createTaskQuery().taskCandidateUser(userId).list();
System.out.println(userId + " 这个用户有权限处理的任务有:");
for(Task t : tasks) {
System.out.println(t.getName());
}
} }
2、设置任务持有人/受理人
package com.wjy.task; import java.util.List;
import java.util.UUID; import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.Task; public class OwnerTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService ts = engine.getTaskService();
IdentityService is = engine.getIdentityService();
// 创建任务
String taskId = UUID.randomUUID().toString();
Task task = ts.newTask(taskId);
task.setName("测试任务");
ts.saveTask(task);
// 创建用户
String userId = UUID.randomUUID().toString();
User user = is.newUser(userId);
user.setFirstName("angus");
is.saveUser(user);
// 设置任务的持有人
ts.setOwner(taskId, userId);
// 根据用户来查询他所持有的任务
List<Task> tasks = ts.createTaskQuery().taskOwner(userId).list();
for(Task t : tasks) {
System.out.println(t.getName());
}
//设置任务受理人 act_ru_task
ts.setAssignee(taskId, userId);
//根据受理人查询
ts.createTaskQuery().taskAssignee(userId).list();
} }
3、任务的声明与完成
package com.wjy.task; import java.util.UUID; import org.activiti.engine.IdentityService;
import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.identity.User;
import org.activiti.engine.task.Task; public class ClaimTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService ts = engine.getTaskService();
IdentityService is = engine.getIdentityService();
// 创建任务
String taskId = UUID.randomUUID().toString();
Task task = ts.newTask(taskId);
task.setName("测试任务");
ts.saveTask(task);
// 创建用户
String userId = UUID.randomUUID().toString();
User user = is.newUser(userId);
user.setFirstName("angus");
is.saveUser(user);
//声明 效果等同于taskAssignee 再次声明会报错
ts.claim(taskId, userId);
ts.claim(taskId, "100"); // List<Task> tasks = ts.createTaskQuery().taskAssignee(userId).list();
// for(Task t : tasks) {
// System.out.println(t.getName());
// }
//任务完成 让流程继续
ts.complete(taskId);
} }
三、任务参数与附件
参数保存在表act_ru_variable里
1、基本类型参数设置
布尔:boolean
日期:date
双精度:double
整形:integer
长整型:long
空值:null
短整型:short
字符型:string
当设置参数是Map时会遍历Map依次插入多个参数;
设置:ts.setVariable(taskId, "参数名称", "参数值");
获取:taskService.getVariable(taskId, "参数名称", 参数类型);
package com.wjy.var; import java.util.UUID; import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task; public class VarTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService ts = engine.getTaskService(); Task task = ts.newTask(UUID.randomUUID().toString());
task.setName("测试任务");
ts.saveTask(task); ts.setVariable(task.getId(), "var1", "hello");
String s = ts.getVariable(task.getId(), "var1", String.class);
System.out.println(s);
} }
2、序列化参数
如果参数是自定义对象,这个类对象需要实现Serializable接口,被序列化存到act_ge_bytearray;
package com.wjy.var;
import java.io.Serializable;
public class Person implements Serializable {
private Integer id;
private String name;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.wjy.var; import java.util.UUID; import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task; public class SerVarTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
TaskService ts = engine.getTaskService(); Task task = ts.newTask(UUID.randomUUID().toString());
task.setName("测试任务");
ts.saveTask(task); Person p = new Person();
p.setId(1);
p.setName("angus");
ts.setVariable(task.getId(), "person1", p); Person pr = ts.getVariable(task.getId(), "person1", Person.class);
System.out.println(pr.getId() + "---" + pr.getName());
} }
3、参数的作用域
setVariable设置参数在整个流程中可用;
setVariableLocal设置参数仅在当前任务中可用;
package com.wjy.var; import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task; public class VarLocalTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 存储服务
RepositoryService rs = engine.getRepositoryService();
// 运行时服务
RuntimeService runService = engine.getRuntimeService();
// 任务服务
TaskService taskService = engine.getTaskService(); Deployment dep = rs.createDeployment().addClasspathResource("var_local.bpmn").deploy();
ProcessDefinition pd = rs.createProcessDefinitionQuery().deploymentId(dep.getId()).singleResult(); ProcessInstance pi = runService.startProcessInstanceById(pd.getId()); Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
taskService.setVariableLocal(task.getId(), "days", 3);
System.out.println("当前任务:" + task.getName() + ", days参数:" + taskService.getVariableLocal(task.getId(), "days")); taskService.complete(task.getId()); task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
System.out.println("当前任务:" + task.getName() + ", days参数:" + taskService.getVariableLocal(task.getId(), "days"));
} }
4、数据参数
在bpmn定义文件中含有一个dataObject:
<?xml version="1.0" encoding="UTF-8"?>
<definitions xmlns="http://www.omg.org/spec/BPMN/20100524/MODEL"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:activiti="http://activiti.org/bpmn" xmlns:bpmndi="http://www.omg.org/spec/BPMN/20100524/DI"
xmlns:omgdc="http://www.omg.org/spec/DD/20100524/DC" xmlns:omgdi="http://www.omg.org/spec/DD/20100524/DI"
typeLanguage="http://www.w3.org/2001/XMLSchema" expressionLanguage="http://www.w3.org/1999/XPath"
targetNamespace="http://www.activiti.org/test">
<process id="myProcess" name="My process" isExecutable="true">
<dataObject id="personName" name="personName"
itemSubjectRef="xsd:string">
<extensionElements>
<activiti:value>Crazyit</activiti:value>
</extensionElements>
</dataObject>
<startEvent id="startevent1" name="Start"></startEvent>
<endEvent id="endevent1" name="End"></endEvent>
<userTask id="usertask1" name="task1"></userTask>
<userTask id="usertask2" name="task2"></userTask>
<sequenceFlow id="flow1" sourceRef="startevent1"
targetRef="usertask1"></sequenceFlow>
<sequenceFlow id="flow2" sourceRef="usertask1"
targetRef="usertask2"></sequenceFlow>
<sequenceFlow id="flow3" sourceRef="usertask2"
targetRef="endevent1"></sequenceFlow>
</process>
<bpmndi:BPMNDiagram id="BPMNDiagram_myProcess">
<bpmndi:BPMNPlane bpmnElement="myProcess"
id="BPMNPlane_myProcess">
<bpmndi:BPMNShape bpmnElement="startevent1"
id="BPMNShape_startevent1">
<omgdc:Bounds height="35.0" width="35.0" x="80.0"
y="120.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="endevent1"
id="BPMNShape_endevent1">
<omgdc:Bounds height="35.0" width="35.0" x="510.0"
y="120.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask1"
id="BPMNShape_usertask1">
<omgdc:Bounds height="55.0" width="105.0"
x="170.0" y="110.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNShape bpmnElement="usertask2"
id="BPMNShape_usertask2">
<omgdc:Bounds height="55.0" width="105.0"
x="340.0" y="110.0"></omgdc:Bounds>
</bpmndi:BPMNShape>
<bpmndi:BPMNEdge bpmnElement="flow1"
id="BPMNEdge_flow1">
<omgdi:waypoint x="115.0" y="137.0"></omgdi:waypoint>
<omgdi:waypoint x="170.0" y="137.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow2"
id="BPMNEdge_flow2">
<omgdi:waypoint x="275.0" y="137.0"></omgdi:waypoint>
<omgdi:waypoint x="340.0" y="137.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
<bpmndi:BPMNEdge bpmnElement="flow3"
id="BPMNEdge_flow3">
<omgdi:waypoint x="445.0" y="137.0"></omgdi:waypoint>
<omgdi:waypoint x="510.0" y="137.0"></omgdi:waypoint>
</bpmndi:BPMNEdge>
</bpmndi:BPMNPlane>
</bpmndi:BPMNDiagram>
</definitions>
package com.wjy.var; import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task; public class DataObjectTest { public static void main(String[] args) {
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 存储服务
RepositoryService rs = engine.getRepositoryService();
// 运行时服务
RuntimeService runService = engine.getRuntimeService();
// 任务服务
TaskService taskService = engine.getTaskService(); Deployment dep = rs.createDeployment().addClasspathResource("dataobject.bpmn").deploy();
ProcessDefinition pd = rs.createProcessDefinitionQuery().deploymentId(dep.getId()).singleResult(); ProcessInstance pi = runService.startProcessInstanceById(pd.getId()); Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
String var = taskService.getVariable(task.getId(), "personName", String.class);
System.out.println(var); } }
5、附件
package com.wjy.var; import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream; import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Task; public class CreateAttachment { public static void main(String[] args) throws FileNotFoundException {
// 获取流程引擎实例
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 获取任务服务组件
TaskService taskService = engine.getTaskService();
// 获取运行服务组件
RuntimeService runtimeService = engine.getRuntimeService();
// 流程存储服务组件
RepositoryService repositoryService = engine.getRepositoryService();
// 部署流程描述文件
Deployment dep = repositoryService.createDeployment()
.addClasspathResource("bpmn/vacation.bpmn").deploy();
// 查找流程定义
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
.deploymentId(dep.getId()).singleResult();
// 启动流程
ProcessInstance pi = runtimeService
.startProcessInstanceById(pd.getId());
// 查找任务
Task task = taskService.createTaskQuery().processInstanceId(pi.getId()).singleResult();
// 设置任务附件
//Attachment createAttachment(String attachmentType, String taskId, String processInstanceId, String attachmentName, String attachmentDescription, InputStream content);
taskService.createAttachment("web url", task.getId(), pi.getId(),"163.com", "163 web page", "http://www.163.com"); // 创建图片输入流
InputStream is = new FileInputStream(new File("result.png"));
// 设置输入流为任务附件
//Attachment createAttachment(String attachmentType, String taskId, String processInstanceId, String attachmentName, String attachmentDescription, String url);
taskService.createAttachment("web url", task.getId(), pi.getId(),"163.com", "163 web page", is); //保存附件
//void saveAttachment(Attachment attachment);
//删除附件
//void deleteAttachment(String attachmentId);
//查询附件
//Attachment getAttachment(String attachmentId);
//InputStream getAttachmentContent(String attachmentId);
//List<Attachment> getTaskAttachments(String taskId);
//List<Attachment> getProcessInstanceAttachments(String processInstanceId); } }
6、评论
package com.wjy.var; import java.util.List; import org.activiti.engine.ProcessEngine;
import org.activiti.engine.ProcessEngines;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.ProcessDefinition;
import org.activiti.engine.runtime.ProcessInstance;
import org.activiti.engine.task.Comment;
import org.activiti.engine.task.Task; public class AddComment { public static void main(String[] args) {
// 获取流程引擎实例
ProcessEngine engine = ProcessEngines.getDefaultProcessEngine();
// 获取任务服务组件
TaskService taskService = engine.getTaskService();
// 获取运行服务组件
RuntimeService runtimeService = engine.getRuntimeService();
// 流程存储服务组件
RepositoryService repositoryService = engine.getRepositoryService();
// 部署流程描述文件
Deployment dep = repositoryService.createDeployment()
.addClasspathResource("vacation.bpmn").deploy();
// 查找流程定义
ProcessDefinition pd = repositoryService.createProcessDefinitionQuery()
.deploymentId(dep.getId()).singleResult();
// 启动流程
ProcessInstance pi = runtimeService
.startProcessInstanceById(pd.getId());
// 查找任务
Task task = taskService.createTaskQuery().processInstanceId(pi.getId())
.singleResult();
// 添加任务评论
taskService.addComment(task.getId(), pi.getId(),"this is comment message");
// 查询评论
List<Comment> comments = taskService.getTaskComments(task.getId());
System.out.println("评论数量:" + comments.size()); //增加评论
// Comment addComment(String taskId, String processInstanceId, String message);
//查询评论
// Comment getComment(String commentId);
// List<Comment> getTaskComments(String taskId);
//删除评论
// void deleteComments(String taskId, String processInstanceId);
// void deleteComment(String commentId);
} }
【Activiti学习之三】Activiti API(二)的更多相关文章
- Activiti 学习笔记记录(二)
上一篇:Activiti 学习笔记记录 导读:对于工作流引擎的使用,我们都知道,需要一个业务事件,比如请假,它会去走一个流程(提交申请->领导审批---(批,不批)---->结束),Act ...
- Java学习随笔---常用API(二)
Object类的toString方法 将一个对象返回为字符串形式,但一般使用的时候会覆盖重写toString方法 Object类是所有类的父亲 // public class Person { pri ...
- Activiti 学习笔记记录(2016-8-31)
上一篇:Activiti 学习笔记记录(二) 导读:上一篇学习了bpmn 画图的常用图形标记.那如何用它们组成一个可用文件呢? 我们知道 bpmn 其实是一个xml 文件
- activiti学习资料(架构描述)
Activiti学习资料 Activiti是业界很流行的java工作流引擎,关于Activiti与JBPM5的关系和如何选择不是本文要讨论的话题,相关内容可以baidu一下.Activiti从架构角度 ...
- Civil 3D API二次开发学习指南
Civil 3D构建于AutoCAD 和 Map 3D之上,在学习Civil 3D API二次开发之前,您至少需要了解AutoCAD API的二次开发,你可以参考AutoCAD .NET API二次开 ...
- 【Activiti学习之四】Activiti API(三)
环境 JDK 1.8 MySQL 5.6 Tomcat 7 Eclipse-Luna activiti 6.0 一.启动流程 多种方式启动 package com.wjy.pro; import or ...
- activiti学习笔记二
上一篇文章大概讲了下什么是流程引擎,为什么我们要用流程引擎,他的基本原理是啥,以及怎么进行基本的使用,这篇文章我们再讲下其他的一些使用. 删除流程部署 package activiti02; impo ...
- Activiti 学习笔记记录
官方在线用户手册(英文版):http://activiti.org/userguide/index.html 中文用户手册:http://www.mossle.com/docs/activiti/in ...
- activiti学习6:启动流程后动态获取流程图
目录 activiti学习6:启动流程后动态获取流程图 一.绘图原理 二.根据流程定义id绘图 三.根据流程实例id绘图 3.1 基本原理 3.2 当前节点的获取 3.3 走过的节点的获取 3.4 绘 ...
随机推荐
- 深入V8引擎-默认Platform之mac篇(1)
又到了常规的堆砌代码凑文章字数环节,很多API我就直接贴官方的英文释义,个人翻译其实有时候并不是很准确,搞错了甚至会误导,还是尽量自己去理解. 首先看看入口方法. std::unique_ptr< ...
- 一般处理程序Session
1.要在一般处理程序中获取其他页面的session值,需要引用名空间: using System.Web.SessionState; 2.然后继承一个接口:IRequiresSessionState ...
- 第一个APP上架IOS审核相关的记录
以前一直没做过APP开发,第一版是用WAP版做的,采用了light7框架制作,没有UI设计. 升级到第二版之后,使用了HBUILDER的方式开发,https://dcloud.io/ 官方在这里. 目 ...
- C# winform 启动外部程序
//class里面放入这段代码[DllImport("shell32.dll")]public static extern int ShellExecute(IntPtr hwnd ...
- 使用GitHub/码云/Git个性化设置
参考链接:https://www.liaoxuefeng.com/wiki/896043488029600/900937935629664 这似乎很可笑,我还从来没有想过为一个网站的使用方法写一篇来记 ...
- Spring所有注解大揭秘
声明bean的注解 @Component 组件,没有明确的角色 @Service 在业务逻辑层使用(service层) @Repository 在数据访问层使用(dao层) @Controller 在 ...
- EF自动创建数据库步骤之四(启用数据库初始器)
在创建完DBIfNotExistsInitializer数据库初始化器类后,需要在程序每一次访问数据库前,告诉EF使用该初始化器进行初始化. 代码如下 : Database.SetInitialize ...
- 《Android开发艺术探索》读书笔记之IntentFillter的匹配规则
使用intent启动不同组件的方法 组件类型 启动方法 Activity startActivity(Intent intent) startActivityForResult(Intent inte ...
- Linux服务之DNS介绍
DNS-------Domain Name System域名系统 介绍:DNS就是把域名和IP地址联系在一起的服务,有了DNS服务器,你就不用输入IP地址来访问一个网站,可以通过输入网址访问. ...
- 逆向破解之160个CrackMe —— 031
CrackMe —— 031 160 CrackMe 是比较适合新手学习逆向破解的CrackMe的一个集合一共160个待逆向破解的程序 CrackMe:它们都是一些公开给别人尝试破解的小程序,制作 c ...